@circle-fin/x402-batching 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -0
- package/dist/client/index.d.mts +653 -0
- package/dist/client/index.d.ts +653 -0
- package/dist/client/index.js +1313 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +1282 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index.d.mts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +33 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server/index.d.mts +318 -0
- package/dist/server/index.d.ts +318 -0
- package/dist/server/index.js +697 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +668 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/types-DnHgU28a.d.mts +90 -0
- package/dist/types-DnHgU28a.d.ts +90 -0
- package/package.json +95 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server/index.ts","../../src/server/BatchFacilitatorClient.ts","../../src/server/GatewayEvmScheme.ts","../../src/client/GatewayClient.ts","../../src/constants.ts","../../src/detection.ts","../../src/server/middleware.ts"],"sourcesContent":["export { BatchFacilitatorClient } from './BatchFacilitatorClient';\nexport { GatewayEvmScheme } from './GatewayEvmScheme';\nexport { isBatchPayment } from '../detection';\nexport {\n createGatewayMiddleware,\n type GatewayMiddleware,\n type GatewayMiddlewareConfig,\n type PaymentRequest,\n type PaymentResponse,\n} from './middleware';\n","import type { BatchFacilitatorConfig } from '../types';\n\n/**\n * PaymentPayload interface (minimal subset needed).\n */\ninterface PaymentPayload {\n x402Version: number;\n resource?: {\n url: string;\n description: string;\n mimeType: string;\n };\n accepted?: Record<string, unknown>;\n payload: Record<string, unknown>;\n extensions?: Record<string, unknown>;\n}\n\n/**\n * PaymentRequirements interface (minimal subset needed).\n */\ninterface PaymentRequirements {\n scheme: string;\n network: string;\n asset: string;\n amount: string;\n payTo: string;\n maxTimeoutSeconds: number;\n extra?: Record<string, unknown>;\n}\n\n/**\n * VerifyResponse from facilitator.\n */\ninterface VerifyResponse {\n isValid: boolean;\n invalidReason?: string;\n payer?: string;\n}\n\n/**\n * SettleResponse from facilitator.\n */\ninterface SettleResponse {\n success: boolean;\n errorReason?: string;\n payer?: string;\n transaction: string;\n network: string;\n}\n\n/**\n * SupportedKind from facilitator.\n */\ninterface SupportedKind {\n x402Version: number;\n scheme: string;\n network: string;\n extra?: Record<string, unknown>;\n}\n\n/**\n * SupportedResponse from facilitator.\n */\ninterface SupportedResponse {\n kinds: SupportedKind[];\n extensions: string[];\n signers: Record<string, string[]>;\n}\n\n/**\n * FacilitatorClient interface from @x402/core.\n */\ninterface FacilitatorClient {\n verify(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<VerifyResponse>;\n\n settle(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<SettleResponse>;\n\n getSupported(): Promise<SupportedResponse>;\n}\n\n/**\n * Circle Gateway Facilitator Client.\n *\n * A FacilitatorClient implementation that communicates with Circle Gateway's\n * x402 endpoints for verification and settlement of batched payments.\n *\n * The client calls:\n * - `POST /v1/x402/verify` for payment verification\n * - `POST /v1/x402/settle` for payment settlement\n * - `GET /v1/x402/supported` for supported payment kinds\n *\n * @example\n * ```typescript\n * import { x402ResourceServer } from \"@x402/core/server\";\n * import { BatchFacilitatorClient } from \"@circle-fin/x402-batching/server\";\n *\n * const circleClient = new BatchFacilitatorClient({\n * url: \"https://gateway.circle.com\",\n * });\n *\n * const server = new x402ResourceServer([circleClient]);\n * await server.initialize();\n * ```\n */\nexport class BatchFacilitatorClient implements FacilitatorClient {\n readonly url: string;\n private readonly _createAuthHeaders?: BatchFacilitatorConfig['createAuthHeaders'];\n\n /**\n * Creates a new BatchFacilitatorClient.\n *\n * @param config - Configuration including Gateway URL and optional auth headers\n */\n constructor(config: BatchFacilitatorConfig = {}) {\n // Ensure URL doesn't have trailing slash\n this.url = (config.url ?? 'https://gateway-api-testnet.circle.com').replace(\n /\\/$/,\n '',\n );\n this._createAuthHeaders = config.createAuthHeaders;\n }\n\n /**\n * Verify a payment with Circle Gateway.\n *\n * @param paymentPayload - The payment payload to verify\n * @param paymentRequirements - The payment requirements\n * @returns Verification response\n */\n async verify(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n let headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this._createAuthHeaders) {\n const authHeaders = await this._createAuthHeaders();\n headers = { ...headers, ...authHeaders.verify };\n }\n\n const response = await fetch(`${this.url}/v1/x402/verify`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n paymentPayload: this.toJsonSafe(paymentPayload),\n paymentRequirements: this.toJsonSafe(paymentRequirements),\n }),\n });\n\n const data = await response.json();\n\n if (typeof data === 'object' && data !== null && 'isValid' in data) {\n return data as VerifyResponse;\n }\n\n throw new Error(\n `Circle Gateway verify failed (${response.status}): ${JSON.stringify(data)}`,\n );\n }\n\n /**\n * Settle a payment with Circle Gateway.\n *\n * @param paymentPayload - The payment payload to settle\n * @param paymentRequirements - The payment requirements\n * @returns Settlement response\n */\n async settle(\n paymentPayload: PaymentPayload,\n paymentRequirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n let headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this._createAuthHeaders) {\n const authHeaders = await this._createAuthHeaders();\n headers = { ...headers, ...authHeaders.settle };\n }\n\n const response = await fetch(`${this.url}/v1/x402/settle`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n paymentPayload: this.toJsonSafe(paymentPayload),\n paymentRequirements: this.toJsonSafe(paymentRequirements),\n }),\n });\n\n const text = await response.text();\n if (!text) {\n throw new Error(\n `Circle Gateway settle returned empty response (${response.status})`,\n );\n }\n\n const data = JSON.parse(text);\n\n if (typeof data === 'object' && data !== null && 'success' in data) {\n return data as SettleResponse;\n }\n\n throw new Error(`Circle Gateway settle failed (${response.status}): ${text}`);\n }\n\n /**\n * Get supported payment kinds from Circle Gateway.\n *\n * This fetches the supported networks and their GatewayWallet contract addresses.\n * The response includes `extra.verifyingContract` for each supported network.\n *\n * @returns Supported payment kinds and extensions\n */\n async getSupported(): Promise<SupportedResponse> {\n let headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (this._createAuthHeaders) {\n const authHeaders = await this._createAuthHeaders();\n headers = { ...headers, ...authHeaders.supported };\n }\n\n const response = await fetch(`${this.url}/v1/x402/supported`, {\n method: 'GET',\n headers,\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => response.statusText);\n throw new Error(\n `Circle Gateway getSupported failed (${response.status}): ${errorText}`,\n );\n }\n\n return (await response.json()) as SupportedResponse;\n }\n\n /**\n * Helper to convert objects to JSON-safe format.\n * Handles BigInt and other non-JSON types.\n */\n private toJsonSafe(obj: unknown): unknown {\n return JSON.parse(\n JSON.stringify(obj, (_, value) =>\n typeof value === 'bigint' ? value.toString() : value,\n ),\n );\n }\n}\n","import { ExactEvmScheme } from '@x402/evm/exact/server';\nimport type { Network, PaymentRequirements } from '@x402/core/types';\nimport { CHAIN_CONFIGS } from '../client/GatewayClient';\n\n/**\n * Gateway EVM Scheme for x402 resource servers.\n *\n * Extends `ExactEvmScheme` with two Gateway-specific behaviors:\n *\n * 1. **enhancePaymentRequirements** — merges `supportedKind.extra` (e.g.\n * `verifyingContract`) into `paymentRequirements.extra`. The base\n * `ExactEvmScheme` discards this, but Gateway clients need it to\n * construct the correct EIP-712 signing domain.\n *\n * 2. **Money parsers** — registers USDC address mappings for all\n * Gateway-supported networks so `parsePrice(\"$0.01\")` works\n * without manual `registerMoneyParser()` calls.\n *\n * @example\n * ```typescript\n * import { GatewayEvmScheme } from \"@circle-fin/x402-batching/server\";\n * import { x402ResourceServer } from \"@x402/express\";\n *\n * const resourceServer = new x402ResourceServer(facilitators)\n * .register(\"eip155:5042002\", new GatewayEvmScheme());\n * ```\n */\nexport class GatewayEvmScheme extends ExactEvmScheme {\n constructor() {\n super();\n this.registerGatewayMoneyParsers();\n }\n\n /**\n * Enhances payment requirements by merging the facilitator's extra data.\n *\n * The base `ExactEvmScheme.enhancePaymentRequirements` returns requirements\n * unchanged, dropping `supportedKind.extra`. Gateway payments require\n * `extra.verifyingContract` (and other fields like `name`, `version`) to\n * be present for clients to construct the correct signing domain.\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n void extensionKeys;\n return {\n ...paymentRequirements,\n maxTimeoutSeconds: 345600, // 4 days — Gateway batches settlements asynchronously\n extra: {\n ...paymentRequirements.extra,\n ...supportedKind.extra,\n },\n };\n }\n\n /**\n * Registers money parsers for all Gateway-supported networks.\n *\n * Builds a `network → USDC address` lookup from `CHAIN_CONFIGS` and\n * registers a single parser that converts dollar amounts to USDC atomic\n * units (6 decimals) for any Gateway-supported chain.\n *\n * Returns `null` for unknown networks so `ExactEvmScheme`'s built-in\n * parser handles them.\n */\n private registerGatewayMoneyParsers(): void {\n // Build network → USDC address map from CHAIN_CONFIGS\n const networkUsdcMap = new Map<string, string>();\n for (const config of Object.values(CHAIN_CONFIGS)) {\n const network = `eip155:${config.chain.id}`;\n networkUsdcMap.set(network, config.usdc);\n }\n\n this.registerMoneyParser(async (amount: number, network: Network) => {\n const usdcAddress = networkUsdcMap.get(network);\n if (!usdcAddress) {\n return null; // Not a Gateway network — let default parser handle it\n }\n return {\n amount: Math.round(amount * 1e6).toString(),\n asset: usdcAddress,\n };\n });\n }\n}\n","import {\n createPublicClient,\n createWalletClient,\n http,\n parseUnits,\n formatUnits,\n pad,\n zeroAddress,\n maxUint256,\n type Address,\n type Hex,\n type Chain,\n type PublicClient,\n type WalletClient,\n type Transport,\n type Account,\n defineChain,\n erc20Abi,\n} from 'viem';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport * as chains from 'viem/chains';\nimport { randomBytes } from 'crypto';\n\n// viem's sonicTestnet uses the old testnet. This uses the new one.\nconst sonicTestnet = defineChain({\n id: 14601,\n name: 'Sonic Testnet',\n nativeCurrency: { decimals: 18, name: 'Sonic', symbol: 'S' },\n rpcUrls: {\n default: { http: ['https://rpc.testnet.soniclabs.com'] },\n },\n blockExplorers: {\n default: { name: 'Sonic Testnet Explorer', url: 'https://testnet.soniclabs.com/' },\n },\n testnet: true,\n});\n\n// ============================================================================\n// CONTRACT ABIS\n// ============================================================================\n\n// Gateway API URL\nconst GATEWAY_API_TESTNET = 'https://gateway-api-testnet.circle.com/v1';\nconst GATEWAY_API_MAINNET = 'https://gateway-api.circle.com/v1';\n\nconst GATEWAY_MINTER_ABI = [\n {\n name: 'gatewayMint',\n type: 'function',\n stateMutability: 'nonpayable',\n inputs: [\n { name: 'attestationPayload', type: 'bytes' },\n { name: 'signature', type: 'bytes' },\n ],\n outputs: [],\n },\n] as const;\n\nconst GATEWAY_WALLET_ABI = [\n {\n name: 'deposit',\n type: 'function',\n stateMutability: 'nonpayable',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'value', type: 'uint256' },\n ],\n outputs: [],\n },\n {\n name: 'depositFor',\n type: 'function',\n stateMutability: 'nonpayable',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'depositor', type: 'address' },\n { name: 'value', type: 'uint256' },\n ],\n outputs: [],\n },\n {\n name: 'totalBalance',\n type: 'function',\n stateMutability: 'view',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'depositor', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n name: 'availableBalance',\n type: 'function',\n stateMutability: 'view',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'depositor', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n name: 'withdrawingBalance',\n type: 'function',\n stateMutability: 'view',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'depositor', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n name: 'withdrawableBalance',\n type: 'function',\n stateMutability: 'view',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'depositor', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n name: 'withdrawalDelay',\n type: 'function',\n stateMutability: 'view',\n inputs: [],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n name: 'withdrawalBlock',\n type: 'function',\n stateMutability: 'view',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'depositor', type: 'address' },\n ],\n outputs: [{ name: '', type: 'uint256' }],\n },\n {\n name: 'initiateWithdrawal',\n type: 'function',\n stateMutability: 'nonpayable',\n inputs: [\n { name: 'token', type: 'address' },\n { name: 'value', type: 'uint256' },\n ],\n outputs: [],\n },\n {\n name: 'withdraw',\n type: 'function',\n stateMutability: 'nonpayable',\n inputs: [{ name: 'token', type: 'address' }],\n outputs: [],\n },\n] as const;\n\n// ============================================================================\n// CHAIN CONFIGURATIONS\n// ============================================================================\n\n/**\n * Gateway domain identifiers for supported chains.\n * See: https://developers.circle.com/gateway/gateway-supported-blockchains\n */\nexport const GATEWAY_DOMAINS = {\n // Testnet\n arbitrumSepolia: 3,\n arcTestnet: 26,\n avalancheFuji: 1,\n baseSepolia: 6,\n sepolia: 0,\n hyperEvmTestnet: 19,\n optimismSepolia: 2,\n polygonAmoy: 7,\n seiAtlantic: 16,\n sonicTestnet: 13,\n unichainSepolia: 10,\n worldChainSepolia: 14,\n // Mainnet\n arbitrum: 3,\n avalanche: 1,\n base: 6,\n mainnet: 0,\n hyperEvm: 19,\n optimism: 2,\n polygon: 7,\n sei: 16,\n sonic: 13,\n unichain: 10,\n worldChain: 14,\n} as const;\n\n/**\n * Supported chain names type.\n */\nexport type SupportedChainName = keyof typeof GATEWAY_DOMAINS;\n\n/**\n * Chain configuration with contract addresses.\n */\nexport interface ChainConfig {\n chain: Chain;\n domain: number;\n usdc: Address;\n gatewayWallet: Address;\n gatewayMinter: Address;\n rpcUrl?: string;\n}\n\n// Testnet GatewayWallet and GatewayMinter addresses (same across all testnet chains)\nconst TESTNET_GATEWAY_WALLET = '0x0077777d7EBA4688BDeF3E311b846F25870A19B9' as Address;\nconst TESTNET_GATEWAY_MINTER = '0x0022222ABE238Cc2C7Bb1f21003F0a260052475B' as Address;\n\n// Mainnet GatewayWallet and GatewayMinter addresses (same across all mainnet chains)\nconst MAINNET_GATEWAY_WALLET = '0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE' as Address;\nconst MAINNET_GATEWAY_MINTER = '0x2222222d7164433c4C09B0b0D809a9b52C04C205' as Address;\n\n/**\n * Chain configurations for supported chains.\n */\nexport const CHAIN_CONFIGS: Record<SupportedChainName, ChainConfig> = {\n // Testnet chains\n arbitrumSepolia: {\n chain: chains.arbitrumSepolia,\n domain: GATEWAY_DOMAINS.arbitrumSepolia,\n usdc: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n arcTestnet: {\n chain: chains.arcTestnet,\n domain: GATEWAY_DOMAINS.arcTestnet,\n usdc: '0x3600000000000000000000000000000000000000' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n rpcUrl: 'https://rpc.testnet.arc.network',\n },\n avalancheFuji: {\n chain: chains.avalancheFuji,\n domain: GATEWAY_DOMAINS.avalancheFuji,\n usdc: '0x5425890298aed601595a70AB815c96711a31Bc65' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n baseSepolia: {\n chain: chains.baseSepolia,\n domain: GATEWAY_DOMAINS.baseSepolia,\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n rpcUrl: 'https://sepolia-preconf.base.org',\n },\n sepolia: {\n chain: chains.sepolia,\n domain: GATEWAY_DOMAINS.sepolia,\n usdc: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n hyperEvmTestnet: {\n chain: chains.hyperliquidEvmTestnet,\n domain: GATEWAY_DOMAINS.hyperEvmTestnet,\n usdc: '0x2B3370eE501B4a559b57D449569354196457D8Ab' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n optimismSepolia: {\n chain: chains.optimismSepolia,\n domain: GATEWAY_DOMAINS.optimismSepolia,\n usdc: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n polygonAmoy: {\n chain: chains.polygonAmoy,\n domain: GATEWAY_DOMAINS.polygonAmoy,\n usdc: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n seiAtlantic: {\n chain: chains.seiTestnet,\n domain: GATEWAY_DOMAINS.seiAtlantic,\n usdc: '0x4fCF1784B31630811181f670Aea7A7bEF803eaED' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n sonicTestnet: {\n chain: sonicTestnet,\n domain: GATEWAY_DOMAINS.sonicTestnet,\n usdc: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n unichainSepolia: {\n chain: chains.unichainSepolia,\n domain: GATEWAY_DOMAINS.unichainSepolia,\n usdc: '0x31d0220469e10c4E71834a79b1f276d740d3768F' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n worldChainSepolia: {\n chain: chains.worldchainSepolia,\n domain: GATEWAY_DOMAINS.worldChainSepolia,\n usdc: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88' as Address,\n gatewayWallet: TESTNET_GATEWAY_WALLET,\n gatewayMinter: TESTNET_GATEWAY_MINTER,\n },\n // Mainnet chains\n arbitrum: {\n chain: chains.arbitrum,\n domain: GATEWAY_DOMAINS.arbitrum,\n usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n avalanche: {\n chain: chains.avalanche,\n domain: GATEWAY_DOMAINS.avalanche,\n usdc: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n base: {\n chain: chains.base,\n domain: GATEWAY_DOMAINS.base,\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n mainnet: {\n chain: chains.mainnet,\n domain: GATEWAY_DOMAINS.mainnet,\n usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n hyperEvm: {\n chain: chains.hyperEvm,\n domain: GATEWAY_DOMAINS.hyperEvm,\n usdc: '0xb88339CB7199b77E23DB6E890353E22632Ba630f' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n optimism: {\n chain: chains.optimism,\n domain: GATEWAY_DOMAINS.optimism,\n usdc: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n polygon: {\n chain: chains.polygon,\n domain: GATEWAY_DOMAINS.polygon,\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n sei: {\n chain: chains.sei,\n domain: GATEWAY_DOMAINS.sei,\n usdc: '0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n sonic: {\n chain: chains.sonic,\n domain: GATEWAY_DOMAINS.sonic,\n usdc: '0x29219dd400f2Bf60E5a23d13Be72B486D4038894' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n unichain: {\n chain: chains.unichain,\n domain: GATEWAY_DOMAINS.unichain,\n usdc: '0x078D782b760474a361dDA0AF3839290b0EF57AD6' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n worldChain: {\n chain: chains.worldchain,\n domain: GATEWAY_DOMAINS.worldChain,\n usdc: '0x79A02482A880bCe3F13E09da970dC34dB4cD24D1' as Address,\n gatewayWallet: MAINNET_GATEWAY_WALLET,\n gatewayMinter: MAINNET_GATEWAY_MINTER,\n },\n};\n\n// ============================================================================\n// GATEWAY CLIENT TYPES\n// ============================================================================\n\n/**\n * Configuration for creating a GatewayClient.\n */\nexport interface GatewayClientConfig {\n /** The chain to connect to */\n chain: SupportedChainName;\n /** Private key for signing transactions */\n privateKey: Hex;\n /** Optional custom RPC URL */\n rpcUrl?: string;\n}\n\n/**\n * Result of a deposit operation.\n */\nexport interface DepositResult {\n /** Transaction hash of the approval (if needed) */\n approvalTxHash?: Hex;\n /** Transaction hash of the deposit */\n depositTxHash: Hex;\n /** Amount deposited in USDC atomic units */\n amount: bigint;\n /** Formatted amount deposited */\n formattedAmount: string;\n /** Depositor address */\n depositor: Address;\n}\n\n/**\n * Result of a withdraw operation (instant transfer).\n */\nexport interface WithdrawResult {\n /** Transaction hash of the mint on destination chain */\n mintTxHash: Hex;\n /** Amount withdrawn in USDC atomic units */\n amount: bigint;\n /** Formatted amount withdrawn */\n formattedAmount: string;\n /** Source chain name */\n sourceChain: string;\n /** Destination chain name */\n destinationChain: string;\n /** Recipient address */\n recipient: Address;\n}\n\n/**\n * Result of a pay operation.\n */\nexport interface PayResult<T = unknown> {\n /** The response data from the resource */\n data: T;\n /** Amount paid in USDC atomic units */\n amount: bigint;\n /** Formatted amount paid */\n formattedAmount: string;\n /** Transaction hash from settlement */\n transaction: string;\n /** HTTP status code */\n status: number;\n}\n\n/**\n * All balances in one object.\n */\nexport interface Balances {\n /** Regular wallet USDC balance */\n wallet: {\n balance: bigint;\n formatted: string;\n };\n /** Gateway balances */\n gateway: {\n total: bigint;\n available: bigint;\n withdrawing: bigint;\n withdrawable: bigint;\n formattedTotal: string;\n formattedAvailable: string;\n formattedWithdrawing: string;\n formattedWithdrawable: string;\n };\n}\n\n/**\n * Result of checking if a URL supports batching.\n */\nexport interface SupportsResult {\n /** Whether the URL supports Gateway batching */\n supported: boolean;\n /** Payment requirements if supported */\n requirements?: Record<string, unknown>;\n /** Error message if not supported */\n error?: string;\n}\n\n/**\n * Result of a trustless withdrawal initiation.\n *\n * NOTE: Trustless withdrawals are for emergency use only when Circle's API is unavailable.\n * For normal withdrawals, use `transfer()` with the same source and destination chain.\n */\nexport interface TrustlessWithdrawalInitResult {\n /** Transaction hash */\n txHash: Hex;\n /** Amount being withdrawn in USDC atomic units */\n amount: bigint;\n /** Formatted amount being withdrawn */\n formattedAmount: string;\n /** Block number at which withdrawal becomes available (~7 days) */\n withdrawalBlock: bigint;\n}\n\n/**\n * Result of a trustless withdrawal completion.\n *\n * NOTE: Trustless withdrawals are for emergency use only when Circle's API is unavailable.\n */\nexport interface TrustlessWithdrawalResult {\n /** Transaction hash */\n txHash: Hex;\n /** Amount withdrawn in USDC atomic units */\n amount: bigint;\n /** Formatted amount withdrawn */\n formattedAmount: string;\n}\n\n/**\n * Gateway balance information.\n */\nexport interface GatewayBalance {\n /** Total balance in Gateway */\n total: bigint;\n /** Available balance (can be used) */\n available: bigint;\n /** Balance in withdrawal process */\n withdrawing: bigint;\n /** Balance ready to be withdrawn */\n withdrawable: bigint;\n /** Formatted total balance */\n formattedTotal: string;\n /** Formatted available balance */\n formattedAvailable: string;\n /** Formatted withdrawing balance */\n formattedWithdrawing: string;\n /** Formatted withdrawable balance */\n formattedWithdrawable: string;\n}\n\n// ============================================================================\n// GATEWAY CLIENT\n// ============================================================================\n\n/**\n * Gateway Client for gasless payments and cross-chain USDC via Circle Gateway.\n *\n * This client provides a simple interface for buyers to:\n * - **Deposit**: Move USDC into Gateway for gasless payments\n * - **Pay**: Pay for x402-protected resources (handles 402 automatically)\n * - **Withdraw**: Get USDC back (same-chain or cross-chain, instant)\n * - **GetBalances**: Check all balances in one call\n *\n * ## Typical Flow\n * ```typescript\n * const gateway = new GatewayClient({ chain: 'arcTestnet', privateKey });\n *\n * // 1. Deposit (one-time setup)\n * await gateway.deposit('100');\n *\n * // 2. Pay for resources (many times, gasless!)\n * const { data } = await gateway.pay('https://api.example.com/resource');\n *\n * // 3. Check balances\n * const balances = await gateway.getBalances();\n *\n * // 4. Withdraw when done\n * await gateway.withdraw('50');\n * // Or withdraw to a different chain (requires gas on destination)\n * await gateway.withdraw('25', { chain: 'baseSepolia' });\n * ```\n *\n * ## Emergency Flow (when Circle's API is down)\n * Use `initiateTrustlessWithdrawal()` and `completeTrustlessWithdrawal()` (7-day delay).\n */\nexport class GatewayClient {\n readonly chainConfig: ChainConfig;\n readonly account: ReturnType<typeof privateKeyToAccount>;\n readonly publicClient: PublicClient<Transport, Chain>;\n readonly walletClient: WalletClient<Transport, Chain, Account>;\n\n /**\n * Creates a new GatewayClient.\n *\n * @param config - Configuration including chain and private key\n */\n constructor(config: GatewayClientConfig) {\n const chainConfig = CHAIN_CONFIGS[config.chain];\n if (!chainConfig) {\n throw new Error(`Unsupported chain: ${config.chain}`);\n }\n\n this.chainConfig = chainConfig;\n this.account = privateKeyToAccount(config.privateKey);\n\n const rpcUrl = config.rpcUrl ?? chainConfig.rpcUrl;\n const transport = rpcUrl ? http(rpcUrl) : http();\n\n this.publicClient = createPublicClient({\n chain: chainConfig.chain,\n transport,\n }) as PublicClient<Transport, Chain>;\n\n this.walletClient = createWalletClient({\n account: this.account,\n chain: chainConfig.chain,\n transport,\n }) as WalletClient<Transport, Chain, Account>;\n }\n\n /**\n * Get the account address.\n */\n get address(): Address {\n return this.account.address;\n }\n\n /**\n * Get the chain name.\n */\n get chainName(): string {\n return this.chainConfig.chain.name;\n }\n\n /**\n * Get the Gateway domain for this chain.\n */\n get domain(): number {\n return this.chainConfig.domain;\n }\n\n /**\n * Get the chain name key for this client.\n */\n getChainName(): SupportedChainName {\n // Find the key in CHAIN_CONFIGS that matches our domain\n for (const [name, config] of Object.entries(CHAIN_CONFIGS)) {\n if (\n config.domain === this.chainConfig.domain &&\n config.chain.id === this.chainConfig.chain.id\n ) {\n return name as SupportedChainName;\n }\n }\n throw new Error('Unknown chain configuration');\n }\n\n /**\n * Get the USDC balance of the account (not in Gateway).\n *\n * @param address - Optional address to check (defaults to account address)\n * @returns USDC balance in atomic units and formatted\n */\n async getUsdcBalance(\n address?: Address,\n ): Promise<{ balance: bigint; formatted: string }> {\n const target = address ?? this.account.address;\n const balance = await this.publicClient.readContract({\n address: this.chainConfig.usdc,\n abi: erc20Abi,\n functionName: 'balanceOf',\n args: [target],\n });\n\n return {\n balance,\n formatted: formatUnits(balance, 6),\n };\n }\n\n /**\n * Get all balances (wallet + gateway) in one call.\n *\n * @param address - Optional address to check (defaults to account address)\n * @returns All balances\n */\n async getBalances(address?: Address): Promise<Balances> {\n const target = address ?? this.account.address;\n\n const [wallet, gateway] = await Promise.all([\n this.getUsdcBalance(target),\n this.getGatewayBalance(target),\n ]);\n\n return {\n wallet,\n gateway,\n };\n }\n\n /**\n * Get the Gateway balance for an address.\n *\n * @param address - Optional address to check (defaults to account address)\n * @returns Gateway balance information\n * @deprecated Use `getBalances()` instead for a unified view\n */\n async getBalance(address?: Address): Promise<GatewayBalance> {\n return this.getGatewayBalance(address);\n }\n\n /**\n * Get the Gateway balance for an address via Gateway API.\n */\n private async getGatewayBalance(address?: Address): Promise<GatewayBalance> {\n const target = address ?? this.account.address;\n const apiBaseUrl = this.isTestnet() ? GATEWAY_API_TESTNET : GATEWAY_API_MAINNET;\n\n const response = await fetch(`${apiBaseUrl}/balances`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n token: 'USDC',\n sources: [{ depositor: target, domain: this.chainConfig.domain }],\n }),\n });\n\n const data = (await response.json()) as {\n balances?: Array<{\n balance: string;\n withdrawing?: string;\n withdrawable?: string;\n }>;\n message?: string;\n };\n\n if (!response.ok) {\n throw new Error(\n `Gateway API balance fetch failed: ${data.message ?? response.statusText}`,\n );\n }\n\n if (!data.balances || data.balances.length === 0) {\n throw new Error('Gateway API returned no balances for the depositor.');\n }\n\n const balanceData = data.balances[0];\n // Gateway API returns formatted decimal strings (e.g. \"0.300000\") for USDC.\n // Convert to atomic units (6 decimals) for bigint fields.\n const available = parseUnits(balanceData.balance, 6);\n const withdrawing = parseUnits(balanceData.withdrawing ?? '0', 6);\n const withdrawable = parseUnits(balanceData.withdrawable ?? '0', 6);\n const total = available + withdrawing;\n\n return {\n total,\n available,\n withdrawing,\n withdrawable,\n formattedTotal: formatUnits(total, 6),\n formattedAvailable: formatUnits(available, 6),\n formattedWithdrawing: formatUnits(withdrawing, 6),\n formattedWithdrawable: formatUnits(withdrawable, 6),\n };\n }\n\n /**\n * Deposit USDC into the Gateway Wallet.\n *\n * This method first approves the Gateway Wallet contract to spend USDC,\n * then deposits the specified amount.\n *\n * @param amount - Amount of USDC to deposit (as a decimal string, e.g., \"10.5\")\n * @param options - Optional deposit options\n * @returns Deposit result with transaction hashes\n */\n async deposit(\n amount: string,\n options?: {\n /** Amount to approve (defaults to amount) */\n approveAmount?: string;\n /** Skip approval if already approved */\n skipApprovalCheck?: boolean;\n },\n ): Promise<DepositResult> {\n const depositAmount = parseUnits(amount, 6);\n const approveAmount = parseUnits(options?.approveAmount ?? amount, 6);\n\n // Check balance\n const { balance } = await this.getUsdcBalance();\n if (balance < depositAmount) {\n throw new Error(\n `Insufficient USDC balance. Have: ${formatUnits(balance, 6)}, Need: ${amount}`,\n );\n }\n\n let approvalTxHash: Hex | undefined;\n\n // Check current allowance unless skipped\n if (!options?.skipApprovalCheck) {\n const allowance = await this.publicClient.readContract({\n address: this.chainConfig.usdc,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [this.account.address, this.chainConfig.gatewayWallet],\n });\n\n if (allowance < depositAmount) {\n // Approve the Gateway Wallet\n approvalTxHash = await this.walletClient.writeContract({\n address: this.chainConfig.usdc,\n abi: erc20Abi,\n functionName: 'approve',\n args: [this.chainConfig.gatewayWallet, approveAmount],\n });\n\n try {\n await this.publicClient.waitForTransactionReceipt({ hash: approvalTxHash });\n } catch (err) {\n throw new Error(`Approval transaction failed: ${approvalTxHash}`, {\n cause: err,\n });\n }\n }\n }\n\n // Deposit into Gateway Wallet\n const depositTxHash = await this.walletClient.writeContract({\n address: this.chainConfig.gatewayWallet,\n abi: GATEWAY_WALLET_ABI,\n functionName: 'deposit',\n args: [this.chainConfig.usdc, depositAmount],\n gas: 120000n,\n });\n\n try {\n await this.publicClient.waitForTransactionReceipt({ hash: depositTxHash });\n } catch (err) {\n throw new Error(`Deposit transaction failed: ${depositTxHash}`, {\n cause: err,\n });\n }\n\n return {\n approvalTxHash,\n depositTxHash,\n amount: depositAmount,\n formattedAmount: amount,\n depositor: this.account.address,\n };\n }\n\n /**\n * Deposit USDC into the Gateway Wallet on behalf of another address.\n *\n * The resulting balance belongs to the specified depositor address,\n * not the caller.\n *\n * @param amount - Amount of USDC to deposit (as a decimal string)\n * @param depositor - Address that will own the resulting balance\n * @param options - Optional deposit options\n * @returns Deposit result with transaction hashes\n */\n async depositFor(\n amount: string,\n depositor: Address,\n options?: {\n /** Amount to approve (defaults to amount) */\n approveAmount?: string;\n /** Skip approval if already approved */\n skipApprovalCheck?: boolean;\n },\n ): Promise<DepositResult> {\n const depositAmount = parseUnits(amount, 6);\n const approveAmount = parseUnits(options?.approveAmount ?? amount, 6);\n\n // Check balance\n const { balance } = await this.getUsdcBalance();\n if (balance < depositAmount) {\n throw new Error(\n `Insufficient USDC balance. Have: ${formatUnits(balance, 6)}, Need: ${amount}`,\n );\n }\n\n let approvalTxHash: Hex | undefined;\n\n // Check current allowance unless skipped\n if (!options?.skipApprovalCheck) {\n const allowance = await this.publicClient.readContract({\n address: this.chainConfig.usdc,\n abi: erc20Abi,\n functionName: 'allowance',\n args: [this.account.address, this.chainConfig.gatewayWallet],\n });\n\n if (allowance < depositAmount) {\n // Approve the Gateway Wallet\n approvalTxHash = await this.walletClient.writeContract({\n address: this.chainConfig.usdc,\n abi: erc20Abi,\n functionName: 'approve',\n args: [this.chainConfig.gatewayWallet, approveAmount],\n });\n\n try {\n await this.publicClient.waitForTransactionReceipt({ hash: approvalTxHash });\n } catch (err) {\n throw new Error(`Approval transaction failed: ${approvalTxHash}`, {\n cause: err,\n });\n }\n }\n }\n\n // Deposit into Gateway Wallet for another address\n const depositTxHash = await this.walletClient.writeContract({\n address: this.chainConfig.gatewayWallet,\n abi: GATEWAY_WALLET_ABI,\n functionName: 'depositFor',\n args: [this.chainConfig.usdc, depositor, depositAmount],\n gas: 120000n,\n });\n\n try {\n await this.publicClient.waitForTransactionReceipt({ hash: depositTxHash });\n } catch (err) {\n throw new Error(`Deposit transaction failed: ${depositTxHash}`, {\n cause: err,\n });\n }\n\n return {\n approvalTxHash,\n depositTxHash,\n amount: depositAmount,\n formattedAmount: amount,\n depositor,\n };\n }\n\n // ============================================================================\n // PAYMENT (Pay for x402 resources)\n // ============================================================================\n\n /**\n * Check if a URL supports Gateway batching before paying.\n *\n * @param url - The URL to check\n * @returns Whether batching is supported and payment requirements\n */\n async supports(url: string): Promise<SupportsResult> {\n try {\n const response = await fetch(url);\n\n if (response.status !== 402) {\n return { supported: false, error: 'Resource does not require payment (not 402)' };\n }\n\n // x402 v2: Read from PAYMENT-REQUIRED header (base64-encoded)\n const paymentRequiredHeader = response.headers.get('PAYMENT-REQUIRED');\n if (!paymentRequiredHeader) {\n return {\n supported: false,\n error: 'Missing PAYMENT-REQUIRED header in 402 response',\n };\n }\n const data: { accepts?: Array<Record<string, unknown>> } = JSON.parse(\n Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8'),\n );\n const accepts = data.accepts;\n\n if (!accepts || accepts.length === 0) {\n return { supported: false, error: 'No payment options in 402 response' };\n }\n\n // Find a batching option matching this client's chain\n const expectedNetwork = `eip155:${this.chainConfig.chain.id}`;\n const batchingOption = accepts.find((opt) => {\n const extra = opt.extra as Record<string, unknown> | undefined;\n return (\n opt.network === expectedNetwork &&\n extra?.name === 'GatewayWalletBatched' &&\n extra?.version === '1' &&\n typeof extra?.verifyingContract === 'string'\n );\n });\n\n if (!batchingOption) {\n return {\n supported: false,\n error: `No Gateway batching option available for network ${expectedNetwork} (${this.chainConfig.chain.name})`,\n };\n }\n\n return { supported: true, requirements: batchingOption };\n } catch (error) {\n return { supported: false, error: (error as Error).message };\n }\n }\n\n /**\n * Pay for an x402-protected resource.\n *\n * This method handles the full 402 payment flow automatically:\n * 1. Makes initial request\n * 2. If 402, finds Gateway batching option\n * 3. Signs payment authorization\n * 4. Retries with Payment-Signature header\n *\n * @param url - The URL to pay for\n * @param options - Optional request options\n * @returns The response data and payment info\n *\n * @example\n * ```typescript\n * const { data, amount } = await gateway.pay('https://api.example.com/resource');\n * console.log('Paid', amount, 'USDC for:', data);\n * ```\n */\n async pay<T = unknown>(\n url: string,\n options?: {\n /** HTTP method (default: GET) */\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n /** Request body for POST/PUT */\n body?: unknown;\n /** Additional headers */\n headers?: Record<string, string>;\n },\n ): Promise<PayResult<T>> {\n const method = options?.method ?? 'GET';\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options?.headers,\n };\n const serializedBody =\n options?.body !== undefined\n ? typeof options.body === 'string'\n ? options.body\n : JSON.stringify(options.body)\n : undefined;\n\n // Step 1: Make initial request\n const initialResponse = await fetch(url, {\n method,\n headers,\n body: serializedBody,\n });\n\n // If not 402, check if it's an error or success\n if (initialResponse.status !== 402) {\n if (initialResponse.ok) {\n const data = (await initialResponse.json()) as T;\n return {\n data,\n amount: 0n,\n formattedAmount: '0',\n transaction: '',\n status: initialResponse.status,\n };\n }\n throw new Error(`Request failed with status ${initialResponse.status}`);\n }\n\n // Step 2: Parse 402 response\n // x402 v2: Read from PAYMENT-REQUIRED header (base64-encoded)\n const paymentRequiredHeader = initialResponse.headers.get('PAYMENT-REQUIRED');\n if (!paymentRequiredHeader) {\n throw new Error('Missing PAYMENT-REQUIRED header in 402 response');\n }\n const paymentRequired = JSON.parse(\n Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8'),\n ) as {\n x402Version?: number;\n accepts?: Array<Record<string, unknown>>;\n resource?: Record<string, unknown>;\n };\n const accepts = paymentRequired.accepts;\n\n if (!accepts || accepts.length === 0) {\n throw new Error('No payment options in 402 response');\n }\n\n // Step 3: Find Gateway batching option matching this client's chain\n const expectedNetwork = `eip155:${this.chainConfig.chain.id}`;\n const batchingOption = accepts.find((opt) => {\n const extra = opt.extra as Record<string, unknown> | undefined;\n return (\n opt.network === expectedNetwork &&\n extra?.name === 'GatewayWalletBatched' &&\n extra?.version === '1' &&\n typeof extra?.verifyingContract === 'string'\n );\n });\n\n if (!batchingOption) {\n throw new Error(\n `No Gateway batching option available for network ${expectedNetwork} (${this.chainConfig.chain.name}). The seller may not support this chain. Use supports() to check first.`,\n );\n }\n\n // Step 4: Create and sign payment\n const paymentPayload = await this.createPaymentPayload(\n paymentRequired.x402Version ?? 2,\n batchingOption,\n );\n\n // Step 5: Retry with payment\n const paymentHeader = Buffer.from(\n JSON.stringify({\n ...paymentPayload,\n resource: paymentRequired.resource,\n accepted: batchingOption,\n }),\n ).toString('base64');\n\n const paidResponse = await fetch(url, {\n method,\n headers: {\n ...headers,\n 'Payment-Signature': paymentHeader,\n },\n body: serializedBody,\n });\n\n if (!paidResponse.ok) {\n const error = await paidResponse.json().catch(() => ({}));\n throw new Error(\n `Payment failed: ${(error as { error?: string }).error || paidResponse.statusText}`,\n );\n }\n\n const data = (await paidResponse.json()) as T;\n const amount = BigInt(batchingOption.amount as string);\n\n // x402 v2: Read settlement info from PAYMENT-RESPONSE header (base64-encoded)\n let transaction = '';\n const paymentResponseHeader = paidResponse.headers.get('PAYMENT-RESPONSE');\n if (paymentResponseHeader) {\n const settleResponse = JSON.parse(\n Buffer.from(paymentResponseHeader, 'base64').toString('utf-8'),\n ) as { transaction?: string };\n transaction = settleResponse.transaction ?? '';\n }\n\n return {\n data,\n amount,\n formattedAmount: formatUnits(amount, 6),\n transaction,\n status: paidResponse.status,\n };\n }\n\n /**\n * Create a payment payload for x402 (low-level).\n */\n private async createPaymentPayload(\n x402Version: number,\n requirements: Record<string, unknown>,\n ) {\n const extra = requirements.extra as Record<string, unknown>;\n const verifyingContract = extra.verifyingContract as Address;\n const network = requirements.network as string;\n const chainId = parseInt(network.split(':')[1]);\n\n const nonce = `0x${randomBytes(32).toString('hex')}` as Hex;\n const now = Math.floor(Date.now() / 1000);\n\n const authorization = {\n from: this.account.address,\n to: requirements.payTo as Address,\n value: requirements.amount as string,\n validAfter: (now - 600).toString(),\n validBefore: (now + (requirements.maxTimeoutSeconds as number)).toString(),\n nonce,\n };\n\n const signature = await this.account.signTypedData({\n domain: {\n name: 'GatewayWalletBatched',\n version: '1',\n chainId,\n verifyingContract,\n },\n types: {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n },\n primaryType: 'TransferWithAuthorization',\n message: {\n from: authorization.from,\n to: authorization.to,\n value: BigInt(authorization.value),\n validAfter: BigInt(authorization.validAfter),\n validBefore: BigInt(authorization.validBefore),\n nonce: authorization.nonce,\n },\n });\n\n return {\n x402Version,\n payload: { authorization, signature },\n };\n }\n\n // ============================================================================\n // WITHDRAW (Instant way to get USDC out)\n // ============================================================================\n\n /**\n * Withdraw USDC from Gateway to your wallet.\n *\n * By default, withdraws to the same chain (instant, no 7-day delay).\n * Optionally, withdraw to a different chain (requires gas on destination).\n *\n * @param amount - Amount of USDC to withdraw (as a decimal string)\n * @param options - Optional withdrawal options\n * @returns Withdrawal result with transaction hash\n *\n * @example\n * ```typescript\n * // Withdraw to same chain (instant!)\n * await gateway.withdraw('50');\n *\n * // Withdraw to Base Sepolia (requires ETH on Base for gas)\n * await gateway.withdraw('25', { chain: 'baseSepolia' });\n * ```\n */\n async withdraw(\n amount: string,\n options?: {\n /** Destination chain (defaults to same chain) */\n chain?: SupportedChainName;\n /** Recipient address (defaults to your address) */\n recipient?: Address;\n /** Max fee willing to pay in USDC (defaults to 2.01) */\n maxFee?: string;\n },\n ): Promise<WithdrawResult> {\n // Find destination chain config\n const destinationChainName = options?.chain ?? this.getChainName();\n const destConfig = CHAIN_CONFIGS[destinationChainName];\n if (!destConfig) {\n throw new Error(`Unsupported destination chain: ${destinationChainName}`);\n }\n\n const withdrawAmount = parseUnits(amount, 6);\n const recipient = options?.recipient ?? this.account.address;\n const maxFee = parseUnits(options?.maxFee ?? '2.01', 6);\n\n // Check available balance\n const balances = await this.getBalances();\n if (balances.gateway.available < withdrawAmount) {\n throw new Error(\n `Insufficient available balance. Have: ${balances.gateway.formattedAvailable}, Need: ${amount}`,\n );\n }\n\n // Create burn intent\n const burnIntent = this.createBurnIntent(\n this.chainConfig,\n destConfig,\n withdrawAmount,\n recipient,\n maxFee,\n );\n\n // Sign the burn intent\n const signature = await this.account.signTypedData({\n domain: { name: 'GatewayWallet', version: '1' },\n types: {\n EIP712Domain: [\n { name: 'name', type: 'string' },\n { name: 'version', type: 'string' },\n ],\n TransferSpec: [\n { name: 'version', type: 'uint32' },\n { name: 'sourceDomain', type: 'uint32' },\n { name: 'destinationDomain', type: 'uint32' },\n { name: 'sourceContract', type: 'bytes32' },\n { name: 'destinationContract', type: 'bytes32' },\n { name: 'sourceToken', type: 'bytes32' },\n { name: 'destinationToken', type: 'bytes32' },\n { name: 'sourceDepositor', type: 'bytes32' },\n { name: 'destinationRecipient', type: 'bytes32' },\n { name: 'sourceSigner', type: 'bytes32' },\n { name: 'destinationCaller', type: 'bytes32' },\n { name: 'value', type: 'uint256' },\n { name: 'salt', type: 'bytes32' },\n { name: 'hookData', type: 'bytes' },\n ],\n BurnIntent: [\n { name: 'maxBlockHeight', type: 'uint256' },\n { name: 'maxFee', type: 'uint256' },\n { name: 'spec', type: 'TransferSpec' },\n ],\n },\n primaryType: 'BurnIntent',\n message: burnIntent,\n });\n\n // Get attestation from Gateway API\n const apiUrl = this.isTestnet() ? GATEWAY_API_TESTNET : GATEWAY_API_MAINNET;\n const response = await fetch(`${apiUrl}/transfer`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify([{ burnIntent, signature }], (_, v) =>\n typeof v === 'bigint' ? v.toString() : v,\n ),\n });\n\n const result = (await response.json()) as {\n success?: boolean;\n error?: string;\n message?: string;\n attestation?: string;\n signature?: string;\n };\n\n if (\n result.success === false ||\n result.error ||\n !result.attestation ||\n !result.signature\n ) {\n throw new Error(\n `Gateway API error: ${result.message || result.error || JSON.stringify(result)}`,\n );\n }\n\n // Create destination chain client for minting\n const destTransport = destConfig.rpcUrl ? http(destConfig.rpcUrl) : http();\n const destWalletClient = createWalletClient({\n account: this.account,\n chain: destConfig.chain,\n transport: destTransport,\n });\n const destPublicClient = createPublicClient({\n chain: destConfig.chain,\n transport: destTransport,\n });\n\n // Mint on destination chain\n const mintTxHash = await destWalletClient.writeContract({\n address: destConfig.gatewayMinter,\n abi: GATEWAY_MINTER_ABI,\n functionName: 'gatewayMint',\n args: [result.attestation as Hex, result.signature as Hex],\n });\n\n try {\n await destPublicClient.waitForTransactionReceipt({ hash: mintTxHash });\n } catch (err) {\n throw new Error(`Mint transaction failed: ${mintTxHash}`, { cause: err });\n }\n\n return {\n mintTxHash,\n amount: withdrawAmount,\n formattedAmount: amount,\n sourceChain: this.chainConfig.chain.name,\n destinationChain: destConfig.chain.name,\n recipient,\n };\n }\n\n /**\n * Transfer USDC to any supported chain (alias for withdraw with destination chain).\n * @deprecated Use `withdraw({ chain })` instead\n */\n async transfer(\n amount: string,\n destinationChain: SupportedChainName,\n recipient?: Address,\n ): Promise<WithdrawResult> {\n return this.withdraw(amount, { chain: destinationChain, recipient });\n }\n\n /**\n * Create a burn intent for a transfer.\n */\n private createBurnIntent(\n from: ChainConfig,\n to: ChainConfig,\n value: bigint,\n recipient: Address,\n maxFee: bigint,\n ) {\n const addressToBytes32 = (addr: string): Hex =>\n pad(addr.toLowerCase() as Address, { size: 32 });\n\n return {\n maxBlockHeight: maxUint256,\n maxFee,\n spec: {\n version: 1,\n sourceDomain: from.domain,\n destinationDomain: to.domain,\n sourceContract: addressToBytes32(from.gatewayWallet),\n destinationContract: addressToBytes32(to.gatewayMinter),\n sourceToken: addressToBytes32(from.usdc),\n destinationToken: addressToBytes32(to.usdc),\n sourceDepositor: addressToBytes32(this.account.address),\n destinationRecipient: addressToBytes32(recipient),\n sourceSigner: addressToBytes32(this.account.address),\n destinationCaller: addressToBytes32(zeroAddress),\n value,\n salt: `0x${randomBytes(32).toString('hex')}` as Hex,\n hookData: '0x' as Hex,\n },\n };\n }\n\n /**\n * Check if this client is connected to a testnet.\n */\n private isTestnet(): boolean {\n const testnetChains: SupportedChainName[] = [\n 'arbitrumSepolia',\n 'arcTestnet',\n 'avalancheFuji',\n 'baseSepolia',\n 'sepolia',\n 'hyperEvmTestnet',\n 'seiAtlantic',\n 'sonicTestnet',\n 'worldChainSepolia',\n ];\n return testnetChains.some(\n (name) => CHAIN_CONFIGS[name].domain === this.chainConfig.domain,\n );\n }\n\n // ============================================================================\n // TRUSTLESS WITHDRAWAL (Emergency use only)\n // ============================================================================\n\n /**\n * Get the trustless withdrawal delay in blocks (~7 days).\n *\n * NOTE: This is for the emergency trustless withdrawal flow only.\n * For normal withdrawals, use `transfer()` to the same chain.\n *\n * @returns Number of blocks to wait after initiating trustless withdrawal\n */\n async getTrustlessWithdrawalDelay(): Promise<bigint> {\n return this.publicClient.readContract({\n address: this.chainConfig.gatewayWallet,\n abi: GATEWAY_WALLET_ABI,\n functionName: 'withdrawalDelay',\n });\n }\n\n /**\n * Get the block at which a pending trustless withdrawal becomes available.\n *\n * @param address - Optional address to check (defaults to account address)\n * @returns Block number, or 0n if no pending withdrawal\n */\n async getTrustlessWithdrawalBlock(address?: Address): Promise<bigint> {\n const target = address ?? this.account.address;\n return this.publicClient.readContract({\n address: this.chainConfig.gatewayWallet,\n abi: GATEWAY_WALLET_ABI,\n functionName: 'withdrawalBlock',\n args: [this.chainConfig.usdc, target],\n });\n }\n\n /**\n * Initiate a trustless withdrawal from the Gateway Wallet.\n *\n * ⚠️ **WARNING: This is for emergency use only!**\n *\n * Use this only when Circle's Gateway API is unavailable. For normal\n * withdrawals, use `transfer()` to the same chain - it's instant.\n *\n * After calling this, you must wait ~7 days (`withdrawalDelay` blocks)\n * before calling `completeTrustlessWithdrawal()`.\n *\n * @param amount - Amount of USDC to withdraw (as a decimal string)\n * @returns Withdrawal initiation result\n */\n async initiateTrustlessWithdrawal(\n amount: string,\n ): Promise<TrustlessWithdrawalInitResult> {\n const withdrawAmount = parseUnits(amount, 6);\n\n // Check available balance\n const balance = await this.getBalance();\n if (balance.available < withdrawAmount) {\n throw new Error(\n `Insufficient available balance. Have: ${balance.formattedAvailable}, Need: ${amount}`,\n );\n }\n\n // Initiate withdrawal\n const txHash = await this.walletClient.writeContract({\n address: this.chainConfig.gatewayWallet,\n abi: GATEWAY_WALLET_ABI,\n functionName: 'initiateWithdrawal',\n args: [this.chainConfig.usdc, withdrawAmount],\n gas: 100000n,\n });\n\n try {\n await this.publicClient.waitForTransactionReceipt({ hash: txHash });\n } catch (err) {\n throw new Error(`Trustless withdrawal initiation failed: ${txHash}`, {\n cause: err,\n });\n }\n\n // Get the withdrawal block\n const withdrawalBlock = await this.getTrustlessWithdrawalBlock();\n\n return {\n txHash,\n amount: withdrawAmount,\n formattedAmount: amount,\n withdrawalBlock,\n };\n }\n\n /**\n * Complete a trustless withdrawal after the ~7-day delay period.\n *\n * ⚠️ **WARNING: This is for emergency use only!**\n *\n * Use this only when Circle's Gateway API is unavailable. For normal\n * withdrawals, use `transfer()` to the same chain - it's instant.\n *\n * @returns Withdrawal result\n */\n async completeTrustlessWithdrawal(): Promise<TrustlessWithdrawalResult> {\n // Check withdrawable balance\n const balance = await this.getBalance();\n if (balance.withdrawable === 0n) {\n const withdrawalBlock = await this.getTrustlessWithdrawalBlock();\n const currentBlock = await this.publicClient.getBlockNumber();\n\n if (withdrawalBlock > 0n && currentBlock < withdrawalBlock) {\n throw new Error(\n `Trustless withdrawal not yet available. Current block: ${currentBlock}, Available at: ${withdrawalBlock}`,\n );\n }\n\n throw new Error(\n 'No withdrawable balance. Call initiateTrustlessWithdrawal() first.',\n );\n }\n\n const withdrawAmount = balance.withdrawable;\n\n // Complete withdrawal\n const txHash = await this.walletClient.writeContract({\n address: this.chainConfig.gatewayWallet,\n abi: GATEWAY_WALLET_ABI,\n functionName: 'withdraw',\n args: [this.chainConfig.usdc],\n gas: 100000n,\n });\n\n try {\n await this.publicClient.waitForTransactionReceipt({ hash: txHash });\n } catch (err) {\n throw new Error(`Trustless withdrawal completion failed: ${txHash}`, {\n cause: err,\n });\n }\n\n return {\n txHash,\n amount: withdrawAmount,\n formattedAmount: formatUnits(withdrawAmount, 6),\n };\n }\n}\n","/**\n * EIP-712 domain name for Circle Gateway batched payments.\n * Used in the `extra.name` field of PaymentRequirements to identify Circle batching options.\n */\nexport const CIRCLE_BATCHING_NAME = 'GatewayWalletBatched';\n\n/**\n * EIP-712 domain version for Circle Gateway batched payments.\n * Used in the `extra.version` field of PaymentRequirements.\n */\nexport const CIRCLE_BATCHING_VERSION = '1';\n\n/**\n * The payment scheme used for Circle batching (same as standard x402 exact scheme).\n */\nexport const CIRCLE_BATCHING_SCHEME = 'exact';\n","import { CIRCLE_BATCHING_NAME, CIRCLE_BATCHING_VERSION } from './constants';\n\n/**\n * PaymentRequirements-like structure for detection.\n * We only need the `extra` field to detect batching options.\n */\ninterface PaymentRequirementsLike {\n extra?: Record<string, unknown>;\n}\n\n/**\n * Check if a PaymentRequirements object supports batched settlement.\n *\n * Batching options are identified by:\n * - `extra.name === \"GatewayWalletBatched\"`\n * - `extra.version === \"1\"`\n *\n * @param requirements - The payment requirements to check\n * @returns true if this supports batched settlement\n *\n * @example\n * ```typescript\n * import { supportsBatching } from \"@circle-fin/x402-batching\";\n *\n * if (supportsBatching(paymentRequirements)) {\n * // This option uses batched settlement (gas-free for users!)\n * }\n * ```\n */\nexport function supportsBatching(requirements: PaymentRequirementsLike): boolean {\n const extra = requirements.extra;\n if (!extra) {\n return false;\n }\n\n return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;\n}\n\n/**\n * Check if a PaymentRequirements object is a batched payment.\n *\n * This is an alias for `supportsBatching`, intended for server-side/facilitator use.\n * Use this to route payments to the appropriate handler.\n *\n * @param requirements - The payment requirements to check\n * @returns true if this is a batched payment\n *\n * @example\n * ```typescript\n * import { isBatchPayment, BatchFacilitatorClient } from \"@circle-fin/x402-batching/server\";\n *\n * const gateway = new BatchFacilitatorClient();\n *\n * // In your facilitator's verify/settle handler:\n * if (isBatchPayment(requirements)) {\n * return gateway.verify(payload, requirements);\n * }\n * // else: handle with existing on-chain logic\n * ```\n */\nexport const isBatchPayment = supportsBatching;\n\n/**\n * Get the verifyingContract address from batching requirements.\n *\n * @param requirements - The payment requirements\n * @returns The batch wallet contract address, or undefined if not a batching option\n */\nexport function getVerifyingContract(\n requirements: PaymentRequirementsLike,\n): string | undefined {\n if (!supportsBatching(requirements)) {\n return undefined;\n }\n\n const verifyingContract = requirements.extra?.verifyingContract;\n if (typeof verifyingContract === 'string') {\n return verifyingContract;\n }\n\n return undefined;\n}\n","import type { IncomingMessage, ServerResponse } from 'http';\nimport { BatchFacilitatorClient } from './BatchFacilitatorClient';\nimport {\n CIRCLE_BATCHING_SCHEME,\n CIRCLE_BATCHING_NAME,\n CIRCLE_BATCHING_VERSION,\n} from '../constants';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\n/**\n * Supported network kind from facilitator.\n */\ninterface SupportedKind {\n network: string;\n extra?: {\n verifyingContract?: string;\n assets?: Array<{ address: string; symbol: string; decimals: number }>;\n [key: string]: unknown;\n };\n}\n\n/**\n * Extract the USDC token address from a SupportedKind's assets list.\n * Returns null if no USDC asset is found.\n */\nfunction getUsdcAddress(kind: SupportedKind): string | null {\n const assets = kind.extra?.assets;\n if (!assets || assets.length === 0) return null;\n const usdc = assets.find((a) => a.symbol === 'USDC');\n return usdc?.address ?? null;\n}\n\n/**\n * Configuration for Gateway middleware.\n */\nexport interface GatewayMiddlewareConfig {\n /** Seller's wallet address to receive payments */\n sellerAddress: string;\n /**\n * Networks to accept payments on.\n * - If omitted, accepts payments on ALL Gateway-supported networks (recommended)\n * - Can be a single network string or array of networks\n * - Format: CAIP-2 (e.g., 'eip155:5042002' for Arc Testnet)\n */\n networks?: string | string[];\n /** Optional Gateway facilitator URL */\n facilitatorUrl?: string;\n /** Optional resource description */\n description?: string;\n}\n\n/**\n * Payment requirements for 402 responses.\n */\nexport interface PaymentRequirements {\n scheme: string;\n network: string;\n asset: string;\n amount: string;\n payTo: string;\n maxTimeoutSeconds: number;\n extra: {\n name: string;\n version: string;\n verifyingContract: string;\n };\n}\n\n/**\n * Express-compatible request with payment info.\n */\nexport interface PaymentRequest extends IncomingMessage {\n payment?: {\n /** Whether the payment was verified */\n verified: boolean;\n /** Payer's address */\n payer: string;\n /** Amount paid in smallest units */\n amount: string;\n /** Network the payment was made on */\n network: string;\n /** Transaction hash after settlement */\n transaction?: string;\n };\n body?: unknown;\n}\n\n/**\n * Express-compatible response.\n */\nexport interface PaymentResponse extends ServerResponse {\n json?: (data: unknown) => void;\n status?: (code: number) => PaymentResponse;\n}\n\n/**\n * Express-compatible next function.\n */\nexport type NextFunction = (err?: unknown) => void;\n\n/**\n * Express-compatible middleware function.\n */\nexport type MiddlewareFunction = (\n req: PaymentRequest,\n res: PaymentResponse,\n next: NextFunction,\n) => void | Promise<void>;\n\n// ============================================================================\n// MIDDLEWARE FACTORY\n// ============================================================================\n\n/**\n * Gateway middleware instance.\n */\nexport interface GatewayMiddleware {\n /**\n * Require payment for a resource.\n *\n * @param price - Price in dollars (e.g., '$0.01' or '0.01')\n * @returns Express-compatible middleware\n *\n * @example\n * ```typescript\n * app.get('/resource', gateway.require('$0.01'), (req, res) => {\n * res.json({ content: '...' });\n * });\n *\n * app.post('/generate', gateway.require('$0.05'), (req, res) => {\n * res.json({ result: '...' });\n * });\n * ```\n */\n require: (price: string) => MiddlewareFunction;\n\n /**\n * Verify a payment without settling.\n */\n verify: (\n payment: unknown,\n ) => Promise<{ valid: boolean; payer?: string; error?: string }>;\n\n /**\n * Settle a verified payment.\n */\n settle: (\n payment: unknown,\n ) => Promise<{ success: boolean; transaction?: string; error?: string }>;\n}\n\n/**\n * Parse a price string (e.g., '$0.01' or '0.01') to atomic units.\n */\nfunction parsePrice(price: string): string {\n const numericPrice = price.replace(/[$]/g, '');\n const amount = parseFloat(numericPrice);\n if (isNaN(amount) || amount <= 0) {\n throw new Error(`Invalid price: ${price}`);\n }\n // Convert to USDC atomic units (6 decimals)\n return Math.round(amount * 1_000_000).toString();\n}\n\n/**\n * Create a Gateway middleware instance.\n *\n * By default, accepts payments on ALL Gateway-supported networks. Buyers can\n * pay from any chain where they have Gateway balance.\n *\n * @param config - Middleware configuration\n * @returns Gateway middleware\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { createGatewayMiddleware } from '@circle-fin/x402-batching/server';\n *\n * const app = express();\n *\n * // Accept payments on all Gateway-supported networks (recommended)\n * const gateway = createGatewayMiddleware({\n * sellerAddress: '0x...',\n * });\n *\n * // Or restrict to specific networks\n * const gateway = createGatewayMiddleware({\n * sellerAddress: '0x...',\n * networks: ['eip155:5042002', 'eip155:84532'], // Arc Testnet + Base Sepolia\n * });\n *\n * app.get('/resource', gateway.require('$0.01'), (req, res) => {\n * res.json({ content: 'Paid content!' });\n * });\n * ```\n */\nexport function createGatewayMiddleware(\n config: GatewayMiddlewareConfig,\n): GatewayMiddleware {\n const facilitator = new BatchFacilitatorClient({\n url: config.facilitatorUrl,\n });\n\n // Normalize networks config\n const configuredNetworks = config.networks\n ? Array.isArray(config.networks)\n ? config.networks\n : [config.networks]\n : null; // null means \"all networks\"\n\n // Cache supported kinds from facilitator\n let cachedSupportedKinds: SupportedKind[] | null = null;\n\n async function getSupportedKinds(): Promise<SupportedKind[]> {\n if (cachedSupportedKinds) {\n return cachedSupportedKinds;\n }\n\n const supported = await facilitator.getSupported();\n cachedSupportedKinds = supported.kinds as SupportedKind[];\n return cachedSupportedKinds;\n }\n\n /**\n * Get networks to advertise in 402 response.\n */\n async function getAcceptedNetworks(): Promise<SupportedKind[]> {\n const allKinds = await getSupportedKinds();\n\n // Filter to configured networks if specified, otherwise use all\n if (configuredNetworks) {\n return allKinds.filter(\n (k) => configuredNetworks.includes(k.network) && k.extra?.verifyingContract,\n );\n }\n\n // Return all networks with verifying contracts\n return allKinds.filter((k) => k.extra?.verifyingContract);\n }\n\n /**\n * Create payment requirements for all accepted networks.\n */\n async function createAllPaymentRequirements(\n price: string,\n ): Promise<PaymentRequirements[]> {\n const networks = await getAcceptedNetworks();\n const amount = parsePrice(price);\n\n return networks\n .filter((kind) => getUsdcAddress(kind)) // Only include networks we have USDC addresses for\n .map((kind) => ({\n scheme: CIRCLE_BATCHING_SCHEME, // Must be 'exact' for Gateway API\n network: kind.network,\n asset: getUsdcAddress(kind)!, // Use actual USDC address\n amount,\n payTo: config.sellerAddress,\n maxTimeoutSeconds: 345600, // 4 days (same as digital-dungeon)\n extra: {\n name: CIRCLE_BATCHING_NAME,\n version: CIRCLE_BATCHING_VERSION,\n verifyingContract: kind.extra!.verifyingContract as string,\n },\n }));\n }\n\n /**\n * Create payment requirements for a specific network.\n */\n async function createPaymentRequirements(\n price: string,\n network: string,\n ): Promise<PaymentRequirements | null> {\n const networks = await getAcceptedNetworks();\n const kind = networks.find((k) => k.network === network);\n\n if (!kind || !kind.extra?.verifyingContract) {\n return null;\n }\n\n const usdcAddress = getUsdcAddress(kind);\n if (!usdcAddress) {\n return null;\n }\n\n return {\n scheme: CIRCLE_BATCHING_SCHEME, // Must be 'exact' for Gateway API\n network: kind.network,\n asset: usdcAddress, // Use actual USDC address\n amount: parsePrice(price),\n payTo: config.sellerAddress,\n maxTimeoutSeconds: 345600, // 4 days\n extra: {\n name: CIRCLE_BATCHING_NAME,\n version: CIRCLE_BATCHING_VERSION,\n verifyingContract: kind.extra.verifyingContract as string,\n },\n };\n }\n\n return {\n require: (price: string): MiddlewareFunction => {\n return async (req, res, next) => {\n try {\n // Check for payment header\n const paymentHeader = req.headers['payment-signature'] as string | undefined;\n\n if (!paymentHeader) {\n // Return 402 Payment Required with all accepted networks\n const allRequirements = await createAllPaymentRequirements(price);\n const url = req.url ?? '/';\n const description = config.description ?? 'Paid resource';\n\n if (allRequirements.length === 0) {\n res.statusCode = 503;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'No payment networks available' }));\n return;\n }\n\n const paymentRequired = {\n x402Version: 2, // Version 2 required by Gateway API\n resource: {\n url,\n description,\n mimeType: 'application/json',\n },\n accepts: allRequirements,\n };\n\n // x402 v2: Payment required info goes in PAYMENT-REQUIRED header (base64-encoded)\n const paymentRequiredHeader = Buffer.from(\n JSON.stringify(paymentRequired),\n ).toString('base64');\n\n res.statusCode = 402;\n res.setHeader('PAYMENT-REQUIRED', paymentRequiredHeader);\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({}));\n return;\n }\n\n // Decode payment\n const paymentPayload = JSON.parse(\n Buffer.from(paymentHeader, 'base64').toString('utf-8'),\n );\n const acceptedRequirements = paymentPayload.accepted as\n | PaymentRequirements\n | undefined;\n\n if (!acceptedRequirements?.network) {\n res.statusCode = 400;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({ error: 'Missing accepted requirements in payment' }),\n );\n return;\n }\n\n // Create requirements for the network the buyer chose\n const requirements = await createPaymentRequirements(\n price,\n acceptedRequirements.network,\n );\n\n if (!requirements) {\n res.statusCode = 400;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({\n error: `Network ${acceptedRequirements.network} not accepted`,\n }),\n );\n return;\n }\n\n // Verify the payment\n const verifyResult = await facilitator.verify(paymentPayload, requirements);\n\n if (!verifyResult.isValid) {\n res.statusCode = 402;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Payment verification failed',\n reason: verifyResult.invalidReason,\n }),\n );\n return;\n }\n\n // Settle the payment\n const settleResult = await facilitator.settle(paymentPayload, requirements);\n\n if (!settleResult.success) {\n res.statusCode = 402;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Payment settlement failed',\n reason: settleResult.errorReason,\n }),\n );\n return;\n }\n\n // Attach payment info to request\n req.payment = {\n verified: true,\n payer: settleResult.payer ?? verifyResult.payer ?? '',\n amount: parsePrice(price),\n network: requirements.network,\n transaction: settleResult.transaction,\n };\n\n // x402 v2: Settlement response goes in PAYMENT-RESPONSE header (base64-encoded)\n const settleResponseHeader = Buffer.from(\n JSON.stringify({\n success: true,\n transaction: settleResult.transaction,\n network: requirements.network,\n payer: settleResult.payer ?? verifyResult.payer ?? '',\n }),\n ).toString('base64');\n res.setHeader('PAYMENT-RESPONSE', settleResponseHeader);\n\n next();\n } catch (error) {\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(\n JSON.stringify({\n error: 'Payment processing error',\n message: (error as Error).message,\n }),\n );\n }\n };\n },\n\n verify: async (payment) => {\n try {\n const paymentPayload = payment as {\n paymentPayload: unknown;\n paymentRequirements: PaymentRequirements;\n };\n const result = await facilitator.verify(\n paymentPayload.paymentPayload as Parameters<typeof facilitator.verify>[0],\n paymentPayload.paymentRequirements,\n );\n return {\n valid: result.isValid,\n payer: result.payer,\n error: result.invalidReason,\n };\n } catch (error) {\n return {\n valid: false,\n error: (error as Error).message,\n };\n }\n },\n\n settle: async (payment) => {\n try {\n const paymentPayload = payment as {\n paymentPayload: unknown;\n paymentRequirements: PaymentRequirements;\n };\n const result = await facilitator.settle(\n paymentPayload.paymentPayload as Parameters<typeof facilitator.settle>[0],\n paymentPayload.paymentRequirements,\n );\n return {\n success: result.success,\n transaction: result.transaction,\n error: result.errorReason,\n };\n } catch (error) {\n return {\n success: false,\n error: (error as Error).message,\n };\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|