Skip to content

Zero-Knowledge Circuits

Withdraw Circuit

The withdraw circuit is written in Circom and compiled to a Groth16 ZK-SNARK.

Private Inputs

  • secret — random value known only to the depositor
  • nullifier — unique identifier to prevent double-spending
  • amount — deposit amount
  • pathElements[20] — Merkle proof siblings
  • pathIndices[20] — Merkle proof path (left/right)

Public Inputs

  1. root — Merkle tree root
  2. nullifierHash — Poseidon(nullifier)
  3. recipient — withdrawal recipient address
  4. token — token address (0x0 for ETH)
  5. withdrawAmount — amount to withdraw
  6. relayer — relayer address
  7. fee — relayer fee
  8. newCommitment — commitment for change (partial withdrawal)
  9. changeAmount — amount returned to pool

What the Circuit Proves

  1. The prover knows secret and nullifier such that commitment = Poseidon(secret, nullifier)
  2. A leaf Poseidon(commitment, token, amount) exists in the Merkle tree at the given root
  3. nullifierHash = Poseidon(nullifier) (for double-spend prevention)
  4. withdrawAmount + changeAmount + fee = amount (conservation)
  5. If changeAmount > 0, then newCommitment ≠ 0 (change must have a commitment)

Hash Function

All hashes use Poseidon — an arithmetic-friendly hash function optimized for ZK circuits. It's significantly cheaper in terms of constraints compared to SHA-256 or Keccak.

Open source privacy technology