{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Hides senders, amounts and links between deposits and withdrawals within the ledger. Tags, deposits and withdrawals are public. The first payment to a new contact reveals the recipient, and the first sponsored transaction after registration reveals the sender’s account."
},
"adversaries": [
{
"id": "publicObserver",
"label": "Public observer",
"description": "Everyone with a block explorer and some basic tools. Sees every public onchain event, but does no correlation beyond following links.",
"examples": "A curious counterparty, an employer, a journalist."
},
{
"id": "chainAnalyst",
"label": "Chain analyst",
"description": "Scrapes all public data and correlates it: timing, amounts, gas and wallet fingerprints, address clusters.",
"examples": "Chain analytics firms, ZachXBT, data brokers."
},
{
"id": "networkObserver",
"label": "Network observer",
"description": "Sits between the user and the chain and sees web2 traffic only: RPC providers, relayers and broadcasters, indexers, ISPs. Learns IP addresses, timing, browser fingerprints, ciphertext and what becomes public. Assumes Tor to send transactions and, where the client has an RPC setting, an own node to read the blockchain.",
"examples": "Infura or Alchemy, a Tornado relayer, a wallet vendor selling telemetry, Google captcha or analytics in the dapp frontend."
},
{
"id": "privilegedInsider",
"label": "Privileged insider",
"description": "Holds a protocol operator role or receives keys or plaintext by design: upgrade admin, sequencer, decryption or view key holder, TEE vendor, association set provider, hosted prover, note registry.",
"examples": "A compliance backdoor key, a DAO with an upgrade key, a KMS committee, an ASP operator."
},
{
"id": "futureAdversary",
"label": "Future adversary",
"description": "Harvest now, decrypt later. Holds every byte ever written onchain plus any retained logs, and future cryptanalysis such as a large quantum computer that breaks elliptic-curve key exchange and pairings, but not hashes, symmetric ciphers or lattices.",
"examples": "First well-funded insiders, then everyone in a potential post-quantum future."
}
],
"fields": [
{
"id": "sender",
"label": "Sender",
"subject": "Sender",
"promiseLabel": "Sender privacy",
"description": "The Ethereum address that funds entered from, or that initiated a transfer."
},
{
"id": "recipient",
"label": "Recipient",
"subject": "Recipient",
"promiseLabel": "Recipient privacy",
"description": "The Ethereum address that funds exit to, or that receives a transfer."
},
{
"id": "amount",
"label": "Amount",
"subject": "Amounts",
"promiseLabel": "Amount privacy",
"description": "The value moved."
},
{
"id": "asset",
"label": "Asset",
"subject": "Asset",
"promiseLabel": "Asset privacy",
"description": "Which token is moved."
},
{
"id": "linkage",
"label": "Link",
"subject": "Link",
"promiseLabel": "Link privacy",
"description": "Whether the entry and exit of the same funds, or sender and recipient of the same transfer, can be tied together."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposure": "Private payments publish encrypted notes. Fixed log tags identify zk.money transactions on Aztec, and the first payment to a new contact reveals the recipient's Aztec address through a handshake. The Ethereum registry maps that address to a tag. The first sponsored transaction after registration also identifies the sender's account. Deposits and withdrawals show address, token and amount. Claiming a tag links it and the Aztec address to the funding L1 wallet.",
"advice": "Claim your tag from a wallet with no public link to you and fund each deposit address once. Wait for deposits to be swept. Recovering them reveals the link to your account.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Your first sponsored transaction after registration is publicly tied to your account."
},
"recipient": {
"verdict": "exposed",
"note": "The first payment to a new contact reveals the recipient's Aztec address."
},
"amount": "private",
"asset": {
"verdict": "exposed",
"note": "zk.money only holds DAI."
},
"linkage": {
"verdict": "atRisk",
"note": "If your first sponsored transaction after registration is a payment to a new contact, it shows both ends."
}
},
"sources": [
{
"title": "Fixed log tags on every zk.money transaction",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/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/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/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/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/contracts/contracts/fee_paying/claim_fpc/src/main.nr#L205-L217"
},
{
"title": "Withdrawals publish their L1 payload",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/vendor/oxide/noir-projects/oxide_token_contract/src/withdrawal.nr#L32-L42"
},
{
"contract": "RegistrationController"
},
{
"contract": "AccountMetadataRegistry"
},
{
"contract": "DepositSIPA"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "The anonymity set is limited to zk.money users. The first payment to a tag from outside zk.money is publicly tied to that tag. Anyone who records Aztec transactions can also tie the first deposit address your wallet creates after registration to your account.",
"advice": "Watch the anonymity set in this early stage. Give outside payers a deposit address your wallet created instead of your tag. Fund your first deposit address after registration from the wallet that claimed your tag. 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": "atRisk",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Inferable when a payment sits between a matching public deposit and withdrawal."
},
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Registration deposits are publicly tied to their tag."
}
},
"sources": [
{
"contract": "ZkMoneyPortal"
},
{
"contract": "RegistrationSIPA"
},
{
"contract": "PlainWithdrawalExecutor"
},
{
"title": "Private transfers credit the recipient with new notes",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/vendor/oxide/noir-projects/oxide_token_contract/src/main.nr#L133-L167"
},
{
"title": "Resolver notifies the recipient of each deposit address",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/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/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/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": "good",
"exposure": "Keys and proving stay on your device. Note discovery reveals your account address to the Aztec node. Ethereum RPC requests reveal funding wallets, deposit addresses and L1 transaction senders. XMTP carries payment requests and contact links under public account addresses, exposing who asks whom for money. Both wallets support custom Aztec, Ethereum and enclave endpoints.",
"advice": "Use zk.money Desktop with your own Aztec and Ethereum nodes. Send L1 transactions from your Ethereum wallet over a public RPC. Route all computer traffic through a VPN or Tor. Avoid payment requests and contact links with counterparties that must stay private.",
"interior": {
"sender": {
"verdict": "atRisk",
"note": "Payment requests over XMTP show who asks whom."
},
"recipient": "exposed",
"amount": "private",
"asset": "exposed",
"linkage": "atRisk"
},
"sources": [
{
"title": "Desktop endpoint overrides",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/web-wallet-desktop/src/config.js#L36-L47"
},
{
"title": "Handshakes are found by a tag derived from your 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"
},
{
"title": "L1 gas estimates over the wallet RPC carry your address",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/web-wallet/src/features/deposit/sipaRecovery.ts#L166-L193"
},
{
"title": "Desktop sends screening to zk.money from its own process",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/web-wallet-desktop/scripts/compose-config.js#L15-L20"
},
{
"title": "XMTP identity is the account bootstrap address",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/web-wallet/src/platform/xmtp/WebXmtpClient.ts#L95-L109"
},
{
"title": "Pointing the wallet at a different service",
"url": "https://docs.zk.money/docs/desktop"
}
],
"id": "networkObserver",
"value": "Link private",
"alsoExposed": []
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "Wallets encrypt payments, withdrawals and deposit claims to an enclave key registered in the portal. The approved code decrypts them inside the enclave. Privacy against the operator depends on AWS Nitro and that code, whose binary has not been reproduced. The resolver operator can re-derive all deposit addresses, including those created by your wallet, linking L1 deposits to L2 recipients.",
"advice": "Run your own enclave, which is permissionless onchain but still depends on AWS. Running your own resolver operator is also permissionless, but its service is unpublished and the released wallets only use Aztec Labs' operator.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "exposed",
"asset": "exposed",
"linkage": "exposed"
},
"sources": [
{
"title": "Enclave requests carry notes and the nullifier hiding key",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/vendor/oxide/yarn-project/oxide-lib/src/types.ts#L336-L392"
},
{
"title": "Wallets encrypt to the enclave key registered in the portal",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/vendor/oxide/yarn-project/oxide-client/src/fleet_signer.ts#L172-L202"
},
{
"title": "Deposit address secrets derive from the resolver key",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/sdk/src/services/sipaStealth.ts#L80-L100"
},
{
"title": "The resolver circuit derives each secret from the operator key",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/68425f9cf408ac803eade04d10318fcf345444a0/vendor/oxide/noir-projects/resolver_circuit/src/main.nr#L23-L79"
},
{
"title": "Self-made deposit addresses use predictable nonces",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/sdk/src/services/sipaSelfResolve.ts#L47-L53"
},
{
"title": "Addresses are sent to Predicate for screening",
"url": "https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/front-core/src/core/services/screening/PredicateScreeningService.ts#L86-L97"
},
{
"contract": "ZkMoneyPortal",
"title": "Registered TEE signers"
},
{
"contract": "Resolver"
},
{
"contract": "AccountMetadataRegistry",
"title": "Resolver operators"
},
{
"section": "permissions"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
{
"field": "sender",
"exposure": "exposed"
},
{
"field": "amount",
"exposure": "exposed"
}
]
},
"futureAdversary": {
"sentiment": "bad",
"exposure": "A quantum computer that breaks elliptic-curve key exchange can decrypt historical notes and payment events published to Ethereum. Registered Aztec addresses identify the accounts. Deposit address secrets and enclave communication use the same class of cryptography.",
"advice": "Treat every payment and every deposit link as permanently disclosed.",
"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/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/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/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/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 funds which withdrawal."
},
"exitWindow": {
"value": "Infinite",
"sentiment": "good",
"orderHint": 9007199254740991,
"description": "The escrow and approved enclave image cannot be upgraded. Every withdrawal and refund still needs a proof and a live approved enclave. If Aztec changes its canonical rollup, anyone can freeze deposits and fix refundable ownership at the last proven checkpoint. Later L2 transfers do not change it.",
"walkawayTest": {
"passed": false,
"reason": "Withdrawals and refunds require a live enclave running the approved image published by Aztec Labs. Running one requires AWS Nitro infrastructure. Both released wallets also depend on zk.money services, so independent operation requires a modified desktop build."
}
},
"relayerTracking": {
"type": "onchainEvents",
"metric": "paidFinalizers",
"sources": [
{
"address": "eth:0xdf410ad448A0f7165181FBdB32f8896f4a0d9449",
"sinceTimestamp": 1790298635,
"extractor": "zkMoneyDepositPayout",
"extractor": "zkMoneyWithdrawalRelayer",
"params": {
"tokenAddress": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"fundingCut": "100000000000000000",
"factoryAddress": "0xc357E34D4C7520a83C9Ec0a242Ba52b4ecABa1Fc",
"depositImplementation": "0xF714D8Cf287F4A410b018514074A9371BC45461b",
"registrationImplementation": "0x0934F010d59b552949DBe44AcBE5AFf0e32C8C34",
"depositFee": "250000000000000000",
"registrationSweepFee": "500000000000000000",
"operationExecutor": "0xe883686d9EC4E0430f2233A6BF12B2D226a39e19"
}
},
{
"address": "eth:0xdf410ad448A0f7165181FBdB32f8896f4a0d9449",
"sinceTimestamp": 1790298635,
"extractor": "zkMoneyWithdrawalPayout",
"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 has not been reproduced. 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. no AWS-attested enclave is running.\n3. resolver screening blocks an external payment. Funds remain recoverable, but the recipient needs their own tooling to re-derive the unannounced deposit address.\n\n \n\n## Privacy can be lost if\n\n1. a user pays a new contact. The handshake reveals the recipient's Aztec address, publicly linked to their name. The first sponsored transaction after registration also reveals the sender's account.\n2. an enclave or AWS Nitro attestation is compromised.\n3. the resolver operator re-derives deposit addresses and links deposits to recipients.\n4. public deposits, withdrawals, registration and deposit-address reuse allow amount or timing correlation.\n5. an external Aztec RPC learns the account from note requests, or zk.money and Predicate correlate screened addresses by IP.\n6. someone reads keys and decrypted notes stored unencrypted on the device.\n7. a freeze forces refunds, which expose note and deposit amounts on Ethereum and use proofs without zero knowledge.\n8. a quantum computer breaks the elliptic-curve encryption of notes published to Ethereum.",
"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": "### Immutable escrow and accounts\n\nThe portal, Aztec token, fee-paying contract, verifiers, withdrawal executors and current SIPA implementations cannot be upgraded.\n\nThe approved enclave image is fixed. Anyone can permanently register a signer with an AWS Nitro attestation at most 1 hour old. 2 signers are registered. A withdrawal or refund needs only one of them, alongside its proof. Registrations do not expire and cannot be revoked.\n\nIf Aztec governance changes the canonical rollup, anyone can permanently freeze the portal. Deposits stop and refundable ownership is fixed at the last proven checkpoint. Later L2 transfers do not change it. Withdrawals and refunds still require a live enclave.\n\n### Name administration\n\nTwo Aztec Labs EOAs control *.zk.money names:\n\n- The deployer owns the NameRegistry and can replace its RegistrationController, AccountMetadataRegistry and Resolver, allowing it to reassign names, rewrite records and redirect future payments. It also owns zk.money in ENS, which the DNS domain holder can reclaim.\n- The domain owner can refuse name claims, sign custom registration fees and add fee beneficiaries. Users select a beneficiary from the allowlist in their signed registration intent. Existing beneficiaries cannot be removed.\n\n### Wallet and services\n\nAztec Labs controls hosted wallet code, desktop releases and two unsigned contract lists consumed by both wallets. These are security dependencies: wallet code handles keys and signatures, and the lists determine where future funds go. The wallet checks the [profile](https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/config-client/src/toContractServiceConfig.ts#L41-L68) and [manifest](https://github.com/aztec-labs-eng/zkmoney-public/blob/fc37a3e25440bc4bd7a9de81a7f4830ec753a4d4/packages/core/src/oxide/index.ts#L108-L170) for consistency, but both come from Aztec Labs. A local build with custom nodes still trusts those lists until their addresses are independently verified and pinned.\n\nThe relayer, resolver, claim server, enclave hosts and passkey domain also control availability and censorship. Self-submission bypasses the relayer, but exits still need an enclave and the released wallets still depend on zk.money services."
},
"zkCatalogId": "barretenberg"
}