Skip to Content
DocumentBuildClaim a payout
Build

Convert a pool commitment into an ERC-20 balance.

When your order matches (or a payment / drop is sent to you), the payout lands in CommitmentPool as a fresh commitment addressed to your wallet. Claim turns that commitment into a real on-chain transfer.

Claims can be locked (have a releaseTime in the future) or unlocked (releaseTime = 0). Both use the same claim circuit.

Discover claimable commitments

Two paths:

A) Through the relayer’s order endpoint — settled orders carry their claim distribution in the RelayerOrder.claims field. Walk the maker’s order history (or the recipient-side view your relayer exposes) and flatten the claim arrays. Pair each entry with the local secret you generated in Place an order — relayers do not see secrets.

const orders = await client.getOrderHistory(account); const fromOrders = orders.orders.flatMap((o) => o.claims ?? []); // Each entry: { claimHash, amount, releaseDelay } as the relayer sees it. // Reconcile with your local pending list (keyed by claimHash) to attach // the secret, ephemeralPubKey, and leafIndex you persisted at order time.

B) Self-scan — replay CommitmentInserted events and identify the commitments your wallet owns by matching recipient against the addresses you control.

Either path, the local record you feed into generateClaimProof looks like:

// [{ commitment, leafIndex, secret, token, amount, releaseTime, recipient }]

secret is the random field element you generated for this claim entry at order-placement time and persisted alongside the order — the proof cannot be built without it.

Generate the claim proof

import { generateClaimProof } from "@zkscatter/sdk/zk"; const claimResult = await generateClaimProof( { secret: claim.secret, leafIndex: claim.leafIndex, merkleProof, // The claim circuit hashes recipient + token as field elements, // so both must be bigint (their 0x address strings widened to // uint256). `account` is the 0x-prefixed connecting wallet. recipient: BigInt(account), token: BigInt(claim.token), amount: claim.amount, releaseTime: claim.releaseTime, }, { wasm: "/zk/claim.wasm", zkey: "/zk/claim_final.zkey" }, );

Submit on-chain

import { callClaimWithProof } from "@zkscatter/sdk/contracts"; const tx = await callClaimWithProof( signer, network.contracts.privateSettlement, claimResult.proof, { recipient: account, token: claim.token, amount: claim.amount, releaseTime: claim.releaseTime, }, ); await tx.wait();

Batch claims

If a user has multiple unlocked claims, batch them:

import { callClaimWithProofBatch, MAX_CLAIM_BATCH_SIZE, // 20 } from "@zkscatter/sdk/contracts"; const items = claims.slice(0, MAX_CLAIM_BATCH_SIZE).map((c) => ({ proof: c.proof, inputs: { recipient: account, token: c.token, amount: c.amount, releaseTime: c.releaseTime }, })); const tx = await callClaimWithProofBatch( signer, network.contracts.privateSettlement, items, );

Atomic — any single revert rolls the whole batch back. Cap at 20 to keep gas in the typical block limit.

Locked claims

A locked claim has releaseTime > 0. The proof is generatable immediately — but claimWithProof reverts until block.timestamp >= releaseTime. UI pattern: show “Claim available in 12h” and disable the button until the deadline.

Common errors

`Claim: nullifier reused`

Already claimed. Remove the entry from your local pending list.

`Claim: not yet releasable`

block.timestamp < releaseTime. Re-render with the unlock countdown.

Last updated on