CLEAN POOLS
$CLEANlaunching on pump.fun

Privacy pools on Solana

Private transfers that can prove they’re clean.

Deposit into a shielded pool, withdraw to any fresh wallet, and prove in zero knowledge that your funds come from a clean set, without revealing which deposit was yours.

Privacy without the dirty money.

A plain mixerevery output inherits the risk
inone poolout

Anyone can deposit. Stolen funds hide among honest ones, exchanges flag every withdrawal, and honest users carry the risk.

A clean poolwithdrawals carry a proof
policy π π π π π inclean setout

Withdrawals carry a proof that the deposit belongs to a set that excludes flagged sources. Your history stays private. The hacker’s doesn’t.

How a clean withdrawal works.

  1. Deposit
  2. Association sets
  3. Prove
  4. Relay
  5. Verify

Deposit.

Your browser draws a random nullifier and secret and computes

commitment = Poseidon(nullifier, secret, asset, amount)

Only the commitment goes on-chain, as a new leaf in the pool’s depth-26 incremental Merkle tree. The note (nullifier + secret) never leaves your device.

nullifier secret asset amount Poseidon new leaf private · stays in your browser public · on-chain as one 32-byte leaf

Association sets.

Association Set Providers publish a Merkle root of approved deposits every epoch, under a public policy. The default policy excludes deposits traced to known exploits, drainers and sanctioned addresses.

pool leaves · every deposit public policy · exploits, drainers, sanctions, one hop association root

Prove.

To withdraw, your browser generates a Groth16 proof that you know the secret of a leaf in the pool tree, that the same leaf is in the association set you chose, and that the nullifier hash is new. No leaf index, no deposit address.

private nullifier secret pool path set path circuit public pool_root association_root nullifier_hash recipient · fee π · 256 bytes

Relay.

A relayer submits the withdrawal and pays the fee, so the fresh recipient wallet doesn’t need SOL beforehand. The proof binds the recipient and the fee, so a relayer can’t redirect funds.

browserproof + inputs relayerpays the tx fee programSolana fresh wallet bound in π: recipient · fee no SOL neededin the recipient beforehand

Verify.

The Solana program checks the proof with the alt_bn128 syscalls, marks the nullifier spent, and pays the recipient. The protocol fee buys back $CLEAN.

program alt_bn128 pairing nullifier PDA roots known recipientamount − fees 0.25% feebuys back $CLEAN

Proof Studio.

Everything cryptographic happens in your browser. Nothing secret is sent anywhere.

Prover idle Wallet none
  1. Generate
  2. Back up
  3. Sign
Asset
Denomination

Fixed amounts keep every deposit in a pool identical, so none stands out.

Outputformulas
commitment
Poseidon(nullifier, secret, asset, amount)
nullifier_hash
Poseidon(nullifier)
note
cleanpools-<asset>-<amount>-<base64url(nullifier‖secret)>
asset
wSOL mint as a field element
amount
base units

Association sets.

Privacy is the default. The set you prove against is your choice.

Policy card

Default policy v1

  1. Exploits and drainers. Addresses named in public incident reports are excluded, each with a link to its report.
  2. Sanctions. Addresses on official sanctions lists are excluded.
  3. One hop. Addresses funded directly by any excluded address are excluded too.
  4. Every epoch. The set is rebuilt each Solana epoch. The root and the full list are published together.
  5. Appeals. The depositing address can sign an appeal. If it’s upheld, the deposit rejoins at the next epoch. Ragequit stays open either way.
sha256(policy.json)computing…
Any provider can publish a set

A set is a provider key, a policy document and one root per epoch. Each withdrawal names the set it proves against, and the program checks the proof’s association root against that set’s published roots.

sethash(provider key, policy hash)
policypublic JSON, versioned
rootone per epoch, depth 26
named byevery withdrawal
pool tree · every deposit association set · approved only pool_root association_root your leaf · proven in both, index hidden

No one can lock you in.

If no association set accepts your deposit, you can always withdraw it back to the address it came from. Publicly, without privacy, but with nothing lost.

depositingaddress pool deposit · 1 SOL ragequit · 1 SOL back, in public no association set needed · no fee to anyone but the network

Built on Solana’s own primitives.

PoseidonNative

Syscall. The same BN254 Poseidon the circuit uses, computed by the runtime.

Groth16 verify1 tx

alt_bn128 syscalls, one transaction: point addition, scalar multiplication and the pairing check.

Tree26

Depth 26 · 67,108,864 deposits per pool.

Proof256B

A and C in G1, B in G2. Small enough for one instruction.

Nullifiers1 PDA

One account per spent note. A second spend can’t create it again.

Denominations0.1·1·10 SOL100·1,000 USDC

Fixed denominations make the anonymity set uniform: every deposit in a pool is the same size, so amounts can’t link a withdrawal to a deposit.

Prove your history when you want to.

Export a viewing key for a single withdrawal and hand it to an exchange or an auditor. It shows which deposit the withdrawal came from, and nothing else in your history.

Threat model.

What an attacker can try, and what stops it.

Front-running

The recipient is a public input of the proof. A copied transaction can only pay the same recipient.

Double spend

Each withdrawal creates a PDA derived from its nullifier hash. A second withdrawal of the same note fails because that account already exists.

Malicious relayer

The fee and the recipient are bound in the proof. A relayer can submit the transaction or drop it, never change where the funds go or what it earns.

Trusted setup

Groth16 needs a circuit-specific setup. The design requires a multi-party ceremony with a published transcript, so one honest participant is enough.

Timing analysis

A withdrawal right after a deposit narrows the set. Wait several epochs and use common denominations. Once the pool is live, the interface shows the current anonymity set size, read from the chain.

Note loss

The note is the only key to the deposit. Nobody can recover it, including Clean Pools. Back it up before you sign.

$CLEAN.

Every withdrawal pays a 0.25% protocol fee. The fee buys back $CLEAN on the open market.

Relayer fees go to the relayers that submit withdrawals.

withdrawal100%
protocol fee0.25% → buys $CLEAN
relayer feeset per withdrawal → relayer

$CLEAN is launching on pump.fun

The contract address appears here, in the header and in the footer the moment it exists.

Questions.

Is Clean Pools a mixer?

It’s a privacy pool with proofs of association. A mixer hides everyone together. Clean Pools lets honest users prove their funds came from a clean set, and keeps flagged deposits out of those sets.

Who decides what’s clean?

Association Set Providers, under published policies. You choose which set to prove against, and you can always ragequit.

Can anyone see my deposit?

Everyone sees that an address deposited a fixed amount. No one can link it to a withdrawal.

What if my deposit is flagged by mistake?

Every policy has an appeal path, and ragequit returns your funds to the original address.

What do I need to keep?

Your note. It’s the only key to the deposit. Clean Pools never sees it.

Is this legal where I live?

Privacy tools are treated differently by different jurisdictions. Check your local rules before you use any privacy protocol.

Where do fees go?

0.25% of every withdrawal buys back $CLEAN. Relayers earn the relayer fee.