A privacy-preserving cryptocurrency mixer using zero-knowledge proofs on the Mantle Network.
- Node.js (v16+)
- npm or yarn
- Circom compiler
npm install
# Install Circom globally
npm install -g circomnpm run buildThis generates:
build/withdraw.wasm- WebAssembly for proof generationbuild/withdraw.r1cs- R1CS constraint system
npm run setupThis:
- Downloads Powers of Tau ceremony file
- Generates proving key (
withdraw_final.zkey) - Generates verification key (
verification_key.json) - Creates Solidity verifier (
contracts/Verifier.sol)
npm run generate-proofCreates a zkSNARK proof in build/proof.json
npm run verify-proofVerifies the generated proof off-chain
// Deploy MantleMask
MantleMask mixer = new MantleMask(1 ether, 20);const { generateCommitment } = require('./scripts/generateProof');
const nullifier = "123456789";
const secret = "987654321";
const { commitment } = generateCommitment(nullifier, secret);
// Deposit on-chain
await mixer.deposit(commitment, { value: ethers.utils.parseEther("1") });const { generateProof } = require('./scripts/generateProof');
const proof = await generateProof(
nullifier,
secret,
recipientAddress
);// Use the generated proof to withdraw
await mixer.withdraw(
proof.proof, // zkSNARK proof
proof.publicSignals[0], // Merkle root
proof.nullifierHash, // Nullifier hash
recipientAddress // Recipient
);βββ circuits/
β βββ withdraw.circom # Main withdrawal circuit
β βββ merkleTree.circom # Merkle tree verification
βββ contracts/
β βββ MantleMask.sol # Main privacy mixer contract
β βββ Verifier.sol # Generated zkSNARK verifier
βββ scripts/
β βββ setup.js # Trusted setup generation
β βββ generateProof.js # Proof generation
β βββ verifyProof.js # Proof verification
βββ build/ # Generated files
βββ withdraw.wasm
βββ withdraw.r1cs
βββ withdraw_final.zkey
βββ verification_key.json
βββ proof.json
- Input Validation: Verifies knowledge of nullifier and secret
- Commitment Generation:
commitment = Poseidon(nullifier, secret) - Nullifier Hash:
nullifierHash = Poseidon(nullifier) - Merkle Proof: Proves commitment is in the deposit tree
- Output: Generates zkSNARK proof without revealing secrets
- β Anonymity: No link between deposit and withdrawal addresses
- β Untraceability: Nullifier and secret never revealed
- β Double-spend Protection: Nullifier hash prevents reuse
- β Merkle Tree Inclusion: Proves legitimate deposit
Edit circuits/withdraw.circom and recompile:
npm run build
npm run setup# Generate and verify a proof
npm run generate-proof
npm run verify-proof- Use a proper Powers of Tau ceremony
- Implement ceremony verification
- Add circuit auditing
- Use hardware for key generation
- Total Constraints: ~2,500 (for 20-level Merkle tree)
- Public Inputs: 3 (root, nullifierHash, recipient)
- Private Inputs: 42 (nullifier, secret, pathElements[20], pathIndices[20])
// Complete workflow example
const MantleMask = require('./scripts/generateProof');
async function privateTransfer() {
// 1. Generate secrets
const nullifier = Math.random().toString();
const secret = Math.random().toString();
// 2. Create commitment and deposit
const { commitment } = MantleMask.generateCommitment(nullifier, secret);
await mixer.deposit(commitment, { value: ethers.utils.parseEther("1") });
// 3. Generate proof and withdraw
const proof = await MantleMask.generateProof(nullifier, secret, recipient);
await mixer.withdraw(proof.proof, proof.publicSignals[0], proof.nullifierHash, recipient);
console.log("β
Private transfer complete!");
}MIT License