ZeroDocumentation
Browse documentation
How it works

Hidden amounts

Moving between Private and Shielded without a number ever touching the chain.

The problem

Private balances live on ristretto255, encrypted by Token-2022. Shielded notes live inside a Groth16 proof on BN254. Two curves, two proof systems, and no native way to show that a number on one equals a number on the other. Shielded pools usually reveal the amount at the door. Zero doesn’t.

1. Keep the amount inside the proof

The hidden-amount circuit takes the amount a and a random blinding ρ as inputs that are never posted. The program verifies the Groth16 proof against a single point in their place:

P = a·IC₂ + ρ·IC₃        built from the verifying key's own points

P is a commitment to the amount on BN254, and the Groth16 proof holds for it without anyone learning a.

2. Prove it matches the transfer

A confidential transfer commits to its amount on ristretto255. A cross-curve equality proof shows that commitment and P hide the same integer. Zero implements Πdleq from Beyond the circuit (Orrù, Kadianakis, Maller and Zaverucha, IACR Communications in Cryptology, 2025): one 104-byte proof with a 128-bit challenge, checked on-chain in about 24,000 compute units.

C_transfer = x·G + r·H     on ristretto255   (range-proven by Token-2022)
P          = x·g + ρ·h     on BN254
             same x, proven without revealing it

The same proof also shows knowledge of P’s opening, which is exactly what makes a Groth16 proof with a committed input sound.

3. Open it with the pool's key

Token-2022 never exposes a transfer’s blinding r. Zero doesn’t need it. The pool’s ElGamal key is PK = s⁻¹·H, and the transfer carries a decrypt handle D = r·PK for it, so:

s·D = r·H     ⇒     C = x·G + s·D

The same commitment, opened by the pool’s own key through its handle. It stays binding because nobody knows the discrete log of H.

4. Credit exactly once

A hidden deposit is two instructions in one transaction: your confidential transfer to the pool, then the pool’s finalize. The program reads the instruction right before its own through the instructions sysvar and checks it is exactly that transfer, from your account, into the pool, with the proof account it checked when the deposit was staged. Then it records the notes’ nullifiers. The transfer and the credit land together or not at all.

The whole flow

  1. 01
    Prove

    Your browser builds the private transfer to the pool and the hidden-amount Groth16 proof.

  2. 02
    Stage

    The relayer verifies the transfer proofs into accounts, checks everything independently, and the pool program verifies both proofs and records the deposit.

  3. 03
    Sign once

    Your wallet signs one transaction under 1 KB: the transfer and the finalize.

  4. 04
    Done

    Your notes are in the tree. The chain saw a private transfer and a proof, and no amount.

Withdrawing to Private runs the same bridge in reverse: the pool program checks your proof and the bridge proof, then signs the confidential transfer out itself.

Two independent locks

LockWhat it checks
On-chainThe Groth16 proof with the committed amount, and the cross-curve proof tying it to the transfer.
The relayerThe Groth16 proof again, with the amount as an ordinary input, and the transfer decrypted with the pool key and compared.
Search the Zero documentationLocal search · no external service