91dd6a84 (main)
and
e29af090 (PR)
+16 -1
+16 -1
null
[
{
"title": "[Disclosed vulnerability] Barretenberg bug critical for Noir",
"url": "https://aztec.network/blog/critical-vulnerability-in-alpha-v4",
"date": "2026-03-27T00:00:00Z",
"description": "Aztec discloses a Barretenberg bug rated critical for Noir, fixed in v5.0.0-nightly.20260324.",
"type": "incident"
},
{
"title": "[Disclosed vulnerability] UltraHonk native verifier accepts invalid recursive proofs",
"url": "https://github.com/AztecProtocol/aztec-packages/security/advisories/GHSA-2hc8-mq24-76v2",
"date": "2026-10-08T00:00:00Z",
"description": "The native verifier can skip the deferred pairing check of recursive proofs. Fixed in 5.2.1.",
"type": "incident"
}
]
+28 -66
+2 -2
{
"badges": [],
"description": "A wallet service with a closed-source hosted frontend that keeps spending keys client-side and gives recipients a fresh stealth address for every payment through reusable ENS names.",
"detailedDescription": "Cloaked is a wallet service that separates incoming payments across fresh Ethereum addresses while presenting them as one account. Whether requested through an ENS name, the app, or the API, each address is controlled by a private key that the recipient can derive locally.\n\n### Stealth address generation\n\nUnder Cloaked's published key model, the client deterministically derives separate viewing and spending keys from a passkey secret or a wallet signature combined with a PIN. It shares the viewing key and public spending key with Cloaked so the service can generate and monitor payment addresses, but keeps the private spending key locally.\n\nFor each payment nonce, the service derives an ephemeral private key, combines it with the recipient's public spending key, and returns the resulting one-time address. The client can recreate the same ephemeral key and combine its public key with the private spending key to derive the private key controlling that address. Under this model, only the client can derive the private key needed to spend from a correctly generated address.\n\nThe derivation SDK, API schema, and standalone recovery client are published. The recovery client can derive exportable stealth private keys without using the Cloaked API.\n\nThe production web wallet itself is closed source, has no published reproducible build, and cannot be self-hosted. Users who rely on it must trust the remotely served application to preserve the client-side spending-key boundary. The API, ENS gateway, indexer, and relay are also hosted and closed source, so the complete wallet service cannot be self-hosted.\n\nThe hosted frontend is not required to hold or use spending keys. A locally run client can create an account and perform payment-address, quote, local derivation and signing, and submission flows against the hosted API. In this setup, the spending key and derived stealth private keys remain local; Cloaked receives the scoped viewing capability and signed authorizations. Assuming the client verifies the data it signs, bypassing the hosted frontend removes it as a spending-key exfiltration risk.\n\nThis differs from the usual sender-driven ERC-5564 flow. The payer does not derive the address from public recipient metadata and publish an announcement. Cloaked's service performs the derivation, keeps the address-to-account mapping, and indexes the resulting balances.\n\nWhen a user spends, the service selects one or more stealth addresses and prepares the transaction. The client re-derives the corresponding private keys and authorizes execution. Cloaked uses EIP-7702 account delegation for actions such as combining balances, paying fees in tokens, swaps, and bridging.\n\n### ENS resolution\n\nCloaked supports both the free `username.clkd.eth` subdomain created during registration and custom `.eth` names or subnames owned by the user. Linking a custom name sets Cloaked's resolver for that name but does not transfer ownership, and the user can unlink it by changing the resolver again. Both kinds of name use the same ENS CCIP-Read flow.\n\nA lookup is redirected to `api.clkd.xyz`, which creates a fresh payment address and returns a signed ENS answer. The onchain resolver checks the answer's expiry and signature against its configurable signer. For custom names, Cloaked stores the linked name only in encrypted form, although the name and its resolver configuration remain public on Ethereum.\n\nThe signature authenticates the answer as one accepted by Cloaked, but it is not a proof that the returned address was correctly derived for the named recipient. An external sender cannot independently verify recipient control from the ENS answer alone. The resolver owner can immediately replace both the gateway URLs and accepted signer.\n\n### Privacy considerations\n\nCloaked's fresh addresses do not hide the sender, token, amount, or receiving address of an individual payment. They prevent separate receives from automatically accumulating under one reused public address. Later transactions can still link addresses when they consolidate balances, use a recognizable destination, or correlate by timing and amount.\n\nThe hosted service has the viewing capability and address index needed to link an account's stealth addresses and activity. Cloaked also integrates [Privacy Pools](/privacy/projects/privacy-pools) for an Incognito balance that can break the public onchain link between a deposit and withdrawal, but Cloaked states that its relay sees both sides. The underlying pools are tracked separately because their public transactions cannot be attributed specifically to Cloaked users.",
"description": "A stealth-address wallet that receives every payment on a fresh address, with passkey login and an Incognito balance in Privacy Pools.",
"detailedDescription": "Cloaked is a wallet that gives the user a fresh receiving address for every payment and shows them all as one account. Payers use the user's name, like ltwobeat.clkd.eth, and each lookup returns a new address. Outsiders see separate payments, while Cloaked sees all of them.\n\nThe user trusts Cloaked, which holds a key that sees all their addresses, runs the closed-source app and submits every send. The user's funds sit in addresses that only they control, and an open recovery tool exports their keys without Cloaked.\n\n### Keys\n\nThe app creates the user's keys in their browser, from a passkey or a wallet signature and a four-digit PIN. Cloaked gets a viewing key that shows all the user's addresses, while only the user can spend.\n\n### Flow\n\n1. **Request an address.** The app, the API or a lookup of your name asks Cloaked for a new address. For name lookups, Cloaked's server signs the answer, so a payer has to trust that the address is yours.\n2. **Receive.** Each address is a plain account that only your spending key can control.\n3. **Send.** Cloaked picks addresses with enough balance and prepares the transaction, your app signs it, and Cloaked submits it. Change goes to a fresh address in the same transaction, and a send that uses several addresses shows onchain that they belong together.\n\n### App\n\nThe web app at [app.clkd.xyz](https://app.clkd.xyz) and the browser extension are closed source. They read balances through Cloaked's servers, and wallet logins use WalletConnect. The open [recovery tool](https://github.com/cloakedxyz/clkd-recovery) recreates the user's address keys offline.\n\n### Incognito\n\nThe Incognito balance moves funds through [Privacy Pools](/privacy/projects/privacy-pools). Cloaked submits both the deposit and the withdrawal, so it knows which belong together, and it keeps that record.\n\n### Compliance\n\nCloaked uses the user's IP address for sanctions and geographic checks and can refuse quotes or relays, for example for tokenized stocks.\n\n### Fees\n\nGas for each send comes out of the sent token, so no native balance is needed. Incognito withdrawals pay Cloaked's relay 0.1% for USDC, per its relayer API.",
"links": {
"websites": [
"https://app.clkd.xyz"
],
"documentation": [
"https://clkd.xyz/docs",
"https://clkd.xyz/docs/ens-names",
"https://clkd.xyz/openapi.json"
],
"repositories": [
"https://github.com/cloakedxyz/clkd-stealth",
"https://github.com/cloakedxyz/clkd-recovery",
"https://github.com/cloakedxyz/account",
"https://github.com/cloakedxyz/clkd-privacy-pools"
],
"socialMedia": [
"https://x.com/staycloakedxyz"
]
}
}
+25 -63
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "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.",
"exposureContinued": "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.",
"exposureShort": "Each payment reaches a fresh address, and nothing onchain names the account behind it.",
"exposureContinued": "A send draws from several addresses in one transaction unless the user picks one with Advanced Control. Its change goes to a fresh address in the same transaction, which links the two.",
"advice": "Generate a fresh address for each receive, spend one address at a time with Advanced Control, and send to destinations with no public link to the user.",
"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",
"title": "Input selection, Advanced Control and change",
"url": "https://clkd.xyz/docs/how-it-works#sending-funds"
},
{
"title": "Quote API: spendableAddresses and reusable singleAddress mode",
"url": "https://clkd.xyz/openapi.json"
"title": "A multi-address send links those addresses",
"url": "https://clkd.xyz/docs/how-it-works#what-is-visible-onchain"
}
],
"id": "publicObserver",
"value": "Recipient private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposureShort": "Spending through shared execution infrastructure makes service use recognizable, which narrows the anonymity set to Cloaked users.",
"exposureContinued": "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.",
"exposureShort": "Cloaked's relayers and fee Safe mark every spend, so an address hides only among Cloaked's users, a small set by onchain activity.",
"exposureContinued": "Timing, distinctive amounts, recurring counterparties and change outputs cluster recipients further. Incognito withdrawals from Privacy Pools carry the same relayer and fee marks.",
"advice": "Space out related payments, avoid distinctive amounts and follow the user's own change outputs to see what a counterparty can trace.",
"sources": [
{
"title": "Documented transaction links and change handling",
"title": "Documented 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"
"title": "Relay fee receiver for Privacy Pools withdrawals",
"url": "https://api.clkd.xyz/relayer/details?chainId=1&assetAddress=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
],
"id": "chainAnalyst",
"value": "Recipient at risk",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "bad",
"exposureShort": "Every spend from the hosted client goes to a third-party relay, with the stealth address, destination and amount.",
"exposureContinued": "The client is closed source, so what else reaches the RPC, hosting and analytics providers named in the privacy policy cannot be verified.",
"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.",
"exposureShort": "The app is closed source with no open alternative, so what it sends to third parties is up to Cloaked and can change at any time.",
"exposureContinued": "Today's app sends spends, quotes and balance reads to Cloaked's own API. Linking the user's own ENS name reads it through Alchemy under Cloaked's key, and wallet logins go through WalletConnect. A payer who looks up the user's name through a third-party RPC shows it that name and the new address.",
"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",
"title": "Service providers: RPC, hosting and wallet connection",
"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"
"contract": "OffchainResolver",
"title": "Name lookups return the signed answer through the payer's RPC"
}
],
"id": "networkObserver",
"value": "Recipient exposed",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "Cloaked holds the viewing key, so it can regenerate every address and tie its activity to your account.",
"exposureContinued": "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.",
"exposureShort": "Cloaked holds the viewing key, so it can regenerate every address and tie its activity to the user's account.",
"exposureContinued": "It also serves the closed-source app that handles the user's keys. Its relay submits both sides of every Incognito round trip through Privacy Pools and keeps the association.",
"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",
"exposureShort": "No announcement is published, so address derivations cannot be replayed from the chain alone.",
"exposureContinued": "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.",
"exposureContinued": "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 secrets do not follow from breaking the passkey.",
"advice": "Create the user's account with a passkey, 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": []
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "Cloaked sends funds to one-time stealth addresses instead of mixing deposits in a shared pool."
},
"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."
}
],
"category": {
"id": "stealthAddress",
"label": "Stealth address",
"description": "Payments land on fresh one-time addresses. Hides which recipient a payment is for."
},
"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.",
"description": "Balances sit in addresses the user's keys control, and the open recovery tool exports their keys. Cloaked can spend them only with the user's key material.",
"walkawayTest": {
"passed": false,
"reason": "New payment-address generation, ENS resolution, address indexing, transaction construction, and relaying depend on the hosted Cloaked service."
}
},
"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."
"description": "The web app and the browser extension are closed source. The open stealth library and recovery tool derive keys and recover funds without Cloaked. The API, ENS gateway, indexer and relay are closed."
},
"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.",
"riskSummary": "## Funds can be stolen if\n1. the closed-source app is malicious or compromised and exfiltrates spending keys.\n2. the accepted ENS signer returns an attacker-controlled payment address for a Cloaked name.\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 the spending key.\n2. the service and the app derive different address data and a payment reaches an address whose key the user cannot recreate.\n
\n## Privacy can be lost if\n1. Cloaked, or anyone who obtains its records now or later, uses the viewing keys to link every account's addresses (see privileged insider and future adversary).\n2. Cloaked's relayers and fee Safe mark every spend, so an address hides only among Cloaked's few users (see chain analyst).\n3. Cloaked changes its closed-source app to send account data to third parties, which users cannot check (see network observer).\n4. a user relies on the Incognito balance, whose relay keeps the link between deposit and withdrawal (see privileged insider).",
"tokens": [],
"trackedOn": [
"ethereum"
],
"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."
"content": "Cloaked changes its hosted app, API, ENS gateway, indexer and relay at will, without notice onchain.\n\nThe OffchainResolver is immutable. Its owner can replace the gateway URLs and the accepted signer instantly, and the signer can authenticate any answer, including a payment address. Owner and signer are the same EOA. This affects new lookups only, since existing addresses stay under their owners' keys."
}
}
+1 -1
{
"baseTimestamp": 1787312501,
"baseTimestamp": 1791536658,
"contractsDiscoDriven": false,
"hasDiscoUi": false,
"isDiscoDriven": false,
"permissionsDiscoDriven": false
}
+313 -103
+2 -2
{
"badges": [],
"description": "A wallet service with a closed-source hosted frontend that keeps spending keys client-side and gives recipients a fresh stealth Safe account for every payment through reusable ENS names.",
"detailedDescription": "Fluidkey is a wallet service that separates incoming payments across fresh Ethereum addresses while presenting them as one account. Whether requested through an ENS name, the app, or the API, each receiving address is a Safe controlled by a private key that the recipient can derive locally.\n\n### Stealth address generation\n\nUnder Fluidkey's published key model, the client deterministically derives separate viewing and spending keys from a wallet signature combined with a PIN. It shares a derived private viewing node and public spending key with Fluidkey so the service can generate and monitor payment addresses, but keeps the private spending key locally. Fluidkey also offers Privy embedded wallets on the web and device-generated keys on mobile. Recovery of those keys depends on the chosen login and backup method.\n\nFor each payment nonce, the service derives an ephemeral private key and combines it with the recipient's public spending key to generate a one-time signer address. The client can recreate the same ephemeral key and combine its public key with the private spending key to derive the private key controlling that signer. Under this model, only the client can derive the private key needed to sign for a correctly generated address.\n\nThe receiving address is a counterfactually predicted 1/1 Safe whose sole owner is the stealth signer. It can receive funds before the Safe contract is deployed. Its address depends on the Safe factory, singleton, proxy bytecode, owners, threshold, salt nonce, and initialization data. Recovery must reproduce the original parameters, including any auto-earn initialization, rather than just the user's current settings.\n\nThe derivation SDK and standalone recovery client are published. The recovery client can derive exportable stealth signer keys, predict receiving Safes, and deploy them without using the Fluidkey API.\n\nThe production web wallet itself is closed source, has no published reproducible build, and cannot be self-hosted. Users who rely on it must trust the remotely served application to preserve the client-side spending-key boundary. The API, ENS gateway, indexer, and relay are also hosted and closed source, so the complete wallet service cannot be self-hosted.\n\nThe hosted frontend is not required to hold or use spending keys. A locally run client can register and authenticate an account, independently verify service-generated receiving addresses, and recover funds from them. In this setup, the spending key and derived stealth private keys remain local. Fluidkey receives the scoped viewing capability and signed authorizations. Assuming the client verifies the data it signs, bypassing the hosted frontend removes it as a spending-key exfiltration risk.\n\nThis differs from the usual sender-driven ERC-5564 flow. The payer does not derive the address from public recipient metadata and publish an announcement. Fluidkey's service performs the derivation, keeps the address-to-account mapping, and indexes the resulting balances.\n\nWhen a user spends, the service selects one or more stealth Safes and prepares the transactions. The client re-derives the corresponding owner keys and authorizes Safe execution. The hosted client also processes accepted actions returned by the backend, so keeping keys client-side does not replace checking the transaction data supplied by the service.\n\n### ENS resolution\n\nFluidkey provides `username.fkey.eth` and `username.fkey.id` names. Both kinds of name use the same ENS CCIP-Read flow. The `.fkey.id` name also works as a web payment link.\n\nA lookup is redirected to Fluidkey's gateway, which creates a fresh payment address and returns a signed ENS answer. The onchain resolver checks the answer's expiry and signature against its configurable set of signers.\n\nThe signature authenticates the answer as one accepted by Fluidkey, but it is not a proof that the returned address was correctly derived for the named recipient. An external sender cannot independently verify recipient control from the ENS answer alone. The resolver owner can immediately replace the gateway URL and add or remove accepted signers.\n\n### Auto-earn\n\nWhen auto-earn is enabled, receiving Safes are initialized with the Fluidkey Earn module. It allows the module owner and authorized relayers to wrap native tokens when needed and deposit funds into configured ERC-4626 vaults, with the vault shares credited to the same Safe. Anyone can also submit a deposit instruction signed by an authorized relayer. These deposits do not require a new Safe owner signature for each execution.\n\nThe module owner publishes token and vault configurations identified by a hash of their contents. Changing a vault produces a different configuration hash, and selecting a different hash requires a call from the Safe. The owner cannot silently replace the vault set under an already selected hash. Both the module owner and authorized relayers can manage relayer permissions. Users can disable the module through their Safe, but funds already deposited remain exposed to the underlying vaults' losses, liquidity, and withdrawal restrictions.\n\n### Privacy considerations\n\nFluidkey's fresh addresses do not hide the sender, token, amount, or receiving address of an individual payment. They prevent separate receives from automatically accumulating under one reused public address. Later transactions can still link addresses when they consolidate balances, use a recognizable destination, or correlate by timing and amount.\n\nThe hosted service has the viewing capability and address index needed to link an account's stealth addresses and activity. Fluidkey also integrates Houdini Swap for its Hide Trail feature, which routes funds through two centralized exchanges (CEXs) to obscure the public link between the original and final addresses. This adds reliance on the swap service and participating exchanges, including their visibility into the transfers and their ability to delay or block them.",
"description": "A stealth-address wallet that receives every payment on a fresh Safe, with optional yield and bank transfers.",
"detailedDescription": "Fluidkey is a wallet that gives the user a fresh receiving address for every payment and shows them all as one account. Payers use the user's name, like l2beat.fkey.eth, and each lookup returns a new address. Outsiders see separate payments, while Fluidkey sees all of them.\n\nThe user trusts Fluidkey, which holds a key that sees all their addresses, runs the closed-source app and submits every send. If the user signs in with Google or Apple on the web, Privy also takes part in creating their keys. The user's funds sit in Safes that only they control, and an open recovery app withdraws them without Fluidkey.\n\n### Keys\n\nThe user's keys come from a wallet signature and a four-digit PIN. On the web, that wallet is usually a Privy wallet behind the user's Google or Apple login. Privy's code creates the signature, and Privy holds part of the wallet's key. The user can connect their own wallet instead, and the mobile apps create keys on their device. Fluidkey gets a viewing key that shows all the user's addresses, while only the user can spend.\n\n### Flow\n\n1. **Request an address.** The app, the API or a lookup of your name asks Fluidkey for a new address. For name lookups, Fluidkey's server signs the answer, so a payer has to trust that the address is yours.\n2. **Receive.** Each address is a new Safe controlled by a one-time key that only you can derive. It can receive funds before the Safe is deployed.\n3. **Send.** Fluidkey picks Safes with enough balance and prepares the transactions, your app signs them, and Fluidkey submits them. While open, the app also signs any transfer Fluidkey's server marks as approved, without asking. A send that spends from several Safes shows onchain that they belong together.\n\n### App\n\nThe web and mobile apps are closed source. The web app reads the chain only through Fluidkey's servers. Outside services see the user's login (Privy), username (Intercom, for support) and app errors (Sentry). Banking and identity checks use Noah, Bridge, Persona and Sumsub. For recovery, two open tools work without the app: the [stealth account kit](https://github.com/fluidkey/fluidkey-stealth-account-kit) creates addresses, and the [recovery app](https://github.com/fluidkey/sara) finds and withdraws the user's funds through any RPC.\n\n### Special features\n\nWith auto-earn on, Fluidkey deposits incoming tokens into yield vaults without repeated user approval. Fluidkey's multisig decides which vaults are offered, and the user's Safe switches to a new set only when they choose. The user can turn auto-earn off, while money already deposited keeps the vaults' risks and withdrawal limits.\n\nFluidkey computes a score for each account, partly from its balance. When the user claims it in the app, a token on Base that cannot be transferred goes to a separate stealth address of their account. That address holds nothing else, but it shows publicly that someone uses Fluidkey.\n\n### Compliance\n\nBank transfers through Noah need identity verification. Hide Trail, an optional route for more privacy, sends funds through Houdini and two exchanges, which see the route and can hold the funds.\n\n### Fees\n\nAuto-earn is free. Sends are free for 20 transactions a day on every chain but Ethereum, where gas is paid in ETH or a supported token. Bank transfers are free up to 20,000 USD or EUR a month, then cost 0.6% in and 0.25% out, per the [docs](https://docs.fluidkey.com/readme/bank).",
"links": {
"websites": [
"https://app.fluidkey.com"
],
"documentation": [
"https://docs.fluidkey.com",
"https://docs.fluidkey.com/technical-documentation/technical-walkthrough/",
"https://docs.fluidkey.com/readme/recovery/"
],
"repositories": [
"https://github.com/fluidkey/fluidkey-stealth-account-kit",
"https://github.com/fluidkey/sara",
"https://github.com/fluidkey/fluidkey-earn-module",
"https://github.com/fluidkey/fluidkey-hydrator"
],
"socialMedia": [
"https://x.com/fluidkey"
]
}
}
+46 -75
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "A payment reaches a predicted Safe before it is deployed, with no onchain data linking it to the recipient account.",
"exposureContinued": "Deployment later exposes the individual stealth signer (private signer controlling the safe) 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.",
"exposureShort": "Each payment reaches a fresh Safe, and nothing onchain names the account behind it.",
"exposureContinued": "A send that draws from several of the user's Safes shows onchain that they belong together.",
"advice": "Generate a fresh address for each receive, label payments by payer and send from one label at a time, and send to destinations with no public link to the user.",
"sources": [
{
"title": "Counterfactual receiving Safe and individual stealth signer",
"title": "Counterfactual receiving Safe and one-time 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",
"title": "Labels limit which Safes fund a send",
"url": "https://docs.fluidkey.com/readme/labels/"
}
],
"id": "publicObserver",
"value": "Recipient private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposureShort": "Safe deployment and auto-earn events make service use recognizable, which narrows the anonymity set to Fluidkey users.",
"exposureContinued": "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.",
"sentiment": "good",
"exposureShort": "At least 2,189 accounts have claimed Fluidkey's 'score' token, giving a lower bound of the anonymity set.",
"exposureContinued": "A Safe looks like any new address until Fluidkey's relayer spends from it or deposits its funds for auto-earn. A claimed score sits on its own stealth address that holds nothing else, and its amount grows partly with the account's balance.",
"advice": "Ask for a new address for each payment, also when it arrives on another chain.",
"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"
"contract": "FluidkeyScore",
"title": "Score holders, one address per claiming account"
},
{
"title": "Different auto-earn versions use different initialization data",
"url": "https://docs.fluidkey.com/technical-documentation/stealth-account-initdata/"
"title": "Score claimed in the app, based partly on total balance",
"url": "https://docs.fluidkey.com/readme/score/"
},
{
"title": "Shared derivation path permits the same addresses across chains",
"url": "https://docs.fluidkey.com/technical-documentation/technical-walkthrough/#3a-stealth-signer-derivation"
"title": "The score accrues on a stealth address that holds nothing else",
"url": "https://www.fluidkey.com/blog/fluidkey-score"
},
{
"title": "Hide Trail integration",
"url": "https://docs.fluidkey.com/readme/advanced-privacy/"
"contract": "SmartAccountRelayer",
"title": "Fluidkey's relayer contract submits every send and auto-earn deposit"
},
{
"title": "Auto-earn setup records the Safe that installs it",
"url": "https://github.com/fluidkey/fluidkey-earn-module/blob/122cde19940d06b94c0027f4cd2e22e7fa19129a/src/FluidkeyEarnModule.sol#L243-L260"
},
{
"title": "Shared derivation path permits the same address on several chains",
"url": "https://docs.fluidkey.com/technical-documentation/technical-walkthrough/#3a-stealth-signer-derivation"
}
],
"id": "chainAnalyst",
"value": "Recipient at risk",
"value": "Recipient private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "The hosted client is closed source, so which RPC and analytics providers see your Safe list cannot be verified.",
"exposureContinued": "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.",
"sentiment": "bad",
"exposureShort": "The app is closed source with no open alternative, so what it sends to third parties is up to Fluidkey and can change at any time.",
"exposureContinued": "Today, the web app reads the chain only through Fluidkey's servers and gives outside services just the user's login and username. A payer who looks up the user's name through a third-party RPC shows it that name and the new address.",
"sources": [
{
"contract": "OffchainResolver",
"title": "Offchain ENS gateway"
"title": "The app's content security policy lists every endpoint it contacts",
"url": "https://app.fluidkey.com"
},
{
"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"
"contract": "OffchainResolver",
"title": "Name lookups return the signed answer through the payer's RPC"
}
],
"id": "networkObserver",
"value": "Recipient at risk",
"value": "Recipient exposed",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "Fluidkey holds the viewing key, so it can regenerate every address and tie its activity to your account.",
"exposureContinued": "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.",
"exposureShort": "Fluidkey holds the viewing key, so it can regenerate every address and tie its activity to the user's account.",
"exposureContinued": "It also runs the closed-source app that handles the user's keys. With Google or Apple login on the web, Privy's code creates the signature the user's keys come from. Bank transfers add the user's verified identity, and Hide Trail shows the user's route to Houdini and two exchanges.",
"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"
"title": "Web keys are generated through a Privy embedded wallet",
"url": "https://docs.fluidkey.com/readme/account-set-up/"
},
{
"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",
"title": "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",
"exposureShort": "No announcement is published, so address derivations cannot be replayed from the chain alone.",
"exposureContinued": "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.",
"advice": "Create the user's account in the mobile app, with Google or Apple login, or with a new wallet that has never sent a transaction. 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"
"url": "https://github.com/fluidkey/sara/blob/e6e4fcd9e718722948b82fa0e819e0c4fca46e0d/src/components/GenerateKeysJourneyStep.tsx#L28-L90"
}
],
"id": "futureAdversary",
"value": "Recipient at risk",
"alsoExposed": []
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "Fluidkey sends funds to individual stealth Safes instead of mixing deposits in a shared pool."
},
"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."
}
],
"category": {
"id": "stealthAddress",
"label": "Stealth address",
"description": "Payments land on fresh one-time addresses. Hides which recipient a payment is for."
},
"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.",
"description": "The user's funds sit in Safes controlled only by their keys. The open recovery app finds and withdraws them without Fluidkey. Money in auto-earn vaults follows the vaults' withdrawal rules.",
"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."
"reason": "Name lookups, new addresses in the app, finding the user's Safes and submitting sends all run on Fluidkey's servers."
}
},
"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."
"description": "The web and mobile apps are closed source. The open stealth account kit and recovery app derive keys, verify addresses and recover funds without Fluidkey. The API, ENS gateway, indexer and relayer are closed."
},
"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.",
"riskSummary": "## Funds can be stolen if\n1. the closed-source app is malicious or hacked and steals the user's spending keys.\n2. Privy's code or key share is compromised, for accounts created with Google or Apple login on the web.\n3. a Fluidkey signer returns an attacker's address when a payer looks up a Fluidkey name.\n4. Fluidkey's server marks a transfer as approved, and the open app signs it without showing the user.\n
\n## Funds can be lost if\n1. a user loses the login, wallet, PIN or backup needed to recover the spending key.\n2. Fluidkey's server gives out a wrong address, for example one built with a different Safe setup, and the user's keys cannot withdraw from it.\n3. an auto-earn vault loses funds or blocks withdrawals, or a service used by Hide Trail keeps the funds.\n
\n## Privacy can be lost if\n1. Fluidkey, or anyone who obtains its records, uses the viewing keys to link all Safes of every account (see privileged insider and future adversary).\n2. Fluidkey changes its closed-source app to send account data to outside services, which users cannot check (see network observer).\n3. a user takes Hide Trail and Houdini or the exchanges link the route from their records (see privileged insider).",
"tokens": [],
"trackedOn": [
"ethereum",
"base",
"arbitrum",
"optimism",
"polygonpos",
"gnosis"
],
"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."
"content": "Fluidkey changes its app, API, name gateway, indexer and relayer at will, without notice onchain.\n\nThe OffchainResolver is immutable. Its owner, the Fluidkey Resolver Multisig, can instantly change where name lookups are answered and which keys may sign the answers. A signer can return any address for a name. This affects new lookups only, since existing Safes stay under their owners' keys.\n\nThe Fluidkey Earn module is immutable on each chain. Its owner, the Fluidkey Earn Owner multisig, publishes vault sets and chooses the relayers, which can also add or remove other relayers. An existing Safe moves to a new vault set only by its own call."
}
}
+121 -19
{
"arbitrum": {
"roles": [],
"actors": [
{
"id": "Fluidkey Earn Owner",
"name": "Fluidkey Earn Owner",
"description": "A Multisig with 2/3 threshold. \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe ",
"accounts": [
{
"address": "arb1:0x9E3eba321427941868cB4123De97DAB145C9e7CD",
"type": "Contract",
"isVerified": true,
"name": "0x9E3e…e7CD",
"url": "https://arbiscan.io/address/0x9E3eba321427941868cB4123De97DAB145C9e7CD"
}
],
"chain": "arbitrum",
"references": [],
"participants": [
{
"address": "arb1:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://arbiscan.io/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "arb1:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://arbiscan.io/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "arb1:0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1",
"type": "EOA",
"isVerified": true,
"name": "0x3066…08c1",
"url": "https://arbiscan.io/address/0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-1",
"name": "EOA 1",
"accounts": [
{
"address": "arb1:0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"type": "EOA",
"isVerified": true,
"name": "0x53d9…151D",
"url": "https://arbiscan.io/address/0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"displayName": "EOA 1"
}
],
"chain": "arbitrum",
"description": "* Can interact with FluidkeyEarnModule\n * initiate deposits from enabled Safes into their selected vault configurations, and add or remove authorized relayers ",
"discoveryDrivenData": true
},
{
"id": "EOA-2",
"name": "EOA 2",
"accounts": [
{
"address": "arb1:0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"type": "EOA",
"isVerified": true,
"name": "0xe782…eD7a",
"url": "https://arbiscan.io/address/0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"displayName": "EOA 2"
}
],
"chain": "arbitrum",
"description": "* Can interact with SmartAccountRelayer\n * authorize every call this contract forwards ",
"discoveryDrivenData": true
}
]
},
"base": {
"roles": [],
"actors": [
{
"id": "Fluidkey Earn Owner",
"name": "Fluidkey Earn Owner",
"description": "A Multisig with 2/3 threshold. \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe ",
"description": "A Multisig with 2/3 threshold. \n* Can upgrade **with no delay**\n * FluidkeyScore \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe \n* Can interact with FluidkeyScore\n * choose which addresses may transfer the score, such as the distributor ",
"accounts": [
{
"address": "base:0x9E3eba321427941868cB4123De97DAB145C9e7CD",
"type": "Contract",
"isVerified": true,
"name": "0x9E3e…e7CD",
"url": "https://basescan.org/address/0x9E3eba321427941868cB4123De97DAB145C9e7CD"
}
],
"chain": "base",
"references": [],
"participants": [
{
"address": "base:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://basescan.org/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "base:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://basescan.org/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "base:0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1",
"type": "EOA",
"isVerified": true,
"name": "0x3066…08c1",
"url": "https://basescan.org/address/0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-2",
"name": "EOA 2",
"id": "EOA-3",
"name": "EOA 3",
"accounts": [
{
"address": "base:0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"type": "EOA",
"isVerified": true,
"name": "0x53d9…151D",
"url": "https://basescan.org/address/0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"displayName": "EOA 2"
"displayName": "EOA 3"
}
],
"chain": "base",
"description": "* Can interact with FluidkeyEarnModule\n * initiate deposits from enabled Safes into their selected vault configurations, and add or remove authorized relayers ",
"discoveryDrivenData": true
},
{
"id": "EOA-4",
"name": "EOA 4",
"accounts": [
{
"address": "base:0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"type": "EOA",
"isVerified": true,
"name": "0xe782…eD7a",
"url": "https://basescan.org/address/0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"displayName": "EOA 4"
}
],
"chain": "base",
"description": "* Can interact with SmartAccountRelayer\n * authorize every call this contract forwards ",
"discoveryDrivenData": true
}
]
},
"ethereum": {
"roles": [],
"actors": [
{
"id": "Fluidkey Earn Owner",
"name": "Fluidkey Earn Owner",
"description": "A Multisig with 2/3 threshold. \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe ",
"accounts": [
{
"address": "eth:0x9E3eba321427941868cB4123De97DAB145C9e7CD",
"type": "Contract",
"isVerified": true,
"name": "0x9E3e…e7CD",
"url": "https://etherscan.io/address/0x9E3eba321427941868cB4123De97DAB145C9e7CD"
}
],
"chain": "ethereum",
"references": [],
"participants": [
{
"address": "eth:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://etherscan.io/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "eth:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://etherscan.io/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "eth:0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1",
"type": "EOA",
"isVerified": true,
"name": "0x3066…08c1",
"url": "https://etherscan.io/address/0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1"
}
],
"discoveryDrivenData": true
},
{
"id": "Fluidkey Resolver Multisig",
"name": "Fluidkey Resolver Multisig",
"description": "A Multisig with 3/5 threshold. \n* Can interact with OffchainResolver\n * change the accepted signers and gateway URL immediately, or transfer resolver ownership ",
"accounts": [
{
"address": "eth:0xdcC34c0da55cEF7AeD38Bb749AD97DAC12A9936C",
"type": "Contract",
"isVerified": true,
"name": "0xdcC3…936C",
"url": "https://etherscan.io/address/0xdcC34c0da55cEF7AeD38Bb749AD97DAC12A9936C"
}
],
"chain": "ethereum",
"references": [],
"participants": [
{
"address": "eth:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://etherscan.io/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "eth:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://etherscan.io/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "eth:0x5a655820821bf7af7B23566b34ce0CcBd9c9a37f",
"type": "EOA",
"isVerified": true,
"name": "0x5a65…a37f",
"url": "https://etherscan.io/address/0x5a655820821bf7af7B23566b34ce0CcBd9c9a37f"
},
{
"address": "eth:0x74C19105f358BAb85f8E9FDA9202A1326A714d89",
"type": "EOA",
"isVerified": true,
"name": "0x74C1…4d89",
"url": "https://etherscan.io/address/0x74C19105f358BAb85f8E9FDA9202A1326A714d89"
},
{
"address": "eth:0xFE972D3976F7cc5ba1C9b471D48362B82C6c5fDd",
"type": "EOA",
"isVerified": true,
"name": "0xFE97…5fDd",
"url": "https://etherscan.io/address/0xFE972D3976F7cc5ba1C9b471D48362B82C6c5fDd"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-4",
"name": "EOA 4",
"id": "EOA-6",
"name": "EOA 6",
"accounts": [
{
"address": "eth:0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"type": "EOA",
"isVerified": true,
"name": "0x53d9…151D",
"url": "https://etherscan.io/address/0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"displayName": "EOA 4"
"displayName": "EOA 6"
}
],
"chain": "ethereum",
"description": "* Can interact with FluidkeyEarnModule\n * initiate deposits from enabled Safes into their selected vault configurations, and add or remove authorized relayers ",
"discoveryDrivenData": true
},
{
"id": "EOA-3",
"name": "EOA 3",
"id": "EOA-7",
"name": "EOA 7",
"accounts": [
{
"address": "eth:0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"type": "EOA",
"isVerified": true,
"name": "0xe782…eD7a",
"url": "https://etherscan.io/address/0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"displayName": "EOA 7"
}
],
"chain": "ethereum",
"description": "* Can interact with SmartAccountRelayer\n * authorize every call this contract forwards ",
"discoveryDrivenData": true
},
{
"id": "EOA-5",
"name": "EOA 5",
"accounts": [
{
"address": "eth:0x269fDaBd799179189C98D960C58C3793CcedDB85",
"type": "EOA",
"isVerified": true,
"name": "0x269f…DB85",
"url": "https://etherscan.io/address/0x269fDaBd799179189C98D960C58C3793CcedDB85",
"displayName": "EOA 3"
"displayName": "EOA 5"
}
],
"chain": "ethereum",
"description": "* Can interact with OffchainResolver\n * sign arbitrary offchain ENS answers, including payment addresses accepted by the resolver ",
"discoveryDrivenData": true
}
]
},
"gnosis": {
"roles": [],
"actors": [
{
"id": "Fluidkey Earn Owner",
"name": "Fluidkey Earn Owner",
"description": "A Multisig with 2/3 threshold. \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe ",
"accounts": [
{
"address": "gno:0x9E3eba321427941868cB4123De97DAB145C9e7CD",
"type": "Contract",
"isVerified": true,
"name": "0x9E3e…e7CD",
"url": "https://gnosis.blockscout.com/address/0x9E3eba321427941868cB4123De97DAB145C9e7CD"
}
],
"chain": "gnosis",
"references": [],
"participants": [
{
"address": "gno:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://gnosis.blockscout.com/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "gno:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://gnosis.blockscout.com/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "gno:0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1",
"type": "EOA",
"isVerified": true,
"name": "0x3066…08c1",
"url": "https://gnosis.blockscout.com/address/0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-5",
"name": "EOA 5",
"id": "EOA-8",
"name": "EOA 8",
"accounts": [
{
"address": "gno:0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"type": "EOA",
"isVerified": true,
"name": "0x53d9…151D",
"url": "https://gnosis.blockscout.com/address/0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"displayName": "EOA 5"
"displayName": "EOA 8"
}
],
"chain": "gnosis",
"description": "* Can interact with FluidkeyEarnModule\n * initiate deposits from enabled Safes into their selected vault configurations, and add or remove authorized relayers ",
"discoveryDrivenData": true
},
{
"id": "EOA-9",
"name": "EOA 9",
"accounts": [
{
"address": "gno:0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"type": "EOA",
"isVerified": true,
"name": "0xe782…eD7a",
"url": "https://gnosis.blockscout.com/address/0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"displayName": "EOA 9"
}
],
"chain": "gnosis",
"description": "* Can interact with SmartAccountRelayer\n * authorize every call this contract forwards ",
"discoveryDrivenData": true
}
]
},
"optimism": {
"roles": [],
"actors": [
{
"id": "Fluidkey Earn Owner",
"name": "Fluidkey Earn Owner",
"description": "A Multisig with 2/3 threshold. \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe ",
"accounts": [
{
"address": "oeth:0x9E3eba321427941868cB4123De97DAB145C9e7CD",
"type": "Contract",
"isVerified": true,
"name": "0x9E3e…e7CD",
"url": "https://optimistic.etherscan.io/address/0x9E3eba321427941868cB4123De97DAB145C9e7CD"
}
],
"chain": "optimism",
"references": [],
"participants": [
{
"address": "oeth:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://optimistic.etherscan.io/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "oeth:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://optimistic.etherscan.io/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "oeth:0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1",
"type": "EOA",
"isVerified": true,
"name": "0x3066…08c1",
"url": "https://optimistic.etherscan.io/address/0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-7",
"name": "EOA 7",
"id": "EOA-12",
"name": "EOA 12",
"accounts": [
{
"address": "oeth:0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"type": "EOA",
"isVerified": true,
"name": "0x53d9…151D",
"url": "https://optimistic.etherscan.io/address/0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"displayName": "EOA 7"
"displayName": "EOA 12"
}
],
"chain": "optimism",
"description": "* Can interact with FluidkeyEarnModule\n * initiate deposits from enabled Safes into their selected vault configurations, and add or remove authorized relayers ",
"discoveryDrivenData": true
},
{
"id": "EOA-13",
"name": "EOA 13",
"accounts": [
{
"address": "oeth:0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"type": "EOA",
"isVerified": true,
"name": "0xe782…eD7a",
"url": "https://optimistic.etherscan.io/address/0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"displayName": "EOA 13"
}
],
"chain": "optimism",
"description": "* Can interact with SmartAccountRelayer\n * authorize every call this contract forwards ",
"discoveryDrivenData": true
}
]
},
"polygonpos": {
"roles": [],
"actors": [
{
"id": "Fluidkey Earn Owner",
"name": "Fluidkey Earn Owner",
"description": "A Multisig with 2/3 threshold. \n* Can interact with FluidkeyEarnModule\n * publish vault configurations, manage authorized relayers, and initiate deposits into the configurations selected by enabled Safes. Changing a Safe's selected configuration hash requires a call from that Safe ",
"accounts": [
{
"address": "matic:0x9E3eba321427941868cB4123De97DAB145C9e7CD",
"type": "Contract",
"isVerified": true,
"name": "0x9E3e…e7CD",
"url": "https://polygonscan.com/address/0x9E3eba321427941868cB4123De97DAB145C9e7CD"
}
],
"chain": "polygonpos",
"references": [],
"participants": [
{
"address": "matic:0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614",
"type": "EOA",
"isVerified": true,
"name": "0xB9e7…4614",
"url": "https://polygonscan.com/address/0xB9e7de28c2e6C8F3C29Fc0e061485A34C5864614"
},
{
"address": "matic:0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10",
"type": "EOA",
"isVerified": true,
"name": "0xa13E…3E10",
"url": "https://polygonscan.com/address/0xa13EFDd0d500cB9DA8140842cf624f1D5e273E10"
},
{
"address": "matic:0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1",
"type": "EOA",
"isVerified": true,
"name": "0x3066…08c1",
"url": "https://polygonscan.com/address/0x3066e11cd9a68AbFd2C3798b4617aA44E9Dc08c1"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-6",
"name": "EOA 6",
"id": "EOA-10",
"name": "EOA 10",
"accounts": [
{
"address": "matic:0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"type": "EOA",
"isVerified": true,
"name": "0x53d9…151D",
"url": "https://polygonscan.com/address/0x53d92eCe145696ff27A7d1F8746632C3deBB151D",
"displayName": "EOA 6"
"displayName": "EOA 10"
}
],
"chain": "polygonpos",
"description": "* Can interact with FluidkeyEarnModule\n * initiate deposits from enabled Safes into their selected vault configurations, and add or remove authorized relayers ",
"discoveryDrivenData": true
},
{
"id": "EOA-11",
"name": "EOA 11",
"accounts": [
{
"address": "matic:0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"type": "EOA",
"isVerified": true,
"name": "0xe782…eD7a",
"url": "https://polygonscan.com/address/0xe78288221aaE3A0D3ddf8Fa08D3602C3014eeD7a",
"displayName": "EOA 11"
}
],
"chain": "polygonpos",
"description": "* Can interact with SmartAccountRelayer\n * authorize every call this contract forwards ",
"discoveryDrivenData": true
}
]
}
}
+129 -6
{
"addresses": {
"arbitrum": [
{
"name": "FluidkeyEarnModule",
"isVerified": true,
"address": "arb1:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "arbitrum",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 1\n * **owner**: Fluidkey Earn Owner",
"discoveryDrivenData": true,
"url": "https://arbiscan.io/address/0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9#code"
},
{
"name": "SmartAccountRelayer",
"isVerified": true,
"address": "arb1:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "arbitrum",
"description": "Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n* Roles:\n * **authorizedCaller**: EOA 2",
"discoveryDrivenData": true,
"url": "https://arbiscan.io/address/0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0#code"
}
],
"base": [
{
"name": "FluidkeyEarnModule",
"isVerified": true,
"address": "base:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "base",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 2\n * **owner**: Fluidkey Earn Owner",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 3\n * **owner**: Fluidkey Earn Owner",
"discoveryDrivenData": true,
"url": "https://basescan.org/address/0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9#code"
},
{
"name": "SmartAccountRelayer",
"isVerified": true,
"address": "base:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "base",
"description": "Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n* Roles:\n * **authorizedCaller**: EOA 4",
"discoveryDrivenData": true,
"url": "https://basescan.org/address/0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0#code"
},
{
"name": "FluidkeyScore",
"isVerified": true,
"address": "base:0x894c663757f6953544548EFA1aebc0846AC08bEa",
"upgradeability": {
"proxyType": "EIP1967 proxy",
"admins": [
"base:0x9E3eba321427941868cB4123De97DAB145C9e7CD"
],
"implementations": [
"base:0xB5311Cf821AB519CCF08e8f80742efDDC7d6659F"
]
},
"chain": "base",
"pastUpgrades": [
{
"timestamp": 1748264389,
"transactionHash": "0xb6021b7130d6faf2da89cf3e606c4e49b72e040e2a8a3f7251b6af3a1aa55f15",
"implementations": [
"base:0xB5311Cf821AB519CCF08e8f80742efDDC7d6659F"
]
}
],
"description": "Non-transferable ERC-20 score that Fluidkey's distributor allocates to its users, based partly on their total balance. Only the owner and whitelisted senders can transfer it.\n* Roles:\n * **admin**: Fluidkey Earn Owner\n * **owner**: Fluidkey Earn Owner",
"upgradableBy": [
{
"name": "Fluidkey Earn Owner",
"delay": "no"
}
],
"discoveryDrivenData": true,
"url": "https://basescan.org/address/0x894c663757f6953544548EFA1aebc0846AC08bEa#code"
}
],
"ethereum": [
{
"name": "FluidkeyEarnModule",
"isVerified": true,
"address": "eth:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 4\n * **owner**: Fluidkey Earn Owner",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 6\n * **owner**: Fluidkey Earn Owner",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9#code"
},
{
"name": "SmartAccountRelayer",
"isVerified": true,
"address": "eth:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n* Roles:\n * **authorizedCaller**: EOA 7",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0#code"
},
{
"name": "OffchainResolver",
"isVerified": true,
"address": "eth:0x9AcF316290AaA62edafdDDaC48B124032C36EB3c",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Immutable ENS CCIP-Read resolver used by Fluidkey. Every query redirects to a configurable gateway and accepts the returned ENS record if it is unexpired and signed by an accepted signer. It does not verify that a returned payment address was derived for the named recipient.\n* Roles:\n * **owner**: Fluidkey Resolver Multisig\n * **signers**: EOA 3",
"description": "Immutable ENS CCIP-Read resolver used by Fluidkey. Every query redirects to a configurable gateway and accepts the returned ENS record if it is unexpired and signed by an accepted signer. It does not verify that a returned payment address was derived for the named recipient.\n* Roles:\n * **owner**: Fluidkey Resolver Multisig\n * **signers**: EOA 5",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x9AcF316290AaA62edafdDDaC48B124032C36EB3c#code"
}
],
"gnosis": [
{
"name": "FluidkeyEarnModule",
"isVerified": true,
"address": "gno:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "gnosis",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 5\n * **owner**: Fluidkey Earn Owner",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 8\n * **owner**: Fluidkey Earn Owner",
"discoveryDrivenData": true,
"url": "https://gnosis.blockscout.com/address/0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9#code"
},
{
"name": "SmartAccountRelayer",
"isVerified": true,
"address": "gno:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "gnosis",
"description": "Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n* Roles:\n * **authorizedCaller**: EOA 9",
"discoveryDrivenData": true,
"url": "https://gnosis.blockscout.com/address/0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0#code"
}
],
"polygonpos": [
{
"name": "FluidkeyEarnModule",
"isVerified": true,
"address": "matic:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "polygonpos",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 6\n * **owner**: Fluidkey Earn Owner",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 10\n * **owner**: Fluidkey Earn Owner",
"discoveryDrivenData": true,
"url": "https://polygonscan.com/address/0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9#code"
},
{
"name": "SmartAccountRelayer",
"isVerified": true,
"address": "matic:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "polygonpos",
"description": "Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n* Roles:\n * **authorizedCaller**: EOA 11",
"discoveryDrivenData": true,
"url": "https://polygonscan.com/address/0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0#code"
}
],
"optimism": [
{
"name": "FluidkeyEarnModule",
"isVerified": true,
"address": "oeth:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "optimism",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 7\n * **owner**: Fluidkey Earn Owner",
"description": "Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n* Roles:\n * **authorizedRelayers**: EOA 12\n * **owner**: Fluidkey Earn Owner",
"discoveryDrivenData": true,
"url": "https://optimistic.etherscan.io/address/0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9#code"
},
{
"name": "SmartAccountRelayer",
"isVerified": true,
"address": "oeth:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "optimism",
"description": "Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n* Roles:\n * **authorizedCaller**: EOA 13",
"discoveryDrivenData": true,
"url": "https://optimistic.etherscan.io/address/0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0#code"
}
]
},
"risks": []
}
+1 -1
{
"baseTimestamp": 1791789271,
"baseTimestamp": 1791549206,
"contractsDiscoDriven": false,
"hasDiscoUi": false,
"isDiscoDriven": false,
"permissionsDiscoDriven": false
}
+14 -0
[
{
"id": "4b078ceb",
"date": "Fri, 09 Oct 2026 12:34:48 GMT",
"timestamp": 1791549206,
"description": "Added the FluidkeyScore token on Base, whose holder count bounds the number of accounts. Added the SmartAccountRelayer on all six chains, through which Fluidkey submits sends and auto-earn deposits. Moved OffchainResolver and FluidkeyEarnModule from config overrides into templates, with no value changes.",
"isHighSeverity": false,
"changeCount": 7,
"sections": [
{
"kind": "config-related-changes",
"body": "```diff\n+ Status: CREATED\n contract SmartAccountRelayer (arb1:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0) [fluidkey/SmartAccountRelayer]\n +++ description: Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n```\n\n```diff\n+ Status: CREATED\n contract SmartAccountRelayer (base:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0) [fluidkey/SmartAccountRelayer]\n +++ description: Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n```\n\n```diff\n+ Status: CREATED\n contract FluidkeyScore (base:0x894c663757f6953544548EFA1aebc0846AC08bEa) [fluidkey/FluidkeyScore]\n +++ description: Non-transferable ERC-20 score that Fluidkey's distributor allocates to its users, based partly on their total balance. Only the owner and whitelisted senders can transfer it.\n```\n\n```diff\n+ Status: CREATED\n contract SmartAccountRelayer (eth:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0) [fluidkey/SmartAccountRelayer]\n +++ description: Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n```\n\n```diff\n+ Status: CREATED\n contract SmartAccountRelayer (gno:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0) [fluidkey/SmartAccountRelayer]\n +++ description: Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n```\n\n```diff\n+ Status: CREATED\n contract SmartAccountRelayer (matic:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0) [fluidkey/SmartAccountRelayer]\n +++ description: Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n```\n\n```diff\n+ Status: CREATED\n contract SmartAccountRelayer (oeth:0x8090a9DB6Aca56fFA186C75Ca0787B18af1058a0) [fluidkey/SmartAccountRelayer]\n +++ description: Immutable contract through which Fluidkey submits Safe deployments, sends, auto-earn deposits and score transfers. Anyone can submit an operation, but each needs a signature from the authorized caller. It holds no rights over Safes, so a send still needs the Safe owner's signature.\n```"
}
]
},
{
"id": "838ab14e",
"date": "Tue, 08 Sep 2026 08:47:06 GMT",
"timestamp": 1788853601,
"description": "Initial discovery of the Fluidkey ENS resolver, its owner and signers, and the auto-earn module and its permissions on Ethereum, Base, Arbitrum, Optimism, Polygon PoS, and Gnosis.",
"isHighSeverity": false,
"changeCount": 14,
"sections": [
{
"kind": "initial-discovery",
"body": "```diff\n+ Status: CREATED\n contract FluidkeyEarnModule (arb1:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9) [N/A]\n +++ description: Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Earn Owner (arb1:0x9E3eba321427941868cB4123De97DAB145C9e7CD) [GnosisSafe]\n +++ description: None\n```\n\n```diff\n+ Status: CREATED\n contract FluidkeyEarnModule (base:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9) [N/A]\n +++ description: Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Earn Owner (base:0x9E3eba321427941868cB4123De97DAB145C9e7CD) [GnosisSafe]\n +++ description: None\n```\n\n```diff\n+ Status: CREATED\n contract FluidkeyEarnModule (eth:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9) [N/A]\n +++ description: Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n```\n\n```diff\n+ Status: CREATED\n contract OffchainResolver (eth:0x9AcF316290AaA62edafdDDaC48B124032C36EB3c) [N/A]\n +++ description: Immutable ENS CCIP-Read resolver used by Fluidkey. Every query redirects to a configurable gateway and accepts the returned ENS record if it is unexpired and signed by an accepted signer. It does not verify that a returned payment address was derived for the named recipient.\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Earn Owner (eth:0x9E3eba321427941868cB4123De97DAB145C9e7CD) [GnosisSafe]\n +++ description: None\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Resolver Multisig (eth:0xdcC34c0da55cEF7AeD38Bb749AD97DAC12A9936C) [GnosisSafe]\n +++ description: None\n```\n\n```diff\n+ Status: CREATED\n contract FluidkeyEarnModule (gno:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9) [N/A]\n +++ description: Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Earn Owner (gno:0x9E3eba321427941868cB4123De97DAB145C9e7CD) [GnosisSafe]\n +++ description: None\n```\n\n```diff\n+ Status: CREATED\n contract FluidkeyEarnModule (matic:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9) [N/A]\n +++ description: Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Earn Owner (matic:0x9E3eba321427941868cB4123De97DAB145C9e7CD) [GnosisSafe]\n +++ description: None\n```\n\n```diff\n+ Status: CREATED\n contract FluidkeyEarnModule (oeth:0x3BDb857AFe9b51d8916D80240d2ADe40D4d3f2f9) [N/A]\n +++ description: Safe module used for Fluidkey auto-earn. Authorized relayers can deposit tokens into the vault selected by the Safe's configuration hash, with vault shares credited to that Safe. The module owner publishes configurations, but selecting a different hash requires a call from the Safe.\n```\n\n```diff\n+ Status: CREATED\n contract Fluidkey Earn Owner (oeth:0x9E3eba321427941868cB4123De97DAB145C9e7CD) [GnosisSafe]\n +++ description: None\n```"
}
]
}
]
+9 -9
+2 -2
{
"badges": [],
"description": "A round trip from Ethereum into Monero and back through the swap screen of Cake Wallet, which hands each leg to a centralized instant exchange. Monero's ledger serves as the privacy pool.",
"detailedDescription": "Cake Wallet lets users move funds out of one Ethereum address and into a new one through Monero, which hides the path between them. Its swap screen sends the funds to an exchange service that pays out XMR. A second swap later turns the XMR back into an Ethereum asset. Both Ethereum transfers are public.\n\nUsers trust the swap services, which hold the funds during each swap, and Cake Labs, which ships the wallet and picks the services. A service can hold a deposit until the user passes identity checks, and a party holding the records of both legs can link them. Funds in transit sit with the service, so leaving depends on its payout or refund.\n\n### Flow\n\n1. **Quote.** EVM asset to XMR: the app polls every enabled service and takes the best rate unless the user picks one.\n2. **Ethereum to the service.** The app sends the amount, the Monero payout address and Cake's API key, for some providers with a markup. The Ethereum wallet address goes along as refund address. The service returns a deposit address it controls. The deposit is a plain, public transfer from the user's wallet. It goes first to the Blink Labs private mempool, on by default, then to the RPC.\n3. **Into Monero.** The service sends XMR to the payout address. The transaction hides sender, recipient and amount.\n4. **Inside Monero.** The user can self-transfer the XMR any number of times. Outputs unlock after ten blocks.\n5. **Back to Ethereum.** From the Monero wallet the user picks XMR to the Ethereum asset. The app sends the Ethereum exit address and, as refund address, a subaddress not yet used for a swap. The service returns a Monero deposit address, the wallet pays it, and the service pays the exit address from its hot wallet.\n\n### Architecture and Privacy\n\nMonero hides which output a transaction spends with ring signatures: each input names 16 outputs, the real one and 15 decoys the wallet picks, and proves that one of them is spent. The rings stay public forever. This protects against observers who see only the chain.\n\nEach service sees its whole leg. The entry service always knows the output it paid, because it created it. The exit service receives the Monero transaction and sees its rings. A party holding both legs therefore finds its payout output in the rings behind the exit. If all of the XMR is paid back, the received amount also matches the payout minus the public fee. Self-transfers in between only widen the search, by about 30 times per transaction. The swap screen lets the user pick a different exchange for each leg.\n\nShielded pools such as Tornado Cash, Privacy Pools or Zcash's shielded pool prove membership in the whole pool. A withdrawal there hides among all deposits, even from a party that knows one of them.\n\nAn EVM wallet in Cake has one address. It is sent as refund address on entry, and an exit to the same wallet pays the very address that deposited. On entry, picking an in-app Monero wallet pays to its primary address every time, so a service can tie a user's entries together.\n\n[Built-in Tor](https://docs.cakewallet.com/features/privacy-and-security/built-in-tor) is off by default. Without it, the swap services, Blink, Etherscan, the nodes, Cake's price API and Moralis see the user's IP. Moralis receives every Ethereum wallet the app opens, and the app offers no switch for it. With Tor on, every call shares one circuit for up to ten minutes, so each leg needs its own app session. Keeping both Ethereum wallets in the app links them for Moralis whenever both are opened close together.\n\n### Custody, fees and compliance\n\nServices can freeze a deposit automatically and hold it for identity and compliance checks, as [ChangeNOW documents](https://changenow.io/faq/kyc-aml). Cake ships its API keys, and for some providers a markup, as build secrets, so its share of the fee is set outside the source. Cake Labs holds no user funds, and the app talks to each service directly.",
"description": "A shielded ledger round trip: Cake Wallet's swap screen moves Ethereum funds into Monero and back, handing each leg to a centralized instant exchange.",
"detailedDescription": "Cake Wallet lets users move funds out of one Ethereum address and into a new one through Monero, which hides the path between them. Its swap screen sends the funds to an exchange service that pays out XMR. A second swap later turns the XMR back into an Ethereum asset. Both Ethereum transfers are public.\n\nUsers trust the swap services, which hold the funds during each swap, and Cake Labs, which ships the wallet and picks the services. A service can hold a deposit until the user passes identity checks, and a party holding the records of both legs can link them. Funds in transit sit with the service, so leaving depends on its payout or refund.\n\n### Architecture\n\nMonero hides which output a transaction spends with ring signatures. Each input names 16 outputs, the real one and 15 decoys the wallet picks, and proves that one of them is spent. The rings stay public forever. This protects against observers who see only the chain.\n\nShielded pools such as Tornado Cash, Privacy Pools or Zcash's shielded pool prove membership in the whole pool. A withdrawal there hides among all deposits, even from a party that knows one of them.\n\n### Flow\n\n1. **Quote.** EVM asset to XMR: the app polls every enabled service and takes the best rate unless the user picks one.\n2. **Ethereum to the service.** The app sends the amount, the Monero payout address and Cake's API key, for some providers with a markup. The Ethereum wallet address goes along as refund address. The service returns a deposit address it controls. The deposit is a plain, public transfer from the user's wallet. It goes first to the Blink Labs private mempool, on by default, then to the RPC.\n3. **Into Monero.** The service sends XMR to the payout address. The transaction hides sender, recipient and amount.\n4. **Inside Monero.** The user can self-transfer the XMR any number of times. Outputs unlock after ten blocks.\n5. **Back to Ethereum.** From the Monero wallet the user picks XMR to the Ethereum asset. The app sends the Ethereum exit address and, as refund address, a subaddress not yet used for a swap. The service returns a Monero deposit address, the wallet pays it, and the service pays the exit address from its hot wallet.\n\n### App\n\nAn EVM wallet in Cake has one address. It is sent as refund address on entry, and an exit to the same wallet pays the very address that deposited. On entry, picking an in-app Monero wallet pays to its primary address every time, so a service can tie a user's entries together.\n\n[Built-in Tor](https://docs.cakewallet.com/features/privacy-and-security/built-in-tor) is off by default. Without it, the swap services, Blink, Etherscan, the nodes, Cake's price API and Moralis see the user's IP. Moralis receives every Ethereum wallet the app opens, and the app offers no switch for it. With Tor on, every call shares one circuit for up to ten minutes, so each leg needs its own app session. Keeping both Ethereum wallets in the app links them for Moralis whenever both are opened close together.\n\n### Swap services\n\nEach service sees its whole leg. The entry service always knows the output it paid, because it created it. The exit service receives the Monero transaction and sees its rings. A party holding both legs therefore finds its payout output in the rings behind the exit. If all of the XMR is paid back, the received amount also matches the payout minus the public fee. Self-transfers in between only widen the search, by about 30 times per transaction. The swap screen lets the user pick a different exchange for each leg. Cake Labs holds no user funds, and the app talks to each service directly.\n\n### Compliance\n\nServices can freeze a deposit automatically and hold it for identity and compliance checks, as [ChangeNOW documents](https://changenow.io/faq/kyc-aml).\n\n### Fees\n\nCake ships its API keys, and for some providers a markup, as build secrets, so its share of the fee is set outside the source.",
"links": {
"websites": [
"https://cakewallet.com",
"https://www.getmonero.org"
],
"documentation": [
"https://docs.cakewallet.com/features/basic/swap",
"https://docs.cakewallet.com/support/swap",
"https://docs.cakewallet.com/features/privacy-and-security/privacy-settings",
"https://www.getmonero.org/library/Zero-to-Monero-2-0-0.pdf"
],
"explorers": [],
"repositories": [
"https://github.com/cake-tech/cake_wallet",
"https://github.com/monero-project/monero"
],
"socialMedia": [
"https://x.com/cakewallet",
"https://x.com/monero"
],
"other": [
"https://github.com/cake-tech/cake_wallet/blob/main/PRIVACY.md",
"https://changenow.io/faq/kyc-aml",
"https://kycnot.me/service/trocador"
]
}
}
+7 -7
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which funds leaving Ethereum come back as which, by passing them through Monero between two custodial swaps. Both Ethereum legs 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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "The Monero transactions between the deposit into a swap service and the payout from its hot wallet hide sender, recipient and amount.",
"exposureContinued": "Both Ethereum transfers show address, token and amount.",
"advice": "Exit to a new in-app Ethereum wallet made with Create New Recovery Phrase.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": {
"verdict": "exposed",
"note": "Only XMR exists inside. The source asset is public on Ethereum."
},
"linkage": {
"verdict": "atRisk",
"note": "An EVM wallet in Cake has one address, so exiting to the wallet that deposited pays the address that deposited."
}
},
"sources": [
{
"title": "The deposit is a plain transfer from the wallet to the address the service returned",
"url": "https://github.com/cake-tech/cake_wallet/blob/7ce3cd75c3fd0c0add1cead41038ab8806ffebde/lib/view_model/exchange/exchange_trade_view_model.dart#L200-L206"
},
{
"title": "An EVM wallet has a single address",
"url": "https://github.com/cake-tech/cake_wallet/blob/7ce3cd75c3fd0c0add1cead41038ab8806ffebde/cw_evm/lib/evm_chain_wallet_addresses.dart#L11-L38"
},
{
"title": "Every input names 16 ring members",
"url": "https://github.com/monero-project/monero/blob/v0.18.5.1/src/cryptonote_core/blockchain.cpp#L3268"
},
{
"title": "Amounts are encrypted to the recipient",
"url": "https://github.com/monero-project/monero/blob/v0.18.5.1/src/ringct/rctOps.cpp#L672-L720"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposureShort": "Only amount and timing can pair a deposit into a swap service with a payout from a hot wallet serving every asset the service trades.",
"advice": "Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "A round trip of similar amount and timing pairs the two Ethereum legs."
}
},
"id": "chainAnalyst",
"value": "Link private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "Built-in Tor sends every call over one shared circuit for up to ten minutes, so only separate app sessions keep the legs apart.",
"exposureContinued": "Moralis receives every Ethereum wallet the app opens, so deposit and exit wallets kept in the app pair up whenever both open close together.",
"advice": "Turn on built-in Tor and restart the app, since the Ethereum client keeps the connection it started with. After the entry, delete the deposit wallet from Cake and restart, so later sessions only see the exit wallet.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Private only over built-in Tor, with each leg in its own app session."
}
},
"sources": [
{
"title": "Built-in Tor is off unless the user enables it",
"url": "https://github.com/cake-tech/cake_wallet/blob/7ce3cd75c3fd0c0add1cead41038ab8806ffebde/lib/store/settings_store.dart#L1590"
},
{
"title": "Opening a wallet connects its node and sends its address to Moralis",
"url": "https://github.com/cake-tech/cake_wallet/blob/7ce3cd75c3fd0c0add1cead41038ab8806ffebde/lib/reactions/on_current_wallet_change.dart#L94-L113"
},
{
"title": "Embedded Tor config: one SocksPort, no isolation flags",
"url": "https://github.com/cake-tech/cake_wallet/blob/7ce3cd75c3fd0c0add1cead41038ab8806ffebde/cw_core/lib/utils/tor/torch.dart#L65-L70"
},
{
"title": "The Ethereum client binds its proxy setting once",
"url": "https://github.com/cake-tech/cake_wallet/blob/7ce3cd75c3fd0c0add1cead41038ab8806ffebde/cw_evm/lib/clients/evm_chain_client.dart#L24"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "Each swap service custodies its leg and can hold the funds until you pass identity checks, and identities on both legs link them.",
"exposureContinued": "The entry service knows the XMR output it paid you, and the exit service sees the rings of the transaction that pays it. Together they find that output in the rings behind the exit, and self-transfers only widen the search.",
"exposureShort": "Each swap service custodies its leg and can hold the funds until the user passes identity checks, and identities on both legs link them.",
"exposureContinued": "The entry service knows the XMR output it paid the user, and the exit service sees the rings of the transaction that pays it. Together they find that output in the rings behind the exit, and self-transfers only widen the search.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Rings that reference the service's payout output point to the spend of your XMR."
"note": "Rings that reference the service's payout output point to the spend of the user's XMR."
},
"recipient": "private",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "A service can hold either leg until you identify yourself."
"note": "A service can hold either leg until the user provides identification."
}
},
"sources": [
{
"title": "Breaking Monero: poisoned outputs, the attack by a counterparty on both sides",
"url": "https://www.monerooutreach.org/breaking-monero/poisoned-outputs.html"
},
{
"title": "ChangeNOW: automated AML holds, KYC through SumSub, refund minus fees",
"url": "https://changenow.io/faq/kyc-aml"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
}
]
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "Whoever breaks elliptic-curve cryptography recovers every Monero output key from public chain data, so every ring shows its real spend.",
"exposureContinued": "A known address also yields its view key, and anyone learns a service's address by starting a trade. Your XMR can then be followed from the entry service to the exit service, where amounts and timing pair it with both Ethereum legs.",
"exposureContinued": "A known address also yields its view key, and anyone learns a service's address by starting a trade. The user's XMR can then be followed from the entry service to the exit service, where amounts and timing pair it with both Ethereum legs.",
"interior": {
"sender": "exposed",
"recipient": {
"verdict": "atRisk",
"note": "Readable for outputs to known addresses, and the services know yours."
"note": "Readable for outputs to known addresses, and the services know the user's address."
},
"amount": {
"verdict": "atRisk",
"note": "Readable for outputs to known addresses."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "A key image is the output key hashed to a point times its private key",
"url": "https://github.com/monero-project/monero/blob/v0.18.5.1/src/crypto/crypto.cpp#L621-L628"
},
{
"title": "Zero to Monero: one-time addresses, amount encryption and key images (chapters 4 to 6)",
"url": "https://www.getmonero.org/library/Zero-to-Monero-2-0-0.pdf"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "recipient",
"exposure": "atRisk"
},
{
"field": "amount",
"exposure": "atRisk"
}
]
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "Each Monero spend hides among the 16 members of its ring, and each self-transfer multiplies the candidates by about 30."
},
"attributes": [
{
"id": "bridged",
"label": "Bridged",
"description": "Funds leave Ethereum through a bridge and gain their privacy on another chain."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "Funds in transit sit with the swap service, which can steal them.",
"walkawayTest": {
"passed": false,
"reason": "Deposit and payout both depend on the swap service. If it stops, funds in transit are lost or refunded at its discretion."
}
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "Cake Wallet and Monero are open source. The swap services run closed systems."
},
"riskSummary": "## Funds can be stolen if\n1. a swap service does not pay out.\n2. a service's hot wallet is compromised during the trade.\n
\n## Funds can be frozen if\n1. a service holds the deposit pending identity verification.\n2. a service refunds minus fees and blacklists the deposit address.\n
\n## Privacy can be lost if\n1. a service holds a leg until the user identifies, since identities on both legs link them (see privileged insider).\n2. one party holds the records of both legs, such as one service used twice, and finds its payout output in the rings behind the exit (see privileged insider).\n3. swap APIs, Blink, Etherscan, Moralis, RPC and Monero node link your identity. Tor is off by default and not circuit-isolated (see network observer).\n4. elliptic-curve cryptography is broken, which reveals the real spend in every ring (see future adversary).",
"riskSummary": "## Funds can be stolen if\n1. a swap service does not pay out.\n2. a service's hot wallet is compromised during the trade.\n
\n## Funds can be frozen if\n1. a service holds the deposit pending identity verification.\n2. a service refunds minus fees and blacklists the deposit address.\n
\n## Privacy can be lost if\n1. a service holds a leg until the user identifies, since identities on both legs link them (see privileged insider).\n2. one party sees both legs, like one service used twice, and finds its payout in the rings behind the exit (see privileged insider).\n3. swap services, Blink, Etherscan, Moralis, the RPC and the Monero node see the user's IP, since Tor is off by default (see network observer).\n4. elliptic-curve cryptography is broken, which reveals the real spend in every ring (see future adversary).",
"tokens": [],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "Three parties change this swap route, each instantly and offchain since there are no onchain contracts.\n\n### Cake Labs LLC (the wallet)\n\nCake picks the swap services, their defaults and its API keys with each app release. Cake Wallet is MIT-licensed and self-custodial.\n\n### The swap services (the custodians)\n\nEach service sets its own terms, limits and compliance rules and can change them at any time.\n\n### Monero (the chain)\n\nRules change through scheduled hard forks. The mainnet table ends at version 16, active since block 2,689,608 (August 2022), with a ring size of 16."
}
}
+53 -67
+1 -1
{
"redWarning": {
"text": "The protocol was [exploited](https://x.com/PeckShieldAlert/status/2103041942405960096) on September 24, 2026. Do not deposit funds."
"text": "The escrow was [drained](https://x.com/payy_link/status/2105047468639658317) on September 24, 2026, and the network has been halted since. Do not deposit funds."
},
"unverifiedContracts": []
}
+1 -1
{
"badges": [
{
"id": "AppChain",
"type": "VM",
"name": "Application-specific chain",
"description": "This project is built to operate a specific application",
"action": {
"type": "scalingFilter",
"id": "vm",
"value": "AppChain"
}
},
{
"id": "CustomDA",
"type": "DA",
"name": "Custom DA solution",
"description": "This project is using a custom DA solution",
"action": {
"type": "selfDaHighlight"
}
},
{
"id": "Privacy",
"type": "Other",
"name": "Privacy",
"description": "This project supports private transactions natively",
"action": {
"type": "scalingFilter",
"id": "other",
"value": "Privacy"
}
}
],
"description": "Payy is a payments-focused ZK network for private stablecoin transfers, settled on Ethereum with aggregated validity proofs and validator signatures, while transaction data is kept offchain.",
"description": "A shielded ledger L2 for USDC payments in a mobile app, with payment links and a card, that hides amounts but not which notes fund which.",
"links": {
"websites": [
"https://payy.network"
],
"explorers": [
"https://payy.network/explorer"
],
"documentation": [
"https://docs.payy.network"
],
"repositories": [
"https://github.com/polybase/payy"
],
"socialMedia": [
"https://x.com/payy_link"
]
}
}
+8 -1
[
{
"title": "Payy Network is live on Ethereum",
"url": "https://payy.network/blog/payy-network-is-live-on-ethereum",
"date": "2026-02-19T00:00:00Z",
"description": "Payy launches its rollup on Ethereum, migrating from its previous deployment on Polygon.",
"description": "Payy launches its network on Ethereum, migrating from its previous deployment on Polygon.",
"type": "general"
},
{
"title": "Critical circuit vulnerability disclosed",
"url": "https://github.com/polybase/payy/security/advisories/GHSA-fhxc-63vg-9gwr",
"date": "2026-06-10T00:00:00Z",
"description": "An aggregation circuit bug allowed forged burn messages, was patched by the team.",
"type": "incident"
},
{
"title": "Escrow drained, network halted",
"url": "https://x.com/payy_link/status/2105047468639658317",
"date": "2026-09-24T00:00:00Z",
"description": "Two state updates pay out almost all escrowed USDC for an invalid burn. Payy halts the network.",
"type": "incident"
}
]
+4 -4
{
"dataAvailability": [
{
"name": "Data is not stored on chain",
"description": "Transaction data and the private note tree are kept offchain by the Payy network. Only state roots and public deposit (mint) and withdrawal (burn) messages are posted to Ethereum with each state update. Although each update includes a data commitment hash, the onchain function intended to assert its availability on a DA layer (`verifyCommitHash()`) is an empty placeholder, so Ethereum does not verify data availability. While Payy has announced Celestia as its DA layer, the commitment format posted onchain cannot be linked to any Celestia blob, and no trace of Payy data on Celestia could be identified.",
"risks": [
{
"category": "Funds can be lost if",
"text": "the external data becomes unavailable.",
"isCritical": true
}
],
"references": [
{
"title": "RollupV1.sol - empty verifyCommitHash() placeholder",
"url": "https://etherscan.io/address/0x7d8837b547F4FEa0053571CB149e845FC58e9B2D#code"
}
]
}
],
"exitMechanisms": [
{
"name": "Regular exit",
"description": "Users can withdraw by burning notes on the Payy network. The burn is included as a public message in the next proven state update, upon which the Rollup contract transfers the escrowed tokens to the specified Ethereum address. Whitelisted burn substitutors can front withdrawals from their own funds before settlement and are refunded when the original burn is processed. There is no mechanism for users to exit independently of the operators.",
"description": "Users can withdraw by burning notes on the Payy network. The burn is included as a public message in the next proven state update, upon which the RollupV1 contract transfers the escrowed tokens to the specified Ethereum address. Whitelisted burn substitutors can front withdrawals from their own funds before settlement and are refunded when the original burn is processed. There is no mechanism for users to exit independently of the operators.",
"risks": [
{
"category": "Funds can be frozen if",
"text": "the operators fail to include withdrawals in state updates, as there is no forced exit mechanism."
}
],
"references": []
}
],
"forceTransactions": {
"name": "Users can't force any transaction",
"description": "There is no general mechanism to force the sequencer to include the transaction. Deposits (mints) can be made permissionlessly on Ethereum, but they only become spendable once the Payy network includes them in a proven state update.",
"risks": [
{
"category": "Users can be censored if",
"text": "the operator refuses to include their transactions."
}
],
"references": []
},
"operator": {
"name": "The system has a centralized operator",
"description": "The operator is the only entity that can propose blocks. A live and trustworthy operator is vital to the health of the system. Payy blocks are produced by validators running the Solid BFT consensus protocol, and state updates are settled on Ethereum by a whitelisted prover. Onchain, the current validator set consists of a single address, and a single prover address has been whitelisted since initialization.",
"risks": [
{
"category": "MEV can be extracted if",
"text": "the operator exploits their centralized position and frontruns user transactions."
}
],
"references": [
{
"title": "Payy docs - Rollup",
"url": "https://docs.payy.network/payy-network/04_rollup"
}
]
},
"otherConsiderations": [
{
"name": "Confidential notes over a public spend graph",
"description": "Funds on Payy are represented as UTXO-like notes, and the chain state is a tree of note commitments. Each transaction proof hides the note contents (owner address, value, asset and entropy) but publishes the commitments of the two notes it consumes and the two it creates, and spending removes the input commitment from the tree. The deployed circuits do not use nullifiers, so anyone with Payy block data (served by the public node RPC and the explorer) can follow which commitments were spent into which, i.e. the transaction graph is public while its contents are not. Deposits to and withdrawals from the Ethereum escrow are public, and a withdrawal reveals asset, amount and Ethereum recipient onchain together with a burn hash that equals the first consumed note commitment, which anchors the withdrawal to a specific node in that graph.",
"risks": [],
"references": [
{
"title": "noir/utxo - commitments are public inputs of the UTXO proof",
"url": "https://github.com/polybase/payy/blob/main/noir/utxo/src/main.nr"
},
{
"title": "Payy docs",
"url": "https://docs.payy.network"
}
]
},
{
"name": "Owner can overwrite the state root",
"description": "The Rollup contract owner can directly overwrite the current state root via `setRoot()` without providing a validity proof or validator signatures, and can replace the ZK verifier contracts referenced by state updates.",
"description": "The RollupV1 contract owner can directly overwrite the current state root via `setRoot()` without providing a validity proof or validator signatures, and can replace the ZK verifier contracts referenced by state updates.",
"risks": [
{
"category": "Funds can be stolen if",
"text": "the owner overwrites the state root or registers a malicious verifier."
}
],
"references": []
}
],
"stateValidation": {
"description": "Each state update submitted via `verifyRollup()` must include a ZK proof that is verified by the registered verifier contract, and signatures from more than 2/3 of the current validator set. The proof attests to the correct state transition of the private note tree, and public deposit (mint) and withdrawal (burn) messages are checked against the public inputs.",
"categories": [
{
"title": "ZK Circuits",
"description": "Payy transactions are proven with [Noir](https://noir-lang.org/) circuits: individual UTXO transaction proofs are recursively aggregated (`utxo` → `agg_utxo` → `agg_agg` → `agg_final`) into a single proof per state update. The circuits are published in the [payy repository](https://github.com/polybase/payy/tree/main/noir). The final proof is verified onchain by a Barretenberg-generated UltraHonk verifier.",
"references": [
{
"title": "HonkVerifier - Etherscan",
"url": "https://etherscan.io/address/0x14DACD534ddc676601B27f41Eb541a7951524a2F#code"
}
]
},
{
"title": "Verification Keys Generation",
"description": "The Rollup contract maps verification key hashes to verifier contracts. Each state update references one of the registered keys. The owner can add, replace and remove verifiers at any time without delay.",
"description": "The RollupV1 contract maps verification key hashes to verifier contracts. Each state update references one of the registered keys. The owner can add, replace and remove verifiers at any time without delay.",
"references": [
{
"title": "RollupV1.sol - addZkVerifier()",
"url": "https://etherscan.io/address/0x7d8837b547F4FEa0053571CB149e845FC58e9B2D#code"
}
]
},
{
"title": "State root proposals",
"description": "In addition to the validity proof, each state update requires signatures from more than 2/3 of the current validator set, which reflects finalization by the Solid BFT consensus of the Payy network. The current validator set registered onchain consists of a single address, making the effective threshold 1-of-1.",
"references": []
}
]
},
"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."
"content": "All privileged functions are ultimately controlled by the PayyMultisig (2/4). It owns the ProxyAdmin, which can instantly upgrade the RollupV1 proxy, and it is the owner of the RollupV1 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."
}
}
+39 -60
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Promises private stablecoin transfers. Delivers hidden amounts and note owners over a public spend graph: which notes fund which, and every deposit and withdrawal, is public."
"text": "Hides note amounts and owners. Which notes fund which, and every deposit and withdrawal, 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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "bad",
"exposureShort": "Every transaction names the notes it spends and creates, so which notes fund which is public by design; only the amount and owner of a note are hidden.",
"exposureContinued": "Empty padding notes have a zero commitment, so single-input and single-output transactions are recognizable. 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.",
"exposureShort": "Every transaction names the notes it spends and creates, so which notes fund which is public.",
"exposureContinued": "Empty padding notes have a zero commitment, so single-input and single-output transactions are recognizable. Each deposit or withdrawal shows address and amount and points at one node of that graph. The per-transaction proofs use a flavor its authors document as not zero-knowledge, and how much of a note it leaks is not established.",
"interior": {
"sender": {
"verdict": "private",
"note": "The owner key is hidden; the spent note is a public graph node."
"note": "The owner key is hidden, but 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": "Spend graph explorer built from Payy blocks (by L2BEAT)",
"url": "https://sekuba.github.io/payy-graph/"
},
{
"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": "Link exposed",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "bad",
"exposureShort": "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.",
"exposureShort": "Along a path without splits or merges, the deposit and withdrawal amounts fix every amount in between.",
"exposureContinued": "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": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "atRisk"
}
]
},
"networkObserver": {
"sentiment": "bad",
"exposureShort": "The payment-link website 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.",
"exposureContinued": "On the other hand, proofs are built on your device and the app talks only to the operator's servers.",
"advice": "Redeem payment links in the app, not in a browser.",
"exposureShort": "The Payy app is closed source and no open client works on mainnet, so what it sends to third parties is up to Payy and can change at any time.",
"exposureContinued": "The payment-link website loads a third-party analytics script that records the page URL, which holds the link's redeem secret.",
"interior": {
"sender": "private",
"recipient": "private",
"sender": "unverifiable",
"recipient": "unverifiable",
"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": "The repository publishes only type bindings of the app",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/app/packages/payy/src"
},
{
"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": "The open TypeScript client knows only dev and testnet",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/app/packages/payy-client/src/network.ts#L6-L17"
},
{
"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": "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": "Node logs every submitted proof",
"url": "https://github.com/polybase/payy/blob/2f95947f12981a838f6f0c9dddace825bf835502/pkg/node/src/rpc/routes/txn.rs#L30-L34"
"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"
}
],
"id": "networkObserver",
"value": "Link exposed",
"alsoExposed": []
"alsoExposed": [
{
"field": "sender",
"exposure": "unverifiable"
},
{
"field": "recipient",
"exposure": "unverifiable"
}
]
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "One company runs the only validator and prover, the node, the note registry, the deposit relayer and the KYC checks.",
"exposureContinued": "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. 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.",
"exposureContinued": "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. For payment links and fiat ramps it holds the notes' private keys and can read and spend them. Its registry sees which wallet delivers a note to which recipient key, and in which block.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Hidden for ordinary transfers; the backend holds the keys of link and ramp notes."
"note": "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": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "recipient",
"exposure": "exposed"
}
]
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "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.",
"exposureShort": "The per-transaction proofs are public and not zero-knowledge, and the commitments inside them rest on elliptic curves.",
"exposureContinued": "Registry ciphertexts use elliptic-curve key exchange. If the operator retains them, whoever breaks elliptic-curve cryptography opens every note delivered through it.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "If the registry is retained; its ciphertexts are not on Ethereum."
"note": "Only if Payy retains the registry, whose ciphertexts are not on Ethereum."
},
"recipient": "atRisk",
"amount": {
"verdict": "atRisk",
"note": "Proofs are public and not zero-knowledge; registry ciphertexts open if retained."
"note": "Public proofs are not zero-knowledge, and retained registry ciphertexts open."
},
"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": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "atRisk"
}
]
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"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.",
"detailedDescription": "Payy is a mobile app for USDC payments. The user deposits USDC, sends it to other users or as a payment link, and withdraws it to Ethereum or tops up the Payy card. Note amounts and owners stay hidden. Which notes fund which, and every deposit and withdrawal with its address and amount, are public.\n\nUsers trust Payy, which runs the only validator, prover and node, delivers notes between users and holds the keys of link and ramp notes. The 2/4 multisig can upgrade the RollupV1 contract and overwrite its state root without delay. Leaving needs Payy: withdrawals settle only in its proven state updates, and transaction data lives only on its servers.\n\n### Architecture\n\nEach balance is a set of notes. A note holds an amount of USDC and an owner key, and only its commitment enters the state tree. A transaction spends up to two notes, creates up to two and names the commitments it spends, since the circuits use no nullifiers. Payy's node serves every block with its proofs. Ethereum receives only state roots, deposits and withdrawals.\n\n### Flow\n\n1. **Deposit.** The app gives each wallet one fixed deposit address. You send USDC there, and Payy's relayer moves it into the RollupV1 contract with your signed authorization. Deposit address and amount are public.\n2. **Send.** Your phone proves the transfer and submits it to Payy's node. The note reaches the recipient through Payy's registry, encrypted to the recipient's key, or as a payment link whose URL holds the note's secret.\n3. **Withdraw.** You burn notes to an Ethereum address, which the next state update pays out. A whitelisted account may pay at once and get refunded at settlement. Recipient and amount are public. Card top-ups are withdrawals to the card issuer's contract.\n\n### Keys\n\nThe app is closed source, so how it creates and backs up keys is unverified. Payy's backend keeps wallet backups, and its diagnostics reports have a required field for the recovery phrase.\n\n### App\n\nThe ratings assume the Payy mobile app, which is closed source and talks only to Payy's servers. The [repository](https://github.com/polybase/payy) publishes the contracts, circuits, node and backend interfaces. Its open TypeScript client knows only Payy's testnet.\n\n### Compliance\n\nKYC is required for the Payy card and fiat ramps, not for transfers. Payy's backend can block a wallet or an IP address from its services, and its validator can refuse any transaction. The protocol has no view key.\n\n### Fees\n\nThe RollupV1 contract charges no fee. Payy pays the gas of deposits and settlement.",
"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.",
"description": "The PayyMultisig can instantly upgrade the RollupV1 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."
}
},
"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."
"description": "The Payy app is closed source. The contracts, circuits and node are published without a license and can be built locally. Transaction data is not posted anywhere public, so the state cannot be rebuilt independently."
},
"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.",
"riskSummary": "## Funds can be stolen if\n1. the verifier or the circuits accept an invalid withdrawal.\n2. the PayyMultisig upgrades the RollupV1 contract or overwrites the state root, which takes effect without delay.\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. anyone with Payy's block data follows which notes fund which, from a deposit address to a withdrawal address (see public observer).\n2. a path without splits or merges reveals every amount between a deposit and a withdrawal (see chain analyst).\n3. the closed-source Payy app sends user data to third parties, which Payy can change at any time (see network observer).\n4. Payy's backend, which ties each wallet to its KYC record, deposit address and notes, leaks or uses this data (see privileged insider).\n5. the public per-transaction proofs, which are not zero-knowledge, or retained registry ciphertexts are opened in the future (see future adversary).",
"tokens": [],
"trackedOn": [
"ethereum"
],
"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."
"content": "All privileged functions are ultimately controlled by the PayyMultisig (2/4). It owns the ProxyAdmin, which can instantly upgrade the RollupV1 proxy, and it is the owner of the RollupV1 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"
}
+27 -9
+1 -1
{
"badges": [],
"description": "A shielded ledger for ERC-20 tokens on Base, aimed at institutional users, whose privacy rests on the operator's TEE.",
"detailedDescription": "Privacy Boost is a shielded ledger for ERC-20 tokens on Base, aimed at institutional users. Privacy and liveness depend on the operator's TEE, while zero-knowledge proofs secure funds and a forced exit.\n\n### Architecture\n\nDeposits become notes whose Poseidon2 commitments enter an onchain Merkle tree, and spending a note publishes its nullifier. The TEE collects the transfers and withdrawals users approve and batches them into epochs. A permissioned relay submits each epoch with a Groth16 validity proof.\n\nAccounts approve spends with registered keys or with onchain spend approvals, which also serve smart-wallet accounts. Both are leaves of an auth Merkle tree in the AuthRegistry. Epoch proofs may use a tree root up to 10 minutes old, so a revoked key or approval stays usable that long.\n\n### Forced withdrawals\n\nA user can prove a forced withdrawal of up to 13 notes locally. The contract checks the authorization at request time, and anyone can execute the withdrawal 3 days later if the notes are still unspent. The account owner can cancel it. This is the exit when the operator stops, and it publishes the spent notes and the linked account.\n\n### Portal deposits, gifts and DeFi\n\nPortal deposits use EIP-7702 addresses that users generate locally. Anyone can send ERC-20 tokens there, and sweeps move them into the pool for the hidden recipient. Gift notes go to the recipient or back to the sender after a deadline, privately through the relay or through a public gift exit. Withdrawals can call approved gateways for DeFi.\n\n### Compliance\n\nAuditors appointed by the admin multisig can query the TEE's Audit API for the balance and history of any address, without the user's consent. The AuditGateway lists 0 auditors today. The TEE is meant to record each access on that contract, but with its code unpublished this is unverifiable.\n\n### Privacy considerations\n\nEvery private transaction exists in plaintext inside the operator's TEE, whose source code is unpublished. Privacy therefore also rests on the hardware vendor and on resistance to attacks by anyone with physical access. Practical privacy also depends on the timing and amounts of deposits and withdrawals, see [OPSEC best practice](/publications/privacy-best-practices).\n\n### Fees\n\nStandard deposits pay no protocol fee. Portal deposits pay a sweeper fee of 0%, capped at 10%, and have token-specific minimum sweep amounts. Withdrawals, forced withdrawals and public gift exits pay 0.4% to the treasury. A forced withdrawal fixes its fee at request time.\n\n### Deposit and withdrawal statistics\n\nThe statistics count every ERC-20 transfer into the pool as a deposit and every transfer out as a withdrawal, since pool events omit per-transfer amounts. This includes portal sweeps, refunds of cancelled deposit requests, withdrawal fees sent to the treasury and both legs of gateway DeFi calls.",
"detailedDescription": "Privacy Boost lets apps on Base give their users a private ERC-20 balance. The user deposits tokens, sends them privately to other users, uses them in DeFi through approved gateways and withdraws to any address. Everything inside stays hidden from the chain, while the operator's enclave sees every transfer in plaintext.\n\nUsers trust the operator, Sunnyside Labs, whose TEE processes and whose relay submits every transaction. They also trust the admin multisig, which can upgrade every contract instantly. If the operator stops, a forced withdrawal takes funds out after 3 days, in public.\n\n### Architecture\n\nDeposits become notes whose Poseidon2 commitments enter an onchain Merkle tree, and spending a note publishes its nullifier. The TEE collects the transfers and withdrawals users approve and batches them into epochs. A permissioned relay submits each epoch with a Groth16 validity proof. The TEE's code is unpublished, so what it does with the plaintext cannot be checked. Its isolation rests on the hardware vendor and on resistance to attacks by anyone with physical access.\n\n### Flow\n\n1. **Deposit.** You deposit tokens directly, or anyone sends them to a portal address (EIP-7702) you generate locally. A sweep then moves them into the pool for you. Depositor, token and amount are public.\n2. **Transfer.** You approve a spend, and the TEE includes it in the next epoch.\n3. **Gift.** A gift note goes to its recipient, or back to the sender after a deadline. Either way runs privately through the relay or through a public gift exit.\n4. **Withdraw.** The pool pays out to any address and can call an approved gateway for DeFi. Recipient, token and amount are public.\n\n### Keys\n\nThe closed SDK can derive the private account's keys from a wallet signature, which links that wallet to the account. The hosted wallet can upload a backup of the private keys to the operator. Accounts approve spends with registered keys or with onchain spend approvals. Both are leaves of an auth Merkle tree in the AuthRegistry contract. Epoch proofs may use a tree root up to 10 minutes old, so a revoked key or approval stays usable that long.\n\n### App\n\nApps integrate the Privacy Boost SDK, whose core ships as closed-source WebAssembly and takes its endpoints from the operator's server.\n\n### Exits\n\n**Forced withdrawal.** If operator turns malicious or stops processing withdrawals, a user can prove a forced withdrawal of up to 13 notes locally. The contract checks the authorization at request time, and anyone can execute the withdrawal 3 days later if the notes are still unspent. The account owner can cancel it. It publishes the spent notes and exposes the linked account.\n\n### Compliance\n\nAuditors appointed by the admin multisig can query the TEE's Audit API for the balance and history of any address, without the user's consent. The AuditGateway lists 0 auditors today. The TEE is meant to record each access on that contract, but with its code unpublished this is unverifiable.\n\n### Fees\n\nStandard deposits pay no protocol fee. Portal deposits pay a sweeper fee of 0%, capped at 10%, and have token-specific minimum sweep amounts. Withdrawals, forced withdrawals and public gift exits pay 0.4% to the treasury. A forced withdrawal fixes its fee at request time.",
"links": {
"websites": [
"https://www.privacyboost.io/"
],
"documentation": [
"https://docs.privacyboost.io/"
],
"repositories": [
"https://github.com/sunnyside-io/privacy-boost-protocol",
"https://github.com/sunnyside-io/privacy-boost-ceremony"
]
}
}
+16 -1
null
[
{
"title": "Privacy Boost live on OP Mainnet",
"url": "https://optimism.io/blog/privacy-comes-to-the-op-stack",
"date": "2026-04-20T00:00:00Z",
"description": "Sunnyside Labs opens Privacy Boost to its customers on OP Mainnet.",
"type": "general"
},
{
"title": "Pool deployed on Base",
"url": "https://basescan.org/tx/0xb57d5d9d8b938eb5567222a01bbddfba9b327ffe0b5219fdb7a410f1f4c006fd",
"date": "2026-09-03T00:00:00Z",
"description": "The pool tracked here goes live on Base.",
"type": "general"
}
]
+10 -7
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "Transfers inside publish only encrypted notes.",
"exposureContinued": "Deposits and withdrawals show address, token and amount. Each epoch pairs every exit with the nullifiers and note shape of the transfer that funded it. Forced withdrawals publish the spent notes and the account, public gift exits the destination.",
"advice": "Exit through the operator's relay.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "Public in gateway DeFi legs."
},
"asset": {
"verdict": "atRisk",
"note": "Public in gateway DeFi legs."
},
"linkage": "private"
},
"sources": [
{
"contract": "PrivacyBoost"
},
{
"title": "Epoch calldata: withdrawals, nullifiers and transfer shapes",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/5792c139b9529ed80643262d75b5489055be11b0/contracts/src/interfaces/IPrivacyBoost.sol#L828-L852"
},
{
"title": "Forced withdrawal publishes account and notes",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/5792c139b9529ed80643262d75b5489055be11b0/contracts/src/interfaces/IPrivacyBoost.sol#L582-L591"
},
{
"title": "Public gift exit names the destination",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/5792c139b9529ed80643262d75b5489055be11b0/contracts/src/interfaces/IPrivacyBoost.sol#L707"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "bad",
"exposureShort": "The anonymity set is very small.",
"exposureContinued": "Most pool activity is one address that keeps depositing and withdrawing 0.001 WETH, which an analyst can filter out.",
"interior": {
"sender": "atRisk",
"recipient": "atRisk",
"amount": {
"verdict": "atRisk",
"note": "Bounded by the public deposit and exit amounts."
},
"asset": "atRisk",
"linkage": {
"verdict": "exposed",
"note": "The set is too small to hide in."
}
},
"sources": [
{
"title": "Address with 0.001 WETH round trips",
"url": "https://basescan.org/address/0xf977237b7d978dde922ee4909619740c73d8a49b"
}
],
"id": "chainAnalyst",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "atRisk"
}
]
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "Anyone on your network path sees when your wallet deposits and when the app contacts the operator, and can match both to the chain.",
"exposureContinued": "The SDK has no Tor or proxy setting.",
"advice": "Route the app through Tor with system-wide tools.",
"sentiment": "bad",
"exposureShort": "The SDK's core is closed source with no open alternative, so what it sends, and where, is up to the operator and can change with any release.",
"exposureContinued": "Today, anyone on the user's network path sees their wallet deposits and app contacts with the operator, and can match both to the chain. The SDK has no Tor or proxy setting, and the operator's server tells it which endpoints to use.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "Public in gateway DeFi legs."
},
"asset": {
"verdict": "atRisk",
"note": "Public in gateway DeFi legs."
},
"linkage": {
"verdict": "atRisk",
"note": "Private only over Tor, which the SDK lacks."
}
},
"sources": [
{
"title": "Operator endpoint",
"url": "https://base.privacyboost.io/api/v1/info"
},
{
"title": "SDK 1.2.5, configured with one server URL",
"url": "https://www.npmjs.com/package/@sunnyside-io/privacy-boost/v/1.2.5"
},
{
"title": "The compiled core builds and sends every request, its Rust source is unpublished",
"url": "https://www.npmjs.com/package/@sunnyside-io/privacy-boost/v/1.2.5?activeTab=code"
}
],
"id": "networkObserver",
"value": "Link at risk",
"value": "Link exposed",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "The operator's enclave holds every transfer in plaintext and a key to every note.",
"exposureContinued": "The SDK trusts the enclave key it fetches from the operator's endpoint. Auditors appointed by the admin can pull any account's history without consent.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "AuditGateway"
},
{
"section": "permissions",
"title": "Admin and operator multisigs"
},
{
"title": "SDK fetches the enclave key from the endpoint",
"url": "https://www.npmjs.com/package/@sunnyside-io/privacy-boost/v/1.2.5"
},
{
"title": "Notes wrap a key for the enclave beside the receiver",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/5792c139b9529ed80643262d75b5489055be11b0/contracts/src/interfaces/IStructs.sol#L36-L56"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "recipient",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
},
{
"field": "asset",
"exposure": "exposed"
}
]
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "Every note wraps its key to the enclave's long-lived elliptic-curve public key as well as the receiver's.",
"exposureContinued": "Whoever breaks elliptic-curve cryptography or obtains that one key decrypts the entire history.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Every output and deposit ciphertext carries an enclave key",
"url": "https://github.com/sunnyside-io/privacy-boost-protocol/blob/5792c139b9529ed80643262d75b5489055be11b0/contracts/src/interfaces/IStructs.sol#L36-L125"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "recipient",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
},
{
"field": "asset",
"exposure": "exposed"
}
]
}
}
},
"anonymitySet": {
"type": "too-small"
},
"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."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The admin multisig upgrades the pool and both registries instantly through their ProxyAdmins.",
"walkawayTest": {
"passed": false,
"reason": "Without the TEE operator, deposits and private transfers stop and only forced withdrawals remain."
}
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The circuits are published. The epoch, forced withdrawal and gift claim verification keys deployed in September 2026 remain unreproduced, and the TEE source code is unpublished."
"description": "The circuits are published. The epoch, forced withdrawal and gift claim verification keys deployed in September 2026 remain unreproduced, and the source code of the TEE and of the SDK's compiled core is unpublished."
},
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid spends or withdrawals.\n2. all [trusted setup](#trusted-setups) participants collude or leak their secrets, allowing forged proofs.\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 both the account-owner wallet and every usable authorization key.\n
\n## Privacy can be lost if\n1. the TEE or its hardware vendor is compromised (see privileged insider).\n2. an appointed auditor fetches the user's history through the Audit API.\n3. a user exits through a forced withdrawal (see public observer).\n4. a user relies on today's anonymity set, which is too small to hide in (see chain analyst).",
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid spends or withdrawals.\n2. all [trusted setup](#trusted-setups) participants collude or leak their secrets, allowing forged proofs.\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 both the account-owner wallet and every usable authorization key.\n
\n## Privacy can be lost if\n1. the TEE or its hardware vendor is compromised (see privileged insider).\n2. an auditor appointed by the admin multisig fetches the user's history through the Audit API (see privileged insider).\n3. a user exits through a forced withdrawal (see public observer).\n4. a user relies on today's anonymity set, which is too small to hide in (see chain analyst).\n5. the operator changes the SDK's closed-source core to send user data elsewhere, which users cannot check (see network observer).",
"tokens": [
{
"token": {
"address": "0x4200000000000000000000000000000000000006",
"iconUrl": "https://coin-images.coingecko.com/coins/images/2518/large/weth.png?1696503332",
"symbol": "WETH",
"decimals": 18,
"priceId": "weth",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-WETH",
"type": "pool",
"label": "WETH",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-USDC",
"type": "pool",
"label": "USDC",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
"iconUrl": "https://coin-images.coingecko.com/coins/images/40143/large/cbbtc.webp?1726136727",
"symbol": "cbBTC",
"decimals": 8,
"priceId": "coinbase-wrapped-btc",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-cbBTC",
"type": "pool",
"label": "cbBTC",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
"iconUrl": "https://coin-images.coingecko.com/coins/images/26045/large/euro.png?1696525125",
"symbol": "EURC",
"decimals": 6,
"priceId": "euro-coin",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-EURC",
"type": "pool",
"label": "EURC",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x311935Cd80B76769bF2ecC9D8Ab7635b2139cf82",
"iconUrl": "https://coin-images.coingecko.com/coins/images/4128/large/solana.png?1718769756",
"symbol": "SOL",
"decimals": 9,
"priceId": "solana",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-SOL",
"type": "pool",
"label": "SOL",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
"iconUrl": "https://coin-images.coingecko.com/coins/images/39963/large/usdt.png?1724952731",
"symbol": "USDT",
"decimals": 6,
"priceId": "l2-standard-bridged-usdt-base",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-USDT",
"type": "pool",
"label": "USDT",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x940181a94A35A4569E4529A3CDfB74e38FD98631",
"iconUrl": "https://assets.coingecko.com/coins/images/31745/large/token.png?1696530564",
"symbol": "AERO",
"decimals": 18,
"priceId": "aerodrome-finance",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-AERO",
"type": "pool",
"label": "AERO",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xB2000000000000000000004c27f6523082f41D01",
"iconUrl": "https://coin-images.coingecko.com/coins/images/102175780/large/zi54rndmdx59nblfgry3d1pyckch.?1787558766",
"symbol": "Basecat",
"decimals": 18,
"priceId": "basecat",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-Basecat",
"type": "pool",
"label": "Basecat",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
"iconUrl": "https://coin-images.coingecko.com/coins/images/18834/large/wstETH.png?1696518295",
"symbol": "wstETH",
"decimals": 18,
"priceId": "wrapped-steth",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-wstETH",
"type": "pool",
"label": "wstETH",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xb200000000000000000000C2e324d24d7eEcd1fb",
"iconUrl": "https://coin-images.coingecko.com/coins/images/102175597/large/aapl_200x200.png?1787068801",
"symbol": "AAPLc",
"decimals": 8,
"priceId": "apple-coinbase-tokenized-stock",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-AAPLc",
"type": "pool",
"label": "AAPLc",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xcb585250f852C6c6bf90434AB21A00f02833a4af",
"iconUrl": "https://coin-images.coingecko.com/coins/images/66267/large/Coinbase_Wrapped_XPR_%28cbXRP%29.png?1749023398",
"symbol": "cbXRP",
"decimals": 6,
"priceId": "coinbase-wrapped-xrp",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-cbXRP",
"type": "pool",
"label": "cbXRP",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xAC1Bd2486aAf3B5C0fc3Fd868558b082a531B2B4",
"iconUrl": "https://assets.coingecko.com/coins/images/31126/large/2023-08-11_13.21.24.png?1697025145",
"symbol": "TOSHI",
"decimals": 18,
"priceId": "toshi",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-TOSHI",
"type": "pool",
"label": "TOSHI",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x532f27101965dd16442E59d40670FaF5eBB142E4",
"iconUrl": "https://assets.coingecko.com/coins/images/35529/large/1000050750.png?1709031995",
"symbol": "BRETT",
"decimals": 18,
"priceId": "based-brett",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-BRETT",
"type": "pool",
"label": "BRETT",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xbeeff7aE5E00Aae3Db302e4B0d8C883810a58100",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "bbqUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-bbqUSDC",
"type": "pool",
"label": "bbqUSDC",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x1deEfABEe758AAbdC29a542B24ca3b75aFD56765",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "gtusdcf",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-gtusdcf",
"type": "pool",
"label": "gtusdcf",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0xFeFeC33668E22677c4762d0853d56245a800ff08",
"iconUrl": "https://coin-images.coingecko.com/coins/images/2518/large/weth.png?1696503332",
"symbol": "gtwethb",
"decimals": 18,
"priceId": "weth",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-gtwethb",
"type": "pool",
"label": "gtwethb",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x1D3b1Cd0a0f242d598834b3F2d126dC6bd774657",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "CSUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-CSUSDC",
"type": "pool",
"label": "CSUSDC",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
},
{
"token": {
"address": "0x5435BC53f2C61298167cdB11Cdf0Db2BFa259ca0",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "edgeUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1788377831
},
"buckets": [
{
"id": "privacy-boost-edgeUSDC",
"type": "pool",
"label": "edgeUSDC",
"address": "base:0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40",
"sinceTimestamp": 1788377831,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x71A0fD3C76E3E937d8275A3cb6a467b70123bD40"
}
}
}
]
}
],
"trackedOn": [
"base"
],
"upgradesAndGovernance": {
"content": "The 3/4 admin multisig upgrades the pool, registries and AuditGateway instantly. It can replace all five verifiers and their keys, set withdrawal and portal fees up to 10% each, configure portal minimum sweeps, appoint auditors, and change the treasury, operator and gateway route manager.\n\nThe gateway owner manages target and selector policies, picks the guardian and can pause execution. The guardian can pause and remove policies. Stranded gateway tokens return to the pool only while paused."
}
}
+70 -68
+9 -2
{
"badges": [],
"description": "A selective-disclosure privacy system for Ethereum that adds compliance-aware association sets.",
"detailedDescription": "Privacy Pools is a non-custodial privacy protocol on Ethereum built around asset-specific pools and private withdrawals, adding compliance by whitelisting all legitimate deposits. A deposit creates a commitment, which is represented by secret and nullifier, and a later withdrawal uses a zero-knowledge proof to spend that commitment, either partially or in full, without revealing the matching deposit. Losing the secret and the nullifier would effectively mean losing deposited tokens.\n\nPrivacy Pools are controlled by a 2/4 multisig, which has authority to stop deposits and manage the deposit whitelist, but users always have an option to publicly withdraw deposited tokens, linking their withdrawal to their deposit.\n\n### Privacy considerations\n\nPrivacy Pools protocol supports [relayed withdrawals](https://etherscan.io/address/0x15e355024de1cdc74addea7ebdf98418ba5b1a2c#code#F1#L133), in which relayer processes withdrawals on user's behalf for a fee, which enables sending funds to fresh addresses.\n\nPractical privacy also depends on the timing and amounts of deposits and withdrawals, underlying network and browser used to interact with Privacy Pools frontend (if used), RPC providers used to send transactions and query public blockchain state. The official frontend accepts a user-supplied RPC endpoint per network and scans pools whole, so with an own node no third party learns which notes are queried. Without one, all reads go through infrastructure operated by 0xbow. Users are advised to research [OPSEC best practice](/publications/privacy-best-practices).\n\n### Fees\n\nPrivacy Pools charges a mandatory onchain vetting fee on deposits and caps relayed-withdrawal fees per asset:\n- 0% vetting fee, 10% maximum relayer fee: frxUSD, fxUSD, sUSDS, USDe, USDS, USDT, WBTC, wOETH, wstETH.\n- 0% vetting fee, 5% maximum relayer fee: DAI, USD1.\n- 0.5% vetting fee, 10% maximum relayer fee: BOLD, ETH, USDC.\n\nThe vetting fees are accumulated in the Entrypoint and can be withdrawn by its owner. Relayer fees are paid on withdrawals to the selected relayer and cannot exceed the per-asset cap; relayers can still choose their own quote below that cap and users can self-relay to not pay the fee.\n\n### Compliance\n\nThe main feature of Privacy Pools is compliance, which is enforced through the ASP. Association set is a whitelist of deposits that are allowed to be withdrawn from the protocol. This set is managed in real time by the provider, which is currently a single entity. The full association set is published via IPFS, only its Merkle root is posted onchain. User's deposit could be excluded from the whitelist at any moment, in this case the user can still ragequit, i.e. publicly withdraw deposited funds and link them to their deposit.\n\nASP is designed to vouch that withdrawals from Privacy Pools are not related to any known illegal activity.\n\n### Anonymity set\n\nThe anonymity set consists of all whitelisted deposits of the same token with the value greater than the withdrawal amount. Note that only deposits approved by the ASP add to the anonymity set. To maximize the anonymity set, users are recommended to withdraw smaller amounts and deposit popular tokens.",
"description": "A privacy pool system for any amount, where only centrally approved deposits can be withdrawn privately.",
"detailedDescription": "Privacy Pools lets the user withdraw ETH or ERC-20 tokens in full or in parts, to an address with no visible link to their deposit. Only deposits that 0xbow's association set provider (ASP) currently approves can leave privately. Depositors, recipients, amounts and timing stay public.\n\nUsers trust 0xbow, which runs the ASP and hosts the website, and the 2/4 multisig that can upgrade the Entrypoint without delay. The ASP can withhold approval for any deposit or remove it at any time. Exiting is permissionless: the depositing address can always ragequit its funds from the immutable pools, in public.\n\n### Flow\n\n1. **Deposit.** You send funds through the Entrypoint, which keeps the vetting fee and adds a commitment to the asset's pool. Your address and the amount are public.\n2. **Approval.** The ASP adds the deposit's label to its approved list, usually within hours. It posts the list's root onchain and the list itself on IPFS.\n3. **Withdraw.** The website proves that you own an approved, unspent deposit without revealing which. A relayer submits the proof and pays the gas for a fee from the withdrawal. What you leave in the pool stays behind as a change note.\n\n### Keys\n\nAll notes derive from one recovery phrase. The website creates a random one or derives it from a signature of the user's wallet. Losing it loses the funds.\n\n### App\n\nThe ratings assume the website at [privacypools.com](https://privacypools.com), built from [public source](https://github.com/0xbow-io/privacy-pools-website) and hosted by 0xbow. It accepts the user's own RPC endpoint and a sign-in with the recovery phrase alone. It offers two relayers, Fast Relay and Cloaked Relay, while anyone can relay onchain. Over Tor, Fast Relay often refuses the connection and the website switches to Cloaked Relay. Accounts from the older 12-word key method send their deposit labels to 0xbow while they migrate.\n\n### Exits\n\n**Ragequit.** The depositing address can take out a note's funds at any time, approved or not, directly from the pool. It reveals which deposit the funds belong to, and for a change note, the withdrawal that left it.\n\n### Compliance\n\nThe ASP screens each depositing address before approving its deposit, under 0xbow's own due diligence. It may ask for a Proof of Association, which 0xbow's [separate app](https://tornado.0xbow.io) creates for Tornado Cash withdrawals. Every withdrawal must prove against the ASP's latest list, so a later list can revoke an earlier approval. A deposit without approval can leave only by ragequit.\n\n### Fees\n\nDeposits pay a vetting fee, which the Entrypoint owner can withdraw. Relayers set their own fee per withdrawal, within a cap per asset:\n- 0% vetting fee, 10% maximum relayer fee: frxUSD, fxUSD, sUSDS, USDe, USDS, USDT, WBTC, wOETH, wstETH.\n- 0% vetting fee, 5% maximum relayer fee: DAI, USD1.\n- 0.5% vetting fee, 10% maximum relayer fee: BOLD, ETH, USDC.\n\nA withdrawal the user submits directly pays no relayer fee.\n\n### Anonymity set\n\nEach pool has its own set: the approved deposits whose notes still hold at least the withdrawn amount. Deposits the ASP has not approved never count. Smaller withdrawals from larger pools hide among more deposits.",
"links": {
"websites": [
"https://privacypools.com"
],
"documentation": [
"https://docs.privacypools.com"
],
"repositories": [
"https://github.com/0xbow-io/privacy-pools-core",
"https://github.com/0xbow-io/privacy-pools-website"
]
}
}
+16 -1
null
[
{
"title": "ASP revokes every fxUSD approval",
"url": "https://etherscan.io/tx/0xd67628764f6ba49ef61e8e16b9aa3b3e4143b5daea4e37e914594c152f6120cb",
"date": "2026-03-11T00:00:00Z",
"description": "Approved fxUSD deposits leave the list for 13 days, with public ragequit as the only exit.",
"type": "incident"
},
{
"title": "ASP halts all private withdrawals",
"url": "https://etherscan.io/tx/0x4fbb5ecd0d2436b89756ddb584d31de6b71eaf743f71d09605bba3dff36adac0",
"date": "2026-03-11T00:00:00Z",
"description": "The ASP posts a root no deposit belongs to, blocking every private withdrawal for two hours.",
"type": "incident"
}
]
+42 -62
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which deposit funds which withdrawal, for deposits approved by the permissioned ASP. Everything else is public."
"text": "Hides which deposit funds which withdrawal, for deposits the ASP approves. 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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "Which approved deposit a withdrawal spends is hidden.",
"exposureShort": "A zero-knowledge proof hides which approved deposit a withdrawal spends.",
"exposureContinued": "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.",
"advice": "Exit through a relayer, so none of the user's wallets pay gas for the receiving address. Ragequit only deposits the user never withdrew from, since ragequitting a change note reveals the withdrawal that left it.",
"sources": [
{
"contract": "PrivacyPoolsEntrypoint"
},
{
"title": "Withdrawal circuit",
"url": "https://github.com/0xbow-io/privacy-pools-core/blob/main/packages/circuits/circuits/withdraw.circom"
"url": "https://github.com/0xbow-io/privacy-pools-core/blob/a80836a47451e662f127af17e11430ffa976c234/packages/circuits/circuits/withdraw.circom"
},
{
"contract": "PrivacyPoolETH",
"title": "ragequit: only the depositing address, in public"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposureShort": "The approved set at any block is public, bounding the anonymity set.",
"exposureContinued": "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.",
"exposureShort": "A relayed withdrawal of a common amount hides among all approved deposits of its pool.",
"exposureContinued": "The approved list at any block is public. Any amount is allowed, so an unusual one pairs a deposit with its withdrawal. Paying gas from the user's wallet instead of a relayer exposes its fingerprint, account implementation and fee habits, even from a fresh address.",
"advice": "Use a pool with a large anonymity set. Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"sources": [
{
"contract": "PrivacyPoolsEntrypoint",
"title": "relay: anyone can relay, the fee comes from the withdrawal"
},
{
"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": "good",
"exposureShort": "The official frontend allows you to set your own RPC endpoint per network, scans every pool whole and matches notes locally, so a node learns only which pools you looked at.",
"exposureContinued": "0xbow's servers still see that a session happened, through bulk feeds that carry nothing about the notes. A relayer receives amount and asset for a quote and the recipient once you confirm.",
"advice": "Set an endpoint for every network before signing in, and sign in with a recovery phrase so no wallet address is queried. Without an endpoint, every read goes through 0xbow's proxy and Alchemy under 0xbow's key. Read the chain from your own node, send through a public RPC over Tor, and use a popular relayer. If you settle for a VPN instead of Tor, pick one you trust: it hides your IP from the relayer but sees it itself.",
"exposureShort": "The website works in Tor Browser and scans every pool whole, matching notes locally without revealing the user's deposits to third parties.",
"exposureContinued": "The relayer receives amount and asset for a quote and the recipient once the user confirms. With a wallet sign-in on the default RPC, Alchemy sees the wallet and then the polling for the user's withdrawal, tying the two by timing.",
"advice": "Withdraw in a new Tor Browser session, signed in with the user's recovery phrase so no wallet address is queried. Before signing in, set the user's own node as the Ethereum endpoint.",
"sources": [
{
"title": "RPC setting, one endpoint per network, offered before sign-in",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/containers/Header.tsx#L39"
"title": "Sign-in with a recovery phrase needs no wallet",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/providers/AuthProvider.tsx#L39-L47"
},
{
"title": "Event sync and reads both move to the custom endpoint",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/config/chainData.ts#L667-L682"
"title": "Pools scanned whole from deployment",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/utils/dataService.ts#L11-L34"
},
{
"title": "Networks without an endpoint are skipped, not proxied",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/config/customRpc.ts#L638-L642"
"title": "A custom endpoint takes over reads and scans",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/config/chainData.ts#L660-L689"
},
{
"title": "Default reads through 0xbow's Hypersync proxy and Alchemy key",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/config/chainData.ts#L140-L141"
},
{
"title": "Pools scanned whole from deployment, dates from bulk logs",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/utils/dataService.ts#L11-L25"
},
{
"title": "No block lookup per deposit",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/utils/sdk.ts#L510-L516"
},
{
"title": "Relayed receipt read from bulk logs, hash never sent",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/utils/relayedReceipt.ts#L3-L44"
},
{
"title": "Wallet balance read on a fixed cadence",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/providers/ChainProvider.tsx#L125-L147"
},
{
"title": "Recipient sent to the relayer on confirm only",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/utils/quotePhases.ts#L4-L10"
},
{
"title": "Sign-in with a recovery phrase needs no wallet",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/providers/AuthProvider.tsx#L39-L47"
"title": "Recipient sent to the relayer on confirm",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/utils/quotePhases.ts#L4-L43"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "warning",
"exposureShort": "The ASP postman sets a new approved list at any time with no delay, and the pool accepts only the latest one.",
"exposureContinued": "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 displayed anonymity set shown before withdrawing.",
"exposureShort": "The ASP postman can publish a new approved list at any time with no delay, and withdrawals must prove against the latest one.",
"exposureContinued": "It can deny the user a private exit or publish a list containing only their deposit, reducing their anonymity set to one. The website counts the anonymity set it shows from 0xbow's own feed, not from the list the user's withdrawal proves against. 0xbow can also change the hosted website at any time.",
"advice": "Before withdrawing, check the size of the approved list behind the latest root, which its RootUpdated event links on IPFS. Run an inspected local build of the client instead of the hosted frontend.",
"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": "Displayed anonymity set counted from 0xbow's amount feed",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/hooks/useASP.ts#L63-L75"
},
{
"title": "Anonymity set counted locally from the published list",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/containers/Modals/Withdraw/WithdrawForm.tsx#L175-L180"
},
{
"title": "Anonymity set shown, not enforced",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/containers/Modals/Withdraw/AmountInputSection.tsx#L94-L95"
"section": "permissions",
"title": "Privacy Pools Multisig and ASP postman"
}
],
"id": "privilegedInsider",
"value": "Link at risk",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "good",
"exposureShort": "Nothing encrypted is written onchain and commitments are hashes, so whoever breaks public-key cryptography recovers nothing from the chain.",
"exposureContinued": "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.",
"advice": "Create the user's account from a recovery 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"
"url": "https://github.com/0xbow-io/privacy-pools-core/blob/a80836a47451e662f127af17e11430ffa976c234/packages/circuits/circuits/commitment.circom"
},
{
"title": "Wallet-signature seed derivation",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/utils/walletSeed.ts#L40-L73"
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/utils/walletSeed.ts#L40-L73"
},
{
"title": "Random seed phrase option",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/f34be7cb666b0607130c8a134973b3b30c33feb1/src/utils/seedPhrase.ts#L3-L7"
"title": "Random recovery phrase option",
"url": "https://github.com/0xbow-io/privacy-pools-website/blob/7df411128f7a18a61a77f7a0371bf0b857969e2a/src/utils/seedPhrase.ts#L3-L7"
},
{
"section": "trusted-setups"
}
],
"id": "futureAdversary",
"value": "Link private",
"alsoExposed": []
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "pool",
"label": "Pool",
"description": "Funds enter a shared pool and leave it later, with no actions inside. Hides which deposit became which withdrawal."
},
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "Even if the Entrypoint or ASP turns malicious, users can always ragequit (withdraw tokens) from the immutable pool contracts, at the cost of losing privacy.",
"description": "The depositing address can always ragequit its funds from the immutable pools, without the Entrypoint or the ASP. A ragequit is public.",
"walkawayTest": {
"passed": false,
"reason": "Association Set Providers must explicitly approve deposited funds before private withdrawals."
"reason": "Private withdrawals need the ASP to approve each deposit and keep posting roots."
}
},
"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."
"description": "The contracts, circuits, SDK, relayer and website are open source and build locally. The ASP service is closed."
},
"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.",
"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 or withdrawals relayed after it.\n
\n## Funds can be lost if\n1. a user loses the recovery phrase that their notes derive from.\n
\n## Privacy can be lost if\n1. the ASP leaves deposits out of its latest list, which blocks their private exit, up to halting all private withdrawals (see privileged insider).\n2. the ASP keeps only a few deposits in its latest list, so withdrawals proven against it point to one of them (see privileged insider).\n3. both relayers on the website refuse a withdrawal, so the user must self-relay or find a relayer elsewhere (see chain analyst).\n4. a user withdraws signed in with a wallet on the default RPC, so Alchemy ties the wallet to the withdrawal by timing (see network observer).",
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000000000000000",
"20000000000000000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000",
"20000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000",
"20000000000"
]
},
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1753975703
},
"buckets": [
{
"id": "privacy-pools-WBTC",
"type": "pool",
"label": "WBTC pool",
"address": "eth:0xF973f4B180A568157Cd7A0E6006449139E6Bfc32",
"sinceTimestamp": 1753975703,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0xDcEe70654261AF21C44c093C300eD3Bb97b78192",
"iconUrl": "https://coin-images.coingecko.com/coins/images/29734/large/woeth-200x200.png?1714796686",
"symbol": "wOETH",
"decimals": 18,
"priceId": "wrapped-oeth",
"sinceTimestamp": 1756377743
},
"buckets": [
{
"id": "privacy-pools-wOETH",
"type": "pool",
"label": "wOETH pool",
"address": "eth:0x7d2959bCFb936a84531518e8391DdBa844e03ebE",
"sinceTimestamp": 1756377743,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
},
{
"token": {
"address": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
"iconUrl": "https://assets.coingecko.com/coins/images/18834/large/wstETH.png?1696518295",
"symbol": "wstETH",
"decimals": 18,
"priceId": "wrapped-steth",
"sinceTimestamp": 1753975583
},
"buckets": [
{
"id": "privacy-pools-wstETH",
"type": "pool",
"label": "wstETH pool",
"address": "eth:0x1A604E9DFa0EFDC7FFda378AF16Cb81243b61633",
"sinceTimestamp": 1753975583,
"deposit": {
"event": "0xe3b53cd1a44fbf11535e145d80b8ef1ed6d57a73bf5daa7e939b6b01657d6549",
"extractor": "privacyPoolsValue",
"params": {}
},
"withdrawal": {
"event": "0x75e161b3e824b114fc1a33274bd7091918dd4e639cede50b78b15a4eea956a21",
"extractor": "privacyPoolsValue",
"params": {}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "The 2/4 Privacy Pools Multisig can instantly change the system’s critical configs, including the Entrypoint implementation and ASP root used for private withdrawals. The ASP postman (EOA) can also remove any deposit from the whitelist at any time, forcing a public rage-quit if the affected party wishes to withdraw. The guaranteed immutable escape hatch is pool-level ragequit (public withdrawal) to the original depositor address, because that logic lives in the immutable pool contracts and does not depend on the Entrypoint registry and config. This means the system is permissioned in its deposit logic and deposit privacy, but non-custodial for deposited assets. Past, successful (non-ragequit) withdrawals can not be deanonymized by the protocol."
"content": "The 2/4 Privacy Pools Multisig holds the Entrypoint's owner role. It can upgrade the Entrypoint, register, configure and wind down pools, set fees and withdraw the vetting fees, all without delay. It also appoints the ASP postmen, who post the approved roots. The pools and verifiers are immutable. Relayed withdrawals and new deposits pass through the Entrypoint, so a malicious upgrade can steal them. Ragequit works without the Entrypoint, so funds already in the pools can always leave to their depositing addresses."
}
}
+2 -2
{
"ethereum": {
"roles": [],
"actors": [
{
"id": "Privacy Pools Multisig",
"name": "Privacy Pools Multisig",
"description": "A Multisig with 2/4 threshold. \n* Can interact with PrivacyPoolsEntrypoint\n * authorize UUPS upgrades to the Entrypoint implementation \n * grant and revoke OWNER_ROLE and ASP_POSTMAN \n * publish new association-set roots and IPFS CIDs used by withdrawals \n * register and remove pools, update per-asset minimum deposits and fee caps, and wind down pools \n * withdraw fees held in the Entrypoint ",
"description": "A Multisig with 2/4 threshold. \n* Can interact with PrivacyPoolsEntrypoint\n * authorize UUPS upgrades to the Entrypoint implementation \n * grant and revoke OWNER_ROLE and ASP_POSTMAN \n * publish a new association-set root and its IPFS list at any time. Private withdrawals must prove against the latest root, so a new root can exclude any deposit or narrow the set to a few \n * register and remove pools, update per-asset minimum deposits and fee caps, and wind down pools \n * withdraw fees held in the Entrypoint ",
"accounts": [
{
"address": "eth:0xAd7f9A19E2598b6eFE0A25C84FB1c87F81eB7159",
"type": "Contract",
"isVerified": true,
"name": "0xAd7f…7159",
"url": "https://etherscan.io/address/0xAd7f9A19E2598b6eFE0A25C84FB1c87F81eB7159"
}
],
"chain": "ethereum",
"references": [],
"participants": [
{
"address": "eth:0xd76eEb2A6fcf55dc80D046FFbc96D1A2B45AB52E",
"type": "EOA",
"isVerified": true,
"name": "0xd76e…B52E",
"url": "https://etherscan.io/address/0xd76eEb2A6fcf55dc80D046FFbc96D1A2B45AB52E"
},
{
"address": "eth:0xb1A1D5b0f3d4E8e89E55daBFEb0B96118cbc16fc",
"type": "EOA",
"isVerified": true,
"name": "0xb1A1…16fc",
"url": "https://etherscan.io/address/0xb1A1D5b0f3d4E8e89E55daBFEb0B96118cbc16fc"
},
{
"address": "eth:0x652E36f8EA937c19417380dCDb48D6e4375d13dA",
"type": "EOA",
"isVerified": true,
"name": "0x652E…13dA",
"url": "https://etherscan.io/address/0x652E36f8EA937c19417380dCDb48D6e4375d13dA"
},
{
"address": "eth:0xE8A2cC5c2349615D947b9d9fE5D7F144730cAf77",
"type": "EOA",
"isVerified": true,
"name": "0xE8A2…Af77",
"url": "https://etherscan.io/address/0xE8A2cC5c2349615D947b9d9fE5D7F144730cAf77"
}
],
"discoveryDrivenData": true
},
{
"id": "EOA-1",
"name": "EOA 1",
"accounts": [
{
"address": "eth:0x1f4Fe25Cf802a0605229e0Dc497aAf653E86E187",
"type": "EOA",
"isVerified": true,
"name": "0x1f4F…E187",
"url": "https://etherscan.io/address/0x1f4Fe25Cf802a0605229e0Dc497aAf653E86E187",
"displayName": "EOA 1"
}
],
"chain": "ethereum",
"description": "* Can interact with PrivacyPoolsEntrypoint\n * publish new association-set roots and IPFS CIDs used by withdrawals ",
"description": "* Can interact with PrivacyPoolsEntrypoint\n * publish a new association-set root and its IPFS list at any time. Private withdrawals must prove against the latest root, so a new root can exclude any deposit or narrow the set to a few ",
"discoveryDrivenData": true
}
]
}
}
+1 -1
{
"baseTimestamp": 1779964040,
"baseTimestamp": 1791570511,
"contractsDiscoDriven": false,
"hasDiscoUi": false,
"isDiscoDriven": false,
"permissionsDiscoDriven": false
}
+29 -16
+2 -2
{
"badges": [],
"description": "An onchain privacy system for Ethereum based on encrypted UTXO-style private balances and zk-proven DeFi interactions.",
"detailedDescription": "Railgun is a shielded ledger on Ethereum. Users shield tokens into encrypted notes, transfer them privately and unshield them to any address. zk-SNARK proofs keep sender, recipient, token and amount hidden. DeFi calls go through the RelayAdapt contract, which unshields tokens, runs the calls and shields the results back in one transaction.\n\n### Privacy considerations\n\n[Broadcasters](https://docs.railgun.org/developer-guide/wallet/transactions/unshielding) submit private transactions and pay their gas. Wallets reach them over the [Waku network](https://blog.waku.org/2024-04-26-railgun-case-study/). Practical privacy also depends on the timing and amounts of shields and unshields, see [OPSEC best practice](/publications/privacy-best-practices).\n\nRailgun names no reference wallet, so the privacy ratings on this page assume [RailOxide](https://github.com/triamazikamno/railoxide). It routes all traffic through built-in Tor and builds Private Proofs of Innocence (PPoI) from a local copy of the lists. Other wallets likely leak significantly more.\n\n### Fees\n\nShields pay a protocol fee of 0.25% and unshields 0.25%, both to the Railgun Treasury. The NFT fee is 0. Broadcasters charge their own fee per transaction.\n\n### Compliance\n\nScreening happens offchain. A [PPoI](https://docs.railgun.org/wiki/assurance/private-proofs-of-innocence) proves that a note descends only from shields a list provider accepted. The default list screens shields with Chainalysis's sanctions API. Broadcasters on Ethereum require a PPoI, self-broadcasting skips the check. Users can also share a read-only viewing key that discloses all their private transactions.\n\n### Anonymity set\n\nEach token has its own anonymity set. Private transfers between users mix their notes within it.",
"description": "A shielded ledger on Ethereum for ERC-20 tokens, with private transfers and DeFi calls, run by no operator and governed by RAIL stakers.",
"detailedDescription": "Railgun lets the user shield tokens on Ethereum, send them privately to other Railgun users, use them in DeFi and unshield them to any address. Sender, recipient, token and amount stay hidden inside the pool, including which shield funds which unshield. Shields and unshields are public.\n\nNo operator holds a key into the pool. RAIL stakers govern the core contract through a DAO, whose upgrades wait 7 days. Leaving is permissionless: the user can submit their own unshield at any time.\n\n### Architecture\n\nBalances are encrypted notes in a public tree of commitments. Spending a note publishes its nullifier, and a zk-SNARK proves the spend without revealing which note it was. Each note is encrypted to the recipient's 0zk address.\n\n### Flow\n\n1. **Shield.** You send tokens to the pool, which creates a note for your 0zk address. Your address, token and amount are public.\n2. **Transfer.** Your wallet proves the spend locally. A [broadcaster](https://docs.railgun.org/developer-guide/wallet/transactions/unshielding), reached over the [Waku network](https://blog.waku.org/2024-04-26-railgun-case-study/), submits it and pays the gas for a fee paid in a private note.\n3. **DeFi.** The RelayAdapt contract unshields tokens, runs the calls and shields the results back in one transaction. Tokens and amounts of the calls are public.\n4. **Unshield.** The pool pays out to any address. Recipient, token and amount are public.\n\n### Keys\n\nThe user's wallet derives spending and viewing keys from a recovery phrase. The user can share a read-only viewing key, which discloses all their private transactions.\n\n### App\n\nRailgun names no reference wallet, so the ratings assume [RailOxide](https://github.com/triamazikamno/railoxide). It routes all traffic through built-in Tor and builds Private Proofs of Innocence (PPoI) from a local copy of the lists. Other wallets likely leak significantly more.\n\n### Compliance\n\nScreening happens offchain. A [PPoI](https://docs.railgun.org/wiki/assurance/private-proofs-of-innocence) proves that a note descends only from shields a list provider accepted. The default list screens shields with Chainalysis's sanctions API. Broadcasters on Ethereum require a PPoI, and a transaction the user submits directly skips the check.\n\n### Fees\n\nShields pay a protocol fee of 0.25% and unshields 0.25%, both to the Railgun Treasury. The NFT fee is 0. Broadcasters charge their own fee per transaction.\n\n### Anonymity set\n\nEach token has its own anonymity set. Private transfers between users mix their notes within it.",
"links": {
"websites": [
"https://railgun.org"
]
}
}
+9 -1
null
[
{
"title": "Current pool goes live on Ethereum",
"url": "https://etherscan.io/tx/0x2bd98cd135e2eaf7b7239bb4951a043f655629b5d0f0ca12334ce05718512361",
"date": "2022-05-08T00:00:00Z",
"description": "The proxy that holds today's pool is unpaused with the RailgunLogic implementation.",
"type": "general"
}
]
+18 -13
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "Everything inside the pool is hidden, including which shield funds which unshield.",
"exposureContinued": "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.",
"advice": "Exit through a broadcaster, so none of the user's wallets pay gas for the receiving address.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"asset": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"linkage": "private"
},
"sources": [
{
"contract": "RailgunSmartWallet",
"title": "Shield and unshield"
},
{
"contract": "RelayAdapt",
"title": "DeFi bundles"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposureShort": "The candidates for an unshield are the shields of the same token.",
"exposureContinued": "In-pool transfers break a one-to-one match, while timing and exact amounts narrow it.",
"advice": "Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"asset": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"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",
"exposureShort": "RailOxide sends all traffic over built-in Tor, broadcaster messages included, and finds your notes by decrypting every note locally.",
"advice": "Use RailOxide, the wallet these ratings assume, and read the chain from your own node.",
"exposureShort": "RailOxide sends all traffic over built-in Tor, broadcaster messages included, and finds the user's notes by decrypting every note locally.",
"exposureContinued": "All traffic shares one Tor session, so the public RPCs it picks at random see the user's public addresses, shields and the gas estimate of each private transaction from one exit. The user's own node as its RPC keeps these from third parties.",
"advice": "Use RailOxide, the wallet these ratings assume, and set the user's own node as its Ethereum RPC.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"asset": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"linkage": {
"verdict": "atRisk",
"note": "Private only over Tor, which RailOxide uses by default."
"note": "Private only with the user's own node as RPC, over the default Tor."
}
},
"sources": [
{
"title": "RailOxide privacy model",
"url": "https://github.com/triamazikamno/railoxide/blob/8773f4ecb00c6ac7cf164ba1f989eb77dd4b0ec5/docs/privacy-model.md"
},
{
"title": "Built-in Tor is the default",
"url": "https://github.com/triamazikamno/railoxide/blob/8773f4ecb00c6ac7cf164ba1f989eb77dd4b0ec5/crates/wallet-ops/src/settings/network_chains.rs#L10-L15"
},
{
"title": "Every stream uses one Tor session",
"url": "https://github.com/triamazikamno/railoxide/blob/8773f4ecb00c6ac7cf164ba1f989eb77dd4b0ec5/crates/wallet-ops/src/http.rs#L1549-L1559"
},
{
"title": "A custom RPC list replaces the public pool",
"url": "https://github.com/triamazikamno/railoxide/blob/8773f4ecb00c6ac7cf164ba1f989eb77dd4b0ec5/crates/wallet-ops/src/settings/effective.rs#L263-L277"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "good",
"exposureShort": "Only the user holds viewing keys, and DAO upgrades wait 7 days.",
"exposureContinued": "PPoI list providers can refuse a shield, which leaves a self-broadcast exit. RailOxide builds PPoIs from a local copy of the lists, so the notes you spend stay on your device.",
"advice": "Watch governance proposals and unshield before an upgrade you reject executes.",
"exposureContinued": "PPoI list providers can refuse a shield, which leaves a self-broadcast exit. RailOxide builds PPoIs from a local copy of the lists. After each transaction it sends the PPoI node only that transaction's new notes with their proofs.",
"advice": "Watch governance proposals and unshield before an upgrade the user rejects executes.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"asset": {
"verdict": "atRisk",
"note": "Public in DeFi bundles."
},
"linkage": {
"verdict": "atRisk",
"note": "Private only with a wallet that builds PPoIs locally, like RailOxide."
}
},
"sources": [
{
"section": "upgrades-and-governance"
},
{
"title": "RailOxide builds PPoIs locally",
"url": "https://github.com/triamazikamno/railoxide/blob/8773f4ecb00c6ac7cf164ba1f989eb77dd4b0ec5/crates/wallet-ops/src/poi_contexts.rs#L116-L150"
}
],
"id": "privilegedInsider",
"value": "Link private",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposureShort": "Notes are encrypted with elliptic-curve key exchange.",
"exposureContinued": "Whoever breaks elliptic-curve cryptography decrypts every note sent to a 0zk address that was ever shared, broadcaster fee notes included.",
"advice": "Share your 0zk address privately, with a fresh one per counterparty where you can.",
"advice": "Share the user's 0zk address privately, with a fresh one per counterparty where possible.",
"interior": {
"sender": "atRisk",
"recipient": "atRisk",
"amount": "atRisk",
"asset": "atRisk",
"linkage": {
"verdict": "atRisk",
"note": "Private only while your 0zk address stays unshared."
"note": "Private only while the user's 0zk address stays unshared."
}
},
"sources": [
{
"title": "Shared key derivation (Ed25519 ECDH)",
"url": "https://github.com/Railgun-Community/engine/blob/6e2614d53a106dd62abad91e7ce03ee4a3956138/src/utils/keys-utils.ts#L186-L224"
},
{
"title": "AES-GCM note encryption",
"url": "https://github.com/Railgun-Community/engine/blob/6e2614d53a106dd62abad91e7ce03ee4a3956138/src/utils/encryption/aes.ts#L24-L40"
}
],
"id": "futureAdversary",
"value": "Link at risk",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "atRisk"
}
]
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "defi",
"label": "DeFi",
"description": "Interop with DeFi (swaps, vaults) from within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"exitWindow": {
"value": "7d",
"sentiment": "warning",
"orderHint": 604800,
"description": "DAO-approved upgrades wait 7d before execution, so users can unshield before a change they reject.",
"walkawayTest": {
"passed": true
}
},
"relayerTracking": {
"type": "railgunWaku",
"chainId": 1,
"sinceTimestamp": 1788134400
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "The contracts, circuits, and supporting software needed to participate in the protocol are publicly available and can be run locally."
},
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid spends or withdrawals.\n2. all [trusted setup](#trusted-setups) participants collude or leak their secrets, allowing forged proofs.\n3. the DAO passes a malicious [upgrade](#upgrades-and-governance) and users stay in the pool past its 7 days execution delay.\n
\n## Funds can be lost if\n1. a user loses the keys of their private balance.\n
\n## Privacy can be lost if\n1. a user sends transactions from their own public address (see public observer).",
"riskSummary": "## Funds can be stolen if\n1. the zk proof system is broken, allowing invalid spends or withdrawals.\n2. all [trusted setup](#trusted-setups) participants collude or leak their secrets, allowing forged proofs.\n3. the DAO passes a malicious [upgrade](#upgrades-and-governance) and users stay in the pool past its 7 days execution delay.\n
\n## Funds can be lost if\n1. a user loses the keys of their private balance.\n
\n## Privacy can be lost if\n1. a user sends transactions from their own public address (see public observer).\n2. elliptic-curve cryptography is broken, which decrypts every note sent to a 0zk address that was ever shared (see future adversary).",
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000",
"20000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000",
"20000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000000000000000",
"20000000000000000000000"
]
},
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
}
}
}
]
},
{
"token": {
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1573689600
},
"buckets": [
{
"id": "railgun-WBTC",
"type": "pool",
"label": "WBTC",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
}
}
}
]
},
{
"token": {
"address": "0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4",
"iconUrl": "https://coin-images.coingecko.com/coins/images/71681/large/near.png?1768905174",
"symbol": "NEAR",
"decimals": 24,
"priceId": "rainbow-bridged-near-ethereum",
"sinceTimestamp": 1768867200
},
"buckets": [
{
"id": "railgun-NEAR",
"type": "pool",
"label": "NEAR",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1768867200,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x85F17Cf997934a597031b2E18a9aB6ebD4B9f6a4"
}
}
}
]
},
{
"token": {
"address": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb",
"iconUrl": "https://coin-images.coingecko.com/coins/images/14688/large/Logo_1_%28brighter%29.png?1734430693",
"symbol": "FLUID",
"decimals": 18,
"priceId": "instadapp",
"sinceTimestamp": 1623801600
},
"buckets": [
{
"id": "railgun-FLUID",
"type": "pool",
"label": "FLUID",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb"
}
}
}
]
},
{
"token": {
"address": "0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D",
"iconUrl": "https://coin-images.coingecko.com/coins/images/16840/large/railgun.jpeg?1696516407",
"symbol": "RAIL",
"decimals": 18,
"priceId": "railgun",
"sinceTimestamp": 1625270400
},
"buckets": [
{
"id": "railgun-RAIL",
"type": "pool",
"label": "RAIL",
"address": "eth:0xFA7093CDD9EE6932B4eb2c9e1cde7CE00B1FA4b9",
"sinceTimestamp": 1651422389,
"deposit": {
"event": "0x3a5b9dc26075a3801a6ddccf95fec485bb7500a91b44cec1add984c21ee6db3b",
"extractor": "railgunShield",
"params": {
"tokenAddress": "0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D"
}
},
"withdrawal": {
"event": "0xd93cf895c7d5b2cd7dc7a098b678b3089f37d91f48d9b83a0800a91cbdf05284",
"extractor": "railgunUnshield",
"params": {
"tokenAddress": "0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D"
}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "RAIL stakers govern Railgun through a DAO. It can upgrade the core contract and its circuits, which changes the rules for every shielded token. It can also block tokens from new shields, mint RAIL and manage governance rewards. [Governance docs](https://docs.railgun.org/wiki/rail-token/protocol-governance).",
"governanceInfo": {
"upgrades": {
"Normal upgrade path": "Create a proposal with an IPFS link and onchain calldata in the [Voting contract](https://etherscan.io/address/0xc480F68A3dcC3EdD82134FAB45C14A0FcF1dA3CC) → collect **500.00 K RAIL** sponsorship within 1mo → wait 2d → cast Yay votes within 5d and Nay votes within 6d → pass with a simple majority and **2.00 M RAIL** quorum → wait 7d → permissionless execution through the Delegator within 14d.",
"Exit window": "**7d** — a passed proposal must wait this long before it can be executed, giving users time to unshield funds."
},
"tokenGovernance": {
"Governance token": "`RAIL` 1 token = 1 vote, delegated. Total supply: **60.00 M RAIL**, DAO-owned treasury (unavailable for voting): **2.30 M RAIL**, staked for voting: **41.61 M RAIL**, the rest is circulating supply.",
"Stake lock": "Unstaking has **1mo** delay.",
"Voting venue": "[Voting contract](https://etherscan.io/address/0xc480F68A3dcC3EdD82134FAB45C14A0FcF1dA3CC) on Ethereum. Proposal text is distributed over IPFS, its CID is available as a parameter of `createPropsal()` call on the voting contract.",
"Proposal threshold": "**No threshold to create a proposal.** A proposal must receive sponsorship from **500.00 K RAIL** stake within 1mo.",
"Quorum": "**2.00 M RAIL**, with a simple majority required for acceptance.",
"Execution model": "**Onchain calldata · Permissionless execution through the Delegator.** A passed proposal waits 7d, after which anyone can execute it through the [Delegator contract](https://etherscan.io/address/0xB6d513f6222Ee92Fff975E901bd792E2513fB53B) within 14d."
}
}
}
}
+44 -76
+2 -2
{
"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 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.",
"description": "A shielded ledger on Starknet for any ERC-20 token, with private transfers and DeFi, where one auditor key can decrypt every user's history.",
"detailedDescription": "STRK-20 lets the user shield any ERC-20 token on Starknet, send it privately to other users, use it in DeFi and withdraw it to any address. Senders, amounts and which deposit funds which withdrawal stay hidden inside the pool. Deposits, withdrawals, the first payment to each new contact and DeFi calls are public.\n\nUsers trust the holder of the auditor key, which can decrypt every user's history, and the holder of the screener key, which must approve every deposit. A multisig can upgrade the [pool](https://voyager.online/contract/0x040337b1af3c663e86e333bab5a4b28da8d4652a15a69beee2b677776ffe812a) without delay. Leaving needs StarkWare's sequencer, which alone checks proofs and includes pool transactions.\n\n### Architecture\n\nEach user registers a viewing key, which the pool stores encrypted to the auditor key. Payments travel over channels: the first payment to a contact opens one and names the recipient, later ones only add encrypted notes. A private transfer needs a registered recipient, and registering takes a transaction and fee from the recipient's own account. A note holds an amount of one token, and spending it publishes a nullifier. DeFi actions use open notes, whose amount a helper contract fills after the swap or loan.\n\n### Flow\n\n1. **Register.** The wallet publishes your viewing key, encrypted to the auditor key.\n2. **Deposit.** The holder of the screener key signs off on your address, and the pool takes the tokens and creates a note. Address, token and amount are public.\n3. **Transfer.** The wallet proves offchain that you may spend your notes and creates notes for the recipient. A paymaster submits the transaction and takes its fee from your notes.\n4. **Withdraw.** The pool pays out to any address. Recipient, token and amount are public, and the withdrawing user is encrypted to the auditor key.\n\n### Keys\n\nThe user's Starknet account signs every action, and their viewing key decrypts their notes. Ready's servers create the viewing key for most accounts, while Xverse derives it from a wallet signature. Registration cannot be changed, so a leaked viewing key exposes the user's history for good.\n\n### App\n\nStarkWare lists Ready and Xverse as wallets, and both are closed source. Both send each action, with the user's viewing key, to their own servers for proving and note discovery. The [SDK](https://github.com/starkware-libs/starknet-privacy) is open, and a custom client on it can use its own [prover](https://github.com/starkware-libs/sequencer/tree/main/crates/starknet_transaction_prover)(high hardware requirements), its own note discovery and an oblivious relay. Deposits still need the screening signature.\n\n### Compliance\n\nEvery deposit needs a fresh signature from the pool's screener key, also for users who run their own prover. Deposits from DeFi helpers follow a policy per helper that governance sets: some are exempt, the rest are screened. The auditor key is one public key in the pool's storage. Who holds the screener and auditor private keys is not visible onchain, and governance can replace either key at once.\n\n### Fees\n\nEvery pool transaction pays a fixed STRK fee, which governance can change at any time, plus gas. Paymasters take their cost from the user's notes.\n\n### Anonymity set\n\nOne pool holds every token, but each token forms its own set. Registered users are the upper bound.",
"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"
]
}
}
+9 -1
null
[
{
"title": "Privacy live on Starknet",
"url": "https://www.starknet.io/blog/privacy-live-on-starknet/",
"date": "2026-06-09T00:00:00Z",
"description": "StarkWare opens the pool to users through the Ready and Xverse wallets.",
"type": "general"
}
]
+33 -73
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "warning",
"exposureShort": "Transfers inside are encrypted, but transactions to new contacts reveil the recipient.",
"exposureContinued": "The token and amount of the fee you pay, and the target and calldata of any DeFi action are public. 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.",
"exposureShort": "Transfers inside the pool are encrypted, but the first payment to each new contact writes the recipient's address in cleartext.",
"exposureContinued": "Deposits and withdrawals show address, token and amount. The fee payment names the token the user pays it in, and DeFi actions name their target contract. A new account must be funded publicly before it can receive privately.",
"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"
"url": "https://github.com/starkware-libs/starknet-privacy/blob/f9256c5e37682d37437e69a795ddd51d924c9b97/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"
"url": "https://github.com/starkware-libs/starknet-privacy/blob/f9256c5e37682d37437e69a795ddd51d924c9b97/packages/privacy/src/events.cairo#L17-L40"
}
],
"id": "publicObserver",
"value": "Link at risk",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposureShort": "The set of registered users is small and split further by token and by fee token.",
"exposureContinued": "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.",
"exposureShort": "The set of active users is small and split further by token and by fee token.",
"exposureContinued": "A channel opened in the same transaction as a deposit ties the two together, and most withdrawal addresses have also deposited. Paying gas from the user's wallet instead of a paymaster exposes its fingerprint, 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 and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"interior": {
"sender": {
"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"
"url": "https://github.com/starkware-libs/starknet-privacy/blob/f9256c5e37682d37437e69a795ddd51d924c9b97/packages/privacy/src/events.cairo#L17-L40"
}
],
"id": "chainAnalyst",
"value": "Link at risk",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
}
]
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "The paymaster and the sequencer receive the client proof with every action.",
"exposureContinued": "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.",
"exposureShort": "Ready and Xverse are closed source, so what they send to third parties is up to their makers and can change at any time.",
"exposureContinued": "Today both wallets send each action to their own servers for proving and note discovery. The paymaster receives the finished transaction with its proof before broadcast, and how much of the private execution the proof reveals is not established.",
"advice": "Use a client built on the open SDK with the user's own prover and note discovery, and turn on its oblivious relay.",
"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": "Ready extension (proving and discovery on Ready servers)",
"url": "https://chromewebstore.google.com/detail/dlcobpjiigpikoobohmabehhmhfoodbb"
},
{
"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": "Xverse extension (proving and discovery on Xverse servers)",
"url": "https://chromewebstore.google.com/detail/idnnbdplmphpflfnlkomgpfbpcgelopg"
},
{
"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": "The SDK takes prover and discovery endpoints from the client",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/fcc7c90f544ceb08621c32616bde29f1fdcbeb54/sdk/src/factory.ts#L45-L62"
},
{
"title": "Stwo Cairo is not zero-knowledge by default",
"url": "https://github.com/starkware-libs/stwo-cairo/blob/2b1fb4ded5496fb2db4e8261cd5ee5ef6b710839/README.md#security-model"
"title": "Oblivious relay is an option of the proving client",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/fcc7c90f544ceb08621c32616bde29f1fdcbeb54/sdk/src/internal/proving-service-provider.ts#L73-L80"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": [
{
"field": "sender",
"exposure": "unverifiable"
},
{
"field": "amount",
"exposure": "unverifiable"
}
]
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "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.",
"exposureContinued": "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.",
"exposureContinued": "The wallet's prover receives the user's viewing key with every action, and for most Ready accounts Ready's servers create that key. Ready's servers also learn every recipient the wallet checks for registration. StarkWare's sequencer alone checks the client proofs, which are not zero-knowledge by default. Every deposit needs a fresh signature from the holder of the screener key, who can block any depositor.",
"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"
"url": "https://github.com/starkware-libs/starknet-privacy/blob/f9256c5e37682d37437e69a795ddd51d924c9b97/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": "Proving request carries the viewing key",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/fcc7c90f544ceb08621c32616bde29f1fdcbeb54/sdk/src/internal/proof-invocation-factory.ts#L132-L136"
},
{
"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": "Deposits require a screening attestation",
"url": "https://github.com/starkware-libs/starknet-privacy/blob/f9256c5e37682d37437e69a795ddd51d924c9b97/packages/privacy/src/interface.cairo#L530-L545"
},
{
"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"
"title": "Stwo Cairo is not zero-knowledge by default",
"url": "https://github.com/starkware-libs/stwo-cairo/blob/2b1fb4ded5496fb2db4e8261cd5ee5ef6b710839/README.md#security-model"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
},
{
"field": "asset",
"exposure": "exposed"
}
]
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "The proofs rest only on hashes, the encryption on elliptic curves.",
"exposureContinued": "Channel keys, note contents and the auditor escrow all use this encryption. Whoever breaks elliptic-curve cryptography recovers the auditor key from its onchain public key, and with it every escrowed viewing key and 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"
"url": "https://github.com/starkware-libs/starknet-privacy/blob/fcc7c90f544ceb08621c32616bde29f1fdcbeb54/sdk/src/utils/encryptions.ts"
},
{
"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"
"url": "https://github.com/starkware-libs/starknet-privacy/blob/f9256c5e37682d37437e69a795ddd51d924c9b97/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": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
},
{
"field": "asset",
"exposure": "exposed"
}
]
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "defi",
"label": "DeFi",
"description": "Interop with DeFi (swaps, vaults) from within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"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."
}
},
"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."
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The pool contract, SDK, discovery service and prover are open source. The deployed pool runs a version without a release tag, which has not been rebuilt from source, and the wallets are closed."
},
"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.",
"riskSummary": "## Funds can be stolen if\n1. the proof system or virtual Starknet execution model is broken, allowing invalid actions.\n2. the Starknet sequencer includes a transaction with fabricated proof facts.\n3. a multisig deploys a malicious pool upgrade, which takes effect without 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 viewing key required to spend their notes.\n2. the pool is paused or upgraded so that valid actions fail.\n
\n## Privacy can be lost if\n1. the holder of the auditor key decrypts users' viewing keys, which exposes their whole history (see privileged insider).\n2. the wallet's prover or note discovery service, which receives the viewing key with every action, leaks or keeps it (see privileged insider).\n3. a first payment to a new contact, a channel opened beside a deposit or a unique amount links activity (see public observer, chain analyst).\n4. elliptic-curve cryptography is broken, which opens the auditor key and with it every user's history (see future adversary).",
"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,
"anonymitySet": {
"minimumAmounts": [
"5000000000000000000000",
"500000000000000000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000",
"20000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"250000",
"25000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"100000000000000000",
"10000000000000000000"
]
},
"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,
"anonymitySet": {
"minimumAmounts": [
"200000000",
"20000000000"
]
},
"deposit": {
"event": "0x9149d2123147c5f43d258257fef0b7b969db78269369ebcf5ebb9eef8592f2",
"extractor": "strk20Deposit",
"params": {
"tokenAddress": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"
}
},
"withdrawal": {
"event": "0x2eed7e29b3502a726faf503ac4316b7101f3da813654e8df02c13449e03da8",
"extractor": "strk20Withdrawal",
"params": {
"tokenAddress": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"
}
}
}
]
}
],
"trackedOn": [
"starknet"
],
"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)"
"content": "The pool is an upgradeable contract with StarkWare's role system. One multisig holds the governance, security and upgrade roles and can replace the pool's code at once, as it did on 2026-09-23. A second multisig sets the fee, the fee collector, the proof validity window and which DeFi helpers need screening. It also owns a delayed executor that holds the upgrade role behind a one-day delay but has not been used. The security governor role, held by the first multisig, can replace the auditor and screener keys at once. Pausing needs a role nobody holds today, which the first multisig can grant."
},
"zkCatalogId": "stwo"
}
+81 -28
+6 -2
{
"badges": [],
"description": "A classic Ethereum mixer design based on fixed-denomination pools and zk withdrawals.",
"detailedDescription": "Tornado Cash is a non-custodial mixer on Ethereum built around separate fixed-denomination pools, which prevents linking deposits and withdrawals via the amount. A deposit publishes a commitment into a Merkle tree producing a secret note, and a later withdrawal uses a zk-SNARK proof and the note to send the same denomination to a different address, breaking the deposit-withdrawal link. The note represents ownership of tokens in a Tornado cash pool, and losing it will effectively mean losing the tokens.\n\nThe core mixer contracts are immutable and have no admin, pause, or upgrade path, so funds can only move out with a valid proof. However Tornado cash features TORN token governance, which controls peripheral smart contracts: official pool registry, relayer registration requirement and TORN tokenomics.\n\n### Privacy considerations\n\nTornado cash introduces a permissionless relayer network, which is essential for practical privacy. Relayers process withdrawals from Tornado cash pools on user's behalf for a fee, which enables withdrawals to fresh addresses without funding them before the withdrawal. Without an active relayer network, practical privacy of Tornado cash deteriorates significantly.\n\nPractical privacy also depends on the timing of deposits and withdrawals, underlying network and browser used to interact with Tornado cash frontend (if used), RPC providers used to send transactions and query public blockchain state. Users are advised to research [OPSEC best practice](/publications/privacy-best-practices).\n\n### Fees\n\nThe immutable Tornado Cash pools do not charge a protocol-level deposit or withdrawal fee. Withdrawals can include a relayer fee chosen offchain between the user and the relayer and passed to the pool withdrawal call.\n\n### Compliance\n\nTornado cash does not have any protocol-level compliance features. However, it provides an optional [Compliance Tool](https://docs.tornado.cash/tornado-cash-classic/compliance-tool), which allows users to generate a proof linking a withdrawal to a specific deposit without revealing this information publicly onchain. This enables users to selectively disclose the origin of funds to third parties, such as exchanges or regulators.\n\nProtocol pools were [sanctioned by OFAC in August 2022](https://home.treasury.gov/news/press-releases/jy0916), flagging funds moved through these smart contracts as illicit and resulting in broad censorship (mainly by block builders) of transactions touching the contracts. Sanctions were lifted on March 21, 2025.\n\n### Anonymity set\n\nA user's anonymity set consists of all previous deposits into the same bucket (i.e. deposits of the same token and amount). Deposits can be mixed only with other deposits of the same token and denomination. To maximize the anonymity set, users are advised to deposit into the buckets with the most usage.\n\n### Secure frontend\n\nFor average users, the Tornado Cash frontend must be recognized as a critical part of the privacy protocol. If compromised, it can steal user assets and violate user privacy, which [was exploited in 2024](https://www.coindesk.com/business/2024/02/26/tornado-cash-reportedly-suffers-backend-exploit-user-deposits-at-risk).\n\nThe frontend's root of trust is the IPFS content hash registered for [tornadocash.eth](https://app.ens.domains/tornadocash.eth?tab=records) in the ENS smart contract on Ethereum. \nThe latest hash can be fetched from a trusted ethereum node (e.g. your own or trusted light node rpc). \nThere were incidents of malicious frontend hash updates passing onchain governance proposals before, so using a publicly audited or self-audited IPFS hash is advised. \nAn example of an audit process with useful links can be found [here](https://gist.github.com/pcaversaccio/ea7f62fd21b6e22f301980007f7c767e) or [here](https://notes.ethereum.org/@GW1ZUbNKR5iRjjKYx6_dJQ/Bk8zsJ9xj).\nThe frontend can then be accessed by running a local IPFS node that automatically fetches the verified frontend hash (e.g. [dapp3.eth](https://github.com/apoorvlathey/dapp3) or [kubo](https://github.com/ipfs/kubo) + [ipfs-companion](https://github.com/ipfs/ipfs-companion) and serves it in the browser locally.",
"description": "An unconditional privacy pool of fixed amounts with immutable contracts, and an app pinned on ENS by token governance.",
"detailedDescription": "Tornado Cash lets the user withdraw a deposit of ETH or a token to an address with no visible link to it. Each pool takes one amount, like 0.1 ETH, so amounts reveal nothing within a pool. Depositors, recipients and timing stay public.\n\nNo one can pause, upgrade or block the pools. TORN token holders govern the app pinned on tornadocash.eth, the router's list of pools and the relayer registry. Leaving needs no one: anyone can prove a withdrawal directly to a pool.\n\n### Flow\n\n1. **Deposit.** The app creates a secret note and sends its commitment to the pool through the router. Your address is public.\n2. **Withdraw.** You paste the note into the app, which proves that it belongs to an unspent deposit without revealing which. A relayer submits the proof and pays the gas for a fee from the withdrawal.\n\n### Keys\n\nEach deposit has its own note, which the app shows once for the user to save. Whoever holds the note can withdraw, and losing it loses the funds. An optional note account backs notes up onchain, encrypted to a key from the user's wallet.\n\n### App\n\nThe ratings assume the classic app pinned on [tornadocash.eth](https://app.ens.domains/tornadocash.eth?tab=records). Each proposal that changes it names the [source commit](https://github.com/tornado-dao/classic-ui) and a [build recipe](https://github.com/tornado-dao/tornado-ipfs-ui) that recomputes its IPFS hash, currently `bafybeigduniojgqrz7e4vntsgmovdli3oi7a2pnzzvz2gfxlvj3yqwk75a`. Gateways like eth.limo serve it, and the user's own IPFS node takes the gateway out of the trust path. The app lists relayers from the registry, accepts the user's own RPC endpoint and withdraws through a relayer without a wallet. In 2024 a pinned version [sent users' notes to an attacker](https://www.coindesk.com/business/2024/02/26/tornado-cash-reportedly-suffers-backend-exploit-user-deposits-at-risk) ([code analysis here](https://github.com/sekuba/tornadocash-pwned)).\n\n### Compliance\n\nTornado Cash has no screening or approval list. The app's [compliance page](https://github.com/tornado-dao/classic-ui/blob/17c54dae9ad91bb3c6d62a7860df6cd365ab76d4/pages/compliance.vue) builds a report that links one of the user's withdrawals to its deposit, for the user to share. The US sanctions on the pools were lifted in March 2025.\n\n### Fees\n\nThe pools charge no fee. Relayers take their fee from the withdrawal. Registered relayers also burn part of their TORN stake on each withdrawal, at a rate governance sets.\n\n### Anonymity set\n\nEach pool has its own set: the deposits of that token and amount not yet withdrawn. Pools differ widely in size, and the ETH pools on Ethereum are by far the largest.",
"links": {
"websites": [
"https://app.ens.domains/tornadocash.eth?tab=records"
],
"repositories": [
"https://github.com/tornadocash/tornado-core",
"https://github.com/tornado-dao/classic-ui"
],
"socialMedia": [
"https://t.me/TornadoCashOfficialDAO"
]
}
}
+37 -1
null
[
{
"title": "Pools become immutable",
"url": "https://www.coindesk.com/tech/2020/05/18/developers-of-ethereum-privacy-tool-tornado-cash-smash-their-keys",
"date": "2020-05-18T00:00:00Z",
"description": "The developers set the operator of every pool to zero, so no one can change the pools.",
"type": "general"
},
{
"title": "Added to the OFAC sanctions list",
"url": "https://home.treasury.gov/news/press-releases/jy0916",
"date": "2022-08-08T00:00:00Z",
"description": "OFAC adds Tornado Cash and the pools' addresses to its sanctions list.",
"type": "incident"
},
{
"title": "Governance taken over by a malicious proposal",
"url": "https://www.theblock.co/post/231637/attacker-uses-malicious-proposal-to-take-over-tornado-cash-governance",
"date": "2023-05-20T00:00:00Z",
"description": "Proposal 20 hands the attacker control of governance until proposal 21 reverts it.",
"type": "incident"
},
{
"title": "Pinned app leaks deposit notes",
"url": "https://forklog.com/en/backdoor-discovered-in-ipfs-version-of-tornado-cash/",
"date": "2024-01-02T00:00:00Z",
"description": "The app pinned by proposal 44 sends deposit notes to an outside server until proposal 52.",
"type": "incident"
},
{
"title": "Removed from the OFAC sanctions list",
"url": "https://home.treasury.gov/news/press-releases/sb0057",
"date": "2025-03-21T00:00:00Z",
"description": "OFAC removes Tornado Cash and the pools' addresses from its sanctions list.",
"type": "general"
}
]
+36 -23
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "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.",
"advice": "Exit through a relayer, so none of the user's wallets pay 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"
"url": "https://github.com/tornadocash/tornado-core/blob/a533ad9ffb62163a42d4fa9a09984c5dd4e5c41d/circuits/withdraw.circom"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposureShort": "The candidates for a withdrawal are the unspent deposits of the same pool, the anonymity set, which differs sharply between pools.",
"exposureContinued": "Timing, address reuse and relayer choice narrow it further. Paying gas from your own wallet instead of a relayer exposes that wallet's fingerprint, its account implementation and fee habits, even from a fresh address.",
"advice": "Use a pool with a large anonymity set. Deposit a large sum as one note. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"exposureShort": "A relayed withdrawal hides among all unspent deposits of its pool, the anonymity set, which differs sharply between pools.",
"exposureContinued": "Timing, address reuse and relayer choice narrow it further. Paying gas from the user's wallet instead of a relayer exposes its fingerprint, account implementation and fee habits, even from a fresh address.",
"advice": "Use a pool with a large anonymity set. Deposit and withdraw several notes at different times, not in batches. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"sources": [
{
"contract": "Pool_1_ETH",
"title": "withdraw: any relayer, paid from the withdrawn amount"
},
{
"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",
"exposureShort": "The app downloads all deposits and matches locally, so a node learns only which pool you looked at.",
"exposureContinued": "The relayer receives only the finished withdrawal.",
"advice": "Read the chain from your own node, send through a public RPC over Tor, and use a popular relayer. If you settle for a VPN instead of Tor, pick one you trust: it hides your IP from the relayer but sees it itself.",
"exposureShort": "The app works in Tor Browser and downloads every deposit of a pool, so its services learn the pool but never the user's note.",
"exposureContinued": "Every listed relayer gets a status request, and the chosen one receives the finished withdrawal.",
"advice": "Read the chain from the user's own node, send through a public RPC over Tor, and use a popular relayer. If using a VPN instead of Tor, choose a trusted provider: it hides the user's IP from the relayer but sees it itself. Withdraw without connecting a wallet.",
"sources": [
{
"title": "Event sync (classic UI)",
"url": "https://github.com/tornadocash/tornado-classic-ui/blob/master/services/events.js"
"title": "Deposits fetched per pool and matched locally",
"url": "https://github.com/tornado-dao/classic-ui/blob/17c54dae9ad91bb3c6d62a7860df6cd365ab76d4/services/events.js#L90-L115"
},
{
"title": "Relayer endpoint",
"url": "https://github.com/tornadocash/tornado-relayer/blob/master/src/contollers/controller.js"
"title": "The relayer receives only the finished withdrawal",
"url": "https://github.com/tornado-dao/classic-ui/blob/17c54dae9ad91bb3c6d62a7860df6cd365ab76d4/store/relayer.js#L430-L460"
},
{
"title": "Custom RPC setting",
"url": "https://github.com/tornado-dao/classic-ui/blob/17c54dae9ad91bb3c6d62a7860df6cd365ab76d4/components/Settings.vue#L8-L50"
},
{
"title": "Only withdrawals without a relayer need a wallet",
"url": "https://github.com/tornado-dao/classic-ui/blob/17c54dae9ad91bb3c6d62a7860df6cd365ab76d4/components/withdraw/Withdraw.vue#L224-L226"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "good",
"exposureShort": "The pools cannot be paused, upgraded or made to reveal anything.",
"exposureContinued": "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.",
"exposureContinued": "Token governance controls the app pinned on tornadocash.eth, the router's pool list and the relayer list. Every change waits at least 7 days from proposal to execution.",
"advice": "Load the app from the IPFS hash on tornadocash.eth through the user's own IPFS node. Run an inspected local build of the client instead of the hosted frontend.",
"sources": [
{
"contract": "Pool_0.1_ETH",
"title": "0.1 ETH pool (no operator)"
},
{
"contract": "InstanceRegistry"
"section": "upgrades-and-governance"
},
{
"section": "permissions"
"title": "Build recipe that recomputes the pinned app's IPFS hash",
"url": "https://github.com/tornado-dao/tornado-ipfs-ui"
}
],
"id": "privilegedInsider",
"value": "Link private",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "warning",
"exposureShort": "Commitments use a Pedersen hash on an elliptic curve.",
"exposureContinued": "Whoever breaks elliptic-curve cryptography can 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.",
"exposureContinued": "Whoever breaks elliptic-curve cryptography 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": "Keep the note on the user's device and leave the onchain backup off.",
"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"
"url": "https://github.com/tornadocash/tornado-core/blob/a533ad9ffb62163a42d4fa9a09984c5dd4e5c41d/circuits/withdraw.circom"
},
{
"contract": "TornadoRouter",
"title": "Onchain note backups"
}
],
"id": "futureAdversary",
"value": "Link at risk",
"alsoExposed": []
}
}
},
"attributes": [
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "fixedAmounts",
"label": "Fixed amounts",
"description": "Pre-defined transfer amounts use distinct buckets (anonymity sets)."
}
],
"category": {
"id": "pool",
"label": "Pool",
"description": "Funds enter a shared pool and leave it later, with no actions inside. Hides which deposit became which withdrawal."
},
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "The core Tornado Cash contracts are immutable and have no admin upgrade path, so users can always withdraw with a valid note and proof.",
"walkawayTest": {
"passed": true
}
},
"relayerTracking": {
"type": "onchainEvents",
"sources": [
{
"address": "eth:0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"sinceTimestamp": 1576523323,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"sinceTimestamp": 1576534673,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"sinceTimestamp": 1576536415,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xA160cdAB225685dA1d56aa342Ad8841c3b53f291",
"sinceTimestamp": 1577296976,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"sinceTimestamp": 1576534688,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"sinceTimestamp": 1577297383,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"sinceTimestamp": 1616114822,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x23773E65ed146A459791799d01336DB287f25334",
"sinceTimestamp": 1616115387,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"sinceTimestamp": 1577297782,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x03893a7c7463AE47D46bc7f091665f1893656003",
"sinceTimestamp": 1616155055,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"sinceTimestamp": 1616135923,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af",
"sinceTimestamp": 1616115450,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"sinceTimestamp": 1577298068,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D",
"sinceTimestamp": 1577298155,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"sinceTimestamp": 1577298900,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f",
"sinceTimestamp": 1577298985,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"sinceTimestamp": 1616134868,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0x610B717796ad172B316836AC95a2ffad065CeaB4",
"sinceTimestamp": 1616123193,
"extractor": "tornadoCashWithdrawal"
},
{
"address": "eth:0xbB93e510BbCD0B7beb5A853875f9eC60275CF498",
"sinceTimestamp": 1616135758,
"extractor": "tornadoCashWithdrawal"
}
]
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "There is at least one practical way to participate in Tornado Cash using published source code that can be audited and run locally."
"description": "The circuits, contracts, app and relayer are open source. Each app release names its commit and a build recipe for its IPFS hash, and rebuilding the current release reproduces it."
},
"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).",
"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. governance pins a malicious app on tornadocash.eth, which can steal the notes, and with them the funds, of users who load it (see privileged insider).\n
\n## Funds can be lost if\n1. a user loses the note required to withdraw.\n
\n## Privacy can be lost if\n1. elliptic-curve cryptography is broken, which lets an attacker test candidate deposits for each withdrawal and read notes backed up onchain (see future adversary).",
"tokens": [
{
"token": {
"address": "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643",
"iconUrl": "https://assets.coingecko.com/coins/images/9281/large/cDAI.png?1696509390",
"symbol": "cDAI",
"decimals": 8,
"priceId": "cdai",
"sinceTimestamp": 1577297782
},
"buckets": [
{
"id": "tornado-cDAI-5000",
"type": "denomination",
"label": "cDAI 5000",
"address": "eth:0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"sinceTimestamp": 1577297782,
"denomination": "5000",
"anonymitySet": {
"minimumAmounts": [
"500000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000"
}
}
},
{
"id": "tornado-cDAI-50000",
"type": "denomination",
"label": "cDAI 50000",
"address": "eth:0x03893a7c7463AE47D46bc7f091665f1893656003",
"sinceTimestamp": 1616155055,
"denomination": "50000",
"anonymitySet": {
"minimumAmounts": [
"5000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "5000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "5000000000000"
}
}
},
{
"id": "tornado-cDAI-500000",
"type": "denomination",
"label": "cDAI 500000",
"address": "eth:0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"sinceTimestamp": 1616135923,
"denomination": "500000",
"anonymitySet": {
"minimumAmounts": [
"50000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "50000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "50000000000000"
}
}
},
{
"id": "tornado-cDAI-5000000",
"type": "denomination",
"label": "cDAI 5000000",
"address": "eth:0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af",
"sinceTimestamp": 1616115450,
"denomination": "5000000",
"anonymitySet": {
"minimumAmounts": [
"500000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "500000000000000"
}
}
}
]
},
{
"token": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"iconUrl": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1576534688
},
"buckets": [
{
"id": "tornado-DAI-100",
"type": "denomination",
"label": "DAI 100",
"address": "eth:0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"sinceTimestamp": 1576534688,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
}
},
{
"id": "tornado-DAI-1000",
"type": "denomination",
"label": "DAI 1000",
"address": "eth:0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"sinceTimestamp": 1577297383,
"denomination": "1000",
"anonymitySet": {
"minimumAmounts": [
"1000000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000000"
}
}
},
{
"id": "tornado-DAI-10000",
"type": "denomination",
"label": "DAI 10000",
"address": "eth:0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"sinceTimestamp": 1616114822,
"denomination": "10000",
"anonymitySet": {
"minimumAmounts": [
"10000000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000000"
}
}
},
{
"id": "tornado-DAI-100000",
"type": "denomination",
"label": "DAI 100000",
"address": "eth:0x23773E65ed146A459791799d01336DB287f25334",
"sinceTimestamp": 1616115387,
"denomination": "100000",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000000"
}
}
}
]
},
{
"token": {
"address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
"symbol": "ETH",
"decimals": 18,
"priceId": "ethereum",
"sinceTimestamp": 1576523323
},
"buckets": [
{
"id": "tornado-ETH-0.1",
"type": "denomination",
"label": "ETH 0.1",
"address": "eth:0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"sinceTimestamp": 1576523323,
"denomination": "0.1",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000"
}
}
},
{
"id": "tornado-ETH-1",
"type": "denomination",
"label": "ETH 1",
"address": "eth:0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"sinceTimestamp": 1576534673,
"denomination": "1",
"anonymitySet": {
"minimumAmounts": [
"1000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000000000000"
}
}
},
{
"id": "tornado-ETH-10",
"type": "denomination",
"label": "ETH 10",
"address": "eth:0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"sinceTimestamp": 1576536415,
"denomination": "10",
"anonymitySet": {
"minimumAmounts": [
"10000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "10000000000000000000"
}
}
},
{
"id": "tornado-ETH-100",
"type": "denomination",
"label": "ETH 100",
"address": "eth:0xA160cdAB225685dA1d56aa342Ad8841c3b53f291",
"sinceTimestamp": 1577296976,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000000000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000000000000000"
}
}
}
]
},
{
"token": {
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"iconUrl": "https://assets.coingecko.com/coins/images/6319/large/usdc.png?1696506694",
"symbol": "USDC",
"decimals": 6,
"priceId": "usd-coin",
"sinceTimestamp": 1577298068
},
"buckets": [
{
"id": "tornado-USDC-100",
"type": "denomination",
"label": "USDC 100",
"address": "eth:0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"sinceTimestamp": 1577298068,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
}
},
{
"id": "tornado-USDC-1000",
"type": "denomination",
"label": "USDC 1000",
"address": "eth:0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D",
"sinceTimestamp": 1577298155,
"denomination": "1000",
"anonymitySet": {
"minimumAmounts": [
"1000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
}
}
]
},
{
"token": {
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"iconUrl": "https://assets.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "USDT",
"decimals": 6,
"priceId": "tether",
"sinceTimestamp": 1577298900
},
"buckets": [
{
"id": "tornado-USDT-100",
"type": "denomination",
"label": "USDT 100",
"address": "eth:0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"sinceTimestamp": 1577298900,
"denomination": "100",
"anonymitySet": {
"minimumAmounts": [
"100000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
}
},
{
"id": "tornado-USDT-1000",
"type": "denomination",
"label": "USDT 1000",
"address": "eth:0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f",
"sinceTimestamp": 1577298985,
"denomination": "1000",
"anonymitySet": {
"minimumAmounts": [
"1000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
}
}
]
},
{
"token": {
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"iconUrl": "https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1696507857",
"symbol": "WBTC",
"decimals": 8,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1616123193
},
"buckets": [
{
"id": "tornado-WBTC-0.1",
"type": "denomination",
"label": "WBTC 0.1",
"address": "eth:0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"sinceTimestamp": 1616134868,
"denomination": "0.1",
"anonymitySet": {
"minimumAmounts": [
"10000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "10000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "10000000"
}
}
},
{
"id": "tornado-WBTC-1",
"type": "denomination",
"label": "WBTC 1",
"address": "eth:0x610B717796ad172B316836AC95a2ffad065CeaB4",
"sinceTimestamp": 1616123193,
"denomination": "1",
"anonymitySet": {
"minimumAmounts": [
"100000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "100000000"
}
}
},
{
"id": "tornado-WBTC-10",
"type": "denomination",
"label": "WBTC 10",
"address": "eth:0xbB93e510BbCD0B7beb5A853875f9eC60275CF498",
"sinceTimestamp": 1616135758,
"denomination": "10",
"anonymitySet": {
"minimumAmounts": [
"1000000000"
]
},
"deposit": {
"event": "0xa945e51eec50ab98c161376f0db4cf2aeba3ec92755fe2fcd388bdbbb80ff196",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
},
"withdrawal": {
"event": "0xe9e508bad6d4c3227e881ca19068f099da81b5164dd6d62b2eaf1e8bc6c34931",
"extractor": "fixedAmount",
"params": {
"amount": "1000000000"
}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "Tornado cash has a TORN DAO, which does not have the authority to upgrade or modify existing pools in any way. However it controls a significant portion of the Tornado cash protocol and periphery, including:\n\n1. **Default router for deposits and withdrawals and the official registry of supported pools**. Malicious upgrades of these components could lead to users losing deposited tokens. \n2. **Standard UI IPFS hash registered on ENS** ([link](https://app.ens.domains/tornadocash.eth)). Malicious upgrades of these components could lead to users losing deposited tokens. \n3. **TORN token itself**. Malicious upgrades of the token could lead to token transfers being frozen. \n4. **Registered relayers**. Malicious upgrades of these components could remove all registered relayers, disrupting user-relayer coordination and complicating private withdrawals.",
"content": "TORN holders govern through the Governance contract, after a vote and a timelock. They cannot change the pools. They control the InstanceRegistry the router reads its pool list from, the RelayerRegistry, the FeeManager, the TORN token and the IPFS hash of the app on tornadocash.eth. A malicious registry upgrade could steal tokens users approved to the router. A malicious app could steal the notes of users who load it. Removing relayers from the registry hides them from the app, while the pools still accept any relayer.",
"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.57 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.",
"Voting venue": "[Governance contract](https://etherscan.io/address/0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce) on Ethereum. Proposals are viewable on the governance page of the app, see the App section 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."
}
}
}
}
+1 -1
{
"addresses": {
"ethereum": [
{
"name": "Pool_50K_cDAI",
"isVerified": true,
"address": "eth:0x03893a7c7463AE47D46bc7f091665f1893656003",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 50K cDAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x03893a7c7463AE47D46bc7f091665f1893656003#code"
},
{
"name": "Pool_10K_DAI",
"isVerified": true,
"address": "eth:0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 10K DAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x07687e702b410Fa43f4cB4Af7FA097918ffD2730#code"
},
{
"name": "Pool_1000_USDT",
"isVerified": true,
"address": "eth:0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 1000 USDT deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f#code"
},
{
"name": "Pool_0.1_ETH",
"isVerified": true,
"address": "eth:0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 0.1 ETH deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc#code"
},
{
"name": "Pool_100_USDT",
"isVerified": true,
"address": "eth:0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 100 USDT deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x169AD27A470D064DEDE56a2D3ff727986b15D52B#code"
},
{
"name": "Pool_0.1_WBTC",
"isVerified": true,
"address": "eth:0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 0.1 WBTC deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x178169B423a011fff22B9e3F3abeA13414dDD0F1#code"
},
{
"name": "Pool_5K_cDAI",
"isVerified": true,
"address": "eth:0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 5K cDAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x22aaA7720ddd5388A3c0A3333430953C68f1849b#code"
},
{
"name": "Pool_100K_DAI",
"isVerified": true,
"address": "eth:0x23773E65ed146A459791799d01336DB287f25334",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 100K DAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x23773E65ed146A459791799d01336DB287f25334#code"
},
{
"name": "Pool_500K_cDAI",
"isVerified": true,
"address": "eth:0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 500K cDAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x2717c5e28cf931547B621a5dddb772Ab6A35B701#code"
},
{
"name": "Pool_1000_USDC",
"isVerified": true,
"address": "eth:0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 1000 USDC deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D#code"
},
{
"name": "Pool_1_ETH",
"isVerified": true,
"address": "eth:0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 1 ETH deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936#code"
},
{
"name": "RelayerRegistry",
"isVerified": true,
"address": "eth:0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2",
"upgradeability": {
"proxyType": "EIP1967 proxy",
"admins": [
"eth:0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"
],
"implementations": [
"eth:0x84b0c75c1F6211450FEC7289c32D717AdaDD702A"
]
},
"chain": "ethereum",
"pastUpgrades": [
{
"timestamp": 1685886611,
"transactionHash": "0x98125b78ef0dc7fd040764f37ee24e40ae5352b8551ba671aa7bd8e8320dcef9",
"implementations": [
"eth:0xe27B91724C55E950F68b394f33FA3B86693179C0"
]
},
{
"timestamp": 1698933491,
"transactionHash": "0x3d11319e48b756e50c873bebe3a34d2e8f536436d70f99bfd6da03e4ea5642e4",
"implementations": [
"eth:0xA3b821A2C6C9a52533178a1f2B9Ff4AedbA880e6"
]
},
{
"timestamp": 1700316599,
"transactionHash": "0x9b8cc0ca7bf2019b408eead6d2db8046957134183dd882007be41822082ab319",
"implementations": [
"eth:0x84b0c75c1F6211450FEC7289c32D717AdaDD702A"
]
}
],
"description": "Upgradeable registry that tracks registered relayers, their worker addresses, and their staked TORN balances used to service withdrawals. Relayers registered here could be queried by a tornadocash frontend to facilitate discovery for users, but users are free to use any (unregistered) relayer at withdrawal time.\n* Roles:\n * **admin**: GovernanceProposalStateUpgrade\n * **governance**: GovernanceProposalStateUpgrade",
"description": "Upgradeable registry of relayers, their worker addresses and their staked TORN. The app lists the relayers registered here, while the pools pay any relayer a withdrawal names.\n* Roles:\n * **admin**: GovernanceProposalStateUpgrade\n * **governance**: GovernanceProposalStateUpgrade",
"upgradableBy": [
{
"name": "GovernanceProposalStateUpgrade",
"delay": "no"
}
],
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2#code"
},
{
"name": "FeeManager",
"isVerified": true,
"address": "eth:0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7",
"upgradeability": {
"proxyType": "EIP1967 proxy",
"admins": [
"eth:0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"
],
"implementations": [
"eth:0xf4B067dD14e95Bab89Be928c07Cb22E3c94E0DAA"
]
},
"chain": "ethereum",
"pastUpgrades": [],
"description": "Upgradeable fee manager that derives relayer burn fees for each Tornado instance from registry settings and Uniswap TWAP data.\n* Roles:\n * **admin**: GovernanceProposalStateUpgrade\n * **governance**: GovernanceProposalStateUpgrade",
"upgradableBy": [
{
"name": "GovernanceProposalStateUpgrade",
"delay": "no"
}
],
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7#code"
},
{
"name": "Pool_1_WBTC",
"isVerified": true,
"address": "eth:0x610B717796ad172B316836AC95a2ffad065CeaB4",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 1 WBTC deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x610B717796ad172B316836AC95a2ffad065CeaB4#code"
},
{
"name": "MiMCHasher",
"isVerified": true,
"address": "eth:0x83584f83f26aF4eDDA9CBe8C730bc87C364b28fe",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"references": [
{
"title": "Tornado Cash hasher build script",
"url": "https://github.com/tornadocash/tornado-core/blob/master/scripts/compileHasher.js"
},
{
"title": "Source Code",
"url": "https://github.com/tornadocash/circomlib/blob/c372f14d324d57339c88451834bf2824e73bbdbc/src/mimcsponge_gencontract.js"
}
],
"description": "MiMC hasher used by every Tornado pool for commitment-tree operations (linked library in the 2019 pools, constructor argument in the 2021 pools), manually verified against the pinned Tornado circomlib generator.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x83584f83f26aF4eDDA9CBe8C730bc87C364b28fe#code"
},
{
"name": "Pool_10_ETH",
"isVerified": true,
"address": "eth:0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 10 ETH deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF#code"
},
{
"name": "Pool_100_ETH",
"isVerified": true,
"address": "eth:0xA160cdAB225685dA1d56aa342Ad8841c3b53f291",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 100 ETH deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xA160cdAB225685dA1d56aa342Ad8841c3b53f291#code"
},
{
"name": "InstanceRegistry",
"isVerified": true,
"address": "eth:0xB20c66C4DE72433F3cE747b58B86830c459CA911",
"upgradeability": {
"proxyType": "EIP1967 proxy",
"admins": [
"eth:0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"
],
"implementations": [
"eth:0x2573BAc39EBe2901B4389CD468F2872cF7767FAF"
]
},
"chain": "ethereum",
"pastUpgrades": [],
"description": "Upgradeable registry of supported Tornado Cash pool instances and their mutable per-instance fee and routing configuration.\n* Roles:\n * **admin**: GovernanceProposalStateUpgrade\n * **governance**: GovernanceProposalStateUpgrade",
"upgradableBy": [
{
"name": "GovernanceProposalStateUpgrade",
"delay": "no"
}
],
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xB20c66C4DE72433F3cE747b58B86830c459CA911#code"
},
{
"name": "Pool_10_WBTC",
"isVerified": true,
"address": "eth:0xbB93e510BbCD0B7beb5A853875f9eC60275CF498",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 10 WBTC deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xbB93e510BbCD0B7beb5A853875f9eC60275CF498#code"
},
{
"name": "Pool_5M_cDAI",
"isVerified": true,
"address": "eth:0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 5M cDAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af#code"
},
{
"name": "Pool_100_DAI",
"isVerified": true,
"address": "eth:0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 100 DAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3#code"
},
{
"name": "TornadoRouter",
"isVerified": true,
"address": "eth:0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Router contract that validates supported instances, forwards deposits and withdrawals, and subtracts from relayer stake through the relayer registry during withdrawals.\n* Roles:\n * **governance**: GovernanceProposalStateUpgrade",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b#code"
},
{
"name": "Pool_100_USDC",
"isVerified": true,
"address": "eth:0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 100 USDC deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xd96f2B1c14Db8458374d9Aca76E26c3D18364307#code"
},
{
"name": "Pool_1000_DAI",
"isVerified": true,
"address": "eth:0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Fixed-denomination Tornado Cash pool for 1000 DAI deposits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144#code"
},
{
"name": "TornadoStakingRewards",
"isVerified": true,
"address": "eth:0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29",
"upgradeability": {
"proxyType": "EIP1967 proxy",
"admins": [
"eth:0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"
],
"implementations": [
"eth:0x9c97be37840f0e754bb7aDB1b16fD0954A2BA248"
]
},
"chain": "ethereum",
"pastUpgrades": [
{
"timestamp": 1687570343,
"transactionHash": "0xf89acb4c80df2c46d8907bcd4152fb044ae0ffb398fc13a6213b49b3135047eb",
"implementations": [
"eth:0x9c97be37840f0e754bb7aDB1b16fD0954A2BA248"
]
}
],
"description": "Staking contract that escrows TORN staked by relayers. With time, their stake is subtracted from for being officially registered as relayers and receiving fees from each withdrawal, and is redirected as TORN rewards for governance participants who are locking their TORN for voting/staking.\n* Roles:\n * **admin**: GovernanceProposalStateUpgrade\n * **governance**: GovernanceProposalStateUpgrade",
"upgradableBy": [
{
"name": "GovernanceProposalStateUpgrade",
"delay": "no"
}
],
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29#code"
},
{
"name": "TORN",
"isVerified": true,
"address": "eth:0x77777FeDdddFfC19Ff86DB637967013e6C6A116C",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "TORN governance token contract. Transfers start paused and can later be toggled by governance, which also controls the paused-transfer allowlist and token rescue functions.\n* Roles:\n * **governance**: GovernanceProposalStateUpgrade",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x77777FeDdddFfC19Ff86DB637967013e6C6A116C#code"
},
{
"name": "WithdrawVerifier",
"isVerified": true,
"address": "eth:0x09193888b3f38C82dEdfda55259A82C0E7De875E",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Verifier contract for Tornado anonymity-mining withdrawal proofs.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x09193888b3f38C82dEdfda55259A82C0E7De875E#code"
},
{
"name": "TornadoTreesV1",
"isVerified": true,
"address": "eth:0x43a3bE4Ae954d9869836702AFd10393D3a7Ea417",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Legacy Tornado Trees contract that records deposit and withdrawal leaves from TornadoProxyV1 and maintains the roots later consumed by migration and mining logic.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x43a3bE4Ae954d9869836702AFd10393D3a7Ea417#code"
},
{
"name": "TornadoTreesV2",
"isVerified": true,
"address": "eth:0x527653eA119F3E6a1F5BD18fbF4714081D7B31ce",
"upgradeability": {
"proxyType": "EIP1967 proxy",
"admins": [
"eth:0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"
],
"implementations": [
"eth:0x200a79068d8141924B511bC78CB55Dca89cF5C2E"
]
},
"chain": "ethereum",
"pastUpgrades": [],
"description": "Anonymity Mining - Upgradeable batch-based Tornado Trees contract that reuses legacy tree state, queues new leaves from the newer proxy, and accepts batched root updates proven by a verifier.\n* Roles:\n * **admin**: GovernanceProposalStateUpgrade\n * **governance**: GovernanceProposalStateUpgrade",
"upgradableBy": [
{
"name": "GovernanceProposalStateUpgrade",
"delay": "no"
}
],
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x527653eA119F3E6a1F5BD18fbF4714081D7B31ce#code"
},
{
"name": "RewardSwap",
"isVerified": true,
"address": "eth:0x5cab7692D4E94096462119ab7bF57319726Eed2A",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Liquidity schedule that pays out TORN rewards for anonymity mining. Only the Miner contract can draw from it or adjust the reward weight.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x5cab7692D4E94096462119ab7bF57319726Eed2A#code"
},
{
"name": "DepositTree",
"isVerified": true,
"address": "eth:0x6234c4C2734D2B246AE074492F402D8f58ff3226",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Legacy Poseidon merkle tree that stores deposit or withdrawal leaves for TornadoTreesV1. Only its owner can insert leaves.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x6234c4C2734D2B246AE074492F402D8f58ff3226#code"
},
{
"name": "TreeUpdateVerifier",
"isVerified": true,
"address": "eth:0x653477c392c16b0765603074f157314Cc4f40c32",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Verifier contract for Tornado mining account-tree update proofs.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x653477c392c16b0765603074f157314Cc4f40c32#code"
},
{
"name": "TornadoProxyV2",
"isVerified": true,
"address": "eth:0x722122dF12D4e14e13Ac3b6895a86e84145b6967",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Newer immutable Tornado proxy that routes deposits and withdrawals to supported pool instances and records them in the upgraded Tornado Trees contract.\n* Roles:\n * **governance**: GovernanceProposalStateUpgrade",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x722122dF12D4e14e13Ac3b6895a86e84145b6967#code"
},
{
"name": "Miner",
"isVerified": true,
"address": "eth:0x746Aebc06D2aE31B71ac51429A19D54E797878E9",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Mining and reward-distribution contract for Tornado anonymity mining. It verifies reward and withdrawal proofs, maintains account roots, and routes TORN rewards through the swap contract.\n* Roles:\n * **governance**: GovernanceProposalStateUpgrade",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x746Aebc06D2aE31B71ac51429A19D54E797878E9#code"
},
{
"name": "RewardVerifier",
"isVerified": true,
"address": "eth:0x88fd245fEdeC4A936e700f9173454D1931B4C307",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Verifier contract for Tornado anonymity-mining reward proofs.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x88fd245fEdeC4A936e700f9173454D1931B4C307#code"
},
{
"name": "TornadoProxyV1",
"isVerified": true,
"address": "eth:0x905b63Fff465B9fFBF41DeA908CEb12478ec7601",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Legacy immutable Tornado proxy that routes deposits and withdrawals to pool instances and records them in the legacy Tornado Trees contract.\n* Roles:\n * **governance**: GovernanceProposalStateUpgrade",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x905b63Fff465B9fFBF41DeA908CEb12478ec7601#code"
},
{
"name": "PoseidonHasher2",
"isVerified": false,
"address": "eth:0x94C92F096437ab9958fC0A37F09348f30389Ae79",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Unverified Poseidon hasher used by the legacy DepositTree and WithdrawalTree to hash tree nodes.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x94C92F096437ab9958fC0A37F09348f30389Ae79#code"
},
{
"name": "WithdrawalTree",
"isVerified": true,
"address": "eth:0xbFA347D89Ac54F7C2De2433458cb98A85fc03CEd",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Legacy Poseidon merkle tree that stores deposit or withdrawal leaves for TornadoTreesV1. Only its owner can insert leaves.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xbFA347D89Ac54F7C2De2433458cb98A85fc03CEd#code"
},
{
"name": "PoseidonHasher",
"isVerified": false,
"address": "eth:0xD82ed8786D7c69DC7e052F7A542AB047971E73d2",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Unverified Poseidon hasher used by TornadoTreesV1 to hash deposit and withdrawal leaves.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xD82ed8786D7c69DC7e052F7A542AB047971E73d2#code"
},
{
"name": "BatchTreeUpdateVerifier",
"isVerified": true,
"address": "eth:0xEd3B00B651c4c7AF77c2fDdce4a388de84b507C6",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Anonymity Mining - Verifier contract for batched Tornado Trees root updates.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xEd3B00B651c4c7AF77c2fDdce4a388de84b507C6#code"
},
{
"name": "TornadoVault",
"isVerified": true,
"address": "eth:0x2F50508a8a3D323B91336FA3eA6ae50E55f32185",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Vault that escrows locked TORN of governance participants who wish to receive voting rights.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x2F50508a8a3D323B91336FA3eA6ae50E55f32185#code"
},
{
"name": "Verifier",
"isVerified": true,
"address": "eth:0xce172ce1F20EC0B3728c9965470eaf994A03557A",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Groth16 verifier contract used by Tornado Cash pool withdrawal proofs.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xce172ce1F20EC0B3728c9965470eaf994A03557A#code"
},
{
"name": "GasCompensationVault",
"isVerified": true,
"address": "eth:0xFA4C1f3f7D5dd7c12a9Adb82Cd7dDA542E3d59ef",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Vault that escrows ETH for governance gas-compensation payouts and can return unused funds to governance.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xFA4C1f3f7D5dd7c12a9Adb82Cd7dDA542E3d59ef#code"
}
]
},
"risks": [
{
"category": "Funds can be stolen if",
"text": "the source code of unverified contracts contains malicious code.",
"isCritical": true
}
],
"zkVerifiers": [
"eth:0xce172ce1F20EC0B3728c9965470eaf994A03557A",
"eth:0xEd3B00B651c4c7AF77c2fDdce4a388de84b507C6",
"eth:0x653477c392c16b0765603074f157314Cc4f40c32",
"eth:0x88fd245fEdeC4A936e700f9173454D1931B4C307",
"eth:0x09193888b3f38C82dEdfda55259A82C0E7De875E"
]
}
+1 -1
{
"baseTimestamp": 1791454331,
"baseTimestamp": 1791619122,
"contractsDiscoDriven": false,
"hasDiscoUi": false,
"isDiscoDriven": false,
"permissionsDiscoDriven": false
}
+102 -72
+2 -2
{
"badges": [],
"description": "A stealth-address payment protocol that hides the recipient behind a fresh address for every transfer.",
"detailedDescription": "Umbra Cash is a stealth-address payment protocol. A recipient registers separate viewing and spending public keys, and a sender uses them to derive a fresh address that only the recipient can control. The sender transfers ETH directly to that address or routes an ERC-20 payment through the immutable Umbra contract, which emits the encrypted data needed to access the payment.\n\n### Privacy considerations\n\nUmbra is not a mixer and does not use zero-knowledge proofs or an anonymity pool. The sender, amount, token, and fresh receiving address remain public. Privacy comes from hiding the identity of the person controlling that address.\n\nAlthough not enforced by the protocol, Umbra Cash users register their public keys on StealthKeyRegistry. On one hand, this allows stealth transfers between the sender and the recipient without exchanging any data offchain. On the other hand, stealth transfer recipients are very likely to be among the registered addresses, which reduces recipient anonymity set.",
"description": "A stealth-address payment protocol for an existing wallet: the user registers once, and anyone can pay them at a fresh address.",
"detailedDescription": "Umbra Cash is a stealth-address protocol that lets anyone pay the user at a fresh address, so the payment does not identify them as its recipient. The user registers once, and payers find their keys in a public registry. Payer, amount and token stay public, and so does where the funds go next.\n\nUsers trust ScopeLift, which hosts the app, runs its indexer and relays token withdrawals. The contracts are immutable, and their owner can only set a fee on new payments. Leaving needs no one: ETH lands at the stealth address itself, and any account can relay a token withdrawal.\n\n### Flow\n\n1. **Register.** The app derives a spending and a viewing key from a wallet signature and publishes the public keys in the StealthKeyRegistry.\n2. **Pay.** The payer's app derives a fresh stealth address from those keys and pays through the Umbra contract. ETH goes straight to the address, tokens wait in the contract. An event publishes the address with a secret only the viewing key decrypts.\n3. **Scan.** The recipient's app fetches every payment since registration and decrypts each locally.\n4. **Withdraw.** ETH leaves in a plain transfer that pays its own gas. Tokens leave through a relayer, which pays the gas for a fee against the recipient's signature.\n\n### App\n\nThe app at [app.umbra.cash](https://app.umbra.cash) is built from public source, while its indexer and relayer API are closed. It finds payments through the indexer, relays token withdrawals through the API and checks every address it handles with its mainnet RPC, Alchemy. The app warns before withdrawals to registered, named or otherwise known addresses. A local build can leave the indexer out and read the chain from the user's own node. It still asks Cloudflare for the ENS name of the user's wallet and POAP about each withdrawal address. The [privacy policy](https://app.umbra.cash/privacy) keeps IP logs for up to 90 days.\n\n### Compliance\n\nScreening runs in the app only. It refuses wallets, recipients and withdrawal addresses on its blocklist or Chainalysis's sanctions oracle, and hides payments from or to listed addresses.\n\n### Fees\n\nEach payment through the Umbra contract pays the owner's ETH toll, 0 ETH today. Token withdrawals through the relayer pay its fee in the withdrawn token, and ETH withdrawals pay only gas.",
"links": {
"websites": [
"https://app.umbra.cash"
],
"documentation": [
"https://app.umbra.cash/faq"
],
"repositories": [
"https://github.com/ScopeLift/umbra-protocol"
],
"socialMedia": [
"https://x.com/UmbraCash",
"https://discord.com/invite/uw4y5J2p7C"
],
"other": [
"https://diligence.security/audits/2021/03/umbra-smart-contracts/"
]
}
}
+50 -66
{
"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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "Announcements mark each stealth payment but not the recipient whose keys were used.",
"exposureContinued": "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.",
"exposureShort": "Each payment goes to a fresh stealth address, and only the recipient's viewing key shows whose it is.",
"exposureContinued": "The registry lists every recipient who could be behind it, with their key history.",
"advice": "Withdraw to a fresh address that is not registered and has no public link to the user.",
"sources": [
{
"contract": "Umbra",
"title": "Announcement, tokenPayments and TokenWithdrawal expose the fund path"
"title": "Announcement and TokenWithdrawal publish 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"
"title": "Matching decrypts the secret with the viewing key and checks the stealth address",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/umbra-js/src/classes/Umbra.ts#L707-L721"
}
],
"id": "publicObserver",
"value": "Recipient private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposureShort": "Protocol use is public once a payment is announced, which narrows the anonymity set to registered recipients.",
"exposureContinued": "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.",
"sentiment": "good",
"exposureShort": "Any of the 11,983 registered recipients could be behind a stealth address.",
"exposureContinued": "Registrations are rare and public, so a payment that lands minutes after one points to that registrant. Several payments withdrawn to one address share a recipient.",
"advice": "Register long before anyone pays the user, and withdraw each payment to its own address. Exit through a relayer for tokens, so none of the user's wallets pay gas for the receiving address.",
"sources": [
{
"title": "Sections 6–7: reuse heuristics, false positives and historical evaluation",
"url": "https://arxiv.org/html/2308.01703v2#S6"
"contract": "StealthKeyRegistry",
"title": "Each registration is public with its block"
},
{
"title": "Table 2: denominator is withdrawn payments; section 7.4: no fee matches",
"url": "https://arxiv.org/html/2308.01703v2#S7"
"title": "Kovács and Seres: reuse heuristics and their reach on mainnet (sections 6 and 7)",
"url": "https://arxiv.org/html/2308.01703v2#S6"
},
{
"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"
"title": "The app warns about registered, named and sender destinations, and lets the user proceed",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/utils/address.ts#L146-L241"
}
],
"id": "chainAnalyst",
"value": "Recipient at risk",
"value": "Recipient private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "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.",
"exposureContinued": "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.",
"exposureShort": "The hosted app checks the payee's address with its Alchemy RPC seconds before each payment, so timing ties the payee to the payment.",
"exposureContinued": "When the payee withdraws, the same RPC receives their wallet address and the withdrawal address in one session. The wallet's own RPC receives the wallet address together with the matched stealth addresses. On connect, the wallet library also sends the user's wallet address to Cloudflare's RPC to look up its ENS name.",
"advice": "Pay and withdraw from a local build with all RPC URLs and the user's wallet pointed at their own node, and block cloudflare-eth.com.",
"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": "Sending checks the recipient against a sanctions list through the app's mainnet RPC",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/umbra-js/src/classes/Umbra.ts#L219-L227"
},
{
"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": "Connecting checks the wallet address through the same RPC",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/store/wallet.ts#L273"
},
{
"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": "Withdrawing checks the destination through the same RPC",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/components/AccountReceiveTable.vue#L736"
},
{
"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"
"title": "Matched stealth addresses go to the wallet RPC in one balance call",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/components/AccountReceiveTable.vue#L606-L625"
}
],
"id": "networkObserver",
"value": "Recipient at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "good",
"exposureShort": "The contracts are immutable and give no administrator a viewing key or a way to replace registered keys without the registrant.",
"exposureContinued": "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.",
"sentiment": "warning",
"exposureShort": "The hosted app gives the operator the user's wallet address on every scan and withdrawal address in the same session.",
"exposureContinued": "Token withdrawals also hand its relayer the stealth address. The operator serves the app and can change it at any time. The contracts are immutable, and only the user holds their viewing key.",
"advice": "Run an inspected local build of the client instead of the hosted frontend. Leave its indexer URL empty, so it reads payments from the user's node.",
"sources": [
{
"contract": "Umbra",
"title": "Owner sets toll, tollCollector and tollReceiver; withdrawals require authorization"
"title": "Each scan first asks the indexer for the connected wallet's registration",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/umbra-js/src/utils/utils.ts#L336-L349"
},
{
"contract": "StealthKeyRegistry",
"title": "Direct registration or registrant-authorized signature, no admin override"
"title": "The registry read stores no block, so the lookup repeats on every scan",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/store/wallet.ts#L605-L615"
},
{
"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": "Every withdrawal sends the destination to the operator's API",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/utils/address.ts#L201-L214"
},
{
"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"
"title": "Token withdrawals send the stealth address to the relayer",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/frontend/src/components/AccountReceiveTable.vue#L767-L768"
}
],
"id": "privilegedInsider",
"value": "Recipient private",
"value": "Recipient at risk",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "Each announcement stores the ephemeral public key and the encrypted scalar.",
"exposureContinued": "Whoever 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.",
"exposureShort": "Whoever breaks elliptic-curve cryptography matches every past payment to its registered recipient from chain data alone.",
"exposureContinued": "The registry holds each recipient's public keys, and each announcement the payer's one-time key and the encrypted secret. Recovering the private keys decrypts the secret and confirms the stealth address.",
"sources": [
{
"contract": "Umbra",
"title": "Announcement archives pkx and ciphertext"
"title": "Announcement archives the one-time key 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"
"title": "A shared secret from elliptic-curve keys encrypts the secret",
"url": "https://github.com/ScopeLift/umbra-protocol/blob/947c4f3b9b89f6cf5d69b2d43d3cffec673da655/umbra-js/src/classes/KeyPair.ts#L121-L157"
}
],
"id": "futureAdversary",
"value": "Recipient exposed",
"alsoExposed": []
}
}
},
"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."
}
],
"category": {
"id": "stealthAddress",
"label": "Stealth address",
"description": "Payments land on fresh one-time addresses. Hides which recipient a payment is for."
},
"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.",
"description": "The contracts are immutable. The toll applies only to new payments, so recipients can always withdraw what they received.",
"walkawayTest": {
"passed": true
}
},
"reproducibility": {
"value": "Reproducible",
"sentiment": "good",
"description": "The immutable core contracts, cryptographic library, and frontend are published and can be built and run locally."
"description": "The contracts, the umbra-js library and the frontend are open source and build locally. The indexer and the relayer API behind the hosted app are closed."
},
"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.",
"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. the operator links the user's wallet to their stealth addresses, since the hosted app sends it both in one session (see privileged insider).\n2. Alchemy, the hosted app's RPC, receives the payee's address seconds before each payment, so timing ties the payee to it (see network observer).\n3. elliptic-curve cryptography is broken, which matches every past payment to its registered recipient (see future adversary).\n
\n## New payments can be stopped if\n1. the Umbra owner raises the toll on payments through the contract, which has no cap.",
"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"
}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"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."
"content": "The Umbra and StealthKeyRegistry contracts are immutable. The Umbra owner sets the ETH toll on every payment through the contract and the addresses that collect it, instantly and without a cap. A high toll stops new payments. ScopeLift changes the hosted app, its indexer and its relayer at will."
}
}
+3 -3
{
"censorshipResistance": {
"sentiment": "good",
"points": [
"The core contracts are immutable, cannot be paused, and have no upgrade path, so the exit window is infinite.",
"Passes the walkaway test: nobody can stop a recipient from claiming a payment already sent to them, and withdrawals can always be self-relayed.",
"Nobody can censor an individual user - the owner can only raise the ETH toll on contract-routed payments, which applies to everyone equally and cannot touch payment or withdrawal logic."
"The contracts censor nobody - the owner can only raise the ETH toll on contract-routed payments, which applies to everyone equally. The hosted app refuses sanctioned and blocklisted addresses."
],
"missing": [
"ERC-20 stealth transfers are currently relayed only by Umbra Cash relayer, privacy guarantees are weakened if it censors withdrawals."
]
},
"openSource": {
"sentiment": "good",
"license": "MIT",
"points": [
"The immutable core contracts, the cryptographic library and the frontend are published and can be built and run locally."
]
},
"privacy": {
"sentiment": "warning",
"points": [
"Recipient privacy with no protocol-level compliance mechanism, no privileged view key, and no way for anyone to retroactively deanonymize a payment."
"Recipient privacy with no protocol-level compliance mechanism and no privileged view key."
],
"missing": [
"Only who controls the receiving address is hidden - the sender, the amount and the stealth address stay public.",
"The number of registered stealth transfer recipients is not large enough to provide a high degree of anonymity."
"The hosted app sends the operator the user's wallet address and withdrawal addresses in one session."
]
},
"security": {
"sentiment": "good",
"points": [
"A small, immutable, externally audited contract set with no proof system and no trusted setup.",
"Umbra Cash main contract is well-ossified: it has been live for a long time without security incidents."
],
"missing": [
"Stealth addresses rely on elliptic-curve key derivation, so they are not quantum-resistant."
]
}
}
+17 -0
{
"ethereum": {
"roles": [],
"actors": [
{
"id": "EOA-1",
"name": "EOA 1",
"accounts": [
{
"address": "eth:0x5777Aa6F437399Af6cef2Fce0BE8D4B4eD7C7232",
"type": "EOA",
"isVerified": true,
"name": "0x5777…7232",
"url": "https://etherscan.io/address/0x5777Aa6F437399Af6cef2Fce0BE8D4B4eD7C7232",
"displayName": "EOA 1"
}
],
"chain": "ethereum",
"description": "* Can interact with Umbra\n * set an arbitrary ETH toll that must be paid on every transfer routed through the Umbra contract ",
"discoveryDrivenData": true
},
{
"id": "EOA-2",
"name": "EOA 2",
"accounts": [
{
"address": "eth:0xB7EE870E2c49B2DEEe70003519cF056247Aac3D4",
"type": "EOA",
"isVerified": true,
"name": "0xB7EE…c3D4",
"url": "https://etherscan.io/address/0xB7EE870E2c49B2DEEe70003519cF056247Aac3D4",
"displayName": "EOA 2"
}
],
"chain": "ethereum",
"description": "* Can interact with UmbraBatchSend\n * approve new tokens so that batch sends can route them to the Umbra contract ",
"discoveryDrivenData": true
}
]
}
}
+15 -0
{
"addresses": {
"ethereum": [
{
"name": "StealthKeyRegistry",
"isVerified": true,
"address": "eth:0x31fe56609C65Cd0C510E7125f051D440424D38f3",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Public registry that maps an Ethereum address to its two secp256k1 stealth public keys: a spending key used to derive a fresh stealth address, and a viewing key used to encrypt the transfer metadata for the recipient.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x31fe56609C65Cd0C510E7125f051D440424D38f3#code"
},
{
"name": "UmbraBatchSend",
"isVerified": true,
"address": "eth:0xDbD0f5EBAdA6632Dde7d47713ea200a7C2ff91EB",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Helper the app uses to send several stealth payments in one transaction. It pulls the tokens from the sender and calls the Umbra contract once per payment, so each payment emits its own Announcement. It holds no funds between transactions.\n* Roles:\n * **owner**: EOA 2",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xDbD0f5EBAdA6632Dde7d47713ea200a7C2ff91EB#code"
},
{
"name": "Umbra",
"isVerified": true,
"address": "eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Main entry point of the Umbra protocol, routing all ETH and ERC-20 stealth payments. On send, it emits an Announcement event that the recipient scans to detect the payment. ETH is forwarded directly to a fresh stealth address, ERC-20s are escrowed in this smart contract.\n* Roles:\n * **owner**: EOA 1",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xFb2dc580Eed955B528407b4d36FfaFe3da685401#code"
}
]
},
"risks": []
}
+1 -1
{
"baseTimestamp": 1786959877,
"baseTimestamp": 1791530581,
"contractsDiscoDriven": false,
"hasDiscoUi": false,
"isDiscoDriven": false,
"permissionsDiscoDriven": false
}
+14 -0
[
{
"id": "f01747ad",
"date": "Fri, 09 Oct 2026 07:24:21 GMT",
"timestamp": 1791530581,
"description": "Added UmbraBatchSend, which the app uses for batch payments. Moved Umbra and StealthKeyRegistry from config overrides into templates, with no value changes.",
"isHighSeverity": false,
"changeCount": 1,
"sections": [
{
"kind": "config-related-changes",
"body": "```diff\n+ Status: CREATED\n contract UmbraBatchSend (eth:0xDbD0f5EBAdA6632Dde7d47713ea200a7C2ff91EB) [umbra/UmbraBatchSend]\n +++ description: Helper the app uses to send several stealth payments in one transaction. It pulls the tokens from the sender and calls the Umbra contract once per payment, so each payment emits its own Announcement. It holds no funds between transactions.\n```"
}
]
},
{
"id": "73958e78",
"date": "Mon, 17 Aug 2026 09:46:02 GMT",
"timestamp": 1786959877,
"description": "7702 delegation.",
"isHighSeverity": true,
"changeCount": 3,
"sections": [
{
"kind": "watched-changes",
"body": "```diff\n EOA (eth:0xb4435399AB53D6136C9AEEBb77a0120620b117F9) {\n +++ description: None\n proxyType:\n- \"EOA\"\n+ \"EIP7702 EOA\"\n sourceHashes:\n+ [\"0x1f44812af62d28f019e30e8eb2af596fb36c7db9d34576972c0405e110a6ef45\"]\n values:\n+ {\"$implementation\":\"eth:0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B\",\"delegationManager\":\"eth:0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3\",\"DOMAIN_VERSION\":\"1\",\"eip712Domain\":{\"fields\":\"0x0f\",\"name\":\"EIP7702StatelessDeleGator\",\"version\":\"1\",\"chainId\":1,\"verifyingContract\":\"eth:0xb4435399AB53D6136C9AEEBb77a0120620b117F9\",\"salt\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"extensions\":[]},\"entryPoint\":\"eth:0x0000000071727De22E5E9d8BAf0edAc6f37da032\",\"getDeposit\":0,\"getDomainHash\":\"0x9962d46a78b1e6906da83f5bd75621a608112e6b851709d6504888d73d532e1c\",\"getNonce\":0,\"NAME\":\"EIP7702StatelessDeleGator\",\"PACKED_USER_OP_TYPEHASH\":\"0xbc37962d8bd1d319c95199bdfda6d3f92baa8903a61b32d5f4ec1f4b36a3bc18\",\"VERSION\":\"1.3.0\"}\n }\n```"
}
]
},
{
"id": "7c965433",
"date": "Mon, 03 Aug 2026 10:26:34 GMT",
"timestamp": 1785499067,
"description": "Initial discovery of Umbra contracts",
"isHighSeverity": false,
"changeCount": 2,
"sections": [
{
"kind": "initial-discovery",
"body": "```diff\n+ Status: CREATED\n contract StealthKeyRegistry (eth:0x31fe56609C65Cd0C510E7125f051D440424D38f3) [N/A]\n +++ description: Public registry that maps an Ethereum address to its two secp256k1 stealth public keys: a spending key used to derive a fresh stealth address, and a viewing key used to encrypt the transfer metadata for the recipient.\n```\n\n```diff\n+ Status: CREATED\n contract Umbra (eth:0xFb2dc580Eed955B528407b4d36FfaFe3da685401) [N/A]\n +++ description: Main entry point of the Umbra protocol, routing all ETH and ERC-20 stealth payments. On send, it emits an Announcement event that the recipient scans to detect the payment. ETH is forwarded directly to a fresh stealth address, ERC-20s are escrowed in this smart contract.\n```"
}
]
}
]
+40 -8
+2 -2
{
"badges": [],
"description": "Zama Confidential Tokens is an app that wraps ERC-20 assets into confidential tokens and hides balances and transfer amounts using Zama FHEVM on Ethereum.",
"detailedDescription": "Zama Confidential Tokens let users hold and send tokens on Ethereum with hidden balances and transfer amounts. A user wraps a token into its confidential version, such as USDC into cUSDC, pays others with it and unwraps it back. Who pays whom and every wrap and unwrap amount stay public.\n\nUsers trust the 13 operators of the key management service (KMS), who share the decryption key, Zama for the coprocessor, relayer and app, and two governance multisigs. Any 5 KMS operators together can decrypt every amount, and the multisigs can upgrade every contract instantly. Each unwrap needs a decryption signed by 7 of them, so funds leave only while the KMS and the coprocessor run.\n\n\n\n### Encryption\n\nThe tokens follow [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) and use fully homomorphic encryption (FHE), which lets contracts compute on encrypted amounts. One FHE key pair protects every balance and amount. Wallets encrypt with its public key, Zama's coprocessor computes on the ciphertexts without decrypting them, and the KMS operators hold the secret key in shares and decrypt on request.\n\nThe [KMS operators](https://docs.zama.org/protocol/protocol-apps/addresses/mainnet/ethereum#operator-staking) are Zama, Dfns, Figment, Fireblocks, InfStones, Unit410, LayerZero, Ledger, Omakase, Stake Capital, OpenZeppelin, Etherscan and Conduit. Each holds two keys: a share of the FHE secret key, which decrypts, and an ECDSA key, which signs the result for Ethereum. They decrypt an amount only for accounts that the ACL, an onchain access list, allows them to read. [Zama states](https://docs.zama.org/protocol/protocol/overview/kms) that they run in AWS Nitro enclaves, and Ethereum checks only their signatures. Governance multisigs manage signer sets in ProtocolConfig: they can add sets, retune their thresholds and destroy old ones. Removing an operator onchain leaves its key share intact.\n\nZama's coprocessor runs the FHE computation offchain and checks every encrypted input. Ethereum accepts an input signed by 1 of 1 coprocessor signers, so the coprocessor is trusted for validity. The Gateway requires 3 matching responses from 5 registered coprocessor senders for ACL updates. Priority mode is on: while on, one coprocessor finalizes inputs and ciphertext commits alone, whatever the signer set. It stores all ciphertexts in a [public bucket](https://coprocessor-1.mainnet.zama.org/). The Zama Gateway, an L3 on Arbitrum, relays decryption requests and publishes who reads which balance and when. Ethereum verifies the KMS signatures itself, while inputs and unwraps depend on the Gateway running.\n\n### Privacy considerations\n\nThe privacy ratings assume [Zama's app](https://app.zama.org), the official interface. Its source is unpublished. Apps reach the protocol through a relayer, and Zama's relayer [requires an API key](https://docs.zama.org/protocol/sdk/guides/relayer-api-keys) on mainnet. It receives each encrypted input and balance read together with the user's address and IP and the token. Practical privacy also depends on the timing and amounts of wraps and unwraps, see [OPSEC best practice](/publications/privacy-best-practices).\n\n### Compliance\n\nEach token's owner, controlled by the multisigs, can block users and check them against the underlying token's denylist, as cUSDC does with USDC's blacklist. It can also appoint a pauser that halts the token, and observers that decrypt all of its balances and amounts. Today no token has a pauser or an observer. Issuers of underlying tokens like USDC can freeze a token's whole escrow.\n\n### Fees\n\nUsers pay Ethereum gas. The Gateway charges 1 ZAMA per encrypted input and 0.1 ZAMA per decryption, paid by the relayer. Zama's relayer bills apps monthly.",
"description": "Confidential amounts for ERC-20 tokens on Ethereum: wrappers that encrypt balances and transfer amounts with FHE, decrypted by a committee of KMS operators.",
"detailedDescription": "Zama Confidential Tokens let users hold and send tokens on Ethereum with hidden balances and transfer amounts. A user wraps a token into its confidential version, such as USDC into cUSDC, pays others with it and unwraps it back. Who pays whom and every wrap and unwrap amount stay public.\n\nUsers trust the 13 operators of the key management service (KMS), who share the decryption key. They also trust Zama for the coprocessor, relayer and app, and two governance multisigs. Any 5 KMS operators together can decrypt every amount, and the multisigs can upgrade every contract instantly. Each unwrap needs a decryption signed by 7 of them, so funds leave only while the KMS and the coprocessor run.\n\n\n\n### Architecture\n\nThe tokens follow [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) and use fully homomorphic encryption (FHE), which lets contracts compute on encrypted amounts. One FHE key pair protects every balance and amount. Wallets encrypt with its public key, and Zama's coprocessor computes on the ciphertexts without decrypting them. The KMS operators hold the secret key in shares and decrypt on request.\n\n### Flow\n\n1. **Wrap.** You deposit a token into its confidential wrapper, such as USDC into cUSDC. Your address and the amount are public.\n2. **Transfer.** Your wallet encrypts the amount, and the coprocessor updates both balances without decrypting them. Sender and recipient are public.\n3. **Unwrap.** The wrapper asks the KMS to decrypt the amount, and the tokens are paid out once enough operators sign it. The amount is public.\n\n### App\n\nThe ratings assume [Zama's app](https://app.zama.org), the official interface. Its source is unpublished. Apps reach the protocol through a relayer, and Zama's relayer [requires an API key](https://docs.zama.org/protocol/sdk/guides/relayer-api-keys) on mainnet. It receives each encrypted input and balance read together with the user's address and IP and the token.\n\n### KMS\n\nThe [KMS operators](https://docs.zama.org/protocol/protocol-apps/addresses/mainnet/ethereum#operator-staking) are Zama, Dfns, Figment, Fireblocks, InfStones, Unit410, LayerZero, Ledger, Omakase, Stake Capital, OpenZeppelin, Etherscan and Conduit. Each holds two keys: a share of the FHE secret key, which decrypts, and an ECDSA key, which signs the result for Ethereum. They decrypt an amount only for accounts that the ACL, an onchain access list, allows them to read. [Zama states](https://docs.zama.org/protocol/protocol/overview/kms) that they run in AWS Nitro enclaves, and Ethereum checks only their signatures. Governance multisigs manage signer sets in ProtocolConfig: they can add sets, retune their thresholds and destroy old ones. Removing an operator onchain leaves its key share intact.\n\n### Coprocessor\n\nZama's coprocessor runs the FHE computation offchain and checks every encrypted input. Ethereum accepts an input signed by 1 of 1 coprocessor signers, so the coprocessor is trusted for validity. The Gateway requires 3 matching responses from 5 registered coprocessor senders for ACL updates. Priority mode is on: while on, one coprocessor finalizes inputs and ciphertext commits alone, whatever the signer set. It stores all ciphertexts in a [public bucket](https://coprocessor-1.mainnet.zama.org/). The Zama Gateway, an L3 on Arbitrum, relays decryption requests and publishes who reads which balance and when. Ethereum verifies the KMS signatures itself, while inputs and unwraps depend on the Gateway running.\n\n### Compliance\n\nEach token's owner, controlled by the multisigs, can block users and check them against the underlying token's denylist, as cUSDC does with USDC's blacklist. It can also appoint a pauser that halts the token, and observers that decrypt all of its balances and amounts. Today no token has a pauser or an observer. Issuers of underlying tokens like USDC can freeze a token's whole escrow.\n\n### Fees\n\nUsers pay Ethereum gas. The Gateway charges 1 ZAMA per encrypted input and 0.1 ZAMA per decryption, paid by the relayer. Zama's relayer bills apps monthly.",
"links": {
"websites": [
"https://www.zama.org"
],
"documentation": [
"https://docs.zama.org/protocol"
],
"repositories": [
"https://github.com/zama-ai"
]
}
}
+23 -1
null
[
{
"title": "Zama Protocol live on Ethereum",
"url": "https://cryptoast.fr/?p=199633",
"date": "2025-12-30T00:00:00Z",
"description": "Zama launches its mainnet with a first confidential stablecoin transfer, in cUSDT.",
"type": "general"
},
{
"title": "ACL paused, every token halted",
"url": "https://etherscan.io/tx/0x1fe5bf2c4c59b4c0034167e8eea979e3a8fa700d590c73ca7587712bb43359b2",
"date": "2026-01-06T00:00:00Z",
"description": "A pauser halts the ACL, and with it every confidential token, for 84 minutes.",
"type": "incident"
},
{
"title": "Circle freezes the cUSDC escrow",
"url": "https://www.zama.org/post/the-freeze-on-zamas-cusdc-contract-has-been-lifted-and-all-systems-are-back-to-normal",
"date": "2026-05-30T00:00:00Z",
"description": "A court order makes Circle blacklist the cUSDC wrapper for almost three days, blocking every unwrap.",
"type": "incident"
}
]
+15 -5
{
"adversaries": {
"promise": {
"protects": "amount",
"text": "Hides balances and transfer amounts. Senders, recipients and every wrap and unwrap amount 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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "warning",
"exposureShort": "Wraps and unwraps publish their amounts, while confidential transfers keep theirs encrypted.",
"exposureContinued": "Either party to a transfer can disclose its amount onchain at any time.",
"advice": "Pay and get paid inside the confidential token, and unwrap only what you need.",
"advice": "Pay and get paid inside the confidential token, and unwrap only what the user needs.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "private",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Wrap event carries the clear amount",
"url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/23ba15402346027f2416667acbb1e741179f8485/contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol#L256"
},
{
"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 disclose a transfer amount",
"url": "https://github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/23ba15402346027f2416667acbb1e741179f8485/contracts/token/ERC7984/ERC7984.sol#L202-L209"
}
],
"id": "publicObserver",
"value": "Amounts at risk",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposureShort": "An account's balance is bounded by its public wraps and unwraps, and exact if it never took part in a confidential transfer.",
"exposureContinued": "Transfer partners and timing are public, so a wrap, a transfer and an unwrap in a row pair up by amount. In October 2026, 95% of unwraps could be linked to a single wrap.",
"advice": "Transfer often, even zero amounts, and unwrap only after a while. Withdraw common amounts rather than everything at once.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Hidden only within an account's confidential transfers."
},
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "ERC-7984: public sender, recipient and encrypted amount per transfer",
"url": "https://eips.ethereum.org/EIPS/eip-7984"
},
{
"title": "Explorer linking unwraps to their wraps, searchable by address, handle and transaction",
"url": "https://sekuba.github.io/zama-graph/"
}
],
"id": "chainAnalyst",
"value": "Amounts at risk",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "good",
"exposureShort": "Amounts leave your device only as FHE ciphertext and come back encrypted to a key only your device holds.",
"sentiment": "warning",
"exposureShort": "Zama's app is closed source, so what it sends to third parties is up to Zama and can change at any time.",
"exposureContinued": "Through Zama's open SDK, amounts leave the user's device only as FHE ciphertext and return encrypted to a key held only by that device. Today the app sends the user's address, chain reads and decryption requests to Zama's own servers, which forward chain reads to an RPC provider Zama does not name. Analytics wait for the user's consent.",
"advice": "Use a client built on Zama's open SDK.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "private",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Inputs are encrypted on the device",
"url": "https://github.com/zama-ai/relayer-sdk/blob/d06f1e585a78181135cb602109e5fa3da523b48d/src/relayer/sendEncryption.ts#L133-L146"
},
{
"title": "Balance reads use a device ML-KEM key pair",
"url": "https://github.com/zama-ai/relayer-sdk/blob/d06f1e585a78181135cb602109e5fa3da523b48d/src/relayer/userDecrypt.ts#L85-L89"
},
{
"title": "The app's content security policy lists every endpoint it contacts",
"url": "https://app.zama.org"
},
{
"title": "The SDK the app bundles (@zama-fhe/sdk 3.6.0) is open source",
"url": "https://github.com/zama-ai/sdk"
}
],
"id": "networkObserver",
"value": "Amounts private",
"value": "Amounts at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "Any 5 of the 13 KMS operators can combine their key shares and decrypt every balance and transfer ever made.",
"exposureContinued": "The token owner can instantly appoint an observer that sees all amounts forever, or upgrade the token and the ACL. Zama serves its closed-source app, so it can read amounts before they are encrypted.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "KMS parties and key-share threshold",
"url": "https://github.com/zama-ai/kms/blob/b5cd5cf465dc488ff04ac8c19f3ef12877abffa0/ai-docs/ARCHITECTURE.md#L22-L25"
},
{
"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": "Zama's official app",
"url": "https://docs.zama.org/protocol/protocol-apps/apps"
}
],
"id": "privilegedInsider",
"value": "Amounts exposed",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "One long-lived lattice key protects every balance and transfer amount, and anyone can download the ciphertexts today.",
"exposureContinued": "Balance reads also publish decryption shares on the Zama Gateway chain, encrypted to each user with ML-KEM, also lattice-based. Whoever breaks lattice cryptography, or later obtains enough KMS key shares, decrypts the entire history.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Coprocessor ciphertext store (public listing)",
"url": "https://coprocessor-1.mainnet.zama.org/"
},
{
"title": "Gateway publishes encrypted decryption shares",
"url": "https://github.com/zama-ai/fhevm/blob/94f1f3b35c71175dc68bb36887e0525f7c5d80c5/gateway-contracts/contracts/Decryption.sol#L808-L817"
},
{
"title": "ML-KEM-512 encryption of decryption shares",
"url": "https://github.com/zama-ai/kms/blob/b5cd5cf465dc488ff04ac8c19f3ef12877abffa0/core/service/src/cryptography/hybrid_ml_kem.rs"
}
],
"id": "futureAdversary",
"value": "Amounts exposed",
"alsoExposed": []
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "Only amounts are hidden. Every transfer names its sender and recipient."
},
"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 vaults from within the confidential token."
}
],
"category": {
"id": "confidentialAmounts",
"label": "Confidential amounts",
"description": "Accounts and counterparties are public, balances and transfer amounts are encrypted."
},
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The governance multisigs upgrade the contracts instantly, before users can leave.",
"walkawayTest": {
"passed": false,
"reason": "Every unwrap needs a decryption signed by 7 of 13 KMS operators, and every input needs Zama's coprocessor."
}
},
"quantumResistant": true,
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The contracts, the SDK and the KMS and coprocessor sources are public. Zama's app source is unpublished."
},
"riskSummary": "## Funds can be stolen if\n1. either governance multisig passes a malicious upgrade of the confidential tokens or Zama's protocol contracts, which takes effect instantly.\n2. the current or any retained KMS signer set, which either multisig can create or retune instantly, attests an inflated unwrap amount.\n3. Zama's coprocessor (1/1) accepts invalid encrypted inputs that bypass balance checks.\n
\n## Funds can be lost if\n1. an underlying token issuer blacklists a confidential token's address.\n2. the token owner or an underlying denylist blocks a user before they unwrap.\n3. an appointed pauser halts a token and the owner keeps it paused.\n4. the KMS or Zama's coprocessor stops or loses ciphertext data, which halts every unwrap.\n
\n## Privacy can be lost if\n1. 5 of 13 KMS operators combine their key shares (see privileged insider).\n2. the token owner appoints an observer or upgrades the token (see privileged insider).\n3. Zama changes its closed-source app to leak amounts (see privileged insider).\n4. public wraps and unwraps bound a user's balance (see chain analyst).\n5. lattice cryptography is broken or old KMS key shares leak, which decrypts every published ciphertext (see future adversary).",
"riskSummary": "## Funds can be stolen if\n1. either governance multisig passes a malicious upgrade of the confidential tokens or Zama's protocol contracts, which takes effect instantly.\n2. the current or any retained KMS signer set, which either multisig can create or retune instantly, attests an inflated unwrap amount.\n3. Zama's coprocessor (1/1) accepts invalid encrypted inputs that bypass balance checks.\n
\n## Funds can be lost if\n1. an underlying token issuer blacklists a confidential token's address.\n2. the token owner or an underlying denylist blocks a user before they unwrap.\n3. an appointed pauser halts a token and the owner keeps it paused.\n4. the KMS or Zama's coprocessor stops or loses ciphertext data, which halts every unwrap.\n
\n## Privacy can be lost if\n1. 5 of 13 KMS operators combine their key shares (see privileged insider).\n2. the token owner appoints an observer or upgrades the token (see privileged insider).\n3. Zama changes its closed-source app to leak amounts to itself or to third parties (see privileged insider and network observer).\n4. public wraps and unwraps bound a user's balance (see chain analyst).\n5. lattice cryptography is broken or old KMS key shares leak, which decrypts every published ciphertext (see future adversary).",
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xe978F22157048E5DB8E5d07971376e86671672B2"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xe978F22157048E5DB8E5d07971376e86671672B2"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xAe0207C757Aa2B4019Ad96edD0092ddc63EF0c50"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xAe0207C757Aa2B4019Ad96edD0092ddc63EF0c50"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xda9396b82634Ea99243cE51258B6A5Ae512D4893"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xda9396b82634Ea99243cE51258B6A5Ae512D4893"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x80CB147Fd86dC6dEe3Eee7e4Cee33d1397d98071"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x80CB147Fd86dC6dEe3Eee7e4Cee33d1397d98071"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xa873750ccBafD5ec7Dd13bfD5237d7129832eDD9"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xa873750ccBafD5ec7Dd13bfD5237d7129832eDD9"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x73cc9aF9d6BEFdb3c3fAf8a5E8c05Cb95FdaEEf1"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x73cc9aF9d6BEFdb3c3fAf8a5E8c05Cb95FdaEEf1"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xBA4cFF6ED6F7Cb2A58776dECa4E984b498446762"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xBA4cFF6ED6F7Cb2A58776dECa4E984b498446762"
}
}
}
]
},
{
"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": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x66Bf74E96900D1a19c7070D939D124f2F565C458"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x66Bf74E96900D1a19c7070D939D124f2F565C458"
}
}
}
]
},
{
"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": 1789148087
},
"buckets": [
{
"id": "zama-cw-cWBTC",
"type": "pool",
"label": "cWBTC token",
"address": "eth:0xd86aE89C089B380f064Fe01899B4071F1eb6Bb3d",
"sinceTimestamp": 1789148087,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xd86aE89C089B380f064Fe01899B4071F1eb6Bb3d"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xd86aE89C089B380f064Fe01899B4071F1eb6Bb3d"
}
}
}
]
},
{
"token": {
"address": "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
"iconUrl": "https://coin-images.coingecko.com/coins/images/39284/large/AUSD_1024px.png?1764684132",
"symbol": "AUSD",
"decimals": 6,
"priceId": "agora-dollar",
"sinceTimestamp": 1789146719
},
"buckets": [
{
"id": "zama-cw-cAUSD",
"type": "pool",
"label": "cAUSD token",
"address": "eth:0xEa9A15A29DD180c40442142CaeEfC491d37bfDB2",
"sinceTimestamp": 1789146719,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xEa9A15A29DD180c40442142CaeEfC491d37bfDB2"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xEa9A15A29DD180c40442142CaeEfC491d37bfDB2"
}
}
}
]
},
{
"token": {
"address": "0x808507121B80c02388fAd14726482e061B8da827",
"iconUrl": "https://assets.coingecko.com/coins/images/15069/large/Pendle_Logo_Normal-03.png?1696514728",
"symbol": "PENDLE",
"decimals": 18,
"priceId": "pendle",
"sinceTimestamp": 1789150991
},
"buckets": [
{
"id": "zama-cw-cPENDLE",
"type": "pool",
"label": "cPENDLE token",
"address": "eth:0x8900123F7e17709C1D80eda1E611A9503Cf1B3df",
"sinceTimestamp": 1789150991,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x8900123F7e17709C1D80eda1E611A9503Cf1B3df"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x8900123F7e17709C1D80eda1E611A9503Cf1B3df"
}
}
}
]
},
{
"token": {
"address": "0xbeef003C68896c7D2c3c60d363e8d71a49Ab2bf9",
"iconUrl": "https://coin-images.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "steakUSDT",
"decimals": 18,
"priceId": "tether",
"sinceTimestamp": 1789152239
},
"buckets": [
{
"id": "zama-cw-csteakUSDT",
"type": "pool",
"label": "csteakUSDT token",
"address": "eth:0x1F6A937119c0c34Db0a2c4F000386a3BfD4A9e73",
"sinceTimestamp": 1789152239,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x1F6A937119c0c34Db0a2c4F000386a3BfD4A9e73"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x1F6A937119c0c34Db0a2c4F000386a3BfD4A9e73"
}
}
}
]
},
{
"token": {
"address": "0xbeeff07d991C04CD640DE9F15C08ba59c4FEDEb7",
"iconUrl": "https://coin-images.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "bbqUSDT",
"decimals": 18,
"priceId": "tether",
"sinceTimestamp": 1789152695
},
"buckets": [
{
"id": "zama-cw-cbbqUSDT",
"type": "pool",
"label": "cbbqUSDT token",
"address": "eth:0x80Ae7dbCe3c1696d3eeD04Ec8348c3C432322273",
"sinceTimestamp": 1789152695,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x80Ae7dbCe3c1696d3eeD04Ec8348c3C432322273"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x80Ae7dbCe3c1696d3eeD04Ec8348c3C432322273"
}
}
}
]
},
{
"token": {
"address": "0xbeeff2C5bF38f90e3482a8b19F12E5a6D2FCa757",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "bbqUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1789153067
},
"buckets": [
{
"id": "zama-cw-cbbqUSDC",
"type": "pool",
"label": "cbbqUSDC token",
"address": "eth:0x501b014f86a73A7199E00cB31C0e9565Ac51e51B",
"sinceTimestamp": 1789153067,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x501b014f86a73A7199E00cB31C0e9565Ac51e51B"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x501b014f86a73A7199E00cB31C0e9565Ac51e51B"
}
}
}
]
},
{
"token": {
"address": "0x3d5EcCb62974178236A768A7ef14D3ce468Fbe91",
"iconUrl": "https://coin-images.coingecko.com/coins/images/7598/large/WBTCLOGO.png?1764496367",
"symbol": "armcWBTC",
"decimals": 18,
"priceId": "wrapped-bitcoin",
"sinceTimestamp": 1789148987
},
"buckets": [
{
"id": "zama-cw-carmcWBTC",
"type": "pool",
"label": "carmcWBTC token",
"address": "eth:0xF921A5B0634f4236B21A4e948317abf9a2E86B6f",
"sinceTimestamp": 1789148987,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xF921A5B0634f4236B21A4e948317abf9a2E86B6f"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xF921A5B0634f4236B21A4e948317abf9a2E86B6f"
}
}
}
]
},
{
"token": {
"address": "0x500aE64100D7DbDb640531085C2F5d40cDC8930D",
"iconUrl": "https://coin-images.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "armUSDTs",
"decimals": 18,
"priceId": "tether",
"sinceTimestamp": 1789150187
},
"buckets": [
{
"id": "zama-cw-carmUSDTs",
"type": "pool",
"label": "carmUSDTs token",
"address": "eth:0xbaC6DeDf2C10D6D381F9DBd9EFd69D93FfCef50B",
"sinceTimestamp": 1789150187,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xbaC6DeDf2C10D6D381F9DBd9EFd69D93FfCef50B"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xbaC6DeDf2C10D6D381F9DBd9EFd69D93FfCef50B"
}
}
}
]
},
{
"token": {
"address": "0xA2EAaD0D586cF9FD73bb2c09cF6A7E3e187D68cd",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "armUSDCs",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1789151411
},
"buckets": [
{
"id": "zama-cw-carmUSDCs",
"type": "pool",
"label": "carmUSDCs token",
"address": "eth:0x77F8207ee451441336538bA07a2F425780dED8DB",
"sinceTimestamp": 1789151411,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x77F8207ee451441336538bA07a2F425780dED8DB"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x77F8207ee451441336538bA07a2F425780dED8DB"
}
}
}
]
},
{
"token": {
"address": "0xBf29043164660C60A2a72Cd15FFe2304e87B6838",
"iconUrl": "https://coin-images.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "armUSDTp",
"decimals": 18,
"priceId": "tether",
"sinceTimestamp": 1789153331
},
"buckets": [
{
"id": "zama-cw-carmUSDTp",
"type": "pool",
"label": "carmUSDTp token",
"address": "eth:0x63a60aCC304b84eF829b263f28e7b636Bf77d45A",
"sinceTimestamp": 1789153331,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x63a60aCC304b84eF829b263f28e7b636Bf77d45A"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x63a60aCC304b84eF829b263f28e7b636Bf77d45A"
}
}
}
]
},
{
"token": {
"address": "0x5dc53a23AdC9f2Bed98de6F59F7F309a7c71FF2B",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "armUSDCp",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1789150751
},
"buckets": [
{
"id": "zama-cw-carmUSDCp",
"type": "pool",
"label": "carmUSDCp token",
"address": "eth:0x2ebBd099f66b0ca94DD82B4c06fe3eEf6AFf853c",
"sinceTimestamp": 1789150751,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x2ebBd099f66b0ca94DD82B4c06fe3eEf6AFf853c"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x2ebBd099f66b0ca94DD82B4c06fe3eEf6AFf853c"
}
}
}
]
},
{
"token": {
"address": "0x55C1B6e461a6334B567bAF0FEb5D728715446f05",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "pendleUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1789150499
},
"buckets": [
{
"id": "zama-cw-cpendleUSDC",
"type": "pool",
"label": "cpendleUSDC token",
"address": "eth:0xb6e5299fc56053DD5440104Fd6Fd2A3CD064edbC",
"sinceTimestamp": 1789150499,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xb6e5299fc56053DD5440104Fd6Fd2A3CD064edbC"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xb6e5299fc56053DD5440104Fd6Fd2A3CD064edbC"
}
}
}
]
},
{
"token": {
"address": "0x32401B9fb79065Bc15949DE0BD43927492f02F0C",
"iconUrl": "https://coin-images.coingecko.com/coins/images/39284/large/AUSD_1024px.png?1764684132",
"symbol": "fAUSDe",
"decimals": 18,
"priceId": "agora-dollar",
"sinceTimestamp": 1789148387
},
"buckets": [
{
"id": "zama-cw-cfAUSDe",
"type": "pool",
"label": "cfAUSDe token",
"address": "eth:0x76A3f985315aAc9789Cb5BF8A5bb83BFF55FD5a6",
"sinceTimestamp": 1789148387,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x76A3f985315aAc9789Cb5BF8A5bb83BFF55FD5a6"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x76A3f985315aAc9789Cb5BF8A5bb83BFF55FD5a6"
}
}
}
]
},
{
"token": {
"address": "0xb48C056C5608bA2Ee4cD94AF2bF4b1F25295Bd7e",
"iconUrl": "https://coin-images.coingecko.com/coins/images/325/large/Tether.png?1696501661",
"symbol": "fcUSDT",
"decimals": 18,
"priceId": "tether",
"sinceTimestamp": 1789388567
},
"buckets": [
{
"id": "zama-cw-cfcUSDT",
"type": "pool",
"label": "cfcUSDT token",
"address": "eth:0xA28A369Df348ac232E4DAF7B0Ef90280d8707D7F",
"sinceTimestamp": 1789388567,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xA28A369Df348ac232E4DAF7B0Ef90280d8707D7F"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xA28A369Df348ac232E4DAF7B0Ef90280d8707D7F"
}
}
}
]
},
{
"token": {
"address": "0xc207d3f66537D7F66456808379A0E560cF20Da36",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "roxcUSDC",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1789153955
},
"buckets": [
{
"id": "zama-cw-croxcUSDC",
"type": "pool",
"label": "croxcUSDC token",
"address": "eth:0xB1B18Ad1F16D744c470cb6B09Cb826AfFcc9EB30",
"sinceTimestamp": 1789153955,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0xB1B18Ad1F16D744c470cb6B09Cb826AfFcc9EB30"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0xB1B18Ad1F16D744c470cb6B09Cb826AfFcc9EB30"
}
}
}
]
},
{
"token": {
"address": "0xE0181090c22579B6A217f1522cbf8c9f1F0C1965",
"iconUrl": "https://coin-images.coingecko.com/coins/images/6319/large/USDC.png?1769615602",
"symbol": "roxUSDCy",
"decimals": 18,
"priceId": "usd-coin",
"sinceTimestamp": 1789154375
},
"buckets": [
{
"id": "zama-cw-croxUSDCy",
"type": "pool",
"label": "croxUSDCy token",
"address": "eth:0x4FCCF8046FB7FB7AB76321BD39C27453e396B88f",
"sinceTimestamp": 1789154375,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x4FCCF8046FB7FB7AB76321BD39C27453e396B88f"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x4FCCF8046FB7FB7AB76321BD39C27453e396B88f"
}
}
}
]
},
{
"token": {
"address": "0xB344e331A3cDa61D329fb3Cca2Be5942da87c418",
"iconUrl": "https://coin-images.coingecko.com/coins/images/39284/large/AUSD_1024px.png?1764684132",
"symbol": "PAPY",
"decimals": 18,
"priceId": "agora-dollar",
"sinceTimestamp": 1789151975
},
"buckets": [
{
"id": "zama-cw-cPAPY",
"type": "pool",
"label": "cPAPY token",
"address": "eth:0x9ABb8f3dC2863B1F108DeD7ea41902fEF6732A8c",
"sinceTimestamp": 1789151975,
"deposit": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"to": "0x9ABb8f3dC2863B1F108DeD7ea41902fEF6732A8c"
}
},
"withdrawal": {
"event": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"extractor": "erc20Transfer",
"params": {
"from": "0x9ABb8f3dC2863B1F108DeD7ea41902fEF6732A8c"
}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "ZamaGovMultisigA (9/17) and ZamaGovMultisigB (3/6) each control, alone and instantly, the Aragon contract that owns the confidential tokens and Zama's protocol contracts on Ethereum. Either multisig can upgrade these contracts, change every compliance setting, manage KMS signer sets and trigger FHE key generation.\n\nSafeL2 (3/5) owns the Gateway contracts and can change coprocessors and KMS settings there or disconnect Ethereum. Any of 17 pausers can pause the ACL, and any of 17 the Gateway. Either halts every token until its owner unpauses."
}
}
+9 -9
+2 -2
{
"badges": [],
"description": "A round trip from Ethereum into shielded ZEC and back through NEAR Intents, so that Zcash's shielded pool serves as the privacy pool.",
"detailedDescription": "Zodl, a Zcash wallet, lets users move funds from one Ethereum address to a new one through Zcash's shielded pool, which hides the path. Its swap screen uses NEAR Intents to turn Ethereum assets into shielded ZEC and, later, ZEC back into Ethereum assets. Both Ethereum transfers and every swap on NEAR are public.\n\nUsers trust the NEAR Intents operator, which holds the Ethereum funds in a custodial bridge, screens every swap and can freeze balances. Two NEAR multisigs upgrade the contracts instantly, and NEAR's MPC network signs every Zcash payout. Leaving needs the operator, which runs the Ethereum side of every swap.\n\n### Flow\n\n1. **Ethereum to NEAR Intents.** Each quote from the operator's 1Click API comes with a fresh Ethereum deposit address. The user sends ETH or an ERC-20 there. The operator's custodial bridge, named Proof-of-Authority (PoA), sweeps the funds into its treasury [`0x2CfF…2680`](https://etherscan.io/address/0x2CfF890f0378a11913B6129B2E97417a2c302680). It then mints a wrapped token on NEAR whose memo names the Ethereum transaction hash.\n2. **Swap to ZEC.** Market makers quote offchain through a relay the operator runs. The winning quote settles in the Verifier contract [`intents.near`](https://nearblocks.io/address/intents.near), whose balances and swaps are public NEAR state.\n3. **Withdraw into Zcash.** The wrapped ZEC is burned through the Zcash connector `zcash-connector.bridge.near`, which builds a transaction from bridge funds and has NEAR's MPC network sign it. The payout goes to a shielded address that Zodl creates for every swap, and the burn message publishes it.\n4. **Inside Zcash.** The ZEC sits in the shielded pool, where transfers hide sender, recipient and amount.\n5. **Back to Ethereum.** Zodl pays ZEC from the pool to a fresh transparent deposit address of the connector. A relayer proves the deposit to the Zcash light client `zcash-client.bridge.near`, and the wrapped ZEC is minted and swapped. The operator then pays the Ethereum address from its treasury.\n\n### Privacy considerations\n\nEvery amount that enters or leaves the shielded pool is public. The connector requires each payout to be encrypted with an all-zero outgoing viewing key so that it can check the payout. Anyone can therefore decrypt the address and amount of every payout, and a round trip of similar size within a short time pairs up.\n\nThe ratings assume [Zodl](https://zodl.com), formerly Zashi, with its built-in Tor on. Zodl syncs from public lightwalletd servers, which serve the Zcash chain to light wallets, zec.rocks by default. Tor carries swap requests, payout fetches and transactions, each on its own circuit. Block sync and lookups of the account's transparent addresses stay direct, so the server can tie the isolated requests to the account by timing. Other wallets may leak more: the NEAR Intents web app pays only transparent Zcash addresses, which adds a public shielding step.\n\n### Custody\n\nThe Ethereum leg is custodial. The operator's deposit and treasury addresses are EOAs, and the operator mints the wrapped tokens on NEAR. The Verifier lets the NEAR Intents multisig and one single-key account freeze any account, which the operator uses for compliance holds. On Zcash, the connector holds funds at MPC-derived addresses and checks deposits against the light client. Relayers post Zcash block headers, credit deposits and request withdrawals. Anyone can become one by staking 1,000 NEAR and waiting seven days, unless the Rainbow Bridge multisig rejects them. The roles are listed under [upgrades and governance](#upgrades-and-governance).\n\n### Fees\n\nThe Verifier charges 1 pip (0.0001%) per swap. Zodl adds a 0.67% app fee to each quote, which it [shares with the 1Click API](https://docs.near-intents.org/resources/fees). Each leg also pays a bridge withdrawal fee, 0.000035 ETH on Ethereum and 0.00047 ZEC on Zcash on 2026-10-08.\n\n### Compliance\n\nIntents Technology Ltd. (British Virgin Islands) screens every quote against the NEAR Intents AML portal, Binance AML, AMLBot, PureFi and TRM Labs. Its [terms](https://docs.near-intents.org/security-compliance/terms-of-service) let it delay, block or freeze bridged funds and collect IP and wallet addresses. Market makers pass KYB, end users swap without an account.",
"description": "A shielded ledger round trip: Zodl swaps Ethereum funds into shielded ZEC and back through NEAR Intents, which custodies the Ethereum side.",
"detailedDescription": "Zodl, a Zcash wallet, lets users move funds from one Ethereum address to a new one through Zcash's shielded pool, which hides the path. Its swap screen uses NEAR Intents to turn Ethereum assets into shielded ZEC and, later, ZEC back into Ethereum assets. Both Ethereum transfers and every swap on NEAR are public.\n\nUsers trust the NEAR Intents operator, which holds the Ethereum funds in a custodial bridge, screens every swap and can freeze balances. Two NEAR multisigs upgrade the contracts instantly, and NEAR's MPC network signs every Zcash payout. Leaving needs the operator, which runs the Ethereum side of every swap.\n\n### Flow\n\n1. **Ethereum to NEAR Intents.** Each quote from the operator's 1Click API comes with a fresh Ethereum deposit address. The user sends ETH or an ERC-20 there. The operator's custodial bridge, named Proof-of-Authority (PoA), sweeps the funds into its treasury [`0x2CfF…2680`](https://etherscan.io/address/0x2CfF890f0378a11913B6129B2E97417a2c302680). It then mints a wrapped token on NEAR whose memo names the Ethereum transaction hash.\n2. **Swap to ZEC.** Market makers quote offchain through a relay the operator runs. The winning quote settles in the Verifier contract [`intents.near`](https://nearblocks.io/address/intents.near), whose balances and swaps are public NEAR state.\n3. **Withdraw into Zcash.** The wrapped ZEC is burned through the Zcash connector `zcash-connector.bridge.near`. The connector builds a transaction from bridge funds and has NEAR's MPC network sign it. The payout goes to a shielded address that Zodl creates for every swap, and the burn message publishes it. The connector requires an all-zero outgoing viewing key so that it can check the payout, so anyone can decrypt its address and amount.\n4. **Inside Zcash.** The ZEC sits in the shielded pool, where transfers hide sender, recipient and amount. Every amount that enters or leaves the pool is public, so a round trip of similar size within a short time pairs up.\n5. **Back to Ethereum.** Zodl pays ZEC from the pool to a fresh transparent deposit address of the connector. A relayer proves the deposit to the Zcash light client `zcash-client.bridge.near`, and the wrapped ZEC is minted and swapped. The operator then pays the Ethereum address from its treasury.\n\n### App\n\nThe ratings assume [Zodl](https://zodl.com), formerly Zashi, with its built-in Tor on. Zodl syncs from public lightwalletd servers, which serve the Zcash chain to light wallets, zec.rocks by default. Tor carries swap requests, payout fetches and transactions, each on its own circuit. Block sync and lookups of the account's transparent addresses stay direct, so the server can tie the isolated requests to the account by timing. Other wallets may leak more: the NEAR Intents web app pays only transparent Zcash addresses, which adds a public shielding step.\n\n### Custody\n\nThe Ethereum leg is custodial. The operator's deposit and treasury addresses are EOAs, and the operator mints the wrapped tokens on NEAR. The Verifier lets the NEAR Intents multisig and one single-key account freeze any account, which the operator uses for compliance holds. On Zcash, the connector holds funds at MPC-derived addresses and checks deposits against the light client. Relayers post Zcash block headers, credit deposits and request withdrawals. Anyone can become one by staking 1,000 NEAR and waiting seven days, unless the Rainbow Bridge multisig rejects them. The roles are listed under [upgrades and governance](#upgrades-and-governance).\n\n### Compliance\n\nIntents Technology Ltd. (British Virgin Islands) screens every quote against the NEAR Intents AML portal, Binance AML, AMLBot, PureFi and TRM Labs. Its [terms](https://docs.near-intents.org/security-compliance/terms-of-service) let it delay, block or freeze bridged funds and collect IP and wallet addresses. Market makers pass KYB, end users swap without an account.\n\n### Fees\n\nThe Verifier charges 1 pip (0.0001%) per swap. Zodl adds a 0.67% app fee to each quote, which it [shares with the 1Click API](https://docs.near-intents.org/resources/fees). Each leg also pays a bridge withdrawal fee, 0.000035 ETH on Ethereum and 0.00047 ZEC on Zcash on 2026-10-08.",
"links": {
"websites": [
"https://near-intents.org",
"https://z.cash",
"https://zodl.com"
],
"documentation": [
"https://docs.near-intents.org",
"https://zips.z.cash/protocol/protocol.pdf"
],
"explorers": [
"https://nearblocks.io/address/intents.near",
"https://nearblocks.io/address/zcash-connector.bridge.near",
"https://etherscan.io/address/0x2CfF890f0378a11913B6129B2E97417a2c302680"
],
"repositories": [
"https://github.com/near/intents",
"https://github.com/Near-One/btc-bridge",
"https://github.com/Near-One/btc-light-client-contract",
"https://github.com/defuse-protocol/defuse-frontend",
"https://github.com/zodl-inc/zodl-ios"
],
"socialMedia": [
"https://x.com/near_intents",
"https://x.com/zodl_app"
],
"other": [
"https://docs.near-intents.org/security-compliance/terms-of-service",
"https://docs.near-intents.org/security-compliance/risk-and-compliance",
"https://mpc-operators.nearone.org/"
]
}
}
+7 -7
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides which funds leaving Ethereum come back as which, by passing through Zcash's shielded pool. Everything on Ethereum and NEAR is public."
},
"adversaries": [
{
"id": "publicObserver",
"label": "Public observer",
"description": "Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links.",
"examples": "A curious counterparty, an employer, a journalist."
},
{
"id": "chainAnalyst",
"label": "Chain analyst",
"description": "Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters.",
"examples": "Chain analytics firms, ZachXBT, data brokers."
},
{
"id": "networkObserver",
"label": "Network observer",
"description": "Sits between the user and the services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "Zcash's shielded pool hides which payout into it funds which later exit.",
"exposureContinued": "Everything up to the pool edge is public: the Ethereum deposit and the NEAR entries that credit, swap and withdraw it. The bridge encrypts each payout so that anyone can decrypt its Zcash address and amount.",
"interior": {
"sender": "private",
"recipient": {
"verdict": "atRisk",
"note": "Payout notes are decryptable by anyone, so a reused address ties payouts together."
},
"amount": "private",
"asset": {
"verdict": "exposed",
"note": "Only ZEC exists inside the pool. The assets swapped from and to are public on NEAR."
},
"linkage": "private"
},
"sources": [
{
"title": "Custodial bridge mint carries the Ethereum transaction hash in its memo",
"url": "https://nearblocks.io/address/eth.omft.near"
},
{
"title": "Withdrawal message names the target Zcash address",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/api/token_receiver.rs#L8-L16"
},
{
"title": "Payouts are encrypted to an all-zero outgoing viewing key",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/zcash_utils/orchard_policy.rs#L13-L63"
},
{
"title": "Zodl creates a fresh shielded address for every quote",
"url": "https://github.com/zodl-inc/zodl-ios/blob/75b83d7601573e165e2dacdfaffb69750dadd05f/secant/Sources/Features/SwapAndPayForm/SwapAndPayStore.swift#L908-L955"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "good",
"exposureShort": "Payouts into the pool and later deposits to the bridge are public with amount and time, so a round trip of similar size pairs them.",
"exposureShort": "Only the amount and timing of a round trip can pair a payout into the pool with a later deposit to the bridge.",
"exposureContinued": "Deposit addresses are fresh per quote and spent together with the bridge change address, which marks every exit as NEAR Intents.",
"advice": "Hold the ZEC in the pool for days, split or merge it with other shielded funds, and swap back amounts that match no payout.",
"interior": {
"sender": "private",
"recipient": {
"verdict": "atRisk",
"note": "Payout notes are decryptable by anyone, so a reused address ties payouts together."
},
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Round-trip amount and timing across the pool edge pair a payout with a later deposit."
}
},
"sources": [
{
"title": "Kappos et al., An Empirical Analysis of Anonymity in Zcash (USENIX 2018)",
"url": "https://arxiv.org/abs/1805.03180"
},
{
"title": "Deposit address derived per quote from the deposit message",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/deposit_msg.rs#L49-L52"
},
{
"title": "Bridge change address and UTXO set",
"url": "https://nearblocks.io/address/zcash-connector.bridge.near"
}
],
"id": "chainAnalyst",
"value": "Link private",
"alsoExposed": []
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "Each sync sends the lightwalletd server your account's transparent addresses outside Tor, so timing ties both the payout fetch and the exit to you.",
"exposureShort": "Each sync sends the user's transparent addresses to lightwalletd outside Tor, tying both the payout fetch and the exit to that user by timing.",
"exposureContinued": "Zodl's Tor isolates each 1Click request, payout fetch and transaction, while block sync and these address lookups stay direct.",
"advice": "Turn on Tor in Zodl and point it at a lightwalletd server you run.",
"advice": "Turn on Tor in Zodl and point it at a lightwalletd server the user runs.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Private only with Tor on and your own lightwalletd server."
"note": "Private only with Tor on and the user's own lightwalletd server."
}
},
"sources": [
{
"title": "Tor is off unless the user enables it",
"url": "https://github.com/zodl-inc/zodl-ios/blob/75b83d7601573e165e2dacdfaffb69750dadd05f/secant/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift#L45"
},
{
"title": "Every sync looks up the account's transparent addresses directly",
"url": "https://github.com/zodl-inc/zodl-swift-wallet-sdk/blob/191d0347186a1c6fcd207eead24a891049444041/Sources/ZcashLightClientKit/Block/FetchUnspentTxOutputs/UTXOFetcher.swift#L39-L52"
},
{
"title": "Each transaction fetch and send gets its own Tor connection",
"url": "https://github.com/zodl-inc/zodl-swift-wallet-sdk/blob/191d0347186a1c6fcd207eead24a891049444041/Sources/ZcashLightClientKit/Modules/Service/LightWalletService.swift#L146-L159"
},
{
"title": "Compact blocks download over a direct connection",
"url": "https://github.com/zodl-inc/zodl-swift-wallet-sdk/blob/191d0347186a1c6fcd207eead24a891049444041/Sources/ZcashLightClientKit/Block/Download/BlockDownloader.swift#L187"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "The operator custodies both legs in transit and can freeze any account or deposit, so it sets the terms on which your funds come back.",
"exposureShort": "The operator custodies both legs in transit and can freeze any account or deposit, setting the terms on which the user's funds return.",
"exposureContinued": "Over Tor, its 1Click API sees each leg only as NEAR publishes it, with a fresh shielded address per swap. It holds no key into the shielded pool.",
"interior": {
"sender": "private",
"recipient": "private",
"amount": "private",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "The operator can freeze either leg and set the terms of its release."
}
},
"sources": [
{
"title": "1Click terms: IP and wallet data, screening, custody",
"url": "https://docs.near-intents.org/security-compliance/terms-of-service"
},
{
"title": "Any account can be locked by the multisig or a locker",
"url": "https://github.com/near/intents/blob/fa44ede9e874931c39a6c4246de82bc40f4f8d99/defuse/src/contract/accounts/force.rs#L21-L33"
},
{
"title": "Owner-only mint of the wrapped Ethereum assets",
"url": "https://github.com/near/intents/blob/fa44ede9e874931c39a6c4246de82bc40f4f8d99/poa-token/src/contract.rs#L79-L81"
},
{
"title": "Zodl sends its partner key and a fresh refund address with each quote",
"url": "https://github.com/zodl-inc/zodl-ios/blob/75b83d7601573e165e2dacdfaffb69750dadd05f/secant/Sources/Dependencies/SwapAndPay/sources/Near1Click.swift#L313-L352"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": []
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "All of an account's rotated addresses share one incoming viewing key on the Pallas curve, and every payout address is public on NEAR.",
"exposureContinued": "Whoever breaks elliptic-curve cryptography recovers that key from any one address and decrypts every note the account ever received. Those include the change notes of its exits, which join both legs of every round trip. ZIP 2005's quantum recoverability protects only funds.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Spends are found through the change notes they pay back to the same account."
},
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Orchard key agreement on the Pallas curve",
"url": "https://zips.z.cash/protocol/protocol.pdf#concreteorchardkeyagreement"
},
{
"title": "ZIP 2005: quantum recoverability of funds",
"url": "https://zips.z.cash/zip-2005"
},
{
"title": "Payouts are encrypted to an all-zero outgoing viewing key",
"url": "https://github.com/Near-One/btc-bridge/blob/4711c2f1d035a208464b9729bb24e45ed9ab7831/contracts/satoshi-bridge/src/zcash_utils/orchard_policy.rs#L13-L63"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "atRisk"
},
{
"field": "recipient",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
}
]
}
}
},
"anonymitySet": {
"type": "not-applicable",
"description": "The set is Zcash's shielded pool, outside the chains L2BEAT indexes. Amounts are public at the pool edge, so an exit hides among the pool entries that could match it in amount and time."
},
"attributes": [
{
"id": "bridged",
"label": "Bridged",
"description": "Funds leave Ethereum through a bridge and gain their privacy on another chain."
},
{
"id": "zk",
"label": "ZK",
"description": "Uses zero-knowledge proofs to prove private actions without revealing private data."
},
{
"id": "transfers",
"label": "Transfers",
"description": "Private transfers within the shielded pool."
},
{
"id": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The NEAR Intents multisig (3/5) upgrades the Verifier and the Rainbow Bridge multisig (3/5) the Zcash bridge, both instantly. The Ethereum funds sit in an EOA the operator controls.",
"description": "The NEAR Intents multisig upgrades the Verifier and the Rainbow Bridge multisig the Zcash bridge, both instantly. The Ethereum funds sit in an EOA the operator controls.",
"walkawayTest": {
"passed": false,
"reason": "The custodial bridge operator executes every Ethereum deposit and withdrawal offchain. For the Zcash light client on NEAR, anyone staking 1,000 NEAR can relay after seven days, unless the Rainbow Bridge multisig rejects them."
}
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The Verifier, the bridge token, the Zcash bridge and Zodl are open source. The bridge back end, the 1Click API and the solver relay that every Ethereum leg needs are closed."
},
"riskSummary": "## Funds can be stolen if\n1. the custodial bridge operator's mint key or its Ethereum treasury key is compromised.\n2. the NEAR Intents multisig (3/5) upgrades the Verifier maliciously.\n3. the Rainbow Bridge multisig (3/5) upgrades the Zcash connector, its token or the light client maliciously.\n4. 11 of the 17 NEAR MPC nodes collude to sign Zcash transactions from bridge addresses.\n
\n## Funds can be frozen if\n1. the NEAR Intents multisig or the single-key account locker freezes the user's account in the Verifier.\n2. the custodial bridge operator holds a deposit or withdrawal for a compliance review.\n3. the relayers stop and the Rainbow Bridge multisig rejects anyone who stakes to replace them.\n
\n## Privacy can be lost if\n1. the operator, which custodies both legs, makes the release of either depend on who the user is (see privileged insider).\n2. the lightwalletd server Zodl syncs from ties the payout and the exit to the account, since every sync sends the account's transparent addresses outside Tor (see network observer).\n3. elliptic-curve cryptography is broken, which recovers each account's viewing key from its payout addresses on NEAR (see future adversary).",
"riskSummary": "## Funds can be stolen if\n1. the custodial bridge operator's mint key or its Ethereum treasury key is compromised.\n2. the NEAR Intents multisig upgrades the Verifier maliciously.\n3. the Rainbow Bridge multisig upgrades the Zcash connector, its token or the light client maliciously.\n4. a threshold of the NEAR MPC nodes colludes to sign Zcash transactions from bridge addresses.\n
\n## Funds can be frozen if\n1. the NEAR Intents multisig or the single-key account locker freezes the user's account in the Verifier.\n2. the custodial bridge operator holds a deposit or withdrawal for a compliance review.\n3. the relayers stop and the Rainbow Bridge multisig rejects anyone who stakes to replace them.\n
\n## Privacy can be lost if\n1. the operator, which custodies both legs, makes the release of either depend on who the user is (see privileged insider).\n2. the lightwalletd server Zodl syncs from ties the payout and the exit to the account, since syncs send transparent addresses outside Tor (see network observer).\n3. elliptic-curve cryptography is broken, which recovers each account's viewing key from its payout addresses on NEAR (see future adversary).",
"tokens": [],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "The route touches three governed systems on NEAR, each can change instantly. The Ethereum side runs on EOAs.\n\n### NEAR Intents Verifier (`intents.near`)\n\n`intents.sputnik-dao.near`, a 3/5 multisig, holds the `DAO` role and is super admin, so it can grant every other role. Upgrades go through `ctl-intents.near`, a controller whose only admin is the same multisig, and deploy at once. Six accounts hold `PauseManager` and can halt all swaps and withdrawals, and only the multisig can resume them. One EOA holds `UnrestrictedAccountLocker` and can lock any user account. A locked account keeps receiving deposits, while its intents and withdrawals stop until the multisig unlocks it.\n\n### Custodial \"PoA\" bridge (`omft.near`, `eth.omft.near`)\n\nThe `poa-factory` contract `omft.near` deploys one token per bridged asset. It mints on the signal of `TokenDepositer`: `bridge-mng.near`, an EOA, and `int-mnt-dao.sputnik-dao.near`, a 3/3 multisig. NEAR takes every mint on trust. The NEAR Intents multisig is super admin.\n\n### Zcash connector (`zcash-connector.bridge.near`, `zec.omft.near`, `zcash-client.bridge.near`)\n\n`rainbowbridge.sputnik-dao.near`, a 3/5 multisig, governs the connector, the token's controller and the light client, and sets the bridge fees. `bridge-ops.near` can stage code and pause the connector, and `pm.bridge.near` can pause it. `omni-relayer.bridge.near` and `intents-relayer.near` prove deposits against the light client, whose headers `zcash-relayer.near` relays. Withdrawals are signed by the NEAR MPC network `v1.signer` (11/17, see the [MPC operators page](https://mpc-operators.nearone.org/)) on request of a relayer. These three hold roles that make them relayers. Anyone else becomes one after staking 1,000 NEAR and waiting seven days, and the multisig can reject any relayer. Anyone can refund a deposit the connector never credited, once the light client holds its block. The refund waits two days, or fourteen when the deposit message names no refund address. The multisig or `omni-relayer.bridge.near` can reject the refund until then."
},
"zkCatalogId": "zcash"
}
+19 -11
+2 -2
{
"badges": [],
"description": "A private DAI wallet on Aztec Network, with funds escrowed on Ethereum.",
"detailedDescription": "zk.money is a private DAI wallet on Aztec, with funds escrowed in an Ethereum portal. Payments inside are hidden, while names, deposits and withdrawals are public.\n\nEvery withdrawal and refund needs a zk proof and a signature from a live AWS Nitro enclave (TEE). The enclave decrypts and reads the operations it handles. Aztec Labs controls zk.money names and the contract lists the released wallets use.\n\n### Multiproof system\nThe portal releases funds only after Aztec proves the withdrawal message and one registered enclave signer co-signs it. Stealing escrowed funds requires both layers to fail, while a failure of either breaks privacy.\n\nIf Aztec Network governance changes its canonical rollup, anyone can freeze the zk.money portal, see [upgrades and governance](#upgrades-and-governance). Refunds then publish note or deposit amounts on Ethereum and use proofs without zero knowledge.\n\n### Wallets\nThe privacy ratings assume [zk.money Desktop v0.1.0](https://github.com/aztec-labs-eng/zkmoney-public/releases/tag/desktop-v0.1.0). There also is a hosted wallet without published source and the potential to build your independent wallet:\n- **Desktop:** runs local code but fetches contract addresses from two unsigned Aztec Labs lists. The wallet checks the [profile](https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/config-client/src/toContractServiceConfig.ts#L41-L68) against the [manifest](https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/core/src/oxide/index.ts#L108-L170), and both come from Aztec Labs. Substituting them can redirect future deposits and payments.\n- **Hosted:** loads code from zk.money on every visit, which makes its owner an insider. It can read viewing keys and request harmful passkey signatures behind prompts that hide the operation.\n- **Independent operation:** would allow a desktop build modified to pin verified addresses, remove screening and serve it under auth.zk.money. Users could submit Ethereum transactions themselves and run the approved enclave on AWS. New names still require Aztec Labs' signature. This setup is untested.\n\n### Deposits\nUsers fund L1 deposit addresses called SIPAs, and anyone can sweep them into the Ethereum portal. USDC and USDT are swapped to DAI through Curve. After fees, DAI stays in escrow and an Inbox message credits private notes on Aztec L2. Reusing a SIPA links its deposits.\n\n### Names\nTags like l2beat.zk.money publicly map to Aztec addresses and keys on Ethereum. They have two uses:\n- **L1 deposits:** any Ethereum wallet with ENS offchain lookups can pay l2beat.zk.money. The lookup returns a deposit address from the recipient's chosen resolver operator.\n- **L2 transfers:** zk.money users add a tag as a contact, and the wallet reads its Aztec address from the onchain registry to send private transfers.\n\nClaims require the domain owner's signature from a closed-source server with a name blocklist. The released wallet requires a registered name to access an account on a new device. Wallets use XMTP, an end-to-end encrypted messaging network, to confirm contacts added by QR code or link and to send payment requests.\n\n### Resolver\nFor ENS lookups, the resolver operator derives each deposit address from a secret shared with the recipient and announces it to them on L2. The wallets derive their own deposit addresses from the same shared secret, so the operator can link them. The onchain Resolver checks a zk proof on every lookup, which stops the operator from redirecting payments. Users can choose a custom operator for name payments.\n\n### Compliance\nThe wallet sends Ethereum addresses to Predicate through zk.money for screening, which funding a deposit SIPA directly avoids. The relayer screens token movements against OFAC and optionally Predicate, and self-submitted sweeps and withdrawals skip it.\n\nThe name resolver screens deposit addresses and funders against OFAC before notifying the recipient on L2. A blocked payment leaves funds at an address unknown to the wallet. Recovery requires the recipient's own tooling to re-derive it and authorization from their L1 account. Screening happens only in these offchain services.\n\n### Fees and limits\nA sweep pays 0.25 DAI, or 0.5 DAI when claiming a name. Registration costs 5 DAI by default, including its sweep fee.\n\nThe portal takes 0.1 DAI from each deposit and ordinary withdrawal to sponsor Aztec fees. Sponsorship requires a registered name or a voucher from a registered account. An empty fee-paying contract, or fees above its allowance, stall the released wallet. Modified code can pay with the account's own Fee Juice. Deposits share a 50,000 DAI capacity, refilling over 1 day. Deposit, payment and withdrawal amounts are capped at 2,583 DAI each.\n\n### Source verification\nThe [token verification steps](https://github.com/l2beat/l2beat/blob/main/packages/config/src/projects/zkmoney/verificationSteps-token.md) compare a fresh source build with the Aztec instance (L2 contract) pinned by the Ethereum portal. The [verifier steps](/zk-catalog/barretenberg#verifiers) regenerate the keys and Solidity verifiers of the refund and resolver circuits.",
"description": "A shielded ledger for DAI payments on Aztec, paid by name, with funds escrowed in an Ethereum portal that an enclave co-signs.",
"detailedDescription": "zk.money is a wallet for private DAI payments on Aztec. The user deposits DAI, USDC or USDT on Ethereum, pays other users by name and withdraws to any Ethereum address. Payments inside are hidden, while names, deposits and withdrawals are public.\n\nFunds stay escrowed in an Ethereum portal. Every withdrawal and refund needs a zk proof and a signature from a live AWS Nitro enclave (TEE). The enclave decrypts and reads the operations it handles. Aztec Labs controls zk.money names and the contract lists the released wallets use.\n\n### Architecture\n\nThe portal releases funds only after Aztec proves the withdrawal message and one registered enclave signer co-signs it. Stealing escrowed funds requires both layers to fail, while a failure of either breaks privacy.\n\n### Flow\n\n1. **Deposit.** You fund an L1 deposit address called a SIPA, and anyone can sweep it into the portal. USDC and USDT are swapped to DAI through Curve. After fees, an Inbox message credits private notes on Aztec. Reusing a SIPA links its deposits.\n2. **Pay.** You add a name as a contact, and the wallet reads its Aztec address from the onchain registry and sends a private transfer.\n3. **Withdraw.** Aztec proves the withdrawal message, an enclave co-signs it and the portal pays out DAI on Ethereum.\n\n### Keys\n\nAccounts sign with a passkey. The released wallet requires a registered name to access an account on a new device.\n\n### App\n\nThe ratings assume [zk.money Desktop v0.1.0](https://github.com/aztec-labs-eng/zkmoney-public/releases/tag/desktop-v0.1.0). There also is a hosted wallet without published source and the potential for the user to build an independent wallet:\n- **Desktop:** runs local code but fetches contract addresses from two unsigned Aztec Labs lists. The wallet checks the [profile](https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/config-client/src/toContractServiceConfig.ts#L41-L68) against the [manifest](https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/core/src/oxide/index.ts#L108-L170), and both come from Aztec Labs. Substituting them can redirect future deposits and payments.\n- **Hosted:** loads code from zk.money on every visit, which makes its owner an insider. It can read viewing keys and request harmful passkey signatures behind prompts that hide the operation.\n- **[CLI](https://github.com/aztec-labs-eng/zkmoney-cli/releases/tag/zkmoney-cli-v0.4.0):** skips XMTP, screens only when configured and accepts the user's own Aztec node and Ethereum RPC. It derives deposit addresses the same way.\n- **Independent operation:** would allow a desktop build modified to pin verified addresses, remove screening and serve it under auth.zk.money. Users could submit Ethereum transactions themselves and run the approved enclave on AWS. New names still require Aztec Labs' signature. This setup is untested.\n\n### Names\n\nTags like l2beat.zk.money publicly map to Aztec addresses and keys on Ethereum. Any Ethereum wallet with ENS offchain lookups can pay l2beat.zk.money, and the lookup returns a deposit address from the recipient's chosen resolver operator. zk.money users pay names directly on L2.\n\nClaims require the domain owner's signature from a closed-source server with a name blocklist. Wallets use XMTP, an end-to-end encrypted messaging network, to confirm contacts added by QR code or link and to send payment requests.\n\n### Resolver\n\nFor ENS lookups, the resolver operator derives each deposit address from a secret shared with the recipient and announces it to them on L2. The wallets derive their own deposit addresses from the same shared secret, so the operator can link them. The onchain Resolver checks a zk proof on every lookup, which stops the operator from redirecting payments. Users can choose a custom operator for name payments.\n\n### Exits\n\n**Refund.** If Aztec Network governance changes its canonical rollup, anyone can freeze the portal, see [upgrades and governance](#upgrades-and-governance). Refunds then publish note or deposit amounts on Ethereum and use proofs without zero knowledge.\n\n### Compliance\n\nThe wallet sends Ethereum addresses to Predicate through zk.money for screening, which funding a deposit SIPA directly avoids. The relayer screens token movements against OFAC and optionally Predicate, and self-submitted sweeps and withdrawals skip it.\n\nThe name resolver screens deposit addresses and funders against OFAC before notifying the recipient on L2. A blocked payment leaves funds at an address unknown to the wallet. Recovery requires the recipient's own tooling to re-derive it and authorization from their L1 account. Screening happens only in these offchain services.\n\n### Fees\n\nA sweep pays 0.25 DAI, or 0.5 DAI when claiming a name. Registration costs 5 DAI by default, including its sweep fee.\n\nThe portal takes 0.1 DAI from each deposit and ordinary withdrawal to sponsor Aztec fees. Sponsorship requires a registered name or a voucher from a registered account. An empty fee-paying contract, or fees above its allowance, stall the released wallet. Modified code can pay with the account's own Fee Juice. Deposits share a 50,000 DAI capacity, refilling over 1 day. Deposit, payment and withdrawal amounts are capped at 2,583 DAI each.\n\n### Source verification\n\nThe [token verification steps](https://github.com/l2beat/l2beat/blob/main/packages/config/src/projects/zkmoney/verificationSteps-token.md) compare a fresh source build with the Aztec instance (L2 contract) pinned by the Ethereum portal. The [verifier steps](/zk-catalog/barretenberg#verifiers) regenerate the keys and Solidity verifiers of the refund and resolver circuits.",
"links": {
"websites": [
"https://zk.money"
],
"documentation": [
"https://docs.zk.money/docs"
],
"repositories": [
"https://github.com/aztec-labs-eng/zkmoney-public"
],
"explorers": [
"https://aztecscan.xyz/contracts/instances/0x015ca4a43f83d08a038c36c06ca92527f120dc1f9176ea85d1ea347decaf444d"
]
}
}
+9 -1
null
[
{
"title": "zk.money relaunches on Aztec",
"url": "https://decrypt.co/379621/ethereum-privacy-aztec-brings-back-zk-money",
"date": "2026-09-29T00:00:00Z",
"description": "Aztec Labs relaunches zk.money as a private DAI wallet on the Aztec network.",
"type": "general"
}
]
+8 -8
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides senders, amounts and the link between deposits and withdrawals inside the ledger. Names, 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 services and sees traffic only: ISPs, VPNs, Tor exits, public RPCs and relayers. Learns IP addresses, timing, fingerprints and ciphertext. Services the operator runs count as the insider.",
"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 an operator role, custodies funds, receives keys or plaintext by design, or delivers the client: upgrade admin, custodian, view key holder, enclave operator, association set provider, hosted prover, registry, frontend host.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator, a custodial bridge."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and breaks all public-key cryptography, by quantum computer or new mathematics: elliptic curves, pairings, RSA, lattices and isogenies. Hashes, Poseidon included, and symmetric ciphers survive.",
"examples": "An intelligence agency archiving chain data today, then anyone once a break is public."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "Who receives the funds: the address they exit to, the receiver of a transfer, or whoever controls a one-time receiving address."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposureShort": "Private payments publish encrypted notes.",
"exposureContinued": "Claiming a name ties it and its Aztec address to the wallet that funds the registration. The first payment to a new contact reveals the recipient through a handshake, and L2 logs mark every transaction of the shared token smart contract. Your first sponsored transaction after registration is tied to your account, with the payment, deposit address or withdrawal it carries.",
"advice": "Claim your name from a wallet with no public link to you, and make your first transaction after registration a deposit from that wallet. Fund each deposit address once and let it be swept/relayed.",
"exposureContinued": "Claiming a name ties it and its Aztec address to the wallet that funds the registration. The first payment to a new contact reveals the recipient through a handshake, and L2 logs mark every transaction of the shared token smart contract. The user's first sponsored transaction after registration is tied to their account, with the payment, deposit address or withdrawal it carries.",
"advice": "Claim the user's name from a wallet without a public link to them, then make the first transaction after registration a deposit from that wallet. Fund each deposit address once and let it be swept/relayed.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Public for your first sponsored transaction after registration."
"note": "Public for the user's first sponsored transaction after registration."
},
"recipient": {
"verdict": "exposed",
"note": "Revealed by the first payment to each new contact."
},
"amount": "private",
"asset": {
"verdict": "exposed",
"note": "Only DAI."
},
"linkage": {
"verdict": "atRisk",
"note": "Exposed when your first sponsored transaction after registration withdraws or pays a new contact."
"note": "Exposed when the user's first sponsored transaction after registration withdraws or pays a new contact."
}
},
"sources": [
{
"title": "Fixed log tags on every token transaction",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/vendor/oxide/noir-projects/oxide_lib/src/constants.nr#L77-L81"
},
{
"title": "First delivery to a new contact opens a handshake",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/sdk/src/feePaymentMethod/claimFpcGasModel.ts#L232-L237"
},
{
"title": "First sponsored transaction nullifies the L1 account",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/contracts/contracts/fee_paying/claim_fpc/src/main.nr#L205-L217"
},
{
"contract": "AccountMetadataRegistry",
"title": "Names map to Aztec addresses"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposureShort": "In practice the anonymity set is zk.money's users on Aztec, although the L2 contract is permissionless.",
"exposureContinued": "The first payment to a name from outside zk.money is publicly tied to that name.",
"advice": "Give outside payers a deposit address your wallet created. Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"advice": "Give outside payers a deposit address the user's wallet created. Withdraw common amounts rather than everything at once. Wait before exiting and pick a different time of day than the deposit. Exit to a fresh address every time and spend from it with a different wallet than the one that deposited.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Public for your first sponsored transaction after registration."
"note": "Public for the user's first sponsored transaction after registration."
},
"recipient": {
"verdict": "exposed",
"note": "Revealed by the first payment to each new contact."
},
"amount": {
"verdict": "atRisk",
"note": "Inferable when a payment sits between a matching public deposit and withdrawal."
},
"asset": {
"verdict": "exposed",
"note": "Only DAI."
},
"linkage": {
"verdict": "atRisk",
"note": "Registration deposits are publicly tied to their name."
}
},
"sources": [
{
"contract": "RegistrationSIPA"
},
{
"title": "Resolver notifies the recipient of each deposit address",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/vendor/oxide/noir-projects/oxide_token_contract/src/main.nr#L236-L251"
},
{
"title": "The notification and the sweep request share one Aztec transaction",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/sdk/src/services/sipaIntents.ts#L171-L186"
},
{
"title": "Handshake announcements are tagged with the recipient address",
"url": "https://github.com/AztecProtocol/aztec-packages/blob/v5.2.0/noir-projects/noir-contracts/contracts/standard/handshake_registry_contract/src/main.nr#L82-L96"
}
],
"id": "chainAnalyst",
"value": "Link at risk",
"alsoExposed": [
{
"field": "amount",
"exposure": "atRisk"
}
]
},
"networkObserver": {
"sentiment": "warning",
"exposureShort": "Anyone on your network path sees when you send Aztec transactions and can match your deposit and withdrawal to the blocks they land in.",
"exposureShort": "Anyone on the user's network path sees their Aztec transactions and can match their deposit and withdrawal to the blocks they land in.",
"exposureContinued": "Desktop has no Tor or proxy setting.",
"advice": "Route zk.money Desktop through Tor with system-wide tools.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "XMTP shows who asks whom for payment."
},
"recipient": {
"verdict": "exposed",
"note": "Revealed by the first payment to each new contact."
},
"amount": "private",
"asset": {
"verdict": "exposed",
"note": "Only DAI."
},
"linkage": {
"verdict": "atRisk",
"note": "Private only over Tor, which Desktop lacks."
}
},
"sources": [
{
"title": "Desktop settings: endpoints only, no proxy",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/web-wallet-desktop/src/config.js#L36-L47"
},
{
"title": "XMTP identity is the account bootstrap address",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/web-wallet/src/platform/xmtp/WebXmtpClient.ts#L95-L109"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposureShort": "The wallets derive their own deposit addresses from an ECDH secret shared with the resolver operator, Aztec Labs today.",
"exposureContinued": "Matching the derived commitments against public portal deposits lets the operator link L1 deposits to L2 recipients. Payments, withdrawals and deposit claims are encrypted to an enclave admitted with an AWS Nitro attestation, which reads them inside.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Wallets derive deposit addresses against the resolver key",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/web-wallet/src/features/deposit/sipaGateway.ts#L857-L871"
},
{
"title": "Their nonces count down a predictable range",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/sdk/src/services/sipaSelfResolve.ts#L49-L58"
},
{
"title": "Wallets encrypt to the enclave key registered in the portal",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/vendor/oxide/yarn-project/oxide-client/src/fleet_signer.ts#L172-L202"
},
{
"contract": "ZkMoneyPortal",
"title": "Deposits publish the recipient commitment"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
}
]
},
"futureAdversary": {
"sentiment": "bad",
"exposureShort": "Whoever breaks elliptic-curve cryptography can decrypt historical notes and payment events published to Ethereum.",
"exposureContinued": "Registered Aztec addresses identify the accounts. Deposit address secrets and enclave communication use the same class of cryptography.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"contract": "AccountMetadataRegistry",
"title": "Registered Aztec addresses"
},
{
"title": "Deposit address secrets use secp256k1 key exchange",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/packages/sdk/src/services/sipaStealth.ts#L80-L100"
},
{
"title": "Enclave channel uses P-256 HPKE",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/f04743f5d57d1b89f9190cad0f6affd28b0793f2/vendor/oxide/yarn-project/oxide-lib/src/encryption.ts#L1-L5"
}
],
"id": "futureAdversary",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
}
]
}
}
},
"anonymitySet": {
"type": "partially-attributed"
},
"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": "anyAmount",
"label": "Any amount",
"description": "Deposits and withdrawals can have any size."
}
],
"category": {
"id": "shieldedLedger",
"label": "Shielded ledger",
"description": "A pool with transfers or DeFi inside. Hides who pays whom and which deposit became which withdrawal."
},
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "The escrow and the approved enclave image are immutable.",
"walkawayTest": {
"passed": false,
"reason": "Withdrawals and refunds need a live enclave running the approved image on AWS Nitro. The released wallets also depend on zk.money services, so independent operation needs a modified desktop build."
}
},
"relayerTracking": {
"type": "onchainEvents",
"sources": [
{
"address": "eth:0xdf410ad448A0f7165181FBdB32f8896f4a0d9449",
"sinceTimestamp": 1790298635,
"extractor": "zkMoneyWithdrawalRelayer",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"executorAddress": "0xC2363b7155EE8391C5395B72C46a6d22BF49151c",
"operationExecutor": "0xe883686d9EC4E0430f2233A6BF12B2D226a39e19"
}
}
]
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The contract, desktop wallet, enclave and circuit sources are public. The deployed refund and resolver verifiers match the published circuits. The approved enclave binary remains unreproduced. The resolver service, frozen-chain snapshot service and hosted wallet release source are unpublished. Verification instructions cover the refund and resolver circuits and the Aztec token pinned by the Ethereum portal."
},
"riskSummary": "## Funds can be stolen if\n\n1. Aztec's private execution or refund proof is unsound and the enclave layer also fails.\n2. malicious wallet code obtains a harmful signature, or substituted unsigned contract lists redirect future deposits and payments.\n3. the NameRegistry owner or owner of zk.money in ENS redirects future payments to names.\n\n
\n\n## Funds can be lost if\n\n1. a user loses their passkey.\n2. every AWS-attested enclave stops running.\n3. resolver screening blocks an external payment and the recipient lacks the tooling to re-derive its deposit address.\n\n
\n\n## Privacy can be lost if\n\n1. a user pays a new contact, which reveals the recipient, or makes their first sponsored transaction after registration, which is tied to their account (see public observer).\n2. the resolver operator derives the wallets' deposit addresses and links L1 deposits to L2 recipients (see privileged insider).\n3. an enclave or AWS Nitro is compromised (see privileged insider).\n4. the portal freezes and users exit through refunds, which publish note or deposit amounts on Ethereum.\n5. elliptic-curve cryptography is broken, which decrypts data published to Ethereum (see future adversary).",
"riskSummary": "## Funds can be stolen if\n\n1. Aztec's private execution or refund proof is unsound and the enclave layer also fails.\n2. malicious wallet code obtains a harmful signature, or substituted unsigned contract lists redirect future deposits and payments.\n3. the NameRegistry owner or owner of zk.money in ENS redirects future payments to names.\n\n
\n\n## Funds can be lost if\n\n1. a user loses their passkey.\n2. every AWS-attested enclave stops running.\n3. resolver screening blocks an external payment and the recipient lacks the tooling to re-derive its deposit address.\n\n
\n\n## Privacy can be lost if\n\n1. a user pays a new contact, whose handshake reveals the recipient (see public observer).\n2. the resolver operator links L1 deposits to L2 recipients, or an enclave or AWS Nitro is compromised (see privileged insider).\n3. anyone on the user's network path times their Aztec transactions, since Desktop has no Tor or proxy setting (see network observer).\n4. the portal freezes, and refunds publish note or deposit amounts on Ethereum.\n5. elliptic-curve cryptography is broken, which decrypts data published to Ethereum (see future adversary).",
"tokens": [
{
"token": {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"iconUrl": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.png?1696509996",
"symbol": "DAI",
"decimals": 18,
"priceId": "dai",
"sinceTimestamp": 1790298635
},
"buckets": [
{
"id": "zkmoney-DAI",
"type": "pool",
"label": "DAI",
"address": "eth:0xdf410ad448A0f7165181FBdB32f8896f4a0d9449",
"sinceTimestamp": 1790298635,
"anonymitySet": {
"minimumAmounts": [
"200000000000000000000"
]
},
"deposit": {
"event": "0x8154af7b1b360f500640de68c82af19c52c4ad4189d7a7c7a41506e19a1fdd6c",
"extractor": "zkMoneyDeposit",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"fundingCut": "100000000000000000",
"factoryAddress": "0xc357E34D4C7520a83C9Ec0a242Ba52b4ecABa1Fc",
"depositImplementation": "0xF714D8Cf287F4A410b018514074A9371BC45461b",
"registrationImplementation": "0x0934F010d59b552949DBe44AcBE5AFf0e32C8C34",
"fundingTokens": [
"0x6B175474E89094C44Da98b954EedeAC495271d0F",
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"0xdAC17F958D2ee523a2206206994597C13D831ec7"
],
"exchangeAddress": "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7",
"historyFromBlock": 26051085
}
},
"withdrawal": {
"event": "0x0ef2e2e9f18042ca214d1bee833209f28326ffa8f4a6b0dc92172caf71bc5433",
"extractor": "zkMoneyWithdrawal",
"params": {}
}
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "The core smart contracts of Aztec Network and zk.money are immutable.\n\n### Enclave signers and freeze\n\nAnyone can permanently register an enclave signer with an AWS Nitro attestation up to 1 hour old, and 2 are registered. If Aztec governance changes its canonical rollup, anyone can freeze the portal for good, which stops deposits and enables refunds at the last proven checkpoint.\n\n### Names and services\n\nTwo Aztec Labs EOAs control *.zk.money names. The deployer owns the NameRegistry and can replace its registration, metadata and resolver contracts, which lets it reassign names and redirect future payments. It also owns zk.money in ENS, which the DNS holder can reclaim. The domain owner can refuse claims, sign custom fees and add permanent fee beneficiaries, which users pick from in their registration intent. Aztec Labs also controls the hosted wallet, the desktop releases and the two unsigned contract lists both wallets currently use."
},
"zkCatalogId": "barretenberg"
}