@relai-fi/x402 0.4.0 → 0.5.1

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/dist/client.cjs CHANGED
@@ -235,8 +235,8 @@ function createX402Client(config) {
235
235
  const paymentAmount = accept.amount || accept.maxAmountRequired;
236
236
  const useRelayer = !!extra.relayerContract;
237
237
  const domain = {
238
- name: useRelayer ? extra.domainName || "A402" : extra.name || "USD Coin",
239
- version: useRelayer ? extra.domainVersion || "1" : extra.version || "2",
238
+ name: useRelayer ? extra.domainName || "A402" : extra.name || "USDC",
239
+ version: useRelayer ? extra.domainVersion || "1" : extra.version || "1",
240
240
  chainId,
241
241
  verifyingContract: useRelayer ? extra.relayerContract : accept.asset
242
242
  };
@@ -253,10 +253,9 @@ function createX402Client(config) {
253
253
  { name: "nonce", type: "bytes32" }
254
254
  ]
255
255
  };
256
- const spender = useRelayer ? accept.payTo : extra.feePayer || accept.payTo;
257
256
  const message = {
258
257
  from: evmWallet.address,
259
- to: spender,
258
+ to: accept.payTo,
260
259
  value: paymentAmount,
261
260
  validAfter: String(validAfter),
262
261
  validBefore: String(validBefore),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["// src/client.ts\nimport {\n Connection,\n PublicKey,\n TransactionMessage,\n VersionedTransaction,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from '@solana/spl-token';\nimport type { SolanaWallet, EvmWallet, WalletSet } from './types';\nimport {\n RELAI_FACILITATOR_URL,\n NETWORK_CAIP2,\n CHAIN_IDS,\n isSolana,\n isEvm,\n normalizeNetwork,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface X402ClientConfig {\n /** Multi-chain wallets (Solana + EVM) */\n wallets?: WalletSet;\n /** Single Solana wallet (legacy shortcut) */\n wallet?: SolanaWallet;\n /** Custom facilitator URL, default: RelAI facilitator */\n facilitatorUrl?: string;\n /** Preferred network when multiple options available */\n preferredNetwork?: RelaiNetwork;\n /** Custom Solana RPC URL */\n solanaRpcUrl?: string;\n /** Custom EVM RPC URLs per network (e.g. { 'skale-base': 'https://...' }) */\n evmRpcUrls?: Record<string, string>;\n /** Maximum payment amount in atomic units */\n maxAmountAtomic?: string;\n /** Enable verbose logging */\n verbose?: boolean;\n}\n\nexport interface X402Client {\n /** Fetch with automatic x402 payment handling */\n fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n}\n\n/**\n * Create an x402 client for automatic payment handling.\n * Supports all RelAI facilitator networks: Solana, Base, Avalanche, SKALE Base.\n * Auto-detects the correct chain from the 402 response and picks the right\n * signing method (Solana SPL transfer, EVM EIP-3009 transferWithAuthorization).\n *\n * @example\n * ```typescript\n * import { createX402Client } from '@relai-fi/x402';\n *\n * const client = createX402Client({\n * wallets: { solana: solanaWallet, evm: evmWalletClient },\n * });\n *\n * // Automatically handles 402 on any RelAI-supported network\n * const response = await client.fetch('https://api.example.com/protected');\n * ```\n */\n// Networks that use EIP-2612 permit instead of EIP-3009 transferWithAuthorization (currently none)\nconst PERMIT_NETWORKS = new Set<string>([]);\n\n// Default EVM RPC URLs\nconst DEFAULT_EVM_RPC_URLS: Record<string, string> = {\n 'skale-base': 'https://skale-base.skalenodes.com/v1/base',\n 'skale-bite': 'https://base-sepolia-testnet.skalenodes.com/v1/bite-v2-sandbox',\n 'base': 'https://mainnet.base.org',\n 'avalanche': 'https://api.avax.network/ext/bc/C/rpc',\n 'polygon': 'https://polygon-rpc.com',\n 'ethereum': 'https://ethereum-rpc.publicnode.com',\n};\n\nexport function createX402Client(config: X402ClientConfig): X402Client {\n const {\n wallets = {},\n wallet: legacyWallet,\n facilitatorUrl = RELAI_FACILITATOR_URL,\n preferredNetwork,\n solanaRpcUrl = 'https://api.mainnet-beta.solana.com',\n evmRpcUrls = {},\n maxAmountAtomic,\n verbose = false,\n } = config;\n\n const log = verbose ? console.log.bind(console, '[relai-x402]') : () => {};\n\n // Merge legacy wallet into wallet set\n const effectiveWallets: WalletSet = { ...wallets };\n if (legacyWallet && !effectiveWallets.solana) {\n effectiveWallets.solana = legacyWallet;\n }\n\n const hasSolanaWallet = Boolean(\n effectiveWallets.solana?.publicKey && effectiveWallets.solana?.signTransaction\n );\n if (hasSolanaWallet) log('Solana wallet ready');\n\n // -----------------------------------------------------------------------\n // Select a payment option from the 402 response's `accepts` array\n // -----------------------------------------------------------------------\n function selectAccept(accepts: any[]): { accept: any; chain: 'solana' | 'evm' } | null {\n // 1) Preferred network first\n if (preferredNetwork) {\n const caip2 = NETWORK_CAIP2[preferredNetwork];\n for (const a of accepts) {\n const net = a.network || '';\n if (net === preferredNetwork || net === caip2) {\n const chain = isSolana(net) ? 'solana' as const : 'evm' as const;\n if ((chain === 'solana' && hasSolanaWallet) || (chain === 'evm' && effectiveWallets.evm)) {\n return { accept: a, chain };\n }\n }\n }\n }\n\n // 2) First option we have a wallet for\n for (const a of accepts) {\n const net = a.network || '';\n if (isSolana(net) && hasSolanaWallet) return { accept: a, chain: 'solana' };\n if (isEvm(net) && effectiveWallets.evm) return { accept: a, chain: 'evm' };\n }\n\n return null;\n }\n\n // -----------------------------------------------------------------------\n // JSON-RPC helper (for reading EVM contract state without ethers)\n // -----------------------------------------------------------------------\n async function evmRpcCall(rpcUrl: string, to: string, data: string): Promise<string> {\n const res = await fetch(rpcUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n method: 'eth_call',\n params: [{ to, data }, 'latest'],\n id: 1,\n }),\n });\n const json = await res.json();\n if (json.error) throw new Error(`RPC error: ${json.error.message}`);\n return json.result;\n }\n\n function getEvmRpcUrl(network: string): string {\n return evmRpcUrls[network] || DEFAULT_EVM_RPC_URLS[network] || '';\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment — EIP-2612 permit (for SKALE Base)\n // -----------------------------------------------------------------------\n async function buildEvmPermitPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n const spender = extra.feePayer || accept.payTo;\n const usdcAddress = accept.asset;\n\n const rpcUrl = getEvmRpcUrl(network || rawNetwork);\n if (!rpcUrl) throw new Error(`[relai-x402] No EVM RPC URL for network ${network || rawNetwork}`);\n\n log('Building EIP-2612 permit on chain', chainId);\n\n // Read nonce from USDC contract: nonces(address) = 0x7ecebe00\n const paddedAddress = evmWallet.address.toLowerCase().replace('0x', '').padStart(64, '0');\n const nonceHex = await evmRpcCall(rpcUrl, usdcAddress, '0x7ecebe00' + paddedAddress);\n const nonce = nonceHex ? parseInt(nonceHex, 16) : 0;\n if (isNaN(nonce)) throw new Error(`[relai-x402] Failed to read permit nonce from ${usdcAddress} on ${rpcUrl}`);\n log(' Permit nonce:', nonce);\n\n // Read token name: name() = 0x06fdde03\n const nameHex = await evmRpcCall(rpcUrl, usdcAddress, '0x06fdde03');\n // Decode ABI-encoded string\n let tokenName = 'USD Coin';\n try {\n const offset = parseInt(nameHex.slice(2, 66), 16) * 2;\n const length = parseInt(nameHex.slice(2 + offset, 2 + offset + 64), 16);\n const hex = nameHex.slice(2 + offset + 64, 2 + offset + 64 + length * 2);\n tokenName = decodeURIComponent(hex.replace(/[0-9a-f]{2}/g, '%$&'));\n } catch {\n tokenName = extra.name || 'USD Coin';\n }\n log(' Token name:', tokenName);\n\n const deadline = Math.floor(Date.now() / 1000) + 600; // 10 min\n\n const domain = {\n name: tokenName,\n version: extra.version || '2',\n chainId,\n verifyingContract: usdcAddress,\n };\n\n const types = {\n Permit: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n };\n\n const message = {\n owner: evmWallet.address,\n spender,\n value: paymentAmount,\n nonce: String(nonce),\n deadline: String(deadline),\n };\n\n log('Signing EIP-2612 permit:', message);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'Permit',\n });\n\n // Split signature into v, r, s\n const sigHex = (signature as string).replace('0x', '');\n const r = '0x' + sigHex.slice(0, 64);\n const s = '0x' + sigHex.slice(64, 128);\n const v = parseInt(sigHex.slice(128, 130), 16);\n\n log(' Permit signed: v=%d r=%s s=%s', v, r, s);\n\n // Build x402 v2 payment payload (SKALE format)\n const paymentPayload = {\n x402Version: 2,\n scheme: 'exact',\n network: network || rawNetwork,\n payload: {\n userAddress: evmWallet.address,\n permit: { deadline: String(deadline), v, r, s },\n amount: paymentAmount,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment (EIP-3009 transferWithAuthorization)\n // -----------------------------------------------------------------------\n async function buildEvmPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n\n // EIP-3009 transferWithAuthorization typed data\n // When extra.relayerContract is present (e.g. 0xGasless), sign against the\n // relayer contract's EIP-712 domain instead of the token contract's domain.\n const useRelayer = !!extra.relayerContract;\n const domain = {\n name: useRelayer ? (extra.domainName || 'A402') : (extra.name || 'USD Coin'),\n version: useRelayer ? (extra.domainVersion || '1') : (extra.version || '2'),\n chainId,\n verifyingContract: useRelayer ? extra.relayerContract : accept.asset,\n };\n\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600;\n const nonce = '0x' + [...crypto.getRandomValues(new Uint8Array(32))]\n .map(b => b.toString(16).padStart(2, '0')).join('');\n\n const 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\n // For relayer-based facilitators (0xGasless), 'to' is the payTo (merchant).\n // For standard x402, 'to' is the feePayer/facilitator address.\n const spender = useRelayer ? accept.payTo : (extra.feePayer || accept.payTo);\n\n const message = {\n from: evmWallet.address,\n to: spender,\n value: paymentAmount,\n validAfter: String(validAfter),\n validBefore: String(validBefore),\n nonce,\n };\n\n log('Signing EIP-3009 transferWithAuthorization on chain', chainId);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'TransferWithAuthorization',\n });\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n authorization: message,\n signature,\n },\n facilitatorUrl,\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build Solana payment (SPL transfer with fee payer sponsorship)\n // -----------------------------------------------------------------------\n async function buildSolanaPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const solWallet = effectiveWallets.solana!;\n const extra = accept.extra || {};\n\n if (!extra.feePayer) {\n throw new Error('[relai-x402] Missing feePayer in Solana payment requirements');\n }\n\n const connection = new Connection(solanaRpcUrl, 'confirmed');\n const userPubkey = new PublicKey(solWallet.publicKey!.toString());\n const merchantPubkey = new PublicKey(accept.payTo);\n const feePayerPubkey = new PublicKey(extra.feePayer);\n const mintPubkey = new PublicKey(accept.asset);\n const paymentAmount = BigInt(accept.amount || accept.maxAmountRequired);\n\n log('Building Solana SPL transfer');\n log(' User:', userPubkey.toBase58());\n log(' Merchant:', merchantPubkey.toBase58());\n log(' FeePayer:', feePayerPubkey.toBase58());\n log(' Mint:', mintPubkey.toBase58());\n log(' Amount:', paymentAmount.toString());\n\n // Determine token program (TOKEN_PROGRAM_ID vs TOKEN_2022)\n const mintInfo = await getMint(connection, mintPubkey);\n const programId = mintInfo.address.equals(mintPubkey)\n ? (mintInfo as any).owner?.toBase58?.() === TOKEN_2022_PROGRAM_ID.toBase58()\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n // Get ATAs\n const sourceAta = await getAssociatedTokenAddress(\n mintPubkey, userPubkey, false, programId,\n );\n const destinationAta = await getAssociatedTokenAddress(\n mintPubkey, merchantPubkey, true, programId,\n );\n\n log(' Source ATA:', sourceAta.toBase58());\n log(' Dest ATA:', destinationAta.toBase58());\n\n // Build transfer instruction\n const transferIx = createTransferCheckedInstruction(\n sourceAta,\n mintPubkey,\n destinationAta,\n userPubkey,\n paymentAmount,\n mintInfo.decimals,\n [],\n programId,\n );\n\n // Build versioned transaction with feePayer\n const { blockhash } = await connection.getLatestBlockhash('confirmed');\n const message = new TransactionMessage({\n payerKey: feePayerPubkey,\n recentBlockhash: blockhash,\n instructions: [transferIx],\n }).compileToV0Message();\n\n const transaction = new VersionedTransaction(message);\n\n // User signs (feePayer signs on backend/facilitator side)\n const signedTx = await solWallet.signTransaction!(transaction) as VersionedTransaction;\n log('Transaction signed by user');\n\n // Serialize to base64\n const serializedTx = Buffer.from(signedTx.serialize()).toString('base64');\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n transaction: serializedTx,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Main fetch\n // -----------------------------------------------------------------------\n async function x402Fetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n log('Request:', url);\n\n const response = await fetch(input, init);\n if (response.status !== 402) return response;\n\n log('Got 402 Payment Required');\n\n let requirements: any;\n try {\n requirements = await response.clone().json();\n } catch {\n throw new Error('[relai-x402] Failed to parse 402 response body');\n }\n\n const accepts = requirements.accepts || [];\n if (!accepts.length) throw new Error('[relai-x402] No payment options in 402 response');\n\n const selected = selectAccept(accepts);\n if (!selected) {\n const networks = accepts.map((a: any) => a.network).join(', ');\n throw new Error(`[relai-x402] No wallet available for networks: ${networks}`);\n }\n\n const { accept, chain } = selected;\n const amount = accept.amount || accept.maxAmountRequired;\n log(`Selected: ${chain} / ${accept.network} / amount=${amount}`);\n\n // Amount guard\n if (maxAmountAtomic && BigInt(amount) > BigInt(maxAmountAtomic)) {\n throw new Error(`[relai-x402] Amount ${amount} exceeds max ${maxAmountAtomic}`);\n }\n\n // Solana — build SPL transfer natively (no x402-solana dependency)\n if (chain === 'solana' && hasSolanaWallet) {\n const paymentHeader = await buildSolanaPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header (Solana)');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n // EVM — build payment header and retry\n if (chain === 'evm') {\n const evmNetwork = normalizeNetwork(accept.network || '');\n const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);\n const paymentHeader = usePermit\n ? await buildEvmPermitPayment(accept, requirements, url)\n : await buildEvmPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n throw new Error('[relai-x402] Unexpected state — no payment handler matched');\n }\n\n return { fetch: x402Fetch };\n}\n\nexport default createX402Client;\n","// src/types.ts\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** RelAI Facilitator URL */\nexport const RELAI_FACILITATOR_URL = 'https://facilitator.x402.fi';\n\n// ============================================================================\n// Supported Networks\n// ============================================================================\n\n/** All networks supported by RelAI facilitator */\nexport type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-bite' | 'polygon' | 'ethereum';\n\n/** CAIP-2 network identifiers */\nexport const NETWORK_CAIP2: Record<RelaiNetwork, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'base': 'eip155:8453',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'skale-bite': 'eip155:103698795',\n 'polygon': 'eip155:137',\n 'ethereum': 'eip155:1',\n};\n\n/** Reverse lookup: CAIP-2 → simple network name */\nexport const CAIP2_TO_NETWORK: Record<string, RelaiNetwork> = Object.fromEntries(\n Object.entries(NETWORK_CAIP2).map(([k, v]) => [v, k as RelaiNetwork])\n) as Record<string, RelaiNetwork>;\n\n/** Chain IDs for EVM networks */\nexport const CHAIN_IDS: Record<string, number> = {\n 'base': 8453,\n 'avalanche': 43114,\n 'skale-base': 1187947933,\n 'skale-bite': 103698795,\n 'polygon': 137,\n 'ethereum': 1,\n};\n\n/** USDC contract addresses per network */\nexport const USDC_ADDRESSES: Record<RelaiNetwork, string> = {\n 'solana': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n 'base': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n 'avalanche': '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',\n 'skale-base': '0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20',\n 'skale-bite': '0xc4083B1E81ceb461Ccef3FDa8A9F24F0d764B6D8',\n 'polygon': '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n 'ethereum': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n};\n\n/** Explorer URLs per network */\nexport const EXPLORER_TX_URL: Record<RelaiNetwork, (tx: string) => string> = {\n 'solana': (tx) => `https://solscan.io/tx/${tx}`,\n 'base': (tx) => `https://basescan.org/tx/${tx}`,\n 'avalanche': (tx) => `https://snowtrace.io/tx/${tx}`,\n 'skale-base': (tx) => `https://skale-base-explorer.skalenodes.com/tx/${tx}`,\n 'skale-bite': (tx) => `https://base-sepolia-testnet.explorer.skalenodes.com/tx/${tx}`,\n 'polygon': (tx) => `https://polygonscan.com/tx/${tx}`,\n 'ethereum': (tx) => `https://etherscan.io/tx/${tx}`,\n};\n\n/** Human-readable network labels */\nexport const NETWORK_LABELS: Record<RelaiNetwork, string> = {\n 'solana': 'Solana',\n 'base': 'Base',\n 'avalanche': 'Avalanche',\n 'skale-base': 'SKALE Base',\n 'skale-bite': 'SKALE BITE V2',\n 'polygon': 'Polygon',\n 'ethereum': 'Ethereum',\n};\n\n/** Legacy CAIP-2 exports for backward compatibility */\nexport const SOLANA_MAINNET_NETWORK = NETWORK_CAIP2['solana'];\nexport const BASE_MAINNET_NETWORK = NETWORK_CAIP2['base'];\n\n/** Legacy USDC exports for backward compatibility */\nexport const USDC_SOLANA = USDC_ADDRESSES['solana'];\nexport const USDC_BASE = USDC_ADDRESSES['base'];\n\n/** All supported RelAI networks list */\nexport const RELAI_NETWORKS: RelaiNetwork[] = ['solana', 'base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'];\n\n/** Check if a network is Solana-based */\nexport function isSolana(network: string): boolean {\n return network === 'solana' || network.startsWith('solana:');\n}\n\n/** Check if a network is EVM-based */\nexport function isEvm(network: string): boolean {\n return ['base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'].includes(network) || network.startsWith('eip155:');\n}\n\n/** Normalize CAIP-2 or simple name to RelaiNetwork */\nexport function normalizeNetwork(network: string): RelaiNetwork | null {\n if (RELAI_NETWORKS.includes(network as RelaiNetwork)) return network as RelaiNetwork;\n const fromCaip2 = CAIP2_TO_NETWORK[network];\n if (fromCaip2) return fromCaip2;\n // Partial match\n if (network.startsWith('solana:')) return 'solana';\n if (network.startsWith('eip155:')) {\n const chainId = parseInt(network.split(':')[1]);\n const entry = Object.entries(CHAIN_IDS).find(([, id]) => id === chainId);\n if (entry) return entry[0] as RelaiNetwork;\n }\n return null;\n}\n\n// ============================================================================\n// Wallet Types\n// ============================================================================\n\n/** Solana wallet interface */\nexport interface SolanaWallet {\n publicKey: { toString(): string } | null;\n signTransaction: ((tx: unknown) => Promise<unknown>) | null;\n signAllTransactions?: ((txs: unknown[]) => Promise<unknown[]>) | null;\n}\n\n/** EVM wallet interface (viem-compatible) */\nexport interface EvmWallet {\n address: string;\n signTypedData: (params: unknown) => Promise<string>;\n chain?: { id: number };\n}\n\n/** Wallet set for multi-chain support */\nexport interface WalletSet {\n solana?: SolanaWallet;\n evm?: EvmWallet;\n}\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/** Extra fields in payment requirements */\nexport interface AcceptsExtra {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n}\n\n/** A single payment option */\nexport interface PaymentAccept {\n x402Version?: 1 | 2;\n scheme: string;\n network: string;\n maxAmountRequired?: string;\n amount?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: AcceptsExtra;\n resource?: string;\n description?: string;\n mimeType?: string;\n outputSchema?: unknown;\n}\n\n/** Resource info for v2 */\nexport interface ResourceInfo {\n url: string;\n description?: string;\n mimeType?: string;\n}\n\n/** Payment requirements (402 response) */\nexport interface PaymentRequired {\n x402Version: 1 | 2;\n error?: string;\n accepts: PaymentAccept[];\n resource?: ResourceInfo;\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Config Types (server-specific types are in server.ts)\n// ============================================================================\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,kBAKO;AACP,uBAMO;;;ACNA,IAAM,wBAAwB;AAU9B,IAAM,gBAA8C;AAAA,EACzD,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AACd;AAGO,IAAM,mBAAiD,OAAO;AAAA,EACnE,OAAO,QAAQ,aAAa,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAiB,CAAC;AACtE;AAGO,IAAM,YAAoC;AAAA,EAC/C,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AACd;AAGO,IAAM,iBAA+C;AAAA,EAC1D,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AACd;AAyBO,IAAM,yBAAyB,cAAc,QAAQ;AACrD,IAAM,uBAAuB,cAAc,MAAM;AAGjD,IAAM,cAAc,eAAe,QAAQ;AAC3C,IAAM,YAAY,eAAe,MAAM;AAGvC,IAAM,iBAAiC,CAAC,UAAU,QAAQ,aAAa,cAAc,cAAc,WAAW,UAAU;AAGxH,SAAS,SAAS,SAA0B;AACjD,SAAO,YAAY,YAAY,QAAQ,WAAW,SAAS;AAC7D;AAGO,SAAS,MAAM,SAA0B;AAC9C,SAAO,CAAC,QAAQ,aAAa,cAAc,cAAc,WAAW,UAAU,EAAE,SAAS,OAAO,KAAK,QAAQ,WAAW,SAAS;AACnI;AAGO,SAAS,iBAAiB,SAAsC;AACrE,MAAI,eAAe,SAAS,OAAuB,EAAG,QAAO;AAC7D,QAAM,YAAY,iBAAiB,OAAO;AAC1C,MAAI,UAAW,QAAO;AAEtB,MAAI,QAAQ,WAAW,SAAS,EAAG,QAAO;AAC1C,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,UAAM,UAAU,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC;AAC9C,UAAM,QAAQ,OAAO,QAAQ,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,MAAM,OAAO,OAAO;AACvE,QAAI,MAAO,QAAO,MAAM,CAAC;AAAA,EAC3B;AACA,SAAO;AACT;;;ADrCA,IAAM,kBAAkB,oBAAI,IAAY,CAAC,CAAC;AAG1C,IAAM,uBAA+C;AAAA,EACnD,cAAc;AAAA,EACd,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,WAAW;AAAA,EACX,YAAY;AACd;AAEO,SAAS,iBAAiB,QAAsC;AACrE,QAAM;AAAA,IACJ,UAAU,CAAC;AAAA,IACX,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB;AAAA,IACA,eAAe;AAAA,IACf,aAAa,CAAC;AAAA,IACd;AAAA,IACA,UAAU;AAAA,EACZ,IAAI;AAEJ,QAAM,MAAM,UAAU,QAAQ,IAAI,KAAK,SAAS,cAAc,IAAI,MAAM;AAAA,EAAC;AAGzE,QAAM,mBAA8B,EAAE,GAAG,QAAQ;AACjD,MAAI,gBAAgB,CAAC,iBAAiB,QAAQ;AAC5C,qBAAiB,SAAS;AAAA,EAC5B;AAEA,QAAM,kBAAkB;AAAA,IACtB,iBAAiB,QAAQ,aAAa,iBAAiB,QAAQ;AAAA,EACjE;AACA,MAAI,gBAAiB,KAAI,qBAAqB;AAK9C,WAAS,aAAa,SAAiE;AAErF,QAAI,kBAAkB;AACpB,YAAM,QAAQ,cAAc,gBAAgB;AAC5C,iBAAW,KAAK,SAAS;AACvB,cAAM,MAAM,EAAE,WAAW;AACzB,YAAI,QAAQ,oBAAoB,QAAQ,OAAO;AAC7C,gBAAM,QAAQ,SAAS,GAAG,IAAI,WAAoB;AAClD,cAAK,UAAU,YAAY,mBAAqB,UAAU,SAAS,iBAAiB,KAAM;AACxF,mBAAO,EAAE,QAAQ,GAAG,MAAM;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,eAAW,KAAK,SAAS;AACvB,YAAM,MAAM,EAAE,WAAW;AACzB,UAAI,SAAS,GAAG,KAAK,gBAAiB,QAAO,EAAE,QAAQ,GAAG,OAAO,SAAS;AAC1E,UAAI,MAAM,GAAG,KAAK,iBAAiB,IAAK,QAAO,EAAE,QAAQ,GAAG,OAAO,MAAM;AAAA,IAC3E;AAEA,WAAO;AAAA,EACT;AAKA,iBAAe,WAAW,QAAgB,IAAY,MAA+B;AACnF,UAAM,MAAM,MAAM,MAAM,QAAQ;AAAA,MAC9B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,CAAC,EAAE,IAAI,KAAK,GAAG,QAAQ;AAAA,QAC/B,IAAI;AAAA,MACN,CAAC;AAAA,IACH,CAAC;AACD,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,KAAK,MAAO,OAAM,IAAI,MAAM,cAAc,KAAK,MAAM,OAAO,EAAE;AAClE,WAAO,KAAK;AAAA,EACd;AAEA,WAAS,aAAa,SAAyB;AAC7C,WAAO,WAAW,OAAO,KAAK,qBAAqB,OAAO,KAAK;AAAA,EACjE;AAKA,iBAAe,sBACb,QACA,cACA,KACiB;AACjB,UAAM,YAAY,iBAAiB;AACnC,UAAM,QAAQ,OAAO,SAAS,CAAC;AAE/B,UAAM,aAAa,OAAO,WAAW;AACrC,UAAM,UAAU,iBAAiB,UAAU;AAC3C,UAAM,UAAU,UAAU,UAAU,OAAO,IAAI,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM;AAC1F,UAAM,gBAAgB,OAAO,UAAU,OAAO;AAC9C,UAAM,UAAU,MAAM,YAAY,OAAO;AACzC,UAAM,cAAc,OAAO;AAE3B,UAAM,SAAS,aAAa,WAAW,UAAU;AACjD,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2CAA2C,WAAW,UAAU,EAAE;AAE/F,QAAI,qCAAqC,OAAO;AAGhD,UAAM,gBAAgB,UAAU,QAAQ,YAAY,EAAE,QAAQ,MAAM,EAAE,EAAE,SAAS,IAAI,GAAG;AACxF,UAAM,WAAW,MAAM,WAAW,QAAQ,aAAa,eAAe,aAAa;AACnF,UAAM,QAAQ,WAAW,SAAS,UAAU,EAAE,IAAI;AAClD,QAAI,MAAM,KAAK,EAAG,OAAM,IAAI,MAAM,iDAAiD,WAAW,OAAO,MAAM,EAAE;AAC7G,QAAI,mBAAmB,KAAK;AAG5B,UAAM,UAAU,MAAM,WAAW,QAAQ,aAAa,YAAY;AAElE,QAAI,YAAY;AAChB,QAAI;AACF,YAAM,SAAS,SAAS,QAAQ,MAAM,GAAG,EAAE,GAAG,EAAE,IAAI;AACpD,YAAM,SAAS,SAAS,QAAQ,MAAM,IAAI,QAAQ,IAAI,SAAS,EAAE,GAAG,EAAE;AACtE,YAAM,MAAM,QAAQ,MAAM,IAAI,SAAS,IAAI,IAAI,SAAS,KAAK,SAAS,CAAC;AACvE,kBAAY,mBAAmB,IAAI,QAAQ,gBAAgB,KAAK,CAAC;AAAA,IACnE,QAAQ;AACN,kBAAY,MAAM,QAAQ;AAAA,IAC5B;AACA,QAAI,iBAAiB,SAAS;AAE9B,UAAM,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AAEjD,UAAM,SAAS;AAAA,MACb,MAAM;AAAA,MACN,SAAS,MAAM,WAAW;AAAA,MAC1B;AAAA,MACA,mBAAmB;AAAA,IACrB;AAEA,UAAM,QAAQ;AAAA,MACZ,QAAQ;AAAA,QACN,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,QACnC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,MACtC;AAAA,IACF;AAEA,UAAM,UAAU;AAAA,MACd,OAAO,UAAU;AAAA,MACjB;AAAA,MACA,OAAO;AAAA,MACP,OAAO,OAAO,KAAK;AAAA,MACnB,UAAU,OAAO,QAAQ;AAAA,IAC3B;AAEA,QAAI,4BAA4B,OAAO;AAEvC,UAAM,YAAY,MAAM,UAAU,cAAc;AAAA,MAC9C;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AAGD,UAAM,SAAU,UAAqB,QAAQ,MAAM,EAAE;AACrD,UAAM,IAAI,OAAO,OAAO,MAAM,GAAG,EAAE;AACnC,UAAM,IAAI,OAAO,OAAO,MAAM,IAAI,GAAG;AACrC,UAAM,IAAI,SAAS,OAAO,MAAM,KAAK,GAAG,GAAG,EAAE;AAE7C,QAAI,mCAAmC,GAAG,GAAG,CAAC;AAG9C,UAAM,iBAAiB;AAAA,MACrB,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,SAAS,WAAW;AAAA,MACpB,SAAS;AAAA,QACP,aAAa,UAAU;AAAA,QACvB,QAAQ,EAAE,UAAU,OAAO,QAAQ,GAAG,GAAG,GAAG,EAAE;AAAA,QAC9C,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,UAAU,cAAc,CAAC;AAAA,EAC5C;AAKA,iBAAe,gBACb,QACA,cACA,KACiB;AACjB,UAAM,YAAY,iBAAiB;AACnC,UAAM,QAAQ,OAAO,SAAS,CAAC;AAE/B,UAAM,aAAa,OAAO,WAAW;AACrC,UAAM,UAAU,iBAAiB,UAAU;AAC3C,UAAM,UAAU,UAAU,UAAU,OAAO,IAAI,SAAS,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM;AAE1F,UAAM,gBAAgB,OAAO,UAAU,OAAO;AAK9C,UAAM,aAAa,CAAC,CAAC,MAAM;AAC3B,UAAM,SAAS;AAAA,MACb,MAAM,aAAc,MAAM,cAAc,SAAW,MAAM,QAAQ;AAAA,MACjE,SAAS,aAAc,MAAM,iBAAiB,MAAQ,MAAM,WAAW;AAAA,MACvE;AAAA,MACA,mBAAmB,aAAa,MAAM,kBAAkB,OAAO;AAAA,IACjE;AAEA,UAAM,aAAa;AACnB,UAAM,cAAc,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AACpD,UAAM,QAAQ,OAAO,CAAC,GAAG,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,CAAC,EAChE,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAEpD,UAAM,QAAQ;AAAA,MACZ,2BAA2B;AAAA,QACzB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,QAChC,EAAE,MAAM,MAAM,MAAM,UAAU;AAAA,QAC9B,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,QACtC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,QACvC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACnC;AAAA,IACF;AAIA,UAAM,UAAU,aAAa,OAAO,QAAS,MAAM,YAAY,OAAO;AAEtE,UAAM,UAAU;AAAA,MACd,MAAM,UAAU;AAAA,MAChB,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,YAAY,OAAO,UAAU;AAAA,MAC7B,aAAa,OAAO,WAAW;AAAA,MAC/B;AAAA,IACF;AAEA,QAAI,uDAAuD,OAAO;AAElE,UAAM,YAAY,MAAM,UAAU,cAAc;AAAA,MAC9C;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,IACf,CAAC;AAGD,UAAM,iBAAiB;AAAA,MACrB,aAAa;AAAA,MACb,UAAU,aAAa,YAAY,EAAE,IAAI;AAAA,MACzC,UAAU;AAAA,MACV,SAAS;AAAA,QACP,eAAe;AAAA,QACf;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,UAAU,cAAc,CAAC;AAAA,EAC5C;AAKA,iBAAe,mBACb,QACA,cACA,KACiB;AACjB,UAAM,YAAY,iBAAiB;AACnC,UAAM,QAAQ,OAAO,SAAS,CAAC;AAE/B,QAAI,CAAC,MAAM,UAAU;AACnB,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAEA,UAAM,aAAa,IAAI,uBAAW,cAAc,WAAW;AAC3D,UAAM,aAAa,IAAI,sBAAU,UAAU,UAAW,SAAS,CAAC;AAChE,UAAM,iBAAiB,IAAI,sBAAU,OAAO,KAAK;AACjD,UAAM,iBAAiB,IAAI,sBAAU,MAAM,QAAQ;AACnD,UAAM,aAAa,IAAI,sBAAU,OAAO,KAAK;AAC7C,UAAM,gBAAgB,OAAO,OAAO,UAAU,OAAO,iBAAiB;AAEtE,QAAI,8BAA8B;AAClC,QAAI,WAAW,WAAW,SAAS,CAAC;AACpC,QAAI,eAAe,eAAe,SAAS,CAAC;AAC5C,QAAI,eAAe,eAAe,SAAS,CAAC;AAC5C,QAAI,WAAW,WAAW,SAAS,CAAC;AACpC,QAAI,aAAa,cAAc,SAAS,CAAC;AAGzC,UAAM,WAAW,UAAM,0BAAQ,YAAY,UAAU;AACrD,UAAM,YAAY,SAAS,QAAQ,OAAO,UAAU,IAC/C,SAAiB,OAAO,WAAW,MAAM,uCAAsB,SAAS,IACvE,yCACA,oCACF;AAGJ,UAAM,YAAY,UAAM;AAAA,MACtB;AAAA,MAAY;AAAA,MAAY;AAAA,MAAO;AAAA,IACjC;AACA,UAAM,iBAAiB,UAAM;AAAA,MAC3B;AAAA,MAAY;AAAA,MAAgB;AAAA,MAAM;AAAA,IACpC;AAEA,QAAI,iBAAiB,UAAU,SAAS,CAAC;AACzC,QAAI,eAAe,eAAe,SAAS,CAAC;AAG5C,UAAM,iBAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,CAAC;AAAA,MACD;AAAA,IACF;AAGA,UAAM,EAAE,UAAU,IAAI,MAAM,WAAW,mBAAmB,WAAW;AACrE,UAAM,UAAU,IAAI,+BAAmB;AAAA,MACrC,UAAU;AAAA,MACV,iBAAiB;AAAA,MACjB,cAAc,CAAC,UAAU;AAAA,IAC3B,CAAC,EAAE,mBAAmB;AAEtB,UAAM,cAAc,IAAI,iCAAqB,OAAO;AAGpD,UAAM,WAAW,MAAM,UAAU,gBAAiB,WAAW;AAC7D,QAAI,4BAA4B;AAGhC,UAAM,eAAe,OAAO,KAAK,SAAS,UAAU,CAAC,EAAE,SAAS,QAAQ;AAGxE,UAAM,iBAAiB;AAAA,MACrB,aAAa;AAAA,MACb,UAAU,aAAa,YAAY,EAAE,IAAI;AAAA,MACzC,UAAU;AAAA,MACV,SAAS;AAAA,QACP,aAAa;AAAA,MACf;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,UAAU,cAAc,CAAC;AAAA,EAC5C;AAKA,iBAAe,UACb,OACA,MACmB;AACnB,UAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAO,MAAM;AAC1F,QAAI,YAAY,GAAG;AAEnB,UAAM,WAAW,MAAM,MAAM,OAAO,IAAI;AACxC,QAAI,SAAS,WAAW,IAAK,QAAO;AAEpC,QAAI,0BAA0B;AAE9B,QAAI;AACJ,QAAI;AACF,qBAAe,MAAM,SAAS,MAAM,EAAE,KAAK;AAAA,IAC7C,QAAQ;AACN,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,UAAM,UAAU,aAAa,WAAW,CAAC;AACzC,QAAI,CAAC,QAAQ,OAAQ,OAAM,IAAI,MAAM,iDAAiD;AAEtF,UAAM,WAAW,aAAa,OAAO;AACrC,QAAI,CAAC,UAAU;AACb,YAAM,WAAW,QAAQ,IAAI,CAAC,MAAW,EAAE,OAAO,EAAE,KAAK,IAAI;AAC7D,YAAM,IAAI,MAAM,kDAAkD,QAAQ,EAAE;AAAA,IAC9E;AAEA,UAAM,EAAE,QAAQ,MAAM,IAAI;AAC1B,UAAM,SAAS,OAAO,UAAU,OAAO;AACvC,QAAI,aAAa,KAAK,MAAM,OAAO,OAAO,aAAa,MAAM,EAAE;AAG/D,QAAI,mBAAmB,OAAO,MAAM,IAAI,OAAO,eAAe,GAAG;AAC/D,YAAM,IAAI,MAAM,uBAAuB,MAAM,gBAAgB,eAAe,EAAE;AAAA,IAChF;AAGA,QAAI,UAAU,YAAY,iBAAiB;AACzC,YAAM,gBAAgB,MAAM,mBAAmB,QAAQ,cAAc,GAAG;AACxE,UAAI,yCAAyC;AAC7C,aAAO,MAAM,OAAO;AAAA,QAClB,GAAG;AAAA,QACH,SAAS;AAAA,UACP,GAAI,MAAM,WAAW,CAAC;AAAA,UACtB,aAAa;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH;AAGA,QAAI,UAAU,OAAO;AACnB,YAAM,aAAa,iBAAiB,OAAO,WAAW,EAAE;AACxD,YAAM,YAAY,cAAc,gBAAgB,IAAI,UAAU;AAC9D,YAAM,gBAAgB,YAClB,MAAM,sBAAsB,QAAQ,cAAc,GAAG,IACrD,MAAM,gBAAgB,QAAQ,cAAc,GAAG;AACnD,UAAI,gCAAgC;AACpC,aAAO,MAAM,OAAO;AAAA,QAClB,GAAG;AAAA,QACH,SAAS;AAAA,UACP,GAAI,MAAM,WAAW,CAAC;AAAA,UACtB,aAAa;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,IAAI,MAAM,iEAA4D;AAAA,EAC9E;AAEA,SAAO,EAAE,OAAO,UAAU;AAC5B;AAEA,IAAO,iBAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["// src/client.ts\nimport {\n Connection,\n PublicKey,\n TransactionMessage,\n VersionedTransaction,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from '@solana/spl-token';\nimport type { SolanaWallet, EvmWallet, WalletSet } from './types';\nimport {\n RELAI_FACILITATOR_URL,\n NETWORK_CAIP2,\n CHAIN_IDS,\n isSolana,\n isEvm,\n normalizeNetwork,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface X402ClientConfig {\n /** Multi-chain wallets (Solana + EVM) */\n wallets?: WalletSet;\n /** Single Solana wallet (legacy shortcut) */\n wallet?: SolanaWallet;\n /** Custom facilitator URL, default: RelAI facilitator */\n facilitatorUrl?: string;\n /** Preferred network when multiple options available */\n preferredNetwork?: RelaiNetwork;\n /** Custom Solana RPC URL */\n solanaRpcUrl?: string;\n /** Custom EVM RPC URLs per network (e.g. { 'skale-base': 'https://...' }) */\n evmRpcUrls?: Record<string, string>;\n /** Maximum payment amount in atomic units */\n maxAmountAtomic?: string;\n /** Enable verbose logging */\n verbose?: boolean;\n}\n\nexport interface X402Client {\n /** Fetch with automatic x402 payment handling */\n fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n}\n\n/**\n * Create an x402 client for automatic payment handling.\n * Supports all RelAI facilitator networks: Solana, Base, Avalanche, SKALE Base.\n * Auto-detects the correct chain from the 402 response and picks the right\n * signing method (Solana SPL transfer, EVM EIP-3009 transferWithAuthorization).\n *\n * @example\n * ```typescript\n * import { createX402Client } from '@relai-fi/x402';\n *\n * const client = createX402Client({\n * wallets: { solana: solanaWallet, evm: evmWalletClient },\n * });\n *\n * // Automatically handles 402 on any RelAI-supported network\n * const response = await client.fetch('https://api.example.com/protected');\n * ```\n */\n// Networks that use EIP-2612 permit instead of EIP-3009 transferWithAuthorization (currently none)\nconst PERMIT_NETWORKS = new Set<string>([]);\n\n// Default EVM RPC URLs\nconst DEFAULT_EVM_RPC_URLS: Record<string, string> = {\n 'skale-base': 'https://skale-base.skalenodes.com/v1/base',\n 'skale-bite': 'https://base-sepolia-testnet.skalenodes.com/v1/bite-v2-sandbox',\n 'base': 'https://mainnet.base.org',\n 'avalanche': 'https://api.avax.network/ext/bc/C/rpc',\n 'polygon': 'https://polygon-rpc.com',\n 'ethereum': 'https://ethereum-rpc.publicnode.com',\n};\n\nexport function createX402Client(config: X402ClientConfig): X402Client {\n const {\n wallets = {},\n wallet: legacyWallet,\n facilitatorUrl = RELAI_FACILITATOR_URL,\n preferredNetwork,\n solanaRpcUrl = 'https://api.mainnet-beta.solana.com',\n evmRpcUrls = {},\n maxAmountAtomic,\n verbose = false,\n } = config;\n\n const log = verbose ? console.log.bind(console, '[relai-x402]') : () => {};\n\n // Merge legacy wallet into wallet set\n const effectiveWallets: WalletSet = { ...wallets };\n if (legacyWallet && !effectiveWallets.solana) {\n effectiveWallets.solana = legacyWallet;\n }\n\n const hasSolanaWallet = Boolean(\n effectiveWallets.solana?.publicKey && effectiveWallets.solana?.signTransaction\n );\n if (hasSolanaWallet) log('Solana wallet ready');\n\n // -----------------------------------------------------------------------\n // Select a payment option from the 402 response's `accepts` array\n // -----------------------------------------------------------------------\n function selectAccept(accepts: any[]): { accept: any; chain: 'solana' | 'evm' } | null {\n // 1) Preferred network first\n if (preferredNetwork) {\n const caip2 = NETWORK_CAIP2[preferredNetwork];\n for (const a of accepts) {\n const net = a.network || '';\n if (net === preferredNetwork || net === caip2) {\n const chain = isSolana(net) ? 'solana' as const : 'evm' as const;\n if ((chain === 'solana' && hasSolanaWallet) || (chain === 'evm' && effectiveWallets.evm)) {\n return { accept: a, chain };\n }\n }\n }\n }\n\n // 2) First option we have a wallet for\n for (const a of accepts) {\n const net = a.network || '';\n if (isSolana(net) && hasSolanaWallet) return { accept: a, chain: 'solana' };\n if (isEvm(net) && effectiveWallets.evm) return { accept: a, chain: 'evm' };\n }\n\n return null;\n }\n\n // -----------------------------------------------------------------------\n // JSON-RPC helper (for reading EVM contract state without ethers)\n // -----------------------------------------------------------------------\n async function evmRpcCall(rpcUrl: string, to: string, data: string): Promise<string> {\n const res = await fetch(rpcUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n method: 'eth_call',\n params: [{ to, data }, 'latest'],\n id: 1,\n }),\n });\n const json = await res.json();\n if (json.error) throw new Error(`RPC error: ${json.error.message}`);\n return json.result;\n }\n\n function getEvmRpcUrl(network: string): string {\n return evmRpcUrls[network] || DEFAULT_EVM_RPC_URLS[network] || '';\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment — EIP-2612 permit (for SKALE Base)\n // -----------------------------------------------------------------------\n async function buildEvmPermitPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n const spender = extra.feePayer || accept.payTo;\n const usdcAddress = accept.asset;\n\n const rpcUrl = getEvmRpcUrl(network || rawNetwork);\n if (!rpcUrl) throw new Error(`[relai-x402] No EVM RPC URL for network ${network || rawNetwork}`);\n\n log('Building EIP-2612 permit on chain', chainId);\n\n // Read nonce from USDC contract: nonces(address) = 0x7ecebe00\n const paddedAddress = evmWallet.address.toLowerCase().replace('0x', '').padStart(64, '0');\n const nonceHex = await evmRpcCall(rpcUrl, usdcAddress, '0x7ecebe00' + paddedAddress);\n const nonce = nonceHex ? parseInt(nonceHex, 16) : 0;\n if (isNaN(nonce)) throw new Error(`[relai-x402] Failed to read permit nonce from ${usdcAddress} on ${rpcUrl}`);\n log(' Permit nonce:', nonce);\n\n // Read token name: name() = 0x06fdde03\n const nameHex = await evmRpcCall(rpcUrl, usdcAddress, '0x06fdde03');\n // Decode ABI-encoded string\n let tokenName = 'USD Coin';\n try {\n const offset = parseInt(nameHex.slice(2, 66), 16) * 2;\n const length = parseInt(nameHex.slice(2 + offset, 2 + offset + 64), 16);\n const hex = nameHex.slice(2 + offset + 64, 2 + offset + 64 + length * 2);\n tokenName = decodeURIComponent(hex.replace(/[0-9a-f]{2}/g, '%$&'));\n } catch {\n tokenName = extra.name || 'USD Coin';\n }\n log(' Token name:', tokenName);\n\n const deadline = Math.floor(Date.now() / 1000) + 600; // 10 min\n\n const domain = {\n name: tokenName,\n version: extra.version || '2',\n chainId,\n verifyingContract: usdcAddress,\n };\n\n const types = {\n Permit: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n };\n\n const message = {\n owner: evmWallet.address,\n spender,\n value: paymentAmount,\n nonce: String(nonce),\n deadline: String(deadline),\n };\n\n log('Signing EIP-2612 permit:', message);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'Permit',\n });\n\n // Split signature into v, r, s\n const sigHex = (signature as string).replace('0x', '');\n const r = '0x' + sigHex.slice(0, 64);\n const s = '0x' + sigHex.slice(64, 128);\n const v = parseInt(sigHex.slice(128, 130), 16);\n\n log(' Permit signed: v=%d r=%s s=%s', v, r, s);\n\n // Build x402 v2 payment payload (SKALE format)\n const paymentPayload = {\n x402Version: 2,\n scheme: 'exact',\n network: network || rawNetwork,\n payload: {\n userAddress: evmWallet.address,\n permit: { deadline: String(deadline), v, r, s },\n amount: paymentAmount,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment (EIP-3009 transferWithAuthorization)\n // -----------------------------------------------------------------------\n async function buildEvmPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n\n // For relayer-based facilitators (0xGasless), 'to' is the payTo (merchant).\n // For standard x402 (RelAI), 'to' is also payTo (merchant).\n const useRelayer = !!extra.relayerContract;\n\n // EIP-3009 transferWithAuthorization typed data\n // When extra.relayerContract is present (e.g. 0xGasless), sign against the\n // relayer contract's EIP-712 domain instead of the token contract's domain.\n const domain = {\n name: useRelayer ? (extra.domainName || 'A402') : (extra.name || 'USDC'),\n version: useRelayer ? (extra.domainVersion || '1') : (extra.version || '1'),\n chainId,\n verifyingContract: useRelayer ? extra.relayerContract : accept.asset,\n };\n\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600;\n const nonce = '0x' + [...crypto.getRandomValues(new Uint8Array(32))]\n .map(b => b.toString(16).padStart(2, '0')).join('');\n\n const 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\n const message = {\n from: evmWallet.address,\n to: accept.payTo,\n value: paymentAmount,\n validAfter: String(validAfter),\n validBefore: String(validBefore),\n nonce,\n };\n\n log('Signing EIP-3009 transferWithAuthorization on chain', chainId);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'TransferWithAuthorization',\n });\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n authorization: message,\n signature,\n },\n facilitatorUrl,\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build Solana payment (SPL transfer with fee payer sponsorship)\n // -----------------------------------------------------------------------\n async function buildSolanaPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const solWallet = effectiveWallets.solana!;\n const extra = accept.extra || {};\n\n if (!extra.feePayer) {\n throw new Error('[relai-x402] Missing feePayer in Solana payment requirements');\n }\n\n const connection = new Connection(solanaRpcUrl, 'confirmed');\n const userPubkey = new PublicKey(solWallet.publicKey!.toString());\n const merchantPubkey = new PublicKey(accept.payTo);\n const feePayerPubkey = new PublicKey(extra.feePayer);\n const mintPubkey = new PublicKey(accept.asset);\n const paymentAmount = BigInt(accept.amount || accept.maxAmountRequired);\n\n log('Building Solana SPL transfer');\n log(' User:', userPubkey.toBase58());\n log(' Merchant:', merchantPubkey.toBase58());\n log(' FeePayer:', feePayerPubkey.toBase58());\n log(' Mint:', mintPubkey.toBase58());\n log(' Amount:', paymentAmount.toString());\n\n // Determine token program (TOKEN_PROGRAM_ID vs TOKEN_2022)\n const mintInfo = await getMint(connection, mintPubkey);\n const programId = mintInfo.address.equals(mintPubkey)\n ? (mintInfo as any).owner?.toBase58?.() === TOKEN_2022_PROGRAM_ID.toBase58()\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n // Get ATAs\n const sourceAta = await getAssociatedTokenAddress(\n mintPubkey, userPubkey, false, programId,\n );\n const destinationAta = await getAssociatedTokenAddress(\n mintPubkey, merchantPubkey, true, programId,\n );\n\n log(' Source ATA:', sourceAta.toBase58());\n log(' Dest ATA:', destinationAta.toBase58());\n\n // Build transfer instruction\n const transferIx = createTransferCheckedInstruction(\n sourceAta,\n mintPubkey,\n destinationAta,\n userPubkey,\n paymentAmount,\n mintInfo.decimals,\n [],\n programId,\n );\n\n // Build versioned transaction with feePayer\n const { blockhash } = await connection.getLatestBlockhash('confirmed');\n const message = new TransactionMessage({\n payerKey: feePayerPubkey,\n recentBlockhash: blockhash,\n instructions: [transferIx],\n }).compileToV0Message();\n\n const transaction = new VersionedTransaction(message);\n\n // User signs (feePayer signs on backend/facilitator side)\n const signedTx = await solWallet.signTransaction!(transaction) as VersionedTransaction;\n log('Transaction signed by user');\n\n // Serialize to base64\n const serializedTx = Buffer.from(signedTx.serialize()).toString('base64');\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n transaction: serializedTx,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Main fetch\n // -----------------------------------------------------------------------\n async function x402Fetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n log('Request:', url);\n\n const response = await fetch(input, init);\n if (response.status !== 402) return response;\n\n log('Got 402 Payment Required');\n\n let requirements: any;\n try {\n requirements = await response.clone().json();\n } catch {\n throw new Error('[relai-x402] Failed to parse 402 response body');\n }\n\n const accepts = requirements.accepts || [];\n if (!accepts.length) throw new Error('[relai-x402] No payment options in 402 response');\n\n const selected = selectAccept(accepts);\n if (!selected) {\n const networks = accepts.map((a: any) => a.network).join(', ');\n throw new Error(`[relai-x402] No wallet available for networks: ${networks}`);\n }\n\n const { accept, chain } = selected;\n const amount = accept.amount || accept.maxAmountRequired;\n log(`Selected: ${chain} / ${accept.network} / amount=${amount}`);\n\n // Amount guard\n if (maxAmountAtomic && BigInt(amount) > BigInt(maxAmountAtomic)) {\n throw new Error(`[relai-x402] Amount ${amount} exceeds max ${maxAmountAtomic}`);\n }\n\n // Solana — build SPL transfer natively (no x402-solana dependency)\n if (chain === 'solana' && hasSolanaWallet) {\n const paymentHeader = await buildSolanaPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header (Solana)');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n // EVM — build payment header and retry\n if (chain === 'evm') {\n const evmNetwork = normalizeNetwork(accept.network || '');\n const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);\n const paymentHeader = usePermit\n ? await buildEvmPermitPayment(accept, requirements, url)\n : await buildEvmPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n throw new Error('[relai-x402] Unexpected state — no payment handler matched');\n }\n\n return { fetch: x402Fetch };\n}\n\nexport default createX402Client;\n","// src/types.ts\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** RelAI Facilitator URL */\nexport const RELAI_FACILITATOR_URL = 'https://facilitator.x402.fi';\n\n// ============================================================================\n// Supported Networks\n// ============================================================================\n\n/** All networks supported by RelAI facilitator */\nexport type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-bite' | 'polygon' | 'ethereum';\n\n/** CAIP-2 network identifiers */\nexport const NETWORK_CAIP2: Record<RelaiNetwork, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'base': 'eip155:8453',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'skale-bite': 'eip155:103698795',\n 'polygon': 'eip155:137',\n 'ethereum': 'eip155:1',\n};\n\n/** Reverse lookup: CAIP-2 → simple network name */\nexport const CAIP2_TO_NETWORK: Record<string, RelaiNetwork> = Object.fromEntries(\n Object.entries(NETWORK_CAIP2).map(([k, v]) => [v, k as RelaiNetwork])\n) as Record<string, RelaiNetwork>;\n\n/** Chain IDs for EVM networks */\nexport const CHAIN_IDS: Record<string, number> = {\n 'base': 8453,\n 'avalanche': 43114,\n 'skale-base': 1187947933,\n 'skale-bite': 103698795,\n 'polygon': 137,\n 'ethereum': 1,\n};\n\n/** USDC contract addresses per network */\nexport const USDC_ADDRESSES: Record<RelaiNetwork, string> = {\n 'solana': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n 'base': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n 'avalanche': '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',\n 'skale-base': '0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20',\n 'skale-bite': '0xc4083B1E81ceb461Ccef3FDa8A9F24F0d764B6D8',\n 'polygon': '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n 'ethereum': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n};\n\n/** Explorer URLs per network */\nexport const EXPLORER_TX_URL: Record<RelaiNetwork, (tx: string) => string> = {\n 'solana': (tx) => `https://solscan.io/tx/${tx}`,\n 'base': (tx) => `https://basescan.org/tx/${tx}`,\n 'avalanche': (tx) => `https://snowtrace.io/tx/${tx}`,\n 'skale-base': (tx) => `https://skale-base-explorer.skalenodes.com/tx/${tx}`,\n 'skale-bite': (tx) => `https://base-sepolia-testnet.explorer.skalenodes.com/tx/${tx}`,\n 'polygon': (tx) => `https://polygonscan.com/tx/${tx}`,\n 'ethereum': (tx) => `https://etherscan.io/tx/${tx}`,\n};\n\n/** Human-readable network labels */\nexport const NETWORK_LABELS: Record<RelaiNetwork, string> = {\n 'solana': 'Solana',\n 'base': 'Base',\n 'avalanche': 'Avalanche',\n 'skale-base': 'SKALE Base',\n 'skale-bite': 'SKALE BITE V2',\n 'polygon': 'Polygon',\n 'ethereum': 'Ethereum',\n};\n\n/** Legacy CAIP-2 exports for backward compatibility */\nexport const SOLANA_MAINNET_NETWORK = NETWORK_CAIP2['solana'];\nexport const BASE_MAINNET_NETWORK = NETWORK_CAIP2['base'];\n\n/** Legacy USDC exports for backward compatibility */\nexport const USDC_SOLANA = USDC_ADDRESSES['solana'];\nexport const USDC_BASE = USDC_ADDRESSES['base'];\n\n/** All supported RelAI networks list */\nexport const RELAI_NETWORKS: RelaiNetwork[] = ['solana', 'base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'];\n\n/** Check if a network is Solana-based */\nexport function isSolana(network: string): boolean {\n return network === 'solana' || network.startsWith('solana:');\n}\n\n/** Check if a network is EVM-based */\nexport function isEvm(network: string): boolean {\n return ['base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'].includes(network) || network.startsWith('eip155:');\n}\n\n/** Normalize CAIP-2 or simple name to RelaiNetwork */\nexport function normalizeNetwork(network: string): RelaiNetwork | null {\n if (RELAI_NETWORKS.includes(network as RelaiNetwork)) return network as RelaiNetwork;\n const fromCaip2 = CAIP2_TO_NETWORK[network];\n if (fromCaip2) return fromCaip2;\n // Partial match\n if (network.startsWith('solana:')) return 'solana';\n if (network.startsWith('eip155:')) {\n const chainId = parseInt(network.split(':')[1]);\n const entry = Object.entries(CHAIN_IDS).find(([, id]) => id === chainId);\n if (entry) return entry[0] as RelaiNetwork;\n }\n return null;\n}\n\n// ============================================================================\n// Wallet Types\n// ============================================================================\n\n/** Solana wallet interface */\nexport interface SolanaWallet {\n publicKey: { toString(): string } | null;\n signTransaction: ((tx: unknown) => Promise<unknown>) | null;\n signAllTransactions?: ((txs: unknown[]) => Promise<unknown[]>) | null;\n}\n\n/** EVM wallet interface (viem-compatible) */\nexport interface EvmWallet {\n address: string;\n signTypedData: (params: unknown) => Promise<string>;\n chain?: { id: number };\n}\n\n/** Wallet set for multi-chain support */\nexport interface WalletSet {\n solana?: SolanaWallet;\n evm?: EvmWallet;\n}\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/** Extra fields in payment requirements */\nexport interface AcceptsExtra {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n}\n\n/** A single payment option */\nexport interface PaymentAccept {\n x402Version?: 1 | 2;\n scheme: string;\n network: string;\n maxAmountRequired?: string;\n amount?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: AcceptsExtra;\n resource?: string;\n description?: string;\n mimeType?: string;\n outputSchema?: unknown;\n}\n\n/** Resource info for v2 */\nexport interface ResourceInfo {\n url: string;\n description?: string;\n mimeType?: string;\n}\n\n/** Payment requirements (402 response) */\nexport interface PaymentRequired {\n x402Version: 1 | 2;\n error?: string;\n accepts: PaymentAccept[];\n resource?: ResourceInfo;\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Config Types (server-specific types are in server.ts)\n// ============================================================================\n"],"mappings":"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","names":[]}
package/dist/client.js CHANGED
@@ -221,8 +221,8 @@ function createX402Client(config) {
221
221
  const paymentAmount = accept.amount || accept.maxAmountRequired;
222
222
  const useRelayer = !!extra.relayerContract;
223
223
  const domain = {
224
- name: useRelayer ? extra.domainName || "A402" : extra.name || "USD Coin",
225
- version: useRelayer ? extra.domainVersion || "1" : extra.version || "2",
224
+ name: useRelayer ? extra.domainName || "A402" : extra.name || "USDC",
225
+ version: useRelayer ? extra.domainVersion || "1" : extra.version || "1",
226
226
  chainId,
227
227
  verifyingContract: useRelayer ? extra.relayerContract : accept.asset
228
228
  };
@@ -239,10 +239,9 @@ function createX402Client(config) {
239
239
  { name: "nonce", type: "bytes32" }
240
240
  ]
241
241
  };
242
- const spender = useRelayer ? accept.payTo : extra.feePayer || accept.payTo;
243
242
  const message = {
244
243
  from: evmWallet.address,
245
- to: spender,
244
+ to: accept.payTo,
246
245
  value: paymentAmount,
247
246
  validAfter: String(validAfter),
248
247
  validBefore: String(validBefore),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["// src/client.ts\nimport {\n Connection,\n PublicKey,\n TransactionMessage,\n VersionedTransaction,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from '@solana/spl-token';\nimport type { SolanaWallet, EvmWallet, WalletSet } from './types';\nimport {\n RELAI_FACILITATOR_URL,\n NETWORK_CAIP2,\n CHAIN_IDS,\n isSolana,\n isEvm,\n normalizeNetwork,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface X402ClientConfig {\n /** Multi-chain wallets (Solana + EVM) */\n wallets?: WalletSet;\n /** Single Solana wallet (legacy shortcut) */\n wallet?: SolanaWallet;\n /** Custom facilitator URL, default: RelAI facilitator */\n facilitatorUrl?: string;\n /** Preferred network when multiple options available */\n preferredNetwork?: RelaiNetwork;\n /** Custom Solana RPC URL */\n solanaRpcUrl?: string;\n /** Custom EVM RPC URLs per network (e.g. { 'skale-base': 'https://...' }) */\n evmRpcUrls?: Record<string, string>;\n /** Maximum payment amount in atomic units */\n maxAmountAtomic?: string;\n /** Enable verbose logging */\n verbose?: boolean;\n}\n\nexport interface X402Client {\n /** Fetch with automatic x402 payment handling */\n fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n}\n\n/**\n * Create an x402 client for automatic payment handling.\n * Supports all RelAI facilitator networks: Solana, Base, Avalanche, SKALE Base.\n * Auto-detects the correct chain from the 402 response and picks the right\n * signing method (Solana SPL transfer, EVM EIP-3009 transferWithAuthorization).\n *\n * @example\n * ```typescript\n * import { createX402Client } from '@relai-fi/x402';\n *\n * const client = createX402Client({\n * wallets: { solana: solanaWallet, evm: evmWalletClient },\n * });\n *\n * // Automatically handles 402 on any RelAI-supported network\n * const response = await client.fetch('https://api.example.com/protected');\n * ```\n */\n// Networks that use EIP-2612 permit instead of EIP-3009 transferWithAuthorization (currently none)\nconst PERMIT_NETWORKS = new Set<string>([]);\n\n// Default EVM RPC URLs\nconst DEFAULT_EVM_RPC_URLS: Record<string, string> = {\n 'skale-base': 'https://skale-base.skalenodes.com/v1/base',\n 'skale-bite': 'https://base-sepolia-testnet.skalenodes.com/v1/bite-v2-sandbox',\n 'base': 'https://mainnet.base.org',\n 'avalanche': 'https://api.avax.network/ext/bc/C/rpc',\n 'polygon': 'https://polygon-rpc.com',\n 'ethereum': 'https://ethereum-rpc.publicnode.com',\n};\n\nexport function createX402Client(config: X402ClientConfig): X402Client {\n const {\n wallets = {},\n wallet: legacyWallet,\n facilitatorUrl = RELAI_FACILITATOR_URL,\n preferredNetwork,\n solanaRpcUrl = 'https://api.mainnet-beta.solana.com',\n evmRpcUrls = {},\n maxAmountAtomic,\n verbose = false,\n } = config;\n\n const log = verbose ? console.log.bind(console, '[relai-x402]') : () => {};\n\n // Merge legacy wallet into wallet set\n const effectiveWallets: WalletSet = { ...wallets };\n if (legacyWallet && !effectiveWallets.solana) {\n effectiveWallets.solana = legacyWallet;\n }\n\n const hasSolanaWallet = Boolean(\n effectiveWallets.solana?.publicKey && effectiveWallets.solana?.signTransaction\n );\n if (hasSolanaWallet) log('Solana wallet ready');\n\n // -----------------------------------------------------------------------\n // Select a payment option from the 402 response's `accepts` array\n // -----------------------------------------------------------------------\n function selectAccept(accepts: any[]): { accept: any; chain: 'solana' | 'evm' } | null {\n // 1) Preferred network first\n if (preferredNetwork) {\n const caip2 = NETWORK_CAIP2[preferredNetwork];\n for (const a of accepts) {\n const net = a.network || '';\n if (net === preferredNetwork || net === caip2) {\n const chain = isSolana(net) ? 'solana' as const : 'evm' as const;\n if ((chain === 'solana' && hasSolanaWallet) || (chain === 'evm' && effectiveWallets.evm)) {\n return { accept: a, chain };\n }\n }\n }\n }\n\n // 2) First option we have a wallet for\n for (const a of accepts) {\n const net = a.network || '';\n if (isSolana(net) && hasSolanaWallet) return { accept: a, chain: 'solana' };\n if (isEvm(net) && effectiveWallets.evm) return { accept: a, chain: 'evm' };\n }\n\n return null;\n }\n\n // -----------------------------------------------------------------------\n // JSON-RPC helper (for reading EVM contract state without ethers)\n // -----------------------------------------------------------------------\n async function evmRpcCall(rpcUrl: string, to: string, data: string): Promise<string> {\n const res = await fetch(rpcUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n method: 'eth_call',\n params: [{ to, data }, 'latest'],\n id: 1,\n }),\n });\n const json = await res.json();\n if (json.error) throw new Error(`RPC error: ${json.error.message}`);\n return json.result;\n }\n\n function getEvmRpcUrl(network: string): string {\n return evmRpcUrls[network] || DEFAULT_EVM_RPC_URLS[network] || '';\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment — EIP-2612 permit (for SKALE Base)\n // -----------------------------------------------------------------------\n async function buildEvmPermitPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n const spender = extra.feePayer || accept.payTo;\n const usdcAddress = accept.asset;\n\n const rpcUrl = getEvmRpcUrl(network || rawNetwork);\n if (!rpcUrl) throw new Error(`[relai-x402] No EVM RPC URL for network ${network || rawNetwork}`);\n\n log('Building EIP-2612 permit on chain', chainId);\n\n // Read nonce from USDC contract: nonces(address) = 0x7ecebe00\n const paddedAddress = evmWallet.address.toLowerCase().replace('0x', '').padStart(64, '0');\n const nonceHex = await evmRpcCall(rpcUrl, usdcAddress, '0x7ecebe00' + paddedAddress);\n const nonce = nonceHex ? parseInt(nonceHex, 16) : 0;\n if (isNaN(nonce)) throw new Error(`[relai-x402] Failed to read permit nonce from ${usdcAddress} on ${rpcUrl}`);\n log(' Permit nonce:', nonce);\n\n // Read token name: name() = 0x06fdde03\n const nameHex = await evmRpcCall(rpcUrl, usdcAddress, '0x06fdde03');\n // Decode ABI-encoded string\n let tokenName = 'USD Coin';\n try {\n const offset = parseInt(nameHex.slice(2, 66), 16) * 2;\n const length = parseInt(nameHex.slice(2 + offset, 2 + offset + 64), 16);\n const hex = nameHex.slice(2 + offset + 64, 2 + offset + 64 + length * 2);\n tokenName = decodeURIComponent(hex.replace(/[0-9a-f]{2}/g, '%$&'));\n } catch {\n tokenName = extra.name || 'USD Coin';\n }\n log(' Token name:', tokenName);\n\n const deadline = Math.floor(Date.now() / 1000) + 600; // 10 min\n\n const domain = {\n name: tokenName,\n version: extra.version || '2',\n chainId,\n verifyingContract: usdcAddress,\n };\n\n const types = {\n Permit: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n };\n\n const message = {\n owner: evmWallet.address,\n spender,\n value: paymentAmount,\n nonce: String(nonce),\n deadline: String(deadline),\n };\n\n log('Signing EIP-2612 permit:', message);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'Permit',\n });\n\n // Split signature into v, r, s\n const sigHex = (signature as string).replace('0x', '');\n const r = '0x' + sigHex.slice(0, 64);\n const s = '0x' + sigHex.slice(64, 128);\n const v = parseInt(sigHex.slice(128, 130), 16);\n\n log(' Permit signed: v=%d r=%s s=%s', v, r, s);\n\n // Build x402 v2 payment payload (SKALE format)\n const paymentPayload = {\n x402Version: 2,\n scheme: 'exact',\n network: network || rawNetwork,\n payload: {\n userAddress: evmWallet.address,\n permit: { deadline: String(deadline), v, r, s },\n amount: paymentAmount,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment (EIP-3009 transferWithAuthorization)\n // -----------------------------------------------------------------------\n async function buildEvmPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n\n // EIP-3009 transferWithAuthorization typed data\n // When extra.relayerContract is present (e.g. 0xGasless), sign against the\n // relayer contract's EIP-712 domain instead of the token contract's domain.\n const useRelayer = !!extra.relayerContract;\n const domain = {\n name: useRelayer ? (extra.domainName || 'A402') : (extra.name || 'USD Coin'),\n version: useRelayer ? (extra.domainVersion || '1') : (extra.version || '2'),\n chainId,\n verifyingContract: useRelayer ? extra.relayerContract : accept.asset,\n };\n\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600;\n const nonce = '0x' + [...crypto.getRandomValues(new Uint8Array(32))]\n .map(b => b.toString(16).padStart(2, '0')).join('');\n\n const 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\n // For relayer-based facilitators (0xGasless), 'to' is the payTo (merchant).\n // For standard x402, 'to' is the feePayer/facilitator address.\n const spender = useRelayer ? accept.payTo : (extra.feePayer || accept.payTo);\n\n const message = {\n from: evmWallet.address,\n to: spender,\n value: paymentAmount,\n validAfter: String(validAfter),\n validBefore: String(validBefore),\n nonce,\n };\n\n log('Signing EIP-3009 transferWithAuthorization on chain', chainId);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'TransferWithAuthorization',\n });\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n authorization: message,\n signature,\n },\n facilitatorUrl,\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build Solana payment (SPL transfer with fee payer sponsorship)\n // -----------------------------------------------------------------------\n async function buildSolanaPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const solWallet = effectiveWallets.solana!;\n const extra = accept.extra || {};\n\n if (!extra.feePayer) {\n throw new Error('[relai-x402] Missing feePayer in Solana payment requirements');\n }\n\n const connection = new Connection(solanaRpcUrl, 'confirmed');\n const userPubkey = new PublicKey(solWallet.publicKey!.toString());\n const merchantPubkey = new PublicKey(accept.payTo);\n const feePayerPubkey = new PublicKey(extra.feePayer);\n const mintPubkey = new PublicKey(accept.asset);\n const paymentAmount = BigInt(accept.amount || accept.maxAmountRequired);\n\n log('Building Solana SPL transfer');\n log(' User:', userPubkey.toBase58());\n log(' Merchant:', merchantPubkey.toBase58());\n log(' FeePayer:', feePayerPubkey.toBase58());\n log(' Mint:', mintPubkey.toBase58());\n log(' Amount:', paymentAmount.toString());\n\n // Determine token program (TOKEN_PROGRAM_ID vs TOKEN_2022)\n const mintInfo = await getMint(connection, mintPubkey);\n const programId = mintInfo.address.equals(mintPubkey)\n ? (mintInfo as any).owner?.toBase58?.() === TOKEN_2022_PROGRAM_ID.toBase58()\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n // Get ATAs\n const sourceAta = await getAssociatedTokenAddress(\n mintPubkey, userPubkey, false, programId,\n );\n const destinationAta = await getAssociatedTokenAddress(\n mintPubkey, merchantPubkey, true, programId,\n );\n\n log(' Source ATA:', sourceAta.toBase58());\n log(' Dest ATA:', destinationAta.toBase58());\n\n // Build transfer instruction\n const transferIx = createTransferCheckedInstruction(\n sourceAta,\n mintPubkey,\n destinationAta,\n userPubkey,\n paymentAmount,\n mintInfo.decimals,\n [],\n programId,\n );\n\n // Build versioned transaction with feePayer\n const { blockhash } = await connection.getLatestBlockhash('confirmed');\n const message = new TransactionMessage({\n payerKey: feePayerPubkey,\n recentBlockhash: blockhash,\n instructions: [transferIx],\n }).compileToV0Message();\n\n const transaction = new VersionedTransaction(message);\n\n // User signs (feePayer signs on backend/facilitator side)\n const signedTx = await solWallet.signTransaction!(transaction) as VersionedTransaction;\n log('Transaction signed by user');\n\n // Serialize to base64\n const serializedTx = Buffer.from(signedTx.serialize()).toString('base64');\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n transaction: serializedTx,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Main fetch\n // -----------------------------------------------------------------------\n async function x402Fetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n log('Request:', url);\n\n const response = await fetch(input, init);\n if (response.status !== 402) return response;\n\n log('Got 402 Payment Required');\n\n let requirements: any;\n try {\n requirements = await response.clone().json();\n } catch {\n throw new Error('[relai-x402] Failed to parse 402 response body');\n }\n\n const accepts = requirements.accepts || [];\n if (!accepts.length) throw new Error('[relai-x402] No payment options in 402 response');\n\n const selected = selectAccept(accepts);\n if (!selected) {\n const networks = accepts.map((a: any) => a.network).join(', ');\n throw new Error(`[relai-x402] No wallet available for networks: ${networks}`);\n }\n\n const { accept, chain } = selected;\n const amount = accept.amount || accept.maxAmountRequired;\n log(`Selected: ${chain} / ${accept.network} / amount=${amount}`);\n\n // Amount guard\n if (maxAmountAtomic && BigInt(amount) > BigInt(maxAmountAtomic)) {\n throw new Error(`[relai-x402] Amount ${amount} exceeds max ${maxAmountAtomic}`);\n }\n\n // Solana — build SPL transfer natively (no x402-solana dependency)\n if (chain === 'solana' && hasSolanaWallet) {\n const paymentHeader = await buildSolanaPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header (Solana)');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n // EVM — build payment header and retry\n if (chain === 'evm') {\n const evmNetwork = normalizeNetwork(accept.network || '');\n const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);\n const paymentHeader = usePermit\n ? await buildEvmPermitPayment(accept, requirements, url)\n : await buildEvmPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n throw new Error('[relai-x402] Unexpected state — no payment handler matched');\n }\n\n return { fetch: x402Fetch };\n}\n\nexport default createX402Client;\n","// src/types.ts\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** RelAI Facilitator URL */\nexport const RELAI_FACILITATOR_URL = 'https://facilitator.x402.fi';\n\n// ============================================================================\n// Supported Networks\n// ============================================================================\n\n/** All networks supported by RelAI facilitator */\nexport type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-bite' | 'polygon' | 'ethereum';\n\n/** CAIP-2 network identifiers */\nexport const NETWORK_CAIP2: Record<RelaiNetwork, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'base': 'eip155:8453',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'skale-bite': 'eip155:103698795',\n 'polygon': 'eip155:137',\n 'ethereum': 'eip155:1',\n};\n\n/** Reverse lookup: CAIP-2 → simple network name */\nexport const CAIP2_TO_NETWORK: Record<string, RelaiNetwork> = Object.fromEntries(\n Object.entries(NETWORK_CAIP2).map(([k, v]) => [v, k as RelaiNetwork])\n) as Record<string, RelaiNetwork>;\n\n/** Chain IDs for EVM networks */\nexport const CHAIN_IDS: Record<string, number> = {\n 'base': 8453,\n 'avalanche': 43114,\n 'skale-base': 1187947933,\n 'skale-bite': 103698795,\n 'polygon': 137,\n 'ethereum': 1,\n};\n\n/** USDC contract addresses per network */\nexport const USDC_ADDRESSES: Record<RelaiNetwork, string> = {\n 'solana': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n 'base': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n 'avalanche': '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',\n 'skale-base': '0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20',\n 'skale-bite': '0xc4083B1E81ceb461Ccef3FDa8A9F24F0d764B6D8',\n 'polygon': '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n 'ethereum': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n};\n\n/** Explorer URLs per network */\nexport const EXPLORER_TX_URL: Record<RelaiNetwork, (tx: string) => string> = {\n 'solana': (tx) => `https://solscan.io/tx/${tx}`,\n 'base': (tx) => `https://basescan.org/tx/${tx}`,\n 'avalanche': (tx) => `https://snowtrace.io/tx/${tx}`,\n 'skale-base': (tx) => `https://skale-base-explorer.skalenodes.com/tx/${tx}`,\n 'skale-bite': (tx) => `https://base-sepolia-testnet.explorer.skalenodes.com/tx/${tx}`,\n 'polygon': (tx) => `https://polygonscan.com/tx/${tx}`,\n 'ethereum': (tx) => `https://etherscan.io/tx/${tx}`,\n};\n\n/** Human-readable network labels */\nexport const NETWORK_LABELS: Record<RelaiNetwork, string> = {\n 'solana': 'Solana',\n 'base': 'Base',\n 'avalanche': 'Avalanche',\n 'skale-base': 'SKALE Base',\n 'skale-bite': 'SKALE BITE V2',\n 'polygon': 'Polygon',\n 'ethereum': 'Ethereum',\n};\n\n/** Legacy CAIP-2 exports for backward compatibility */\nexport const SOLANA_MAINNET_NETWORK = NETWORK_CAIP2['solana'];\nexport const BASE_MAINNET_NETWORK = NETWORK_CAIP2['base'];\n\n/** Legacy USDC exports for backward compatibility */\nexport const USDC_SOLANA = USDC_ADDRESSES['solana'];\nexport const USDC_BASE = USDC_ADDRESSES['base'];\n\n/** All supported RelAI networks list */\nexport const RELAI_NETWORKS: RelaiNetwork[] = ['solana', 'base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'];\n\n/** Check if a network is Solana-based */\nexport function isSolana(network: string): boolean {\n return network === 'solana' || network.startsWith('solana:');\n}\n\n/** Check if a network is EVM-based */\nexport function isEvm(network: string): boolean {\n return ['base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'].includes(network) || network.startsWith('eip155:');\n}\n\n/** Normalize CAIP-2 or simple name to RelaiNetwork */\nexport function normalizeNetwork(network: string): RelaiNetwork | null {\n if (RELAI_NETWORKS.includes(network as RelaiNetwork)) return network as RelaiNetwork;\n const fromCaip2 = CAIP2_TO_NETWORK[network];\n if (fromCaip2) return fromCaip2;\n // Partial match\n if (network.startsWith('solana:')) return 'solana';\n if (network.startsWith('eip155:')) {\n const chainId = parseInt(network.split(':')[1]);\n const entry = Object.entries(CHAIN_IDS).find(([, id]) => id === chainId);\n if (entry) return entry[0] as RelaiNetwork;\n }\n return null;\n}\n\n// ============================================================================\n// Wallet Types\n// ============================================================================\n\n/** Solana wallet interface */\nexport interface SolanaWallet {\n publicKey: { toString(): string } | null;\n signTransaction: ((tx: unknown) => Promise<unknown>) | null;\n signAllTransactions?: ((txs: unknown[]) => Promise<unknown[]>) | null;\n}\n\n/** EVM wallet interface (viem-compatible) */\nexport interface EvmWallet {\n address: string;\n signTypedData: (params: unknown) => Promise<string>;\n chain?: { id: number };\n}\n\n/** Wallet set for multi-chain support */\nexport interface WalletSet {\n solana?: SolanaWallet;\n evm?: EvmWallet;\n}\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/** Extra fields in payment requirements */\nexport interface AcceptsExtra {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n}\n\n/** A single payment option */\nexport interface PaymentAccept {\n x402Version?: 1 | 2;\n scheme: string;\n network: string;\n maxAmountRequired?: string;\n amount?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: AcceptsExtra;\n resource?: string;\n description?: string;\n mimeType?: string;\n outputSchema?: unknown;\n}\n\n/** Resource info for v2 */\nexport interface ResourceInfo {\n url: string;\n description?: string;\n mimeType?: string;\n}\n\n/** Payment requirements (402 response) */\nexport interface PaymentRequired {\n x402Version: 1 | 2;\n error?: string;\n accepts: PaymentAccept[];\n resource?: ResourceInfo;\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Config Types (server-specific types are in server.ts)\n// ============================================================================\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/types.ts"],"sourcesContent":["// src/client.ts\nimport {\n Connection,\n PublicKey,\n TransactionMessage,\n VersionedTransaction,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from '@solana/spl-token';\nimport type { SolanaWallet, EvmWallet, WalletSet } from './types';\nimport {\n RELAI_FACILITATOR_URL,\n NETWORK_CAIP2,\n CHAIN_IDS,\n isSolana,\n isEvm,\n normalizeNetwork,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface X402ClientConfig {\n /** Multi-chain wallets (Solana + EVM) */\n wallets?: WalletSet;\n /** Single Solana wallet (legacy shortcut) */\n wallet?: SolanaWallet;\n /** Custom facilitator URL, default: RelAI facilitator */\n facilitatorUrl?: string;\n /** Preferred network when multiple options available */\n preferredNetwork?: RelaiNetwork;\n /** Custom Solana RPC URL */\n solanaRpcUrl?: string;\n /** Custom EVM RPC URLs per network (e.g. { 'skale-base': 'https://...' }) */\n evmRpcUrls?: Record<string, string>;\n /** Maximum payment amount in atomic units */\n maxAmountAtomic?: string;\n /** Enable verbose logging */\n verbose?: boolean;\n}\n\nexport interface X402Client {\n /** Fetch with automatic x402 payment handling */\n fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n}\n\n/**\n * Create an x402 client for automatic payment handling.\n * Supports all RelAI facilitator networks: Solana, Base, Avalanche, SKALE Base.\n * Auto-detects the correct chain from the 402 response and picks the right\n * signing method (Solana SPL transfer, EVM EIP-3009 transferWithAuthorization).\n *\n * @example\n * ```typescript\n * import { createX402Client } from '@relai-fi/x402';\n *\n * const client = createX402Client({\n * wallets: { solana: solanaWallet, evm: evmWalletClient },\n * });\n *\n * // Automatically handles 402 on any RelAI-supported network\n * const response = await client.fetch('https://api.example.com/protected');\n * ```\n */\n// Networks that use EIP-2612 permit instead of EIP-3009 transferWithAuthorization (currently none)\nconst PERMIT_NETWORKS = new Set<string>([]);\n\n// Default EVM RPC URLs\nconst DEFAULT_EVM_RPC_URLS: Record<string, string> = {\n 'skale-base': 'https://skale-base.skalenodes.com/v1/base',\n 'skale-bite': 'https://base-sepolia-testnet.skalenodes.com/v1/bite-v2-sandbox',\n 'base': 'https://mainnet.base.org',\n 'avalanche': 'https://api.avax.network/ext/bc/C/rpc',\n 'polygon': 'https://polygon-rpc.com',\n 'ethereum': 'https://ethereum-rpc.publicnode.com',\n};\n\nexport function createX402Client(config: X402ClientConfig): X402Client {\n const {\n wallets = {},\n wallet: legacyWallet,\n facilitatorUrl = RELAI_FACILITATOR_URL,\n preferredNetwork,\n solanaRpcUrl = 'https://api.mainnet-beta.solana.com',\n evmRpcUrls = {},\n maxAmountAtomic,\n verbose = false,\n } = config;\n\n const log = verbose ? console.log.bind(console, '[relai-x402]') : () => {};\n\n // Merge legacy wallet into wallet set\n const effectiveWallets: WalletSet = { ...wallets };\n if (legacyWallet && !effectiveWallets.solana) {\n effectiveWallets.solana = legacyWallet;\n }\n\n const hasSolanaWallet = Boolean(\n effectiveWallets.solana?.publicKey && effectiveWallets.solana?.signTransaction\n );\n if (hasSolanaWallet) log('Solana wallet ready');\n\n // -----------------------------------------------------------------------\n // Select a payment option from the 402 response's `accepts` array\n // -----------------------------------------------------------------------\n function selectAccept(accepts: any[]): { accept: any; chain: 'solana' | 'evm' } | null {\n // 1) Preferred network first\n if (preferredNetwork) {\n const caip2 = NETWORK_CAIP2[preferredNetwork];\n for (const a of accepts) {\n const net = a.network || '';\n if (net === preferredNetwork || net === caip2) {\n const chain = isSolana(net) ? 'solana' as const : 'evm' as const;\n if ((chain === 'solana' && hasSolanaWallet) || (chain === 'evm' && effectiveWallets.evm)) {\n return { accept: a, chain };\n }\n }\n }\n }\n\n // 2) First option we have a wallet for\n for (const a of accepts) {\n const net = a.network || '';\n if (isSolana(net) && hasSolanaWallet) return { accept: a, chain: 'solana' };\n if (isEvm(net) && effectiveWallets.evm) return { accept: a, chain: 'evm' };\n }\n\n return null;\n }\n\n // -----------------------------------------------------------------------\n // JSON-RPC helper (for reading EVM contract state without ethers)\n // -----------------------------------------------------------------------\n async function evmRpcCall(rpcUrl: string, to: string, data: string): Promise<string> {\n const res = await fetch(rpcUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n method: 'eth_call',\n params: [{ to, data }, 'latest'],\n id: 1,\n }),\n });\n const json = await res.json();\n if (json.error) throw new Error(`RPC error: ${json.error.message}`);\n return json.result;\n }\n\n function getEvmRpcUrl(network: string): string {\n return evmRpcUrls[network] || DEFAULT_EVM_RPC_URLS[network] || '';\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment — EIP-2612 permit (for SKALE Base)\n // -----------------------------------------------------------------------\n async function buildEvmPermitPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n const spender = extra.feePayer || accept.payTo;\n const usdcAddress = accept.asset;\n\n const rpcUrl = getEvmRpcUrl(network || rawNetwork);\n if (!rpcUrl) throw new Error(`[relai-x402] No EVM RPC URL for network ${network || rawNetwork}`);\n\n log('Building EIP-2612 permit on chain', chainId);\n\n // Read nonce from USDC contract: nonces(address) = 0x7ecebe00\n const paddedAddress = evmWallet.address.toLowerCase().replace('0x', '').padStart(64, '0');\n const nonceHex = await evmRpcCall(rpcUrl, usdcAddress, '0x7ecebe00' + paddedAddress);\n const nonce = nonceHex ? parseInt(nonceHex, 16) : 0;\n if (isNaN(nonce)) throw new Error(`[relai-x402] Failed to read permit nonce from ${usdcAddress} on ${rpcUrl}`);\n log(' Permit nonce:', nonce);\n\n // Read token name: name() = 0x06fdde03\n const nameHex = await evmRpcCall(rpcUrl, usdcAddress, '0x06fdde03');\n // Decode ABI-encoded string\n let tokenName = 'USD Coin';\n try {\n const offset = parseInt(nameHex.slice(2, 66), 16) * 2;\n const length = parseInt(nameHex.slice(2 + offset, 2 + offset + 64), 16);\n const hex = nameHex.slice(2 + offset + 64, 2 + offset + 64 + length * 2);\n tokenName = decodeURIComponent(hex.replace(/[0-9a-f]{2}/g, '%$&'));\n } catch {\n tokenName = extra.name || 'USD Coin';\n }\n log(' Token name:', tokenName);\n\n const deadline = Math.floor(Date.now() / 1000) + 600; // 10 min\n\n const domain = {\n name: tokenName,\n version: extra.version || '2',\n chainId,\n verifyingContract: usdcAddress,\n };\n\n const types = {\n Permit: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n };\n\n const message = {\n owner: evmWallet.address,\n spender,\n value: paymentAmount,\n nonce: String(nonce),\n deadline: String(deadline),\n };\n\n log('Signing EIP-2612 permit:', message);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'Permit',\n });\n\n // Split signature into v, r, s\n const sigHex = (signature as string).replace('0x', '');\n const r = '0x' + sigHex.slice(0, 64);\n const s = '0x' + sigHex.slice(64, 128);\n const v = parseInt(sigHex.slice(128, 130), 16);\n\n log(' Permit signed: v=%d r=%s s=%s', v, r, s);\n\n // Build x402 v2 payment payload (SKALE format)\n const paymentPayload = {\n x402Version: 2,\n scheme: 'exact',\n network: network || rawNetwork,\n payload: {\n userAddress: evmWallet.address,\n permit: { deadline: String(deadline), v, r, s },\n amount: paymentAmount,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build EVM payment (EIP-3009 transferWithAuthorization)\n // -----------------------------------------------------------------------\n async function buildEvmPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const evmWallet = effectiveWallets.evm!;\n const extra = accept.extra || {};\n\n const rawNetwork = accept.network || '';\n const network = normalizeNetwork(rawNetwork);\n const chainId = network ? CHAIN_IDS[network] : parseInt(rawNetwork.split(':')[1] || '8453');\n\n const paymentAmount = accept.amount || accept.maxAmountRequired;\n\n // For relayer-based facilitators (0xGasless), 'to' is the payTo (merchant).\n // For standard x402 (RelAI), 'to' is also payTo (merchant).\n const useRelayer = !!extra.relayerContract;\n\n // EIP-3009 transferWithAuthorization typed data\n // When extra.relayerContract is present (e.g. 0xGasless), sign against the\n // relayer contract's EIP-712 domain instead of the token contract's domain.\n const domain = {\n name: useRelayer ? (extra.domainName || 'A402') : (extra.name || 'USDC'),\n version: useRelayer ? (extra.domainVersion || '1') : (extra.version || '1'),\n chainId,\n verifyingContract: useRelayer ? extra.relayerContract : accept.asset,\n };\n\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600;\n const nonce = '0x' + [...crypto.getRandomValues(new Uint8Array(32))]\n .map(b => b.toString(16).padStart(2, '0')).join('');\n\n const 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\n const message = {\n from: evmWallet.address,\n to: accept.payTo,\n value: paymentAmount,\n validAfter: String(validAfter),\n validBefore: String(validBefore),\n nonce,\n };\n\n log('Signing EIP-3009 transferWithAuthorization on chain', chainId);\n\n const signature = await evmWallet.signTypedData({\n domain,\n types,\n message,\n primaryType: 'TransferWithAuthorization',\n });\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n authorization: message,\n signature,\n },\n facilitatorUrl,\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Build Solana payment (SPL transfer with fee payer sponsorship)\n // -----------------------------------------------------------------------\n async function buildSolanaPayment(\n accept: any,\n requirements: any,\n url: string,\n ): Promise<string> {\n const solWallet = effectiveWallets.solana!;\n const extra = accept.extra || {};\n\n if (!extra.feePayer) {\n throw new Error('[relai-x402] Missing feePayer in Solana payment requirements');\n }\n\n const connection = new Connection(solanaRpcUrl, 'confirmed');\n const userPubkey = new PublicKey(solWallet.publicKey!.toString());\n const merchantPubkey = new PublicKey(accept.payTo);\n const feePayerPubkey = new PublicKey(extra.feePayer);\n const mintPubkey = new PublicKey(accept.asset);\n const paymentAmount = BigInt(accept.amount || accept.maxAmountRequired);\n\n log('Building Solana SPL transfer');\n log(' User:', userPubkey.toBase58());\n log(' Merchant:', merchantPubkey.toBase58());\n log(' FeePayer:', feePayerPubkey.toBase58());\n log(' Mint:', mintPubkey.toBase58());\n log(' Amount:', paymentAmount.toString());\n\n // Determine token program (TOKEN_PROGRAM_ID vs TOKEN_2022)\n const mintInfo = await getMint(connection, mintPubkey);\n const programId = mintInfo.address.equals(mintPubkey)\n ? (mintInfo as any).owner?.toBase58?.() === TOKEN_2022_PROGRAM_ID.toBase58()\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n // Get ATAs\n const sourceAta = await getAssociatedTokenAddress(\n mintPubkey, userPubkey, false, programId,\n );\n const destinationAta = await getAssociatedTokenAddress(\n mintPubkey, merchantPubkey, true, programId,\n );\n\n log(' Source ATA:', sourceAta.toBase58());\n log(' Dest ATA:', destinationAta.toBase58());\n\n // Build transfer instruction\n const transferIx = createTransferCheckedInstruction(\n sourceAta,\n mintPubkey,\n destinationAta,\n userPubkey,\n paymentAmount,\n mintInfo.decimals,\n [],\n programId,\n );\n\n // Build versioned transaction with feePayer\n const { blockhash } = await connection.getLatestBlockhash('confirmed');\n const message = new TransactionMessage({\n payerKey: feePayerPubkey,\n recentBlockhash: blockhash,\n instructions: [transferIx],\n }).compileToV0Message();\n\n const transaction = new VersionedTransaction(message);\n\n // User signs (feePayer signs on backend/facilitator side)\n const signedTx = await solWallet.signTransaction!(transaction) as VersionedTransaction;\n log('Transaction signed by user');\n\n // Serialize to base64\n const serializedTx = Buffer.from(signedTx.serialize()).toString('base64');\n\n // Build x402 v2 payment payload\n const paymentPayload = {\n x402Version: 2,\n resource: requirements.resource || { url },\n accepted: accept,\n payload: {\n transaction: serializedTx,\n },\n };\n\n return btoa(JSON.stringify(paymentPayload));\n }\n\n // -----------------------------------------------------------------------\n // Main fetch\n // -----------------------------------------------------------------------\n async function x402Fetch(\n input: string | URL | Request,\n init?: RequestInit,\n ): Promise<Response> {\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n log('Request:', url);\n\n const response = await fetch(input, init);\n if (response.status !== 402) return response;\n\n log('Got 402 Payment Required');\n\n let requirements: any;\n try {\n requirements = await response.clone().json();\n } catch {\n throw new Error('[relai-x402] Failed to parse 402 response body');\n }\n\n const accepts = requirements.accepts || [];\n if (!accepts.length) throw new Error('[relai-x402] No payment options in 402 response');\n\n const selected = selectAccept(accepts);\n if (!selected) {\n const networks = accepts.map((a: any) => a.network).join(', ');\n throw new Error(`[relai-x402] No wallet available for networks: ${networks}`);\n }\n\n const { accept, chain } = selected;\n const amount = accept.amount || accept.maxAmountRequired;\n log(`Selected: ${chain} / ${accept.network} / amount=${amount}`);\n\n // Amount guard\n if (maxAmountAtomic && BigInt(amount) > BigInt(maxAmountAtomic)) {\n throw new Error(`[relai-x402] Amount ${amount} exceeds max ${maxAmountAtomic}`);\n }\n\n // Solana — build SPL transfer natively (no x402-solana dependency)\n if (chain === 'solana' && hasSolanaWallet) {\n const paymentHeader = await buildSolanaPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header (Solana)');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n // EVM — build payment header and retry\n if (chain === 'evm') {\n const evmNetwork = normalizeNetwork(accept.network || '');\n const usePermit = evmNetwork && PERMIT_NETWORKS.has(evmNetwork);\n const paymentHeader = usePermit\n ? await buildEvmPermitPayment(accept, requirements, url)\n : await buildEvmPayment(accept, requirements, url);\n log('Retrying with X-PAYMENT header');\n return fetch(input, {\n ...init,\n headers: {\n ...(init?.headers || {}),\n 'X-PAYMENT': paymentHeader,\n },\n });\n }\n\n throw new Error('[relai-x402] Unexpected state — no payment handler matched');\n }\n\n return { fetch: x402Fetch };\n}\n\nexport default createX402Client;\n","// src/types.ts\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** RelAI Facilitator URL */\nexport const RELAI_FACILITATOR_URL = 'https://facilitator.x402.fi';\n\n// ============================================================================\n// Supported Networks\n// ============================================================================\n\n/** All networks supported by RelAI facilitator */\nexport type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-bite' | 'polygon' | 'ethereum';\n\n/** CAIP-2 network identifiers */\nexport const NETWORK_CAIP2: Record<RelaiNetwork, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'base': 'eip155:8453',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'skale-bite': 'eip155:103698795',\n 'polygon': 'eip155:137',\n 'ethereum': 'eip155:1',\n};\n\n/** Reverse lookup: CAIP-2 → simple network name */\nexport const CAIP2_TO_NETWORK: Record<string, RelaiNetwork> = Object.fromEntries(\n Object.entries(NETWORK_CAIP2).map(([k, v]) => [v, k as RelaiNetwork])\n) as Record<string, RelaiNetwork>;\n\n/** Chain IDs for EVM networks */\nexport const CHAIN_IDS: Record<string, number> = {\n 'base': 8453,\n 'avalanche': 43114,\n 'skale-base': 1187947933,\n 'skale-bite': 103698795,\n 'polygon': 137,\n 'ethereum': 1,\n};\n\n/** USDC contract addresses per network */\nexport const USDC_ADDRESSES: Record<RelaiNetwork, string> = {\n 'solana': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',\n 'base': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n 'avalanche': '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',\n 'skale-base': '0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20',\n 'skale-bite': '0xc4083B1E81ceb461Ccef3FDa8A9F24F0d764B6D8',\n 'polygon': '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n 'ethereum': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n};\n\n/** Explorer URLs per network */\nexport const EXPLORER_TX_URL: Record<RelaiNetwork, (tx: string) => string> = {\n 'solana': (tx) => `https://solscan.io/tx/${tx}`,\n 'base': (tx) => `https://basescan.org/tx/${tx}`,\n 'avalanche': (tx) => `https://snowtrace.io/tx/${tx}`,\n 'skale-base': (tx) => `https://skale-base-explorer.skalenodes.com/tx/${tx}`,\n 'skale-bite': (tx) => `https://base-sepolia-testnet.explorer.skalenodes.com/tx/${tx}`,\n 'polygon': (tx) => `https://polygonscan.com/tx/${tx}`,\n 'ethereum': (tx) => `https://etherscan.io/tx/${tx}`,\n};\n\n/** Human-readable network labels */\nexport const NETWORK_LABELS: Record<RelaiNetwork, string> = {\n 'solana': 'Solana',\n 'base': 'Base',\n 'avalanche': 'Avalanche',\n 'skale-base': 'SKALE Base',\n 'skale-bite': 'SKALE BITE V2',\n 'polygon': 'Polygon',\n 'ethereum': 'Ethereum',\n};\n\n/** Legacy CAIP-2 exports for backward compatibility */\nexport const SOLANA_MAINNET_NETWORK = NETWORK_CAIP2['solana'];\nexport const BASE_MAINNET_NETWORK = NETWORK_CAIP2['base'];\n\n/** Legacy USDC exports for backward compatibility */\nexport const USDC_SOLANA = USDC_ADDRESSES['solana'];\nexport const USDC_BASE = USDC_ADDRESSES['base'];\n\n/** All supported RelAI networks list */\nexport const RELAI_NETWORKS: RelaiNetwork[] = ['solana', 'base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'];\n\n/** Check if a network is Solana-based */\nexport function isSolana(network: string): boolean {\n return network === 'solana' || network.startsWith('solana:');\n}\n\n/** Check if a network is EVM-based */\nexport function isEvm(network: string): boolean {\n return ['base', 'avalanche', 'skale-base', 'skale-bite', 'polygon', 'ethereum'].includes(network) || network.startsWith('eip155:');\n}\n\n/** Normalize CAIP-2 or simple name to RelaiNetwork */\nexport function normalizeNetwork(network: string): RelaiNetwork | null {\n if (RELAI_NETWORKS.includes(network as RelaiNetwork)) return network as RelaiNetwork;\n const fromCaip2 = CAIP2_TO_NETWORK[network];\n if (fromCaip2) return fromCaip2;\n // Partial match\n if (network.startsWith('solana:')) return 'solana';\n if (network.startsWith('eip155:')) {\n const chainId = parseInt(network.split(':')[1]);\n const entry = Object.entries(CHAIN_IDS).find(([, id]) => id === chainId);\n if (entry) return entry[0] as RelaiNetwork;\n }\n return null;\n}\n\n// ============================================================================\n// Wallet Types\n// ============================================================================\n\n/** Solana wallet interface */\nexport interface SolanaWallet {\n publicKey: { toString(): string } | null;\n signTransaction: ((tx: unknown) => Promise<unknown>) | null;\n signAllTransactions?: ((txs: unknown[]) => Promise<unknown[]>) | null;\n}\n\n/** EVM wallet interface (viem-compatible) */\nexport interface EvmWallet {\n address: string;\n signTypedData: (params: unknown) => Promise<string>;\n chain?: { id: number };\n}\n\n/** Wallet set for multi-chain support */\nexport interface WalletSet {\n solana?: SolanaWallet;\n evm?: EvmWallet;\n}\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/** Extra fields in payment requirements */\nexport interface AcceptsExtra {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n}\n\n/** A single payment option */\nexport interface PaymentAccept {\n x402Version?: 1 | 2;\n scheme: string;\n network: string;\n maxAmountRequired?: string;\n amount?: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds?: number;\n extra?: AcceptsExtra;\n resource?: string;\n description?: string;\n mimeType?: string;\n outputSchema?: unknown;\n}\n\n/** Resource info for v2 */\nexport interface ResourceInfo {\n url: string;\n description?: string;\n mimeType?: string;\n}\n\n/** Payment requirements (402 response) */\nexport interface PaymentRequired {\n x402Version: 1 | 2;\n error?: string;\n accepts: PaymentAccept[];\n resource?: ResourceInfo;\n extensions?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Config Types (server-specific types are in server.ts)\n// ============================================================================\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -51,6 +51,7 @@ __export(index_exports, {
51
51
  networkV2ToV1: () => networkV2ToV1,
52
52
  normalizeNetwork: () => normalizeNetwork,
53
53
  normalizePaymentHeader: () => normalizePaymentHeader,
54
+ stripePayTo: () => stripePayTo,
54
55
  toAtomicUnits: () => toAtomicUnits
55
56
  });
56
57
  module.exports = __toCommonJS(index_exports);
@@ -129,11 +130,98 @@ function normalizeNetwork(network) {
129
130
  }
130
131
 
131
132
  // src/server.ts
133
+ function stripePayTo(stripeSecretKey, options) {
134
+ if (!stripeSecretKey) {
135
+ throw new Error("stripePayTo requires a Stripe secret key");
136
+ }
137
+ return {
138
+ __brand: "stripePayTo",
139
+ secretKey: stripeSecretKey,
140
+ stripeNetwork: options?.network || "base"
141
+ };
142
+ }
143
+ function isStripePayTo(payTo) {
144
+ return typeof payTo === "object" && payTo !== null && payTo.__brand === "stripePayTo";
145
+ }
146
+ async function createStripeDepositAddress(secretKey, amountUsdCents, network = "base") {
147
+ const params = new URLSearchParams();
148
+ params.append("amount", String(amountUsdCents));
149
+ params.append("currency", "usd");
150
+ params.append("payment_method_types[]", "crypto");
151
+ params.append("payment_method_data[type]", "crypto");
152
+ params.append("confirm", "true");
153
+ const res = await fetch("https://api.stripe.com/v1/payment_intents", {
154
+ method: "POST",
155
+ headers: {
156
+ "Authorization": `Bearer ${secretKey}`,
157
+ "Content-Type": "application/x-www-form-urlencoded"
158
+ },
159
+ body: params.toString()
160
+ });
161
+ if (!res.ok) {
162
+ const err = await res.json().catch(() => ({}));
163
+ const msg = err?.error?.message || res.statusText;
164
+ if (msg.includes("unknown parameter") || msg.includes("crypto")) {
165
+ throw new Error(
166
+ `Stripe crypto payins not enabled on this account. Enable at: https://support.stripe.com/questions/get-started-with-pay-with-crypto (Original: ${msg})`
167
+ );
168
+ }
169
+ throw new Error(`Stripe PaymentIntent creation failed: ${msg}`);
170
+ }
171
+ const pi = await res.json();
172
+ const depositDetails = pi.next_action?.crypto_collect_deposit_details;
173
+ if (!depositDetails) {
174
+ throw new Error(
175
+ "Stripe PaymentIntent missing crypto deposit details. Ensure crypto payins are enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto"
176
+ );
177
+ }
178
+ const address = depositDetails.deposit_addresses?.[network]?.address;
179
+ if (!address) {
180
+ throw new Error(`No Stripe deposit address for network: ${network}`);
181
+ }
182
+ return address;
183
+ }
132
184
  var Relai = class {
185
+ // Cache feePayer per network
133
186
  constructor(config) {
187
+ this.feePayerCache = /* @__PURE__ */ new Map();
134
188
  this.network = config.network;
135
189
  this.facilitatorUrl = config.facilitatorUrl || RELAI_FACILITATOR_URL;
136
190
  }
191
+ /**
192
+ * Get feePayer address for a network (cached)
193
+ */
194
+ async getFeePayer(caip2) {
195
+ if (this.feePayerCache.has(caip2)) {
196
+ return this.feePayerCache.get(caip2);
197
+ }
198
+ const isRelAI = this.facilitatorUrl.includes("facilitator.x402.fi") || this.facilitatorUrl.includes("relai");
199
+ if (isRelAI) {
200
+ const relaiFeePayer = "0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085";
201
+ this.feePayerCache.set(caip2, relaiFeePayer);
202
+ return relaiFeePayer;
203
+ }
204
+ try {
205
+ const supportedUrl = `${this.facilitatorUrl}/supported`;
206
+ const supportedRes = await fetch(supportedUrl);
207
+ if (supportedRes.ok) {
208
+ const supportedData = await supportedRes.json();
209
+ supportedData.kinds?.forEach((kind) => {
210
+ if (kind.network && kind.extra?.feePayer) {
211
+ this.feePayerCache.set(kind.network, kind.extra.feePayer);
212
+ }
213
+ });
214
+ return this.feePayerCache.get(caip2);
215
+ }
216
+ } catch (err) {
217
+ const envFeePayer = process.env.BACKEND_WALLET_ADDRESS || process.env.FEEPAYER_EVM_WALLET_ADDRESS;
218
+ if (envFeePayer) {
219
+ this.feePayerCache.set(caip2, envFeePayer);
220
+ return envFeePayer;
221
+ }
222
+ }
223
+ return void 0;
224
+ }
137
225
  /**
138
226
  * Express middleware to protect an endpoint with x402 micropayments.
139
227
  *
@@ -150,13 +238,41 @@ var Relai = class {
150
238
  if (typeof resolvedPrice !== "number" || !isFinite(resolvedPrice) || resolvedPrice <= 0) {
151
239
  return res.status(400).json({ error: "Invalid price configuration" });
152
240
  }
153
- const network = options.network || self.network;
241
+ const stripeConfig = isStripePayTo(options.payTo) ? options.payTo : null;
242
+ const network = stripeConfig ? stripeConfig.stripeNetwork || "base" : options.network || self.network;
154
243
  const caip2 = NETWORK_CAIP2[network];
155
244
  const asset = USDC_ADDRESSES[network];
156
245
  const amount = String(Math.floor(resolvedPrice * 1e6));
157
246
  const paymentHeader = req.headers["x-payment"] || req.headers["payment-signature"] || req.headers["x-payment-signature"];
158
247
  if (!paymentHeader) {
159
248
  options.onPaymentRequired?.(req, { price: resolvedPrice, network });
249
+ let resolvedPayTo;
250
+ if (stripeConfig) {
251
+ const amountInCents = Math.max(1, Math.round(resolvedPrice * 100));
252
+ resolvedPayTo = await createStripeDepositAddress(
253
+ stripeConfig.secretKey,
254
+ amountInCents,
255
+ stripeConfig.stripeNetwork
256
+ );
257
+ } else {
258
+ resolvedPayTo = options.payTo;
259
+ }
260
+ const feePayer = await self.getFeePayer(caip2);
261
+ const tokenMetadata = {
262
+ "eip155:103698795": { name: "USDC", version: "1" },
263
+ // SKALE BITE
264
+ "eip155:1187947933": { name: "USD Coin", version: "2" },
265
+ // SKALE Base
266
+ "eip155:8453": { name: "USD Coin", version: "2" },
267
+ // Base
268
+ "eip155:43114": { name: "USD Coin", version: "2" },
269
+ // Avalanche
270
+ "eip155:137": { name: "USD Coin", version: "2" },
271
+ // Polygon
272
+ "eip155:1": { name: "USD Coin", version: "2" }
273
+ // Ethereum
274
+ };
275
+ const metadata = tokenMetadata[caip2] || { name: "USDC", version: "1" };
160
276
  return res.status(402).json({
161
277
  x402Version: 2,
162
278
  error: "Payment required",
@@ -170,12 +286,14 @@ var Relai = class {
170
286
  network: caip2,
171
287
  amount,
172
288
  asset,
173
- payTo: options.payTo,
289
+ payTo: resolvedPayTo,
174
290
  maxTimeoutSeconds: options.maxTimeoutSeconds || 60,
175
291
  extra: {
176
- name: "USD Coin",
177
- version: "2",
178
- decimals: 6
292
+ name: metadata.name,
293
+ version: metadata.version,
294
+ decimals: 6,
295
+ ...feePayer && { feePayer }
296
+ // Add feePayer if available
179
297
  }
180
298
  }]
181
299
  });
@@ -194,12 +312,24 @@ var Relai = class {
194
312
  });
195
313
  }
196
314
  }
315
+ let settlePayTo;
316
+ if (stripeConfig) {
317
+ settlePayTo = paymentProof.payload?.authorization?.to || paymentProof.accepted?.payTo || "";
318
+ if (!settlePayTo) {
319
+ return res.status(400).json({
320
+ x402Version: 2,
321
+ error: "Cannot extract destination address from payment proof"
322
+ });
323
+ }
324
+ } else {
325
+ settlePayTo = options.payTo;
326
+ }
197
327
  const paymentRequirements = {
198
328
  scheme: "exact",
199
329
  network,
200
330
  amount,
201
331
  asset,
202
- payTo: options.payTo,
332
+ payTo: settlePayTo,
203
333
  maxTimeoutSeconds: options.maxTimeoutSeconds || 60
204
334
  };
205
335
  const settleUrl = `${self.facilitatorUrl}/settle`;
@@ -416,8 +546,8 @@ function createX402Client(config) {
416
546
  const paymentAmount = accept.amount || accept.maxAmountRequired;
417
547
  const useRelayer = !!extra.relayerContract;
418
548
  const domain = {
419
- name: useRelayer ? extra.domainName || "A402" : extra.name || "USD Coin",
420
- version: useRelayer ? extra.domainVersion || "1" : extra.version || "2",
549
+ name: useRelayer ? extra.domainName || "A402" : extra.name || "USDC",
550
+ version: useRelayer ? extra.domainVersion || "1" : extra.version || "1",
421
551
  chainId,
422
552
  verifyingContract: useRelayer ? extra.relayerContract : accept.asset
423
553
  };
@@ -434,10 +564,9 @@ function createX402Client(config) {
434
564
  { name: "nonce", type: "bytes32" }
435
565
  ]
436
566
  };
437
- const spender = useRelayer ? accept.payTo : extra.feePayer || accept.payTo;
438
567
  const message = {
439
568
  from: evmWallet.address,
440
- to: spender,
569
+ to: accept.payTo,
441
570
  value: paymentAmount,
442
571
  validAfter: String(validAfter),
443
572
  validBefore: String(validBefore),
@@ -747,6 +876,7 @@ function formatUsd(usd, maxDecimals = 4) {
747
876
  networkV2ToV1,
748
877
  normalizeNetwork,
749
878
  normalizePaymentHeader,
879
+ stripePayTo,
750
880
  toAtomicUnits
751
881
  });
752
882
  //# sourceMappingURL=index.cjs.map