fd1ddb46 (main)
and
ef9d90cb (PR)
[ADDED]
zkapi
+660 -0
+1 -0
"zkapi"
+1 -0
"zkapi"
+1 -0
"zkAPI"
+1 -0
null
+1 -0
null
+1 -0
1790899200
+7 -0
{
"redWarning": {
"text": "The vault owner can block withdrawals until deposits expire and become sweepable to the treasury. The proofs rely on a single-party trusted setup.",
"detailAnchor": "permissions"
},
"unverifiedContracts": []
}
+16 -0
{
"badges": [],
"description": "An ETH-funded system that helps unlink an onchain payment from the AI inference token it paid for.",
"detailedDescription": "zkAPI hides the link between a user's onchain money and their AI usage. It does not hide deposits, withdrawals or prompts.\n\nzkAPI lets users prepay for AI inference with ETH and then use it without revealing which deposit pays for which request. A user deposits ETH into a vault on Ethereum once and then makes API requests. Each request carries a zero-knowledge proof that some funded deposit can pay, without saying which one. It is built by Open Anonymity with the Ethereum Foundation and powers the Ethereum wallet option of the 'OA Chat' website.\n\n### Typical user flow\n\n1. **Deposit:** the app creates a secret locally and deposits ETH from the user's wallet. This creates a public, numbered note showing the funding address and amount. The note expires after 1 month, rounded up in 1 day steps.\n2. **Get access:** the app proves locally that it controls a funded, unspent note and sends the proof to the zkAPI operator. The operator checks it and hands out a short-lived OpenRouter API key with a budget of at most 0.00005 ETH.\n3. **Chat:** the app sends prompts with that key to OpenRouter, which forwards them to the model provider.\n4. **Settle:** the operator charges the measured usage and returns a new signed balance. The app stores it locally for the next request.\n5. **Withdraw:** with the operator's approval, the app closes the note, sends the remaining ETH to an address of the user's choice and the used part to the operator's treasury. Without approval, an escape withdrawal takes 1 day. Users must withdraw before expiry: anyone can send the **entire deposit of an expired note, including unused funds**, to the treasury.\n\n### Architecture\n\n- **ZkApiVault** on Ethereum holds the ETH and a Merkle tree of active notes. It verifies withdrawal proofs and handles escapes and expiry. It is not upgradeable.\n- **Clients** keep note secrets and the latest signed balance on the user's device and generate proofs there. OA Chat uses the browser SDK. `zkapi-clientd` is a local daemon offering an OpenAI-compatible API for apps like Open WebUI.\n- **The operator** (Open Anonymity) verifies request proofs, issues API keys, signs new balances and approves withdrawals. It also runs the indexer serving Merkle paths and a challenger that disputes escape withdrawals using outdated balances.\n- **Open Anonymity's key stations and verifier** hold the OpenRouter accounts behind the issued keys and check provider privacy settings. OpenRouter and the model providers run the inference.\n- **Chainlink's ETH/USD feed** converts USD inference costs into ETH.\n\nBalances live offchain. The vault only knows each note's original deposit. The current balance is a hidden commitment signed by the operator and held by the user. Each request spends that state exactly once, enforced by a nullifier, and the operator signs a successor.\n\nThe proofs are Groth16 over BN254, with Poseidon hashes, Baby-JubJub balance commitments and Schnorr signatures. The proving keys come from a single-party trusted setup.\n\n### Privacy considerations\n\n**Private:** which note pays for a request. Request proofs hide the note, its exact balance and its signed state. Individual charges stay offchain.\n\n**Public:** deposits show the funding address, amount and note id. Withdrawals show the same note id, the payout address and the remaining balance. Deposit and withdrawal of a note are always linked, and its total spending is public.\n\n**Seen by services:**\n\n- The operator sees request timing, budgets and billed usage. During an escape withdrawal it can publish a request proof onchain, linking that request to the note.\n- OpenRouter and the model provider read prompts and responses and can link all requests using the same key. The verifier's approval does not prove that providers keep no logs. Avoid identifying prompts.\n- Services see the user's IP unless a proxy or Tor is used. OA Chat sends inference through a Wisp proxy, but falls back to direct connections by default and sends protocol requests outside the proxy. `zkapi-clientd` connects directly unless configured with Tor and reuses a key for 60 seconds by default. Neither client isolates Tor circuits or hides request timing.\n- The hosted app can read local secrets and chats after an update. Inspected local builds avoid this.\n\nUsers are advised to research [OPSEC best practice](/publications/privacy-best-practices).\n\n### Risks and trust assumptions\n\n- **Owner:** an EOA can pause deposits, cooperative withdrawals and new escapes at any time, and change the treasury. Expiry sweeps keep working while paused, so a pause until expiry lets the owner redirect all active deposits to its treasury.\n- **Operator:** controls billing, key issuance and withdrawal approval. Its signing keys are fixed in the vault. If it receives a request but never returns the signed new balance, it can challenge the user's escape with that request and the note eventually expires into the treasury.\n- **Trusted setup:** whoever ran the single-party setup can forge withdrawal proofs if they kept its secrets.\n- **Local data:** losing the note secret or latest signed balance means losing the funds.\n\n### Fees\n\nThe public server code adds no fee on top of the usage reported by Open Anonymity's key service. Usage is priced in USD, converted to ETH via Chainlink and paid to the treasury at withdrawal or expiry. Users pay Ethereum gas for deposits and withdrawals.\n\n### Compliance\n\nThere is no onchain screening or allowlist. The operator can refuse to issue keys or approve withdrawals.\n\n### Anonymity set\n\nTo the operator, a request can come from any active, unexpired note whose deposit covers the request budget. This set only contains zkAPI users and is small. Using a fresh deposit right away, unusual budgets or distinctive timing can narrow it to a single note. Waiting after a deposit helps.",
"links": {
"websites": [
"https://chat.openanonymity.ai"
],
"documentation": [
"https://zkapi.openanonymity.ai"
],
"repositories": [
"https://github.com/ethereum/zkapi"
]
}
}
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+11 -0
[
{
"address": "0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe",
"chain": "ethereum",
"sinceTimestamp": 1790783879,
"tokens": [
"ETH"
],
"chainId": 1
}
]
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+62 -0
{
"creator": "Open Anonymity",
"projectsForTvs": [
{
"projectId": "zkapi",
"sinceTimestamp": 1790783879
}
],
"proofSystemInfo": "Application-specific request and withdrawal Groth16 circuits over BN254. Poseidon Merkle membership and note-bound Baby-JubJub balance commitments hide the note and balance during authorization. Withdrawals expose the note id and remaining balance.",
"techStack": {
"snark": [
{
"id": "Arkworks",
"type": "Groth16",
"name": "Arkworks",
"description": "Arkworks implementation of Groth16 written in Rust."
},
{
"id": "BN254",
"type": "curve",
"name": "BN254",
"description": "BN254, aka BN256, aka alt_bn128 pairing-friendly 254-bit prime field Weierstrass elliptic curve."
}
]
},
"trustedSetups": [
{
"id": "ZkApiNoteBoundV1",
"name": "zkapi-v2-note-bound-v1 single-party setup",
"proofSystem": {
"id": "Arkworks",
"type": "Groth16",
"name": "Arkworks",
"description": "Arkworks implementation of Groth16 written in Rust."
},
"participantCount": 1,
"risk": "red",
"shortDescription": "The published request and withdrawal keys were generated by one party. Retained setup secrets can allow forged proofs.",
"longDescription": "The request and withdrawal Groth16 keys were generated by one party using operating-system randomness. There was no multi-party ceremony. Security requires that this party erased the setup secrets, otherwise it can forge withdrawal proofs. Matching public artifacts cannot prove erasure. [Setup implementation](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/compact.rs#L288-L383), [provenance](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/setup/v2/README.md)."
}
],
"verifierHashes": [
{
"name": "zkapi note-bound request and withdrawal verifier",
"hash": "0xd31acf861825869e6a84b6b0025cb6940077f5981a10e73d2e4e8e5d7417c853",
"sourceLink": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/groth16.rs",
"proofSystem": {
"id": "Arkworks",
"type": "Groth16",
"name": "Arkworks",
"description": "Arkworks implementation of Groth16 written in Rust."
},
"knownDeployments": [
{
"address": "eth:0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1"
}
],
"verificationStatus": "notVerified",
"verificationSteps": "### Contracts and proof artifacts\n\nReviewed [ethereum/zkapi revision](https://github.com/ethereum/zkapi/tree/045b444ea1b52538d1b40273c7cb6ed09468a052), whose vault, adapter and circuits match the deployment revision reported by the production manifest. Compile with the checked-in configuration: solc 0.8.28, optimizer 200, viaIR and Prague.\n\n```bash\ngit clone https://github.com/ethereum/zkapi.git\ncd zkapi\ngit checkout 045b444ea1b52538d1b40273c7cb6ed09468a052\ncd demo/contracts\nforge build --skip test --build-info\ncast tx 0xf753fd93c7e672f7b851f261f600e06930e502bdfd73be29ce8a065643df6985 --json --rpc-url \"$ZKAPI_RESEARCH_RPC_URL\" > vault-tx.json\ncast tx 0x45f2f5259476afd5f64a6a62a36c9012c3a26efcdd35b048a7fac5ae1ba43780 --json --rpc-url \"$ZKAPI_RESEARCH_RPC_URL\" > verifier-tx.json\n```\n\nThe adapter creation bytecode matches exactly, including metadata. The vault matches after linking the discovered Poseidon address. Decode its ten constructor arguments from the remaining transaction input. Sources are verified on Etherscan: [vault](https://etherscan.io/address/0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe#code), [adapter](https://etherscan.io/address/0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1#code), [Poseidon](https://etherscan.io/address/0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f#code).\n\nCompare deployed verification-key coordinates and public inputs against the [published setup artifacts](https://github.com/ethereum/zkapi/tree/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/setup/v2). Proving-key hashes match SDK and production pins. Circuit source is public, but rerunning the single-party setup with OS randomness generates different keys. Artifact matching does not independently establish circuit-to-key correspondence or setup-secret erasure.\n\n### Frontend and local client\n\nThe team offers the full [browser frontend with localhost build/run instructions](https://github.com/OpenAnonymity/oa-chat/blob/60ed5be2957d984c70a7299474927becec47b61f/README.md#quick-start) and a released [local API daemon for Open WebUI and compatible clients](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/README.md). Local clients still depend on remote authorization and inference services.\n\n### TEE verifier\n\nTODO"
}
]
}
+1 -0
null
+376 -0
{
"adversaries": {
"promise": {
"protects": "linkage",
"text": "Inference request proofs hide the paying Ethereum deposit. Deposits and withdrawals remain publicly linked by note id. The issuer knows the API credential, and the inference provider receives it along with prompts and responses."
},
"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 an API authorization can be tied to the deposit funding it."
}
],
"cells": {
"publicObserver": {
"sentiment": "good",
"exposure": "Ordinary API authorizations stay offchain and hide the paying note. Deposits publish the funder, amount and note id; withdrawals publish the same note id, payout address and remaining balance, revealing total consumption. An escape challenge publishes a request proof linked to its funding note.",
"advice": "Treat funding, payouts and total consumption as public. Use addresses you accept being linked. An escape dispute also reveals an authorization.",
"interior": {
"sender": "exposed",
"recipient": {
"verdict": "exposed",
"note": "Payments are for the configured service operator."
},
"amount": {
"verdict": "private",
"note": "Individual request charges stay offchain. A closed note reveals its total consumed amount."
},
"asset": "exposed",
"linkage": {
"verdict": "private",
"note": "Request proofs hide the funding note. Publishing a challenge reveals its deposit-to-authorization link."
}
},
"sources": [
{
"contract": "ZkApiVault"
},
{
"contract": "Groth16ProofAdapter"
},
{
"title": "Request circuit hides note id, balance and state signature",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/groth16.rs#L368-L485"
}
],
"id": "publicObserver",
"value": "Link private",
"alsoExposed": []
},
"chainAnalyst": {
"sentiment": "warning",
"exposure": "The anonymity set is small, limited to zkapi users. Only eligible zkAPI notes can fund an authorization. If an analyst obtains its proof, timing and spending bounds can narrow the candidates to one deposit. An escape challenge directly identifies the funding note. Deposits and withdrawals are publicly linked.",
"advice": "Check that other active notes could cover your request budget. Wait before using a new deposit. A fresh payout address still remains linked to the deposit.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": "atRisk",
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "The eligible set may contain only one plausible note."
}
},
"sources": [
{
"contract": "ZkApiVault"
},
{
"title": "Authorization checks expiry, balance bound and active-note membership",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/protocol/rust/crates/zkapi-proof/src/groth16.rs#L388-L437"
}
],
"id": "chainAnalyst",
"value": "Link at risk",
"alsoExposed": [
{
"field": "amount",
"exposure": "atRisk"
}
]
},
"networkObserver": {
"sentiment": "warning",
"exposure": "Tor hides the source IP, but shared circuits and timing can still link deposits to API authorizations. The browser app derives note paths from a common snapshot. The local daemon (zkapi-clientd) queries by public note id before authorization; indexing and authorization share a hostname. Its Tor setting covers the Rust wallet companion, but neither client isolates unrelated operations onto separate Tor circuits or obscures request timing.",
"advice": "Prefer the browser app, route the app and wallet broadcasts through Tor, and wait before using a new deposit. For zkapi-clientd, configure a local Tor SOCKS proxy and disable key reuse. These settings do not provide circuit isolation or prevent timing correlation. Using your own Ethereum node requires a reviewed deployment profile and matching manifest.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Request budgets, settlement responses and provider usage reach their respective services over TLS."
},
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Note-id queries, shared Tor circuits and timing can link authorizations to deposits. Browser snapshots avoid disclosing the queried note id, but do not remove timing correlation."
}
},
"sources": [
{
"title": "Browser derives note paths from the common snapshot",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/sdk/services/browserWalletRuntime.js#L829-L845"
},
{
"title": "Local daemon queries the indexer by public note id",
"url": "https://github.com/ethereum/zkapi/blob/20aa542ae98e767c0507133fd34b12a56f5ccd3d/crates/zkapi-clientd/src/indexer.rs#L71-L80"
},
{
"title": "Indexer and authorization service share a hostname",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/internal/zkapi/deployments/mainnet.json#L20-L22"
},
{
"title": "SOCKS transport supplies no Tor isolation credentials",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/internal/relay/socks5.go#L53-L59"
},
{
"title": "Companion inherits the configured local CONNECT proxy",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/internal/zkapi/companion.go#L109-L127"
},
{
"title": "CONNECT bridge uses the same SOCKS or Wisp route as Go",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/internal/relay/connect.go#L18-L44"
},
{
"title": "Browser fetches its snapshot and quote before proving",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/sdk/services/browserWalletRuntime.js#L1624-L1659"
}
],
"id": "networkObserver",
"value": "Link at risk",
"alsoExposed": [
{
"field": "amount",
"exposure": "atRisk"
}
]
},
"privilegedInsider": {
"sentiment": "bad",
"exposure": "During an escape withdrawal, the zkAPI operator can publish a request proof linked to it. It also receives authorization timing, budgets and billed usage, and can time-correlate the local daemon's note-id queries with requests. The issuer knows the API credential. The provider sees prompts and responses and can link requests sharing it. Hosted wallet code can read secrets or redirect payments.",
"advice": "Run inspected local code and avoid identifying prompts. Prefer the browser's common snapshots. For zkapi-clientd, set --key-reuse-window-seconds 0. These steps do not prevent the operator from linking an authorization to its note through an escape challenge.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "exposed",
"note": "The operator and provider know individual billed usage even though the remaining note balance is hidden."
},
"asset": "exposed",
"linkage": {
"verdict": "exposed",
"note": "An operator can publish an escape challenge linking its request proof to the public funding note."
}
},
"sources": [
{
"title": "Operator signs balance updates and persists request transcripts",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/crates/zkapi-serverd/src/processor_v2.rs#L1267-L1381"
},
{
"title": "Local daemon fetches the note path before authorization",
"url": "https://github.com/ethereum/zkapi/blob/20aa542ae98e767c0507133fd34b12a56f5ccd3d/crates/zkapi-clientd/src/service.rs#L1648-L1685"
},
{
"title": "Provider access reuse and plaintext boundaries",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/zkapi-clientd/docs/PRIVACY.md"
},
{
"title": "Wallet secrets and recovery state are stored locally",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/sdk/services/browserWalletStore.js"
},
{
"contract": "ZkApiVault",
"title": "Challenge calldata links a submitted authorization to its note"
},
{
"section": "permissions"
}
],
"id": "privilegedInsider",
"value": "Link exposed",
"alsoExposed": [
{
"field": "amount",
"exposure": "exposed"
}
]
},
"futureAdversary": {
"sentiment": "warning",
"exposure": "Deposit and payout links, note totals and published challenges are permanent. Retained service logs can connect note-id queries, credentials, prompts and timing. Quantum attacks threaten signatures and proof soundness, but do not by themselves reveal hashed note secrets or uniquely open blinded balances.",
"advice": "Treat public chain data as permanent and assume providers may retain what you send. Protect local secrets and avoid identifying prompts.",
"interior": {
"sender": "exposed",
"recipient": "exposed",
"amount": {
"verdict": "atRisk",
"note": "Per-request charges require retained offchain records. Note-level totals are already public."
},
"asset": "exposed",
"linkage": {
"verdict": "atRisk",
"note": "Retained indexer and service logs can connect authorizations to public deposits."
}
},
"sources": [
{
"contract": "ZkApiVault"
},
{
"title": "Blinded note-bound commitments and cryptographic assumptions",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/docs/note-bound-commitments.md"
},
{
"title": "Durable operator transcripts",
"url": "https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/crates/zkapi-serverd/src/nullifier_store.rs"
}
],
"id": "futureAdversary",
"value": "Link at risk",
"alsoExposed": [
{
"field": "amount",
"exposure": "atRisk"
}
]
}
}
},
"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": "anonymousAccess",
"label": "Anonymous access",
"description": "Proves eligibility to use a service without revealing which account or deposit grants access."
},
"exitWindow": {
"value": "None",
"sentiment": "bad",
"orderHint": 0,
"description": "The owner can immediately pause cooperative withdrawals and new escapes. An already pending escape can be finalized after 1 day, including during a pause. An active note becomes sweepable after its deposit expiry.",
"walkawayTest": {
"passed": false,
"reason": "An unpaused vault and the latest usable local note state are needed to start an escape. A submitted request proof can cancel an escape from the same state, even without evidence of service acceptance or delivery. The operator must return a signed successor state to recover normal use. API access itself requires the operator, credential issuer and inference provider."
}
},
"reproducibility": {
"value": "Partially reproducible",
"sentiment": "warning",
"description": "The browser frontend and local API daemon have public source and local run instructions. Production frontend, client, credential issuer and TEE builds have not been independently reproduced. Circuit source is public, but rerunning the single-party setup produces different keys. Published proof artifacts match the deployed verifier."
},
"riskSummary": "## Funds can be stolen if\n\n1. the owner blocks withdrawals before expiry and redirects the full expired deposits to its treasury.\n2. retained setup secrets or a circuit/verifier flaw allow forged withdrawal proofs.\n3. malicious wallet code steals note secrets or redirects payments.\n\n
\n\n## Funds can be lost or frozen if\n\n1. note secrets, settlement state or operator signing keys are lost. The signing keys cannot rotate.\n2. the operator withholds a successor signature after a request proof is submitted and used to challenge an escape.\n3. active notes expire, withdrawals are paused or a destination/treasury rejects ETH.\n\n
\n\n## Privacy can be lost if\n\n1. note-id queries, shared Tor circuits, rpc fallback, timing or budgets identify the paying deposit.\n2. providers identify users from prompts or link requests sharing credentials. Verifier approval does not prove absence of provider logging.\n3. challenges publish authorizations, linking their nullifiers to deposits.\n4. hosted code or another process reads local secrets and chats.",
"tokens": [
{
"token": {
"address": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
"symbol": "ETH",
"decimals": 18,
"priceId": "ethereum",
"sinceTimestamp": 1790783879
},
"buckets": [
{
"id": "zkapi-ETH",
"type": "pool",
"label": "ETH API balances",
"address": "eth:0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe",
"sinceTimestamp": 1790783879,
"deposit": {
"event": "0x7c83dba8534bea9e30d6444f9ca6462dc906897f9938d220dbbe4358c1f7a063",
"extractor": "zkApiDeposit",
"params": {
"weiPerUnit": "1000000000"
}
},
"withdrawal": {
"event": "0x1f43fa4711ca18e1d26398f26bf598bd3a62992cdd0e84f055f2bb506e9d7031",
"extractor": "zkApiWithdrawal",
"params": {
"weiPerUnit": "1000000000"
}
},
"additionalWithdrawals": [
{
"event": "0x163f2e46c4004f0ed9682e2db8c84efac31310720266b17ea9904ba348c26504",
"extractor": "zkApiWithdrawal",
"params": {
"weiPerUnit": "1000000000"
}
}
]
}
]
}
],
"trackedOn": [
"ethereum"
],
"upgradesAndGovernance": {
"content": "The vault code, verifier, signing keys and exit parameters are immutable.\n\nThe owner can change the treasury, pause deposits and new withdrawals, and transfer or renounce ownership without a notice period. Pending escape finalizations, challenges and expiry sweeps continue while paused. Pausing until expiry lets the owner redirect entire active deposits to its treasury. [Verified vault source](https://etherscan.io/address/0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe#code).\n\nThe offchain operator controls credential issuance, settlement signatures and cooperative withdrawal clearance. Its fixed signing keys have no publicly established mapping to the owner's Ethereum account. Missing keys or settlement evidence cannot be replaced in this vault.\n\nOpen Anonymity controls the hosted frontend, which can read local secrets and chats after an update. Inspected local builds still depend on operator signatures, credential issuance, verifier and inference availability. The public SDK and hosted app pin different verifier endpoints. [SDK profile](https://github.com/ethereum/zkapi/blob/045b444ea1b52538d1b40273c7cb6ed09468a052/sdk/assets/config/mainnet.json), [production profile](https://chat.openanonymity.ai/zkapi/browser-config.json)."
}
}
+1 -0
null
+1 -0
null
+1 -0
null
+4 -0
{
"associatedTokens": [],
"warnings": []
}
+21 -0
[
{
"mode": "auto",
"id": "zkapi-ETH",
"priceId": "ethereum",
"symbol": "ETH",
"name": "Ether",
"iconUrl": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1696501628",
"amount": {
"type": "balanceOfEscrow",
"chain": "ethereum",
"sinceTimestamp": 1790783879,
"address": "native",
"decimals": 18,
"escrowAddress": "0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe"
},
"category": "ether",
"source": "canonical",
"isAssociated": false
}
]
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+1 -0
null
+24 -0
{
"ethereum": {
"roles": [],
"actors": [
{
"id": "EOA-1",
"name": "EOA 1",
"accounts": [
{
"address": "eth:0x83f7eA47811A288f2052Bc31aaAE0803c761D156",
"type": "EOA",
"isVerified": true,
"name": "0x83f7…D156",
"url": "https://etherscan.io/address/0x83f7eA47811A288f2052Bc31aaAE0803c761D156",
"displayName": "EOA 1"
}
],
"chain": "ethereum",
"description": "* Can interact with ZkApiVault\n * pause and unpause deposits, cooperative withdrawals and new escape withdrawals, and change the treasury receiving consumed balances and expired notes. Pausing can prevent users from exiting before their notes become sweepable ",
"discoveryDrivenData": true
}
]
}
}
+55 -0
{
"addresses": {
"ethereum": [
{
"name": "ZkApiVault",
"isVerified": true,
"address": "eth:0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Escrows native ETH for anonymous API authorizations. Each deposit creates a publicly identified note. A Groth16 proof closes it with operator clearance or starts a delayed escape withdrawal that can be challenged with a valid request proof sharing its nullifier. The challenge does not prove service acceptance or delivery. Closures reveal the original note id, payout address and remaining balance, and send the consumed balance to the treasury. Anyone can sweep an expired active note entirely to the treasury. Pausing blocks deposits, cooperative closes and new escapes, while challenges, escape finalization and expiry sweeps remain available.\n* Roles:\n * **owner**: EOA 1",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe#code"
},
{
"name": "Groth16ProofAdapter",
"isVerified": true,
"address": "eth:0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "BN254 Groth16 verifier containing the request and withdrawal verification keys for zkapi-v2-note-bound-v1. Request proofs hide the note identity, exact balance and state signature. Withdrawal proofs expose the note id, destination, final balance and nullifier. The published circuit-specific keys were generated in a single-party setup. Retained setup secrets can allow forged proofs.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1#code"
},
{
"name": "Bn254Poseidon",
"isVerified": true,
"address": "eth:0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f",
"upgradeability": {
"proxyType": "immutable",
"admins": [],
"implementations": [],
"immutable": true
},
"chain": "ethereum",
"description": "Linked Poseidon hash library used by the vault for active-note leaves and Merkle updates. Its BN254 parameters must agree with the request and withdrawal circuits.\n",
"discoveryDrivenData": true,
"url": "https://etherscan.io/address/0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f#code"
}
]
},
"risks": [],
"zkVerifiers": [
"eth:0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1"
]
}
+7 -0
{
"baseTimestamp": 1790944823,
"contractsDiscoDriven": false,
"hasDiscoUi": false,
"isDiscoDriven": false,
"permissionsDiscoDriven": false
}
+16 -0
[
{
"id": "193c1624",
"date": "Fri, 02 Oct 2026 12:42:14 GMT",
"timestamp": 1790944823,
"description": "Initial discovery of the active native ETH vault, its Groth16 proof adapter and linked Poseidon library. All three contracts were verified from public source. The owner can pause new exits and change the treasury.",
"isHighSeverity": false,
"changeCount": 3,
"sections": [
{
"kind": "initial-discovery",
"body": "```diff\n+ Status: CREATED\n contract ZkApiVault (eth:0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe) [zkapi/ZkApiVault]\n +++ description: Escrows native ETH for anonymous API authorizations. Each deposit creates a publicly identified note. A Groth16 proof closes it with operator clearance or starts a delayed escape withdrawal that can be challenged with a valid request proof sharing its nullifier. The challenge does not prove service acceptance or delivery. Closures reveal the original note id, payout address and remaining balance, and send the consumed balance to the treasury. Anyone can sweep an expired active note entirely to the treasury. Pausing blocks deposits, cooperative closes and new escapes, while challenges, escape finalization and expiry sweeps remain available.\n```\n\n```diff\n+ Status: CREATED\n contract Groth16ProofAdapter (eth:0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1) [zkapi/Groth16ProofAdapter]\n +++ description: BN254 Groth16 verifier containing the request and withdrawal verification keys for zkapi-v2-note-bound-v1. Request proofs hide the note identity, exact balance and state signature. Withdrawal proofs expose the note id, destination, final balance and nullifier. The published circuit-specific keys were generated in a single-party setup. Retained setup secrets can allow forged proofs.\n```\n\n```diff\n+ Status: CREATED\n contract Bn254Poseidon (eth:0xc6B55e86668d8c446B3D81273AAb9CBb20F28c7f) [zkapi/Bn254Poseidon]\n +++ description: Linked Poseidon hash library used by the vault for active-note leaves and Merkle updates. Its BN254 parameters must agree with the request and withdrawal circuits.\n```"
}
]
}
]
+26 -0
{
"changes": [],
"contracts": [
{
"name": "ZkApiVault",
"address": "eth:0x4386FDbdA35D995beB3BF8625118Ec5982ec81fe",
"isVerified": true,
"ossifyingSince": 1790783879,
"codeChangeCount": 0,
"stateChangeCount": 0
},
{
"name": "Groth16ProofAdapter",
"address": "eth:0x8e92013Dd7cc86f75b539DBD3814B2e603e0F9C1",
"isVerified": true,
"ossifyingSince": 1790782859,
"codeChangeCount": 0,
"stateChangeCount": 0
}
],
"observedSince": 1790782859,
"resets": [
1790782859,
1790783879
]
}
+1 -0
null
+1 -0
null