Privacy Pool
A ZK-UTXO privacy pool — deposit any amount, withdraw with a FFLONK proof. Split withdrawals prevent amount fingerprinting. No fixed denominations required.
The Fan-In Problem
Stealth transfers give every payment a unique, unlinkable address. But once you claim those payments, all roads lead to your real wallet. An observer watching the claim address sees 10 inbound transactions from 10 different stealth addresses — and immediately knows those wallets belong to you.
DustPool V2 solves this with a ZK-UTXO model. You deposit any amount — ETH or ERC-20 tokens — into a shared pool. Each deposit creates a UTXO-style note with a Poseidon commitment. To withdraw, you generate a FFLONK proof (no trusted setup) that proves you own valid notes without revealing which ones. The 2-in-2-out circuit consumes input notes and creates change notes, just like Bitcoin's UTXO model but with full privacy.
How the Privacy Pool Works
- 01
Deposit and create a UTXO note
Your browser generates a random blinding factor and computes a Poseidon commitment:C = Poseidon(ownerPubKey, amount, asset, chainId, blinding). CallDustPoolV2.deposit(commitment)ordepositERC20(token, amount, commitment). The commitment is added to the relayer's off-chain Merkle tree. Your note (amount, blinding, asset, commitment) is encrypted with AES-256-GCM and stored in IndexedDB — not plaintext localStorage. - 02
Notes accumulate (UTXO model)
Each deposit creates an independent note. Unlike V1's fixed-amount mixer, you can deposit any amount at any time. Notes are like Bitcoin UTXOs — they have a specific value and are consumed whole when spent. The “change” from a partial withdrawal becomes a new output note. - 03
Generate a FFLONK proof (in-browser)
The browser runs the 2-in-2-out transaction circuit (~12,400 constraints) to produce a FFLONK proof. Two input notes are consumed, two output notes are created (one for the withdrawal amount, one for change). Public signals:merkleRoot, null0, null1, outC0, outC1, pubAmount, pubAsset, recipient, chainId. The FFLONK system requires no trusted setup ceremony. - 04
Submit to relayer for on-chain verification
The proof is sent to the relayer (same-origin Next.js API at/api/v2/withdraw). The relayer screens the recipient against the Chainalysis sanctions oracle, then submits toDustPoolV2.withdraw(). The contract verifies the FFLONK proof, checks nullifier freshness, validates chainId binding, confirms solvency, marks nullifiers spent, and transfers funds. - 05
Split withdrawals for denomination privacy (optional)
To prevent amount fingerprinting, use the 2-in-8-out split circuit (~32,074 constraints). The denomination engine automatically breaks your withdrawal into common ETH chunks (10, 5, 3, 2, 1, 0.5, 0.3, etc.). Each chunk is submitted as a separate transaction with randomized timing — an observer sees only standard-looking amounts with no pattern.
Circuit Details
| Property | Value |
|---|---|
| Proof system | FFLONK (no trusted setup, BN254 curve) |
| Hash function | Poseidon (ZK-friendly, ~5 constraints per hash) |
| Transaction circuit | 2-in-2-out, ~12,400 constraints, 9 public signals |
| Split circuit | 2-in-8-out, ~32,074 constraints, 15 public signals |
| Merkle tree depth | 20 (2²⁰ ≈ 1,048,576 leaves) |
| Merkle tree location | Off-chain, relayer-maintained (verified via root history) |
| Proving environment | In-browser via snarkjs + WASM |
| Proof generation time | ~2–3 seconds (transaction), ~4–5 seconds (split) |
| Gas for verification | ~220,000 gas (FFLONK, 22% cheaper than Groth16) |
| Double-spend prevention | Nullifier = Poseidon(nullifierKey, leafIndex), stored on-chain |
| Commitment structure | Poseidon(ownerPubKey, amount, asset, chainId, blinding) |
| Note encryption | AES-256-GCM via Web Crypto API, key = SHA-256(spendingKey) |
Anonymity Set
The anonymity set is the number of deposits in the Merkle tree at the time of withdrawal. A larger set means a withdrawal could correspond to any of more possible deposits, reducing the probability of correct guessing.
Best Practice
Root History
Security Notes
Notes are encrypted in IndexedDB. V2 encrypts deposit notes with AES-256-GCM (key derived from your spending key). Even if someone accesses your browser storage, they cannot read note data without your stealth keys. Export and back up notes from the Settings page.
Compliance screening is built-in. The Chainalysis oracle screens every depositor address. A 1-hour cooldown after deposit restricts withdrawals to the original depositor's address — giving compliance systems time to flag suspicious activity.
Denomination privacy via split withdrawals. Instead of fixed denominations, the split circuit breaks withdrawals into common amounts automatically. This prevents amount-based correlation while supporting arbitrary deposit sizes.
Chain ID binding prevents cross-chain replay. Every proof includes the chain ID as a public signal. A proof generated for Ethereum Sepolia cannot be replayed on Thanos Sepolia or any other chain.