Streaming USDC micropayments for AI agents on Arc — an open-source self-hostable implementation of the Nanopayments pattern.
On 2026-04-29 Circle officially shipped Agent Stack, which includes their own production Nanopayments implementation via Circle Gateway. Cadence is not a competitor — it's the open-source reference implementation of the same architectural pattern, useful when you want:
- Self-hostable infrastructure without relying on Circle's hosted services
- Forkable, modifiable Solidity + TypeScript you can audit and adapt
- Educational reference for understanding how EIP-712 streaming payments work on Arc
- A starting point for protocols that need streaming-payment primitives but with different settlement semantics (see: Crucible, the companion protocol that uses Cadence's pattern as the payment layer below quality-conditional settlement above)
What Cadence is NOT trying to be: the production payment rail for the Circle ecosystem — that's what Circle's official Nanopayments does, and it does it well. Cadence is the open reference.
Arc402 is the underlying open protocol (and the on-chain EIP-712 domain). Cadence is the developer brand around it. The pair lets any API charge per call in USDC with:
- Sub-cent on-chain cost when batched (32-37k gas per claim at n≥10)
- Zero on-chain overhead per request — claims are signed off-chain
- Gas-free agent onboarding via the sponsorship pattern (third party funds agent's escrow)
- Session keys so the agent's master key never touches a hot service
- Composable with Circle's stack: ERC-8183 (discrete jobs), ERC-8004 (agent identity), Agent Stack (wallets + marketplace), and Circle Gateway for outflow (example)
- Native to the Python AI stack via
@cadence-sdk— FastAPIrequire_payment_fastapi+ Flask decorator +settle_batch, 18 pytest tests passing
| Value | |
|---|---|
| PaymentEscrowV2 (current) | 0xc95b1b20f91901206ba3ea94bbc7313e7cd82f8d |
| PaymentEscrowV1 (legacy, historical record) | 0x55aFA5Cf28B98DD6DC550F15c075F46B5eaf2a98 |
| Chain | Arc Testnet (chainId 5042002) |
| Contract tests | 34/34 passing (15 V1 + 15 V2 + 3 invariants × 128k random calls + fuzz + gas curve) |
| V2 EIP-712 domain version | "2" — sigs do not cross-replay with V1 |
| License | MIT |
claimBatch(Claim[])— settle N claims in one tx. Per-claim gas drops from 69k to 32-37k (50%+ savings).depositFor(agent)— third-party sponsorship; agent's escrow can be funded without the agent ever holding gas.authorizeSession(sessionKey, expiry)— agent's master key delegates claim-signing to an ephemeral session key with expiry.- Cross-version EIP-712 isolation — V1 signatures cannot be replayed against V2 (confirmed live in adversarial test #5 below).
| Experiment | Proof |
|---|---|
| V2 deployment | tx 0xed61c372... |
| 20-claim batch settled in one tx (5 agents × 4 claims) | tx 0xce39b45b... — gas 732,180 (36,609/claim) |
| 5/5 adversarial attacks blocked | replay, expired, wrong-service, forged-sig, V1→V2 cross-version — each reverted with correct custom error |
| depositFor sponsorship | agents zero-gas onboarded via main wallet pre-funding their escrow |
| LLM-style paid endpoint (3 agents × 2 calls, batched) | OpenAI-compatible /v1/chat/completions priced at 0.005 USDC/call; 106ms avg latency; 6 claims settled in 0xd93df460... |
| Cross-protocol integration with Crucible (1 routine Cadence call + 1 Crucible-routed quality-attested call, same SERVICE) | Cadence deposit 0xe5d0f7aa... · Crucible openMarket 0x3fa47a01... · Crucible collect (scoreBps=10000) 0x0a1f6ab9... · Cadence settleBatch 0xe5c15625.... Orchestration script: hackathon-submission/integration/cross-protocol.ts (separate repo) |
| Single lifecycle (V1 origin demo) | deposit / 402 / sign / settle — 0x1931d9... |
At Arc's observed gas price of 20 gwei (verified in batch tx above):
| Service price per call | Single-claim margin | Batched margin (n≥10) |
|---|---|---|
| $0.01 (typical SaaS API) | 86% | 93% |
| $0.005 (cheap API) | 72% | 85% |
| $0.002 (low-cost LLM) | 31% | 63% |
| $0.001 (premium nanopayment) | -38% | -27% -- needs state channel / Merkle batching, future work |
Cadence is economically viable for $0.002+ per call when batched. Sub-millicent payments require next-gen settlement (open W3).
git clone https://github.com/Ccheh/arc402.git
cd arc402
git submodule update --init --recursive
# 1. Run all 34 contract tests (V1 + V2 + invariants + fuzz + gas curve)
cd contracts && forge test -vv
# 2. Reproduce the 20-claim batch settlement on live Arc Testnet
# (requires PRIVATE_KEY, SERVICE_PRIVATE_KEY, ESCROW_V2_ADDRESS in .env)
cd ../sdk-ts && npm install
npx tsx examples/stress-batch.ts
# 3. Reproduce 5 adversarial attacks (must all revert)
npx tsx examples/adversarial.ts
# 4. Demo: OpenAI-compatible paid LLM endpoint, 3 agents x 2 calls, batched settle
npx tsx examples/llm-paid-demo.ts┌──────────────┐ ┌────────────────────────────────┐ ┌──────────────┐
│ Agent │ │ Service │ │ PaymentEscrow│
│ (smart acc / │ │ ┌──────────────┐ ┌───────────┐ │ │ V2 contract │
│ EOA + sess.) │──▶│ │ requirePayment│ │ settleBatch│ │──▶│ on Arc │
│ │HTTP middleware │ │ flusher │ │ │ │
│ AgentClient │ │ │ (off-chain │ │ (every │ │ │ - claimBatch │
│ - deposit │ │ │ EIP-712 ver) │ │ 5s) │ │ │ - depositFor │
│ - signClaim │ │ └──────────────┘ └───────────┘ │ │ - sessions │
│ - fetch() │ └────────────────────────────────┘ └──────────────┘
└──────────────┘
Per call: HTTP 402 → sign claim → 200
Server: queue claim → flush in batch when economic
Settle: 1 tx settles N claims → service gets USDC
Cadence and Circle Gateway are complementary, not competing. They split the service-side payment problem at the natural seam: collection vs routing.
Agent ──Cadence──▶ Service wallet (Arc) ──Gateway──▶ Base / ETH / Op / Arb
per-call sigs per-batch USDC cross-chain settlement
(off-chain) (1 tx settles N) (Circle hosted)
| Layer | What it does | Where it runs |
|---|---|---|
| Cadence | Collect per-call EIP-712 claims from agents, batch-settle N claims on Arc in one tx (~32-37k gas/claim) | On-chain, Arc Testnet/Mainnet, self-hosted by the service |
| Circle Gateway | Route the resulting USDC across chains (CCTP), settle to wherever the service treasury lives | Circle hosted infrastructure, requires Circle account |
A working Cadence + Gateway pattern walks through this in
sdk-ts/examples/with-gateway.ts — runs the
Cadence batch settlement live on Arc Testnet, then documents the Gateway
handoff call (which requires a Circle account so we document rather than
execute it).
Key positioning takeaway: a real Cadence deployment ends where Gateway begins. Cadence is the seller-side middleware that produces the USDC stream Gateway routes. Treating them as alternatives is a category error.
| Layer | Tool | Cadence's relationship |
|---|---|---|
| Identity | ERC-8004 | read (planned: surface reputation in middleware) |
| Discrete jobs | ERC-8183 | complementary (large discrete contracts vs continuous stream) |
| Cross-chain | @circle-fin/app-kit |
adapter pattern (planned) |
| Smart accounts | ZeroDev / Pimlico | optional layer on top (protocol session keys built-in) |
| Streaming payments | Circle's official Nanopayments (via Gateway) — production hosted | Cadence (Arc402 protocol) — open self-hostable reference |
See docs/spec.md for full positioning.
Cadence is a portfolio-quality reference implementation, not a production payment rail. Things to know before depending on it:
- No production adopters yet. Every transaction on Arc Testnet was generated by our own scripts. We have not yet seen a third-party service integrate Cadence for real traffic.
- Pre-audit. 34 forge tests + 27 SDK vitest tests pass, 5/5 adversarial scenarios are blocked, and
audits/slither-report.mdreports no high or medium severity findings. No independent security audit has been performed yet — treat as testnet-only. - Circle's official Nanopayments covers the same architectural ground. If you don't need self-hosting or forkable code, prefer the official version — it has Circle's reliability guarantees and direct support.
- Sub-millicent payments not viable at observed Arc gas prices (20 gwei). The $0.001/call price point lands at -38% margin even when batched. Truly nano payments need state channels or Merkle batching — out of scope for this version.
- The "streaming" framing is one-shot escrow + signed claims, not literal per-second streaming. Each claim is an off-chain EIP-712 signature; on-chain settlement is batched. This is correct architecture but worth being precise about.
| Folder | Purpose |
|---|---|
contracts/ |
Solidity — PaymentEscrow.sol (V1) + PaymentEscrowV2.sol (current) + 34 tests |
sdk-ts/ |
TypeScript SDK — requirePayment middleware, AgentClient, settle, settleBatch |
sdk-ts/examples/ |
Live Arc Testnet demos: run.ts, stress-batch.ts, adversarial.ts |
sdk-py/ |
Python SDK — agent (AgentClient) + service-side (FastAPI / Flask middleware, verify_claim, settle_batch). 18 pytest tests passing |
web/ |
Static landing page (deployable to GitHub Pages / Vercel zero-config) |
docs/ |
Protocol spec, positioning |
- W1 ✅ V1 contract + Node SDK + single-claim demo
- W2 ✅ V2 with batched settlement + session keys + 20-claim live test + 5 adversarial proofs
- W3 ✅ vitest SDK test suite (27/27) ✅ LLM-style paid endpoint demo (batched, on-chain settled) ✅ Python SDK MVP (
cadence-sdk) ✅ ERC-8004 IdentityRegistry read (live on Arc Testnet) ✅ Security analysis + audit prep doc ✅ GitHub Actions CI ✅ LangChain integration example · ⏳ formal Arc402 spec polish · independent audit (Grant M2) - W4 Next.js demo marketplace · Circle Developer Grant submission · audit + mainnet deploy plan
Built by Zen Chen — MSc Data Science (Sheffield). Building on Arc.