Live on Mainnet
Technical Paper|v1.0

LaundryProtocol

A Non-Custodial Privacy Protocol for Ethereum Using Zero-Knowledge Proofs and Homomorphic Commitments

Abstract

Laundry Protocol is a non-custodial privacy solution for Ethereum that enables users to break the on-chain link between deposit and withdrawal addresses. By leveraging zero-knowledge proofs (ZK-SNARKs), Pedersen commitments, and incremental Merkle trees, the protocol ensures that withdrawals cannot be traced back to specific deposits while maintaining full verifiability and trustlessness.

The protocol implements a 0.3% fee mechanism to sustain development and security audits, with all fees transparently collected on-chain. Cross-chain functionality via Hash Time-Locked Contracts (HTLCs) enables privacy-preserving transfers between Ethereum and Layer 2 networks.

1. Problem Statement

Ethereum's transparent ledger creates significant privacy challenges. Every transaction is publicly visible, allowing:

  • Complete transaction history analysis of any address
  • Correlation of real-world identities with blockchain addresses
  • Front-running and MEV extraction based on pending transactions
  • Targeted attacks on high-value addresses

While pseudonymous, Ethereum addresses can be de-anonymized through exchange KYC data, social engineering, or advanced blockchain analysis. Laundry Protocol addresses this by creating cryptographic unlinkability between transaction inputs and outputs.

2. Technical Architecture

2.1 Pedersen Commitments

Deposits are recorded as Pedersen commitments of the form:

C = g^v · h^r (mod p)

Where v is the deposit value, r is a random blinding factor, and g, h are generator points on the alt_bn128 elliptic curve. This commitment scheme is:

  • Hiding: The commitment reveals nothing about v without r
  • Binding: A commitment cannot be opened to two different values
  • Homomorphic: C(v₁) · C(v₂) = C(v₁ + v₂) enabling private transfers

2.2 Incremental Merkle Tree

Commitments are stored in a depth-32 incremental Merkle tree, supporting up to 2³² deposits. The tree uses Poseidon hash function optimized for ZK circuits:

root = H(H(H(leaf₀, leaf₁), H(leaf₂, leaf₃)), ...)

Tree Properties:
- Depth: 32 levels
- Capacity: 4,294,967,296 leaves
- Hash: Poseidon (ZK-optimized)
- Insertion: O(log n) on-chain

2.3 Nullifier Scheme

To prevent double-spending while maintaining privacy, each deposit generates a unique nullifier:

nullifier = H(secret, leafIndex, commitment)

The nullifier is revealed during withdrawal and stored on-chain. If the same nullifier is submitted twice, the transaction reverts, preventing double-spending without revealing which deposit was spent.

3. Zero-Knowledge Proof System

The protocol uses Noir circuits compiled to UltraPlonk proofs via the Barretenberg backend. This provides:

  • ~280k gas verification cost on-chain
  • Sub-second proof generation in browser (WASM)
  • Trusted setup from Aztec's ceremony (100+ participants)

3.1 Withdrawal Circuit

The withdrawal circuit proves knowledge of a valid deposit without revealing which one:

Public Inputs:
- merkleRoot: bytes32      // Current tree root
- nullifier: bytes32       // Unique spend identifier
- recipient: address       // Withdrawal destination
- amount: uint256          // Withdrawal amount

Private Inputs:
- secret: bytes32          // User's secret key
- commitment: bytes32      // Original commitment
- merklePath: bytes32[32]  // Inclusion proof
- pathIndices: uint32      // Path direction bits

Constraints:
1. commitment = Pedersen(amount, secret)
2. MerkleVerify(commitment, merklePath, root) == true
3. nullifier = Hash(secret, leafIndex, commitment)

3.2 Proof Verification

On-chain verification uses the UltraVerifier contract which validates:

  • Proof structure and format validity
  • Public input consistency with contract state
  • Cryptographic verification of the SNARK proof

4. Protocol Operations

4.1 Deposit Flow

