d903624f (main)
and
70061467 (PR)
+3 -3
+3 -3
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which deposit funds which withdrawal, for deposits approved by the permissioned association set provider. Everything else is public."
"text": "Hides which deposit funds which withdrawal, for deposits approved by the permissioned ASP. Everything else is public."
},
"adversaries": [
{
"id": "publicObserver",
"label": "Public observer",
"description": "Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links.",
"examples": "A curious counterparty, an employer, a journalist."
},
{
"id": "chainAnalyst",
"label": "Chain analyst",
"description": "Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters.",
"examples": "Chain analytics firms, ZachXBT, data brokers."
},
{
"id": "networkObserver",
"label": "Network observer",
"description": "Sits between the user and the chain and sees web2 traffic only: RPC providers, relayers and broadcasters, indexers, ISPs. Learns IP addresses, timing, browser fingerprints, ciphertext and what becomes public. Assumes Tor to send transactions and, where the client has an RPC setting, an own node to read the blockchain.",
"examples": "Infura or Alchemy, a Tornado relayer, a wallet vendor selling telemetry, Google captcha or analytics in the dapp frontend."
},
{
"id": "privilegedInsider",
"label": "Privileged insider",
"description": "Holds a protocol operator role or receives keys or plaintext by design: upgrade admin, sequencer, decryption or view key holder, TEE vendor, association set provider, hosted prover, note registry.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and future cryptanalysis such as a large quantum computer that breaks elliptic-curve key exchange and pairings, but not hashes, symmetric ciphers or lattices.",
"examples": "First well-funded insiders, then everyone in a potential post-quantum future."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposure": "Which approved deposit a withdrawal spends is hidden. Partial withdrawals leave a change note and look like full ones.",
"advice": "Exit through a relayer, so no wallet of yours pays gas for the receiving address. Withdraw partially to fresh addresses, and ragequit only untouched deposits: ragequitting a change note reveals the withdrawal it came from.",
"sources": [
{
"contract": "PrivacyPoolsEntrypoint"
},
{
"title": "Withdrawal circuit",
"url": "https://github.com/0xbow-io/privacy-pools-core/blob/main/packages/circuits/circuits/withdraw.circom"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposure": "The approved set at any block is public, so the candidates for a withdrawal are known exactly and matched by amount and timing. Any amount is allowed, so an unusual one pairs a deposit with its withdrawal. Paying gas from your own wallet instead of a relayer exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"exposure": "The approved set at any block is public, bounding the anonymity set. Any amount is allowed, so an unusual one pairs a deposit with its withdrawal. Paying gas from your own wallet instead of a relayer exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"advice": "Use a pool with a large anonymity set. Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"sources": [
{
"title": "Blockchain Privacy and Regulatory Compliance (design paper)",
"url": "https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4563364"
},
{
"title": "Tutela (arXiv:2201.06811)",
"url": "https://arxiv.org/abs/2201.06811"
}
],
"id": "chainAnalyst",
"value": "Link private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "bad",
"exposure": "Event sync is forced through 0xbow's own proxy with no option for a public node. On login the app looks up the block of each of your deposits, then polls your wallet balance and the withdrawal receipt from the same session. Relayers receive amount and recipient for a quote.",
"advice": "Use a client with an RPC setting, such as the raw SDK, pointed at your own node. The official frontend cannot avoid the lookups.",
"sources": [
{
"title": "Reads go to Alchemy under 0xbow's key, no RPC setting",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/config/wagmiConfig.ts#L46-L58"
},
{
"title": "Event sync through 0xbow's Hypersync proxy",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/config/chainData.ts#L135-L140"
},
{
"title": "Block lookup for every deposit on login",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/utils/sdk.ts#L439-L442"
},
{
"title": "Wallet balance polled every 10 seconds",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/providers/ChainProvider.tsx#L126-L133"
},
{
"title": "Withdrawal receipt polled after relay",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/hooks/useWithdraw.ts#L414-L416"
},
{
"title": "Login requires a connected wallet",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/providers/AuthProvider.tsx#L33-L36"
},
{
"title": "Relayer quote carries amount and recipient",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/utils/relayerClient.ts#L45-L58"
}
],
"id": "networkObserver",
"value": "Link exposed",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "warning",
"exposure": "The ASP postman sets a new approved list at any time with no delay, and the pool accepts only the latest one. It can deny you a private exit or publish a list with only your deposit, which is then your whole anonymity set. The website shows the anonymity set but does not block a tiny one.",
"advice": "Check the anonymity set shown before withdrawing, or count the approved list yourself with a client that fetches it whole, such as the raw SDK.",
"sources": [
{
"contract": "PrivacyPoolsEntrypoint",
"title": "updateRoot: the postman sets a new root with no delay"
},
{
"contract": "PrivacyPoolETH",
"title": "validWithdrawal: only the latest ASP root is accepted"
},
{
"section": "permissions",
"title": "Privacy Pools Multisig and ASP postman"
},
{
"title": "Anonymity set shown, not enforced",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/62a962f68def3cbeb801c4259e743fd3d4762928/src/containers/Modals/Withdraw/WithdrawForm.tsx#L204-L213"
},
{
"title": "Kohaku fetches the approved list whole",
"url": "https://github.com/ethereum/kohaku/blob/master/packages/privacy-pools/src/data/0xbowAsp.service.ts"
}
],
"id": "privilegedInsider",
"value": "Link at risk",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "good",
"exposure": "Nothing encrypted is written onchain and commitments are plain hashes, so a quantum computer recovers nothing. Accounts created from a wallet signature reduce to that wallet's key.",
"advice": "Create your account from a seed phrase, not from a wallet signature.",
"sources": [
{
"title": "Commitment circuit (Poseidon)",
"url": "https://github.com/0xbow-io/privacy-pools-core/blob/main/packages/circuits/circuits/commitment.circom"
},
{
"title": "Wallet-signature seed derivation",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/main/src/utils/walletSeed.ts"
},
{
"section": "trusted-setups"
}
],
"id": "futureAdversary",
"value": "Link private",
"alsoExposed": []
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "pool",
"label": "Pool",
"description": "Funds enter a shared pool and leave it later, with no transfers inside. Hides which deposit funds which withdrawal."
"description": "Funds enter a shared pool and leave it later, with no actions inside. Hides which deposit funds which withdrawal."
},
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "Even if the Entrypoint or ASP turns malicious, users can always ragequit (withdraw tokens) from the immutable pool contracts, at the cost of losing privacy.",
"walkawayTest": {
"passed": false,
"reason": "Association Set Providers must explicitly approve deposited funds before private withdrawals."
}
},
"relayerTracking": {
"type": "onchainEvents",
"sources": [
{
"address": "eth:0x6818809EefCe719E480a7526D76bD3e561526b46",
"sinceTimestamp": 1743265247,
"extractor": "privacyPoolsWithdrawalRelayed"
}
]
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "The contracts, circuits, and supporting software needed to participate in Privacy Pools are publicly available and can be run locally."
},
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid withdrawals.\n2. the [trusted setup](#trusted-setups) is compromised or all ceremony participants collude, allowing invalid withdrawals.\n3. the Entrypoint owner deploys a malicious [upgrade](#upgrades-and-governance) that steals new deposits.\n
\n## Funds can be lost if\n1. a user loses the secret and nullifier required to spend their deposit.\n
\n## Privacy can be lost if\n1. no relayer is available and the withdrawal must be submitted from an address that can be linked to the user.\n2. the ASP manager refuses to whitelist a deposit, forcing the user to either wait or exit publicly through ragequit.",
"tokens": [
{
"token": {
"address": "0x6440f144b7e50D6a8439336510312d2F54beB01D",
"iconUrl": "https://coin-images.coingecko.com/coins/images/56069/large/BOLD_logo.png?1748265087",
"symbol": "BOLD",
"decimals": 18,
"priceId": "liquity-bold-2",
"sinceTimestamp": 1770806867
},
"buckets": [
{
"id": "privacy-pools-BOLD",
"type": "pool",
"label": "BOLD pool",
"address": "eth:0xb4b5Fd38Fd4788071d7287e3cB52948e0d10b23E",
"sinceTimestamp": 1770806867,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"iconUrl": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1752848471
},
"buckets": [
{
"id": "privacy-pools-DAI",
"type": "pool",
"label": "DAI pool",
"address": "eth:0x1c31C03B8CB2EE674D0F11De77135536db828257",
"sinceTimestamp": 1752848471,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
"symbol": "ETH",
"decimals": 18,
"priceId": "ethereum",
"sinceTimestamp": 1743265259
},
"buckets": [
{
"id": "privacy-pools-ETH",
"type": "pool",
"label": "ETH pool",
"address": "eth:0xF241d57C6DebAe225c0F2e6eA1529373C9A9C9fB",
"sinceTimestamp": 1743265259,
"anonymitySet": {
"minimumAmounts": [
"100000000000000000",
"10000000000000000000"
]
},
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29",
"iconUrl": "https://coin-images.coingecko.com/coins/images/53963/large/frxUSD.png?1737792154",
"symbol": "frxUSD",
"decimals": 18,
"priceId": "frax-usd",
"sinceTimestamp": 1754583623
},
"buckets": [
{
"id": "privacy-pools-frxUSD",
"type": "pool",
"label": "frxUSD pool",
"address": "eth:0xC6C769fac7AABEadd31a03fAe5Ca0Ec5B4C50f84",
"sinceTimestamp": 1754583623,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x085780639CC2cACd35E474e71f4d000e2405d8f6",
"iconUrl": "https://coin-images.coingecko.com/coins/images/36202/large/fxUSD.jpg?1710833113",
"symbol": "fxUSD",
"decimals": 18,
"priceId": "f-x-protocol-fxusd",
"sinceTimestamp": 1765446455
},
"buckets": [
{
"id": "privacy-pools-fxUSD",
"type": "pool",
"label": "fxUSD pool",
"address": "eth:0xD14F4B36E1D1D98c218db782c49149876042BC56",
"sinceTimestamp": 1765446455,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD",
"iconUrl": "https://coin-images.coingecko.com/coins/images/52721/large/sUSDS_Coin.png?1734086971",
"symbol": "sUSDS",
"decimals": 18,
"priceId": "susds",
"sinceTimestamp": 1752782999
},
"buckets": [
{
"id": "privacy-pools-sUSDS",
"type": "pool",
"label": "sUSDS pool",
"address": "eth:0xBBdA2173CDFEA1c3bD7F2908798F1265301d750c",
"sinceTimestamp": 1752782999,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
"iconUrl": "https://coin-images.coingecko.com/coins/images/54977/large/USD1_1000x1000_transparent.png?1749297002",
"symbol": "USD1",
"decimals": 18,
"priceId": "usd1-wlfi",
"sinceTimestamp": 1754583179
},
"buckets": [
{
"id": "privacy-pools-USD1",
"type": "pool",
"label": "USD1 pool",
"address": "eth:0xc0A8Bc0F4F982b4d4f1fFae8F4FCCb58c9B29c98",
"sinceTimestamp": 1754583179,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1753353035
},
"buckets": [
{
"id": "privacy-pools-USDC",
"type": "pool",
"label": "USDC pool",
"address": "eth:0xb419c2867aB3CBc78921660cB95150d95A94ce86",
"sinceTimestamp": 1753353035,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3",
"iconUrl": "https://assets.coingecko.com/coins/images/33613/large/3466ef_3c088c66c7d941e8856339d0bddf33cc_mv2.png?1702514458",
"symbol": "USDe",
"decimals": 18,
"priceId": "ethena-usde",
"sinceTimestamp": 1754583083
},
"buckets": [
{
"id": "privacy-pools-USDe",
"type": "pool",
"label": "USDe pool",
"address": "eth:0xe6D36B33b00A7C0cB0C2a8d39D07e7dB0c526Abc",
"sinceTimestamp": 1754583083,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xdC035D45d973E3EC169d2276DDab16f1e407384F",
"iconUrl": "https://coin-images.coingecko.com/coins/images/39926/large/usds.webp?1726666683",
"symbol": "USDS",
"decimals": 18,
"priceId": "usds",
"sinceTimestamp": 1752503003
},
"buckets": [
{
"id": "privacy-pools-USDS",
"type": "pool",
"label": "USDS pool",
"address": "eth:0x05e4DBD71B56861eeD2Aaa12d00A797F04B5D3c0",
"sinceTimestamp": 1752503003,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "USDT",
"decimals": 6,
"priceId": "tether",
"sinceTimestamp": 1753352915
},
"buckets": [
{
"id": "privacy-pools-USDT",
"type": "pool",
"label": "USDT pool",
"address": "eth:0xe859C0bD25f260BaEE534Fb52e307D3b64D24572",
"sinceTimestamp": 1753352915,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1753975703
},
"buckets": [
{
"id": "privacy-pools-WBTC",
"type": "pool",
"label": "WBTC pool",
"address": "eth:0xF973f4B180A568157Cd7A0E6006449139E6Bfc32",
"sinceTimestamp": 1753975703,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xDcEe70654261AF21C44c093C300eD3Bb97b78192",
"iconUrl": "https://coin-images.coingecko.com/coins/images/29734/large/woeth-200x200.png?1714796686",
"symbol": "wOETH",
"decimals": 18,
"priceId": "wrapped-oeth",
"sinceTimestamp": 1756377743
},
"buckets": [
{
"id": "privacy-pools-wOETH",
"type": "pool",
"label": "wOETH pool",
"address": "eth:0x7d2959bCFb936a84531518e8391DdBa844e03ebE",
"sinceTimestamp": 1756377743,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
"iconUrl": "https://assets.coingecko.com/coins/images/18834/large/wstETH.png?1696518295",
"symbol": "wstETH",
"decimals": 18,
"priceId": "wrapped-steth",
"sinceTimestamp": 1753975583
},
"buckets": [
{
"id": "privacy-pools-wstETH",
"type": "pool",
"label": "wstETH pool",
"address": "eth:0x1A604E9DFa0EFDC7FFda378AF16Cb81243b61633",
"sinceTimestamp": 1753975583,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "The 2/4 Privacy Pools Multisig can instantly change the system’s critical configs, including the Entrypoint implementation and ASP root used for private withdrawals. The ASP postman (EOA) can also remove any deposit from the whitelist at any time, forcing a public rage-quit if the affected party wishes to withdraw. The guaranteed immutable escape hatch is pool-level ragequit (public withdrawal) to the original depositor address, because that logic lives in the immutable pool contracts and does not depend on the Entrypoint registry and config. This means the system is permissioned in its deposit logic and deposit privacy, but non-custodial for deposited assets. Past, successful (non-ragequit) withdrawals can not be deanonymized by the protocol."
}
}
+1 -1
+1 -1
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides everything inside the pool, including which shield funds which unshield. Shields and unshields are public."
},
"adversaries": [
{
"id": "publicObserver",
"label": "Public observer",
"description": "Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links.",
"examples": "A curious counterparty, an employer, a journalist."
},
{
"id": "chainAnalyst",
"label": "Chain analyst",
"description": "Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters.",
"examples": "Chain analytics firms, ZachXBT, data brokers."
},
{
"id": "networkObserver",
"label": "Network observer",
"description": "Sits between the user and the chain and sees web2 traffic only: RPC providers, relayers and broadcasters, indexers, ISPs. Learns IP addresses, timing, browser fingerprints, ciphertext and what becomes public. Assumes Tor to send transactions and, where the client has an RPC setting, an own node to read the blockchain.",
"examples": "Infura or Alchemy, a Tornado relayer, a wallet vendor selling telemetry, Google captcha or analytics in the dapp frontend."
},
{
"id": "privilegedInsider",
"label": "Privileged insider",
"description": "Holds a protocol operator role or receives keys or plaintext by design: upgrade admin, sequencer, decryption or view key holder, TEE vendor, association set provider, hosted prover, note registry.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and future cryptanalysis such as a large quantum computer that breaks elliptic-curve key exchange and pairings, but not hashes, symmetric ciphers or lattices.",
"examples": "First well-funded insiders, then everyone in a potential post-quantum future."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposure": "Everything inside the pool is hidden, including which shield funds which unshield. Shields and unshields show address, token and amount. DeFi bundles unshield to the adapter in cleartext.",
"advice": "Exit through a broadcaster, so no wallet of yours pays gas for the receiving address.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "private",
"note": "Leaked for DeFi bundles, which unshield to the adapter in cleartext."
},
"asset": {
"verdict": "private",
"note": "Leaked for DeFi bundles."
},
"linkage": "private"
},
"sources": [
{
"contract": "RailgunSmartWallet"
},
{
"title": "Note ciphertext format",
"url": "https://github.com/Railgun-Community/engine/blob/main/src/note/transact-note.ts"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposure": "The candidates for an unshield are the shields of the same token. In-pool transfers break the one-to-one match of a mixer, but timing and exact or round amounts narrow the set. Paying gas from your own wallet instead of a broadcaster exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"advice": "Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "private",
"linkage": "private"
},
"sources": [
{
"title": "A Tattered Cloak of Invisibility (arXiv:2606.25926)",
"url": "https://arxiv.org/abs/2606.25926"
},
{
"title": "The Anonymity Gap (arXiv:2608.22987)",
"url": "https://arxiv.org/abs/2608.22987"
}
],
"id": "chainAnalyst",
"value": "Link private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "good",
"exposure": "The wallet finds its notes by trial-decrypting every note locally, so nodes learn nothing about which are yours. By default it sends the pending unshield to the configured node for a gas estimate, which reveals the destination early.",
"advice": "Read the chain from your own node, send through a public RPC over Tor, and use a popular broadcaster. If you settle for a VPN instead of Tor, pick one you trust: it hides your IP from the broadcaster but sees it itself.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "private",
"linkage": "private"
},
"sources": [
{
"title": "Gas estimation with dummy proof",
"url": "https://github.com/Railgun-Community/wallet/blob/main/src/services/transactions/tx-gas-details.ts"
},
{
"title": "Broadcaster decrypts request",
"url": "https://github.com/Railgun-Community/ppoi-safe-broadcaster-example/blob/main/src/server/waku-broadcaster/methods/transact-method.ts"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "good",
"exposure": "There is no view key, so nobody can read past activity. The DAO can upgrade the contracts after a seven-day delay. The proof-of-innocence list provider can refuse to list a shield, leaving only a self-broadcast exit.",
"advice": "Watch governance proposals; you have seven days to unshield before an upgrade takes effect. Be ready to self-broadcast if the list provider censors you.",
"advice": "Watch governance proposals. You have seven days to unshield before an upgrade takes effect. Be ready to self-broadcast if the list provider censors you.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "private",
"linkage": {
"verdict": "atRisk",
"note": "POI nodes tie a session to a note cluster and its transaction ids."
}
},
"sources": [
{
"section": "permissions",
"title": "Governance roles"
},
{
"title": "POI required lists",
"url": "https://github.com/Railgun-Community/shared-models/blob/main/src/models/poi.ts"
},
{
"title": "POI node interface",
"url": "https://github.com/Railgun-Community/engine/blob/main/src/poi/poi.ts"
}
],
"id": "privilegedInsider",
"value": "Link private",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposure": "Notes are encrypted with elliptic-curve key exchange. A quantum computer decrypts every note sent to a 0zk address that was ever shared, including broadcaster fee notes: amounts, tokens and counterparties.",
"advice": "Treat your 0zk address as a secret: share it privately, and use a fresh one per counterparty where you can.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Decrypts for notes touching any shared 0zk address."
},
"recipient": "atRisk",
"amount": "atRisk",
"asset": "atRisk",
"linkage": "atRisk"
},
"sources": [
{
"title": "Shared key derivation (Ed25519 ECDH)",
"url": "https://github.com/Railgun-Community/engine/blob/main/src/utils/keys-utils.ts"
},
{
"title": "AES-GCM note encryption",
"url": "https://github.com/Railgun-Community/engine/blob/main/src/utils/encryption/aes.ts"
}
],
"id": "futureAdversary",
"value": "Link at risk",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "atRisk"
},
{
"field": "amount",
"exposure": "atRisk"
},
{
"field": "asset",
"exposure": "atRisk"
}
]
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "defi",
"label": "DeFi",
"description": "Interop with DeFi (swaps, vaults) from within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit funds which withdrawal."
},
"exitWindow": {
"value": "7d",
"sentiment": "warning",
"orderHint": 604800,
"description": "DAO-approved upgrades wait 7d before they can execute, giving users time to unshield funds if they do not approve the change.",
"walkawayTest": {
"passed": true
}
},
"relayerTracking": {
"type": "railgunWaku",
"chainId": 1,
"sinceTimestamp": 1788134400
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "The contracts, circuits, and supporting software needed to participate in the protocol are publicly available and can be run locally."
},
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid spends or withdrawals.\n2. the [trusted setup](#trusted-setups) is compromised or all ceremony participants collude, allowing invalid spends or withdrawals.\n3. the DAO passes a malicious [upgrade](#upgrades-and-governance) and users do not react before the 7-day execution delay expires.\n
\n## Funds can be lost if\n1. a user loses the private keys required to control their private balance.\n
\n## Privacy can be lost if\n1. no broadcaster is available and transactions must be sent from a public address that can be linked to the user.",
"tokens": [
{
"token": {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"iconUrl": "https://assets.coingecko.com/coins/images/2518/large/weth.png?1696503332",
"symbol": "WETH",
"decimals": 18,
"priceId": "weth",
"sinceTimestamp": 1573689600
},
"buckets": [
{
"id": "railgun-WETH",
"type": "pool",
"label": "WETH",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"anonymitySet": {
"minimumAmounts": [
"100000000000000000",
"10000000000000000000"
]
},
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
}
}
]
},
{
"token": {
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "USDT",
"decimals": 6,
"priceId": "tether",
"sinceTimestamp": 1573689600
},
"buckets": [
{
"id": "railgun-USDT",
"type": "pool",
"label": "USDT",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}
}
}
]
},
{
"token": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1573689600
},
"buckets": [
{
"id": "railgun-USDC",
"type": "pool",
"label": "USDC",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
}
}
]
},
{
"token": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"iconUrl": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1574121600
},
"buckets": [
{
"id": "railgun-DAI",
"type": "pool",
"label": "DAI",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
}
}
]
},
{
"token": {
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1573689600
},
"buckets": [
{
"id": "railgun-WBTC",
"type": "pool",
"label": "WBTC",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
}
}
}
]
},
{
"token": {
"address": "0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4",
"iconUrl": "https://coin-images.coingecko.com/coins/images/71681/large/near.png?1768905174",
"symbol": "NEAR",
"decimals": 24,
"priceId": "rainbow-bridged-near-ethereum",
"sinceTimestamp": 1768867200
},
"buckets": [
{
"id": "railgun-NEAR",
"type": "pool",
"label": "NEAR",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1768867200,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4"
}
}
}
]
},
{
"token": {
"address": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb",
"iconUrl": "https://coin-images.coingecko.com/coins/images/14688/large/Logo_1_%28brighter%29.png?1734430693",
"symbol": "FLUID",
"decimals": 18,
"priceId": "instadapp",
"sinceTimestamp": 1623801600
},
"buckets": [
{
"id": "railgun-FLUID",
"type": "pool",
"label": "FLUID",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb"
}
}
}
]
},
{
"token": {
"address": "0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D",
"iconUrl": "https://coin-images.coingecko.com/coins/images/16840/large/railgun.jpeg?1696516407",
"symbol": "RAIL",
"decimals": 18,
"priceId": "railgun",
"sinceTimestamp": 1625270400
},
"buckets": [
{
"id": "railgun-RAIL",
"type": "pool",
"label": "RAIL",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D"
}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "Railgun features an omnipotent DAO governed by the stakers of the RAIL token. The DAO has the authority to change ZK circuit logic on the core Railgun contract, which can arbitrarily change the rules for shielded tokens; as well as manage blacklisted tokens, mint RAIL tokens and manage governance rewards. See docs here: ",
"governanceInfo": {
"upgrades": {
"Normal upgrade path": "Create a proposal with an IPFS link and onchain calldata in the [Voting contract](https://etherscan.io/address/0xc480F68A3dcC3EdD82134FAB45C14A0FcF1dA3CC) → collect **500.00 K RAIL** sponsorship within 1mo → wait 2d → cast Yay votes within 5d and Nay votes within 6d → pass with a simple majority and **2.00 M RAIL** quorum → wait 7d → permissionless execution through the Delegator within 14d.",
"Exit window": "**7d** — a passed proposal must wait this long before it can be executed, giving users time to unshield funds."
},
"tokenGovernance": {
"Governance token": "`RAIL` 1 token = 1 vote, delegated. Total supply: **60.00 M RAIL**, DAO-owned treasury (unavailable for voting): **2.30 M RAIL**, staked for voting: **41.61 M RAIL**, the rest is circulating supply.",
"Stake lock": "Unstaking has **1mo** delay.",
"Voting venue": "[Voting contract](https://etherscan.io/address/0xc480F68A3dcC3EdD82134FAB45C14A0FcF1dA3CC) on Ethereum. Proposal text is distributed over IPFS, its CID is available as a parameter of `createPropsal()` call on the voting contract.",
"Proposal threshold": "**No threshold to create a proposal.** A proposal must receive sponsorship from **500.00 K RAIL** stake within 1mo.",
"Quorum": "**2.00 M RAIL**, with a simple majority required for acceptance.",
"Execution model": "**Onchain calldata · Permissionless execution through the Delegator.** A passed proposal waits 7d, after which anyone can execute it through the [Delegator contract](https://etherscan.io/address/0xB6d513f6222Ee92Fff975E901bd792E2513fB53B) within 14d."
}
}
}
}
+3 -3
+3 -3
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which deposit funds which withdrawal. Everything else is public."
},
"adversaries": [
{
"id": "publicObserver",
"label": "Public observer",
"description": "Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links.",
"examples": "A curious counterparty, an employer, a journalist."
},
{
"id": "chainAnalyst",
"label": "Chain analyst",
"description": "Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters.",
"examples": "Chain analytics firms, ZachXBT, data brokers."
},
{
"id": "networkObserver",
"label": "Network observer",
"description": "Sits between the user and the chain and sees web2 traffic only: RPC providers, relayers and broadcasters, indexers, ISPs. Learns IP addresses, timing, browser fingerprints, ciphertext and what becomes public. Assumes Tor to send transactions and, where the client has an RPC setting, an own node to read the blockchain.",
"examples": "Infura or Alchemy, a Tornado relayer, a wallet vendor selling telemetry, Google captcha or analytics in the dapp frontend."
},
{
"id": "privilegedInsider",
"label": "Privileged insider",
"description": "Holds a protocol operator role or receives keys or plaintext by design: upgrade admin, sequencer, decryption or view key holder, TEE vendor, association set provider, hosted prover, note registry.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and future cryptanalysis such as a large quantum computer that breaks elliptic-curve key exchange and pairings, but not hashes, symmetric ciphers or lattices.",
"examples": "First well-funded insiders, then everyone in a potential post-quantum future."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposure": "Which deposit funds which withdrawal is hidden. Each pool has one fixed amount and no in-pool transfers, so every withdrawal spends exactly one deposit.",
"exposure": "Each pool has one fixed amount and no in-pool transfers, so every withdrawal spends exactly one deposit.",
"advice": "Exit through a relayer, so no wallet of yours pays gas for the receiving address.",
"sources": [
{
"contract": "Pool_1_ETH",
"title": "1 ETH pool"
},
{
"title": "Withdrawal circuit",
"url": "https://github.com/tornadocash/tornado-core/blob/master/circuits/withdraw.circom"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposure": "The candidates for a withdrawal are the unspent deposits of the same pool, the tracked anonymity set, which differs sharply between pools. Timing, address reuse and relayer choice narrow it further. Paying gas from your own wallet instead of a relayer exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"exposure": "The candidates for a withdrawal are the unspent deposits of the same pool, the anonymity set, which differs sharply between pools. Timing, address reuse and relayer choice narrow it further. Paying gas from your own wallet instead of a relayer exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"advice": "Use a pool with a large anonymity set. Deposit a large sum as one note. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"sources": [
{
"title": "Tutela (arXiv:2201.06811)",
"url": "https://arxiv.org/abs/2201.06811"
},
{
"title": "Blockchain is Watching You (arXiv:2005.14051)",
"url": "https://arxiv.org/abs/2005.14051"
}
],
"id": "chainAnalyst",
"value": "Link private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "good",
"exposure": "The app downloads all deposits and matches locally, so a node learns only which pool you looked at. The relayer receives only the finished withdrawal.",
"advice": "Read the chain from your own node, send through a public RPC over Tor, and use a popular relayer. If you settle for a VPN instead of Tor, pick one you trust: it hides your IP from the relayer but sees it itself.",
"sources": [
{
"title": "Event sync (classic UI)",
"url": "https://github.com/tornadocash/tornado-classic-ui/blob/master/services/events.js"
},
{
"title": "Relayer endpoint",
"url": "https://github.com/tornadocash/tornado-relayer/blob/master/src/contollers/controller.js"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "good",
"exposure": "The pools cannot be paused, upgraded or made to reveal anything. Governance controls only the website, the router and the relayer list.",
"advice": "Run an inspected local build of the client instead of the hosted frontend. Otherwise load it from its ENS name or IPFS hash.",
"sources": [
{
"contract": "Pool_0.1_ETH",
"title": "0.1 ETH pool (no operator)"
},
{
"contract": "InstanceRegistry"
},
{
"section": "permissions"
}
],
"id": "privilegedInsider",
"value": "Link private",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposure": "Commitments use a Pedersen hash on an elliptic curve. A quantum computer could test whether a given deposit can fund a withdrawal, shrinking the candidate set without singling out one deposit. Notes backed up onchain are encrypted with elliptic-curve keys and become readable.",
"advice": "Use a pool with a large anonymity set. Keep the note on your device and skip the onchain backup.",
"sources": [
{
"title": "Pedersen hash circuit (pinned circomlib fork)",
"url": "https://github.com/tornadocash/circomlib/blob/c372f14d324d57339c88451834bf2824e73bbdbc/circuits/pedersen.circom"
},
{
"title": "Commitment and nullifier hashing",
"url": "https://github.com/tornadocash/tornado-core/blob/v2.1/circuits/withdraw.circom"
},
{
"contract": "TornadoRouter",
"title": "Onchain note backups"
}
],
"id": "futureAdversary",
"value": "Link at risk",
"alsoExposed": []
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "fixedAmounts",
"label": "Fixed amounts",
"description": "Pre-defined transfer amounts use distinct buckets (anonymity sets)."
}
],
"category": {
"id": "pool",
"label": "Pool",
"description": "Funds enter a shared pool and leave it later, with no transfers inside. Hides which deposit funds which withdrawal."
"description": "Funds enter a shared pool and leave it later, with no actions inside. Hides which deposit funds which withdrawal."
},
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "The core Tornado Cash contracts are immutable and have no admin upgrade path, so users can always withdraw with a valid note and proof.",
"walkawayTest": {
"passed": true
}
},
"relayerTracking": {
"type": "onchainEvents",
"sources": [
{
"address": "eth:0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"sinceTimestamp": 1576523323,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"sinceTimestamp": 1576534673,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"sinceTimestamp": 1576536415,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xA160cdAB225685dA1d56aa342Ad8841c3b53f291",
"sinceTimestamp": 1577296976,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"sinceTimestamp": 1576534688,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"sinceTimestamp": 1577297383,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"sinceTimestamp": 1616114822,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x23773E65ed146A459791799d01336DB287f25334",
"sinceTimestamp": 1616115387,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"sinceTimestamp": 1577297782,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x03893a7c7463AE47D46bc7f091665f1893656003",
"sinceTimestamp": 1616155055,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"sinceTimestamp": 1616135923,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af",
"sinceTimestamp": 1616115450,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"sinceTimestamp": 1577298068,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D",
"sinceTimestamp": 1577298155,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"sinceTimestamp": 1577298900,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f",
"sinceTimestamp": 1577298985,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"sinceTimestamp": 1616134868,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x610B717796ad172B316836AC95a2ffad065CeaB4",
"sinceTimestamp": 1616123193,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xbB93e510BbCD0B7beb5A853875f9eC60275CF498",
"sinceTimestamp": 1616135758,
"extractor": "tornadoCashWithdrawal"
}
]
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "There is at least one practical way to participate in Tornado Cash using published source code that can be audited and run locally."
},
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid withdrawals.\n2. the [trusted setup](#trusted-setups) is compromised or all ceremony participants collude, allowing invalid withdrawals.\n
\n## Funds can be lost if\n1. a user loses the note secrets required to withdraw. \n
\n## Privacy can be lost if\n1. no relayer is available and the withdrawal must be submitted from an address that can be linked to the user (see [best practice page](/publications/privacy-best-practices) for more info).",
"tokens": [
{
"token": {
"address": "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643",
"iconUrl": "https://assets.coingecko.com/coins/images/9281/large/cDAI.png?1696509390",
"symbol": "cDAI",
"decimals": 8,
"priceId": "cdai",
"sinceTimestamp": 1577297782
},
"buckets": [
{
"id": "tornado-cDAI-5000",
"type": "denomination",
"label": "cDAI 5000",
"address": "eth:0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"sinceTimestamp": 1577297782,
"denomination": "5000",
"anonymitySet": {
"minimumAmounts": [
"500000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000"
}
}
},
{
"id": "tornado-cDAI-50000",
"type": "denomination",
"label": "cDAI 50000",
"address": "eth:0x03893a7c7463AE47D46bc7f091665f1893656003",
"sinceTimestamp": 1616155055,
"denomination": "50000",
"anonymitySet": {
"minimumAmounts": [
"5000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "5000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "5000000000000"
}
}
},
{
"id": "tornado-cDAI-500000",
"type": "denomination",
"label": "cDAI 500000",
"address": "eth:0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"sinceTimestamp": 1616135923,
"denomination": "500000",
"anonymitySet": {
"minimumAmounts": [
"50000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "50000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "50000000000000"
}
}
},
{
"id": "tornado-cDAI-5000000",
"type": "denomination",
"label": "cDAI 5000000",
"address": "eth:0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af",
"sinceTimestamp": 1616115450,
"denomination": "5000000",
"anonymitySet": {
"minimumAmounts": [
"500000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000000"
}
}
}
]
},
{
"token": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"iconUrl": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1576534688
},
"buckets": [
{
"id": "tornado-DAI-100",
"type": "denomination",
"label": "DAI 100",
"address": "eth:0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"sinceTimestamp": 1576534688,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
}
},
{
"id": "tornado-DAI-1000",
"type": "denomination",
"label": "DAI 1000",
"address": "eth:0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"sinceTimestamp": 1577297383,
"denomination": "1000",
"anonymitySet": {
"minimumAmounts": [
"1000000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000000"
}
}
},
{
"id": "tornado-DAI-10000",
"type": "denomination",
"label": "DAI 10000",
"address": "eth:0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"sinceTimestamp": 1616114822,
"denomination": "10000",
"anonymitySet": {
"minimumAmounts": [
"10000000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000000"
}
}
},
{
"id": "tornado-DAI-100000",
"type": "denomination",
"label": "DAI 100000",
"address": "eth:0x23773E65ed146A459791799d01336DB287f25334",
"sinceTimestamp": 1616115387,
"denomination": "100000",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000000"
}
}
}
]
},
{
"token": {
"address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
"symbol": "ETH",
"decimals": 18,
"priceId": "ethereum",
"sinceTimestamp": 1576523323
},
"buckets": [
{
"id": "tornado-ETH-0.1",
"type": "denomination",
"label": "ETH 0.1",
"address": "eth:0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"sinceTimestamp": 1576523323,
"denomination": "0.1",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000"
}
}
},
{
"id": "tornado-ETH-1",
"type": "denomination",
"label": "ETH 1",
"address": "eth:0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"sinceTimestamp": 1576534673,
"denomination": "1",
"anonymitySet": {
"minimumAmounts": [
"1000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000"
}
}
},
{
"id": "tornado-ETH-10",
"type": "denomination",
"label": "ETH 10",
"address": "eth:0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"sinceTimestamp": 1576536415,
"denomination": "10",
"anonymitySet": {
"minimumAmounts": [
"10000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000"
}
}
},
{
"id": "tornado-ETH-100",
"type": "denomination",
"label": "ETH 100",
"address": "eth:0xA160cdAB225685dA1d56aa342Ad8841c3b53f291",
"sinceTimestamp": 1577296976,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
}
}
]
},
{
"token": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1577298068
},
"buckets": [
{
"id": "tornado-USDC-100",
"type": "denomination",
"label": "USDC 100",
"address": "eth:0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"sinceTimestamp": 1577298068,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
}
},
{
"id": "tornado-USDC-1000",
"type": "denomination",
"label": "USDC 1000",
"address": "eth:0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D",
"sinceTimestamp": 1577298155,
"denomination": "1000",
"anonymitySet": {
"minimumAmounts": [
"1000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
}
}
]
},
{
"token": {
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "USDT",
"decimals": 6,
"priceId": "tether",
"sinceTimestamp": 1577298900
},
"buckets": [
{
"id": "tornado-USDT-100",
"type": "denomination",
"label": "USDT 100",
"address": "eth:0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"sinceTimestamp": 1577298900,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
}
},
{
"id": "tornado-USDT-1000",
"type": "denomination",
"label": "USDT 1000",
"address": "eth:0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f",
"sinceTimestamp": 1577298985,
"denomination": "1000",
"anonymitySet": {
"minimumAmounts": [
"1000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
}
}
]
},
{
"token": {
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1616123193
},
"buckets": [
{
"id": "tornado-WBTC-0.1",
"type": "denomination",
"label": "WBTC 0.1",
"address": "eth:0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"sinceTimestamp": 1616134868,
"denomination": "0.1",
"anonymitySet": {
"minimumAmounts": [
"10000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "10000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "10000000"
}
}
},
{
"id": "tornado-WBTC-1",
"type": "denomination",
"label": "WBTC 1",
"address": "eth:0x610B717796ad172B316836AC95a2ffad065CeaB4",
"sinceTimestamp": 1616123193,
"denomination": "1",
"anonymitySet": {
"minimumAmounts": [
"100000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
}
},
{
"id": "tornado-WBTC-10",
"type": "denomination",
"label": "WBTC 10",
"address": "eth:0xbB93e510BbCD0B7beb5A853875f9eC60275CF498",
"sinceTimestamp": 1616135758,
"denomination": "10",
"anonymitySet": {
"minimumAmounts": [
"1000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "Tornado cash has a TORN DAO, which does not have the authority to upgrade or modify existing pools in any way. However it controls a significant portion of the Tornado cash protocol and periphery, including:\n\n1. **Default router for deposits and withdrawals and the official registry of supported pools**. Malicious upgrades of these components could lead to users losing deposited tokens. \n2. **Standard UI IPFS hash registered on ENS** ([link](https://app.ens.domains/tornadocash.eth)). Malicious upgrades of these components could lead to users losing deposited tokens. \n3. **TORN token itself**. Malicious upgrades of the token could lead to token transfers being frozen. \n4. **Registered relayers**. Malicious upgrades of these components could remove all registered relayers, disrupting user-relayer coordination and complicating private withdrawals.",
"governanceInfo": {
"upgrades": {
"Normal upgrade path": "Lock and optionally delegate TORN in the [Governance contract](https://etherscan.io/address/0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce) → submit a proposal with at least **1.00 K TORN** → wait 1m 15s → vote for 5d (extended by 6h if the outcome changes during the last 1h) → pass with a simple majority and **100.00 K TORN** quorum → wait 2d → permissionless execution within 3d. The DAO controls protocol and periphery components, but cannot upgrade or modify existing pools.",
"Exit window": "**2d** for DAO-controlled changes — an accepted proposal remains timelocked for this period before it can be executed. Existing Tornado Cash pools are immutable and cannot be upgraded by the DAO."
},
"tokenGovernance": {
"Governance token": "`TORN`, 1 token = 1 vote, delegated. Total supply: **9.99 M TORN**, DAO-owned TORN (unavailable for voting): **4.73 M TORN**, staked for voting: **2.56 M TORN**, the rest is circulating supply.",
"Stake lock": "After voting or proposing, staked tokens are locked for **5d 6h** after proposal ends, preventing governance hopping.",
"Voting venue": "[Governance contract](https://etherscan.io/address/0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce) on Ethereum. Proposals are viewable on the voting tab of the frontend, see the note on secure frontend above.",
"Proposal threshold": "**1.00 K TORN** locked in governance.",
"Quorum": "**100.00 K TORN**, with a simple majority required for acceptance.",
"Execution model": "**Permissionless execution after an onchain vote and timelock.** An accepted proposal waits 2d and can then be executed by anyone within 3d. Proposal executable payload is committed in the `propose()` transaction."
}
}
}
}
+5 -8
+5 -8
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which funds leaving Ethereum come back as which, by passing through Zcash's shielded pool. Everything on Ethereum and NEAR is public."
},
"adversaries": [
{
"id": "publicObserver",
"label": "Public observer",
"description": "Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links.",
"examples": "A curious counterparty, an employer, a journalist."
},
{
"id": "chainAnalyst",
"label": "Chain analyst",
"description": "Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters.",
"examples": "Chain analytics firms, ZachXBT, data brokers."
},
{
"id": "networkObserver",
"label": "Network observer",
"description": "Sits between the user and the chain and sees web2 traffic only: RPC providers, relayers and broadcasters, indexers, ISPs. Learns IP addresses, timing, browser fingerprints, ciphertext and what becomes public. Assumes Tor to send transactions and, where the client has an RPC setting, an own node to read the blockchain.",
"examples": "Infura or Alchemy, a Tornado relayer, a wallet vendor selling telemetry, Google captcha or analytics in the dapp frontend."
},
{
"id": "privilegedInsider",
"label": "Privileged insider",
"description": "Holds a protocol operator role or receives keys or plaintext by design: upgrade admin, sequencer, decryption or view key holder, TEE vendor, association set provider, hosted prover, note registry.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and future cryptanalysis such as a large quantum computer that breaks elliptic-curve key exchange and pairings, but not hashes, symmetric ciphers or lattices.",
"examples": "First well-funded insiders, then everyone in a potential post-quantum future."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposure": "Nothing ties the ZEC that enters the shielded pool to the ZEC that leaves it. Everything up to the pool edge is public: the Ethereum deposit, the NEAR ledger entries that credit, swap and withdraw it, and the Zcash address and amount of every payout, which the bridge encrypts so that anyone can decrypt them.",
"advice": "Receive the ZEC straight into a fresh shielded address, as Zodl does per swap, and pay the return leg from the pool. The web app pays transparent addresses only, which adds a public shielding step.",
"interior": {
"sender": "private",
"recipient": {
"verdict": "atRisk",
"note": "Payout notes are decryptable by anyone, so a reused address ties payouts together."
},
"amount": "private",
"asset": {
"verdict": "exposed",
"note": "Only ZEC exists inside the pool; the assets swapped from and to are public on NEAR."
},
"linkage": "private"
},
"sources": [
{
"title": "Custodial bridge mint carries the Ethereum transaction hash in its memo",
"url": "https://nearblocks.io/address/eth.omft.near"
},
{
"title": "Withdrawal message names the target Zcash address",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/api/token_receiver.rs#L8-L16"
},
{
"title": "Payouts are encrypted to an all-zero outgoing viewing key",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/zcash_utils/orchard_policy.rs#L13-L63"
},
{
"title": "Zodl requests a fresh shielded address per swap",
"url": "https://github.com/zodl-inc/zodl-ios/blob/15f1eed4024d2c996b0d38c746d5aed235704a01/secant/Sources/Features/SwapAndPayForm/SwapAndPayStore.swift#L943"
},
{
"title": "Web app accepts transparent and TEX addresses only",
"url": "https://github.com/defuse-protocol/defuse-frontend/blob/752a7838d1e73b00e6965a9cb6a08af68b447e41/src/components/DefuseSDK/utils/validateAddress.ts#L131-L145"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposure": "The amount paid into the pool and the amount later sent to a bridge deposit address are both public with their timing, so a round trip of similar size within hours pairs them. Deposit addresses are fresh per quote but spent together with the bridge change address, so every exit is attributable to NEAR Intents.",
"advice": "Hold the ZEC in the pool for days, split or merge it with other shielded funds, and swap back amounts that match no payout.",
"interior": {
"sender": "private",
"recipient": "atRisk",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Round-trip amount and timing across the pool edge pair a payout with a later deposit."
}
},
"sources": [
{
"title": "Kappos et al., An Empirical Analysis of Anonymity in Zcash (USENIX 2018)",
"url": "https://arxiv.org/abs/1805.03180"
},
{
"title": "Deposit address derived per quote from the deposit message",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/deposit_msg.rs#L49-L52"
},
{
"title": "Bridge change address and UTXO set",
"url": "https://nearblocks.io/address/zcash-connector.bridge.near"
}
],
"id": "chainAnalyst",
"value": "Link private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposure": "Zodl syncs through public lightwalletd servers, which see which transactions the wallet fetches and broadcasts, and so the payout and the later exit of the same wallet. Tor covers these calls and the swap requests but is off by default and does not cover block sync. Ethereum RPCs and NEAR relayers see only what is public anyway.",
"advice": "Turn on Tor in Zodl before the first swap, pin one server or your own lightwalletd in manual mode, and use a separate wallet account per round trip.",
"exposure": "Zodl syncs through public lightwalletd servers, which see which transactions the wallet fetches and broadcasts, and so the payout and the later exit of the same wallet. Tor covers these calls and the swap requests including session isolation but is opt-in and does not cover block sync.",
"advice": "Turn on Tor in Zodl before the first swap.",
"interior": {
"sender": "private",
"recipient": "atRisk",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "The lightwalletd server sees the wallet fetch the payout and later broadcast the exit."
}
},
"sources": [
{
"title": "Tor is off unless the user enables it",
"url": "https://github.com/zodl-inc/zodl-ios/blob/15f1eed4024d2c996b0d38c746d5aed235704a01/secant/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift#L28"
},
{
"title": "Default lightwalletd endpoint",
"url": "https://github.com/zodl-inc/zodl-ios/blob/15f1eed4024d2c996b0d38c746d5aed235704a01/secant/Sources/Dependencies/ZcashSDKEnvironment/ZcashSDKEnvironmentInterface.swift#L21"
},
{
"title": "Zashi 2.1: which calls Tor covers",
"url": "https://electriccoin.co/blog/zashi-2-1-enhanced-privacy-with-tor-beta/"
},
{
"title": "Hornby, Fixing Privacy Problems in the Zcash Light Wallet Protocol",
"url": "https://defuse.ca/downloads/Fixing%20Privacy%20Problems%20in%20the%20Zcash%20Light%20Wallet%20Protocol.pdf"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "The Operator runs the 1Click API, the Ethereum custody and the screening, so both legs sit in its logs with the IP address and wallet identifiers of each request, plus the fresh Zcash refund address Zodl attaches to every swap out of ZEC. It has no key into the shielded pool, so joining the legs still needs IP, session or timing. It screens every address with KYT vendors, can lock any account and can hold bridged funds.",
"advice": "Use Tor for both legs, and do them from different sessions days apart with amounts that do not match.",
"exposure": "The Operator runs the 1Click bridge API, the Ethereum custody and the screening, so both legs sit in its logs with the IP address and wallet identifiers of each request, plus the fresh Zcash refund address Zodl attaches to every swap out of ZEC. It has no key into the shielded pool, so joining the legs still needs IP or timing. It screens every address with KYT vendors, can lock any account and can hold bridged funds.",
"advice": "Always use the Zodl Tor feature, wait some time in the shielded pool and use amounts that do not match.",
"interior": {
"sender": "private",
"recipient": {
"verdict": "atRisk",
"note": "The 1Click API receives a fresh refund address of the Zodl account for every swap out of ZEC."
},
"recipient": "private",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "IP address, partner key and timing of both legs sit in one operator's logs."
}
},
"sources": [
{
"title": "1Click terms: IP and wallet data, screening, freezes",
"url": "https://docs.near-intents.org/security-compliance/terms-of-service"
},
{
"title": "Screening providers",
"url": "https://docs.near-intents.org/security-compliance/risk-and-compliance"
},
{
"title": "Any account can be locked by DAO or locker role",
"url": "https://github.com/near/intents/blob/fa44ede9e874931c39a6c4246de82bc40f4f8d99/defuse/src/contract/accounts/force.rs#L21-L33"
},
{
"title": "Owner-only mint of the wrapped Ethereum assets",
"url": "https://github.com/near/intents/blob/fa44ede9e874931c39a6c4246de82bc40f4f8d99/poa-token/src/contract.rs#L79-L81"
},
{
"title": "Zodl sends its partner key and refund address with each quote",
"url": "https://github.com/zodl-inc/zodl-ios/blob/15f1eed4024d2c996b0d38c746d5aed235704a01/secant/Sources/Dependencies/SwapAndPay/sources/Near1Click.swift#L313-L344"
},
{
"section": "upgrades-and-governance"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "bad",
"exposure": "All of an account's rotated addresses share one incoming viewing key on the Pallas curve, and every payout address is public on NEAR. A quantum computer recovers that key from any one address and decrypts every note the account ever received, including the change notes of its exits, which joins both legs of every round trip. Ironwood's quantum-recoverable notes protect funds, not privacy.",
"advice": "Use a separate Zodl account per round trip, so one recovered key exposes only that trip.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Spends are found through the change notes they pay back to the same account."
},
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Orchard key agreement on the Pallas curve",
"url": "https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement"
},
{
"title": "ZIP 2005: quantum recoverability, not quantum privacy",
"url": "https://zips.z.cash/zip-2005"
},
{
"title": "Payouts are encrypted to an all-zero outgoing viewing key",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/zcash_utils/orchard_policy.rs#L13-L63"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
}
]
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "The anonymity set is Zcash's Ironwood shielded pool, which lives outside the chains L2BEAT indexes. Payout and deposit amounts are public at the pool edge, so the effective set behind an exit is the pool entries (public on NEAR) that could match it in amount and time."
},
"attributes": [
{
"id": "bridged",
"label": "Bridged",
"description": "Funds leave Ethereum through a bridge and gain their privacy on another chain."
},
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit funds which withdrawal."
},
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The Verifier can be upgraded at once by 4/5 NEAR Intents DAO members, the Zcash bridge by 2/5 Rainbow Bridge DAO members with no verified delay, and the Ethereum-side funds sit in an operator-controlled EOA, so there is no window to leave before a change takes effect.",
"walkawayTest": {
"passed": false,
"reason": "Ethereum deposits and withdrawals are executed off-chain by the custodial bridge operator, and Zcash withdrawals need a whitelisted relayer to trigger the MPC signature. Without the operators, only Zcash deposits that were never credited can be refunded, after a two-day timelock."
}
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The Verifier, the custodial bridge token, the Zcash bridge and the Zodl wallet are open source, but the bridge back end, the 1Click API and the solver relay are closed services that every Ethereum leg depends on."
},
"riskSummary": "## Funds can be stolen if\n1. the custodial bridge operator's single mint key or its Ethereum treasury key is compromised.\n2. 4/5 NEAR Intents DAO council members upgrade the Verifier maliciously.\n3. 2/5 Rainbow Bridge DAO council members upgrade the Zcash connector, its token or the light client maliciously.\n4. 9/15 NEAR MPC nodes collude to sign Zcash transactions from bridge addresses.\n
\n## Funds can be frozen if\n1. the DAO or one of the two single-key account lockers freezes the user's account in the Verifier.\n2. the custodial bridge operator freezes a deposit or withdrawal for a compliance review.\n3. the whitelisted relayers stop proving Zcash deposits or triggering Zcash withdrawals.\n
\n## Privacy can be lost if\n1. the ZEC is swapped back in an amount and at a time that matches the payout; both are public at the edge of the shielded pool.\n2. the same Zcash address receives more than one payout, since every payout note is decryptable by anyone.\n3. the operator or its screening vendors join the two legs through IP address, session or partner key. Tor is off by default in Zodl.\n4. the lightwalletd server links the wallet's fetch of the payout transaction with its later broadcast of the exit.\n5. elliptic-curve cryptography is broken: all of an account's addresses share one incoming viewing key, and the addresses are public.",
"tokens": [],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "The route touches three governed systems. None of them has an onchain delay that a user could rely on, and the Ethereum side has no contracts at all.\n\n### NEAR Intents Verifier (`intents.near`)\n\nThe Verifier is a NEAR contract with `near-plugins` roles. `intents.sputnik-dao.near`, a Sputnik DAO with five council members and a 4/5 threshold, holds the `DAO` role and is super admin, so it can grant every other role. Upgrades go through `ctl-intents.near`, a controller contract whose only admin is the same DAO. A passed proposal deploys the new code at once. Six accounts hold `PauseManager` and can halt all swaps and withdrawals. Nobody holds `UnpauseManager`, so only the DAO can resume. Two EOAs each hold `UnrestrictedAccountLocker` and can lock any user account, which stops its intents and withdrawals while deposits keep arriving, only the DAO can unlock.\n\n### Custodial \"PoA\" bridge (`omft.near`, `eth.omft.near`)\n\nDespite the name, nothing here is a proof-of-authority consensus or a multisig. The `poa-factory` contract `omft.near` deploys one token per bridged asset and mints on the permission of `TokenDepositer`: `bridge-mng.near`, an EOA, and `int-mnt-dao.sputnik-dao.near`, a three-member DAO whose members can each execute a call alone. The same DAO as above is super admin. Nothing on NEAR verifies that a mint is backed. On Ethereum the funds sit in the escrow [`0x2CfF…2680`](https://etherscan.io/address/0x2CfF890f0378a11913B6129B2E97417a2c302680) and in per-quote deposit accounts, all plain EOAs.\n\n### Zcash connector (`zcash-connector.bridge.near`, `zec.omft.near`, `zcash-client.bridge.near`)\n\nThe connector, the token's controller and the light client are all governed by `rainbowbridge.sputnik-dao.near`, a Sputnik DAO with a 2/5 threshold. `bridge-ops.near` (five EOAs) can stage code, pause the connector and run migrations; `pm.bridge.near` can pause. Deposits are credited only after `omni-relayer.bridge.near` or `intents-relayer.near` submit an inclusion proof against the light client, whose headers are relayed by `zcash-relayer.near`. Withdrawals are signed by the NEAR MPC network `v1.signer` (9/15) on request of a whitelisted relayer. A user cannot trigger the signature alone. The one permissionless path is a refund of a deposit that was never credited, executable by anyone after a two-day timelock (fourteen days when the relayer has flagged the request). Bridge fees are 0.0001 ZEC minimum per deposit and withdrawal, adjustable by the DAO."
},
"zkCatalogId": "zcash"
}