Reference document

BitKuruş — Third-party wallet integration

This is the authoritative version, rendered from the project's own source document rather than retyped — so it cannot drift from what the team maintains.

Note: Reference documents are maintained in English. A Turkish summary of this material is on the summary page.

This document is for developers building wallet clients (mobile, desktop, browser, hardware) that talk to an existing BitKuruş node over HTTPS. You do not need to run a federation node unless you also want to validate or replicate the ledger.

Related pages

Resource URL / path
Wallet docs (web) /docs
Federation / node protocol /run-a-node
Reference wallet UI /wallet (see public/bitkurus/wallet.js)
Network profile GET /api/network

1. Wallet vs node

Role Responsibility
Wallet Generate Ed25519 keys, read balances, plan UTXO spends, sign transactions, submit to POST /api/tx/submit
Node Persist ledger, peer validation quorum, replication, issuance (apple tree, commission)

A correct wallet never sends private keys to the API.


2. Identity and signing

  • Address: 64-character lowercase hex Ed25519 public key (sender on every transaction).
  • Signature: 128-character lowercase hex detached Ed25519 over canonical JSON of the payload without the signature field.

Canonical JSON rules (must match verifiers byte-for-byte):

  1. Object keys sorted ascending (byte-wise UTF-8 order).
  2. Arrays keep submission order.
  3. No \uXXXX escapes for Unicode — raw UTF-8.
  4. Forward slashes not escaped.
  5. Money amounts are decimal strings with up to 18 fractional digits, e.g. "1.000000000000000000" — never JSON numbers/floats.

Signing payload shape:

{
  "tx_id": "wallet-tx-…",
  "type": "transfer",
  "sender": "<64-hex pubkey>",
  "nonce": "wallet-tx-…-nonce",
  "inputs": [{ "token_id": "…", "version": 1 }],
  "outputs": [
    { "token_id": "wallet-out-…", "value": "10.000000000000000000", "owner": "<receiver>" }
  ]
}

Full rules: /run-a-nodeIdentity & canonical JSON.


3. Read APIs (before send)

GET /api/wallet/{public_key}

Returns balance, tokens[] with token_id, value, version, status, origin, origin_tx_id.

GET /api/network

Returns peer list, validator_commission_rate_ppm, capabilities. Use peers.length + 1 to estimate quorum size (floor(N/2)+1 on a typical federation).

GET /api/token/{token_id}
GET /api/tx/{tx_id}
GET /api/wallet/{public_key}/activity?limit=30

activity returns recent sends, receives, merge/split, commission, apple tree rows from the ledger (for history UI).

Optional presence probe after minting a new output:

GET /api/cluster/token/{token_id}?expect=active

Returns confirmed_count, node_count — how many federation members see the token as active.


4. UTXO transfer planning

BitKuruş uses UTXO-style tokens: each token_id is an unspent output. The wallet must select inputs whose sum ≥ amount (and handle change).

Situation type Steps
1 input, exact amount transfer 1× submit
1 input, pay less than value split 1× submit (receiver + change to self)
N inputs, exact total merge 1× submit (burn all inputs → one output to receiver)
N inputs, pay less than total merge then split 2× submit (see below)

Value conservation: sum(inputs.value) = sum(outputs.value) (fee is implicit burn of the difference when you omit change; reference wallet sends explicit change output to sender).

Versions: Every input must include the current version from GET /api/wallet/{pubkey}. After any committed spend, versions change — always refresh before a new tx.

Reference implementation: planWalletTransfer() in public/bitkurus/wallet.js.


5. Multi-step flow: merge then pay

When the user has multiple tokens and needs change (most common failure scenario):

1. Plan merge tx:
   - type: merge
   - inputs: all selected UTXOs (with versions)
   - outputs: one token to sender with value = sum(inputs)

2. POST /api/tx/submit (signed merge)
   - Response 202 accepted — not final yet

3. Poll GET /api/tx/{merge_tx_id} until status === "committed"

