Privacy Layer
Groth16 over BLS12-381, Poseidon, in-circuit u64 range proofs, replay-protected withdrawals.
Primitives
Poseidon hash
| Parameter | Value |
|---|---|
| Curve / field | BLS12-381 scalar field |
| Full rounds | 8 |
| Partial rounds | 57 |
| Alpha | 5 |
| Security level | 128 bits |
| Cost in-circuit | ~500 constraints / hash |
Groth16 zk-SNARK
| Value | |
|---|---|
| Curve | BLS12-381 |
| Proof size | 192 bytes |
| Verify | ~10 ms (single CPU core) |
| Generate | ~2–3 s |
| Setup | trusted (BGM17 phase-2 MPC ceremony) |
| Batch verify | yes — amortises pairings across a batch |
Three transaction types
Deposit, transfer and withdraw give a Sapling-shaped lifecycle: ETH enters the pool publicly, moves privately inside it, and leaves to a recipient address unlinkable to any input.
Commitment
c = Poseidon(value || randomness || owner_pk)
Nullifier
n = Poseidon(c || secret)
A nullifier reveals only that some commitment was spent — not which one. The on-chain contract creates a per-nullifier record on both withdraw and shielded_transfer under a shared namespace, so a note can never be spent twice.
Range proofs
Values are constrained to a u64 range inside the circuit via a lookup argument, preventing inflation from negative or overflowed amounts.
Replay protection
Each withdraw carries an expiration_slot; the on-chain contract rejects any request submitted past its deadline. Combined with single-use nullifier records, this gives a strict at-most-once semantic.