Docs / Circuit
Inside Ordeo
One circuit proves every spend
Every private send, unshield and sale uses the same Noir circuit: up to two notes in, exactly two notes out. This page lists its inputs, what it proves and what the contract checks around it.
On this page
Inputs
Ten public inputs, in the order the pool passes them to the verifier:
| # | Input | Meaning |
|---|---|---|
| 0 | root | A tree root the pool knows |
| 1 | asset | 0 for ETH, 1 for the token; all four notes share it |
| 2-3 | nullifier0, nullifier1 | Nullifier for each input |
| 4-5 | commitment0, commitment1 | Commitment for each output |
| 6 | exitValue | Note units leaving the pool |
| 7 | transferFee | Note units paid as the private transfer fee |
| 8 | feeBps | The pool's transfer fee rate, supplied by the contract |
| 9 | extDataHash | External data hash (see below) |
Private inputs: the spending key sk; for each input its value, blinding, leaf index and 24-node Merkle path; for each output its value, owner key and blinding.
What the proof shows
- Each input with a non-zero value is a leaf of the tree under
root. The leaf index is range-checked to 24 bits. - The prover knows
sk, andPoseidon(sk)is the owner key inside both inputs. - Both nullifiers are computed correctly from commitment, leaf index and
sk, and they differ. - Both output commitments match their value, owner key and blinding.
- Value is conserved:
in0 + in1 = out0 + out1 + exitValue + transferFee. - Every value fits in 120 bits, so no sum can wrap around the field.
feeBpsfits in 14 bits. transferFee × 10000 ≥ feeBps × sent, wheresentis the value of outputs going to a key other than the prover’s. Change back to yourself pays no transfer fee.
Checks the pool does outside the circuit
- The root appears among the 100 most recent roots (
UnknownRoototherwise). - Neither nullifier is spent yet, and the two differ (
NullifierSpent,SameNullifier). - Nullifiers and commitments are below the field order; exit value and fee are at most 2^120 - 1; asset is 0 or 1.
- Each encrypted output is at most 512 bytes.
feeBpsis the pool’s own immutable, andextDataHashis computed by the pool from the call data. The prover cannot choose either.- Relayer rules: a spend that pays a relayer must be sent by that relayer (
NotRelayer); a private send must have an empty recipient; the exit must cover the fees (ExitTooSmall).
Binding the external data
extDataHash = keccak256(abi.encode(chainId, poolAddress,
(recipient, relayer, relayerFee, encryptedOutput0, encryptedOutput1))) mod pBecause the proof takes this hash as a public input, it attests to everything the hash covers. Alter the recipient, the bundler, its fee, either ciphertext, the chain or the pool, and the pool computes a different hash and the proof fails. That is why a bundler can only submit a proof as built, and why sale terms placed in the second ciphertext cannot be edited.
Sizes
| Item | Value |
|---|---|
| Tree depth | 24 (16,777,216 leaves) |
| Inputs / outputs per proof | 2 / 2 |
| Circuit size | 2^16 gates (65,536) |
| Public input words the verifier reads | 18: the 10 inputs above plus 8 words of pairing points |
| Note value bound | 120 bits |
| Ciphertext per output | 114 bytes (the pool allows up to 512) |
| Measured gas | Private send about 4.4M, unshield about 4.5M, router sale about 3.7M |
Proof system and toolchain
- Circuit language: Noir 1.0.0-beta.22, with the Noir Poseidon library (BN254, circomlib constants).
- Prover and verifier generator: Barretenberg bb 5.0.0-nightly.20260522, UltraHonk using a keccak transcript, with zero-knowledge enabled.
- No setup ceremony specific to Ordeo. UltraHonk uses the public Aztec Ignition SRS. The site serves the first 2^17 points itself, because the prover loads points in blocks of that size; the circuit needs 2^16.
circuits/build.sh --checkrebuilds the circuit and confirms that the committedOrdeoAttestor.soland the browser circuit file equal a fresh build.
Where proving happens
In your browser, in a dedicated web worker. The worker solves the witness with noir_js and proves with Barretenberg compiled to WebAssembly. The circuit, the WebAssembly files and the SRS points all come from this site. Nothing about your notes leaves the worker except the finished proof. On a laptop, a proof needs a few seconds.