The fixed-denomination pool
The pool unlinks the payout from your specific deposit. You deposit a fixed denomination into a shared on-chain pool and later withdraw to a fresh address with a zero-knowledge proof that you own a deposit, without revealing which one. The denomination tier is public; the withdrawal has no on-chain link to the deposit.
Commitments, not balances
A deposit does not store your name or a balance. It inserts a single commitment into an on-chain Merkle tree. The commitment is a Poseidon hash that binds the value, a label, and a secret you keep:
precommit = Poseidon(nullifier, secret)
leaf = Poseidon(value, label, precommit)The nullifier and secret are random values generated in your browser at deposit time. You keep them. They are the only way to withdraw later, so Jat shows them once and asks you to save them.
The Merkle tree lives on-chain
Jat keeps an incremental Merkle tree of depth 20 inside the program, hashed with Poseidon over the BN254 field. Each deposit appends a leaf and updates the root using the Solana Poseidon syscall, so the on-chain root is authoritative. There is no operator who can edit the set. The tree depth bounds the pool to a large but finite number of leaves.
Withdrawing with a proof
To withdraw, your browser builds a Groth16 proof that says, in effect, "I know a nullifier and secret whose commitment is a leaf in this tree, the value is the denomination I am withdrawing, and it pays out to this exact recipient," while revealing none of the private parts. The proof is verified inside the Solana program using the alt_bn128 syscalls, and the program pays the amount out of its vault by signing with a program derived address.
public inputs : merkleRoot, nullifierHash, value, recipientHash
private inputs: nullifier, secret, label, Merkle path
checks : membership in the tree + value == denomination
+ recipientHash == Poseidon(recipient) + single useThe recipient is bound inside the proof, so a relayer that submits it on your behalf cannot redirect the funds. Spending is gated by a nullifier, covered in nullifiers and double-spend.
The Merkle path is rebuilt in your browser
A proof needs the path from your leaf to the root. The indexer publishes only the public list of deposit leaves, never anything tied to your keys. Your browser rebuilds the tree from that list with the same Poseidon hashing the program uses, finds your leaf by its precommit, and extracts the path locally. The reconstructed root matches the on-chain root byte for byte.
Fixed denominations
Deposits and withdrawals use a small set of fixed denominations rather than arbitrary amounts. Equal-sized notes are what makes one deposit indistinguishable from another of the same size, which is the whole point of the anonymity set. The exact set is on the denominations page.