Overview
Privacy Cash enables private transactions on Solana by breaking the on-chain link between deposits and withdrawals. Users deposit SOL or SPL tokens into a shared pool, creating commitments that are stored in a Merkle tree. When withdrawing, they prove ownership of a commitment using zero-knowledge proofs without revealing which specific commitment they own.Transaction Flow
Deposit (Shielding)
Users deposit funds into the privacy pool, generating a secret commitment that represents their deposit.When you deposit:
- Generate a random private key and blinding factor
- Create a UTXO (Unspent Transaction Output) containing:
- Amount
- Public key (derived from private key)
- Blinding factor
- Mint address (token type)
- Compute commitment:
hash(amount, publicKey, blinding, mintAddress) - Submit transaction with the commitment
- Commitment is added to the on-chain Merkle tree
Privacy Pool Storage
All commitments are stored in a Merkle tree on-chain, creating a growing anonymity set.The Merkle tree:
- Height: 26 levels
- Maximum capacity: 67,108,864 leaves (2^26)
- Root history: 100 most recent roots
- Hash function: Poseidon (ZK-friendly)
Withdrawal (Unshielding)
Users withdraw funds by proving they own a commitment in the tree without revealing which one.When you withdraw:
- Select input commitments to spend
- Generate a Merkle proof showing the commitment exists in the tree
- Create a nullifier to prevent double-spending
- Generate output commitments for change or recipients
- Create a zero-knowledge proof that proves:
- You know the private key for the input commitment
- The commitment exists in the Merkle tree
- The nullifier is correctly computed
- Input amounts equal output amounts (plus fees)
- Submit the proof and nullifiers to the program
Nullifier Check
The program verifies the proof and checks that nullifiers haven’t been used before.Verification steps:
- Verify the Merkle root exists in root history (confirms commitment was in tree at some point)
- Check that nullifiers haven’t been used (prevents double-spending)
- Verify the zero-knowledge proof using Groth16
- Verify the external data hash matches (recipient, amount, fees)
- Execute the transfer if all checks pass
Universal JoinSplit Transactions
Privacy Cash implements universal JoinSplit transactions, supporting flexible transaction types:Deposits
Shield funds by creating new commitments
- Transfer SOL/tokens from wallet to privacy pool
- Generate 2 output commitments (for amount splitting)
- External amount is positive
- Subject to deposit limits (configurable per token)
Withdrawals
Unshield funds by spending commitments
- Transfer from privacy pool to any recipient
- Consume up to 2 input commitments
- External amount is negative
- No withdrawal limits
Transfers
Internal transfers between commitments
- Spend old commitments, create new ones
- External amount is zero
- Stay within the privacy pool
- Refresh commitments for enhanced privacy
Splitting/Merging
Reorganize amounts within the pool
- Split large commitment into smaller ones
- Merge multiple commitments into one
- Useful for making exact-amount withdrawals
UTXO Model
Privacy Cash uses a UTXO (Unspent Transaction Output) model similar to Bitcoin, adapted for zero-knowledge proofs:UTXO Structure
- Consumes up to 2 input UTXOs (must be in Merkle tree)
- Creates exactly 2 output UTXOs (added to Merkle tree)
- Input amounts must equal output amounts (accounting for external transfers and fees)
The circuit enforces the amount invariant:
sum(inputs) + publicAmount = sum(outputs)Where publicAmount = extAmount - feeMulti-Token Support
Privacy Cash maintains separate Merkle trees for different token types:- SOL
- SPL Tokens
Native SOL uses a dedicated Merkle tree with PDA seeds
["merkle_tree"].- Mint address:
11111111111111111111111111111112 - Default deposit limit: 1,000 SOL
- Deposit fee: 0% (free)
- Withdrawal fee: 0.25%
Fee Structure
Privacy Cash uses a transparent fee model:| Operation | Fee Rate | Adjustable |
|---|---|---|
| Deposits | 0% (free) | ✅ Yes |
| Withdrawals | 0.25% (25 basis points) | ✅ Yes |
| Internal transfers | 0% | - |
Fees include a 5% error margin (configurable). Relayers can charge slightly more to cover gas costs and slippage.
expected_fee = amount × fee_rate / 10000(for deposits/withdrawals)fee_error_margin = 500(5% tolerance)
Security Properties
Transaction Privacy
Transaction Privacy
Deposits and withdrawals cannot be linked unless:
- The anonymity set is too small (< 10 users)
- Timing correlation is possible (only user in time window)
- Amount correlation exists (unique amount)
- Wait between deposit and withdrawal
- Use common amounts (0.1, 1, 10 SOL)
- Split large amounts across multiple commitments
Double-Spend Prevention
Double-Spend Prevention
Nullifiers prevent the same commitment from being spent twice.The program:
- Creates nullifier account on first spend (PDA:
["nullifier0", nullifier]) - Transaction fails if nullifier account already exists
- Checks both nullifiers in 2-input transactions
- Prevents nullifier collisions between input slots
Amount Integrity
Amount Integrity
The zero-knowledge circuit enforces that:
- Input amounts equal output amounts (conservation of value)
- Output amounts fit in 248 bits (prevents overflow)
- Public amount correctly accounts for external transfers and fees
- All commitments are computed correctly
- External amounts don’t exceed deposit limits
- Fees meet minimum requirements
- Pool has sufficient balance for withdrawals
Proof Soundness
Proof Soundness
Groth16 proofs ensure computational integrity:
- Prover cannot forge proofs for invalid statements
- Proof verification is deterministic
- Verifying key is hardcoded in program (no substitution attacks)
- Public inputs are bound to the proof
- Accretion
- HashCloak
- Zigtur
- Kriko
Implementation Reference
Key files in the source code:Transaction Circuit
circuits/transaction.circom:22Universal JoinSplit circuit with 2 inputs and 2 outputs
Transact Instruction
lib.rs:213Main instruction for SOL deposits and withdrawals
Merkle Tree
merkle_tree.rsOn-chain Merkle tree implementation
Proof Verification
utils.rs:214Groth16 proof verification logic
Next Steps
Privacy Model
Learn about the privacy guarantees and threat model
Zero-Knowledge Proofs
Understand the cryptographic proofs that power Privacy Cash
Commitments & Nullifiers
Deep dive into the core cryptographic primitives
Quick Start
Start building with Privacy Cash SDK