Privacy Rails
Privacy in zkde.fi isn't a checkbox feature — it's a proving pipeline. The system uses a commitment → nullifier → claim hash → relayer → settlement flow that keeps capital movements private while maintaining on-chain verifiability. You can prove you own something, transfer it, and withdraw it, all without revealing who you are or how much you hold.
This page walks through the full flow from deposit to withdrawal, explains the three enforcement tiers, and covers the circuits and contracts that make it work.
The pipeline, step by step
flowchart LR DEP["1. Deposit"] --> CMT["2. Commitment"] CMT --> TREE["3. Merkle tree"] TREE --> POOL["4. Shielded pool"] POOL --> WD["5. Withdraw intent"] WD --> NUL["6. Nullifier"] NUL --> CLAIM["7. Claim hash"] CLAIM --> RELAY["8. Relayer"] RELAY --> SETTLE["9. Settlement"] SETTLE --> RCPT["10. Receipt"]
Here's what happens at each step:
1. Deposit — the user wants to move funds into a private pool. They don't just send tokens — that would be traceable.
2. Commitment generation — instead, they create a commitment: hash(secret, nullifier, amount). The secret is a random value known only to the depositor. The nullifier is a unique identifier that will be revealed later to prevent double-spending. The commitment goes on-chain; the preimage stays private.
3. Merkle tree insertion — the commitment is inserted as a leaf in the pool's Merkle tree. The tree root is publicly readable, but individual leaves are not. Think of it as a set membership structure — you can prove something is in the set without revealing which element.
4. Shielded pool state — the pool now holds the deposited value, and the Merkle tree contains the commitment. At this point, the depositor's address is the only link between them and the funds.
5. Withdrawal intent — some time later, the user (or a different address) wants to withdraw. They need to prove they own a commitment in the tree without revealing which one.
6. Nullifier reveal — the user reveals the nullifier from their commitment. This gets recorded on-chain. If the same nullifier has been used before, the withdrawal is rejected — that's the double-spend prevention. Crucially, the nullifier doesn't reveal the commitment's position in the tree.
7. Claim hash — a binding hash that ties the withdrawal proof to a specific recipient and amount. This ensures the proof can't be replayed for a different recipient.
8. Relayer submission — optionally, a relayer submits the withdrawal transaction on the user's behalf. This breaks the tx-graph link: the withdrawal comes from the relayer's address, not the depositor's. The relayer can't steal funds — they're just a gas-paying intermediary.
9. Settlement — the withdrawal settles on Starknet L2 or via the Madara L3 path.
10. Receipt — an immutable receipt is stored in ReceiptRegistry. The privacy guarantees are preserved — the receipt shows that a valid withdrawal happened, not who made it.
Privacy tiers
Not every operation needs maximum privacy. The system offers three tiers that trade privacy strength for convenience:
| Tier | What's different | Trade-off |
|---|---|---|
| Strict | Full ZK proof, Merkle inclusion, nullifier. User submits their own tx — no relayer. | Maximum control, but the withdrawal tx is linked to the withdrawer's address |
| Standard | Same proofs, but a relayer submits the tx on the user's behalf. Configurable batching delay. | Breaks the depositor ↔ withdrawer link. This is the default tier for most users. |
| Express | Optimistic batch verification with relayer aggregation. | Lower gas costs via batching, but verification is optimistic rather than per-withdrawal |
Most users should use Standard. It gives you the privacy benefit (broken tx-graph link) without the trust trade-off of optimistic verification.
Privacy circuits
Each privacy operation has a dedicated Circom circuit:
| Circuit | What it does | When it fires |
|---|---|---|
FullPrivacyWithdraw | Full withdrawal proof — Merkle membership + nullifier + claim hash | Standard withdrawal from shielded pool |
FullPrivacyWithdrawHashed | Withdrawal with hashed recipient binding | When recipient address needs to be committed without revealing |
FullPrivacyWithdrawWithChange | Partial withdrawal with change output | Withdraw part of a commitment, leave the rest in the pool |
PrivateDeposit | Commitment generation and Merkle insertion proof | Depositing into a shielded pool |
PrivateWithdraw | Simplified privacy withdrawal | Lighter-weight withdrawal for lower-value operations |
PoolMembership | Proves membership in a shielded pool without revealing position | "I'm in this pool" without revealing how much |
Selective disclosure
This is privacy's killer feature for DeFi. You can prove specific properties about your private state without revealing the state itself:
- "I have at least $10K collateral" — the lending protocol knows you're creditworthy without seeing your balance
- "My position has been open for 90+ days" — the insurance contract knows you qualify without seeing your entry date
- "I passed the risk screening" — the vault knows you're eligible without seeing your risk score
This is implemented via the BalanceAboveThreshold and TenureAboveThreshold circuits, verified by the SelectiveDisclosure contract on Starknet Sepolia.
Privacy contracts
| Contract | Address | What it does |
|---|---|---|
| FullPrivacyPoolV2 | 0x03dde...1559 | Main shielded pool — Merkle tree, commitments, withdrawals |
| SelectiveDisclosure | 0x00ab6...0c77 | Verifier for threshold proofs (balance, tenure) |
| ConfidentialTransfer | 0x07fdc...faa4 | Private transfer with proof — move value without revealing amounts |
| FullyShieldedPool | 0x07fed...5ff5 | Fully shielded variant with stronger isolation |
Privacy API endpoints
| Method | Endpoint | What it does |
|---|---|---|
POST | /api/v1/zkdefi/full_privacy/deposit/generate_commitment | Generate commitment for private deposit |
POST | /api/v1/zkdefi/full_privacy/withdraw/generate_proof | Generate Merkle withdrawal proof |
GET | /api/v1/zkdefi/full_privacy/merkle/root | Current Merkle root state |
POST | /api/v1/zkdefi/privacy/deposit | Unified privacy deposit |
POST | /api/v1/zkdefi/privacy/withdraw | Unified privacy withdraw |
GET | /api/v1/zkdefi/compliance/profiles/{user_address} | Compliance profile — disclosure artifacts |
Beyond capital flows
The commitment → nullifier pattern extends to other domains:
Private voting — the private_vote circuit lets users participate in DAO governance without revealing voting power or direction. The proof says "I'm an eligible voter and I cast a valid vote," not "I have 10,000 tokens and I voted yes." Implemented via DAOConstraintManager.
Private lending — credit eligibility proofs via the CreditEligibility circuit prove collateral sufficiency without revealing portfolio composition. A lending protocol can offer you a loan without seeing your positions.
Compliance artifacts — disclosure proofs satisfy compliance requirements without revealing raw private state. You can prove "I meet the criteria" to a regulator without handing over your entire transaction history.
Next: zkML + Circuit Stack · Proof Pipeline · Deployed Contracts