{
  "markdown": "# x402-rpc-gateway\n\nA Cloudflare Worker that sells JSON-RPC access to your own Ethereum node, one request\nat a time, over [x402](https://docs.x402.org). No accounts, no API keys, no invoices:\na caller signs a USDC authorization on Base and gets the answer in the same exchange.\n\nRunning in production at [402rpc.com](https://402rpc.com).\n\n## Why this exists\n\nSelling RPC access normally means building signup, billing, key rotation and rate\nplans before you serve a single request. x402 removes all of it — but a naive gateway\nthat simply charges per call creates two new problems, and this template solves both.\n\n**Callers must never pay for an answer you cannot give.** Every request is checked\n*before* the paywall. A blocked namespace, a block below your history floor, a log\nrange that is too wide — all return HTTP 400, and x402 does not settle on a response\nof 400 or higher. Node failures return 502 for the same reason.\n\n**Flat pricing loses money.** `eth_getLogs` across ten thousand blocks costs your node\nhundreds of times more than `eth_blockNumber`. Prices here scale with the work a method\nactually costs.\n\n**Your node's own refusals arrive as HTTP 200.** A node that says \"no state available\nat that block\" or \"too many logs in one response\" returns a JSON-RPC error with a 200\nstatus — and x402 settles on a 200. Left alone, your customers pay for answers you\ncould not give. This template translates that class of error into a 4xx so nothing\nsettles. Errors that are a real answer, `execution reverted` for instance, stay\nchargeable.\n\n## What you get\n\n- Per-method pricing, configurable, with a ceiling so a batch never surprises anyone\n- A policy layer that refuses before charging, with reasons a caller can act on\n- Raw transactions relayed through public RPCs instead of your own node — see Privacy\n- Discovery metadata so agents can find and use the service without a human\n- A landing page for humans and JSON for machines, chosen by the `Accept` header\n- An MCP server, so any agent can use it as a tool\n- A fallback example: one line to add your gateway as a paid standby behind a primary\n\n## Setup\n\n```\nnpm install\ncp wrangler.jsonc.example wrangler.jsonc   # edit the vars\nnpx wrangler deploy\n```\n\nAt minimum, set `PAY_TO_ADDRESS`, `ORIGIN_URL` and `PUBLIC_URL`. Everything else has\na working default.\n\nFor a mainnet network you need Coinbase CDP credentials — the public x402.org\nfacilitator serves **testnets only**:\n\n```\nnpx wrangler secret put CDP_API_KEY_ID\nnpx wrangler secret put CDP_API_KEY_SECRET\n```\n\nWithout them the Worker deliberately answers 503 rather than issuing payment\nchallenges it cannot settle.\n\n### Three things the documentation will not tell you\n\n**The facilitator has an undocumented floor.** Amounts around $0.0002 are rejected with\n`amount_too_low`; $0.001 goes through. Price differentiation therefore only works\nupward from roughly a tenth of a cent.\n\n**The Bazaar only indexes x402 version 2.** Every one of the twenty resources I\nchecked runs v2; none run v1. If you build on the v1 packages (`x402-hono`,\n`x402-fetch` — last published April 2026) your service works but is invisible to the\ndiscovery layer agents actually query. This template uses v2 throughout.\n\n**Pin `resource` to your public URL.** Left to derive it from the request, the\nmiddleware puts your Worker's internal hostname — which contains your Cloudflare\naccount name — into every payment challenge, right next to your receiving address.\n\n## Verify your own claims\n\nEverything this template puts in your payment terms and landing page is a promise to a\npaying stranger. Test them against what your node actually does, not against what you\nbelieve it does. Three that bit us:\n\n- \"History from block N onward\" — check block N and N-1.\n- \"Historical state calls are refused, not charged\" — a node's own refusal arrives as\n  HTTP 200, so without translation it settles. See `nodeRefusal` in `policy.ts`.\n- \"eth_getLogs up to N blocks\" — your client has its own cap on logs per response\n  (Nethermind defaults to 20,000), which bites long before your block limit does.\n\n## Getting found\n\nRanking in the Bazaar is driven by measured usage — `l30DaysTotalCalls` and\n`l30DaysUniquePayers` — not by how well you write your description. Worth knowing\nbefore you spend an afternoon on copy.\n\nWorth knowing too: as of August 2026 the entire visible Bazaar handled roughly 1,200\npaid calls in thirty days across eighty services. The busiest Ethereum RPC among them\nserved fourteen. Build for the market this becomes, not the one it is.\n\nWhat this template does for you anyway: a `bazaar` discovery extension so you are\nindexed at all, an `iconUrl`, and payment accepted on Base, Polygon and Arbitrum so\nmore wallets can reach you. What it deliberately does not do is inflate your own call\ncount — that buys a position in a list nobody reads yet, with your own money.\n\n## Protecting your origin\n\nSet `ORIGIN_TOKEN` as a secret and have your origin require the matching\n`X-Origin-Token` header. Without it, anyone who learns your origin hostname uses your\nnode for free and the paywall is decorative. A minimal reference proxy is in\n`examples/origin-proxy.py`.\n\n## Privacy\n\nThe receiving address is necessarily public: it is in every payment challenge. Your\nnode's IP is also public, because it takes part in the peer-to-peer network. The link\nbetween the two is what you protect.\n\nSomeone who submits a transaction through your gateway and watches which IP broadcasts\nit first learns where your service physically runs. Setting `RELAY_URLS` sends raw\ntransactions out through public endpoints instead, and that trail disappears. Every\nother method only returns answers and reveals nothing.\n\n## Client usage\n\nAny x402 client pays and retries automatically:\n\n```js\nimport { wrapFetchWithPayment, x402Client } from \"@x402/fetch\";\nimport { ExactEvmScheme } from \"@x402/evm\";\n\nconst x402 = new x402Client().register(\"eip155:8453\", new ExactEvmScheme(account));\nconst fetchWithPay = wrapFetchWithPayment(fetch, x402);\nawait fetchWithPay(\"https://your-gateway/rpc\", { method: \"POST\", body });\n```\n\nAs a paid fallback behind your own node — see `examples/fallback.mjs`:\n\n```js\ntransport: fallback([\n  http(YOUR_OWN_RPC),\n  http(\"https://your-gateway/rpc\", { fetchFn: fetchWithPay }),\n], { retryCount: 0 })\n```\n\nA standby you never use costs nothing. That is the property a subscription cannot\noffer, and it is the strongest reason to run one of these.\n\n## Licence\n\nMIT.\n",
  "bytes": 6478,
  "sha": "86b177e4677bda7c56d5a274fa77335e79634cdd4ed0a3b11ad784b85533eca0",
  "repo_slug": "ffboers/x402-rpc-gateway",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_ffboers_x402_rpc_gateway_362ead63/readme"
}