x402-wrappy-sdk
v1.0.1
Published
This repository contains a self-hosted x402 gateway/proxy, a facilitator that verifies and settles EIP-3009 payments, a browser demo, and (new) an SDK that automates the full client workflow (EIP‑712 payload generation → signing → base64 header constructi
Readme
Encode October Hack – x402 Gateway & SDK
This repository contains a self-hosted x402 gateway/proxy, a facilitator that verifies and settles EIP-3009 payments, a browser demo, and (new) an SDK that automates the full client workflow (EIP‑712 payload generation → signing → base64 header construction → paid request execution).
Current Architecture
- Proxy (
src/proxy/server.ts) wraps any upstream REST API. It inspects requests, issues HTTP 402 offers, verifiesX-Token/X-Paymentheaders via the facilitator, and forwards traffic to the upstream server on success. - Facilitator (
src/facilitator) validates TransferWithAuthorization signatures against the TestUSDC token on Arbitrum Sepolia, settles transfers on-chain, and responds back to the proxy with settlement receipts. - Shared primitives (
src/shared/payments.ts) capture x402 offer shapes, EIP-3009 payloads, and supported networks/schemes. - Frontend example (
frontend-example/) shows the manual flow with MetaMask: fetch → receive 402 → call_signTypedData→ resend withX-Token.
With the latest changes, the system now exposes pricing metadata via x402-api.json, and ships a TypeScript SDK under src/sdk so client developers can integrate the payment flow with a few method calls.
SDK Overview (src/sdk)
import {
createX402Sdk,
signEIP712PayloadPrivateKey,
base64Payload,
payPerRequest,
} from './dist/sdk';
const sdk = createX402Sdk({
docs: '/x402-api.json', // can also be a JS object or async loader
fetch: globalThis.fetch,
walletSigner, // optional – ethers.js signer with _signTypedData
});
const payload = await sdk.generateEIP712Payload('https://proxy.dev/paid-resource');
const signature = await sdk.signEIP712PayloadWallet(payload); // or use signEIP712PayloadPrivateKey
const { hash } = sdk.encodePaymentHeader(payload, signature);
const response = await sdk.payPerRequest(hash, 'https://proxy.dev/paid-resource', { parseAs: 'json' });Exported building blocks
generateEIP712Payload(endpointURL)– fetches the x402 error, combines it withx402-api.jsonpricing, and returns{ domain, types, message }for TransferWithAuthorization. It enforces our canonical domain (TestUSDC on Arbitrum Sepolia), max timeout, and nonce capture from the 402 response so end-users can review the quote before signing.signEIP712PayloadWallet(payload, signer?)– requests a wallet signature (MetaMask/ethers_signTypedData) and returns the 65-byte hex string. The SDK can cache a signer so callers can simply runsdk.signEIP712PayloadWallet(payload).signEIP712PayloadPrivateKey(privateKey, payload)– server-side helper for clients that own a hot wallet/private key and want unattended signing (e.g., arbitrage/data scraping bots).base64Payload(payload, signature)– wraps{ x402Version: 1, scheme, network, payload: { ...message, signature } }and returns the base64 blob expected byX-Token/X-Payment.payPerRequest(base64Hash, endpointURL, requestInit?)– performs the paid request, attaches the encoded hash to both headers, and returns{ ok, status, headers, data, response }.
Additionally, createX402Sdk wires those steps together with shared configuration (docs source, wallet resolver, default validity seconds, custom fetch). For advanced usage you can also import the low-level helpers directly from src/sdk/index.ts.
x402-api.json
Clients need prepaid knowledge of endpoint pricing. x402-api.json mirrors the /docs output but adds fees (or null when free):
{
"network": "arbitrum-sepolia",
"token": { "name": "TestUSDC", "version": "1", "address": "0xf4983A..." },
"defaults": { "payTo": "0x53CA...", "price": null, "maxTimeoutSeconds": 600 },
"endpoints": [
{ "method": "GET", "path": "/hello", "price": null },
{ "method": "GET", "path": "/paid-resource", "price": "1000000" }
]
}The SDK ships X402DocsRepository to load and query this structure (from a file path, HTTP URL, or inline JSON).
/docs interactive flow
Once you price endpoints in /conf, the proxy automatically emits x402-api.json and exposes /docs:
- The page lists every paid endpoint with a five-step playground (unauthorized call → generate payload → wallet sign → base64 encoding → paid request) alongside a full Swagger UI explorer for the same OpenAPI spec.
- Each button hits the real SDK helpers on the backend (
generateEIP712Payload,base64Payload,payPerRequest), so it doubles as a live spec and debugging tool. - The wallet step uses MetaMask via
_signTypedData, then pipes the signature back into the SDK for encoding and the final paid request. - Need the manifest programmatically? Hit
/x402-docs?format=jsonfor the latest prices.
Next Steps
pnpm installpnpm dev:gateway– boots the proxy onlocalhost:4000.pnpm tsx src/facilitator/server.ts– starts the facilitator onlocalhost:3002.- Build the SDK with
pnpm buildand consumedist/sdkfrom your client app or bundle it via your preferred tooling.
The starter frontend (frontend-example) still demonstrates the manual MetaMask flow for clarity, but new integrations should rely on the SDK to stay consistent with the protocol defaults and reduce implementation errors.
x402-wrappy-sdk
Running docker image
Provide the .env file in the following format TODO: format
An example .env file:
ARBITRUM_SEPOLIA_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc QUOTE_SERVICE_PRIVATE_KEY=0xaf127dbb5eb5b6a0584666c2be287a9d9f93bdaa4e94f85e692a966f21938e94 ENABLE_SETTLEMENT=false # Set to 'true' to execute payments on-chain
X402_DEFAULT_NETWORK=arbitrum-sepolia
X402_FACILITATOR_URL=http://localhost:3002 X402_DEFAULT_UPSTREAM=http://localhost:8080
X402_DEFAULT_TIMEOUT_SECONDS=600
X402_CONFIG_PATH=/specs/config.json X402_OPENAPI_PATH=/specs/openapi.json
X402_ADMIN_SECRET_PATH= X402_ADMIN_TOKEN=85639bb83fb19c3856eb701a837e951776ff499ec3cb25e6e36716adab245c36
X402_TOKEN_NAME=TestUSDC X402_TOKEN_VERSION=1 X402_CHAIN_ID=421614 X402_TOKEN_ADDRESS=0xf4983A096c2F71f8aFC4D519AB936B35f8e09256 X402_PAYTO_ADDRESS=0x53CA79589Def72F27A332433955Dc8d13f4A8Dc7 FACILITATOR_PORT=3002
Provide directory for configurations This is where config.json will be saved And openapi.json will be read from
Say: ./specs
Final command
docker run --rm
-p 4000:4000
--env-file ./local.env
--add-host=host.docker.internal:host-gateway
-v "./specs:/specs"
--name wrappy-proxy
wrappy:latest