4. Wait until merged output is spendable ON PEERS:
   - GET /api/token/{merged_token_id} → status active
   - GET /api/cluster/token/{merged_token_id}?expect=active
     until confirmed_count >= floor(node_count/2)+1 (or retry policy below)

5. GET /api/wallet/{sender} again — refresh versions

6. Plan payment tx:
   - type: split (or transfer if no change)
   - inputs: [{ token_id: merged_token_id, version: <current> }]
   - outputs: receiver + optional change to sender

7. POST /api/tx/submit (signed payment)
8. Poll until committed

Do not skip step 4. If you submit the payment immediately after local merge commit, peers may still return Peer validation failed because they have not replicated the merged token yet.


6. Peer validation (why submits fail)

Before any transaction is persisted, the receiving node broadcasts a transaction_validation envelope to configured peers. Each peer runs the same rules against its own database and answers validated or rejected with a reason.

Typical rejection reasons for wallet authors:

reason (examples) Meaning
Input token does not exist. Peer has not replicated the input yet (common right after merge).
Input token is not active. Already spent or locked.
Input version mismatch. Stale version in your tx — refresh wallet state.
Input token is locked. Another tx holds the lock — wait and retry.

HTTP 409 response shape:

{
  "result": "rejected",
  "data": {
    "error": "Peer validation failed.",
    "peer_id": "node2",
    "reason": "Input token does not exist."
  }
}

Also possible:

{
  "error": "Insufficient peer approvals.",
  "required": 2,
  "validated": 0
}

Recommended client behaviour

  1. Show peer_id and reason to the user.
  2. On Peer validation failed or Insufficient peer approvals, wait 1.5–3s, refresh wallet, retry (reference wallet retries up to 4 times).
  3. After merge, poll /api/cluster/token/{id} until quorum before the payment submit.

7. Submit and finality

POST /api/tx/submit
Content-Type: application/json

{ ... full signed transaction including signature ... }
HTTP Meaning
202 + result: accepted Queued — poll GET /api/tx/{tx_id}
409 + result: rejected Validation failed — see data.error, data.reason
403 Observer node / read-only

Poll until status is committed or rejected. Do not chain a second spend until the first tx is committed (or you risk version/lock errors).


8. Transaction types (summary)

type Inputs Outputs Use
transfer 1 1 Simple send
split 1 2+ Send + change
merge 2+ 1 Consolidate UTXOs
merge + split Two transactions (merge, then split)

sender must equal the 64-hex public key that owns all inputs.


9. Integration checklist

  • Ed25519 keygen; private key never leaves device
  • Canonical JSON signer tested against reference vectors (/run-a-node)
  • GET /api/wallet/{pubkey} drives input selection
  • Correct version on every input
  • UTXO planner: merge / split / merge-then-split
  • Poll GET /api/tx/{id} after each submit
  • After merge: wait for token active + peer quorum before next submit
  • Retry policy for peer validation failures
  • Refresh wallet state after successful commit
  • Optional: GET /api/wallet/{pubkey}/activity for history UI
  • Optional: GET /api/cluster/token/{id} for replication indicator

10. What you do not need (wallet-only)

  • POST /api/peer/replicate (node operators only)
  • Validator commission wallet configuration
  • Apple tree issuance (unless you build a faucet/tree client)
  • Running queue workers or MySQL

11. Reference code map

Concern File
Transfer planning public/bitkurus/wallet.jsplanWalletTransfer
Merge-then-pay + peer wait submitPaymentAfterMerge, waitForActiveToken, waitForTokenPeerQuorum
Submit + retry submitSignedTransaction, submitSignedTransactionOnce
Canonical sign signPayload
Server validation app/Services/Transactions/TransactionService.php
Peer preflight app/Http/Controllers/Api/TransactionController.php

12. FAQ

Can I use one transfer with two inputs?
No. Use merge or two-step merge + split.

Is 202 accepted enough to show success?
No. Wait for committed.

Will a custom wallet “just work” if signing is correct?
Only if UTXO planning and post-merge peer sync are implemented. Signing alone is not sufficient.

Is BitKuruş mainnet-ready?
Prototype / trusted federation. See README trust model.