Search projects, hackathons, sponsors and tech

    ← All projects

    StableCoinManager

    Pragmatic stablecoin payments for AI agents: x402, MCP wallet with code-enforced limits, receipts.

    Demo

    About

    Pragmatic stablecoin payments for AI agents. Plug in one MCP server, and your agent can pay over x402 — with limits enforced in code, and a receipt for every payment. Not a demo of a payment; the parts a company needs before it can let an agent pay. The purchase itself is simple. An x402 service answers with a 402 that names a stablecoin, a network, an amount and a payee. StableCoinManager, an MCP server on Cloudflare Workers, holds the wallet for the agent, reads that 402, checks it, signs an EIP-3009 authorization (no gas needed), retries the request with the payment header and returns the paid response. In the demo, Claude buys ERPC credit with EURC on Base. What makes this usable by a company is everything around the payment. Guardrails in code, not in the prompt. A per-payment ceiling and a daily ceiling, the allowed networks and assets, and an allow-listed payee are enforced by the server. The agent can tighten its own limits at runtime and relax them back up to the deploy-time ceiling, but never above it. A request over a limit is refused before anything is signed; nothing is clamped silently. Comparisons fail closed (an unusable limit refuses, never passes), every money tool is idempotent (a retried key returns the first receipt and signs nothing), and the signature is bound to exactly the requirement that passed the check. In the demo the limit is set to 2 EURC, a 2.42 EURC purchase is refused, and a 1.21 EURC purchase goes through. Receipts, automatically. Every payment is written to a ledger before it is signed and resolved to settled, stuck or failed afterwards, and the agent can read it back. On the ERPC side the same payment produces a VAT invoice (ERPC-2026-000449 in the demo) with the on-chain settlement, downloadable from the dashboard. An agent that pays without receipts is a bookkeeping problem; this one leaves an audit trail on both ends. Everything on-chain travels over the RPC line through erpc-sdk, ERPC's open-source multi-network SDK: no aggregator API, no indexer, no extra keys to leak. Uniswap is the swap venue for agents that hold the wrong asset: routes are quoted, simulated and prepared against Uniswap's on-chain pools over those same RPC calls. This is a Continuity Track project. ERPC (erpc.global) is our live platform for multichain RPC and zero-distance compute: customer servers in the same rack as the nodes, wired over an internal network at 0.1 ms ping, on our own network, AS200261, across 11 locations. Its x402 storefront, invoicing and receipts already exist. The MCP server is the new work: the scaffold, OAuth 2.1 login, x402 payment path, policy and ledger layer, and swap/bridge routing were built in the week before the event (Sep 21-24). Since hacking began (Sep 25, 21:00 JST) we added the login path through our central OIDC broker, Base and EVM balances in holdings via erpc-sdk 0.9.0, the standalone public repository as an erpc-cli template, the Uniswap integration feedback, and the first real payment through it, which is the one in the demo video. x402 for the payment, MCP for the agent, receipts for the books: that is what makes stablecoin payments practical, and practical is how we intend to make them spread.

    How it's made

    One TypeScript codebase on Cloudflare Workers, 13 MCP tools, and a test suite that pins the guardrails. MCP and login. The Worker speaks MCP over Streamable HTTP (Hono). It is its own OAuth 2.1 authorization server: dynamic client registration is allowlisted to the hosted Claude/ChatGPT callbacks and RFC 8252 loopback, PKCE S256 is mandatory, tokens carry an audience pinned to the server's URL, and refresh tokens are single-use and rotated. Identity comes from Google through ERPC's auth-api (or, for third-party deploys, through a small OIDC broker we built this week), and an email allowlist is re-checked on every /mcp call. Tool arguments are zod schemas; the JSON Schema in tools/list is derived from them. Wallet. One 24-word mnemonic in a Workers Secret, re-derived per request, gives an ed25519 Solana key and a secp256k1 EVM key (the same address on Ethereum, Base and Avalanche; MetaMask/Phantom compatible). It is never logged; the one export path needs an explicit confirm, writes an audit row and is rate-limited inside the Durable Object. Payment path. x402_inspect reads the 402 with @x402/core (probing twice to detect volatile fields). x402_pay and erpc_topup run policy → reserve → sign → record: the ledger row is written as pending before anything is signed; the daily total, replay check and insert happen inside one Durable Object (SQLite) method with no await; then the worker signs an EIP-3009 transferWithAuthorization with viem and resends the request with the payment header. A custom requirement selector signs only the option that passed the policy check and re-verifies all five fields (scheme, network, asset, amount, payee). Rows resolve to settled, stuck (a transaction hash came back without confirmation — it still counts toward the ceiling) or failed. Receipts. The ledger is a Durable Object (SQLite): a payment row is written as pending before signing and resolved afterwards, and the receipt and history tools read it back. On ERPC's side, the x402 settlement is matched to a VAT invoice PDF with the transaction hash and a Basescan QR code, listed on the dashboard's crypto payments page, so an agent's purchase is booked like any other. Swaps and bridges over RPC only. plan, swap and bridge go through @elsoul/erpc-sdk: quote → simulate → prepare → allowance for exact-input swaps against Uniswap's USDC/WETH pool on Ethereum (and LFJ on Avalanche), and Mayan for EURC between Ethereum and Solana — all as JSON-RPC calls to ERPC, with no private DEX client. Where the published SDK cannot do something yet (Base balances, a Base pool, a Base bridge leg, Solana quotes) the tool answers unsupported_yet with the exact gap instead of reaching around it, and the swap tool returns the fully prepared route but does not broadcast until the funded production canary has run. Hacky and proud of it: every ceiling check treats an unusable number as a refusal (x > NaN is false in JavaScript, which would otherwise turn a broken limit into no limit); policy_set can only tighten, and the one-way rule is applied on write and again on read; and the dead-code banner in the swap module is pinned by a test that counts its importers, so the commit that wires it in has to come back and delete the banner.