1. User generates random secret s
2. Computes commitment C = Pedersen(amount, s)
3. Calls deposit(C) with ETH value
4. Contract:
   - Validates amount (0.01 - 100 ETH)
   - Deducts 0.3% protocol fee
   - Inserts C into Merkle tree
   - Emits Deposit(C, leafIndex, timestamp)
5. User stores note: {secret, amount, leafIndex}

4.2 Withdrawal Flow

1. User loads note and fetches current merkleRoot
2. Generates ZK proof of valid deposit
3. Calls withdraw(proof, nullifier, recipient, amount)
4. Contract:
   - Verifies nullifier not spent
   - Verifies proof against UltraVerifier
   - Marks nullifier as spent
   - Transfers amount to recipient
   - Emits Withdrawal(nullifier, recipient, amount)

4.3 Private Transfers

The homomorphic property of Pedersen commitments enables splitting deposits:

transfer(proof, nullifier, newCommitmentA, newCommitmentB)

Proves: C_old = C_A + C_B (value conservation)
Result: Two new commitments in tree, original nullified

5. Cross-Chain Privacy (HTLC)

Hash Time-Locked Contracts enable trustless cross-chain swaps while preserving privacy:

struct Swap {
    address sender;
    address recipient;
    uint256 amount;
    bytes32 hashlock;    // H(preimage)
    uint256 timelock;    // Expiry timestamp
    bool redeemed;
    bool refunded;
}

Flow:
1. Alice initiates HTLC on Chain A with hashlock
2. Bob initiates matching HTLC on Chain B
3. Alice redeems on Chain B (reveals preimage)
4. Bob uses preimage to redeem on Chain A
5. If timeout: funds return to original senders

6. Security Considerations

6.1 Anonymity Set

Privacy strength depends on the anonymity set size—the number of deposits that could plausibly be the source of a withdrawal. Larger pools provide stronger privacy guarantees.

6.2 Timing Analysis Resistance

Users are encouraged to wait before withdrawing. The protocol stores 100 historical Merkle roots, allowing withdrawals to reference older tree states and reducing timing correlation attacks.

6.3 Note Security

The secret note is the bearer instrument for funds. Users must:

  • Store notes securely (encrypted backup recommended)
  • Never share notes—possession enables withdrawal
  • Verify note integrity before attempting withdrawal

7. Deployed Contracts

All contracts are deployed on Ethereum Mainnet and verified on Etherscan:

HomomorphicPoolView on Etherscan
0x6482c2007846eb37A8421C0B0ae8e0E40B88352E

Main privacy pool contract

0x04927F8134e958E776ba2B82D4892e9b6A86975A

Cross-chain atomic swap contract

UltraVerifierView on Etherscan
0x3836E3B26bcc37Cdd6B4f7677C07B8Be744d8C10

ZK proof verification contract

VerifierAdapterView on Etherscan
0xDF5eD05eb7d34b4eC86E96D78192065c70f21945

Verifier interface adapter

Protocol Fee

0.3% (30 basis points) on deposits

8. Conclusion

Laundry Protocol provides a production-ready privacy solution for Ethereum users seeking to protect their financial privacy. By combining proven cryptographic primitives—Pedersen commitments, Merkle trees, and zero-knowledge proofs—with a user-friendly interface, the protocol makes privacy accessible without sacrificing security or decentralization.

The protocol is fully non-custodial: users maintain complete control of their funds through secret notes, and all operations are verified on-chain through immutable smart contracts. No trusted third party can access, freeze, or censor user funds.

References

[1] Ben-Sasson, E., et al. "SNARKs for C: Verifying Program Executions Succinctly and in Zero Knowledge." CRYPTO 2013.

[2] Pedersen, T. P. "Non-Interactive and Information-Theoretic Secure Verifiable Secret Sharing." CRYPTO 1991.

[3] Aztec Protocol. "PLONK: Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge." 2019.

[4] Ethereum Foundation. "EIP-196: Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128."