Security and risk
Privé is unaudited, pre-release software running on a testnet. This page is the honest list of what can go wrong. Every item below is a real property of the code as it exists today, not boilerplate to skip.
The contracts are not audited
No third party has audited the pool, the verifier, or the circuits. The deployment on Robinhood Chain Testnet (chain id 46630) is the only one that exists. There is no mainnet contract, and any address claiming to be a Privé mainnet pool is fraudulent.
The token in the app is MockUSDG, a test ERC-20 with six decimals and a fully open mint function that anyone can call. It is a faucet token. It is worth nothing, it is not the real Paxos-issued USDG, and it must never be treated as money. Testnet ETH for gas is likewise free.
Do not send real funds to any Privé contract. There is currently no way for real funds to be at stake, and if that ever changes it will be announced only after an audit.
Smart contract risk
A bug in PrivacyPool, in the on-chain WithdrawVerifier, or in the Merkle tree could mean the total and permanent loss of everything in the pool. The pool is a bearer-asset contract: it holds the tokens and releases them to whoever presents a valid proof. There is no admin key that can pause it, no upgrade path, and no way to reverse a withdrawal or recover funds from a broken state. That is deliberate, since an admin key is itself a risk, but it also means there is no safety net if the code is wrong.
The circuit is adapted from the 0xbow privacy-pools-core withdraw circuit, which reduces novel-design risk. The adaptation is ours and has not been reviewed.
The trusted setup is a development ceremony
Groth16 needs a trusted setup. The proving and verifying keys shipped today were produced by the repo build script with deterministic, hardcoded entropy, on one machine, by one person. It is not a multi party ceremony, and the build script says so in its own comments.
The consequence is precise: anyone who can reproduce that entropy holds the toxic waste and can forge a withdrawal proof for a note that never existed. The current proving key therefore permits undetectable minting of value out of the pool. On a testnet with a free faucet token that costs nothing, which is why it is acceptable today, and exactly why the current key must never secure real value. A production multi party ceremony is a prerequisite for mainnet, alongside the audit.
Your note is the money. Lose it and it is gone.
When you deposit, you keep a secret note in your browser. The chain stores only a Poseidon commitment to it. Nobody, including us, can reconstruct your note from on-chain data, because the secrets were never on-chain.
So there is no recovery. No password reset, no support ticket, no seed phrase that regenerates it. If the note is gone, the deposit is permanently unspendable and the tokens sit in the pool forever.
- Clearing site data destroys your notes. Notes live in browser storage. Clearing cookies and site data, closing a private window, an aggressive privacy extension, an OS reinstall, or switching browsers or devices will lose them.
- Back up, and keep the backup elsewhere. The app exports an encrypted backup, sealed with a passphrase you choose and derived with PBKDF2 (200,000 iterations, SHA-256) into an AES-GCM key. A weak passphrase means a weak backup, and a forgotten one means an unrecoverable backup.
- Claim links contain a live secret. A claim link carries the note in the URL fragment. Anyone who sees the link can claim the funds. Send it over a channel you trust and assume it is spent once it leaks.
Browser and device risk
Proofs are generated locally and your secrets stay on your device, which is good for privacy but moves the trust to your machine. Malware, a malicious extension with access to page storage, or a compromised device can read your notes and steal your funds. An attacker who serves you a modified frontend can exfiltrate secrets as you use it. Use a clean browser profile and confirm the domain.
The relayer can fail, but it cannot steal
The optional relayer submits your withdrawal so you do not need a funded gas account. Its powers are bounded by the proof itself.
The recipient, the fee recipient, and the fee amount are hashed with the pool scope into a context value that is a public input to your proof. The contract recomputes that hash independently and reverts with ContextMismatch if it differs by one bit. A relayer therefore cannot redirect your payout, raise its fee after the fact, or replay your proof elsewhere. The contract also rejects a fee greater than or equal to the withdrawn value (FeeExceedsValue).
What it can do: refuse you, go offline, rate-limit you, or censor a specific withdrawal. It also sees your IP address and your recipient address when you ask it to relay, so it learns a link between those two. It does not learn which deposit you are spending. If it is down or censors you, you can always self-submit the same withdrawal from your own wallet and pay gas yourself. That escape hatch is why the relayer is a convenience, not a dependency.
Privacy is not automatic
The cryptography unlinks a deposit from a withdrawal. It cannot protect you from patterns you create yourself. The common ways users deanonymize themselves:
| Behaviour | Why it leaks |
|---|---|
| Depositing and withdrawing the same unusual amount | An observer matches the two by value. Deposit 1,337.42 and later withdraw 1,337.42 and you have linked them yourself. Round amounts hide better, and partial withdrawals help. |
| Withdrawing immediately after depositing | Timing correlation. If yours is the only deposit in the window, the anonymity set is one and the maths gives you nothing. |
| Reusing one address for deposit and withdrawal | It publicly reconnects both ends and defeats the point. Use a fresh recipient address. |
| Paying withdrawal gas from the depositing wallet | Funding a fresh recipient from your deposit address links them on-chain. Use the relayer, or fund gas independently. |
Above all: the anonymity set on testnet is small. Your privacy is bounded by how many other deposits you hide among, and that number is currently low. Treat testnet activity as effectively public.
The compliance layer is not live
Planned The Association Set Provider, deposit screening, the approved-set root, and proof-of-innocence membership are designed but not deployed. The withdraw circuit shipping today contains no ASP constraint at all, as its own source comment states. Nothing in the protocol currently screens deposits or restricts who may withdraw.
Do not rely on it, do not describe Privé as screened or compliant today, and do not assume any deposit has been checked against anything. In-protocol enforcement is intended to land with the mainnet audit. See Compliance model for the design and its status.
Regulatory risk
Privacy tooling for transferable value sits in a contested and shifting legal area. Rules differ by jurisdiction, they are changing, and protocols in this category have been sanctioned before. You are responsible for whether your use of Privé is lawful where you are. Nothing in these docs is legal advice, and the planned compliance layer is not a promise that any given regulator will accept it.
Responsible disclosure
If you find a vulnerability we want to hear about it, and we would rather hear it privately first. That includes anything that lets a user withdraw more than they deposited, spend a note twice, forge a proof, bypass the context binding, drain the relayer, or link a deposit to a withdrawal.
Report it to security@privepay.app with enough detail to reproduce, and give us a reasonable window to respond before publishing. Please test only against the testnet deployment. We will not pursue good-faith research that follows this policy, and we will credit you unless you prefer otherwise. There is no paid bounty yet: one is planned to open alongside the audit, before any mainnet deployment.