@relai-fi/x402 0.5.5 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/client.cjs +31 -4
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +31 -4
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +39 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +39 -6
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +33 -4
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +33 -4
- package/dist/react/index.js.map +1 -1
- package/dist/server.cjs +5 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +5 -1
- package/dist/server.js.map +1 -1
- package/dist/{types-C9LlAJj8.d.cts → types-C-2GNyMh.d.cts} +1 -1
- package/dist/{types-C9LlAJj8.d.ts → types-C-2GNyMh.d.ts} +1 -1
- package/dist/utils/index.cjs +3 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +3 -1
- package/dist/utils/index.js.map +1 -1
- package/examples/skale-bite-nextjs/.next/trace +1 -0
- package/package.json +1 -1
package/dist/server.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/types.ts"],"sourcesContent":["// src/server.ts\nimport {\n NETWORK_CAIP2,\n USDC_ADDRESSES,\n RELAI_FACILITATOR_URL,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RelaiServerConfig {\n /** Network to accept payments on */\n network: RelaiNetwork;\n /** RelAI facilitator URL (default: https://facilitator.x402.fi) */\n facilitatorUrl?: string;\n}\n\nexport type DynamicPrice = number | ((req: any) => number | Promise<number>);\n\nexport interface ProtectOptions {\n /** Price in USD (e.g., 0.01 for 1 cent) */\n price: DynamicPrice;\n /** Wallet address to receive payments, or stripePayTo() for Stripe settlement */\n payTo: string | StripePayTo;\n /** Description shown to payer */\n description?: string;\n /** MIME type of the response (default: application/json) */\n mimeType?: string;\n /** Maximum timeout in seconds (default: 60) */\n maxTimeoutSeconds?: number;\n /** Override network for this endpoint */\n network?: RelaiNetwork;\n /** Custom validation after payment is settled */\n customRules?: (req: any) => boolean | Promise<boolean>;\n /** Callback when 402 is returned (no payment provided) */\n onPaymentRequired?: (req: any, info: { price: number; network: RelaiNetwork }) => void;\n /** Callback when payment is settled successfully */\n onPaymentSettled?: (req: any, result: SettleResult) => void;\n /** Callback on error */\n onError?: (req: any, error: unknown) => void;\n}\n\nexport interface SettleResult {\n success: boolean;\n transaction?: string;\n payer?: string;\n network?: string;\n error?: string;\n errorReason?: string;\n}\n\nexport interface PaymentInfo {\n verified: boolean;\n transactionId?: string;\n payer?: string;\n network: RelaiNetwork;\n amount: number;\n}\n\n// ============================================================================\n// Stripe Pay-To Helper\n// ============================================================================\n\n/** Config returned by stripePayTo() - used by protect() to create Stripe deposit addresses */\nexport interface StripePayTo {\n readonly __brand: 'stripePayTo';\n readonly secretKey: string;\n /** Stripe crypto deposits network (default: 'base') */\n readonly stripeNetwork: string;\n}\n\n/**\n * Create a Stripe pay-to configuration for x402 payments.\n * Payments settle as USD in your Stripe Dashboard - no crypto knowledge required.\n *\n * Stripe creates a fresh PaymentIntent + deposit address per request.\n * Network is auto-set to Base (Stripe settles USDC on Base).\n *\n * @example\n * ```typescript\n * import Relai, { stripePayTo } from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * price: 0.01,\n * payTo: stripePayTo(process.env.STRIPE_SECRET_KEY!),\n * }), (req, res) => {\n * res.json({ data: 'paid content' });\n * });\n * ```\n */\nexport function stripePayTo(\n stripeSecretKey: string,\n options?: { network?: string },\n): StripePayTo {\n if (!stripeSecretKey) {\n throw new Error('stripePayTo requires a Stripe secret key');\n }\n return {\n __brand: 'stripePayTo' as const,\n secretKey: stripeSecretKey,\n stripeNetwork: options?.network || 'base',\n };\n}\n\n/** @internal Type guard for StripePayTo */\nfunction isStripePayTo(payTo: unknown): payTo is StripePayTo {\n return (\n typeof payTo === 'object' &&\n payTo !== null &&\n (payTo as any).__brand === 'stripePayTo'\n );\n}\n\n/**\n * @internal Create a Stripe PaymentIntent with crypto payment method\n * and extract the deposit address.\n */\nasync function createStripeDepositAddress(\n secretKey: string,\n amountUsdCents: number,\n network: string = 'base',\n): Promise<string> {\n const params = new URLSearchParams();\n params.append('amount', String(amountUsdCents));\n params.append('currency', 'usd');\n params.append('payment_method_types[]', 'crypto');\n params.append('payment_method_data[type]', 'crypto');\n params.append('confirm', 'true');\n\n const res = await fetch('https://api.stripe.com/v1/payment_intents', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${secretKey}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as any;\n const msg = err?.error?.message || res.statusText;\n\n // Provide actionable guidance for common issues\n if (msg.includes('unknown parameter') || msg.includes('crypto')) {\n throw new Error(\n `Stripe crypto payins not enabled on this account. ` +\n `Enable at: https://support.stripe.com/questions/get-started-with-pay-with-crypto ` +\n `(Original: ${msg})`,\n );\n }\n throw new Error(`Stripe PaymentIntent creation failed: ${msg}`);\n }\n\n const pi = await res.json() as any;\n const depositDetails = pi.next_action?.crypto_collect_deposit_details;\n if (!depositDetails) {\n throw new Error(\n 'Stripe PaymentIntent missing crypto deposit details. ' +\n 'Ensure crypto payins are enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto',\n );\n }\n\n const address = depositDetails.deposit_addresses?.[network]?.address;\n if (!address) {\n throw new Error(`No Stripe deposit address for network: ${network}`);\n }\n\n return address;\n}\n\n// ============================================================================\n// Relai Server SDK\n// ============================================================================\n\n/**\n * Server-side SDK for protecting Express endpoints with x402 micropayments.\n * Settles payments through the RelAI facilitator (zero gas fees for users).\n *\n * Supports: Solana, Base, Avalanche, SKALE Base.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * payTo: '0xYourWallet',\n * price: 0.01, // $0.01 USDC\n * }), (req, res) => {\n * res.json({ data: 'Protected content', payment: req.payment });\n * });\n * ```\n */\nexport class Relai {\n private network: RelaiNetwork;\n private facilitatorUrl: string;\n private feePayerCache: Map<string, string> = new Map(); // Cache feePayer per network\n\n constructor(config: RelaiServerConfig) {\n this.network = config.network;\n this.facilitatorUrl = config.facilitatorUrl || RELAI_FACILITATOR_URL;\n }\n\n /**\n * Get feePayer address for a network (cached)\n */\n private async getFeePayer(caip2: string): Promise<string | undefined> {\n // Check cache first\n if (this.feePayerCache.has(caip2)) {\n return this.feePayerCache.get(caip2);\n }\n\n // If using RelAI facilitator, use hardcoded address (no fetch needed)\n const isRelAI = this.facilitatorUrl.includes('facilitator.x402.fi') || \n this.facilitatorUrl.includes('relai');\n \n if (isRelAI) {\n const relaiFeePayer = '0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085';\n this.feePayerCache.set(caip2, relaiFeePayer);\n return relaiFeePayer;\n }\n\n // For other facilitators, fetch from /supported\n try {\n const supportedUrl = `${this.facilitatorUrl}/supported`;\n const supportedRes = await fetch(supportedUrl);\n if (supportedRes.ok) {\n const supportedData = await supportedRes.json();\n // Cache all feePayers from supported kinds\n supportedData.kinds?.forEach((kind: any) => {\n if (kind.network && kind.extra?.feePayer) {\n this.feePayerCache.set(kind.network, kind.extra.feePayer);\n }\n });\n return this.feePayerCache.get(caip2);\n }\n } catch (err) {\n // feePayer MUST come from facilitator - cannot use env for security\n console.error(`[Relai] Failed to fetch feePayer from facilitator: ${err}`);\n }\n return undefined;\n }\n\n /**\n * Express middleware to protect an endpoint with x402 micropayments.\n *\n * Flow:\n * 1. No payment header → returns 402 with payment requirements\n * 2. Payment header present → calls RelAI facilitator `/settle`\n * 3. Settlement success → sets `PAYMENT-RESPONSE` header, attaches `req.payment`, calls `next()`\n */\n protect(options: ProtectOptions) {\n const self = this;\n\n return async (req: any, res: any, next: any) => {\n try {\n // Resolve dynamic price\n const resolvedPrice = typeof options.price === 'function'\n ? await options.price(req)\n : options.price;\n\n if (typeof resolvedPrice !== 'number' || !isFinite(resolvedPrice) || resolvedPrice <= 0) {\n return res.status(400).json({ error: 'Invalid price configuration' });\n }\n\n // Resolve network (Stripe auto-sets to base)\n const stripeConfig = isStripePayTo(options.payTo) ? options.payTo : null;\n const network = stripeConfig\n ? (stripeConfig.stripeNetwork as RelaiNetwork) || 'base'\n : (options.network || self.network);\n const caip2 = NETWORK_CAIP2[network];\n const asset = USDC_ADDRESSES[network];\n const amount = String(Math.floor(resolvedPrice * 1_000_000)); // USD → USDC atomic units (6 decimals)\n\n // Check for payment header (base64-encoded JSON)\n const paymentHeader =\n req.headers['x-payment'] ||\n req.headers['payment-signature'] ||\n req.headers['x-payment-signature'];\n\n // -----------------------------------------------------------\n // No payment → return 402 Payment Required\n // -----------------------------------------------------------\n if (!paymentHeader) {\n options.onPaymentRequired?.(req, { price: resolvedPrice, network });\n\n // Resolve payTo address (Stripe creates a fresh deposit address per request)\n let resolvedPayTo: string;\n if (stripeConfig) {\n const amountInCents = Math.max(1, Math.round(resolvedPrice * 100));\n resolvedPayTo = await createStripeDepositAddress(\n stripeConfig.secretKey,\n amountInCents,\n stripeConfig.stripeNetwork,\n );\n } else {\n resolvedPayTo = options.payTo as string;\n }\n\n // Get facilitator feePayer address (cached)\n const feePayer = await self.getFeePayer(caip2);\n\n // Token metadata per network\n // IMPORTANT: These must match the actual EIP-712 domain on each network\n const tokenMetadata: Record<string, { name: string; version: string }> = {\n 'eip155:103698795': { name: 'USDC', version: '1' }, // SKALE BITE\n 'eip155:1187947933': { name: 'USD Coin', version: '2' }, // SKALE Base\n 'eip155:8453': { name: 'USD Coin', version: '2' }, // Base\n 'eip155:43114': { name: 'USD Coin', version: '2' }, // Avalanche\n 'eip155:137': { name: 'USD Coin', version: '2' }, // Polygon\n 'eip155:1': { name: 'USD Coin', version: '2' }, // Ethereum\n };\n const metadata = tokenMetadata[caip2] || { name: 'USDC', version: '1' };\n\n return res.status(402).json({\n x402Version: 2,\n error: 'Payment required',\n resource: {\n url: `${req.protocol}://${req.get('host')}${req.originalUrl}`,\n description: options.description || 'API access',\n mimeType: options.mimeType || 'application/json',\n },\n accepts: [{\n scheme: 'exact',\n network: caip2,\n amount,\n asset,\n payTo: resolvedPayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n extra: {\n name: metadata.name,\n version: metadata.version,\n decimals: 6,\n ...(feePayer && { feePayer }), // Add feePayer if available\n },\n }],\n });\n }\n\n // -----------------------------------------------------------\n // Payment header present → parse and settle via facilitator\n // -----------------------------------------------------------\n let paymentProof: any;\n try {\n // Try base64 first (standard x402 format)\n const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');\n paymentProof = JSON.parse(decoded);\n } catch {\n try {\n // Fallback: raw JSON string\n paymentProof = JSON.parse(paymentHeader);\n } catch {\n return res.status(400).json({\n x402Version: 2,\n error: 'Invalid payment header — expected base64-encoded JSON',\n });\n }\n }\n\n // Resolve payTo for settle (extract from signed proof when using Stripe)\n let settlePayTo: string;\n if (stripeConfig) {\n settlePayTo =\n paymentProof.payload?.authorization?.to ||\n paymentProof.accepted?.payTo ||\n '';\n if (!settlePayTo) {\n return res.status(400).json({\n x402Version: 2,\n error: 'Cannot extract destination address from payment proof',\n });\n }\n } else {\n settlePayTo = options.payTo as string;\n }\n\n // Build payment requirements for facilitator\n const paymentRequirements = {\n scheme: 'exact',\n network,\n amount,\n asset,\n payTo: settlePayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n };\n\n // Call facilitator /settle\n const settleUrl = `${self.facilitatorUrl}/settle`;\n const settleRes = await fetch(settleUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentPayload: paymentProof,\n paymentRequirements,\n }),\n });\n\n const result: SettleResult = await settleRes.json() as SettleResult;\n\n if (!result.success) {\n return res.status(402).json({\n x402Version: 2,\n error: result.errorReason || result.error || 'Payment settlement failed',\n });\n }\n\n // Attach payment info to request\n const paymentInfo: PaymentInfo = {\n verified: true,\n transactionId: result.transaction,\n payer: result.payer,\n network,\n amount: resolvedPrice,\n };\n req.payment = paymentInfo;\n req.x402Payer = result.payer;\n req.x402Paid = true;\n req.x402Transaction = result.transaction;\n req.x402Network = network;\n\n // Set x402 v2 PAYMENT-RESPONSE header (base64 JSON)\n const paymentResponse = {\n x402Version: 2,\n scheme: 'exact',\n network: caip2,\n transaction: result.transaction,\n payer: result.payer,\n amount,\n asset,\n };\n res.setHeader(\n 'PAYMENT-RESPONSE',\n Buffer.from(JSON.stringify(paymentResponse)).toString('base64'),\n );\n\n options.onPaymentSettled?.(req, result);\n\n // Custom validation after payment\n if (options.customRules) {\n const valid = await options.customRules(req);\n if (!valid) {\n return res.status(403).json({ error: 'Custom validation failed' });\n }\n }\n\n next();\n } catch (error) {\n options.onError?.(req, error);\n console.error('[Relai] Protection error:', error);\n res.status(500).json({ error: 'Internal server error' });\n }\n };\n }\n}\n\nexport default Relai;\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/server.ts","../src/types.ts"],"sourcesContent":["// src/server.ts\nimport {\n NETWORK_CAIP2,\n USDC_ADDRESSES,\n RELAI_FACILITATOR_URL,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RelaiServerConfig {\n /** Network to accept payments on */\n network: RelaiNetwork;\n /** RelAI facilitator URL (default: https://facilitator.x402.fi) */\n facilitatorUrl?: string;\n}\n\nexport type DynamicPrice = number | ((req: any) => number | Promise<number>);\n\nexport interface ProtectOptions {\n /** Price in USD (e.g., 0.01 for 1 cent) */\n price: DynamicPrice;\n /** Wallet address to receive payments, or stripePayTo() for Stripe settlement */\n payTo: string | StripePayTo;\n /** Description shown to payer */\n description?: string;\n /** MIME type of the response (default: application/json) */\n mimeType?: string;\n /** Maximum timeout in seconds (default: 60) */\n maxTimeoutSeconds?: number;\n /** Override network for this endpoint */\n network?: RelaiNetwork;\n /** Custom validation after payment is settled */\n customRules?: (req: any) => boolean | Promise<boolean>;\n /** Callback when 402 is returned (no payment provided) */\n onPaymentRequired?: (req: any, info: { price: number; network: RelaiNetwork }) => void;\n /** Callback when payment is settled successfully */\n onPaymentSettled?: (req: any, result: SettleResult) => void;\n /** Callback on error */\n onError?: (req: any, error: unknown) => void;\n}\n\nexport interface SettleResult {\n success: boolean;\n transaction?: string;\n payer?: string;\n network?: string;\n error?: string;\n errorReason?: string;\n}\n\nexport interface PaymentInfo {\n verified: boolean;\n transactionId?: string;\n payer?: string;\n network: RelaiNetwork;\n amount: number;\n}\n\n// ============================================================================\n// Stripe Pay-To Helper\n// ============================================================================\n\n/** Config returned by stripePayTo() - used by protect() to create Stripe deposit addresses */\nexport interface StripePayTo {\n readonly __brand: 'stripePayTo';\n readonly secretKey: string;\n /** Stripe crypto deposits network (default: 'base') */\n readonly stripeNetwork: string;\n}\n\n/**\n * Create a Stripe pay-to configuration for x402 payments.\n * Payments settle as USD in your Stripe Dashboard - no crypto knowledge required.\n *\n * Stripe creates a fresh PaymentIntent + deposit address per request.\n * Network is auto-set to Base (Stripe settles USDC on Base).\n *\n * @example\n * ```typescript\n * import Relai, { stripePayTo } from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * price: 0.01,\n * payTo: stripePayTo(process.env.STRIPE_SECRET_KEY!),\n * }), (req, res) => {\n * res.json({ data: 'paid content' });\n * });\n * ```\n */\nexport function stripePayTo(\n stripeSecretKey: string,\n options?: { network?: string },\n): StripePayTo {\n if (!stripeSecretKey) {\n throw new Error('stripePayTo requires a Stripe secret key');\n }\n return {\n __brand: 'stripePayTo' as const,\n secretKey: stripeSecretKey,\n stripeNetwork: options?.network || 'base',\n };\n}\n\n/** @internal Type guard for StripePayTo */\nfunction isStripePayTo(payTo: unknown): payTo is StripePayTo {\n return (\n typeof payTo === 'object' &&\n payTo !== null &&\n (payTo as any).__brand === 'stripePayTo'\n );\n}\n\n/**\n * @internal Create a Stripe PaymentIntent with crypto payment method\n * and extract the deposit address.\n */\nasync function createStripeDepositAddress(\n secretKey: string,\n amountUsdCents: number,\n network: string = 'base',\n): Promise<string> {\n const params = new URLSearchParams();\n params.append('amount', String(amountUsdCents));\n params.append('currency', 'usd');\n params.append('payment_method_types[]', 'crypto');\n params.append('payment_method_data[type]', 'crypto');\n params.append('confirm', 'true');\n\n const res = await fetch('https://api.stripe.com/v1/payment_intents', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${secretKey}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as any;\n const msg = err?.error?.message || res.statusText;\n\n // Provide actionable guidance for common issues\n if (msg.includes('unknown parameter') || msg.includes('crypto')) {\n throw new Error(\n `Stripe crypto payins not enabled on this account. ` +\n `Enable at: https://support.stripe.com/questions/get-started-with-pay-with-crypto ` +\n `(Original: ${msg})`,\n );\n }\n throw new Error(`Stripe PaymentIntent creation failed: ${msg}`);\n }\n\n const pi = await res.json() as any;\n const depositDetails = pi.next_action?.crypto_collect_deposit_details;\n if (!depositDetails) {\n throw new Error(\n 'Stripe PaymentIntent missing crypto deposit details. ' +\n 'Ensure crypto payins are enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto',\n );\n }\n\n const address = depositDetails.deposit_addresses?.[network]?.address;\n if (!address) {\n throw new Error(`No Stripe deposit address for network: ${network}`);\n }\n\n return address;\n}\n\n// ============================================================================\n// Relai Server SDK\n// ============================================================================\n\n/**\n * Server-side SDK for protecting Express endpoints with x402 micropayments.\n * Settles payments through the RelAI facilitator (zero gas fees for users).\n *\n * Supports: Solana, Base, Avalanche, SKALE Base, SKALE Base Sepolia, SKALE BITE, Polygon, and Ethereum.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * payTo: '0xYourWallet',\n * price: 0.01, // $0.01 USDC\n * }), (req, res) => {\n * res.json({ data: 'Protected content', payment: req.payment });\n * });\n * ```\n */\nexport class Relai {\n private network: RelaiNetwork;\n private facilitatorUrl: string;\n private feePayerCache: Map<string, string> = new Map(); // Cache feePayer per network\n\n constructor(config: RelaiServerConfig) {\n this.network = config.network;\n this.facilitatorUrl = config.facilitatorUrl || RELAI_FACILITATOR_URL;\n }\n\n /**\n * Get feePayer address for a network (cached)\n */\n private async getFeePayer(caip2: string): Promise<string | undefined> {\n // Check cache first\n if (this.feePayerCache.has(caip2)) {\n return this.feePayerCache.get(caip2);\n }\n\n // If using RelAI facilitator, use hardcoded address (no fetch needed)\n const isRelAI = this.facilitatorUrl.includes('facilitator.x402.fi') || \n this.facilitatorUrl.includes('relai');\n \n if (isRelAI) {\n const relaiFeePayer = '0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085';\n this.feePayerCache.set(caip2, relaiFeePayer);\n return relaiFeePayer;\n }\n\n // For other facilitators, fetch from /supported\n try {\n const supportedUrl = `${this.facilitatorUrl}/supported`;\n const supportedRes = await fetch(supportedUrl);\n if (supportedRes.ok) {\n const supportedData = await supportedRes.json();\n // Cache all feePayers from supported kinds\n supportedData.kinds?.forEach((kind: any) => {\n if (kind.network && kind.extra?.feePayer) {\n this.feePayerCache.set(kind.network, kind.extra.feePayer);\n }\n });\n return this.feePayerCache.get(caip2);\n }\n } catch (err) {\n // feePayer MUST come from facilitator - cannot use env for security\n console.error(`[Relai] Failed to fetch feePayer from facilitator: ${err}`);\n }\n return undefined;\n }\n\n /**\n * Express middleware to protect an endpoint with x402 micropayments.\n *\n * Flow:\n * 1. No payment header → returns 402 with payment requirements\n * 2. Payment header present → calls RelAI facilitator `/settle`\n * 3. Settlement success → sets `PAYMENT-RESPONSE` header, attaches `req.payment`, calls `next()`\n */\n protect(options: ProtectOptions) {\n const self = this;\n\n return async (req: any, res: any, next: any) => {\n try {\n // Resolve dynamic price\n const resolvedPrice = typeof options.price === 'function'\n ? await options.price(req)\n : options.price;\n\n if (typeof resolvedPrice !== 'number' || !isFinite(resolvedPrice) || resolvedPrice <= 0) {\n return res.status(400).json({ error: 'Invalid price configuration' });\n }\n\n // Resolve network (Stripe auto-sets to base)\n const stripeConfig = isStripePayTo(options.payTo) ? options.payTo : null;\n const network = stripeConfig\n ? (stripeConfig.stripeNetwork as RelaiNetwork) || 'base'\n : (options.network || self.network);\n const caip2 = NETWORK_CAIP2[network];\n const asset = USDC_ADDRESSES[network];\n const amount = String(Math.floor(resolvedPrice * 1_000_000)); // USD → USDC atomic units (6 decimals)\n\n // Check for payment header (base64-encoded JSON)\n const paymentHeader =\n req.headers['x-payment'] ||\n req.headers['payment-signature'] ||\n req.headers['x-payment-signature'];\n\n // -----------------------------------------------------------\n // No payment → return 402 Payment Required\n // -----------------------------------------------------------\n if (!paymentHeader) {\n options.onPaymentRequired?.(req, { price: resolvedPrice, network });\n\n // Resolve payTo address (Stripe creates a fresh deposit address per request)\n let resolvedPayTo: string;\n if (stripeConfig) {\n const amountInCents = Math.max(1, Math.round(resolvedPrice * 100));\n resolvedPayTo = await createStripeDepositAddress(\n stripeConfig.secretKey,\n amountInCents,\n stripeConfig.stripeNetwork,\n );\n } else {\n resolvedPayTo = options.payTo as string;\n }\n\n // Get facilitator feePayer address (cached)\n const feePayer = await self.getFeePayer(caip2);\n\n // Token metadata per network\n // IMPORTANT: These must match the actual EIP-712 domain on each network\n const tokenMetadata: Record<string, { name: string; version: string }> = {\n 'eip155:103698795': { name: 'USDC', version: '1' }, // SKALE BITE\n 'eip155:1187947933': { name: 'Bridged USDC (SKALE Bridge)', version: '2' }, // SKALE Base\n 'eip155:324705682': { name: 'Bridged USDC (SKALE Bridge)', version: '2' }, // SKALE Base Sepolia\n 'eip155:8453': { name: 'USD Coin', version: '2' }, // Base\n 'eip155:43114': { name: 'USD Coin', version: '2' }, // Avalanche\n 'eip155:137': { name: 'USD Coin', version: '2' }, // Polygon\n 'eip155:1': { name: 'USD Coin', version: '2' }, // Ethereum\n };\n const metadata = tokenMetadata[caip2] || { name: 'USDC', version: '1' };\n\n return res.status(402).json({\n x402Version: 2,\n error: 'Payment required',\n resource: {\n url: `${req.protocol}://${req.get('host')}${req.originalUrl}`,\n description: options.description || 'API access',\n mimeType: options.mimeType || 'application/json',\n },\n accepts: [{\n scheme: 'exact',\n network: caip2,\n amount,\n asset,\n payTo: resolvedPayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n extra: {\n name: metadata.name,\n version: metadata.version,\n decimals: 6,\n ...(feePayer && { feePayer }), // Add feePayer if available\n },\n }],\n });\n }\n\n // -----------------------------------------------------------\n // Payment header present → parse and settle via facilitator\n // -----------------------------------------------------------\n let paymentProof: any;\n try {\n // Try base64 first (standard x402 format)\n const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');\n paymentProof = JSON.parse(decoded);\n } catch {\n try {\n // Fallback: raw JSON string\n paymentProof = JSON.parse(paymentHeader);\n } catch {\n return res.status(400).json({\n x402Version: 2,\n error: 'Invalid payment header — expected base64-encoded JSON',\n });\n }\n }\n\n // Resolve payTo for settle (extract from signed proof when using Stripe)\n let settlePayTo: string;\n if (stripeConfig) {\n settlePayTo =\n paymentProof.payload?.authorization?.to ||\n paymentProof.accepted?.payTo ||\n '';\n if (!settlePayTo) {\n return res.status(400).json({\n x402Version: 2,\n error: 'Cannot extract destination address from payment proof',\n });\n }\n } else {\n settlePayTo = options.payTo as string;\n }\n\n // Build payment requirements for facilitator\n const paymentRequirements = {\n scheme: 'exact',\n network,\n amount,\n asset,\n payTo: settlePayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n };\n\n // Call facilitator /settle\n const settleUrl = `${self.facilitatorUrl}/settle`;\n const settleRes = await fetch(settleUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentPayload: paymentProof,\n paymentRequirements,\n }),\n });\n\n const result: SettleResult = await settleRes.json() as SettleResult;\n\n if (!result.success) {\n return res.status(402).json({\n x402Version: 2,\n error: result.errorReason || result.error || 'Payment settlement failed',\n });\n }\n\n // Attach payment info to request\n const paymentInfo: PaymentInfo = {\n verified: true,\n transactionId: result.transaction,\n payer: result.payer,\n network,\n amount: resolvedPrice,\n };\n req.payment = paymentInfo;\n req.x402Payer = result.payer;\n req.x402Paid = true;\n req.x402Transaction = result.transaction;\n req.x402Network = network;\n\n // Set x402 v2 PAYMENT-RESPONSE header (base64 JSON)\n const paymentResponse = {\n x402Version: 2,\n scheme: 'exact',\n network: caip2,\n transaction: result.transaction,\n payer: result.payer,\n amount,\n asset,\n };\n res.setHeader(\n 'PAYMENT-RESPONSE',\n Buffer.from(JSON.stringify(paymentResponse)).toString('base64'),\n );\n\n options.onPaymentSettled?.(req, result);\n\n // Custom validation after payment\n if (options.customRules) {\n const valid = await options.customRules(req);\n if (!valid) {\n return res.status(403).json({ error: 'Custom validation failed' });\n }\n }\n\n next();\n } catch (error) {\n options.onError?.(req, error);\n console.error('[Relai] Protection error:', error);\n res.status(500).json({ error: 'Internal server error' });\n }\n };\n }\n}\n\nexport default Relai;\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 =\n | 'solana'\n | 'base'\n | 'avalanche'\n | 'skale-base'\n | 'skale-base-sepolia'\n | 'skale-bite'\n | 'polygon'\n | '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-base-sepolia': 'eip155:324705682',\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-base-sepolia': 324705682,\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-base-sepolia': '0x2e08028E3C4c2356572E096d8EF835cD5C6030bD',\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-base-sepolia': (tx) => `https://base-sepolia-testnet-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-base-sepolia': 'SKALE Base Sepolia',\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[] = [\n 'solana',\n 'base',\n 'avalanche',\n 'skale-base',\n 'skale-base-sepolia',\n 'skale-bite',\n 'polygon',\n 'ethereum',\n];\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-base-sepolia', '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/server.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as RelaiNetwork } from './types-
|
|
1
|
+
import { a as RelaiNetwork } from './types-C-2GNyMh.cjs';
|
|
2
2
|
|
|
3
3
|
interface RelaiServerConfig {
|
|
4
4
|
/** Network to accept payments on */
|
|
@@ -82,7 +82,7 @@ declare function stripePayTo(stripeSecretKey: string, options?: {
|
|
|
82
82
|
* Server-side SDK for protecting Express endpoints with x402 micropayments.
|
|
83
83
|
* Settles payments through the RelAI facilitator (zero gas fees for users).
|
|
84
84
|
*
|
|
85
|
-
* Supports: Solana, Base, Avalanche, SKALE Base.
|
|
85
|
+
* Supports: Solana, Base, Avalanche, SKALE Base, SKALE Base Sepolia, SKALE BITE, Polygon, and Ethereum.
|
|
86
86
|
*
|
|
87
87
|
* @example
|
|
88
88
|
* ```typescript
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as RelaiNetwork } from './types-
|
|
1
|
+
import { a as RelaiNetwork } from './types-C-2GNyMh.js';
|
|
2
2
|
|
|
3
3
|
interface RelaiServerConfig {
|
|
4
4
|
/** Network to accept payments on */
|
|
@@ -82,7 +82,7 @@ declare function stripePayTo(stripeSecretKey: string, options?: {
|
|
|
82
82
|
* Server-side SDK for protecting Express endpoints with x402 micropayments.
|
|
83
83
|
* Settles payments through the RelAI facilitator (zero gas fees for users).
|
|
84
84
|
*
|
|
85
|
-
* Supports: Solana, Base, Avalanche, SKALE Base.
|
|
85
|
+
* Supports: Solana, Base, Avalanche, SKALE Base, SKALE Base Sepolia, SKALE BITE, Polygon, and Ethereum.
|
|
86
86
|
*
|
|
87
87
|
* @example
|
|
88
88
|
* ```typescript
|
package/dist/server.js
CHANGED
|
@@ -5,6 +5,7 @@ var NETWORK_CAIP2 = {
|
|
|
5
5
|
"base": "eip155:8453",
|
|
6
6
|
"avalanche": "eip155:43114",
|
|
7
7
|
"skale-base": "eip155:1187947933",
|
|
8
|
+
"skale-base-sepolia": "eip155:324705682",
|
|
8
9
|
"skale-bite": "eip155:103698795",
|
|
9
10
|
"polygon": "eip155:137",
|
|
10
11
|
"ethereum": "eip155:1"
|
|
@@ -17,6 +18,7 @@ var USDC_ADDRESSES = {
|
|
|
17
18
|
"base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
18
19
|
"avalanche": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
19
20
|
"skale-base": "0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",
|
|
21
|
+
"skale-base-sepolia": "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",
|
|
20
22
|
"skale-bite": "0xc4083B1E81ceb461Ccef3FDa8A9F24F0d764B6D8",
|
|
21
23
|
"polygon": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
22
24
|
"ethereum": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
|
@@ -154,8 +156,10 @@ var Relai = class {
|
|
|
154
156
|
const tokenMetadata = {
|
|
155
157
|
"eip155:103698795": { name: "USDC", version: "1" },
|
|
156
158
|
// SKALE BITE
|
|
157
|
-
"eip155:1187947933": { name: "
|
|
159
|
+
"eip155:1187947933": { name: "Bridged USDC (SKALE Bridge)", version: "2" },
|
|
158
160
|
// SKALE Base
|
|
161
|
+
"eip155:324705682": { name: "Bridged USDC (SKALE Bridge)", version: "2" },
|
|
162
|
+
// SKALE Base Sepolia
|
|
159
163
|
"eip155:8453": { name: "USD Coin", version: "2" },
|
|
160
164
|
// Base
|
|
161
165
|
"eip155:43114": { name: "USD Coin", version: "2" },
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/server.ts"],"sourcesContent":["// 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","// src/server.ts\nimport {\n NETWORK_CAIP2,\n USDC_ADDRESSES,\n RELAI_FACILITATOR_URL,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RelaiServerConfig {\n /** Network to accept payments on */\n network: RelaiNetwork;\n /** RelAI facilitator URL (default: https://facilitator.x402.fi) */\n facilitatorUrl?: string;\n}\n\nexport type DynamicPrice = number | ((req: any) => number | Promise<number>);\n\nexport interface ProtectOptions {\n /** Price in USD (e.g., 0.01 for 1 cent) */\n price: DynamicPrice;\n /** Wallet address to receive payments, or stripePayTo() for Stripe settlement */\n payTo: string | StripePayTo;\n /** Description shown to payer */\n description?: string;\n /** MIME type of the response (default: application/json) */\n mimeType?: string;\n /** Maximum timeout in seconds (default: 60) */\n maxTimeoutSeconds?: number;\n /** Override network for this endpoint */\n network?: RelaiNetwork;\n /** Custom validation after payment is settled */\n customRules?: (req: any) => boolean | Promise<boolean>;\n /** Callback when 402 is returned (no payment provided) */\n onPaymentRequired?: (req: any, info: { price: number; network: RelaiNetwork }) => void;\n /** Callback when payment is settled successfully */\n onPaymentSettled?: (req: any, result: SettleResult) => void;\n /** Callback on error */\n onError?: (req: any, error: unknown) => void;\n}\n\nexport interface SettleResult {\n success: boolean;\n transaction?: string;\n payer?: string;\n network?: string;\n error?: string;\n errorReason?: string;\n}\n\nexport interface PaymentInfo {\n verified: boolean;\n transactionId?: string;\n payer?: string;\n network: RelaiNetwork;\n amount: number;\n}\n\n// ============================================================================\n// Stripe Pay-To Helper\n// ============================================================================\n\n/** Config returned by stripePayTo() - used by protect() to create Stripe deposit addresses */\nexport interface StripePayTo {\n readonly __brand: 'stripePayTo';\n readonly secretKey: string;\n /** Stripe crypto deposits network (default: 'base') */\n readonly stripeNetwork: string;\n}\n\n/**\n * Create a Stripe pay-to configuration for x402 payments.\n * Payments settle as USD in your Stripe Dashboard - no crypto knowledge required.\n *\n * Stripe creates a fresh PaymentIntent + deposit address per request.\n * Network is auto-set to Base (Stripe settles USDC on Base).\n *\n * @example\n * ```typescript\n * import Relai, { stripePayTo } from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * price: 0.01,\n * payTo: stripePayTo(process.env.STRIPE_SECRET_KEY!),\n * }), (req, res) => {\n * res.json({ data: 'paid content' });\n * });\n * ```\n */\nexport function stripePayTo(\n stripeSecretKey: string,\n options?: { network?: string },\n): StripePayTo {\n if (!stripeSecretKey) {\n throw new Error('stripePayTo requires a Stripe secret key');\n }\n return {\n __brand: 'stripePayTo' as const,\n secretKey: stripeSecretKey,\n stripeNetwork: options?.network || 'base',\n };\n}\n\n/** @internal Type guard for StripePayTo */\nfunction isStripePayTo(payTo: unknown): payTo is StripePayTo {\n return (\n typeof payTo === 'object' &&\n payTo !== null &&\n (payTo as any).__brand === 'stripePayTo'\n );\n}\n\n/**\n * @internal Create a Stripe PaymentIntent with crypto payment method\n * and extract the deposit address.\n */\nasync function createStripeDepositAddress(\n secretKey: string,\n amountUsdCents: number,\n network: string = 'base',\n): Promise<string> {\n const params = new URLSearchParams();\n params.append('amount', String(amountUsdCents));\n params.append('currency', 'usd');\n params.append('payment_method_types[]', 'crypto');\n params.append('payment_method_data[type]', 'crypto');\n params.append('confirm', 'true');\n\n const res = await fetch('https://api.stripe.com/v1/payment_intents', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${secretKey}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as any;\n const msg = err?.error?.message || res.statusText;\n\n // Provide actionable guidance for common issues\n if (msg.includes('unknown parameter') || msg.includes('crypto')) {\n throw new Error(\n `Stripe crypto payins not enabled on this account. ` +\n `Enable at: https://support.stripe.com/questions/get-started-with-pay-with-crypto ` +\n `(Original: ${msg})`,\n );\n }\n throw new Error(`Stripe PaymentIntent creation failed: ${msg}`);\n }\n\n const pi = await res.json() as any;\n const depositDetails = pi.next_action?.crypto_collect_deposit_details;\n if (!depositDetails) {\n throw new Error(\n 'Stripe PaymentIntent missing crypto deposit details. ' +\n 'Ensure crypto payins are enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto',\n );\n }\n\n const address = depositDetails.deposit_addresses?.[network]?.address;\n if (!address) {\n throw new Error(`No Stripe deposit address for network: ${network}`);\n }\n\n return address;\n}\n\n// ============================================================================\n// Relai Server SDK\n// ============================================================================\n\n/**\n * Server-side SDK for protecting Express endpoints with x402 micropayments.\n * Settles payments through the RelAI facilitator (zero gas fees for users).\n *\n * Supports: Solana, Base, Avalanche, SKALE Base.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * payTo: '0xYourWallet',\n * price: 0.01, // $0.01 USDC\n * }), (req, res) => {\n * res.json({ data: 'Protected content', payment: req.payment });\n * });\n * ```\n */\nexport class Relai {\n private network: RelaiNetwork;\n private facilitatorUrl: string;\n private feePayerCache: Map<string, string> = new Map(); // Cache feePayer per network\n\n constructor(config: RelaiServerConfig) {\n this.network = config.network;\n this.facilitatorUrl = config.facilitatorUrl || RELAI_FACILITATOR_URL;\n }\n\n /**\n * Get feePayer address for a network (cached)\n */\n private async getFeePayer(caip2: string): Promise<string | undefined> {\n // Check cache first\n if (this.feePayerCache.has(caip2)) {\n return this.feePayerCache.get(caip2);\n }\n\n // If using RelAI facilitator, use hardcoded address (no fetch needed)\n const isRelAI = this.facilitatorUrl.includes('facilitator.x402.fi') || \n this.facilitatorUrl.includes('relai');\n \n if (isRelAI) {\n const relaiFeePayer = '0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085';\n this.feePayerCache.set(caip2, relaiFeePayer);\n return relaiFeePayer;\n }\n\n // For other facilitators, fetch from /supported\n try {\n const supportedUrl = `${this.facilitatorUrl}/supported`;\n const supportedRes = await fetch(supportedUrl);\n if (supportedRes.ok) {\n const supportedData = await supportedRes.json();\n // Cache all feePayers from supported kinds\n supportedData.kinds?.forEach((kind: any) => {\n if (kind.network && kind.extra?.feePayer) {\n this.feePayerCache.set(kind.network, kind.extra.feePayer);\n }\n });\n return this.feePayerCache.get(caip2);\n }\n } catch (err) {\n // feePayer MUST come from facilitator - cannot use env for security\n console.error(`[Relai] Failed to fetch feePayer from facilitator: ${err}`);\n }\n return undefined;\n }\n\n /**\n * Express middleware to protect an endpoint with x402 micropayments.\n *\n * Flow:\n * 1. No payment header → returns 402 with payment requirements\n * 2. Payment header present → calls RelAI facilitator `/settle`\n * 3. Settlement success → sets `PAYMENT-RESPONSE` header, attaches `req.payment`, calls `next()`\n */\n protect(options: ProtectOptions) {\n const self = this;\n\n return async (req: any, res: any, next: any) => {\n try {\n // Resolve dynamic price\n const resolvedPrice = typeof options.price === 'function'\n ? await options.price(req)\n : options.price;\n\n if (typeof resolvedPrice !== 'number' || !isFinite(resolvedPrice) || resolvedPrice <= 0) {\n return res.status(400).json({ error: 'Invalid price configuration' });\n }\n\n // Resolve network (Stripe auto-sets to base)\n const stripeConfig = isStripePayTo(options.payTo) ? options.payTo : null;\n const network = stripeConfig\n ? (stripeConfig.stripeNetwork as RelaiNetwork) || 'base'\n : (options.network || self.network);\n const caip2 = NETWORK_CAIP2[network];\n const asset = USDC_ADDRESSES[network];\n const amount = String(Math.floor(resolvedPrice * 1_000_000)); // USD → USDC atomic units (6 decimals)\n\n // Check for payment header (base64-encoded JSON)\n const paymentHeader =\n req.headers['x-payment'] ||\n req.headers['payment-signature'] ||\n req.headers['x-payment-signature'];\n\n // -----------------------------------------------------------\n // No payment → return 402 Payment Required\n // -----------------------------------------------------------\n if (!paymentHeader) {\n options.onPaymentRequired?.(req, { price: resolvedPrice, network });\n\n // Resolve payTo address (Stripe creates a fresh deposit address per request)\n let resolvedPayTo: string;\n if (stripeConfig) {\n const amountInCents = Math.max(1, Math.round(resolvedPrice * 100));\n resolvedPayTo = await createStripeDepositAddress(\n stripeConfig.secretKey,\n amountInCents,\n stripeConfig.stripeNetwork,\n );\n } else {\n resolvedPayTo = options.payTo as string;\n }\n\n // Get facilitator feePayer address (cached)\n const feePayer = await self.getFeePayer(caip2);\n\n // Token metadata per network\n // IMPORTANT: These must match the actual EIP-712 domain on each network\n const tokenMetadata: Record<string, { name: string; version: string }> = {\n 'eip155:103698795': { name: 'USDC', version: '1' }, // SKALE BITE\n 'eip155:1187947933': { name: 'USD Coin', version: '2' }, // SKALE Base\n 'eip155:8453': { name: 'USD Coin', version: '2' }, // Base\n 'eip155:43114': { name: 'USD Coin', version: '2' }, // Avalanche\n 'eip155:137': { name: 'USD Coin', version: '2' }, // Polygon\n 'eip155:1': { name: 'USD Coin', version: '2' }, // Ethereum\n };\n const metadata = tokenMetadata[caip2] || { name: 'USDC', version: '1' };\n\n return res.status(402).json({\n x402Version: 2,\n error: 'Payment required',\n resource: {\n url: `${req.protocol}://${req.get('host')}${req.originalUrl}`,\n description: options.description || 'API access',\n mimeType: options.mimeType || 'application/json',\n },\n accepts: [{\n scheme: 'exact',\n network: caip2,\n amount,\n asset,\n payTo: resolvedPayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n extra: {\n name: metadata.name,\n version: metadata.version,\n decimals: 6,\n ...(feePayer && { feePayer }), // Add feePayer if available\n },\n }],\n });\n }\n\n // -----------------------------------------------------------\n // Payment header present → parse and settle via facilitator\n // -----------------------------------------------------------\n let paymentProof: any;\n try {\n // Try base64 first (standard x402 format)\n const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');\n paymentProof = JSON.parse(decoded);\n } catch {\n try {\n // Fallback: raw JSON string\n paymentProof = JSON.parse(paymentHeader);\n } catch {\n return res.status(400).json({\n x402Version: 2,\n error: 'Invalid payment header — expected base64-encoded JSON',\n });\n }\n }\n\n // Resolve payTo for settle (extract from signed proof when using Stripe)\n let settlePayTo: string;\n if (stripeConfig) {\n settlePayTo =\n paymentProof.payload?.authorization?.to ||\n paymentProof.accepted?.payTo ||\n '';\n if (!settlePayTo) {\n return res.status(400).json({\n x402Version: 2,\n error: 'Cannot extract destination address from payment proof',\n });\n }\n } else {\n settlePayTo = options.payTo as string;\n }\n\n // Build payment requirements for facilitator\n const paymentRequirements = {\n scheme: 'exact',\n network,\n amount,\n asset,\n payTo: settlePayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n };\n\n // Call facilitator /settle\n const settleUrl = `${self.facilitatorUrl}/settle`;\n const settleRes = await fetch(settleUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentPayload: paymentProof,\n paymentRequirements,\n }),\n });\n\n const result: SettleResult = await settleRes.json() as SettleResult;\n\n if (!result.success) {\n return res.status(402).json({\n x402Version: 2,\n error: result.errorReason || result.error || 'Payment settlement failed',\n });\n }\n\n // Attach payment info to request\n const paymentInfo: PaymentInfo = {\n verified: true,\n transactionId: result.transaction,\n payer: result.payer,\n network,\n amount: resolvedPrice,\n };\n req.payment = paymentInfo;\n req.x402Payer = result.payer;\n req.x402Paid = true;\n req.x402Transaction = result.transaction;\n req.x402Network = network;\n\n // Set x402 v2 PAYMENT-RESPONSE header (base64 JSON)\n const paymentResponse = {\n x402Version: 2,\n scheme: 'exact',\n network: caip2,\n transaction: result.transaction,\n payer: result.payer,\n amount,\n asset,\n };\n res.setHeader(\n 'PAYMENT-RESPONSE',\n Buffer.from(JSON.stringify(paymentResponse)).toString('base64'),\n );\n\n options.onPaymentSettled?.(req, result);\n\n // Custom validation after payment\n if (options.customRules) {\n const valid = await options.customRules(req);\n if (!valid) {\n return res.status(403).json({ error: 'Custom validation failed' });\n }\n }\n\n next();\n } catch (error) {\n options.onError?.(req, error);\n console.error('[Relai] Protection error:', error);\n res.status(500).json({ error: 'Internal server error' });\n }\n };\n }\n}\n\nexport default Relai;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/server.ts"],"sourcesContent":["// 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 =\n | 'solana'\n | 'base'\n | 'avalanche'\n | 'skale-base'\n | 'skale-base-sepolia'\n | 'skale-bite'\n | 'polygon'\n | '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-base-sepolia': 'eip155:324705682',\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-base-sepolia': 324705682,\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-base-sepolia': '0x2e08028E3C4c2356572E096d8EF835cD5C6030bD',\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-base-sepolia': (tx) => `https://base-sepolia-testnet-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-base-sepolia': 'SKALE Base Sepolia',\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[] = [\n 'solana',\n 'base',\n 'avalanche',\n 'skale-base',\n 'skale-base-sepolia',\n 'skale-bite',\n 'polygon',\n 'ethereum',\n];\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-base-sepolia', '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","// src/server.ts\nimport {\n NETWORK_CAIP2,\n USDC_ADDRESSES,\n RELAI_FACILITATOR_URL,\n type RelaiNetwork,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RelaiServerConfig {\n /** Network to accept payments on */\n network: RelaiNetwork;\n /** RelAI facilitator URL (default: https://facilitator.x402.fi) */\n facilitatorUrl?: string;\n}\n\nexport type DynamicPrice = number | ((req: any) => number | Promise<number>);\n\nexport interface ProtectOptions {\n /** Price in USD (e.g., 0.01 for 1 cent) */\n price: DynamicPrice;\n /** Wallet address to receive payments, or stripePayTo() for Stripe settlement */\n payTo: string | StripePayTo;\n /** Description shown to payer */\n description?: string;\n /** MIME type of the response (default: application/json) */\n mimeType?: string;\n /** Maximum timeout in seconds (default: 60) */\n maxTimeoutSeconds?: number;\n /** Override network for this endpoint */\n network?: RelaiNetwork;\n /** Custom validation after payment is settled */\n customRules?: (req: any) => boolean | Promise<boolean>;\n /** Callback when 402 is returned (no payment provided) */\n onPaymentRequired?: (req: any, info: { price: number; network: RelaiNetwork }) => void;\n /** Callback when payment is settled successfully */\n onPaymentSettled?: (req: any, result: SettleResult) => void;\n /** Callback on error */\n onError?: (req: any, error: unknown) => void;\n}\n\nexport interface SettleResult {\n success: boolean;\n transaction?: string;\n payer?: string;\n network?: string;\n error?: string;\n errorReason?: string;\n}\n\nexport interface PaymentInfo {\n verified: boolean;\n transactionId?: string;\n payer?: string;\n network: RelaiNetwork;\n amount: number;\n}\n\n// ============================================================================\n// Stripe Pay-To Helper\n// ============================================================================\n\n/** Config returned by stripePayTo() - used by protect() to create Stripe deposit addresses */\nexport interface StripePayTo {\n readonly __brand: 'stripePayTo';\n readonly secretKey: string;\n /** Stripe crypto deposits network (default: 'base') */\n readonly stripeNetwork: string;\n}\n\n/**\n * Create a Stripe pay-to configuration for x402 payments.\n * Payments settle as USD in your Stripe Dashboard - no crypto knowledge required.\n *\n * Stripe creates a fresh PaymentIntent + deposit address per request.\n * Network is auto-set to Base (Stripe settles USDC on Base).\n *\n * @example\n * ```typescript\n * import Relai, { stripePayTo } from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * price: 0.01,\n * payTo: stripePayTo(process.env.STRIPE_SECRET_KEY!),\n * }), (req, res) => {\n * res.json({ data: 'paid content' });\n * });\n * ```\n */\nexport function stripePayTo(\n stripeSecretKey: string,\n options?: { network?: string },\n): StripePayTo {\n if (!stripeSecretKey) {\n throw new Error('stripePayTo requires a Stripe secret key');\n }\n return {\n __brand: 'stripePayTo' as const,\n secretKey: stripeSecretKey,\n stripeNetwork: options?.network || 'base',\n };\n}\n\n/** @internal Type guard for StripePayTo */\nfunction isStripePayTo(payTo: unknown): payTo is StripePayTo {\n return (\n typeof payTo === 'object' &&\n payTo !== null &&\n (payTo as any).__brand === 'stripePayTo'\n );\n}\n\n/**\n * @internal Create a Stripe PaymentIntent with crypto payment method\n * and extract the deposit address.\n */\nasync function createStripeDepositAddress(\n secretKey: string,\n amountUsdCents: number,\n network: string = 'base',\n): Promise<string> {\n const params = new URLSearchParams();\n params.append('amount', String(amountUsdCents));\n params.append('currency', 'usd');\n params.append('payment_method_types[]', 'crypto');\n params.append('payment_method_data[type]', 'crypto');\n params.append('confirm', 'true');\n\n const res = await fetch('https://api.stripe.com/v1/payment_intents', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${secretKey}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({})) as any;\n const msg = err?.error?.message || res.statusText;\n\n // Provide actionable guidance for common issues\n if (msg.includes('unknown parameter') || msg.includes('crypto')) {\n throw new Error(\n `Stripe crypto payins not enabled on this account. ` +\n `Enable at: https://support.stripe.com/questions/get-started-with-pay-with-crypto ` +\n `(Original: ${msg})`,\n );\n }\n throw new Error(`Stripe PaymentIntent creation failed: ${msg}`);\n }\n\n const pi = await res.json() as any;\n const depositDetails = pi.next_action?.crypto_collect_deposit_details;\n if (!depositDetails) {\n throw new Error(\n 'Stripe PaymentIntent missing crypto deposit details. ' +\n 'Ensure crypto payins are enabled: https://support.stripe.com/questions/get-started-with-pay-with-crypto',\n );\n }\n\n const address = depositDetails.deposit_addresses?.[network]?.address;\n if (!address) {\n throw new Error(`No Stripe deposit address for network: ${network}`);\n }\n\n return address;\n}\n\n// ============================================================================\n// Relai Server SDK\n// ============================================================================\n\n/**\n * Server-side SDK for protecting Express endpoints with x402 micropayments.\n * Settles payments through the RelAI facilitator (zero gas fees for users).\n *\n * Supports: Solana, Base, Avalanche, SKALE Base, SKALE Base Sepolia, SKALE BITE, Polygon, and Ethereum.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n *\n * const relai = new Relai({ network: 'base' });\n *\n * app.get('/api/data', relai.protect({\n * payTo: '0xYourWallet',\n * price: 0.01, // $0.01 USDC\n * }), (req, res) => {\n * res.json({ data: 'Protected content', payment: req.payment });\n * });\n * ```\n */\nexport class Relai {\n private network: RelaiNetwork;\n private facilitatorUrl: string;\n private feePayerCache: Map<string, string> = new Map(); // Cache feePayer per network\n\n constructor(config: RelaiServerConfig) {\n this.network = config.network;\n this.facilitatorUrl = config.facilitatorUrl || RELAI_FACILITATOR_URL;\n }\n\n /**\n * Get feePayer address for a network (cached)\n */\n private async getFeePayer(caip2: string): Promise<string | undefined> {\n // Check cache first\n if (this.feePayerCache.has(caip2)) {\n return this.feePayerCache.get(caip2);\n }\n\n // If using RelAI facilitator, use hardcoded address (no fetch needed)\n const isRelAI = this.facilitatorUrl.includes('facilitator.x402.fi') || \n this.facilitatorUrl.includes('relai');\n \n if (isRelAI) {\n const relaiFeePayer = '0x1892f72fdB3A966b2AD8595aA5f7741Ef72d6085';\n this.feePayerCache.set(caip2, relaiFeePayer);\n return relaiFeePayer;\n }\n\n // For other facilitators, fetch from /supported\n try {\n const supportedUrl = `${this.facilitatorUrl}/supported`;\n const supportedRes = await fetch(supportedUrl);\n if (supportedRes.ok) {\n const supportedData = await supportedRes.json();\n // Cache all feePayers from supported kinds\n supportedData.kinds?.forEach((kind: any) => {\n if (kind.network && kind.extra?.feePayer) {\n this.feePayerCache.set(kind.network, kind.extra.feePayer);\n }\n });\n return this.feePayerCache.get(caip2);\n }\n } catch (err) {\n // feePayer MUST come from facilitator - cannot use env for security\n console.error(`[Relai] Failed to fetch feePayer from facilitator: ${err}`);\n }\n return undefined;\n }\n\n /**\n * Express middleware to protect an endpoint with x402 micropayments.\n *\n * Flow:\n * 1. No payment header → returns 402 with payment requirements\n * 2. Payment header present → calls RelAI facilitator `/settle`\n * 3. Settlement success → sets `PAYMENT-RESPONSE` header, attaches `req.payment`, calls `next()`\n */\n protect(options: ProtectOptions) {\n const self = this;\n\n return async (req: any, res: any, next: any) => {\n try {\n // Resolve dynamic price\n const resolvedPrice = typeof options.price === 'function'\n ? await options.price(req)\n : options.price;\n\n if (typeof resolvedPrice !== 'number' || !isFinite(resolvedPrice) || resolvedPrice <= 0) {\n return res.status(400).json({ error: 'Invalid price configuration' });\n }\n\n // Resolve network (Stripe auto-sets to base)\n const stripeConfig = isStripePayTo(options.payTo) ? options.payTo : null;\n const network = stripeConfig\n ? (stripeConfig.stripeNetwork as RelaiNetwork) || 'base'\n : (options.network || self.network);\n const caip2 = NETWORK_CAIP2[network];\n const asset = USDC_ADDRESSES[network];\n const amount = String(Math.floor(resolvedPrice * 1_000_000)); // USD → USDC atomic units (6 decimals)\n\n // Check for payment header (base64-encoded JSON)\n const paymentHeader =\n req.headers['x-payment'] ||\n req.headers['payment-signature'] ||\n req.headers['x-payment-signature'];\n\n // -----------------------------------------------------------\n // No payment → return 402 Payment Required\n // -----------------------------------------------------------\n if (!paymentHeader) {\n options.onPaymentRequired?.(req, { price: resolvedPrice, network });\n\n // Resolve payTo address (Stripe creates a fresh deposit address per request)\n let resolvedPayTo: string;\n if (stripeConfig) {\n const amountInCents = Math.max(1, Math.round(resolvedPrice * 100));\n resolvedPayTo = await createStripeDepositAddress(\n stripeConfig.secretKey,\n amountInCents,\n stripeConfig.stripeNetwork,\n );\n } else {\n resolvedPayTo = options.payTo as string;\n }\n\n // Get facilitator feePayer address (cached)\n const feePayer = await self.getFeePayer(caip2);\n\n // Token metadata per network\n // IMPORTANT: These must match the actual EIP-712 domain on each network\n const tokenMetadata: Record<string, { name: string; version: string }> = {\n 'eip155:103698795': { name: 'USDC', version: '1' }, // SKALE BITE\n 'eip155:1187947933': { name: 'Bridged USDC (SKALE Bridge)', version: '2' }, // SKALE Base\n 'eip155:324705682': { name: 'Bridged USDC (SKALE Bridge)', version: '2' }, // SKALE Base Sepolia\n 'eip155:8453': { name: 'USD Coin', version: '2' }, // Base\n 'eip155:43114': { name: 'USD Coin', version: '2' }, // Avalanche\n 'eip155:137': { name: 'USD Coin', version: '2' }, // Polygon\n 'eip155:1': { name: 'USD Coin', version: '2' }, // Ethereum\n };\n const metadata = tokenMetadata[caip2] || { name: 'USDC', version: '1' };\n\n return res.status(402).json({\n x402Version: 2,\n error: 'Payment required',\n resource: {\n url: `${req.protocol}://${req.get('host')}${req.originalUrl}`,\n description: options.description || 'API access',\n mimeType: options.mimeType || 'application/json',\n },\n accepts: [{\n scheme: 'exact',\n network: caip2,\n amount,\n asset,\n payTo: resolvedPayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n extra: {\n name: metadata.name,\n version: metadata.version,\n decimals: 6,\n ...(feePayer && { feePayer }), // Add feePayer if available\n },\n }],\n });\n }\n\n // -----------------------------------------------------------\n // Payment header present → parse and settle via facilitator\n // -----------------------------------------------------------\n let paymentProof: any;\n try {\n // Try base64 first (standard x402 format)\n const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');\n paymentProof = JSON.parse(decoded);\n } catch {\n try {\n // Fallback: raw JSON string\n paymentProof = JSON.parse(paymentHeader);\n } catch {\n return res.status(400).json({\n x402Version: 2,\n error: 'Invalid payment header — expected base64-encoded JSON',\n });\n }\n }\n\n // Resolve payTo for settle (extract from signed proof when using Stripe)\n let settlePayTo: string;\n if (stripeConfig) {\n settlePayTo =\n paymentProof.payload?.authorization?.to ||\n paymentProof.accepted?.payTo ||\n '';\n if (!settlePayTo) {\n return res.status(400).json({\n x402Version: 2,\n error: 'Cannot extract destination address from payment proof',\n });\n }\n } else {\n settlePayTo = options.payTo as string;\n }\n\n // Build payment requirements for facilitator\n const paymentRequirements = {\n scheme: 'exact',\n network,\n amount,\n asset,\n payTo: settlePayTo,\n maxTimeoutSeconds: options.maxTimeoutSeconds || 60,\n };\n\n // Call facilitator /settle\n const settleUrl = `${self.facilitatorUrl}/settle`;\n const settleRes = await fetch(settleUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n paymentPayload: paymentProof,\n paymentRequirements,\n }),\n });\n\n const result: SettleResult = await settleRes.json() as SettleResult;\n\n if (!result.success) {\n return res.status(402).json({\n x402Version: 2,\n error: result.errorReason || result.error || 'Payment settlement failed',\n });\n }\n\n // Attach payment info to request\n const paymentInfo: PaymentInfo = {\n verified: true,\n transactionId: result.transaction,\n payer: result.payer,\n network,\n amount: resolvedPrice,\n };\n req.payment = paymentInfo;\n req.x402Payer = result.payer;\n req.x402Paid = true;\n req.x402Transaction = result.transaction;\n req.x402Network = network;\n\n // Set x402 v2 PAYMENT-RESPONSE header (base64 JSON)\n const paymentResponse = {\n x402Version: 2,\n scheme: 'exact',\n network: caip2,\n transaction: result.transaction,\n payer: result.payer,\n amount,\n asset,\n };\n res.setHeader(\n 'PAYMENT-RESPONSE',\n Buffer.from(JSON.stringify(paymentResponse)).toString('base64'),\n );\n\n options.onPaymentSettled?.(req, result);\n\n // Custom validation after payment\n if (options.customRules) {\n const valid = await options.customRules(req);\n if (!valid) {\n return res.status(403).json({ error: 'Custom validation failed' });\n }\n }\n\n next();\n } catch (error) {\n options.onError?.(req, error);\n console.error('[Relai] Protection error:', error);\n res.status(500).json({ error: 'Internal server error' });\n }\n };\n }\n}\n\nexport default Relai;\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** RelAI Facilitator URL */
|
|
2
2
|
declare const RELAI_FACILITATOR_URL = "https://facilitator.x402.fi";
|
|
3
3
|
/** All networks supported by RelAI facilitator */
|
|
4
|
-
type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-bite' | 'polygon' | 'ethereum';
|
|
4
|
+
type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-base-sepolia' | 'skale-bite' | 'polygon' | 'ethereum';
|
|
5
5
|
/** CAIP-2 network identifiers */
|
|
6
6
|
declare const NETWORK_CAIP2: Record<RelaiNetwork, string>;
|
|
7
7
|
/** Reverse lookup: CAIP-2 → simple network name */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** RelAI Facilitator URL */
|
|
2
2
|
declare const RELAI_FACILITATOR_URL = "https://facilitator.x402.fi";
|
|
3
3
|
/** All networks supported by RelAI facilitator */
|
|
4
|
-
type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-bite' | 'polygon' | 'ethereum';
|
|
4
|
+
type RelaiNetwork = 'solana' | 'base' | 'avalanche' | 'skale-base' | 'skale-base-sepolia' | 'skale-bite' | 'polygon' | 'ethereum';
|
|
5
5
|
/** CAIP-2 network identifiers */
|
|
6
6
|
declare const NETWORK_CAIP2: Record<RelaiNetwork, string>;
|
|
7
7
|
/** Reverse lookup: CAIP-2 → simple network name */
|
package/dist/utils/index.cjs
CHANGED
|
@@ -47,6 +47,7 @@ var NETWORK_V1_TO_V2 = {
|
|
|
47
47
|
"polygon": "eip155:137",
|
|
48
48
|
"avalanche": "eip155:43114",
|
|
49
49
|
"skale-base": "eip155:1187947933",
|
|
50
|
+
"skale-base-sepolia": "eip155:324705682",
|
|
50
51
|
"peaq": "eip155:3338",
|
|
51
52
|
"sei": "eip155:1329"
|
|
52
53
|
};
|
|
@@ -73,6 +74,7 @@ function networkV2ToV1(caip2Network) {
|
|
|
73
74
|
"84532": "base-sepolia",
|
|
74
75
|
"43114": "avalanche",
|
|
75
76
|
"1187947933": "skale-base",
|
|
77
|
+
"324705682": "skale-base-sepolia",
|
|
76
78
|
"3338": "peaq",
|
|
77
79
|
"1329": "sei"
|
|
78
80
|
};
|
|
@@ -158,7 +160,7 @@ function isSolanaNetwork(network) {
|
|
|
158
160
|
return network === "solana" || network === "solana-devnet" || network.startsWith("solana:");
|
|
159
161
|
}
|
|
160
162
|
function isEvmNetwork(network) {
|
|
161
|
-
const evmNetworks = ["base", "base-sepolia", "ethereum", "polygon", "avalanche", "skale-base", "skale-bite", "peaq", "sei"];
|
|
163
|
+
const evmNetworks = ["base", "base-sepolia", "ethereum", "polygon", "avalanche", "skale-base", "skale-base-sepolia", "skale-bite", "peaq", "sei"];
|
|
162
164
|
return evmNetworks.includes(network) || network.startsWith("eip155:");
|
|
163
165
|
}
|
|
164
166
|
function toAtomicUnits(usd, decimals = 6) {
|
package/dist/utils/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/index.ts","../../src/utils/payload-converter.ts"],"sourcesContent":["export {\n // Payload conversion\n convertV1ToV2,\n convertV2ToV1,\n convertPayloadToVersion,\n detectPayloadVersion,\n normalizePaymentHeader,\n \n // Network conversion\n networkV1ToV2,\n networkV2ToV1,\n NETWORK_V1_TO_V2,\n NETWORK_V2_TO_V1,\n \n // Network detection\n isSolanaNetwork,\n isEvmNetwork,\n \n // Amount utilities\n toAtomicUnits,\n fromAtomicUnits,\n formatUsd,\n \n // Types\n type X402Version,\n type V1PaymentPayload,\n type V2PaymentPayload,\n type PaymentPayload,\n} from './payload-converter';\n","/**\n * Utility to convert x402 payment payloads between v1 and v2 formats.\n * \n * V1 PaymentPayload format:\n * {\n * \"x402Version\": 1,\n * \"scheme\": \"exact\",\n * \"network\": \"solana\",\n * \"payload\": { ... }\n * }\n * \n * V2 PaymentPayload format:\n * {\n * \"x402Version\": 2,\n * \"resource\": { \"url\": \"...\", \"description\": \"...\", \"mimeType\": \"...\" },\n * \"accepted\": { \"scheme\": \"exact\", \"network\": \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\", ... },\n * \"payload\": { ... }\n * }\n */\n\n// Network name mappings between v1 (simple names) and v2 (CAIP-2)\nexport const NETWORK_V1_TO_V2: Record<string, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',\n 'base': 'eip155:8453',\n 'base-sepolia': 'eip155:84532',\n 'ethereum': 'eip155:1',\n 'polygon': 'eip155:137',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'peaq': 'eip155:3338',\n 'sei': 'eip155:1329',\n};\n\nexport const NETWORK_V2_TO_V1: Record<string, string> = Object.fromEntries(\n Object.entries(NETWORK_V1_TO_V2).map(([k, v]) => [v, k])\n);\n\n/**\n * Convert CAIP-2 network identifier to simple v1 network name\n */\nexport function networkV2ToV1(caip2Network: string): string {\n if (!caip2Network) return 'solana';\n \n // Direct lookup\n if (NETWORK_V2_TO_V1[caip2Network]) {\n return NETWORK_V2_TO_V1[caip2Network];\n }\n \n // Handle partial matches for Solana\n if (caip2Network.startsWith('solana:')) {\n const chainId = caip2Network.split(':')[1];\n if (chainId === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') return 'solana';\n if (chainId === 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1') return 'solana-devnet';\n return 'solana';\n }\n \n // Handle EVM chains by chain ID\n if (caip2Network.startsWith('eip155:')) {\n const chainId = caip2Network.split(':')[1];\n const mapping: Record<string, string> = {\n '1': 'ethereum',\n '137': 'polygon',\n '8453': 'base',\n '84532': 'base-sepolia',\n '43114': 'avalanche',\n '1187947933': 'skale-base',\n '3338': 'peaq',\n '1329': 'sei',\n };\n return mapping[chainId] || caip2Network;\n }\n \n return caip2Network;\n}\n\n/**\n * Convert simple v1 network name to CAIP-2 identifier\n */\nexport function networkV1ToV2(v1Network: string): string {\n if (!v1Network) return 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n return NETWORK_V1_TO_V2[v1Network] || v1Network;\n}\n\nexport type X402Version = 1 | 2;\n\nexport interface V1PaymentPayload {\n x402Version: 1;\n scheme: string;\n network: string;\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport interface V2PaymentPayload {\n x402Version: 2;\n resource: {\n url: string;\n description?: string;\n mimeType?: string;\n };\n accepted: {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n };\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport type PaymentPayload = V1PaymentPayload | V2PaymentPayload;\n\n/**\n * Detect x402 version from payload\n */\nexport function detectPayloadVersion(payload: unknown): X402Version | null {\n if (!payload || typeof payload !== 'object') return null;\n \n const p = payload as Record<string, unknown>;\n \n if (p.x402Version === 1) return 1;\n if (p.x402Version === 2) return 2;\n \n // Heuristics if x402Version is missing\n if ('accepted' in p && 'resource' in p) return 2;\n if ('scheme' in p && 'network' in p && !('accepted' in p)) return 1;\n \n return null;\n}\n\n/**\n * Convert v2 payment payload to v1 format\n */\nexport function convertV2ToV1(v2Payload: V2PaymentPayload): V1PaymentPayload {\n const accepted = v2Payload.accepted || {};\n \n return {\n x402Version: 1,\n scheme: accepted.scheme || 'exact',\n network: networkV2ToV1(accepted.network),\n payload: v2Payload.payload,\n };\n}\n\n/**\n * Convert v1 payment payload to v2 format\n */\nexport function convertV1ToV2(\n v1Payload: V1PaymentPayload,\n resourceInfo: { url?: string; description?: string; mimeType?: string } = {}\n): V2PaymentPayload {\n return {\n x402Version: 2,\n resource: {\n url: resourceInfo.url || '',\n description: resourceInfo.description || '',\n mimeType: resourceInfo.mimeType || 'application/json',\n },\n accepted: {\n scheme: v1Payload.scheme || 'exact',\n network: networkV1ToV2(v1Payload.network),\n },\n payload: v1Payload.payload,\n };\n}\n\n/**\n * Convert payment payload to target version\n */\nexport function convertPayloadToVersion(\n payload: PaymentPayload,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): PaymentPayload | null {\n const sourceVersion = detectPayloadVersion(payload);\n \n if (!sourceVersion) {\n console.warn('[payload-converter] Could not detect source payload version');\n return null;\n }\n \n // No conversion needed\n if (sourceVersion === targetVersion) {\n return payload;\n }\n \n // V2 -> V1\n if (sourceVersion === 2 && targetVersion === 1) {\n return convertV2ToV1(payload as V2PaymentPayload);\n }\n \n // V1 -> V2\n if (sourceVersion === 1 && targetVersion === 2) {\n return convertV1ToV2(payload as V1PaymentPayload, options.resourceInfo);\n }\n \n return null;\n}\n\n/**\n * Normalize payment header for target x402 version\n */\nexport function normalizePaymentHeader(\n base64Header: string,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): { header: string; payload: PaymentPayload } | null {\n if (!base64Header) return null;\n \n try {\n const decoded = JSON.parse(\n typeof window !== 'undefined' \n ? atob(base64Header) \n : Buffer.from(base64Header, 'base64').toString()\n );\n const converted = convertPayloadToVersion(decoded, targetVersion, options);\n \n if (!converted) {\n return null;\n }\n \n const encoded = typeof window !== 'undefined'\n ? btoa(JSON.stringify(converted))\n : Buffer.from(JSON.stringify(converted)).toString('base64');\n \n return {\n header: encoded,\n payload: converted,\n };\n } catch (e) {\n console.error('[payload-converter] Failed to normalize header:', e);\n return null;\n }\n}\n\n/**\n * Check if network is Solana-based\n */\nexport function isSolanaNetwork(network: string): boolean {\n return network === 'solana' || \n network === 'solana-devnet' || \n network.startsWith('solana:');\n}\n\n/**\n * Check if network is EVM-based\n */\nexport function isEvmNetwork(network: string): boolean {\n const evmNetworks = ['base', 'base-sepolia', 'ethereum', 'polygon', 'avalanche', 'skale-base', 'skale-bite', 'peaq', 'sei'];\n return evmNetworks.includes(network) || network.startsWith('eip155:');\n}\n\n// ============================================================================\n// Amount Conversion Utilities\n// ============================================================================\n\n/**\n * Convert USD amount to atomic units\n * @param usd - Amount in USD (e.g., 0.05)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns Atomic units as string\n */\nexport function toAtomicUnits(usd: number, decimals: number = 6): string {\n return Math.floor(usd * Math.pow(10, decimals)).toString();\n}\n\n/**\n * Convert atomic units to USD amount\n * @param atomic - Atomic units (string or bigint)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns USD amount as number\n */\nexport function fromAtomicUnits(atomic: string | bigint, decimals: number = 6): number {\n const value = typeof atomic === 'bigint' ? atomic : BigInt(atomic);\n return Number(value) / Math.pow(10, decimals);\n}\n\n/**\n * Format USD amount for display\n * @param usd - Amount in USD\n * @param maxDecimals - Maximum decimal places (default: 4)\n * @returns Formatted string\n */\nexport function formatUsd(usd: number, maxDecimals: number = 4): string {\n if (usd < 0.0001) return '<$0.0001';\n return `$${usd.toFixed(maxDecimals).replace(/\\.?0+$/, '')}`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/index.ts","../../src/utils/payload-converter.ts"],"sourcesContent":["export {\n // Payload conversion\n convertV1ToV2,\n convertV2ToV1,\n convertPayloadToVersion,\n detectPayloadVersion,\n normalizePaymentHeader,\n \n // Network conversion\n networkV1ToV2,\n networkV2ToV1,\n NETWORK_V1_TO_V2,\n NETWORK_V2_TO_V1,\n \n // Network detection\n isSolanaNetwork,\n isEvmNetwork,\n \n // Amount utilities\n toAtomicUnits,\n fromAtomicUnits,\n formatUsd,\n \n // Types\n type X402Version,\n type V1PaymentPayload,\n type V2PaymentPayload,\n type PaymentPayload,\n} from './payload-converter';\n","/**\n * Utility to convert x402 payment payloads between v1 and v2 formats.\n * \n * V1 PaymentPayload format:\n * {\n * \"x402Version\": 1,\n * \"scheme\": \"exact\",\n * \"network\": \"solana\",\n * \"payload\": { ... }\n * }\n * \n * V2 PaymentPayload format:\n * {\n * \"x402Version\": 2,\n * \"resource\": { \"url\": \"...\", \"description\": \"...\", \"mimeType\": \"...\" },\n * \"accepted\": { \"scheme\": \"exact\", \"network\": \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\", ... },\n * \"payload\": { ... }\n * }\n */\n\n// Network name mappings between v1 (simple names) and v2 (CAIP-2)\nexport const NETWORK_V1_TO_V2: Record<string, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',\n 'base': 'eip155:8453',\n 'base-sepolia': 'eip155:84532',\n 'ethereum': 'eip155:1',\n 'polygon': 'eip155:137',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'skale-base-sepolia': 'eip155:324705682',\n 'peaq': 'eip155:3338',\n 'sei': 'eip155:1329',\n};\n\nexport const NETWORK_V2_TO_V1: Record<string, string> = Object.fromEntries(\n Object.entries(NETWORK_V1_TO_V2).map(([k, v]) => [v, k])\n);\n\n/**\n * Convert CAIP-2 network identifier to simple v1 network name\n */\nexport function networkV2ToV1(caip2Network: string): string {\n if (!caip2Network) return 'solana';\n \n // Direct lookup\n if (NETWORK_V2_TO_V1[caip2Network]) {\n return NETWORK_V2_TO_V1[caip2Network];\n }\n \n // Handle partial matches for Solana\n if (caip2Network.startsWith('solana:')) {\n const chainId = caip2Network.split(':')[1];\n if (chainId === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') return 'solana';\n if (chainId === 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1') return 'solana-devnet';\n return 'solana';\n }\n \n // Handle EVM chains by chain ID\n if (caip2Network.startsWith('eip155:')) {\n const chainId = caip2Network.split(':')[1];\n const mapping: Record<string, string> = {\n '1': 'ethereum',\n '137': 'polygon',\n '8453': 'base',\n '84532': 'base-sepolia',\n '43114': 'avalanche',\n '1187947933': 'skale-base',\n '324705682': 'skale-base-sepolia',\n '3338': 'peaq',\n '1329': 'sei',\n };\n return mapping[chainId] || caip2Network;\n }\n \n return caip2Network;\n}\n\n/**\n * Convert simple v1 network name to CAIP-2 identifier\n */\nexport function networkV1ToV2(v1Network: string): string {\n if (!v1Network) return 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n return NETWORK_V1_TO_V2[v1Network] || v1Network;\n}\n\nexport type X402Version = 1 | 2;\n\nexport interface V1PaymentPayload {\n x402Version: 1;\n scheme: string;\n network: string;\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport interface V2PaymentPayload {\n x402Version: 2;\n resource: {\n url: string;\n description?: string;\n mimeType?: string;\n };\n accepted: {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n };\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport type PaymentPayload = V1PaymentPayload | V2PaymentPayload;\n\n/**\n * Detect x402 version from payload\n */\nexport function detectPayloadVersion(payload: unknown): X402Version | null {\n if (!payload || typeof payload !== 'object') return null;\n \n const p = payload as Record<string, unknown>;\n \n if (p.x402Version === 1) return 1;\n if (p.x402Version === 2) return 2;\n \n // Heuristics if x402Version is missing\n if ('accepted' in p && 'resource' in p) return 2;\n if ('scheme' in p && 'network' in p && !('accepted' in p)) return 1;\n \n return null;\n}\n\n/**\n * Convert v2 payment payload to v1 format\n */\nexport function convertV2ToV1(v2Payload: V2PaymentPayload): V1PaymentPayload {\n const accepted = v2Payload.accepted || {};\n \n return {\n x402Version: 1,\n scheme: accepted.scheme || 'exact',\n network: networkV2ToV1(accepted.network),\n payload: v2Payload.payload,\n };\n}\n\n/**\n * Convert v1 payment payload to v2 format\n */\nexport function convertV1ToV2(\n v1Payload: V1PaymentPayload,\n resourceInfo: { url?: string; description?: string; mimeType?: string } = {}\n): V2PaymentPayload {\n return {\n x402Version: 2,\n resource: {\n url: resourceInfo.url || '',\n description: resourceInfo.description || '',\n mimeType: resourceInfo.mimeType || 'application/json',\n },\n accepted: {\n scheme: v1Payload.scheme || 'exact',\n network: networkV1ToV2(v1Payload.network),\n },\n payload: v1Payload.payload,\n };\n}\n\n/**\n * Convert payment payload to target version\n */\nexport function convertPayloadToVersion(\n payload: PaymentPayload,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): PaymentPayload | null {\n const sourceVersion = detectPayloadVersion(payload);\n \n if (!sourceVersion) {\n console.warn('[payload-converter] Could not detect source payload version');\n return null;\n }\n \n // No conversion needed\n if (sourceVersion === targetVersion) {\n return payload;\n }\n \n // V2 -> V1\n if (sourceVersion === 2 && targetVersion === 1) {\n return convertV2ToV1(payload as V2PaymentPayload);\n }\n \n // V1 -> V2\n if (sourceVersion === 1 && targetVersion === 2) {\n return convertV1ToV2(payload as V1PaymentPayload, options.resourceInfo);\n }\n \n return null;\n}\n\n/**\n * Normalize payment header for target x402 version\n */\nexport function normalizePaymentHeader(\n base64Header: string,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): { header: string; payload: PaymentPayload } | null {\n if (!base64Header) return null;\n \n try {\n const decoded = JSON.parse(\n typeof window !== 'undefined' \n ? atob(base64Header) \n : Buffer.from(base64Header, 'base64').toString()\n );\n const converted = convertPayloadToVersion(decoded, targetVersion, options);\n \n if (!converted) {\n return null;\n }\n \n const encoded = typeof window !== 'undefined'\n ? btoa(JSON.stringify(converted))\n : Buffer.from(JSON.stringify(converted)).toString('base64');\n \n return {\n header: encoded,\n payload: converted,\n };\n } catch (e) {\n console.error('[payload-converter] Failed to normalize header:', e);\n return null;\n }\n}\n\n/**\n * Check if network is Solana-based\n */\nexport function isSolanaNetwork(network: string): boolean {\n return network === 'solana' || \n network === 'solana-devnet' || \n network.startsWith('solana:');\n}\n\n/**\n * Check if network is EVM-based\n */\nexport function isEvmNetwork(network: string): boolean {\n const evmNetworks = ['base', 'base-sepolia', 'ethereum', 'polygon', 'avalanche', 'skale-base', 'skale-base-sepolia', 'skale-bite', 'peaq', 'sei'];\n return evmNetworks.includes(network) || network.startsWith('eip155:');\n}\n\n// ============================================================================\n// Amount Conversion Utilities\n// ============================================================================\n\n/**\n * Convert USD amount to atomic units\n * @param usd - Amount in USD (e.g., 0.05)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns Atomic units as string\n */\nexport function toAtomicUnits(usd: number, decimals: number = 6): string {\n return Math.floor(usd * Math.pow(10, decimals)).toString();\n}\n\n/**\n * Convert atomic units to USD amount\n * @param atomic - Atomic units (string or bigint)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns USD amount as number\n */\nexport function fromAtomicUnits(atomic: string | bigint, decimals: number = 6): number {\n const value = typeof atomic === 'bigint' ? atomic : BigInt(atomic);\n return Number(value) / Math.pow(10, decimals);\n}\n\n/**\n * Format USD amount for display\n * @param usd - Amount in USD\n * @param maxDecimals - Maximum decimal places (default: 4)\n * @returns Formatted string\n */\nexport function formatUsd(usd: number, maxDecimals: number = 4): string {\n if (usd < 0.0001) return '<$0.0001';\n return `$${usd.toFixed(maxDecimals).replace(/\\.?0+$/, '')}`;\n}\n"],"mappings":"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","names":[]}
|
package/dist/utils/index.js
CHANGED
|
@@ -8,6 +8,7 @@ var NETWORK_V1_TO_V2 = {
|
|
|
8
8
|
"polygon": "eip155:137",
|
|
9
9
|
"avalanche": "eip155:43114",
|
|
10
10
|
"skale-base": "eip155:1187947933",
|
|
11
|
+
"skale-base-sepolia": "eip155:324705682",
|
|
11
12
|
"peaq": "eip155:3338",
|
|
12
13
|
"sei": "eip155:1329"
|
|
13
14
|
};
|
|
@@ -34,6 +35,7 @@ function networkV2ToV1(caip2Network) {
|
|
|
34
35
|
"84532": "base-sepolia",
|
|
35
36
|
"43114": "avalanche",
|
|
36
37
|
"1187947933": "skale-base",
|
|
38
|
+
"324705682": "skale-base-sepolia",
|
|
37
39
|
"3338": "peaq",
|
|
38
40
|
"1329": "sei"
|
|
39
41
|
};
|
|
@@ -119,7 +121,7 @@ function isSolanaNetwork(network) {
|
|
|
119
121
|
return network === "solana" || network === "solana-devnet" || network.startsWith("solana:");
|
|
120
122
|
}
|
|
121
123
|
function isEvmNetwork(network) {
|
|
122
|
-
const evmNetworks = ["base", "base-sepolia", "ethereum", "polygon", "avalanche", "skale-base", "skale-bite", "peaq", "sei"];
|
|
124
|
+
const evmNetworks = ["base", "base-sepolia", "ethereum", "polygon", "avalanche", "skale-base", "skale-base-sepolia", "skale-bite", "peaq", "sei"];
|
|
123
125
|
return evmNetworks.includes(network) || network.startsWith("eip155:");
|
|
124
126
|
}
|
|
125
127
|
function toAtomicUnits(usd, decimals = 6) {
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/payload-converter.ts"],"sourcesContent":["/**\n * Utility to convert x402 payment payloads between v1 and v2 formats.\n * \n * V1 PaymentPayload format:\n * {\n * \"x402Version\": 1,\n * \"scheme\": \"exact\",\n * \"network\": \"solana\",\n * \"payload\": { ... }\n * }\n * \n * V2 PaymentPayload format:\n * {\n * \"x402Version\": 2,\n * \"resource\": { \"url\": \"...\", \"description\": \"...\", \"mimeType\": \"...\" },\n * \"accepted\": { \"scheme\": \"exact\", \"network\": \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\", ... },\n * \"payload\": { ... }\n * }\n */\n\n// Network name mappings between v1 (simple names) and v2 (CAIP-2)\nexport const NETWORK_V1_TO_V2: Record<string, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',\n 'base': 'eip155:8453',\n 'base-sepolia': 'eip155:84532',\n 'ethereum': 'eip155:1',\n 'polygon': 'eip155:137',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'peaq': 'eip155:3338',\n 'sei': 'eip155:1329',\n};\n\nexport const NETWORK_V2_TO_V1: Record<string, string> = Object.fromEntries(\n Object.entries(NETWORK_V1_TO_V2).map(([k, v]) => [v, k])\n);\n\n/**\n * Convert CAIP-2 network identifier to simple v1 network name\n */\nexport function networkV2ToV1(caip2Network: string): string {\n if (!caip2Network) return 'solana';\n \n // Direct lookup\n if (NETWORK_V2_TO_V1[caip2Network]) {\n return NETWORK_V2_TO_V1[caip2Network];\n }\n \n // Handle partial matches for Solana\n if (caip2Network.startsWith('solana:')) {\n const chainId = caip2Network.split(':')[1];\n if (chainId === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') return 'solana';\n if (chainId === 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1') return 'solana-devnet';\n return 'solana';\n }\n \n // Handle EVM chains by chain ID\n if (caip2Network.startsWith('eip155:')) {\n const chainId = caip2Network.split(':')[1];\n const mapping: Record<string, string> = {\n '1': 'ethereum',\n '137': 'polygon',\n '8453': 'base',\n '84532': 'base-sepolia',\n '43114': 'avalanche',\n '1187947933': 'skale-base',\n '3338': 'peaq',\n '1329': 'sei',\n };\n return mapping[chainId] || caip2Network;\n }\n \n return caip2Network;\n}\n\n/**\n * Convert simple v1 network name to CAIP-2 identifier\n */\nexport function networkV1ToV2(v1Network: string): string {\n if (!v1Network) return 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n return NETWORK_V1_TO_V2[v1Network] || v1Network;\n}\n\nexport type X402Version = 1 | 2;\n\nexport interface V1PaymentPayload {\n x402Version: 1;\n scheme: string;\n network: string;\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport interface V2PaymentPayload {\n x402Version: 2;\n resource: {\n url: string;\n description?: string;\n mimeType?: string;\n };\n accepted: {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n };\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport type PaymentPayload = V1PaymentPayload | V2PaymentPayload;\n\n/**\n * Detect x402 version from payload\n */\nexport function detectPayloadVersion(payload: unknown): X402Version | null {\n if (!payload || typeof payload !== 'object') return null;\n \n const p = payload as Record<string, unknown>;\n \n if (p.x402Version === 1) return 1;\n if (p.x402Version === 2) return 2;\n \n // Heuristics if x402Version is missing\n if ('accepted' in p && 'resource' in p) return 2;\n if ('scheme' in p && 'network' in p && !('accepted' in p)) return 1;\n \n return null;\n}\n\n/**\n * Convert v2 payment payload to v1 format\n */\nexport function convertV2ToV1(v2Payload: V2PaymentPayload): V1PaymentPayload {\n const accepted = v2Payload.accepted || {};\n \n return {\n x402Version: 1,\n scheme: accepted.scheme || 'exact',\n network: networkV2ToV1(accepted.network),\n payload: v2Payload.payload,\n };\n}\n\n/**\n * Convert v1 payment payload to v2 format\n */\nexport function convertV1ToV2(\n v1Payload: V1PaymentPayload,\n resourceInfo: { url?: string; description?: string; mimeType?: string } = {}\n): V2PaymentPayload {\n return {\n x402Version: 2,\n resource: {\n url: resourceInfo.url || '',\n description: resourceInfo.description || '',\n mimeType: resourceInfo.mimeType || 'application/json',\n },\n accepted: {\n scheme: v1Payload.scheme || 'exact',\n network: networkV1ToV2(v1Payload.network),\n },\n payload: v1Payload.payload,\n };\n}\n\n/**\n * Convert payment payload to target version\n */\nexport function convertPayloadToVersion(\n payload: PaymentPayload,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): PaymentPayload | null {\n const sourceVersion = detectPayloadVersion(payload);\n \n if (!sourceVersion) {\n console.warn('[payload-converter] Could not detect source payload version');\n return null;\n }\n \n // No conversion needed\n if (sourceVersion === targetVersion) {\n return payload;\n }\n \n // V2 -> V1\n if (sourceVersion === 2 && targetVersion === 1) {\n return convertV2ToV1(payload as V2PaymentPayload);\n }\n \n // V1 -> V2\n if (sourceVersion === 1 && targetVersion === 2) {\n return convertV1ToV2(payload as V1PaymentPayload, options.resourceInfo);\n }\n \n return null;\n}\n\n/**\n * Normalize payment header for target x402 version\n */\nexport function normalizePaymentHeader(\n base64Header: string,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): { header: string; payload: PaymentPayload } | null {\n if (!base64Header) return null;\n \n try {\n const decoded = JSON.parse(\n typeof window !== 'undefined' \n ? atob(base64Header) \n : Buffer.from(base64Header, 'base64').toString()\n );\n const converted = convertPayloadToVersion(decoded, targetVersion, options);\n \n if (!converted) {\n return null;\n }\n \n const encoded = typeof window !== 'undefined'\n ? btoa(JSON.stringify(converted))\n : Buffer.from(JSON.stringify(converted)).toString('base64');\n \n return {\n header: encoded,\n payload: converted,\n };\n } catch (e) {\n console.error('[payload-converter] Failed to normalize header:', e);\n return null;\n }\n}\n\n/**\n * Check if network is Solana-based\n */\nexport function isSolanaNetwork(network: string): boolean {\n return network === 'solana' || \n network === 'solana-devnet' || \n network.startsWith('solana:');\n}\n\n/**\n * Check if network is EVM-based\n */\nexport function isEvmNetwork(network: string): boolean {\n const evmNetworks = ['base', 'base-sepolia', 'ethereum', 'polygon', 'avalanche', 'skale-base', 'skale-bite', 'peaq', 'sei'];\n return evmNetworks.includes(network) || network.startsWith('eip155:');\n}\n\n// ============================================================================\n// Amount Conversion Utilities\n// ============================================================================\n\n/**\n * Convert USD amount to atomic units\n * @param usd - Amount in USD (e.g., 0.05)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns Atomic units as string\n */\nexport function toAtomicUnits(usd: number, decimals: number = 6): string {\n return Math.floor(usd * Math.pow(10, decimals)).toString();\n}\n\n/**\n * Convert atomic units to USD amount\n * @param atomic - Atomic units (string or bigint)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns USD amount as number\n */\nexport function fromAtomicUnits(atomic: string | bigint, decimals: number = 6): number {\n const value = typeof atomic === 'bigint' ? atomic : BigInt(atomic);\n return Number(value) / Math.pow(10, decimals);\n}\n\n/**\n * Format USD amount for display\n * @param usd - Amount in USD\n * @param maxDecimals - Maximum decimal places (default: 4)\n * @returns Formatted string\n */\nexport function formatUsd(usd: number, maxDecimals: number = 4): string {\n if (usd < 0.0001) return '<$0.0001';\n return `$${usd.toFixed(maxDecimals).replace(/\\.?0+$/, '')}`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/payload-converter.ts"],"sourcesContent":["/**\n * Utility to convert x402 payment payloads between v1 and v2 formats.\n * \n * V1 PaymentPayload format:\n * {\n * \"x402Version\": 1,\n * \"scheme\": \"exact\",\n * \"network\": \"solana\",\n * \"payload\": { ... }\n * }\n * \n * V2 PaymentPayload format:\n * {\n * \"x402Version\": 2,\n * \"resource\": { \"url\": \"...\", \"description\": \"...\", \"mimeType\": \"...\" },\n * \"accepted\": { \"scheme\": \"exact\", \"network\": \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\", ... },\n * \"payload\": { ... }\n * }\n */\n\n// Network name mappings between v1 (simple names) and v2 (CAIP-2)\nexport const NETWORK_V1_TO_V2: Record<string, string> = {\n 'solana': 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n 'solana-devnet': 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',\n 'base': 'eip155:8453',\n 'base-sepolia': 'eip155:84532',\n 'ethereum': 'eip155:1',\n 'polygon': 'eip155:137',\n 'avalanche': 'eip155:43114',\n 'skale-base': 'eip155:1187947933',\n 'skale-base-sepolia': 'eip155:324705682',\n 'peaq': 'eip155:3338',\n 'sei': 'eip155:1329',\n};\n\nexport const NETWORK_V2_TO_V1: Record<string, string> = Object.fromEntries(\n Object.entries(NETWORK_V1_TO_V2).map(([k, v]) => [v, k])\n);\n\n/**\n * Convert CAIP-2 network identifier to simple v1 network name\n */\nexport function networkV2ToV1(caip2Network: string): string {\n if (!caip2Network) return 'solana';\n \n // Direct lookup\n if (NETWORK_V2_TO_V1[caip2Network]) {\n return NETWORK_V2_TO_V1[caip2Network];\n }\n \n // Handle partial matches for Solana\n if (caip2Network.startsWith('solana:')) {\n const chainId = caip2Network.split(':')[1];\n if (chainId === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') return 'solana';\n if (chainId === 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1') return 'solana-devnet';\n return 'solana';\n }\n \n // Handle EVM chains by chain ID\n if (caip2Network.startsWith('eip155:')) {\n const chainId = caip2Network.split(':')[1];\n const mapping: Record<string, string> = {\n '1': 'ethereum',\n '137': 'polygon',\n '8453': 'base',\n '84532': 'base-sepolia',\n '43114': 'avalanche',\n '1187947933': 'skale-base',\n '324705682': 'skale-base-sepolia',\n '3338': 'peaq',\n '1329': 'sei',\n };\n return mapping[chainId] || caip2Network;\n }\n \n return caip2Network;\n}\n\n/**\n * Convert simple v1 network name to CAIP-2 identifier\n */\nexport function networkV1ToV2(v1Network: string): string {\n if (!v1Network) return 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n return NETWORK_V1_TO_V2[v1Network] || v1Network;\n}\n\nexport type X402Version = 1 | 2;\n\nexport interface V1PaymentPayload {\n x402Version: 1;\n scheme: string;\n network: string;\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport interface V2PaymentPayload {\n x402Version: 2;\n resource: {\n url: string;\n description?: string;\n mimeType?: string;\n };\n accepted: {\n scheme: string;\n network: string;\n amount?: string;\n maxAmountRequired?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n };\n payload: {\n transaction?: string;\n signature?: string;\n authorization?: unknown;\n };\n facilitatorUrl?: string;\n}\n\nexport type PaymentPayload = V1PaymentPayload | V2PaymentPayload;\n\n/**\n * Detect x402 version from payload\n */\nexport function detectPayloadVersion(payload: unknown): X402Version | null {\n if (!payload || typeof payload !== 'object') return null;\n \n const p = payload as Record<string, unknown>;\n \n if (p.x402Version === 1) return 1;\n if (p.x402Version === 2) return 2;\n \n // Heuristics if x402Version is missing\n if ('accepted' in p && 'resource' in p) return 2;\n if ('scheme' in p && 'network' in p && !('accepted' in p)) return 1;\n \n return null;\n}\n\n/**\n * Convert v2 payment payload to v1 format\n */\nexport function convertV2ToV1(v2Payload: V2PaymentPayload): V1PaymentPayload {\n const accepted = v2Payload.accepted || {};\n \n return {\n x402Version: 1,\n scheme: accepted.scheme || 'exact',\n network: networkV2ToV1(accepted.network),\n payload: v2Payload.payload,\n };\n}\n\n/**\n * Convert v1 payment payload to v2 format\n */\nexport function convertV1ToV2(\n v1Payload: V1PaymentPayload,\n resourceInfo: { url?: string; description?: string; mimeType?: string } = {}\n): V2PaymentPayload {\n return {\n x402Version: 2,\n resource: {\n url: resourceInfo.url || '',\n description: resourceInfo.description || '',\n mimeType: resourceInfo.mimeType || 'application/json',\n },\n accepted: {\n scheme: v1Payload.scheme || 'exact',\n network: networkV1ToV2(v1Payload.network),\n },\n payload: v1Payload.payload,\n };\n}\n\n/**\n * Convert payment payload to target version\n */\nexport function convertPayloadToVersion(\n payload: PaymentPayload,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): PaymentPayload | null {\n const sourceVersion = detectPayloadVersion(payload);\n \n if (!sourceVersion) {\n console.warn('[payload-converter] Could not detect source payload version');\n return null;\n }\n \n // No conversion needed\n if (sourceVersion === targetVersion) {\n return payload;\n }\n \n // V2 -> V1\n if (sourceVersion === 2 && targetVersion === 1) {\n return convertV2ToV1(payload as V2PaymentPayload);\n }\n \n // V1 -> V2\n if (sourceVersion === 1 && targetVersion === 2) {\n return convertV1ToV2(payload as V1PaymentPayload, options.resourceInfo);\n }\n \n return null;\n}\n\n/**\n * Normalize payment header for target x402 version\n */\nexport function normalizePaymentHeader(\n base64Header: string,\n targetVersion: X402Version,\n options: { resourceInfo?: { url?: string; description?: string; mimeType?: string } } = {}\n): { header: string; payload: PaymentPayload } | null {\n if (!base64Header) return null;\n \n try {\n const decoded = JSON.parse(\n typeof window !== 'undefined' \n ? atob(base64Header) \n : Buffer.from(base64Header, 'base64').toString()\n );\n const converted = convertPayloadToVersion(decoded, targetVersion, options);\n \n if (!converted) {\n return null;\n }\n \n const encoded = typeof window !== 'undefined'\n ? btoa(JSON.stringify(converted))\n : Buffer.from(JSON.stringify(converted)).toString('base64');\n \n return {\n header: encoded,\n payload: converted,\n };\n } catch (e) {\n console.error('[payload-converter] Failed to normalize header:', e);\n return null;\n }\n}\n\n/**\n * Check if network is Solana-based\n */\nexport function isSolanaNetwork(network: string): boolean {\n return network === 'solana' || \n network === 'solana-devnet' || \n network.startsWith('solana:');\n}\n\n/**\n * Check if network is EVM-based\n */\nexport function isEvmNetwork(network: string): boolean {\n const evmNetworks = ['base', 'base-sepolia', 'ethereum', 'polygon', 'avalanche', 'skale-base', 'skale-base-sepolia', 'skale-bite', 'peaq', 'sei'];\n return evmNetworks.includes(network) || network.startsWith('eip155:');\n}\n\n// ============================================================================\n// Amount Conversion Utilities\n// ============================================================================\n\n/**\n * Convert USD amount to atomic units\n * @param usd - Amount in USD (e.g., 0.05)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns Atomic units as string\n */\nexport function toAtomicUnits(usd: number, decimals: number = 6): string {\n return Math.floor(usd * Math.pow(10, decimals)).toString();\n}\n\n/**\n * Convert atomic units to USD amount\n * @param atomic - Atomic units (string or bigint)\n * @param decimals - Token decimals (default: 6 for USDC)\n * @returns USD amount as number\n */\nexport function fromAtomicUnits(atomic: string | bigint, decimals: number = 6): number {\n const value = typeof atomic === 'bigint' ? atomic : BigInt(atomic);\n return Number(value) / Math.pow(10, decimals);\n}\n\n/**\n * Format USD amount for display\n * @param usd - Amount in USD\n * @param maxDecimals - Maximum decimal places (default: 4)\n * @returns Formatted string\n */\nexport function formatUsd(usd: number, maxDecimals: number = 4): string {\n if (usd < 0.0001) return '<$0.0001';\n return `$${usd.toFixed(maxDecimals).replace(/\\.?0+$/, '')}`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"next-dev","duration":104358256850,"timestamp":2292636581280,"id":1,"tags":{},"startTime":1771321531578,"traceId":"dc5d1d82c5be2e16"}]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relai-fi/x402",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"description": "Unified x402 payment SDK for Solana, Base, Avalanche, SKALE Base, SKALE BITE, Polygon, and Ethereum. Automatic 402 handling with zero gas fees.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|