A deposit creates one bundle commitment containing up to two spendable child notes. The pool stores the bundle commitment once. Later, each child can be withdrawn once without creating another commitment.

One complete flow

Bundle commitment

The client chooses a supported template before deposit. That template fixes the parent amount and two child slots. Their amounts add up to the deposited amount; a zero second slot represents a standalone note. The pool sees one bundle commitment, the deposit amount, timing, sender, and encrypted payload. Child secrets remain in the recovery envelope.

Accepted root

Each inserted bundle commitment changes the Merkle root. A withdrawal witness proves two things together: the selected child belongs to the bundle, and the bundle commitment belongs to an accepted tree root. The private Merkle path and child secret stay in the client. The accepted root is public.

Nullifier and sibling

Each active child has its own nullifier. A successful withdrawal records it and blocks a second spend of that child. The sibling was already committed at deposit and remains usable until its own nullifier is recorded.

Public statement

The withdrawal proof fixes nine values in order:
  1. Accepted root
  2. Child nullifier
  3. Public destination
  4. Gross child amount
  5. Fee
  6. Chain ID
  7. Verifying pool
  8. Proof context hash
  9. Encrypted payload hash
See Withdrawal proof inputs for exact integration rules.

Scope of proof acceptance

An accepted proof shows that a private witness satisfies the selected circuit and public statement. Anonymity, setup integrity, source-to-circuit equivalence, frontend safety, wallet safety, RPC honesty, and deployment operations each rely on separate evidence. Read Published status for the evidence boundary.