Burner is a Solana-native mixer that breaks on-chain transaction linkability for SPL token transfers.
This project took inspiration from Privacy Cash
Burner is a privacy protocol that allows users to deposit tokens (SOL or SPL tokens) into a shielded pool and withdraw them later without revealing which deposit was theirs. It offers transaction privacy similar to Tornado Cash, but built natively on Solana.
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β User deposits 10 SOL β
β β Receives secret commitment β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Shielded Pool (Merkle Tree) β
β β’ Contains all user commitments β
β β’ No link between deposits and withdrawals β
βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β User proves ownership with ZK proof β
β β Withdraws to new address β
β β Privacy preserved! β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Key Features:
- Zero-knowledge proofs using Noir circuits and Groth16
- Support for SOL and SPL tokens (USDC, USDT, ORE, ZEC, stORE)
- Sparse Merkle tree supporting ~67M transactions (height 26)
- Configurable deposit/withdrawal fees
- On-chain proof verification using BN254 pairing
- Nullifier tracking to prevent double-spending
Zero-knowledge circuits written in Noir that prove transaction validity without revealing inputs:
-
JoinSplit Transaction (
main.nr) - Proves:- Input UTXOs exist in merkle tree
- Balance conservation:
sum(inputs) + public_amount == sum(outputs) - Unique nullifiers generated
- Outputs are correctly formed
-
Merkle Proof (
merkle_proof.nr) - Verifies UTXO membership in tree -
Keypair (
keypair.nr) - Key derivation and signature generation -
Utils (
utils.nr) - Helper functions (conditional swaps, assertions)
Circuit Constants:
LEVELS: u32 = 26 // Merkle tree depth (67M capacity)
N_INS: u32 = 2 // Number of inputs
N_OUTS: u32 = 2 // Number of outputsSolana smart contract that verifies proofs and manages the shielded pool:
- Groth16 Verification (
groth16.rs) - Verifies zero-knowledge proofs using BN254 pairing - Merkle Tree (
merkle_tree.rs) - Manages commitment tree and root history (100 roots) - Main Program (
lib.rs) - Transaction processing, nullifier tracking, token transfers
Key Instructions:
initialize()- Create main SOL merkle treeinitialize_tree_account_for_spl_token()- Create SPL token treestransact()- Process SOL deposits/withdrawals with ZK proofstransact_spl()- Process SPL token deposits/withdrawalsupdate_deposit_limit()- Configure max deposit amountsupdate_global_config()- Set deposit/withdrawal fees
-
Client Side (TypeScript/JavaScript):
- Generates JoinSplit transaction using Noir circuit
- Creates Groth16 proof with snarkjs
- Encrypts transaction details
-
Proof Submission:
- Submits
transact()instruction with:- Proof components (proof_a, proof_b, proof_c)
- Public inputs (root, nullifiers, commitments)
- External data (amounts, fees, recipients)
- Submits
-
On-Chain Verification (Anchor Program):
// Verify ZK proof verify_proof(proof, VERIFYING_KEY)? // Verify merkle root in history MerkleTree::is_known_root(&tree, proof.root)? // Prevent double-spending check_nullifiers_unspent(proof.input_nullifiers)? // Execute token transfers process_transaction(proof.public_amount)? // Update tree with new commitments tree.insert(proof.output_commitments)?
-
Privacy Guarantee:
- Proof reveals ONLY: "I have valid inputs and they balance"
- Proof hides: Which specific inputs, who owns them, amounts
- Merkle tree breaks link between deposits and withdrawals
burner/
βββ circuits/ # Noir zero-knowledge circuits
β βββ src/
β β βββ main.nr # JoinSplit transaction circuit
β β βββ merkle_proof.nr
β β βββ keypair.nr
β β βββ utils.nr
β βββ Nargo.toml # Noir project config
β βββ README.md # Circuit documentation
β
βββ anchor/ # Solana Anchor program
β βββ programs/zkcash/
β β βββ src/
β β βββ lib.rs # Main program (~950 lines)
β β βββ groth16.rs # Proof verification
β β βββ merkle_tree.rs
β β βββ utils.rs
β βββ tests/ # Anchor tests
β βββ package.json
β
βββ scripts/ # Deployment and utility scripts
β βββ initialize_devnet.ts
β βββ package.json
β
βββ artifacts/circuits/ # Compiled circuit artifacts
β βββ transaction2.r1cs
β βββ transaction2.zkey # Proving key (16.4 MB)
β βββ transaction2.wasm
β βββ verifyingkey2.json # Hardcoded in Anchor program
β
βββ README.md # This file
- Rust (latest stable)
- Solana CLI (v1.18+)
- Anchor (v0.31.0)
- Node.js (v18+)
- Noir/Nargo (v0.36.0+)
-
Clone the repository:
git clone <repository-url> cd burner
-
Install Noir:
curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash noirup -
Install Anchor dependencies:
cd anchor npm install -
Install script dependencies:
cd scripts npm install
-
Compile Noir circuits:
cd circuits nargo check # Verify syntax nargo test # Run tests nargo compile # Compile to ACIR
-
Build Anchor program:
cd anchor anchor build
Run circuit tests:
cd circuits
nargo testRun Anchor tests:
cd anchor
# Test SOL transactions
npm run test:sol
# Test SPL transactions
npm run test:spl
# Test with mint authority checks
npm run test:mint-checked-
Configure Solana CLI:
solana config set --url devnet solana-keygen new # Create keypair if needed solana airdrop 2 # Get devnet SOL
-
Deploy program:
cd anchor anchor deploy --provider.cluster devnet -
Initialize program:
cd scripts npx ts-node initialize_devnet.ts
- Hash Function: Poseidon (BN254-optimized)
- Proof System: Groth16
- Curve: BN254 (alt_bn128)
- Tree Structure: Sparse Merkle Tree (height 26)
-
Transaction Privacy:
- ZK proofs hide input/output relationships
- Encrypted transaction details
- No on-chain metadata linking deposits to withdrawals
-
Double-Spend Prevention:
- Nullifiers mark UTXOs as spent
- Nullifier uniqueness enforced on-chain
- Historical root tracking prevents replay attacks
-
Balance Integrity:
- Amount invariant:
sum(inputs) + public_amount == sum(outputs) - Range checks on all amounts (248-bit limit)
- Fee validation and extraction
- Amount invariant:
-
Access Control:
- Ownership proven via ZK signatures
- Private keys never revealed
- Public key derived using Poseidon
- Output amounts are range-checked to 248 bits
- Duplicate nullifiers are rejected
- Merkle proofs only verified for non-zero amounts
- Circuit constants (LEVELS, N_INS, N_OUTS) must be < 16 to prevent overflow
- 100 historical roots maintained for proof flexibility
Each UTXO (Unspent Transaction Output) contains:
{
amount: Field, // Token amount
pubkey: Field, // Owner's public key
blinding: Field, // Random blinding factor
mint_address: Field // SOL or SPL token mint
}Commitment Calculation:
commitment = Poseidon(amount, pubkey, blinding, mint_address)
Nullifier Calculation:
signature = Poseidon(privKey, commitment, merklePath)
nullifier = Poseidon(commitment, merklePath, signature)
- SOL (native Solana)
- USDC (USD Coin)
- USDT (Tether)
- ORE (Ore token)
- ZEC (Zcash wrapped)
- stORE (Staked ORE)
Each token type has its own merkle tree for isolation and scalability.
Configurable fees (in basis points, 1 bp = 0.01%):
- Deposit Fee: Charged on deposits
- Withdrawal Fee: Charged on withdrawals
- Fee Recipient: Configurable admin account
Fees are extracted during transaction processing and transferred to the fee recipient.
- Noir version: β₯0.36.0
- Poseidon library: v0.1.1 (noir-lang/poseidon)
- Field: BN254 curve
- Anchor: 0.31.0
- Solana: Compatible with 1.18+
- Dependencies:
light-poseidon: 0.4.0ark-bn254: 0.5.0solana-bn254: 2.2.2
This project was migrated from Circom to Noir for better developer experience while maintaining full compatibility:
- Same Poseidon hash function (BN254)
- Identical UTXO structure and commitment scheme
- Preserved all security checks and constraints
- Compatible proof outputs
See circuits/README.md for detailed migration notes.
cd anchor
npm run lint # Check formatting
npm run lint:fix # Auto-format- Unit Tests - Test individual components (circuits, merkle tree, utils)
- Integration Tests - Test full transaction flows
- Feature Flags - Test different network configurations:
localnet- Local developmentlocalnet-mint-checked- With mint authority checksdevnet- Devnet deployment
Burner is licensed under the Business Source License 1.1 (BSL), with automatic transition to GPL v2.0 or later on 12/27/2027.
- View and audit source code
- Modify for research or non-commercial purposes
- Use internally within your organization
- Build on top without offering competing product
- Offer as hosted/paid service competing with Burner
- Embed into competing commercial product
- Monetize fork that overlaps with Burner's offerings
After 2 years from release, the code becomes GPL v2.0 or later permanently.
See LICENSE.md for full details.
Contributions are welcome! Please ensure:
- All tests pass (
nargo testandanchor test) - Code is formatted (
npm run lint:fix) - Security properties are preserved
- Changes are documented
- Inspired by Tornado Cash and other privacy protocols
- Uses Noir language by Aztec Labs
- Built with Anchor framework for Solana
- Groth16 implementation based on arkworks
This software is provided "AS IS" without warranties. Use at your own risk. See LICENSE.md for full disclaimer.