You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Wallet integrations (the in-tree zk-coins/app, and external integrators like Cake Wallet and Layerz Wallet) need a per-address transaction history endpoint. Today the public API surface (/api/balance, /api/info, /api/mint, /api/send, /api/commit, /api/receive, /api/proof/:id, /api/username/*) has no way to list past coin movements for a given account address.
The web app currently sidesteps this by tracking its own state locally, but that breaks the documented thin-client invariant (feedback_zkcoins_thin_client: app only holds keys + UI; before every signed request, the app calls api.balance for authoritative server state). Mobile wallets cannot follow the same shortcut — they need server-authoritative history to render a transaction list.
This is the gating endpoint for everything downstream:
Pure-JS @zkcoins/sdk cannot expose getTransactions() without it.
OpenAPI 3.x spec cannot be published as complete without it.
cw_zkcoins (Cake Wallet) and zkcoins-wallet.ts (Layerz Wallet) adapters need it for getCommonTransactions() / fetchTransactions().
Proposed shape
GET /api/history?address=<hex>&limit=<n>&offset=<n>
address (required): the account address (hex, same encoding as /api/balance).
txid is the on-chain commit txid where this coin movement was inscribed. For pending mints/sends not yet broadcast or not yet confirmed, the proof_id can be exposed in the same slot (TBD on the canonical identifier — see Open Questions).
direction is from the queried address's perspective.
counterparty is optional — sends include the recipient address, receives include the sender. Mints have no counterparty.
status: pending until the inscription confirms in a block, then confirmed. failed for the broadcast-rejected case the publisher already surfaces (503 SERVICE_UNAVAILABLE path).
block_height is set once status === "confirmed".
Acceptance criteria
Endpoint registered in router.rs, behind always status (no feature gate).
Postgres query joins the existing coin/send tables; no new schema work expected.
100% line + function coverage on the activated MVP surface (router + handler).
Unit tests: happy path, empty result, pagination boundaries (limit=0 / limit=max+1 → 400; offset beyond total → empty items with correct total), missing address → 400, invalid hex → 400.
Live contract test in node/tests/api_remote.rs adds a /api/history round-trip on a freshly-minted address.
Zod schema (zk-coins/app/src/lib/api/schemas.ts) + typed client method (zk-coins/app/src/lib/api/client.ts) added — keeps the app in sync as the in-tree consumer. App's behaviour stays unchanged until a later PR wires the UI to it.
README §Features table entry added.
Out of scope
App UI changes that consume the new endpoint — separate PR once the server side is stable.
OpenAPI 3.x export (depends on this PR landing first).
WebSocket push of new history rows (post-MVP).
Open questions for the PR author
Canonical identifier for an unconfirmed row — txid won't exist until the publisher broadcasts. Options: (a) use the proof_id (server-internal monotonic ID, already exposed via /api/proof/:id); (b) use a synthetic id field separate from txid. Recommendation: separate fields, id always set, txid only once broadcast.
Sort order — newest first (ORDER BY timestamp DESC) is the wallet-UI default; pin this in the docs.
Address normalization — match whatever /api/balance accepts (case sensitivity, length checks). Reuse the existing helper.
Problem
Wallet integrations (the in-tree
zk-coins/app, and external integrators like Cake Wallet and Layerz Wallet) need a per-address transaction history endpoint. Today the public API surface (/api/balance,/api/info,/api/mint,/api/send,/api/commit,/api/receive,/api/proof/:id,/api/username/*) has no way to list past coin movements for a given account address.The web app currently sidesteps this by tracking its own state locally, but that breaks the documented thin-client invariant (
feedback_zkcoins_thin_client: app only holds keys + UI; before every signed request, the app callsapi.balancefor authoritative server state). Mobile wallets cannot follow the same shortcut — they need server-authoritative history to render a transaction list.This is the gating endpoint for everything downstream:
@zkcoins/sdkcannot exposegetTransactions()without it.cw_zkcoins(Cake Wallet) andzkcoins-wallet.ts(Layerz Wallet) adapters need it forgetCommonTransactions()/fetchTransactions().Proposed shape
address(required): the account address (hex, same encoding as/api/balance).limit(optional, default50, max200).offset(optional, default0).Response
{ "items": [ { "txid": "<hex>", "timestamp": 1717081234, "direction": "send" | "receive" | "mint", "amount": 10000, "counterparty": "<hex>", "status": "pending" | "confirmed" | "failed", "block_height": 871234, "memo": null } ], "total": 137, "limit": 50, "offset": 0 }txidis the on-chain commit txid where this coin movement was inscribed. For pending mints/sends not yet broadcast or not yet confirmed, theproof_idcan be exposed in the same slot (TBD on the canonical identifier — see Open Questions).directionis from the queried address's perspective.counterpartyis optional — sends include the recipient address, receives include the sender. Mints have no counterparty.status:pendinguntil the inscription confirms in a block, thenconfirmed.failedfor the broadcast-rejected case the publisher already surfaces (503 SERVICE_UNAVAILABLEpath).block_heightis set oncestatus === "confirmed".Acceptance criteria
router.rs, behindalwaysstatus (no feature gate).itemswith correcttotal), missingaddress→ 400, invalid hex → 400.node/tests/api_remote.rsadds a/api/historyround-trip on a freshly-minted address.zk-coins/app/src/lib/api/schemas.ts) + typed client method (zk-coins/app/src/lib/api/client.ts) added — keeps the app in sync as the in-tree consumer. App's behaviour stays unchanged until a later PR wires the UI to it.Out of scope
Open questions for the PR author
txidwon't exist until the publisher broadcasts. Options: (a) use theproof_id(server-internal monotonic ID, already exposed via/api/proof/:id); (b) use a syntheticidfield separate fromtxid. Recommendation: separate fields,idalways set,txidonly once broadcast.ORDER BY timestamp DESC) is the wallet-UI default; pin this in the docs./api/balanceaccepts (case sensitivity, length checks). Reuse the existing helper.References
feedback_zkcoins_thin_client— thin-client invariant.project_zkcoins_server_heavy_architecture— server-heavy design rationale.project_zkcoins_api_info_normalization— sibling deferred enhancement, to be picked up alongside the SDK work.