0c3a841a (main)
and
2ca75b87 (PR)
+214 -5
+214 -5
{
"adversaries": {
"promise": {
"protects": "recipient",
"text": "Hides which account a receiving address belongs to. The payer knows the address it was given; sender, asset, amount and where the funds go next 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "A fresh receiving address has no public derivation linking it to the recipient account, but its funds can be followed through later sends and change outputs. Combining addresses exposes their joint use, and EIP-7702 delegation reveals the account implementation without proving who owns the address.",
"advice": "Generate a fresh address for each receive, spend address-held balances separately with Advanced Control, and send to destinations with no public link to you.",
"sources": [
{
"title": "Address derivation from server-held ephemeral material",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/shared/genStealthAddress.ts#L17-L58"
},
{
"title": "Input selection, visible change and EIP-7702 execution",
"url": "https://clkd.xyz/docs/how-it-works#sending-funds"
},
{
"title": "Quote API: spendableAddresses and reusable singleAddress mode",
"url": "https://clkd.xyz/openapi.json"
}
],
"id": "publicObserver",
"value": "Recipient private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "Spending through shared execution infrastructure makes service use recognizable, which narrows the anonymity set to Cloaked users. No registry or announcement maps accounts to addresses, but timing, distinctive amounts, recurring counterparties and consolidation can identify or cluster recipients. A send draws from several addresses by default and leaves a change output that links them.",
"advice": "Space out related payments and check whether amounts or recurring patterns identify you. Follow your own change outputs to see what a counterparty can trace.",
"sources": [
{
"title": "Documented transaction links and change handling",
"url": "https://clkd.xyz/docs/how-it-works#what-is-visible-onchain"
},
{
"title": "Deterministic ephemeral keys stay offchain",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/shared/deriveDeterministicEphemeralKey.ts#L35-L68"
},
{
"title": "API permits repeated use; receive-once/spend-once is not enforced",
"url": "https://clkd.xyz/openapi.json"
}
],
"id": "chainAnalyst",
"value": "Recipient at risk",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposure": "Every spend from the hosted client goes to a third-party relay, Porto, with the stealth address, destination and amount, which clusters your addresses per session even over Tor. The client is closed source, so what else reaches the RPC, hosting and analytics providers named in the privacy policy cannot be verified. Only the recovery tool runs without any server.",
"advice": "Derive your address keys with the recovery tool, which makes no network calls, and spend them from a wallet on your own node over Tor. Fund gas for token transfers from a fresh wallet, since sponsored execution is only available through the hosted client.",
"sources": [
{
"title": "Client sends prepareCalls with address, recipient and amount to Porto RPC",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/README.md#architecture"
},
{
"contract": "OffchainResolver",
"title": "CCIP-Read forwards the ENS query and verifies the returned answer"
},
{
"title": "Disclosed metadata, relay data and external service providers",
"url": "https://clkd.xyz/docs/privacy"
},
{
"title": "Recovery derives address keys locally without API calls",
"url": "https://github.com/cloakedxyz/clkd-recovery/blob/b432a33873e2cfa4769807f8c95ed20a67234eed/src/lib/deriveKeys.ts#L31-L68"
},
{
"title": "Recovered keys are spent from any wallet; token sends need gas",
"url": "https://github.com/cloakedxyz/clkd-recovery/blob/b432a33873e2cfa4769807f8c95ed20a67234eed/src/components/PostRecoveryGuide.tsx#L81-L106"
}
],
"id": "networkObserver",
"value": "Recipient at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "Cloaked holds the viewing key, so it can regenerate every address and tie its activity to your account. Its Incognito relay states that it retains deposit-to-withdrawal associations, and the hosted wallet code handles your spending secrets.",
"advice": "Verify address derivations and transactions with an inspected local client. This protects your spending keys but does not remove Cloaked's view.",
"sources": [
{
"title": "Viewing key and public spending key shared with the server",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/client/deriveServerBoundKeys.ts#L16-L53"
},
{
"title": "Account-scoped addresses, quotes, submissions and pool state",
"url": "https://clkd.xyz/openapi.json"
},
{
"title": "Privacy policy: retained pool associations and account data",
"url": "https://clkd.xyz/docs/privacy"
},
{
"contract": "OffchainResolver",
"title": "Signature and expiry checks do not prove recipient control"
},
{
"section": "permissions",
"title": "Owner can replace gateway and signer"
},
{
"section": "upgrades-and-governance",
"title": "Hosted-client trust boundary"
}
],
"id": "privilegedInsider",
"value": "Recipient exposed",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposure": "No announcement is published, so a quantum computer cannot replay address derivations from the chain alone. The viewing key held by Cloaked exposes the history regardless. Accounts created from a wallet signature reduce to that wallet's key plus a four-digit PIN. Passkey PRF secrets do not follow from breaking the passkey.",
"advice": "Create your account with the passkey PRF setup, not from a wallet signature. Treat the address history shared with Cloaked as permanently disclosed.",
"sources": [
{
"title": "Stealth signing keys mix the spending key with a hashed secret",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/client/genStealthPrivateKey.ts#L17-L34"
},
{
"title": "Four-digit PIN and wallet address determine the signed message",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/client/genCloakedMessage.ts#L15-L48"
},
{
"title": "Signature components are hashed into the account keys",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/client/genKeysFromSignature.ts#L23-L42"
},
{
"title": "Independent secrets, including two WebAuthn PRF outputs",
"url": "https://github.com/cloakedxyz/clkd-stealth/blob/9eb359efdd4ea31f5504bf81e57dcfe6f966dc14/src/client/genKeys.ts#L7-L67"
},
{
"title": "FIDO hmac-secret uses a separate random credential secret",
"url": "https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-hmac-secret-extension"
},
{
"title": "Operator data and retention",
"url": "https://clkd.xyz/docs/privacy"
}
],
"id": "futureAdversary",
"value": "Recipient at risk",
"alsoExposed": []
}
}
},
"attributes": [
{
"id": "stealthAddresses",
"label": "Stealth addresses",
"description": "Uses fresh, one-time recipient addresses to obscure who controls the receiving address."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "Under the documented key model, existing stealth balances are held in user-controlled EOAs and can be recovered with the published client-side recovery tool. The hosted service cannot spend them without obtaining client-side key material.",
"walkawayTest": {
"passed": false,
"reason": "New payment-address generation, ENS resolution, address indexing, transaction construction, and relaying depend on the hosted Cloaked service."
}
},
"privacy": {
"value": "Admin view key",
"sentiment": "bad",
"description": "Cloaked holds a private, per-account viewing capability that lets its service derive and link every stealth address for that account. Its relay also sees both sides of Privacy Pools activity. This centralized visibility does not provide spending authority, and users can keep spending keys outside the closed-source wallet by using an inspected local client."
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The production web wallet is closed source and cannot be self-hosted. The published derivation code and API schema can nevertheless be used to build a local client that creates an account, derives keys, and signs and submits payments through the hosted API. The backend implementations of the API, ENS gateway, indexer, and relay service are not published, so the complete service cannot be reproduced."
},
"riskSummary": "## Funds can be stolen if\n1. a user relies on the hosted wallet and it is malicious or compromised and exfiltrates derived spending keys. The production wallet is closed source and has no published reproducible build, so users cannot inspect its source or verify the code being served.\n2. the accepted ENS signer returns an attacker-controlled payment address for a Cloaked name. The resolver authenticates the answer but does not prove that the intended recipient controls it.\n
\n## Funds can be lost if\n1. a user loses the passkey and encrypted backup, or the wallet and PIN, needed to re-derive their private spending keys.\n2. the service and client derive different address data and a payment is sent to an address for which the user cannot recreate the private key.\n
\n## Privacy can be lost if\n1. Cloaked, or an attacker who obtains its viewing data, uses the service's viewing capability and index to link an account's stealth addresses and onchain activity.\n2. spending from several stealth addresses together, reusing destinations, or recognizable timing and amounts links otherwise separate payments onchain.\n3. a user relies on the Incognito balance, because Cloaked's relay observes the association between Privacy Pools deposits and withdrawals even though it is hidden from public onchain observers.",
"summaryTrackedItemName": "address",
"tokens": [],
"upgradesAndGovernance": {
"content": "Cloaked has no public onchain governance process for its hosted wallet, API, indexer, ENS gateway, or relay. These components can change without an onchain notice period. The production wallet is closed source and has no published reproducible build, so users who rely on it cannot inspect its source, verify which client code is deployed, or determine whether an update preserves the client-side spending-key boundary. Users can bypass the hosted frontend with an inspected local client, but this does not reproduce the other hosted components or prevent them from changing.\n\nThe deployed ENS `OffchainResolver` code is immutable, but its owner can immediately replace the gateway URLs and accepted signer or transfer ownership. The accepted signer can authenticate any offchain ENS answer, including a payment address. The resolver callback verifies only the signature and expiry; it does not verify stealth-address derivation or recipient control. The resolver owner and signer are currently the same EOA.\n\nThese powers affect new address generation and ENS lookups. They do not let the owner or signer spend from an existing, correctly derived stealth address because its private spending key remains with the user. Existing balances can be recovered independently with the published recovery client, but the full Cloaked experience does not remain available if the hosted service disappears."
}
}
+225 -5
+225 -5
{
"adversaries": {
"promise": {
"protects": "recipient",
"text": "Hides which account a receiving Safe belongs to. The payer knows the address it was given; sender, asset, amount and where the funds go next 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "A payment reaches a predicted Safe before it is deployed, with no onchain announcement linking it to the recipient account. Deployment later exposes the individual stealth owner and enabled modules, not the parent account.",
"advice": "Generate a fresh address for each receive, keep unrelated funds separate with labels, and send to destinations with no public link to you.",
"sources": [
{
"title": "Counterfactual receiving Safe and individual stealth signer",
"url": "https://docs.fluidkey.com/technical-documentation/technical-walkthrough/#3-stealth-accounts"
},
{
"title": "Safe owner and initialization data determine the address",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/predictStealthSafeAddress.ts#L32-L104"
},
{
"contract": "FluidkeyEarnModule",
"title": "AutoEarnExecuted exposes Safe, token and deposited amount"
},
{
"title": "Labels restrict which balances fund a send",
"url": "https://docs.fluidkey.com/readme/labels/"
}
],
"id": "publicObserver",
"value": "Recipient private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "Safe deployment and auto-earn events make service use recognizable, which narrows the anonymity set to Fluidkey users. Nothing onchain maps Safes to accounts, but timing, amounts, common destinations and reuse across chains can identify or cluster recipients, and a send draws from several Safes by default, which links them. The optional Hide Trail routes through Houdini and two exchanges, a separate service with its own trust assumptions.",
"advice": "Label payments so a send draws from one Safe. Check related activity across chains and space out distinctive payments.",
"sources": [
{
"title": "onInstall records its caller; an event alone is not proof of a Fluidkey user",
"url": "https://github.com/fluidkey/fluidkey-earn-module/blob/122cde19940d06b94c0027f4cd2e22e7fa19129a/src/FluidkeyEarnModule.sol#L243-L260"
},
{
"title": "Different auto-earn versions use different initialization data",
"url": "https://docs.fluidkey.com/technical-documentation/stealth-account-initdata/"
},
{
"title": "Shared derivation path permits the same addresses across chains",
"url": "https://docs.fluidkey.com/technical-documentation/technical-walkthrough/#3a-stealth-signer-derivation"
},
{
"title": "Hide Trail integration",
"url": "https://docs.fluidkey.com/readme/advanced-privacy/"
}
],
"id": "chainAnalyst",
"value": "Recipient at risk",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposure": "The hosted client is closed source, so which RPC and analytics providers see your Safe list cannot be verified. Paying to your fkey.id name hands the name and the fresh address to whoever resolves ENS for the payer, often the payer's RPC provider. Only the open kit and the recovery app can be pointed at your own node.",
"advice": "Derive addresses with the kit and hand them to payers directly instead of the ENS name. Read balances with the recovery app on your own RPC, and deploy and spend from a fresh gas wallet, since the recovery app deploys Safes from the connected wallet.",
"sources": [
{
"contract": "OffchainResolver",
"title": "Offchain ENS gateway"
},
{
"title": "Local CREATE2 prediction needs no RPC",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/predictStealthSafeAddress.ts#L120-L180"
},
{
"title": "Recovery app accepts a custom RPC",
"url": "https://github.com/fluidkey/sara/blob/6ab939e176cbae60a33214d292070d6a1dfe9b30/src/components/RecoverAddressesJourneyStep.tsx#L468"
},
{
"title": "Recovery app deploys the Safe from the connected wallet",
"url": "https://github.com/fluidkey/sara/blob/6ab939e176cbae60a33214d292070d6a1dfe9b30/src/hooks/useDeployStealthSafe.ts#L63-L99"
},
{
"title": "Disclosed service providers and transport encryption",
"url": "https://www.fluidkey.com/privacy"
}
],
"id": "networkObserver",
"value": "Recipient at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "Fluidkey holds the viewing key, so it can regenerate every address and tie its activity to your account. Optional identity-verified services add identity attributes, and Hide Trail adds route knowledge at Houdini and each exchange.",
"advice": "Verify address derivations and transactions with an inspected local client. This protects your spending keys but does not remove Fluidkey's view.",
"sources": [
{
"title": "Private viewing key shared with the service",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/extractViewingPrivateKeyNode.ts#L14-L30"
},
{
"title": "Ephemeral key plus public spending key generates each signer",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/generateStealthAddresses.ts#L14-L49"
},
{
"contract": "OffchainResolver",
"title": "ENS signature authenticates the service, not recipient control"
},
{
"section": "permissions"
},
{
"section": "upgrades-and-governance",
"title": "Hosted-client trust boundary"
},
{
"title": "Optional verification results and identity attributes",
"url": "https://www.fluidkey.com/privacy"
},
{
"title": "Hide Trail and its exchange intermediaries",
"url": "https://docs.fluidkey.com/readme/advanced-privacy/"
}
],
"id": "privilegedInsider",
"value": "Recipient exposed",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposure": "No announcement is published, so a quantum computer cannot replay address derivations from the chain alone. The viewing key held by Fluidkey exposes the history regardless. Accounts created from a wallet signature reduce to that wallet's key plus a four-digit PIN.",
"advice": "Create your account with independently generated keys, not from a wallet signature. Treat the viewing key shared with Fluidkey as permanently disclosed.",
"sources": [
{
"title": "Stealth signer depends on a hashed shared secret",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/generateStealthPrivateKey.ts#L11-L21"
},
{
"title": "Signature halves generate the viewing and spending keys",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/generateKeysFromSignature.ts#L11-L36"
},
{
"title": "Wallet address and PIN determine the key-generation message",
"url": "https://github.com/fluidkey/fluidkey-stealth-account-kit/blob/2a4ccfafef127165c11ba16fc16235c919698ec2/src/utils/generateFluidkeyMessage.ts#L10-L32"
},
{
"title": "Recovery supports a four-digit PIN, defaulting to 0000",
"url": "https://github.com/fluidkey/sara/blob/6ab939e176cbae60a33214d292070d6a1dfe9b30/src/components/GenerateKeysJourneyStep.tsx#L28-L90"
},
{
"title": "Distinct web embedded-wallet and mobile device-key setups",
"url": "https://docs.fluidkey.com/readme/account-set-up/"
},
{
"title": "Operator data retention",
"url": "https://www.fluidkey.com/privacy"
}
],
"id": "futureAdversary",
"value": "Recipient at risk",
"alsoExposed": []
}
}
},
"attributes": [
{
"id": "stealthAddresses",
"label": "Stealth addresses",
"description": "Uses fresh, one-time recipient addresses to obscure who controls the receiving address."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
},
{
"id": "defi",
"label": "DeFi",
"description": "Interop with DeFi vaults through stealth Safes."
}
],
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "Under the documented key model, existing stealth balances are held in user-controlled Safes and can be recovered with the published client-side recovery tool and original Safe initialization parameters. Auto-earn positions remain subject to the withdrawal conditions and risks of their underlying vaults.",
"walkawayTest": {
"passed": false,
"reason": "New payment-address generation through the app and ENS, address indexing, transaction construction, and relaying depend on the hosted Fluidkey service."
}
},
"privacy": {
"value": "Admin view key",
"sentiment": "bad",
"description": "Fluidkey holds a private, per-account viewing capability that lets its service derive and link the stealth addresses generated for that account."
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The production web wallet is closed source and cannot be self-hosted. The published derivation code and recovery client can nevertheless be used to derive keys, verify service-generated receiving addresses, and recover funds independently. A local client can also register and authenticate through the hosted API. The backend implementations of the API, ENS gateway, indexer, and relay service are not published, so the complete service cannot be reproduced."
},
"riskSummary": "## Funds can be stolen if\n1. a user relies on the hosted wallet and it is malicious or compromised and exfiltrates derived spending keys. The production wallet is closed source and has no published reproducible build, so users cannot inspect its source or verify the code being served.\n2. an accepted ENS signer returns an attacker-controlled payment address for a Fluidkey name. The resolver authenticates the answer but does not prove that the intended recipient controls it.\n3. the hosted service supplies malicious transaction data and the client signs it without independently checking the destination, amount, and calls being authorized.\n
\n## Funds can be lost if\n1. a user loses the wallet, PIN, login, or backup needed to recover their private spending keys.\n2. the service and client derive different address data or Safe initialization parameters and a payment is sent to an address that the user cannot recover.\n3. an auto-earn vault loses funds or cannot honor withdrawals, or a service used by Hide Trail fails to return the funds routed through it.\n
\n## Privacy can be lost if\n1. Fluidkey, or an attacker who obtains its viewing data, uses the service's viewing capability and index to link an account's stealth addresses and onchain activity.\n2. spending from several stealth addresses together, reusing destinations, or recognizable timing and amounts links otherwise separate payments onchain.\n3. a user relies on Hide Trail and the swap service or participating exchanges use their transfer records to link activity that is obscured from public onchain observers.",
"summaryTrackedItemName": "address",
"tokens": [],
"upgradesAndGovernance": {
"content": "Fluidkey has no public onchain governance process for its hosted wallet, API, indexer, ENS gateway, or relay. These components can change without an onchain notice period. The production wallet is closed source and has no published reproducible build, so users who rely on it cannot inspect its source, verify which client code is deployed, or determine whether an update preserves the client-side spending-key boundary. Users can bypass the hosted frontend with an inspected local client, but this does not reproduce the other hosted components or prevent them from changing.\n\nThe deployed ENS `OffchainResolver` code is immutable, but its owner can immediately replace the gateway URL, add or remove accepted signers, or transfer ownership. An accepted signer can authenticate any offchain ENS answer, including a payment address. The resolver callback verifies only the signature and expiry. It does not verify stealth-address derivation or recipient control. The resolver is owned by a 3/5 Safe.\n\nThese powers affect new address generation and ENS lookups. They do not let the resolver owner or signers spend from an existing, correctly derived stealth Safe because its private owner key remains with the user. Existing balances can be recovered independently with the published recovery client and the original Safe initialization parameters, but the full Fluidkey experience does not remain available if the hosted service disappears.\n\nThe Fluidkey Earn module is separate from the resolver and has immutable code. Its owner is a 2/3 Safe on each tracked chain. Its owner can publish new vault configurations and manage authorized relayers. Relayers can initiate deposits into a Safe's selected configuration and add or remove relayers. Each configuration is identified by a hash of its contents, so adopting a different vault set requires a call from the Safe. Users can disable the module through their Safe. The governance and withdrawal conditions of the underlying vaults remain separate dependencies."
}
}
+299 -12
+299 -12
{
"adversaries": {
"promise": {
"protects": "amount",
"text": "Hides the amount and owner of each note. Which notes fund which, and every deposit and withdrawal, 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "warning",
"exposure": "Only the amount and owner of a note are hidden. Every transaction names the notes it spends and creates, all blocks and proofs are served openly, and each deposit or withdrawal shows address and amount and points at one node of that graph. The per-transaction proofs use a flavor whose authors document that it is not zero-knowledge; how much of a note it leaks is not established.",
"advice": "Withdraw to an address that cannot be tied to you, and never an amount that matches a deposit.",
"interior": {
"sender": {
"verdict": "private",
"note": "The owner key is hidden; the spent note is a public graph node."
},
"recipient": {
"verdict": "private",
"note": "Recipient and change outputs are indistinguishable."
},
"amount": {
"verdict": "atRisk",
"note": "Protected by a proof flavor documented to leak witness data."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "RollupV1"
},
{
"title": "UTXO circuit: spent and created commitments are public, no nullifier",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/noir/utxo/src/main.nr#L9-L15"
},
{
"title": "Burn hash is the first consumed commitment",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/noir/utxo/src/main.nr#L66-L71"
},
{
"title": "Client proofs use the non-ZK Honk flavor",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/barretenberg-rs/src/binding.rs#L109-L118"
},
{
"title": "Honk docs: the base flavor leaks witness information",
"url": "https://github.com/AztecProtocol/aztec-packages/blob/58bf73a289f7da78c8920c9c73604a8e5d105025/barretenberg/cpp/src/barretenberg/ultra_honk/README.md#zero-knowledge"
},
{
"title": "Public transactions endpoint serves every proof",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/node/src/rpc/routes/configure.rs#L19-L25"
},
{
"title": "Docs describe nullifiers the deployed circuit does not have",
"url": "https://docs.payy.network/protocol/privacy-layer/nullifiers"
}
],
"id": "publicObserver",
"value": "Amounts at risk",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "bad",
"exposure": "Along a path without splits or merges the public deposit and withdrawal amounts fix every amount in between, and the graph shows who paid whom end to end. Each wallet has one fixed deposit address, so all its deposits link to each other and to whoever funded them.",
"advice": "Keep funds in the network across many transfers; only a split or merge stops amounts from being inferred. Fund the deposit address from a wallet that is not yours to keep.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Nodes are labelled by their terminal deposit and withdrawal addresses."
},
"recipient": "atRisk",
"amount": {
"verdict": "atRisk",
"note": "Inferable along a linear path from a deposit to a withdrawal."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Public block API",
"url": "https://validators.mainnet.payy.network/v0/blocks"
},
{
"title": "One deposit address stored per wallet",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/database/src/schema.rs#L464-L481"
},
{
"title": "Deposit relayed from the deposit address with a signed USDC authorization",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/guild-interface/src/mint.rs#L56-L90"
},
{
"title": "Wallet FAQ: the deposit address forwards to the bridge",
"url": "https://docs.payy.network/payy-wallet/payy-wallet-faq"
}
],
"id": "chainAnalyst",
"value": "Amounts exposed",
"alsoExposed": [
"sender",
"recipient"
]
},
"networkObserver": {
"sentiment": "bad",
"exposure": "Proofs are built on your device and the app talks only to the operator's servers. The payment-link website is different: it loads a third-party analytics script that records the page URL, which holds the redeem secret of the link, and reports a device fingerprint to the backend.",
"advice": "Redeem payment links in the app, not in a browser.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "A link opened in a browser hands its redeem secret to the analytics provider."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Analytics with default settings on the link website",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/app/packages/link/src/config/posthog.ts#L10-L18"
},
{
"title": "Send links carry the secret in the URL fragment",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/parse-link/src/lib.rs#L50-L56"
},
{
"title": "Device fingerprint and analytics id posted to the backend",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/app/packages/link/src/hooks/useStoreRedirect.ts#L30-L58"
},
{
"title": "Node logs every submitted proof",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/node/src/rpc/routes/txn.rs#L30-L34"
}
],
"id": "networkObserver",
"value": "Amounts exposed",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "One company runs the only validator and prover, the node, the note registry, the deposit relayer and the KYC checks. Its backend ties each session to a wallet address and keeps, per wallet, the deposit address, KYC record, country, IP country and the commitment and owner of every note the app reports; its diagnostics table has a field for the recovery phrase. For payment links and fiat ramps it holds the notes' private keys and can read and spend them. It can drop your withdrawals, and its owner can rewrite the state root with no delay.",
"advice": "Use only ordinary transfers for anything you want to keep private. Links, ramps and the card hand the operator the note keys.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Hidden for ordinary transfers; the backend holds the keys of link and ramp notes."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "RollupV1",
"title": "setRoot, provers, validators, burn substitutors"
},
{
"section": "permissions"
},
{
"title": "Session bound to a wallet address by signature",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/guild-interface/src/auth.rs#L1-L16"
},
{
"title": "Registry entries are keyed by recipient key and posted authenticated",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/guild-client-http/src/registry.rs#L45-L56"
},
{
"title": "Link and ramp notes are created with their private key on the backend",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/guild-interface/src/notes/create.rs#L36-L45"
},
{
"title": "Wallet table: deposit address, KYC, country, IP country",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/database/src/schema.rs#L464-L481"
},
{
"title": "Wallet notes table: commitment and owner per note",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/database/src/schema.rs#L452-L462"
},
{
"title": "Diagnostics table with a recovery phrase field",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/database/src/schema.rs#L36-L48"
}
],
"id": "privilegedInsider",
"value": "Amounts exposed",
"alsoExposed": [
"sender",
"recipient"
]
},
"futureAdversary": {
"sentiment": "bad",
"exposure": "Every per-transaction proof is public forever and not zero-knowledge, so whatever it leaks stays available to future compute, and a discrete-log break opens the commitments inside it. Registry ciphertexts use elliptic-curve key exchange; if the operator retains them, a quantum computer opens every note delivered through it.",
"advice": "Receive notes in person rather than through the registry where the app allows it.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "If the registry is retained; its ciphertexts are not on Ethereum."
},
"recipient": "atRisk",
"amount": {
"verdict": "atRisk",
"note": "Proofs are public and not zero-knowledge; registry ciphertexts open if retained."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Registry encryption (X25519-XSalsa20-Poly1305)",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/encrypt/src/asymmetric.rs#L80-L81"
},
{
"title": "Client proofs use the non-ZK Honk flavor",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/barretenberg-rs/src/binding.rs#L109-L118"
},
{
"title": "Honk docs: ZK flavors add masking the base flavor lacks",
"url": "https://github.com/AztecProtocol/aztec-packages/blob/58bf73a289f7da78c8920c9c73604a8e5d105025/barretenberg/cpp/src/barretenberg/ultra_honk/README.md#zero-knowledge"
},
{
"contract": "HonkVerifier",
"title": "Onchain verifier (ZK flavor)"
}
],
"id": "futureAdversary",
"value": "Amounts exposed",
"alsoExposed": [
"sender",
"recipient"
]
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "privateAmounts",
"label": "Private amounts",
"description": "Transfer amounts are private, while sender and recipient addresses remain public."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"detailedDescription": "Payy is a payments-focused ZK network, designed around private stablecoin transfers. Ethereum receives only state roots and the public deposit and withdrawal messages of each state update. Balances live offchain as UTXO-style notes whose commitments form a sparse Merkle tree maintained by the Payy validators. \n\n### Privacy considerations\n\nTo spend notes, the user's client generates a [Noir](https://noir-lang.org/) `utxo` proof that demonstrates ownership of the input notes and conservation of value; the public inputs of that proof are the commitments of the two notes consumed and the two notes created. Transfers therefore hide who paid whom and how much, but they do not hide which commitments were consumed: the deployed circuits do not use nullifiers, and a spend names its input commitments directly, which the node then marks as spent. Anyone with Payy block data can follow the resulting spend graph.\n\nBecause only commitments are stored in the tree, a recipient cannot reconstruct a received note from chain data alone: the sender must transmit the note contents out of band. In practice it is done by an encrypted registry operated by Payy, which stores the recipient's public key in the clear and serves each client the entries addressed to it. Thus it learns the delivery metadata even though it cannot read note contents. \n\n### Fees\n\nThe protocol charges no transfer or withdrawal fee.\n\n### Compliance\n\nThe deployed protocol enforces no compliance measures and contains no auditor key, view key or other mechanism that could retroactively decrypt past activity. Identity data is collected at the application layer: KYC is required for the Payy card and fiat ramps, but it is not necessary for using Payy L2.\n\n### Anonymity set\n\nPayy hides the contents of each note but not the links between notes, so its anonymity set should not be read as the set of all users. An observer of Payy block data sees a graph of opaque commitments; privacy rests on not knowing which address, asset or amount each node in that graph carries.",
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The PayyMultisig can instantly upgrade the Rollup contract, replace ZK verifiers and directly overwrite the state root, so users have no time to exit before a malicious change takes effect.",
"walkawayTest": {
"passed": false,
"reason": "Withdrawals must be included in a proven state update by the permissioned Payy operators. There is no mechanism for users to exit on Ethereum if the operators halt."
}
},
"noteDiscovery": {
"description": "Payy operator runs a centralized note discovery service, where the sender submits full transfer data encrypted to the receiver. The centralized operator learns which addresses receive notes and when, and who transacted with whom.\n\nThe protocol itself allows direct peer-to-peer note communication that bypasses the registry.",
"risks": [
"The Payy-operated note registry, note lookup and wallet backup services learn which addresses receive and hold which notes and can link senders to recipients. The same operator holds KYC data for users of the Payy card and fiat ramps, which can tie those addresses to real identities.",
"A compromise of a recipient key or of the encryption scheme exposes all note data ever stored in the encrypted registry."
]
},
"privacy": {
"value": "Linkable notes",
"sentiment": "bad",
"description": "Transactions within the Payy network do not hide the links between token transferred: anyone with Payy block data can reconstruct the spend graph and trace every withdrawal back to contributing deposits. Only the contents of each individual note (owner address, amount and asset) are hidden."
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The sources for UI / wallet are not published, so users have to rely on Payy operated software. Also, DA is not posted to identifiable public source, so L2 state could not be independently reproduced.\n\nThe contracts, Noir circuits and node software are published in the Payy repository (without any license) and can be built and run locally."
},
"riskSummary": "## Funds can be stolen if\n1. the proof system is broken and a malicious validator is registered, allowing forged spends or withdrawals.\n2. the PayyMultisig upgrades the Rollup contract or directly overwrites the state root to a malicious version.\n
\n## Funds can be lost if\n1. a user loses the note data or the keys required to spend their notes.\n2. the offchain transaction data becomes unavailable, as data availability is not verified on Ethereum.\n3. the operators stop including withdrawals in proven state updates, as there is no forced exit mechanism.\n
\n## Privacy can be lost if\n1. the Payy-operated note registry, note lookup or wallet backup services are compromised or their records are disclosed, revealing which addresses receive and hold which notes and who transacted with whom.",
"tokens": [],
"upgradesAndGovernance": {
"content": "All privileged functions are ultimately controlled by the PayyMultisig (2/4). It owns the ProxyAdmin, which can instantly upgrade the Rollup proxy, and it is the owner of the Rollup contract, allowing it to manage provers, validators, ZK verifiers, supported tokens and burn substitutors, and to directly overwrite the state root via setRoot(). There is no timelock or exit window on any of these actions."
},
"zkCatalogId": "barretenberg"
}
+243 -5
+243 -5
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides everything inside the ledger, including which deposits fund a withdrawal. Deposits and withdrawals 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "Transfers inside publish only encrypted notes. Deposits and withdrawals show address, token and amount. Each epoch's calldata pairs every exit with the nullifiers and the input/output shape of the transfer that funded it.",
"advice": "Exit through the operator's relay; a forced exit reveals your account and the notes you spend. A public gift exit names the destination.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "private",
"note": "Leaked for gateway DeFi legs."
},
"asset": {
"verdict": "private",
"note": "Leaked for gateway DeFi legs."
},
"linkage": {
"verdict": "private",
"note": "Epoch calldata ties each exit to the nullifiers and shape of one transfer."
}
},
"sources": [
{
"contract": "PrivacyBoost"
},
{
"title": "Epoch calldata: withdrawals, nullifiers and transfer shapes",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/23907eeebf0e50cd18da42a287671189e61ecb0f/contracts/src/interfaces/IPrivacyBoost.sol#L828-L852"
},
{
"title": "Forced withdrawal publishes account, destination and notes",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/23907eeebf0e50cd18da42a287671189e61ecb0f/contracts/src/interfaces/IPrivacyBoost.sol#L582-L592"
},
{
"title": "Gift claim and refund are indistinguishable",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/23907eeebf0e50cd18da42a287671189e61ecb0f/contracts/src/interfaces/IPrivacyBoost.sol#L694-L707"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "bad",
"exposure": "The anonymity set is small, so most withdrawals can be linked to their funding deposits by amount and timing.",
"advice": "There is currently no crowd to hide in. Keep funds inside and transfer often, withdraw amounts that match no deposit, and never exit to an address that has deposited.",
"interior": {
"sender": "atRisk",
"recipient": "atRisk",
"amount": {
"verdict": "atRisk",
"note": "Bounded by the public deposit and exit amounts."
},
"asset": "atRisk",
"linkage": {
"verdict": "atRisk",
"note": "The anonymity set is too small for care to hide the link."
}
},
"sources": [
{
"title": "Operator heartbeat address",
"url": "https://optimistic.etherscan.io/address/0x1b10c04536c01a51cb5d20cf3ac717047303d89c"
},
{
"contract": "PrivacyBoost",
"title": "Withdrawal fee in basis points"
}
],
"id": "chainAnalyst",
"value": "Link exposed",
"alsoExposed": [
"sender",
"recipient",
"amount",
"asset"
]
},
"networkObserver": {
"sentiment": "good",
"exposure": "All shielded actions go to the operator's server as an encrypted envelope, and note keys stay in a local vault. Only the operator's relay submits epochs onchain, so nothing of yours reaches a public node.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "private",
"linkage": "private"
},
"sources": [
{
"title": "SDK package, closed Rust core",
"url": "https://www.npmjs.com/package/@sunnyside-io/privacy-boost"
},
{
"title": "Server info endpoint",
"url": "https://optimism.privacyboost.io/api/v1/info"
},
{
"contract": "PrivacyBoost",
"title": "Single permitted relay"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "The operator runs the enclave that holds every transfer in plaintext and the key to every onchain note. The client fetches that key from the operator's web endpoint and the published SDK checks no enclave attestation, so a substituted key would read every note unnoticed. Appointed auditors can pull any account's history without consent, and the audit log is written by the operator's own unpublished code.",
"advice": "Treat everything in the pool as visible to the operator. A forced exit is the only path that does not need it, and it is public.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "AuditGateway"
},
{
"section": "permissions",
"title": "Admin and operator multisigs"
},
{
"title": "TEE key is fetched from the operator endpoint",
"url": "https://www.npmjs.com/package/@sunnyside-io/privacy-boost"
},
{
"title": "Notes wrap an ephemeral key for the TEE alongside the receiver",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/23907eeebf0e50cd18da42a287671189e61ecb0f/contracts/src/interfaces/IStructs.sol#L36-L56"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
"sender",
"recipient",
"amount",
"asset"
]
},
"futureAdversary": {
"sentiment": "bad",
"exposure": "Every note wraps its key to the enclave's long-lived elliptic-curve public key as well as the receiver's. A quantum computer, or a leak of that one key, decrypts the entire history.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "PrivacyBoost",
"title": "Ciphertext layout"
},
{
"title": "Every output and deposit ciphertext carries a TEE-wrapped key",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/23907eeebf0e50cd18da42a287671189e61ecb0f/contracts/src/interfaces/IStructs.sol#L36-L125"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
"sender",
"recipient",
"amount",
"asset"
]
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "tee",
"label": "TEE",
"description": "Privacy assumptions of this project are secured by trusted hardware."
},
{
"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."
}
],
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The pool and both registries sit behind transparent proxies whose ProxyAdmins are owned by the admin multisig, which can upgrade them with no delay. Users get no window to exit before a change takes effect.",
"walkawayTest": {
"passed": false,
"reason": "If the TEE operators disappear, no new deposits or private transfers can be processed and the system enters exit-only mode."
}
},
"privacy": {
"value": "Admin API",
"sentiment": "bad",
"description": "Registered 'auditors' can query the TEE's Audit API to retrieve the balance and transaction history of any address. These queries can be logged publicly on the AuditGateway smart contract, but there is no verifiable guarantee that all queries are logged."
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "ZK circuits guaranteeing user fund security are published and reproduced, however the TEE sources guaranteeing privacy are not yet published. TEE logic could not be verified for correctness."
},
"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 admin multisig deploys a malicious [upgrade](#upgrades-and-governance) or registers a malicious verifying key.\n
\n## Funds can be lost if\n1. a user loses their note secrets, or loses access to both the account-owner wallet and any usable authorization keys.\n
\n## Privacy can be lost if\n1. the TEE is compromised.\n2. a registered auditor fetches the user's balance and transaction history through the Audit API.\n3. a user exits through a forced withdrawal, which publicly links the spent notes and the withdrawal address to the account that registered the approval key.",
"tokens": [
{
"token": {
"address": "0x4200000000000000000000000000000000000006",
"iconUrl": "https://coin-images.coingecko.com/coins/images/2518/large/weth.png?1696503332",
"symbol": "WETH",
"decimals": 18,
"priceId": "weth",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-WETH",
"type": "pool",
"label": "WETH",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x4200000000000000000000000000000000000042",
"iconUrl": "/images/custom-tokens/optimism.png",
"symbol": "OP",
"decimals": 18,
"priceId": "optimism",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-OP",
"type": "pool",
"label": "OP",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
"iconUrl": "https://coin-images.coingecko.com/coins/images/35001/large/logo.png?1706959346",
"symbol": "USDT",
"decimals": 6,
"priceId": "bridged-usdt",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-USDT",
"type": "pool",
"label": "USDT",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-USDC",
"type": "pool",
"label": "USDC",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x68f180fcCe6836688e9084f035309E29Bf0A2095",
"iconUrl": "https://coin-images.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "wBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-wBTC",
"type": "pool",
"label": "wBTC",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb",
"iconUrl": "https://coin-images.coingecko.com/coins/images/18834/large/wstETH.png?1696518295",
"symbol": "wstETH",
"decimals": 18,
"priceId": "wrapped-steth",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-wstETH",
"type": "pool",
"label": "wstETH",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db",
"iconUrl": "https://assets.coingecko.com/coins/images/25783/large/velo.png?1696524870",
"symbol": "VELO",
"decimals": 18,
"priceId": "velodrome-finance",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-VELO",
"type": "pool",
"label": "VELO",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0xDCB612005417Dc906fF72c87DF732e5a90D49e11",
"iconUrl": "https://coin-images.coingecko.com/coins/images/26045/large/EURC.png?1769615705",
"symbol": "EURC",
"decimals": 6,
"priceId": "euro-coin",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-EURC",
"type": "pool",
"label": "EURC",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
"iconUrl": "https://coin-images.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-DAI",
"type": "pool",
"label": "DAI",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0xC30ce6A5758786e0F640cC5f881Dd96e9a1C5C59",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "gtusdcp",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-gtusdcp",
"type": "pool",
"label": "gtusdcp",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
},
{
"token": {
"address": "0x3d63934715b6D4c4DFbBC1a00Fe2A2145079DD76",
"iconUrl": "https://coin-images.coingecko.com/coins/images/2518/large/weth.png?1696503332",
"symbol": "Re7WETH",
"decimals": 18,
"priceId": "weth",
"sinceTimestamp": 1776188243
},
"buckets": [
{
"id": "privacy-boost-Re7WETH",
"type": "pool",
"label": "Re7WETH",
"address": "oeth:0xca689828854a422CF1f778be03CA80549408F620",
"sinceTimestamp": 1776188243,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xca689828854a422CF1f778be03CA80549408F620"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xca689828854a422CF1f778be03CA80549408F620"
}
}
}
]
}
],
"upgradesAndGovernance": {
"content": "Privacy Boost is controlled by the 3/4 admin multisig, which can upgrade the pool, registries and AuditGateway without delay. It can replace all five proof verifiers and register or replace their verification keys, set withdrawal and portal sweep fees up to 10% each, configure portal minimum sweeps, and change the treasury, operator and gateway route manager.\n\nThe external-call gateway owner manages target/selector policies, selects its guardian, and can pause or unpause execution. The guardian can pause and remove policies. Stranded gateway tokens can only be swept back to the pool while paused."
}
}
+196 -8
+196 -8
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which deposit funds which withdrawal, for deposits approved by the permissioned association set provider. 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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.",
"advice": "Use a pool with a large anonymity set. Withdraw common amounts rather than everything at once. Wait before exiting, 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."
}
],
"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."
}
},
"noteDiscovery": {
"description": "In privacy Pools UI, the user needs to provide the seed phrase, from which their nullifiers and secrets are derived deterministically. The private user balance is computed locally by scanning every `Deposited`, `Withdrawn`, and `Ragequit` event of every supported pool and matching the derived commitments against the ones in the events. Because every event is requested, the RPC provider learns neither which events belong to the user, nor into which pool the user has deposited from the queries alone."
},
"privacy": {
"value": "Compliance gated",
"sentiment": "good",
"description": "Compliance is enforced through centralized association set providers, which can refuse deposits into the pool, sending them back to the sender."
},
"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": {}
}
}
]
}
],
"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."
}
}
+217 -8
+217 -8
{
"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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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, 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.",
"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.",
"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": [
"sender",
"recipient",
"amount",
"asset"
]
}
}
},
"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."
}
],
"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
}
},
"noteDiscovery": {
"description": "To find incoming transfers and rebuild the balance, a wallet downloads every new commitment emitted by the Railgun contract and tries to decrypt each one locally with the user's viewing key; successful decryptions are saved as the user's notes. Because every commitment is requested, the RPC provider does not learn which notes belong to the user from the queries alone."
},
"privacy": {
"value": "Optional compliance",
"sentiment": "good",
"description": "Compliance is optional at the core protocol level: users can create proofs of innocence to disassociate deposits from flagged addresses, and relayers can choose to require them."
},
"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"
}
}
}
]
}
],
"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.50 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."
}
}
}
}
+285 -13
+1 -1
{
"badges": [],
"description": "A privacy pool on Starknet for arbitrary-amount private transfers and DeFi actions, using Cairo execution proofs and auditor-accessible compliance data.",
"detailedDescription": "STRK-20 is a privacy pool deployed as [a smart contract on starknet](https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a). It uses a UTXO-style note model: users deposit ERC20 tokens into the pool, create encrypted notes, spend notes by publishing nullifiers, and withdraw to public Starknet addresses.\n\nThe pool contract source code was reviewed for this entry. Other parts of the protocol stack are open source in the [starknet-privacy repository](https://github.com/starkware-libs/starknet-privacy) — the Cairo pool contract, TypeScript SDK, discovery service, audit reports, and Lean formal verification — and StarkWare published [instructions to run a local transaction prover](https://github.com/starkware-libs/sequencer/tree/main/crates/starknet_transaction_prover). Users can therefore audit what is actually proven and generate ZK proofs themselves without revealing private data to the proving service run by Starkware, although proving is compute-intensive (a ~48 vCPU / 96 GB machine is recommended).\n\nNote that the proven (L1-verified) Starknet OS only checks that submitted proof facts are well-formed, reference a real recent block, and claim an allowlisted virtual OS program hash — the client's STARK proof itself is verified by the permissioned Starknet sequencer before transaction inclusion and is not re-verified on L2 or L1. As a consequence, the protocol would accept forged withdrawals from the Sequencer as valid.\n\n### Privacy considerations\nThe protocol supports private transfers, arbitrary amounts, partial withdrawals through private change notes, and DeFi actions through external helper contracts. DeFi integrations use open notes: the pool creates a note whose final amount is filled after an external helper, such as a swap or lending adapter, measures the onchain output.\n\n### Fees\nThe pool currently charges a flat fee of 4 STRK plus gas for any action that uses the privacy pool, including deposits, swaps, and withdrawals.\n\n### Compliance\nThe compliance model relies on an 'auditor' public key. Users register an encrypted private viewing key, but all 'private' actions must include auditor-encrypted metadata. Whoever controls the auditor private key can decrypt user metadata offchain from onchain-emitted cyphertexts; this does not grant spending authority, but it can centrally remove any user's privacy, even retroactively.\n\n### Anonymity set\nThe anonymity set, in the best case, corresponds to the set of all users of the privacy pool. But metadata leaks and the centralized auditor reduce the anonymity set in practice.",
"detailedDescription": "STRK-20 is a privacy pool deployed as [a smart contract on starknet](https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a). It uses a UTXO-style note model: users deposit ERC20 tokens into the pool, create encrypted notes, spend notes by publishing nullifiers, and withdraw to public Starknet addresses.\n\nThe pool contract source code was reviewed for this entry. Other parts of the protocol stack are open source in the [starknet-privacy repository](https://github.com/starkware-libs/starknet-privacy) — the Cairo pool contract, TypeScript SDK, discovery service, audit reports, and Lean formal verification — and StarkWare published [instructions to run a local transaction prover](https://github.com/starkware-libs/sequencer/tree/main/crates/starknet_transaction_prover). Users can therefore audit what is actually proven and generate ZK proofs themselves without revealing private data to the proving service run by Starkware, although proving is compute-intensive (a ~48 vCPU / 96 GB machine is recommended).\n\nNote that the proven (L1-verified) Starknet OS only checks that submitted proof facts are well-formed, reference a real recent block, and claim an allowlisted virtual OS program hash — the client's STARK proof itself is verified by the permissioned Starknet sequencer before transaction inclusion and is not re-verified on L2 or L1. As a consequence, the protocol would accept forged withdrawals from the Sequencer as valid.\n\n### Privacy considerations\nThe protocol supports private transfers, arbitrary amounts, partial withdrawals through private change notes, and DeFi actions through external helper contracts. DeFi integrations use open notes: the pool creates a note whose final amount is filled after an external helper, such as a swap or lending adapter, measures the onchain output.\n\n### Fees\nThe pool currently charges a flat fee of 6 STRK plus gas for any action that uses the privacy pool, including deposits, swaps, and withdrawals.\n\n### Compliance\nThe compliance model relies on an 'auditor' public key. Users register an encrypted private viewing key, but all 'private' actions must include auditor-encrypted metadata. Whoever controls the auditor private key can decrypt user metadata offchain from onchain-emitted cyphertexts; this does not grant spending authority, but it can centrally remove any user's privacy, even retroactively.\n\n### Anonymity set\nThe anonymity set, in the best case, corresponds to the set of all users of the privacy pool. But metadata leaks and the centralized auditor reduce the anonymity set in practice.",
"links": {
"documentation": [
"https://docs.starknet.io/build/starknet-privacy/overview"
],
"websites": [
"https://strk20.starknet.io/"
],
"explorers": [
"https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
],
"repositories": [
"https://github.com/starkware-libs/starknet-privacy",
"https://github.com/starkware-libs/sequencer/tree/main/crates/starknet_transaction_prover"
],
"other": [
"https://eprint.iacr.org/2026/474"
]
}
}
+284 -12
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides amounts, senders and the link between deposit and withdrawal inside the pool. The first payment to each new contact 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "warning",
"exposure": "Transfers inside are encrypted, but the public part of every transaction lists the recipient address when you open a channel to a new contact, the token and amount of the fee you pay, and the target and calldata of any DeFi action. Deposits and withdrawals show address, token and amount. Submitting from your own wallet names you; a paymaster hides the submitter but shows the fee token and amount.",
"advice": "Submit every pool action through a paymaster and pay its fee in the most common token. Treat the first payment to a new contact, and any DeFi action, as public.",
"interior": {
"sender": "private",
"recipient": {
"verdict": "exposed",
"note": "Opening a channel writes the recipient address in cleartext; later transfers do not."
},
"amount": "private",
"asset": {
"verdict": "atRisk",
"note": "The fee payment names the token; DeFi actions name the target contract."
},
"linkage": "private"
},
"sources": [
{
"title": "PrivacyPool contract on Voyager",
"url": "https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
{
"title": "Channel opening carries the recipient address",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/actions.cairo#L329-L334"
},
{
"title": "DeFi actions carry target contract and calldata",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/actions.cairo#L360-L365"
},
{
"title": "Deposit and withdrawal events carry address, token and amount",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/events.cairo#L17-L41"
},
{
"title": "Paymaster fee fixes the visible fee token",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/sdk/src/interfaces.ts#L160-L172"
},
{
"title": "Protocol fee is pulled in STRK from the submitter",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/privacy.cairo#L845-L856"
},
{
"title": "Without a paymaster the client submits from the user wallet",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/demo/src/hooks/useTransactionBuilder.ts#L247-L256"
}
],
"id": "publicObserver",
"value": "Link at risk",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "The set of registered users is small and split further by token and by fee token. A channel opened in the same transaction as a deposit ties the two together, and most withdrawal addresses have also deposited. Paying gas from your own wallet instead of a paymaster exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"advice": "Open channels in a transaction without a deposit, pay fees in the most common token, and withdraw uneven amounts that match no deposit. Wait before exiting, exit to a fresh address every time, and spend from it with a different wallet than the one that deposited.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "A channel opened in the same transaction as a deposit names the depositor."
},
"recipient": "exposed",
"amount": "private",
"asset": "atRisk",
"linkage": {
"verdict": "atRisk",
"note": "Most withdrawal addresses have also deposited, which pairs the two ends."
}
},
"sources": [
{
"title": "PrivacyPool contract on Voyager",
"url": "https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
{
"title": "Deposit and withdrawal events carry address, token and amount",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/events.cairo#L17-L41"
},
{
"title": "Fee is charged per action in a visible token",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/privacy.cairo#L845-L856"
}
],
"id": "chainAnalyst",
"value": "Link at risk",
"alsoExposed": [
"sender"
]
},
"networkObserver": {
"sentiment": "warning",
"exposure": "The paymaster and the sequencer receive the client proof with every action. Stwo proofs are not zero-knowledge by default, and how much of the private execution they reveal is not established. Traffic to the proving and note-discovery services is encrypted, and both support an oblivious relay that hides your address, but it is off unless the client turns it on.",
"advice": "Turn on the oblivious relay for the proving and discovery services, or run both yourself.",
"interior": {
"sender": "unverifiable",
"recipient": "unverifiable",
"amount": "unverifiable",
"asset": "atRisk",
"linkage": "unverifiable"
},
"sources": [
{
"title": "Oblivious relay support is opt-in",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/sdk/src/internal/ohttp-client.ts#L24-L32"
},
{
"title": "Discovery service: per-request keys and oblivious relay",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/crates/discovery-service/README.md#L14-L32"
},
{
"title": "Proof and proof facts travel in the submitted transaction",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/demo/src/hooks/useTransactions.ts#L140-L160"
},
{
"title": "Stwo Cairo is not zero-knowledge by default",
"url": "https://github.com/starkware-libs/stwo-cairo/blob/2b1fb4ded5496fb2db4e8261cd5ee5ef6b710839/README.md#security-model"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": [
"sender",
"amount"
]
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "Every user's viewing key is escrowed onchain, encrypted to one auditor key that a role holder can replace at any time with no delay; that key decrypts everything the protocol hides. The default proving service receives your address, viewing key and actions in the clear, the note-discovery service receives the viewing key on every sync, and deposits need a fresh attestation from a screening provider that sees and can block every depositor.",
"advice": "Run the prover and note discovery yourself, or read the pool directly from chain. Nothing you do removes the auditor key.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Auditor and screener keys in pool storage",
"url": "https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
{
"title": "Viewing key escrowed to the auditor key onchain",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/events.cairo#L5-L13"
},
{
"title": "Auditor and screener keys are settable roles",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/events.cairo#L43-L52"
},
{
"title": "Withdrawals carry the auditor-decryptable withdrawer address",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/events.cairo#L17-L29"
},
{
"title": "Proving request carries the user address and viewing key",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/sdk/src/internal/proof-invocation-factory.ts#L128-L136"
},
{
"title": "Discovery request carries the viewing key",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/sdk/src/internal/indexer-discovery.ts#L158-L165"
},
{
"title": "Deposits require a signed screening attestation",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/privacy.cairo#L784-L800"
},
{
"title": "Screening sidecar forwards the depositor to an external provider",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/proof-interceptor/README.md#L37"
},
{
"section": "upgrades-and-governance"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
"sender",
"amount",
"asset"
]
},
"futureAdversary": {
"sentiment": "bad",
"exposure": "The proofs are post-quantum, the encryption is not. Channel keys, note contents and the auditor escrow use elliptic-curve key exchange, and the auditor public key sits onchain. A quantum computer recovers that one key and with it every escrowed viewing key, and so the whole history.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Stark-curve ECDH encryption of channel and note data",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/sdk/src/utils/encryptions.ts#L120-L136"
},
{
"title": "Viewing key escrowed under the onchain auditor public key",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/cc0dc408dee41f5658cd528ae59ab0794be2e298/packages/privacy/src/events.cairo#L5-L13"
},
{
"title": "Auditor public key in pool storage",
"url": "https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
"sender",
"amount",
"asset"
]
}
}
},
"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."
}
],
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The pool implementation is immediately upgradeable, so users have no delay to withdraw before a malicious upgrade can take effect.",
"walkawayTest": {
"passed": false,
"reason": "Although the full software stack needed to generate proofs and exit is published and self-hostable, STRK-20 heavily relies on the permissioned Starknet sequencer: it alone verifies client proofs and must include pool transactions, so users cannot exit without its cooperation."
}
},
"noteDiscovery": {
"description": "STRK-20 stores notes onchain in encrypted channels organized by recipient, sender, and token. Instead of scanning all pool activity, the wallet currently sends its Starknet account address and private viewing key to a [discovery service](https://github.com/starkware-libs/starknet-privacy/blob/main/crates/discovery-service/README.md), which reads the relevant contract storage, decrypts channels and notes, checks public nullifiers, and returns the user's unspent notes.\n\nA third-party discovery operator therefore receives the private viewing key and can see the user's counterparties, tokens, amounts, note history, and which notes remain unspent, together with sync timing and normally the user's IP address. Oblivious HTTP can separate the IP address from the request content unless the relay and operator collude, but it does not hide note data from the operator. The discovery service is open source, so users can avoid this disclosure by running it themselves against their own or a trusted Starknet RPC node.",
"risks": [
"A third-party discovery service retains the supplied viewing key or decrypted results, revealing the user's private transfer history and unspent notes."
]
},
"privacy": {
"value": "Admin view key",
"sentiment": "bad",
"description": "All private actions include auditor-encrypted metadata, so whoever controls the auditor private key can decrypt user activity retroactively. Users can not know whether their privacy was violated. Compliance is facilitated by this mandatory auditor-encrypted metadata."
},
"quantumResistant": true,
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "The pool contract, TypeScript SDK, discovery service, and proving stack are publicly available and can be run locally, so users can audit what is actually proven and generate the required ZK proofs without revealing private data to a third-party prover."
},
"riskSummary": "## Funds can be stolen if\n1. the proof system or virtual Starknet execution model is broken or backdoored, allowing invalid server actions to be applied.\n2. the Starknet sequencer includes a transaction with fabricated proof facts.\n3. the escrow smart contract is maliciously upgraded (no delay).\n4. an external DeFi helper or target protocol invoked by the user mishandles assets.\n
\n## Funds can be lost if\n1. a user loses the Starknet account key or private viewing key required to spend their notes.\n2. the pool is paused or upgraded in a way that prevents users from applying valid actions.\n
\n## Privacy can be lost if\n1. the auditor private key holder decrypts registered users' viewing keys or withdrawal and metadata.\n2. a third-party-hosted prover or note discovery service leaks user secrets or metadata (both can be self-hosted).\n3. deposits, withdrawals, open-note fills, timing, unique amounts, or DeFi helper calldata make a user's activity linkable.",
"tokens": [
{
"token": {
"address": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
"iconUrl": "https://assets.coingecko.com/coins/images/26433/large/starknet.png?1696525507",
"symbol": "STRK",
"decimals": 18,
"priceId": "starknet",
"sinceTimestamp": 1776679728
},
"buckets": [
{
"id": "strk20-STRK",
"type": "pool",
"label": "STRK pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1776679728,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"
}
}
}
]
},
{
"token": {
"address": "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696501661",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1776679728
},
"buckets": [
{
"id": "strk20-USDC",
"type": "pool",
"label": "USDC pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1776679728,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb"
}
}
}
]
},
{
"token": {
"address": "0x0787150e306e6eae6e3f79dea881770e8bbff2c1b8eb490f969669ee945b3135",
"iconUrl": "https://coin-images.coingecko.com/coins/images/102173511/large/strkBTC_Logo.png?1779888214",
"symbol": "strkBTC",
"decimals": 8,
"priceId": "strkbtc",
"sinceTimestamp": 1777893725
},
"buckets": [
{
"id": "strk20-strkBTC",
"type": "pool",
"label": "strkBTC pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1777893725,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x0787150e306e6eae6e3f79dea881770e8bbff2c1b8eb490f969669ee945b3135"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x0787150e306e6eae6e3f79dea881770e8bbff2c1b8eb490f969669ee945b3135"
}
}
}
]
},
{
"token": {
"address": "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1776679728
},
"buckets": [
{
"id": "strk20-WBTC",
"type": "pool",
"label": "WBTC pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1776679728,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac"
}
}
}
]
},
{
"token": {
"address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
"symbol": "ETH",
"decimals": 18,
"priceId": "ethereum",
"sinceTimestamp": 1776679728
},
"buckets": [
{
"id": "strk20-ETH",
"type": "pool",
"label": "ETH pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1776679728,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"
}
}
}
]
},
{
"token": {
"address": "0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a",
"iconUrl": "https://assets.coingecko.com/coins/images/54172/standard/logo200x200.png?1738561141",
"symbol": "XSTRK",
"decimals": 18,
"priceId": "endur-fi-staked-strk",
"sinceTimestamp": 1776679728
},
"buckets": [
{
"id": "strk20-XSTRK",
"type": "pool",
"label": "XSTRK pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1776679728,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
}
}
}
]
},
{
"token": {
"address": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"decimals": 6,
"symbol": "USDT",
"priceId": "tether",
"sinceTimestamp": 1776679728
},
"buckets": [
{
"id": "strk20-USDT",
"type": "pool",
"label": "USDT pool",
"address": {
"chain": "starknet",
"address": "0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a"
},
"sinceTimestamp": 1776679728,
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"
}
}
}
]
}
],
"upgradesAndGovernance": {
"content": "The pool uses StarkWare-style role components and an instantly upgradeable smart contract implementation.\n\n- `APP_GOVERNOR` can set the fee amount, fee collector, and proof validity window.\n- `GOVERNANCE_ADMIN` can grant governance and upgrade-governor roles.\n- `SECURITY_ADMIN` can grant pause, unpause, and auditor-key administration roles.\n- `SECURITY_AGENT` can pause the pool, if granted.\n- `SECURITY_GOVERNOR` can unpause the pool and change the auditor public key, if granted.\n- `UPGRADE_GOVERNOR` can approve and execute upgrades, if granted.\n- `UPGRADE_AGENT` can execute approved upgrades, if granted.\n\nThe live role holders observed were:\n\n- `APP_GOVERNOR`: `0x2796da10aba2e1f445c38eba07e5a4393d6dab30d203d3432deb824e891619a` (2/4 Multisig)\n- `GOVERNANCE_ADMIN`: `0x3103066e6c7037ba947ea9a7b5b8d110ae7f3d631fa5849435d0dc1fc5ef785` (EOA)\n- `GOVERNANCE_ADMIN` and `SECURITY_ADMIN`: `0x663cc699d9c51b7d4d434e06f5982692167546ce525d9155edb476ac9a117d6` (7/12 Multisig)"
},
"zkCatalogId": "stwo"
}
+168 -8
+168 -8
{
"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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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.",
"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.",
"advice": "Use a pool with a large anonymity set. Deposit a large sum as one note. Wait before exiting, 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.",
"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)."
}
],
"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
}
},
"noteDiscovery": {
"description": "A Tornado Cash note is generated locally at deposit time and kept by the user, so normally nothing has to be discovered to spend it. Users can additionally back up notes onchain: the note is encrypted to a user's private key and is emitted as an `EncryptedNote` event on `TornadoRouter`. The recovery downloads all such events and tries to decrypt each one locally. Because every event is requested, the RPC provider learns neither which events belong to the user, nor into which pool the user has deposited from the queries alone."
},
"privacy": {
"value": "Unconditional",
"sentiment": "good",
"description": "There is no protocol-level compliance mechanism or way to compromise user privacy."
},
"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"
}
}
}
]
}
],
"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."
}
}
}
}
+205 -8
+205 -8
{
"adversaries": {
"promise": {
"protects": "recipient",
"text": "Hides which registered recipient a stealth payment is for. The payer knows; sender, asset, amount and where the funds go next 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "Announcements mark each stealth payment but not the recipient whose keys were used. The registry publishes the candidate recipients and their key history.",
"advice": "Withdraw to a fresh address with no ENS name or prior activity, through the token relayer so your known wallet never funds the stealth address for gas. Keep funds from different stealth addresses apart.",
"sources": [
{
"contract": "Umbra",
"title": "Announcement, tokenPayments and TokenWithdrawal expose the fund path"
},
{
"contract": "StealthKeyRegistry",
"title": "StealthKeyChanged publishes recipients and key changes"
},
{
"title": "Recipient matching decrypts locally and checks the stealth address",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/umbra-js/src/classes/Umbra.ts#L704-L720"
}
],
"id": "publicObserver",
"value": "Recipient private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "Protocol use is public once a payment is announced, which narrows the anonymity set to registered recipients. Withdrawals to registered addresses, round trips back to the sender and a shared collecting address reveal or cluster recipients; membership alone does not identify them. The client withdraws one stealth address at a time and never merges them.",
"advice": "Keep withdrawal destinations separate across payments and chains, and check that timing, amounts or recurring counterparties do not reconnect them to an identified account.",
"sources": [
{
"title": "Sections 6–7: reuse heuristics, false positives and historical evaluation",
"url": "https://arxiv.org/html/2308.01703v2#S6"
},
{
"title": "Table 2: denominator is withdrawn payments; section 7.4: no fee matches",
"url": "https://arxiv.org/html/2308.01703v2#S7"
},
{
"title": "Client warns on linked destinations, but allows proceeding",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/components/AccountReceiveTable.vue#L671-L715"
}
],
"id": "chainAnalyst",
"value": "Recipient at risk",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposure": "On the payer's side the wallet RPC resolves the recipient, reads their registry entry and broadcasts the stealth payment seconds apart, so that provider can pair the payment with the recipient. On your side the wallet RPC receives the matched stealth-address balance batch, while a build-configured mainnet RPC looks up your connected wallet and the senders of matched payments, and withdrawal destinations are checked against ENS, POAP and Gitcoin APIs.",
"advice": "Run an inspected local build with every RPC pointed at your own node and the external name and safety lookups disabled. Send broadcasts and relay requests over Tor.",
"sources": [
{
"title": "Payer flow: registry lookup and send through the wallet provider",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/pages/AccountSend.vue#L955-L1059"
},
{
"title": "The wallet provider wraps the connected wallet",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/store/wallet.ts#L262-L267"
},
{
"title": "Mainnet and Polygon RPCs are configured separately from the wallet",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/utils/constants.ts#L1-L17"
},
{
"title": "Connected-wallet name lookup uses MAINNET_PROVIDER",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/store/wallet.ts#L336-L358"
},
{
"title": "Matched senders go to mainnet name lookup; stealth balances to wallet RPC",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/components/AccountReceiveTable.vue#L584-L626"
},
{
"title": "Withdrawal checks send destination to ENS, POAP and Gitcoin lookups",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/utils/address.ts#L255-L319"
},
{
"title": "Signed relay request carries stealthAddr and acceptor",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/components/AccountReceiveTable.vue#L758-L768"
}
],
"id": "networkObserver",
"value": "Recipient at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "good",
"exposure": "The contracts are immutable and give no administrator a viewing key or a way to replace registered keys without the registrant. The client matches announcements locally, so no indexer or relayer has a protocol-wide view.",
"advice": "Run an inspected local build of the client instead of the hosted frontend.",
"sources": [
{
"contract": "Umbra",
"title": "Owner sets toll, tollCollector and tollReceiver; withdrawals require authorization"
},
{
"contract": "StealthKeyRegistry",
"title": "Direct registration or registrant-authorized signature, no admin override"
},
{
"title": "Viewing-key decryption is local to the client",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/umbra-js/src/classes/Umbra.ts#L704-L720"
},
{
"title": "Relay API receives signed withdrawal data, not a viewing key",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/frontend/src/utils/umbra-api.ts#L96-L111"
}
],
"id": "privilegedInsider",
"value": "Recipient private",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "bad",
"exposure": "Each announcement stores the ephemeral public key and the encrypted scalar. A quantum computer that breaks secp256k1 decrypts the scalar against every registered viewing key and checks which spending key yields the stealth address, identifying the recipient of every past payment.",
"sources": [
{
"contract": "Umbra",
"title": "Announcement archives pkx and ciphertext"
},
{
"contract": "StealthKeyRegistry",
"title": "Historical public viewing and spending keys"
},
{
"title": "ECDH-derived hash encrypts the scalar by XOR",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/umbra-js/src/classes/KeyPair.ts#L121-L157"
},
{
"title": "Recovered scalar is checked against the receiving address",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/a81df24e76a0d6ab1ec79c6353e28c527b1b1a80/umbra-js/src/classes/Umbra.ts#L704-L720"
}
],
"id": "futureAdversary",
"value": "Recipient exposed",
"alsoExposed": []
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "Umbra sends funds to one-time stealth addresses instead of mixing deposits in a shared pool."
},
"attributes": [
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
},
{
"id": "stealthAddresses",
"label": "Stealth addresses",
"description": "Uses fresh, one-time recipient addresses to obscure who controls the receiving address."
}
],
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "The core contracts are immutable. The owner can change the toll for future payments, but cannot stop recipients from accessing payments that have already been sent.",
"walkawayTest": {
"passed": true
}
},
"noteDiscovery": {
"description": "Each Umbra payment emits an `Announcement` containing a one-time stealth address and encrypted data for the recipient. The recipient downloads all announcement events and checks them locally with their viewing key; a successful check identifies a payment and lets the recipient derive the key controlling its stealth address. The indexer or RPC provider does not learn which announcements belong to the user from the queries alone, because all announcements are fetched.\n\nUmbra Advanced mode allows setting start and end block numbers for transfer discovery scan. Using this feature could degrade your privacy against an RPC provider, who would be able to narrow down potential window for your incoming transfers. It is recommended to use this feature only with a trusted (e.g. your own) RPC node."
},
"privacy": {
"value": "Recipient privacy",
"sentiment": "warning",
"description": "There is no protocol-level compliance mechanism or privileged view key. Umbra hides who controls the receiving address, but the sender, amount, and stealth address remain public and privacy can be weakened by transaction patterns or poor withdrawal hygiene."
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "The immutable core contracts, cryptographic library, and frontend are published and can be built and run locally."
},
"riskSummary": "## Funds can be lost if\n1. an unsupported ERC-20, such as a fee-on-transfer or rebasing token, causes the Umbra contract's internal accounting to diverge from its token balance.\n
\n## Privacy can be lost if\n1. a recipient sends funds from a stealth address to a publicly linked address or consolidates payments in a recognizable way.\n2. public timing, amount, token, or ENS registration patterns let an observer narrow down the possible recipient.\n
\n## New payments can be stopped if\n1. the Umbra owner sets an arbitrarily high toll for contract-routed payments.",
"summaryTrackedItemName": "token",
"tokens": [
{
"token": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"iconUrl": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1619813937
},
"buckets": [
{
"id": "umbra-DAI",
"type": "pool",
"label": "DAI",
"address": "eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401",
"sinceTimestamp": 1619813937,
"deposit": {
"event": "0x29877766fa2bfe3b90008d6d92f965eca91cbc5757ed775740e460799fb92219",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
},
"withdrawal": {
"event": "0x30eb3583ad09933b693a45452ab07512244cdbc5868701aa004c27b7b267c249",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
}
}
]
},
{
"token": {
"address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
"symbol": "ETH",
"decimals": 18,
"priceId": "ethereum",
"sinceTimestamp": 1619813937
},
"buckets": [
{
"id": "umbra-ETH",
"type": "pool",
"label": "ETH",
"address": "eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401",
"sinceTimestamp": 1619813937,
"deposit": {
"event": "0x29877766fa2bfe3b90008d6d92f965eca91cbc5757ed775740e460799fb92219",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
}
},
"withdrawal": {
"event": "0x29877766fa2bfe3b90008d6d92f965eca91cbc5757ed775740e460799fb92219",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
}
}
}
]
},
{
"token": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1619813937
},
"buckets": [
{
"id": "umbra-USDC",
"type": "pool",
"label": "USDC",
"address": "eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401",
"sinceTimestamp": 1619813937,
"deposit": {
"event": "0x29877766fa2bfe3b90008d6d92f965eca91cbc5757ed775740e460799fb92219",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
},
"withdrawal": {
"event": "0x30eb3583ad09933b693a45452ab07512244cdbc5868701aa004c27b7b267c249",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
}
}
]
},
{
"token": {
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "USDT",
"decimals": 6,
"priceId": "tether",
"sinceTimestamp": 1619813937
},
"buckets": [
{
"id": "umbra-USDT",
"type": "pool",
"label": "USDT",
"address": "eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401",
"sinceTimestamp": 1619813937,
"deposit": {
"event": "0x29877766fa2bfe3b90008d6d92f965eca91cbc5757ed775740e460799fb92219",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}
},
"withdrawal": {
"event": "0x30eb3583ad09933b693a45452ab07512244cdbc5868701aa004c27b7b267c249",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7"
}
}
}
]
},
{
"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": 1619813937
},
"buckets": [
{
"id": "umbra-WBTC",
"type": "pool",
"label": "WBTC",
"address": "eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401",
"sinceTimestamp": 1619813937,
"deposit": {
"event": "0x29877766fa2bfe3b90008d6d92f965eca91cbc5757ed775740e460799fb92219",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
}
},
"withdrawal": {
"event": "0x30eb3583ad09933b693a45452ab07512244cdbc5868701aa004c27b7b267c249",
"extractor": "umbraAmount",
"params": {
"tokenAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
}
}
}
]
}
],
"upgradesAndGovernance": {
"content": "Umbra has no governance process or contract upgrade mechanism. The Umbra and StealthKeyRegistry contracts are immutable.\n\nThe Umbra contract has a permissioned owner that can immediately set the ETH toll charged on every contract-routed payment and change the addresses that collect and receive those tolls. Because the toll has no upper bound, the owner can effectively stop new payments through the Umbra contract at any time. The owner cannot change the payment or withdrawal logic and cannot prevent recipients from accessing payments already sent to them, so the exit window is infinite and the protocol passes the walkaway test."
}
}
+257 -5
+257 -5
{
"adversaries": {
"promise": {
"protects": "amount",
"text": "Hides transfer amounts. Who pays whom, and every wrap and unwrap amount, 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",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"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": "Only the amount of a confidential transfer is hidden. Sender, recipient, every wrap and unwrap amount and the ciphertext handles are public, and either party can later disclose a transfer amount onchain.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "private",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "ConfidentialUSDCWrapper"
},
{
"title": "Failed transfer moves an encrypted zero",
"url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/23ba15402346027f2416667acbb1e741179f8485/contracts/token/ERC7984/ERC7984.sol#L336-L352"
},
{
"title": "Unwrap makes the burned amount publicly decryptable",
"url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/23ba15402346027f2416667acbb1e741179f8485/contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol#L262-L268"
},
{
"title": "Either party can request disclosure of a transfer amount",
"url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/23ba15402346027f2416667acbb1e741179f8485/contracts/token/ERC7984/ERC7984.sol#L202-L209"
}
],
"id": "publicObserver",
"value": "Amounts private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "All wraps and unwraps are public, so an account's balance is bounded by what went in and out, and exact for any account that never made a confidential transfer. Transfer partners and timing are public, so a wrap, a transfer and an unwrap in a row pair up by amount.",
"advice": "Keep funds wrapped and transfer often, even zero amounts. Unwrap only after a while, and never an amount that matches a recent wrap or a known payment.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Bounded by public wrap and unwrap totals; exact for accounts without confidential transfers."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "ConfidentialUSDCWrapper"
},
{
"title": "Wrap event carries the clear rounded amount",
"url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/23ba15402346027f2416667acbb1e741179f8485/contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol#L256"
},
{
"title": "ERC-7984: public sender, recipient and handle per transfer",
"url": "https://eips.ethereum.org/EIPS/eip-7984"
}
],
"id": "chainAnalyst",
"value": "Amounts at risk",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "good",
"exposure": "Amounts are encrypted on your device and never leave it in clear. The default hosted relayer, which needs an operator API key, receives your address and the token contract with every encrypted input and balance view, so it learns who transacts and when, plus your IP.",
"advice": "Route relayer requests through Tor, or self-host the relayer.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "private",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Hosted relayer URL",
"url": "https://github.com/zama-ai/relayer-sdk/blob/d06f1e585a78181135cb602109e5fa3da523b48d/src/configs.ts#L7"
},
{
"title": "Encrypted input bound to user and contract address",
"url": "https://github.com/zama-ai/relayer-sdk/blob/d06f1e585a78181135cb602109e5fa3da523b48d/src/relayer/sendEncryption.ts#L136-L145"
},
{
"title": "Balance view request carries user address and handles",
"url": "https://github.com/zama-ai/relayer-sdk/blob/d06f1e585a78181135cb602109e5fa3da523b48d/src/relayer/userDecrypt.ts#L288-L297"
},
{
"title": "Mainnet relayer needs an API key",
"url": "https://docs.zama.org/protocol/sdk/guides/relayer-api-keys"
},
{
"title": "Self-hosting a relayer",
"url": "https://github.com/zama-ai/fhevm/blob/94f1f3b35c71175dc68bb36887e0525f7c5d80c5/relayer/docs/SELF_HOSTING.md"
}
],
"id": "networkObserver",
"value": "Amounts private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "KMS operators share one FHE key under a threshold and can collude to decrypt every balance and transfer ever made. The token owner can appoint an observer with a never-expiring view of all balances and amounts, and can upgrade the token and the ACL, all with no delay.",
"advice": "Watch for ObserverAdded events and upgrades on your token; there is no delay to react. Nothing you do prevents KMS collusion.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "exposed",
"note": "To colluding KMS operators, any owner-appointed observer, or the owner via upgrade."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "ConfidentialUSDCWrapper",
"title": "Wrapper (addObserver, blockUser, upgrade)"
},
{
"contract": "ProtocolConfig",
"title": "KMS signers and thresholds"
},
{
"contract": "ACL",
"title": "Access control the KMS honors"
},
{
"section": "permissions"
},
{
"title": "Observer gets a wildcard delegation without expiration",
"url": "https://github.com/zama-ai/protocol-apps/blob/85eef95c797da20875fe7eeae7f8d212d760486f/contracts/confidential-wrapper/contracts/ConfidentialWrapper.sol#L395-L403"
},
{
"title": "Wrapper upgrade authorized by the owner alone",
"url": "https://github.com/zama-ai/protocol-apps/blob/85eef95c797da20875fe7eeae7f8d212d760486f/contracts/confidential-wrapper/contracts/ConfidentialWrapper.sol#L497"
},
{
"title": "ACL upgrade authorized by the owner alone",
"url": "https://github.com/zama-ai/fhevm/blob/94f1f3b35c71175dc68bb36887e0525f7c5d80c5/host-contracts/contracts/ACL.sol#L654"
},
{
"title": "13 KMS parties with threshold t = 4",
"url": "https://github.com/zama-ai/kms/blob/b5cd5cf465dc488ff04ac8c19f3ef12877abffa0/ai-docs/ARCHITECTURE.md#L22-L23"
},
{
"title": "Shamir sharing with t < n/3 (TKMS announcement)",
"url": "https://www.zama.ai/post/introducing-zama-threshold-key-management-system-tkms"
}
],
"id": "privilegedInsider",
"value": "Amounts exposed",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposure": "The encryption is lattice-based and survives quantum computers, but every ciphertext is publicly downloadable and one long-lived key protects them all. If enough key shares ever leak, the entire history is exposed.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Lattice cryptography, but one long-lived key."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Coprocessor ciphertext store (public listing)",
"url": "https://coprocessor-1.mainnet.zama.org/?max-keys=3"
},
{
"title": "ML-KEM hybrid encryption of decryption shares",
"url": "https://github.com/zama-ai/kms/blob/b5cd5cf465dc488ff04ac8c19f3ef12877abffa0/core/service/src/cryptography/hybrid_ml_kem.rs"
},
{
"title": "Key resharing and key switching in the KMS",
"url": "https://docs.zama.org/protocol/protocol/overview/kms"
}
],
"id": "futureAdversary",
"value": "Amounts at risk",
"alsoExposed": []
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "Zama confidential tokens hide amounts, but sender and receiver addresses remain public and are not mixed in a shared anonymity set."
},
"attributes": [
{
"id": "fhe",
"label": "FHE",
"description": "Uses fully homomorphic encryption for private computation over encrypted values."
},
{
"id": "privateAmounts",
"label": "Private amounts",
"description": "Transfer amounts are private, while sender and recipient addresses remain public."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
},
{
"id": "defi",
"label": "DeFi",
"description": "Interop with DeFi (swaps, vaults) from within the confidential token."
}
],
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The confidential token contracts and system contracts are upgradeable without an onchain delay, so users do not get a guaranteed withdrawal window before changes take effect.",
"walkawayTest": {
"passed": false,
"reason": "Only KMS 7/13 multisig members can decrypt FHE ciphertext. Also, FHE coprocessor operator is currently essential for protocol liveness."
}
},
"privacy": {
"value": "Transparent transfer graph",
"sentiment": "bad",
"description": "Zama confidential tokens do not hide the links between senders and recipients, only the amounts. Anyone can retrace each confidential transfer to its contributing plaintext deposits.\nAdditionally, a threshold with usable KMS key shares can decrypt current and past private balances. Zama states that KMS nodes run inside TEEs, but this is not verified onchain. Compliance can be enforced by confidential token owners blocking users and by configured underlying-token denylist hooks during deposits, transfers, unwrap requests, and unwrap finalization. Confidential token owners can also appoint observer accounts that receive wildcard decryption access to all balances and transfer amounts of their token (currently none are configured)."
},
"quantumResistant": true,
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The smart contracts are source-available, but users also rely on offchain FHE execution and threshold decryption services whose outputs are accepted onchain through signature verification. The offchain data cannot currently be fully reproduced from Ethereum DA."
},
"riskSummary": "## Funds can be stolen if\n1. a malicious upgrade compromises the confidential token contracts or FHEVM contracts with their escrowed underlying tokens.\n2. the threshold of the current or any non-destroyed historical Ethereum KMS context attests an inflated unwrap amount, allowing pooled underlying deposited before or after that context existed to be released. Governance can also install a malicious context or lower the threshold of any live context in place; 7/13 describes only the current context.\n3. the 1/1 coprocessor input verification path accepts invalid encrypted inputs that bypass confidential balance checks.\n
\n## Funds can be lost if\n1. an underlying token admin blacklists a confidential token address or otherwise prevents transfers from the contract.\n2. a user is blocked by the confidential token owner or by a configured underlying-token denylist before finalization/withdrawal.\n3. a confidential token is paused by an owner-appointed pauser and not unpaused by the owner, halting confidential transfers, unwrap requests, and unwrap finalization (no pauser is currently set on any confidential token).\n4. offchain ciphertext data required by the coprocessor or KMS for encrypted handles becomes unavailable and cannot be reconstructed.\n
\n## Privacy can be lost if\n1. enough KMS operators with usable key shares collude or are compromised and decrypt private ciphertext handles. 7/13 describes only the current verifier context, while retained contexts can have different memberships and thresholds.\n2. the confidential token owner appoints an observer: observers receive a wildcard, non-expiring user-decryption delegation over all of that token's encrypted handles and can decrypt its balances and transfer amounts (no observers are currently configured on any confidential token).\n3. the offchain coprocessor, wallet, relayer, frontend, or RPC path records enough metadata to link a user to encrypted inputs or transactions.\n4. deposits and withdrawals are linked by clear boundary amounts, recipient graph, timing, or address reuse.",
"summaryTrackedItemName": "token",
"tokens": [
{
"token": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1766750819
},
"buckets": [
{
"id": "zama-cw-cUSDC",
"type": "pool",
"label": "cUSDC token",
"address": "eth:0xe978F22157048E5DB8E5d07971376e86671672B2",
"sinceTimestamp": 1766750819,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1"
}
}
}
]
},
{
"token": {
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "USDT",
"decimals": 6,
"priceId": "tether",
"sinceTimestamp": 1766750831
},
"buckets": [
{
"id": "zama-cw-cUSDT",
"type": "pool",
"label": "cUSDT token",
"address": "eth:0xAe0207C757Aa2B4019Ad96edD0092ddc63EF0c50",
"sinceTimestamp": 1766750831,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1"
}
}
}
]
},
{
"token": {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"iconUrl": "https://assets.coingecko.com/coins/images/2518/large/weth.png?1696503332",
"symbol": "WETH",
"decimals": 18,
"priceId": "weth",
"sinceTimestamp": 1766750843
},
"buckets": [
{
"id": "zama-cw-cWETH",
"type": "pool",
"label": "cWETH token",
"address": "eth:0xda9396b82634Ea99243cE51258B6A5Ae512D4893",
"sinceTimestamp": 1766750843,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1000000000000"
}
}
}
]
},
{
"token": {
"address": "0xA12CC123ba206d4031D1c7f6223D1C2Ec249f4f3",
"iconUrl": "https://coin-images.coingecko.com/coins/images/70921/large/zama.png?1764591992",
"symbol": "ZAMA",
"decimals": 18,
"priceId": "zama",
"sinceTimestamp": 1766750867
},
"buckets": [
{
"id": "zama-cw-cZAMA",
"type": "pool",
"label": "cZAMA token",
"address": "eth:0x80CB147Fd86dC6dEe3Eee7e4Cee33d1397d98071",
"sinceTimestamp": 1766750867,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1000000000000"
}
}
}
]
},
{
"token": {
"address": "0x27f6c8289550fCE67f6B50BeD1F519966aFE5287",
"iconUrl": "https://coin-images.coingecko.com/coins/images/70647/large/tgbp-square.png?1762953800",
"symbol": "tGBP",
"decimals": 18,
"priceId": "tokenised-gbp",
"sinceTimestamp": 1767000887
},
"buckets": [
{
"id": "zama-cw-ctGBP",
"type": "pool",
"label": "ctGBP token",
"address": "eth:0xa873750ccBafD5ec7Dd13bfD5237d7129832eDD9",
"sinceTimestamp": 1767000887,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1000000000000"
}
}
}
]
},
{
"token": {
"address": "0x68749665FF8D2d112Fa859AA293F07A622782F38",
"iconUrl": "https://assets.coingecko.com/coins/images/10481/large/Tether_Gold.png?1696510471",
"symbol": "XAUt",
"decimals": 6,
"priceId": "tether-gold",
"sinceTimestamp": 1772116091
},
"buckets": [
{
"id": "zama-cw-cXAUt",
"type": "pool",
"label": "cXAUt token",
"address": "eth:0x73cc9aF9d6BEFdb3c3fAf8a5E8c05Cb95FdaEEf1",
"sinceTimestamp": 1772116091,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1"
}
}
}
]
},
{
"token": {
"address": "0xbeeffABcd0dB09589Dd21854aa760C52aB4bf04F",
"iconUrl": "https://coin-images.coingecko.com/coins/images/70647/large/tgbp-square.png?1762953800",
"symbol": "bbqTGBP",
"decimals": 18,
"priceId": "tokenised-gbp",
"sinceTimestamp": 1780585031
},
"buckets": [
{
"id": "zama-cw-cbbqTGBP",
"type": "pool",
"label": "cbbqTGBP token",
"address": "eth:0xBA4cFF6ED6F7Cb2A58776dECa4E984b498446762",
"sinceTimestamp": 1780585031,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1000000000000"
}
}
}
]
},
{
"token": {
"address": "0xbEEF00A59B577423653A1526c7009bdE103F542B",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "steakcUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1781877707
},
"buckets": [
{
"id": "zama-cw-csteakcUSDC",
"type": "pool",
"label": "csteakcUSDC token",
"address": "eth:0x66Bf74E96900D1a19c7070D939D124f2F565C458",
"sinceTimestamp": 1781877707,
"deposit": {
"event": "0xcda691c81d2fd787d8c209adb4ae8b138f857d7575adf7669195ed05482e701b",
"extractor": "zamaWrap",
"params": {}
},
"withdrawal": {
"event": "0x87061fd1a5b3714805472c94c9eb8a6b8491992ee77791aa2594be67b92fd962",
"extractor": "zamaUnwrap",
"params": {
"rate": "1000000000000"
}
}
}
]
}
],
"upgradesAndGovernance": {
"content": "Zama Confidential Tokens governance is implemented through an Aragon 'DAO' deployment controlled by two multisigs: ZamaGovMultisigA with a 9/17 threshold and ZamaGovMultisigB with a 3/5 threshold. The Gateway contracts are controlled separately by the SafeL2 multisig listed in the permissions section. These accounts can upgrade contracts or change critical configuration without a timelock. On the Gateway, the owner multisig can create and destroy KMS contexts, change the thresholds of any live context, and disable or remove registered host chains, halting Gateway workflows for those chains.\n\nConfidential token owners and underlying token owners can freeze users. Confidential token owners can further appoint a pauser that halts all confidential transfers, wrapping, and unwrapping of their token (only the owner can unpause), change or disable the underlying-token denylist hook, and appoint observers that gain wildcard decryption access to all balances and transfer amounts of their token. Currently, no pausers or observers are configured. The Ethereum ACL owner can manage ACL-level account blocking, unpause the ACL, change HCU limits, and — through the ProtocolConfig contract — create KMS contexts with arbitrary signer sets and thresholds, retune the thresholds of any live context (including the current one) in place, and trigger or abort FHE key- and CRS-generation in the KMSGeneration contract. A new context immediately becomes the default verifier authority and can attest unwrap amounts for assets deposited before it existed. Superseded contexts remain equally authoritative for all past and future ciphertext handles until the ACL owner explicitly destroys them; the current context cannot be destroyed. Changing an onchain KMS context changes only the accepted ECDSA credentials and does not trigger or verify resharing or destruction of the underlying FHE key shares.\n\nOffchain components like the coprocessor and KMS are trusted for liveness, privacy and security and are currently not decentralized."
}
}