How zBase works (diagrams)¶
The whole system in pictures. If you read nothing else, read this page.
The one-picture version¶
Your agent pays an API. Normally that payment is a public receipt linking your wallet → the API. zBase breaks that link: you deposit once into a shared pool, and every payment is funded through a fresh single-use wallet drawn from the pool with a zero-knowledge proof. The seller is paid normally and can't tell zBase was involved.
flowchart LR
A[Your wallet] -->|deposit USDC once| P[(Privacy Pool<br/>many depositors)]
P -.->|ZK withdrawal<br/>no link to you| E[Fresh single-use<br/>payer wallet]
E -->|standard x402 payment| S[Seller API]
S -->|data 200| G[Your agent]
style P fill:#e7eef8,stroke:#365f9f,color:#18191b
style E fill:#eef1f4,stroke:#9aa0a6,color:#18191b
The public chain sees "a deposit" and, later, "some fresh wallet paid a seller." It can't join the two — the ZK proof hides which deposit funded the payer, and the anonymity set is the crowd you hide in.
A payment, step by step¶
sequenceDiagram
participant Agent
participant zBase as zBase facilitator
participant Pool as Privacy Pool (chain)
participant Seller as Seller (CDP)
Agent->>Seller: GET /endpoint (no payment)
Seller-->>Agent: 402 Payment Required (accepts: exact, $X USDC)
Agent->>zBase: settle-x402 (note + change-note secrets)
Note over zBase: build + sign EIP-3009 header<br/>for a deterministic payer EOA
zBase->>Pool: reserve nullifier, then ZK withdraw $X → payer EOA
Pool-->>zBase: funded (payer holds $X)
Note over zBase: finalize (store header) — idempotent on the note
zBase-->>Agent: settled: true + X-PAYMENT header + change note
Agent->>Seller: GET /endpoint + X-PAYMENT
Seller-->>Agent: 200 + data
The note is spent once. If the response is lost, a retry replays the same payment (idempotent on the note) instead of paying twice — see the tri-state below.
The two gates (why privacy + compliance coexist)¶
zBase runs two independent gates. Break one and a payment still isn't unmasked.
flowchart TB
D[Deposit] --> G1{Gate 1: ASP<br/>who MAY withdraw}
G1 -->|label in approved set<br/>excludes known-illicit| W[ZK withdrawal]
W --> G2{Gate 2: Stealth<br/>who RECEIVES}
G2 -->|fresh address<br/>only the payee can find| R[Seller paid]
style G1 fill:#e4f0e6,stroke:#4a7a52,color:#18191b
style G2 fill:#e5edf7,stroke:#365f9f,color:#18191b
Gate 1 (ASP / Association Sets) proves your funds are not in the set of known-illicit deposits — compliance without revealing which deposit is yours. Gate 2 (stealth addresses) hides the recipient. This is why zBase is a Privacy Pool, not a blind mixer.
The result you must handle (money-safety)¶
Every payment resolves to exactly one of three states. Never treat "unknown" as "unspent."
flowchart TD
Start[payAndFetch] --> Q{settled?}
Q -->|delivered: paid=true, 2xx| OK[Use the data<br/>persist change note]
Q -->|throws: PROVEN unspent| RETRY[Note untouched —<br/>safe to retry same note]
Q -->|uncertain: paid=false, uncertain=true| UNC[Note MAY be spent —<br/>retry THIS call idempotently,<br/>never pay from another note]
style OK fill:#e4f0e6,stroke:#4a7a52,color:#18191b
style RETRY fill:#e5edf7,stroke:#365f9f,color:#18191b
style UNC fill:#f6efe0,stroke:#b08a3e,color:#18191b
Full contract: Developer Guides.
Where this sits vs the alternatives¶
flowchart LR
subgraph zBase
Z1[Pooled ZK anonymity set] --- Z2[ASP compliance] --- Z3[standard x402 seller]
end
subgraph Others
O1[CDP: public link]
O2[Dexter Shield: 1-hop forward, timing-linkable]
O3[Mixer: unlinkable but no compliance]
end
See How zBase compares for the full breakdown, including Dexter Shield.