Skip to content

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:

TierWhat's differentTrade-off
StrictFull 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
StandardSame 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.
ExpressOptimistic 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:

CircuitWhat it doesWhen it fires
FullPrivacyWithdrawFull withdrawal proof — Merkle membership + nullifier + claim hashStandard withdrawal from shielded pool
FullPrivacyWithdrawHashedWithdrawal with hashed recipient bindingWhen recipient address needs to be committed without revealing
FullPrivacyWithdrawWithChangePartial withdrawal with change outputWithdraw part of a commitment, leave the rest in the pool
PrivateDepositCommitment generation and Merkle insertion proofDepositing into a shielded pool
PrivateWithdrawSimplified privacy withdrawalLighter-weight withdrawal for lower-value operations
PoolMembershipProves 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

ContractAddressWhat it does
FullPrivacyPoolV20x03dde...1559Main shielded pool — Merkle tree, commitments, withdrawals
SelectiveDisclosure0x00ab6...0c77Verifier for threshold proofs (balance, tenure)
ConfidentialTransfer0x07fdc...faa4Private transfer with proof — move value without revealing amounts
FullyShieldedPool0x07fed...5ff5Fully shielded variant with stronger isolation

Privacy API endpoints

MethodEndpointWhat it does
POST/api/v1/zkdefi/full_privacy/deposit/generate_commitmentGenerate commitment for private deposit
POST/api/v1/zkdefi/full_privacy/withdraw/generate_proofGenerate Merkle withdrawal proof
GET/api/v1/zkdefi/full_privacy/merkle/rootCurrent Merkle root state
POST/api/v1/zkdefi/privacy/depositUnified privacy deposit
POST/api/v1/zkdefi/privacy/withdrawUnified 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

Built by Obsqra Labs