moltspay 1.4.1 → 1.6.0
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 +187 -0
- package/dist/cli/index.js +486 -152
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +483 -149
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +245 -116
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +241 -114
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/web/index.d.mts +418 -0
- package/dist/client/web/index.mjs +1289 -0
- package/dist/client/web/index.mjs.map +1 -0
- package/dist/facilitators/index.d.mts +24 -2
- package/dist/facilitators/index.d.ts +24 -2
- package/dist/facilitators/index.js +127 -13
- package/dist/facilitators/index.js.map +1 -1
- package/dist/facilitators/index.mjs +127 -13
- package/dist/facilitators/index.mjs.map +1 -1
- package/dist/index.js +463 -149
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +460 -146
- package/dist/index.mjs.map +1 -1
- package/dist/mcp/index.d.mts +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +1623 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/index.mjs +1617 -0
- package/dist/mcp/index.mjs.map +1 -0
- package/dist/server/index.d.mts +43 -1
- package/dist/server/index.d.ts +43 -1
- package/dist/server/index.js +205 -18
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +205 -18
- package/dist/server/index.mjs.map +1 -1
- package/package.json +19 -4
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts","../../src/chains/index.ts","../../src/wallet/solana.ts","../../src/chains/solana.ts","../../src/facilitators/solana.ts"],"sourcesContent":["/**\n * MoltsPay Client - Pay for AI Agent services\n * \n * Uses x402 protocol for gasless, pay-for-success payments.\n * \n * Usage:\n * const client = new MoltsPayClient(); // Loads from ~/.moltspay/\n * const services = await client.getServices('http://provider:3000');\n * const result = await client.pay('http://provider:3000', 'text-to-video', { prompt: '...' });\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, chmodSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Wallet, ethers } from 'ethers';\nimport { getChain, type ChainName, type EvmChainName, type TokenSymbol, type ChainConfig } from '../chains/index.js';\nimport { SOLANA_CHAINS, type SolanaChainName } from '../chains/solana.js';\nimport { loadSolanaWallet, getSolanaAddress } from '../wallet/solana.js';\nimport { createSolanaPaymentTransaction } from '../facilitators/solana.js';\nimport { Connection, PublicKey, Keypair } from '@solana/web3.js';\nimport {\n ClientConfig,\n WalletData,\n ServicesResponse,\n MoltsPayClientOptions,\n} from './types.js';\n\nexport * from './types.js';\n\nexport interface PayOptions {\n /** Token to pay with (default: USDC, or auto-select based on balance) */\n token?: TokenSymbol;\n /** Auto-select token based on balance (default: false) */\n autoSelect?: boolean;\n /** Chain to pay on */\n chain?: 'base' | 'polygon' | 'base_sepolia' | 'tempo_moderato' | 'bnb' | 'bnb_testnet' | 'solana' | 'solana_devnet';\n /** Send raw data at top level instead of wrapped in { params } */\n rawData?: boolean;\n}\n\n// x402 constants\nconst X402_VERSION = 2;\nconst PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nconst PAYMENT_HEADER = 'x-payment';\n\ninterface X402PaymentRequirements {\n scheme: string;\n network: string;\n // v2 fields\n amount?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n // v1 fields (legacy)\n maxAmountRequired?: string;\n resource?: string;\n description?: string;\n}\n\ninterface EIP3009Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\nconst DEFAULT_CONFIG: ClientConfig = {\n chain: 'base',\n limits: {\n maxPerTx: 100,\n maxPerDay: 1000,\n },\n};\n\nexport class MoltsPayClient {\n private configDir: string;\n private config: ClientConfig;\n private walletData: WalletData | null = null;\n private wallet: Wallet | null = null;\n private todaySpending: number = 0;\n private lastSpendingReset: number = 0;\n\n constructor(options: MoltsPayClientOptions = {}) {\n this.configDir = options.configDir || join(homedir(), '.moltspay');\n this.config = this.loadConfig();\n this.walletData = this.loadWallet();\n this.loadSpending(); // Load persisted spending data\n \n if (this.walletData) {\n this.wallet = new Wallet(this.walletData.privateKey);\n }\n }\n\n /**\n * Check if client is initialized (has wallet)\n */\n get isInitialized(): boolean {\n return this.wallet !== null;\n }\n\n /**\n * Get wallet address\n */\n get address(): string | null {\n return this.wallet?.address || null;\n }\n\n /**\n * Get wallet instance (for direct operations like approvals)\n */\n getWallet(): Wallet | null {\n return this.wallet;\n }\n\n /**\n * Get current config\n */\n getConfig(): ClientConfig {\n return { ...this.config };\n }\n\n /**\n * Update config\n */\n updateConfig(updates: Partial<ClientConfig['limits']>): void {\n if (updates.maxPerTx !== undefined) {\n this.config.limits.maxPerTx = updates.maxPerTx;\n }\n if (updates.maxPerDay !== undefined) {\n this.config.limits.maxPerDay = updates.maxPerDay;\n }\n this.saveConfig();\n }\n\n /**\n * Get services from a provider\n */\n async getServices(serverUrl: string): Promise<ServicesResponse> {\n // Normalize URL - don't append /services if already present\n const normalizedUrl = serverUrl.replace(/\\/(services|api\\/services|registry\\/services)\\/?$/, '');\n \n // Try /services first (standard provider endpoint)\n const endpoints = ['/services', '/api/services', '/registry/services'];\n \n for (const endpoint of endpoints) {\n try {\n const res = await fetch(`${normalizedUrl}${endpoint}`);\n if (!res.ok) continue;\n \n const contentType = res.headers.get('content-type') || '';\n if (!contentType.includes('application/json')) continue;\n \n return await res.json() as ServicesResponse;\n } catch {\n continue;\n }\n }\n \n throw new Error(`Failed to get services: no valid endpoint found at ${normalizedUrl}`);\n }\n\n /**\n * Pay for a service and get the result (x402 protocol)\n * \n * This is GASLESS for the client - server pays gas to claim payment.\n * This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.\n * \n * @param serverUrl - Server URL\n * @param service - Service ID\n * @param params - Service parameters\n * @param options - Payment options (token selection)\n */\n async pay(\n serverUrl: string,\n service: string,\n params: Record<string, any>,\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n if (!this.wallet || !this.walletData) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Step 1: Discover service endpoint\n console.log(`[MoltsPay] Requesting service: ${service}`);\n let executeUrl = `${serverUrl}/execute`; // Default fallback\n \n try {\n const services = await this.getServices(serverUrl);\n const svc = services.services?.find((s: any) => s.id === service);\n if (svc?.endpoint) {\n // Use the endpoint from service discovery (for Cloudflare Workers, etc.)\n executeUrl = `${serverUrl}${svc.endpoint}`;\n console.log(`[MoltsPay] Using service endpoint: ${svc.endpoint}`);\n }\n } catch {\n // Fall back to /execute if service discovery fails\n }\n \n // Build request body - raw mode sends data at top level, standard mode wraps in { params }\n let requestBody: any;\n if (options.rawData) {\n // Raw mode: { service, chain, ...params } - user's data at top level\n requestBody = { service, ...params };\n } else {\n // Standard mode: { service, params } - wrapped format\n requestBody = { service, params };\n }\n if (options.chain) {\n requestBody.chain = options.chain;\n }\n const initialRes = await fetch(executeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n });\n\n // If not 402, check for success or error\n if (initialRes.status !== 402) {\n const data = await initialRes.json() as any;\n if (initialRes.ok && data.result) {\n return data.result;\n }\n throw new Error(data.error || 'Unexpected response');\n }\n\n // Step 2: Detect protocol from 402 response\n // MPP uses WWW-Authenticate header, x402 uses X-Payment-Required header\n const wwwAuthHeader = initialRes.headers.get('www-authenticate');\n const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);\n \n // If WWW-Authenticate with Payment scheme, use MPP flow\n if (wwwAuthHeader && wwwAuthHeader.toLowerCase().includes('payment')) {\n console.log('[MoltsPay] Detected MPP protocol, using Tempo flow...');\n return await this.handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options);\n }\n \n if (!paymentRequiredHeader) {\n throw new Error('Missing payment header (x-payment-required or www-authenticate)');\n }\n\n let requirements: X402PaymentRequirements[];\n try {\n const decoded = Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8');\n const parsed = JSON.parse(decoded);\n \n // Handle both v1 (array) and v2 (object with accepts) formats\n if (Array.isArray(parsed)) {\n // v1 format: direct array of requirements\n requirements = parsed;\n } else if (parsed.accepts && Array.isArray(parsed.accepts)) {\n // v2 format: { x402Version: 2, accepts: [...] }\n requirements = parsed.accepts;\n } else {\n // Single requirement object\n requirements = [parsed];\n }\n } catch {\n throw new Error('Invalid x-payment-required header');\n }\n\n // Helper to convert network ID to chain name\n const networkToChainName = (network: string): string | null => {\n // Handle Solana networks\n if (network === 'solana:mainnet') return 'solana';\n if (network === 'solana:devnet') return 'solana_devnet';\n \n // Handle EVM networks\n const match = network.match(/^eip155:(\\d+)$/);\n if (!match) return null;\n const chainId = parseInt(match[1]);\n if (chainId === 8453) return 'base';\n if (chainId === 137) return 'polygon';\n if (chainId === 84532) return 'base_sepolia';\n if (chainId === 42431) return 'tempo_moderato';\n if (chainId === 56) return 'bnb';\n if (chainId === 97) return 'bnb_testnet';\n return null;\n };\n\n // Get server's accepted chains\n const serverChains = requirements\n .map(r => networkToChainName(r.network))\n .filter((c): c is string => c !== null);\n\n // Determine which chain to use\n const userSpecifiedChain = options.chain;\n let selectedChain: string;\n\n if (userSpecifiedChain) {\n // User specified --chain, validate it's accepted by server\n if (!serverChains.includes(userSpecifiedChain)) {\n throw new Error(\n `Server doesn't accept '${userSpecifiedChain}'.\\n` +\n `Server accepts: ${serverChains.join(', ')}`\n );\n }\n selectedChain = userSpecifiedChain;\n } else {\n // No --chain provided\n if (serverChains.length === 1 && serverChains[0] === 'base') {\n // Only default to base if server ONLY accepts base\n selectedChain = 'base';\n } else {\n throw new Error(\n `Server accepts: ${serverChains.join(', ')}\\n` +\n `Please specify: --chain <chain_name>`\n );\n }\n }\n\n // Handle Solana chains separately\n if (selectedChain === 'solana' || selectedChain === 'solana_devnet') {\n const solanaChain = selectedChain as SolanaChainName;\n const network = solanaChain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n const req = requirements.find(r => r.network === network);\n \n if (!req) {\n throw new Error(`Failed to find payment requirement for ${selectedChain}`);\n }\n \n return await this.handleSolanaPayment(executeUrl, service, params, req, solanaChain, options);\n }\n\n // EVM chain handling\n const chainName = selectedChain as EvmChainName;\n const chain = getChain(chainName);\n const network = `eip155:${chain.chainId}`;\n const req = requirements.find(r => r.scheme === 'exact' && r.network === network);\n\n if (!req) {\n throw new Error(`Failed to find payment requirement for ${chainName}`);\n }\n\n // Step 3: Check limits\n // v2 uses 'amount', v1 uses 'maxAmountRequired'\n const amountRaw = req.amount || req.maxAmountRequired;\n if (!amountRaw) {\n throw new Error('Missing amount in payment requirements');\n }\n const amount = Number(amountRaw) / 1e6;\n this.checkLimits(amount);\n\n // Determine which token to use\n let token: TokenSymbol = options.token || 'USDC';\n \n // Auto-select token based on balance if requested\n if (options.autoSelect) {\n const balances = await this.getBalance();\n if (balances.usdc >= amount) {\n token = 'USDC';\n } else if (balances.usdt >= amount) {\n token = 'USDT';\n } else {\n throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);\n }\n }\n\n // USDT does not support gasless transfers (no EIP-2612 permit)\n // It requires on-chain approve + transfer, meaning the user pays gas\n if (token === 'USDT') {\n const balances = await this.getBalance();\n if (balances.native < 0.0001) {\n throw new Error(\n `USDT requires ETH for gas (~$0.01 on Base). ` +\n `Your ETH balance: ${balances.native.toFixed(6)} ETH. ` +\n `Please add a small amount of ETH to your wallet, or use USDC (gasless).`\n );\n }\n console.log(`[MoltsPay] ⚠️ USDT requires gas (~$0.01). Proceeding with payment...`);\n } else {\n console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);\n }\n\n // BNB chains use intent-based flow (pre-approval + intent signature)\n if (chainName === 'bnb' || chainName === 'bnb_testnet') {\n console.log(`[MoltsPay] Using BNB intent-based payment flow...`);\n const payTo = req.payTo || req.resource;\n if (!payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n // Get spender address from server response (dynamic, not hardcoded)\n const bnbSpender = (req.extra as any)?.bnbSpender;\n if (!bnbSpender) {\n throw new Error('Server did not provide bnbSpender address. Server may not support BNB payments.');\n }\n return await this.handleBNBPayment(executeUrl, service, params, {\n to: payTo,\n amount,\n token,\n chainName,\n chain,\n spender: bnbSpender,\n }, options);\n }\n\n // Step 4: Sign EIP-3009 authorization (GASLESS - just signing)\n // payTo is the recipient address (v2 format)\n const payTo = req.payTo || req.resource; // fallback for v1 compatibility\n if (!payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n \n // Use server's extra field for domain info (contains correct EIP-712 domain for the token on this network)\n const domainOverride = (req.extra && typeof req.extra === 'object' && req.extra.name) \n ? { name: req.extra.name as string, version: (req.extra.version as string) || '2' }\n : undefined;\n \n const authorization = await this.signEIP3009(payTo, amount, chain, token, domainOverride);\n\n // Get token-specific info for accepted field\n const tokenConfig = chain.tokens[token];\n\n // Step 5: Create x402 payment payload (v2 requires scheme, network, payload, AND accepted)\n // Use server's extra field if provided (contains correct EIP-712 domain for the token on this network)\n // Fall back to local config for backward compatibility\n const extra = (req.extra && typeof req.extra === 'object') \n ? req.extra \n : {\n name: (tokenConfig as any).eip712Name || 'USD Coin',\n version: '2',\n };\n \n const payload = {\n x402Version: X402_VERSION,\n scheme: 'exact',\n network,\n payload: authorization, // { authorization: {...}, signature: \"0x...\" }\n accepted: {\n scheme: 'exact',\n network,\n asset: tokenConfig.address,\n amount: amountRaw,\n payTo,\n maxTimeoutSeconds: req.maxTimeoutSeconds || 300,\n extra,\n },\n };\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Step 6: Retry with payment header\n console.log(`[MoltsPay] Sending request with payment...`);\n const paidRequestBody: any = options.rawData\n ? { service, ...params }\n : { service, params };\n if (options.chain) {\n paidRequestBody.chain = options.chain;\n }\n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [PAYMENT_HEADER]: paymentHeader,\n },\n body: JSON.stringify(paidRequestBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Service execution failed');\n }\n\n // Update spending tracking\n this.recordSpending(amount);\n\n console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || 'claimed'}`);\n \n // Support both MoltsPay Server format ({ result: ... }) and direct response format\n return result.result || result;\n }\n\n /**\n * Handle MPP (Machine Payments Protocol) payment flow\n * Called when pay() detects WWW-Authenticate header in 402 response\n */\n private async handleMPPPayment(\n executeUrl: string,\n service: string,\n params: Record<string, any>,\n wwwAuthHeader: string,\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n // Dynamic imports for ESM-only packages\n const { privateKeyToAccount } = await import('viem/accounts');\n const { createWalletClient, createPublicClient, http } = await import('viem');\n const { tempoModerato } = await import('viem/chains');\n const { Actions } = await import('viem/tempo');\n\n // Get private key from wallet data\n const privateKey = this.walletData!.privateKey as `0x${string}`;\n const account = privateKeyToAccount(privateKey);\n\n console.log(`[MoltsPay] Using MPP protocol on Tempo`);\n console.log(`[MoltsPay] Account: ${account.address}`);\n\n // Parse WWW-Authenticate: Payment id=\"...\", method=\"tempo\", request=\"...\"\n const parseAuthParam = (header: string, key: string): string | null => {\n const match = header.match(new RegExp(`${key}=\"([^\"]+)\"`, 'i'));\n return match ? match[1] : null;\n };\n\n const challengeId = parseAuthParam(wwwAuthHeader, 'id');\n const method = parseAuthParam(wwwAuthHeader, 'method');\n const realm = parseAuthParam(wwwAuthHeader, 'realm');\n const requestB64 = parseAuthParam(wwwAuthHeader, 'request');\n\n if (method !== 'tempo') {\n throw new Error(`Unsupported payment method: ${method}`);\n }\n\n if (!requestB64) {\n throw new Error('Missing request in WWW-Authenticate');\n }\n\n // Decode payment request\n const requestJson = Buffer.from(requestB64, 'base64').toString('utf-8');\n const paymentRequest = JSON.parse(requestJson);\n \n const { amount, currency, recipient, methodDetails } = paymentRequest;\n const chainId = methodDetails?.chainId || 42431;\n const amountDisplay = Number(amount) / 1e6;\n\n console.log(`[MoltsPay] Payment: $${amountDisplay} to ${recipient}`);\n\n // Check limits\n this.checkLimits(amountDisplay);\n\n // Execute transfer on Tempo\n console.log(`[MoltsPay] Sending transaction on Tempo...`);\n\n const tempoChain = { ...tempoModerato, feeToken: currency as `0x${string}` };\n \n const publicClient = createPublicClient({\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n const walletClient = createWalletClient({\n account,\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n // TIP-20 transfer\n const txHash = await Actions.token.transfer(walletClient, {\n to: recipient as `0x${string}`,\n amount: BigInt(amount),\n token: currency as `0x${string}`,\n });\n\n console.log(`[MoltsPay] Transaction: ${txHash}`);\n\n // Wait for confirmation\n await publicClient.waitForTransactionReceipt({ hash: txHash });\n console.log(`[MoltsPay] Confirmed! Retrying with credential...`);\n\n // Build credential\n const credential = {\n challenge: {\n id: challengeId,\n realm,\n method: 'tempo',\n intent: 'charge',\n request: paymentRequest,\n },\n payload: { hash: txHash, type: 'hash' },\n source: `did:pkh:eip155:${chainId}:${account.address}`,\n };\n\n const credentialB64 = Buffer.from(JSON.stringify(credential))\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\n // Retry with credential - respect rawData option\n const retryBody = options.rawData \n ? { service, ...params, chain: 'tempo_moderato' }\n : { service, params, chain: 'tempo_moderato' };\n \n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Payment ${credentialB64}`,\n },\n body: JSON.stringify(retryBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Payment verification failed');\n }\n\n // Update spending tracking\n this.recordSpending(amountDisplay);\n\n console.log(`[MoltsPay] Success!`);\n return result.result || result;\n }\n\n /**\n * Handle BNB Chain payment flow (pre-approval + intent signature)\n * \n * Flow:\n * 1. Check client has approved server wallet (done via `moltspay init`)\n * 2. Sign EIP-712 payment intent (no gas, just signature)\n * 3. Send intent to server\n * 4. Server executes service\n * 5. Server calls transferFrom if successful (pay-for-success)\n */\n private async handleBNBPayment(\n executeUrl: string,\n service: string,\n params: Record<string, any>,\n paymentDetails: {\n to: string;\n amount: number;\n token: TokenSymbol;\n chainName: ChainName;\n chain: ChainConfig;\n spender: string;\n },\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n const { to, amount, token, chainName, chain, spender } = paymentDetails;\n const tokenConfig = chain.tokens[token];\n \n // Check approval status\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);\n const amountWeiCheck = BigInt(Math.floor(amount * (10 ** tokenConfig.decimals)));\n \n if (allowance < amountWeiCheck) {\n // Check if user has enough BNB for gas to approve\n const nativeBalance = await provider.getBalance(this.wallet!.address);\n const minGasBalance = ethers.parseEther('0.0005'); // ~0.0005 BNB minimum for approval\n \n if (nativeBalance < minGasBalance) {\n const nativeBNB = parseFloat(ethers.formatEther(nativeBalance)).toFixed(4);\n const isTestnet = chainName === 'bnb_testnet';\n \n if (isTestnet) {\n throw new Error(\n `❌ Insufficient tBNB for approval transaction\\n\\n` +\n ` Current tBNB: ${nativeBNB}\\n` +\n ` Required: ~0.001 tBNB\\n\\n` +\n ` Get testnet tokens: npx moltspay faucet --chain bnb_testnet\\n` +\n ` (Gives USDC + tBNB for gas)`\n );\n } else {\n throw new Error(\n `❌ Insufficient BNB for approval transaction\\n\\n` +\n ` Current BNB: ${nativeBNB}\\n` +\n ` Required: ~0.001 BNB (~$0.60)\\n\\n` +\n ` To get BNB:\\n` +\n ` • Withdraw from Binance/exchange to your wallet\\n` +\n ` • Most exchanges include BNB dust with withdrawals\\n\\n` +\n ` After funding, run:\\n` +\n ` npx moltspay approve --chain ${chainName} --spender ${spender}`\n );\n }\n }\n \n throw new Error(\n `Insufficient allowance for ${spender.slice(0, 10)}...\\n` +\n `Run: npx moltspay approve --chain ${chainName} --spender ${spender}`\n );\n }\n \n // Convert amount to wei (BNB uses 18 decimals)\n const amountWei = BigInt(Math.floor(amount * (10 ** tokenConfig.decimals))).toString();\n \n // Create payment intent\n const intent = {\n from: this.wallet!.address,\n to,\n amount: amountWei,\n token: tokenConfig.address,\n service,\n nonce: Date.now(), // Use timestamp as nonce for simplicity\n deadline: Date.now() + 3600000, // 1 hour\n };\n\n // EIP-712 domain\n const domain = {\n name: 'MoltsPay',\n version: '1',\n chainId: chain.chainId,\n };\n\n // EIP-712 types\n const types = {\n PaymentIntent: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'amount', type: 'uint256' },\n { name: 'token', type: 'address' },\n { name: 'service', type: 'string' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n };\n\n // Sign the intent\n console.log(`[MoltsPay] Signing BNB payment intent...`);\n const signature = await this.wallet!.signTypedData(domain, types, intent);\n\n // Create x402 payment payload with BNB-specific format\n const network = `eip155:${chain.chainId}`;\n const payload = {\n x402Version: 2,\n scheme: 'exact',\n network,\n payload: {\n intent: {\n ...intent,\n signature,\n },\n chainId: chain.chainId,\n },\n accepted: {\n scheme: 'exact',\n network,\n asset: tokenConfig.address,\n amount: amountWei,\n payTo: to,\n maxTimeoutSeconds: 300,\n },\n };\n\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Send request with payment - respect rawData option\n console.log(`[MoltsPay] Sending BNB payment request...`);\n const bnbRequestBody = options.rawData\n ? { service, ...params, chain: chainName }\n : { service, params, chain: chainName };\n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Payment': paymentHeader,\n },\n body: JSON.stringify(bnbRequestBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'BNB payment failed');\n }\n\n // Update spending tracking\n this.recordSpending(amount);\n\n console.log(`[MoltsPay] Success! BNB payment settled.`);\n return result.result || result;\n }\n\n /**\n * Handle Solana payment flow\n * \n * Solana uses SPL token transfers with pay-for-success model:\n * 1. Client creates and signs a transfer transaction\n * 2. Server submits the transaction after service completes\n */\n private async handleSolanaPayment(\n executeUrl: string,\n service: string,\n params: Record<string, any>,\n requirements: X402PaymentRequirements,\n chain: SolanaChainName,\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n // Load Solana wallet\n const solanaWallet = loadSolanaWallet(this.configDir);\n if (!solanaWallet) {\n throw new Error('No Solana wallet found. Run: npx moltspay init --chain solana_devnet');\n }\n\n const amount = Number(requirements.amount);\n const amountUSDC = amount / 1e6;\n \n // Check limits\n this.checkLimits(amountUSDC);\n\n console.log(`[MoltsPay] Creating Solana payment: $${amountUSDC} USDC`);\n\n // Validate payTo address\n if (!requirements.payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n\n // Check for gasless mode (server pays fees)\n const solanaFeePayer = (requirements.extra as any)?.solanaFeePayer;\n const feePayerPubkey = solanaFeePayer ? new PublicKey(solanaFeePayer) : undefined;\n \n if (feePayerPubkey) {\n console.log(`[MoltsPay] Gasless mode: server pays fees`);\n }\n\n // Create the transfer transaction\n const recipientPubkey = new PublicKey(requirements.payTo);\n const transaction = await createSolanaPaymentTransaction(\n solanaWallet.publicKey,\n recipientPubkey,\n BigInt(amount),\n chain,\n feePayerPubkey // Optional fee payer for gasless mode\n );\n\n // Sign the transaction (partial sign if gasless mode)\n if (feePayerPubkey) {\n // Gasless mode: only sign for token transfer authority\n transaction.partialSign(solanaWallet);\n } else {\n // Normal mode: sign as both authority and fee payer\n transaction.sign(solanaWallet);\n }\n const signedTx = transaction.serialize({ requireAllSignatures: false }).toString('base64');\n\n console.log(`[MoltsPay] Transaction signed, sending to server...`);\n\n // Create x402 payload with Solana-specific format\n const network = chain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n const payload = {\n x402Version: 2,\n scheme: 'exact',\n network,\n payload: {\n signedTransaction: signedTx,\n sender: solanaWallet.publicKey.toBase58(),\n chain,\n },\n accepted: {\n scheme: 'exact',\n network,\n asset: requirements.asset,\n amount: requirements.amount,\n payTo: requirements.payTo,\n maxTimeoutSeconds: 300,\n },\n };\n\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Send request with payment - respect rawData option\n const solanaRequestBody = options.rawData\n ? { service, ...params, chain }\n : { service, params, chain };\n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Payment': paymentHeader,\n },\n body: JSON.stringify(solanaRequestBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Solana payment failed');\n }\n\n // Update spending tracking\n this.recordSpending(amountUSDC);\n\n console.log(`[MoltsPay] Success! Solana payment settled.`);\n if (result.payment?.transaction) {\n const explorerUrl = chain === 'solana' \n ? `https://solscan.io/tx/${result.payment.transaction}`\n : `https://solscan.io/tx/${result.payment.transaction}?cluster=devnet`;\n console.log(`[MoltsPay] Transaction: ${explorerUrl}`);\n }\n\n return result.result || result;\n }\n\n /**\n * Check ERC20 allowance for a spender\n */\n private async checkAllowance(\n tokenAddress: string,\n spender: string,\n provider: ethers.JsonRpcProvider\n ): Promise<bigint> {\n const contract = new ethers.Contract(\n tokenAddress,\n ['function allowance(address owner, address spender) view returns (uint256)'],\n provider\n );\n return await contract.allowance(this.wallet!.address, spender);\n }\n\n /**\n * Sign EIP-3009 transferWithAuthorization (GASLESS)\n * This only signs - no on-chain transaction, no gas needed.\n * Supports both USDC and USDT.\n */\n private async signEIP3009(\n to: string,\n amount: number,\n chain: { chainId: number; tokens: Record<TokenSymbol, { address: string; decimals: number }> },\n token: TokenSymbol = 'USDC',\n domainOverride?: { name: string; version: string }\n ): Promise<{ authorization: EIP3009Authorization; signature: string }> {\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600; // 1 hour\n const nonce = ethers.hexlify(ethers.randomBytes(32));\n \n const tokenConfig = chain.tokens[token];\n const value = BigInt(Math.floor(amount * (10 ** tokenConfig.decimals))).toString();\n\n const authorization: EIP3009Authorization = {\n from: this.wallet!.address,\n to,\n value,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n };\n\n // EIP-712 domain - use server's domain info if provided (handles mainnet vs testnet differences)\n // Fall back to local token config for backward compatibility\n const tokenName = domainOverride?.name || (tokenConfig as any).eip712Name || (token === 'USDC' ? 'USD Coin' : 'Tether USD');\n const tokenVersion = domainOverride?.version || '2';\n const domain = {\n name: tokenName,\n version: tokenVersion,\n chainId: chain.chainId,\n verifyingContract: tokenConfig.address,\n };\n\n // EIP-3009 types\n const types = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n };\n\n const signature = await this.wallet!.signTypedData(domain, types, authorization);\n\n return { authorization, signature };\n }\n\n /**\n * Check spending limits\n */\n private checkLimits(amount: number): void {\n // Check per-tx limit\n if (amount > this.config.limits.maxPerTx) {\n throw new Error(\n `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`\n );\n }\n\n // Reset daily spending if new day\n const today = new Date().setHours(0, 0, 0, 0);\n if (today > this.lastSpendingReset) {\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n this.saveSpending(); // Persist reset\n }\n\n // Check daily limit\n if (this.todaySpending + amount > this.config.limits.maxPerDay) {\n throw new Error(\n `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`\n );\n }\n }\n\n /**\n * Record spending and persist to disk\n */\n private recordSpending(amount: number): void {\n this.todaySpending += amount;\n this.saveSpending();\n }\n\n // --- Config & Wallet Management ---\n\n private loadConfig(): ClientConfig {\n const configPath = join(this.configDir, 'config.json');\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) };\n }\n return { ...DEFAULT_CONFIG };\n }\n\n private saveConfig(): void {\n mkdirSync(this.configDir, { recursive: true });\n const configPath = join(this.configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n }\n\n /**\n * Load spending data from disk\n */\n private loadSpending(): void {\n const spendingPath = join(this.configDir, 'spending.json');\n if (existsSync(spendingPath)) {\n try {\n const data = JSON.parse(readFileSync(spendingPath, 'utf-8'));\n const today = new Date().setHours(0, 0, 0, 0);\n \n // Only load if it's from today\n if (data.date && data.date === today) {\n this.todaySpending = data.amount || 0;\n this.lastSpendingReset = data.date;\n } else {\n // Data is from a previous day, reset\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n }\n } catch {\n // Ignore parse errors, start fresh\n this.todaySpending = 0;\n this.lastSpendingReset = new Date().setHours(0, 0, 0, 0);\n }\n }\n }\n\n /**\n * Save spending data to disk\n */\n private saveSpending(): void {\n mkdirSync(this.configDir, { recursive: true });\n const spendingPath = join(this.configDir, 'spending.json');\n const data = {\n date: this.lastSpendingReset || new Date().setHours(0, 0, 0, 0),\n amount: this.todaySpending,\n updatedAt: Date.now(),\n };\n writeFileSync(spendingPath, JSON.stringify(data, null, 2));\n }\n\n private loadWallet(): WalletData | null {\n const walletPath = join(this.configDir, 'wallet.json');\n if (existsSync(walletPath)) {\n // Security check: warn and fix if permissions are too open\n try {\n const stats = statSync(walletPath);\n const mode = stats.mode & 0o777;\n if (mode !== 0o600) {\n console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);\n console.warn(`[MoltsPay] Fixing permissions to 0600...`);\n chmodSync(walletPath, 0o600);\n }\n } catch (err) {\n // Ignore permission check errors on Windows\n }\n \n const content = readFileSync(walletPath, 'utf-8');\n return JSON.parse(content);\n }\n return null;\n }\n\n /**\n * Initialize a new wallet (called by CLI)\n */\n static init(\n configDir: string,\n options: { chain: string; maxPerTx: number; maxPerDay: number }\n ): { address: string; configDir: string } {\n mkdirSync(configDir, { recursive: true });\n\n // Create wallet\n const wallet = Wallet.createRandom();\n const walletData: WalletData = {\n address: wallet.address,\n privateKey: wallet.privateKey,\n createdAt: Date.now(),\n };\n\n // Save wallet with secure permissions (0o600 = owner read/write only)\n const walletPath = join(configDir, 'wallet.json');\n writeFileSync(walletPath, JSON.stringify(walletData, null, 2), { mode: 0o600 });\n\n // Save config\n const config: ClientConfig = {\n chain: options.chain,\n limits: {\n maxPerTx: options.maxPerTx,\n maxPerDay: options.maxPerDay,\n },\n };\n const configPath = join(configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n return { address: wallet.address, configDir };\n }\n\n /**\n * Get wallet balance (USDC, USDT, and native token) on default chain\n */\n async getBalance(): Promise<{ usdc: number; usdt: number; native: number }> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n let chain;\n try {\n chain = getChain(this.config.chain as EvmChainName);\n } catch {\n throw new Error(`Unknown chain: ${this.config.chain}`);\n }\n\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n const tokenAbi = ['function balanceOf(address) view returns (uint256)'];\n\n // Get all balances in parallel\n const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([\n provider.getBalance(this.wallet.address),\n new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),\n new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address),\n ]);\n\n return {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),\n usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\n }\n\n /**\n * Get wallet balances on all supported chains (Base + Polygon + Tempo)\n */\n async getAllBalances(): Promise<Record<string, { usdc: number; usdt: number; native: number; tempo?: { pathUSD: number; alphaUSD: number; betaUSD: number; thetaUSD: number } }>> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n const supportedChains: EvmChainName[] = ['base', 'polygon', 'base_sepolia', 'tempo_moderato', 'bnb', 'bnb_testnet'];\n const tokenAbi = ['function balanceOf(address) view returns (uint256)'];\n const results: Record<string, { usdc: number; usdt: number; native: number; tempo?: { pathUSD: number; alphaUSD: number; betaUSD: number; thetaUSD: number } }> = {};\n\n // Tempo testnet token addresses\n const tempoTokens = {\n pathUSD: '0x20c0000000000000000000000000000000000000',\n alphaUSD: '0x20c0000000000000000000000000000000000001',\n betaUSD: '0x20c0000000000000000000000000000000000002',\n thetaUSD: '0x20c0000000000000000000000000000000000003',\n };\n\n // Query all chains in parallel\n await Promise.all(\n supportedChains.map(async (chainName) => {\n try {\n const chain = getChain(chainName);\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n\n if (chainName === 'tempo_moderato') {\n // Tempo: fetch all 4 testnet tokens\n const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([\n provider.getBalance(this.wallet!.address),\n new ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n ]);\n\n results[chainName] = {\n usdc: parseFloat(ethers.formatUnits(pathUSD, 6)), // pathUSD as default USDC\n usdt: parseFloat(ethers.formatUnits(alphaUSD, 6)), // alphaUSD as default USDT\n native: parseFloat(ethers.formatEther(nativeBalance)),\n tempo: {\n pathUSD: parseFloat(ethers.formatUnits(pathUSD, 6)),\n alphaUSD: parseFloat(ethers.formatUnits(alphaUSD, 6)),\n betaUSD: parseFloat(ethers.formatUnits(betaUSD, 6)),\n thetaUSD: parseFloat(ethers.formatUnits(thetaUSD, 6)),\n },\n };\n } else {\n // Other chains: fetch USDC and USDT\n const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([\n provider.getBalance(this.wallet!.address),\n new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet!.address),\n ]);\n\n results[chainName] = {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),\n usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\n }\n } catch (err) {\n // If chain query fails, show zeros\n results[chainName] = { usdc: 0, usdt: 0, native: 0 };\n }\n })\n );\n\n return results;\n }\n\n /**\n * Pay for a service using MPP (Machine Payments Protocol)\n * \n * This implements the MPP flow manually for EOA wallets:\n * 1. Request service → get 402 with WWW-Authenticate\n * 2. Parse payment requirements\n * 3. Execute transfer on Tempo chain\n * 4. Retry with transaction hash as credential\n * \n * @param url - Full URL of the MPP-enabled endpoint\n * @param options - Request options (body, headers)\n * @returns Response from the service\n */\n async payWithMPP(\n url: string,\n options: {\n body?: any;\n headers?: Record<string, string>;\n } = {}\n ): Promise<any> {\n if (!this.wallet || !this.walletData) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Dynamic imports for ESM-only packages\n const { privateKeyToAccount } = await import('viem/accounts');\n const { createWalletClient, createPublicClient, http } = await import('viem');\n const { tempoModerato } = await import('viem/chains');\n const { Actions } = await import('viem/tempo');\n\n // Get private key from wallet data\n const privateKey = this.walletData.privateKey as `0x${string}`;\n const account = privateKeyToAccount(privateKey);\n\n console.log(`[MoltsPay] Making MPP request to: ${url}`);\n console.log(`[MoltsPay] Using account: ${account.address}`);\n\n // Step 1: Initial request to get 402 with payment requirements\n const initResponse = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n\n // If not 402, handle directly\n if (initResponse.status !== 402) {\n if (initResponse.ok) {\n return initResponse.json();\n }\n const errorText = await initResponse.text();\n throw new Error(`Request failed (${initResponse.status}): ${errorText}`);\n }\n\n // Step 2: Parse WWW-Authenticate header\n const wwwAuth = initResponse.headers.get('www-authenticate');\n if (!wwwAuth || !wwwAuth.toLowerCase().includes('payment')) {\n throw new Error('No WWW-Authenticate Payment challenge in 402 response');\n }\n\n console.log(`[MoltsPay] Got 402, parsing payment challenge...`);\n\n // Parse WWW-Authenticate: Payment id=\"...\", method=\"tempo\", request=\"...\"\n const parseAuthParam = (header: string, key: string): string | null => {\n const match = header.match(new RegExp(`${key}=\"([^\"]+)\"`, 'i'));\n return match ? match[1] : null;\n };\n\n const challengeId = parseAuthParam(wwwAuth, 'id');\n const method = parseAuthParam(wwwAuth, 'method');\n const realm = parseAuthParam(wwwAuth, 'realm');\n const requestB64 = parseAuthParam(wwwAuth, 'request');\n\n if (method !== 'tempo') {\n throw new Error(`Unsupported payment method: ${method}`);\n }\n\n if (!requestB64) {\n throw new Error('Missing request in WWW-Authenticate');\n }\n\n // Decode payment request\n const requestJson = Buffer.from(requestB64, 'base64').toString('utf-8');\n const paymentRequest = JSON.parse(requestJson);\n \n console.log(`[MoltsPay] Payment request:`, paymentRequest);\n\n const { amount, currency, recipient, methodDetails } = paymentRequest;\n const chainId = methodDetails?.chainId || 42431;\n\n // Step 3: Execute transfer on Tempo\n console.log(`[MoltsPay] Executing transfer on Tempo (chainId: ${chainId})...`);\n console.log(`[MoltsPay] Amount: ${amount}, To: ${recipient}`);\n\n // Create viem client for Tempo (with feeToken for gas-free transactions)\n const tempoChain = { ...tempoModerato, feeToken: currency as `0x${string}` };\n \n const publicClient = createPublicClient({\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n const walletClient = createWalletClient({\n account,\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n // Use viem's Tempo Actions for TIP-20 transfer\n const txHash = await Actions.token.transfer(walletClient, {\n to: recipient as `0x${string}`,\n amount: BigInt(amount),\n token: currency as `0x${string}`,\n });\n\n console.log(`[MoltsPay] Transaction sent: ${txHash}`);\n\n // Wait for confirmation\n console.log(`[MoltsPay] Waiting for confirmation...`);\n await publicClient.waitForTransactionReceipt({ hash: txHash });\n console.log(`[MoltsPay] Transaction confirmed!`);\n\n // Step 4: Build credential and retry\n const challenge = {\n id: challengeId,\n realm,\n method: 'tempo',\n intent: 'charge',\n request: paymentRequest,\n };\n\n const credential = {\n challenge,\n payload: { hash: txHash, type: 'hash' },\n source: `did:pkh:eip155:${chainId}:${account.address}`,\n };\n\n const credentialB64 = Buffer.from(JSON.stringify(credential))\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, ''); // base64url without padding\n\n console.log(`[MoltsPay] Retrying with payment credential...`);\n\n // Retry with credential\n const paidResponse = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Payment ${credentialB64}`,\n ...options.headers,\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n\n if (!paidResponse.ok) {\n const errorText = await paidResponse.text();\n throw new Error(`Payment verification failed (${paidResponse.status}): ${errorText}`);\n }\n\n console.log(`[MoltsPay] Payment verified! Service completed.`);\n return paidResponse.json();\n }\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName, EvmChainName, TokenSymbol } from '../types/index.js';\n\nexport const CHAINS: Record<EvmChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n tokens: {\n USDC: {\n address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USD Coin', // EIP-712 domain name\n },\n USDT: {\n address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // deprecated, for backward compat\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-bor-rpc.publicnode.com',\n tokens: {\n USDC: {\n address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',\n decimals: 6,\n symbol: 'USDT',\n eip712Name: '(PoS) Tether USD', // Polygon uses this name\n },\n },\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n tokens: {\n USDC: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USDC', // Testnet USDC uses 'USDC' not 'USD Coin'\n },\n USDT: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Same as USDC on testnet (no official USDT)\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'USDC', // Uses same contract as USDC\n },\n },\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n // ============ Tempo Testnet (Moderato) ============\n tempo_moderato: {\n name: 'Tempo Moderato',\n chainId: 42431,\n rpc: 'https://rpc.moderato.tempo.xyz',\n tokens: {\n // TIP-20 stablecoins on Tempo testnet (from mppx SDK)\n // Note: Tempo uses USD as native gas token, not ETH\n USDC: {\n address: '0x20c0000000000000000000000000000000000000', // pathUSD - primary testnet stablecoin\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'pathUSD',\n },\n USDT: {\n address: '0x20c0000000000000000000000000000000000001', // alphaUSD\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'alphaUSD',\n },\n },\n usdc: '0x20c0000000000000000000000000000000000000',\n explorer: 'https://explore.testnet.tempo.xyz/address/',\n explorerTx: 'https://explore.testnet.tempo.xyz/tx/',\n avgBlockTime: 0.5, // ~500ms finality\n },\n // ============ BNB Chain Testnet ============\n bnb_testnet: {\n name: 'BNB Testnet',\n chainId: 97,\n rpc: 'https://data-seed-prebsc-1-s1.binance.org:8545',\n tokens: {\n // Note: BNB uses 18 decimals for stablecoins (unlike Base/Polygon which use 6)\n // Using official Binance-Peg testnet tokens\n USDC: {\n address: '0x64544969ed7EBf5f083679233325356EbE738930', // Testnet USDC\n decimals: 18,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0x337610d27c682E347C9cD60BD4b3b107C9d34dDd', // Testnet USDT\n decimals: 18,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x64544969ed7EBf5f083679233325356EbE738930',\n explorer: 'https://testnet.bscscan.com/address/',\n explorerTx: 'https://testnet.bscscan.com/tx/',\n avgBlockTime: 3,\n // BNB-specific: requires approval for pay-for-success flow\n requiresApproval: true,\n },\n // ============ BNB Chain Mainnet ============\n bnb: {\n name: 'BNB Smart Chain',\n chainId: 56,\n rpc: 'https://bsc-dataseed.binance.org',\n tokens: {\n // Note: BNB uses 18 decimals for stablecoins\n USDC: {\n address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',\n decimals: 18,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0x55d398326f99059fF775485246999027B3197955',\n decimals: 18,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',\n explorer: 'https://bscscan.com/address/',\n explorerTx: 'https://bscscan.com/tx/',\n avgBlockTime: 3,\n // BNB-specific: requires approval for pay-for-success flow\n requiresApproval: true,\n },\n};\n\n/**\n * Get token address for a chain\n */\nexport function getTokenAddress(chainName: EvmChainName, token: TokenSymbol): string {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n const tokenConfig = chain.tokens[token];\n if (!tokenConfig) {\n throw new Error(`Token ${token} not supported on ${chainName}`);\n }\n return tokenConfig.address;\n}\n\n/**\n * Get token config for a chain\n */\nexport function getTokenConfig(chainName: EvmChainName, token: TokenSymbol) {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n return chain.tokens[token];\n}\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: EvmChainName): ChainConfig {\n const config = CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * List all supported EVM chains\n */\nexport function listChains(): EvmChainName[] {\n return Object.keys(CHAINS) as EvmChainName[];\n}\n\n/**\n * Get chain config by chainId\n */\nexport function getChainById(chainId: number): ChainConfig | undefined {\n return Object.values(CHAINS).find(c => c.chainId === chainId);\n}\n\n/**\n * ERC20 ABI (minimal, only required methods)\n */\nexport const ERC20_ABI = [\n 'function balanceOf(address owner) view returns (uint256)',\n 'function transfer(address to, uint256 amount) returns (bool)',\n 'function approve(address spender, uint256 amount) returns (bool)',\n 'function allowance(address owner, address spender) view returns (uint256)',\n 'function decimals() view returns (uint8)',\n 'function symbol() view returns (string)',\n 'function name() view returns (string)',\n 'function nonces(address owner) view returns (uint256)',\n 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)',\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n 'event Approval(address indexed owner, address indexed spender, uint256 value)',\n];\n\nexport type { ChainConfig, ChainName, EvmChainName, TokenSymbol };\n","/**\n * Solana Wallet Management\n * \n * Separate from EVM wallets - uses ed25519 keypairs.\n * Stored in ~/.moltspay/wallet-solana.json\n */\n\nimport { Keypair, PublicKey, Connection, LAMPORTS_PER_SOL } from '@solana/web3.js';\nimport { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport bs58 from 'bs58';\nimport { getSolanaConnection, getUSDCMint, type SolanaChainName } from '../chains/solana.js';\n\nconst DEFAULT_CONFIG_DIR = join(homedir(), '.moltspay');\nconst SOLANA_WALLET_FILE = 'wallet-solana.json';\n\nexport interface SolanaWalletData {\n publicKey: string; // Base58 encoded\n secretKey: string; // Base58 encoded (should be encrypted in production)\n createdAt: string;\n}\n\n/**\n * Get the path to the Solana wallet file\n */\nexport function getSolanaWalletPath(configDir: string = DEFAULT_CONFIG_DIR): string {\n return join(configDir, SOLANA_WALLET_FILE);\n}\n\n/**\n * Check if Solana wallet exists\n */\nexport function solanaWalletExists(configDir: string = DEFAULT_CONFIG_DIR): boolean {\n return existsSync(getSolanaWalletPath(configDir));\n}\n\n/**\n * Load existing Solana wallet\n */\nexport function loadSolanaWallet(configDir: string = DEFAULT_CONFIG_DIR): Keypair | null {\n const walletPath = getSolanaWalletPath(configDir);\n \n if (!existsSync(walletPath)) {\n return null;\n }\n \n try {\n const data: SolanaWalletData = JSON.parse(readFileSync(walletPath, 'utf-8'));\n const secretKey = bs58.decode(data.secretKey);\n return Keypair.fromSecretKey(secretKey);\n } catch (error) {\n console.error('Failed to load Solana wallet:', error);\n return null;\n }\n}\n\n/**\n * Create new Solana wallet\n */\nexport function createSolanaWallet(configDir: string = DEFAULT_CONFIG_DIR): Keypair {\n // Ensure config directory exists\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n \n const keypair = Keypair.generate();\n const data: SolanaWalletData = {\n publicKey: keypair.publicKey.toBase58(),\n secretKey: bs58.encode(keypair.secretKey),\n createdAt: new Date().toISOString(),\n };\n \n const walletPath = getSolanaWalletPath(configDir);\n writeFileSync(walletPath, JSON.stringify(data, null, 2));\n \n return keypair;\n}\n\n/**\n * Get Solana wallet address (public key as Base58)\n */\nexport function getSolanaAddress(configDir: string = DEFAULT_CONFIG_DIR): string | null {\n const wallet = loadSolanaWallet(configDir);\n return wallet?.publicKey.toBase58() || null;\n}\n\n/**\n * Get SOL balance (native token for gas)\n */\nexport async function getSolanaBalance(\n address: string,\n chain: SolanaChainName\n): Promise<number> {\n const connection = getSolanaConnection(chain);\n const pubkey = new PublicKey(address);\n \n const balance = await connection.getBalance(pubkey);\n return balance / LAMPORTS_PER_SOL;\n}\n\n/**\n * Get USDC balance on Solana\n */\nexport async function getSolanaUSDCBalance(\n address: string,\n chain: SolanaChainName\n): Promise<number> {\n const connection = getSolanaConnection(chain);\n const owner = new PublicKey(address);\n const mint = getUSDCMint(chain);\n \n try {\n const ata = await getAssociatedTokenAddress(mint, owner);\n const account = await getAccount(connection, ata);\n // USDC has 6 decimals on Solana\n return Number(account.amount) / 1e6;\n } catch (error: any) {\n // Account doesn't exist = 0 balance\n if (error.name === 'TokenAccountNotFoundError' || \n error.message?.includes('could not find account')) {\n return 0;\n }\n throw error;\n }\n}\n\n/**\n * Get all Solana balances (SOL + USDC)\n */\nexport async function getSolanaBalances(\n address: string,\n chain: SolanaChainName\n): Promise<{ sol: number; usdc: number }> {\n const [sol, usdc] = await Promise.all([\n getSolanaBalance(address, chain),\n getSolanaUSDCBalance(address, chain),\n ]);\n \n return { sol, usdc };\n}\n\n/**\n * Request SOL airdrop (devnet only)\n */\nexport async function requestSolanaAirdrop(\n address: string,\n chain: SolanaChainName,\n amount: number = 1\n): Promise<string> {\n if (chain !== 'solana_devnet') {\n throw new Error('Airdrop only available on devnet');\n }\n \n const connection = getSolanaConnection(chain);\n const pubkey = new PublicKey(address);\n \n const signature = await connection.requestAirdrop(\n pubkey,\n amount * LAMPORTS_PER_SOL\n );\n \n // Wait for confirmation\n await connection.confirmTransaction(signature, 'confirmed');\n \n return signature;\n}\n\n/**\n * Validate Solana address format\n */\nexport function isValidSolanaAddress(address: string): boolean {\n try {\n new PublicKey(address);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Solana Chain Configuration\n * \n * Solana is NOT an EVM chain - uses different:\n * - Key format: ed25519 (EdDSA) vs secp256k1 (ECDSA)\n * - Address format: Base58 vs 0x hex\n * - Token standard: SPL vs ERC-20\n */\n\nimport { Connection, PublicKey } from '@solana/web3.js';\n\nexport interface SolanaChainConfig {\n name: string;\n cluster: 'mainnet-beta' | 'devnet' | 'testnet';\n rpc: string;\n explorer: string;\n explorerTx: string;\n tokens: {\n USDC: {\n mint: string;\n decimals: number;\n };\n };\n}\n\nexport type SolanaChainName = 'solana' | 'solana_devnet';\n\nexport const SOLANA_CHAINS: Record<SolanaChainName, SolanaChainConfig> = {\n solana: {\n name: 'Solana Mainnet',\n cluster: 'mainnet-beta',\n rpc: 'https://api.mainnet-beta.solana.com',\n explorer: 'https://solscan.io/account/',\n explorerTx: 'https://solscan.io/tx/',\n tokens: {\n USDC: {\n mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // Circle official USDC\n decimals: 6,\n },\n },\n },\n solana_devnet: {\n name: 'Solana Devnet',\n cluster: 'devnet',\n rpc: 'https://api.devnet.solana.com',\n explorer: 'https://solscan.io/account/',\n explorerTx: 'https://solscan.io/tx/',\n tokens: {\n USDC: {\n // Circle's devnet USDC (if not available, we'll deploy our own test token)\n mint: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',\n decimals: 6,\n },\n },\n },\n};\n\n/**\n * Get Solana RPC connection\n */\nexport function getSolanaConnection(chain: SolanaChainName): Connection {\n const config = SOLANA_CHAINS[chain];\n return new Connection(config.rpc, 'confirmed');\n}\n\n/**\n * Get USDC mint public key for a Solana chain\n */\nexport function getUSDCMint(chain: SolanaChainName): PublicKey {\n return new PublicKey(SOLANA_CHAINS[chain].tokens.USDC.mint);\n}\n\n/**\n * Get Solana chain config\n */\nexport function getSolanaChain(name: SolanaChainName): SolanaChainConfig {\n const config = SOLANA_CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported Solana chain: ${name}. Supported: ${Object.keys(SOLANA_CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * Check if a chain name is a Solana chain\n */\nexport function isSolanaChain(chain: string): chain is SolanaChainName {\n return chain === 'solana' || chain === 'solana_devnet';\n}\n\n/**\n * Get explorer URL for a Solana address\n */\nexport function getSolanaExplorerUrl(chain: SolanaChainName, address: string): string {\n const config = SOLANA_CHAINS[chain];\n const clusterParam = chain === 'solana_devnet' ? '?cluster=devnet' : '';\n return `${config.explorer}${address}${clusterParam}`;\n}\n\n/**\n * Get explorer URL for a Solana transaction\n */\nexport function getSolanaTxExplorerUrl(chain: SolanaChainName, signature: string): string {\n const config = SOLANA_CHAINS[chain];\n const clusterParam = chain === 'solana_devnet' ? '?cluster=devnet' : '';\n return `${config.explorerTx}${signature}${clusterParam}`;\n}\n","/**\n * Solana Facilitator\n * \n * Pay-for-success payment settlement for Solana SPL token transfers.\n * Unlike EVM chains, Solana doesn't have a third-party facilitator - \n * we verify and settle directly on-chain.\n * \n * Flow:\n * 1. Client signs a SPL token transfer authorization\n * 2. Server receives the signed transaction\n * 3. Server verifies the signature and amount\n * 4. Server submits the transaction to settle payment\n */\n\nimport { \n Connection, \n PublicKey, \n Transaction,\n VersionedTransaction,\n sendAndConfirmTransaction,\n Keypair,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getAccount,\n createAssociatedTokenAccountInstruction,\n TOKEN_PROGRAM_ID,\n} from '@solana/spl-token';\nimport { \n BaseFacilitator, \n type X402PaymentPayload, \n type X402PaymentRequirements,\n type VerifyResult,\n type SettleResult,\n type HealthCheckResult,\n} from './interface.js';\nimport { SOLANA_CHAINS, type SolanaChainName } from '../chains/solana.js';\n\n/**\n * Solana payment payload structure\n */\nexport interface SolanaPaymentPayload {\n /** Base58 encoded signed transaction */\n signedTransaction: string;\n /** Sender's public key (Base58) */\n sender: string;\n /** Chain: solana or solana_devnet */\n chain: SolanaChainName;\n}\n\n/**\n * Solana Facilitator configuration\n */\nexport interface SolanaFacilitatorConfig {\n /** Optional fee payer keypair for gasless transactions */\n feePayerKeypair?: Keypair;\n}\n\n/**\n * Solana Facilitator for pay-for-success payments\n * \n * Supports gasless mode: if feePayerKeypair is provided, server pays tx fees\n */\nexport class SolanaFacilitator extends BaseFacilitator {\n readonly name = 'solana';\n readonly displayName = 'Solana Direct';\n readonly supportedNetworks = ['solana:mainnet', 'solana:devnet'];\n\n private connections: Map<SolanaChainName, Connection> = new Map();\n private feePayerKeypair?: Keypair;\n\n constructor(config?: SolanaFacilitatorConfig) {\n super();\n this.feePayerKeypair = config?.feePayerKeypair;\n \n // Initialize connections\n for (const [chain, config] of Object.entries(SOLANA_CHAINS)) {\n this.connections.set(\n chain as SolanaChainName, \n new Connection(config.rpc, 'confirmed')\n );\n }\n \n if (this.feePayerKeypair) {\n console.log(`[SolanaFacilitator] Gasless mode enabled. Fee payer: ${this.feePayerKeypair.publicKey.toBase58()}`);\n }\n }\n \n /**\n * Get fee payer public key (for gasless transactions)\n */\n getFeePayerPubkey(): string | null {\n return this.feePayerKeypair?.publicKey.toBase58() || null;\n }\n\n private getConnection(chain: SolanaChainName): Connection {\n const conn = this.connections.get(chain);\n if (!conn) {\n throw new Error(`No connection for chain: ${chain}`);\n }\n return conn;\n }\n\n /**\n * Convert our chain name to network identifier\n */\n static chainToNetwork(chain: SolanaChainName): string {\n return chain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n }\n\n /**\n * Convert network identifier to chain name\n */\n static networkToChain(network: string): SolanaChainName | null {\n if (network === 'solana:mainnet') return 'solana';\n if (network === 'solana:devnet') return 'solana_devnet';\n return null;\n }\n\n async healthCheck(): Promise<HealthCheckResult> {\n const start = Date.now();\n try {\n // Check devnet connection\n const conn = this.getConnection('solana_devnet');\n await conn.getSlot();\n return {\n healthy: true,\n latencyMs: Date.now() - start,\n };\n } catch (error: any) {\n return {\n healthy: false,\n error: error.message,\n };\n }\n }\n\n /**\n * Verify a Solana payment\n * \n * Checks:\n * 1. Transaction is valid and properly signed\n * 2. Transfer instruction matches expected amount and recipient\n */\n async verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult> {\n try {\n const solanaPayload = paymentPayload.payload as SolanaPaymentPayload;\n if (!solanaPayload || !solanaPayload.signedTransaction) {\n return { valid: false, error: 'Missing signed transaction' };\n }\n\n const chain = solanaPayload.chain || 'solana_devnet';\n const chainConfig = SOLANA_CHAINS[chain];\n if (!chainConfig) {\n return { valid: false, error: `Invalid chain: ${chain}` };\n }\n\n // Decode the transaction\n const txBuffer = Buffer.from(solanaPayload.signedTransaction, 'base64');\n let tx: Transaction | VersionedTransaction;\n \n try {\n // Try legacy transaction first\n tx = Transaction.from(txBuffer);\n } catch {\n // Try versioned transaction\n tx = VersionedTransaction.deserialize(txBuffer);\n }\n\n // Verify at least one signature exists (may be partial in gasless mode)\n if (tx instanceof Transaction) {\n // In gasless mode, fee payer signature is added by server\n // Client only signs for token transfer authority\n const hasAnySignature = tx.signatures.some(sig => \n sig.signature && !sig.signature.every(b => b === 0)\n );\n if (!hasAnySignature) {\n return { valid: false, error: 'Transaction not signed' };\n }\n }\n\n // Parse expected values from requirements\n const expectedAmount = BigInt(requirements.amount);\n const expectedRecipient = new PublicKey(requirements.payTo);\n\n // For now, we trust the transaction structure\n // Full verification happens at settlement time\n return {\n valid: true,\n details: {\n chain,\n sender: solanaPayload.sender,\n recipient: requirements.payTo,\n amount: requirements.amount,\n },\n };\n } catch (error: any) {\n return { valid: false, error: error.message };\n }\n }\n\n /**\n * Settle a Solana payment\n * \n * Submits the signed transaction to the network.\n * In gasless mode, adds fee payer signature before submitting.\n */\n async settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult> {\n try {\n const solanaPayload = paymentPayload.payload as SolanaPaymentPayload;\n if (!solanaPayload || !solanaPayload.signedTransaction) {\n return { success: false, error: 'Missing signed transaction' };\n }\n\n const chain = solanaPayload.chain || 'solana_devnet';\n const connection = this.getConnection(chain);\n\n // Decode the transaction\n const txBuffer = Buffer.from(solanaPayload.signedTransaction, 'base64');\n \n let txToSend: Buffer;\n \n try {\n // Try legacy transaction\n const tx = Transaction.from(txBuffer);\n \n // Check if we need to add fee payer signature (gasless mode)\n if (this.feePayerKeypair && tx.feePayer) {\n const feePayerPubkey = this.feePayerKeypair.publicKey.toBase58();\n const txFeePayer = tx.feePayer.toBase58();\n \n if (txFeePayer === feePayerPubkey) {\n // Gasless mode: add fee payer signature\n console.log(`[SolanaFacilitator] Gasless mode: adding fee payer signature`);\n tx.partialSign(this.feePayerKeypair);\n }\n }\n \n txToSend = tx.serialize();\n } catch (e: any) {\n // Fall back to versioned transaction (no gasless support for versioned yet)\n txToSend = txBuffer;\n }\n\n // Send the transaction\n const signature = await connection.sendRawTransaction(txToSend, {\n skipPreflight: false,\n preflightCommitment: 'confirmed',\n });\n\n // Wait for confirmation\n const confirmation = await connection.confirmTransaction(signature, 'confirmed');\n \n if (confirmation.value.err) {\n return {\n success: false,\n error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,\n transaction: signature,\n };\n }\n\n return {\n success: true,\n transaction: signature,\n status: 'confirmed',\n };\n } catch (error: any) {\n return { success: false, error: error.message };\n }\n }\n\n supportsNetwork(network: string): boolean {\n return this.supportedNetworks.includes(network);\n }\n}\n\n/**\n * Create a Solana payment transaction for signing\n * \n * This is called by the client to create the transaction to sign.\n * \n * @param senderPubkey - The sender's public key (token owner)\n * @param recipientPubkey - The recipient's public key\n * @param amount - Amount in token base units\n * @param chain - Solana chain (solana or solana_devnet)\n * @param feePayerPubkey - Optional fee payer public key for gasless transactions\n */\nexport async function createSolanaPaymentTransaction(\n senderPubkey: PublicKey,\n recipientPubkey: PublicKey,\n amount: bigint,\n chain: SolanaChainName,\n feePayerPubkey?: PublicKey,\n): Promise<Transaction> {\n const chainConfig = SOLANA_CHAINS[chain];\n const connection = new Connection(chainConfig.rpc, 'confirmed');\n const mint = new PublicKey(chainConfig.tokens.USDC.mint);\n\n // Determine who pays fees (gasless mode uses server's fee payer)\n const actualFeePayer = feePayerPubkey || senderPubkey;\n\n // Get ATAs\n const senderATA = await getAssociatedTokenAddress(mint, senderPubkey);\n const recipientATA = await getAssociatedTokenAddress(mint, recipientPubkey);\n\n const transaction = new Transaction();\n\n // Check if recipient ATA exists\n try {\n await getAccount(connection, recipientATA);\n } catch {\n // Create ATA for recipient (fee payer pays rent in gasless mode)\n transaction.add(\n createAssociatedTokenAccountInstruction(\n actualFeePayer, // payer (fee payer in gasless mode)\n recipientATA, // ata to create\n recipientPubkey, // owner\n mint // mint\n )\n );\n }\n\n // Add transfer instruction\n transaction.add(\n createTransferCheckedInstruction(\n senderATA, // source\n mint, // mint\n recipientATA, // destination\n senderPubkey, // owner (sender still authorizes the transfer)\n amount, // amount\n chainConfig.tokens.USDC.decimals // decimals\n )\n );\n\n // Get recent blockhash\n const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();\n transaction.recentBlockhash = blockhash;\n transaction.feePayer = actualFeePayer;\n\n return transaction;\n}\n\nexport default SolanaFacilitator;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,IAAAA,aAAwF;AACxF,IAAAC,aAAwB;AACxB,IAAAC,eAAqB;AACrB,oBAA+B;;;ACRxB,IAAM,SAA4C;AAAA;AAAA,EAEvD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA,MACN,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA;AAAA,MACd;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,MAAM;AAAA;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA,MACN,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA;AAAA,MACd;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAAA;AAAA,EAEA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA,MACN,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA;AAAA,MACd;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA;AAAA,MACd;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAAA;AAAA,EAEA,gBAAgB;AAAA,IACd,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA;AAAA;AAAA,MAGN,MAAM;AAAA,QACJ,SAAS;AAAA;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA;AAAA,EAChB;AAAA;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA;AAAA;AAAA,MAGN,MAAM;AAAA,QACJ,SAAS;AAAA;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA;AAAA,IAEd,kBAAkB;AAAA,EACpB;AAAA;AAAA,EAEA,KAAK;AAAA,IACH,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,QAAQ;AAAA;AAAA,MAEN,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA;AAAA,IAEd,kBAAkB;AAAA,EACpB;AACF;AA+BO,SAAS,SAAS,MAAiC;AACxD,QAAM,SAAS,OAAO,IAAI;AAC1B,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,sBAAsB,IAAI,gBAAgB,OAAO,KAAK,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EAC5F;AACA,SAAO;AACT;;;AC7LA,IAAAC,eAAiE;AACjE,uBAAsD;AACtD,gBAAmE;AACnE,kBAAqB;AACrB,gBAAwB;AACxB,kBAAiB;;;ACHjB,kBAAsC;AAkB/B,IAAM,gBAA4D;AAAA,EACvE,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,MACN,MAAM;AAAA,QACJ,MAAM;AAAA;AAAA,QACN,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EACA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,MACN,MAAM;AAAA;AAAA,QAEJ,MAAM;AAAA,QACN,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF;;;ADxCA,IAAM,yBAAqB,sBAAK,mBAAQ,GAAG,WAAW;AACtD,IAAM,qBAAqB;AAWpB,SAAS,oBAAoB,YAAoB,oBAA4B;AAClF,aAAO,kBAAK,WAAW,kBAAkB;AAC3C;AAYO,SAAS,iBAAiB,YAAoB,oBAAoC;AACvF,QAAM,aAAa,oBAAoB,SAAS;AAEhD,MAAI,KAAC,sBAAW,UAAU,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,OAAyB,KAAK,UAAM,wBAAa,YAAY,OAAO,CAAC;AAC3E,UAAM,YAAY,YAAAC,QAAK,OAAO,KAAK,SAAS;AAC5C,WAAO,qBAAQ,cAAc,SAAS;AAAA,EACxC,SAAS,OAAO;AACd,YAAQ,MAAM,iCAAiC,KAAK;AACpD,WAAO;AAAA,EACT;AACF;;;AE1CA,IAAAC,eAOO;AACP,IAAAC,oBAMO;AA0QP,eAAsB,+BACpB,cACA,iBACA,QACA,OACA,gBACsB;AACtB,QAAM,cAAc,cAAc,KAAK;AACvC,QAAM,aAAa,IAAI,wBAAW,YAAY,KAAK,WAAW;AAC9D,QAAM,OAAO,IAAI,uBAAU,YAAY,OAAO,KAAK,IAAI;AAGvD,QAAM,iBAAiB,kBAAkB;AAGzC,QAAM,YAAY,UAAM,6CAA0B,MAAM,YAAY;AACpE,QAAM,eAAe,UAAM,6CAA0B,MAAM,eAAe;AAE1E,QAAM,cAAc,IAAI,yBAAY;AAGpC,MAAI;AACF,cAAM,8BAAW,YAAY,YAAY;AAAA,EAC3C,QAAQ;AAEN,gBAAY;AAAA,UACV;AAAA,QACE;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,cAAY;AAAA,QACV;AAAA,MACE;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA;AAAA,MACA,YAAY,OAAO,KAAK;AAAA;AAAA,IAC1B;AAAA,EACF;AAGA,QAAM,EAAE,WAAW,qBAAqB,IAAI,MAAM,WAAW,mBAAmB;AAChF,cAAY,kBAAkB;AAC9B,cAAY,WAAW;AAEvB,SAAO;AACT;;;AJxUA,IAAAC,eAA+C;AAsB/C,IAAM,eAAe;AACrB,IAAM,0BAA0B;AAChC,IAAM,iBAAiB;AA0BvB,IAAM,iBAA+B;AAAA,EACnC,OAAO;AAAA,EACP,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AACF;AAEO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EACA;AAAA,EACA,aAAgC;AAAA,EAChC,SAAwB;AAAA,EACxB,gBAAwB;AAAA,EACxB,oBAA4B;AAAA,EAEpC,YAAY,UAAiC,CAAC,GAAG;AAC/C,SAAK,YAAY,QAAQ,iBAAa,uBAAK,oBAAQ,GAAG,WAAW;AACjE,SAAK,SAAS,KAAK,WAAW;AAC9B,SAAK,aAAa,KAAK,WAAW;AAClC,SAAK,aAAa;AAElB,QAAI,KAAK,YAAY;AACnB,WAAK,SAAS,IAAI,qBAAO,KAAK,WAAW,UAAU;AAAA,IACrD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,gBAAyB;AAC3B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAyB;AAC3B,WAAO,KAAK,QAAQ,WAAW;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,YAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,YAA0B;AACxB,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAAgD;AAC3D,QAAI,QAAQ,aAAa,QAAW;AAClC,WAAK,OAAO,OAAO,WAAW,QAAQ;AAAA,IACxC;AACA,QAAI,QAAQ,cAAc,QAAW;AACnC,WAAK,OAAO,OAAO,YAAY,QAAQ;AAAA,IACzC;AACA,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,WAA8C;AAE9D,UAAM,gBAAgB,UAAU,QAAQ,qDAAqD,EAAE;AAG/F,UAAM,YAAY,CAAC,aAAa,iBAAiB,oBAAoB;AAErE,eAAW,YAAY,WAAW;AAChC,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,GAAG,aAAa,GAAG,QAAQ,EAAE;AACrD,YAAI,CAAC,IAAI,GAAI;AAEb,cAAM,cAAc,IAAI,QAAQ,IAAI,cAAc,KAAK;AACvD,YAAI,CAAC,YAAY,SAAS,kBAAkB,EAAG;AAE/C,eAAO,MAAM,IAAI,KAAK;AAAA,MACxB,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI,MAAM,sDAAsD,aAAa,EAAE;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,IACJ,WACA,SACA,QACA,UAAsB,CAAC,GACO;AAC9B,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,YAAY;AACpC,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAGA,YAAQ,IAAI,kCAAkC,OAAO,EAAE;AACvD,QAAI,aAAa,GAAG,SAAS;AAE7B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,YAAY,SAAS;AACjD,YAAM,MAAM,SAAS,UAAU,KAAK,CAAC,MAAW,EAAE,OAAO,OAAO;AAChE,UAAI,KAAK,UAAU;AAEjB,qBAAa,GAAG,SAAS,GAAG,IAAI,QAAQ;AACxC,gBAAQ,IAAI,sCAAsC,IAAI,QAAQ,EAAE;AAAA,MAClE;AAAA,IACF,QAAQ;AAAA,IAER;AAGA,QAAI;AACJ,QAAI,QAAQ,SAAS;AAEnB,oBAAc,EAAE,SAAS,GAAG,OAAO;AAAA,IACrC,OAAO;AAEL,oBAAc,EAAE,SAAS,OAAO;AAAA,IAClC;AACA,QAAI,QAAQ,OAAO;AACjB,kBAAY,QAAQ,QAAQ;AAAA,IAC9B;AACA,UAAM,aAAa,MAAM,MAAM,YAAY;AAAA,MACzC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,WAAW;AAAA,IAClC,CAAC;AAGD,QAAI,WAAW,WAAW,KAAK;AAC7B,YAAM,OAAO,MAAM,WAAW,KAAK;AACnC,UAAI,WAAW,MAAM,KAAK,QAAQ;AAChC,eAAO,KAAK;AAAA,MACd;AACA,YAAM,IAAI,MAAM,KAAK,SAAS,qBAAqB;AAAA,IACrD;AAIA,UAAM,gBAAgB,WAAW,QAAQ,IAAI,kBAAkB;AAC/D,UAAM,wBAAwB,WAAW,QAAQ,IAAI,uBAAuB;AAG5E,QAAI,iBAAiB,cAAc,YAAY,EAAE,SAAS,SAAS,GAAG;AACpE,cAAQ,IAAI,uDAAuD;AACnE,aAAO,MAAM,KAAK,iBAAiB,YAAY,SAAS,QAAQ,eAAe,OAAO;AAAA,IACxF;AAEA,QAAI,CAAC,uBAAuB;AAC1B,YAAM,IAAI,MAAM,iEAAiE;AAAA,IACnF;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,UAAU,OAAO,KAAK,uBAAuB,QAAQ,EAAE,SAAS,OAAO;AAC7E,YAAM,SAAS,KAAK,MAAM,OAAO;AAGjC,UAAI,MAAM,QAAQ,MAAM,GAAG;AAEzB,uBAAe;AAAA,MACjB,WAAW,OAAO,WAAW,MAAM,QAAQ,OAAO,OAAO,GAAG;AAE1D,uBAAe,OAAO;AAAA,MACxB,OAAO;AAEL,uBAAe,CAAC,MAAM;AAAA,MACxB;AAAA,IACF,QAAQ;AACN,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAGA,UAAM,qBAAqB,CAACC,aAAmC;AAE7D,UAAIA,aAAY,iBAAkB,QAAO;AACzC,UAAIA,aAAY,gBAAiB,QAAO;AAGxC,YAAM,QAAQA,SAAQ,MAAM,gBAAgB;AAC5C,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,UAAU,SAAS,MAAM,CAAC,CAAC;AACjC,UAAI,YAAY,KAAM,QAAO;AAC7B,UAAI,YAAY,IAAK,QAAO;AAC5B,UAAI,YAAY,MAAO,QAAO;AAC9B,UAAI,YAAY,MAAO,QAAO;AAC9B,UAAI,YAAY,GAAI,QAAO;AAC3B,UAAI,YAAY,GAAI,QAAO;AAC3B,aAAO;AAAA,IACT;AAGA,UAAM,eAAe,aAClB,IAAI,OAAK,mBAAmB,EAAE,OAAO,CAAC,EACtC,OAAO,CAAC,MAAmB,MAAM,IAAI;AAGxC,UAAM,qBAAqB,QAAQ;AACnC,QAAI;AAEJ,QAAI,oBAAoB;AAEtB,UAAI,CAAC,aAAa,SAAS,kBAAkB,GAAG;AAC9C,cAAM,IAAI;AAAA,UACR,0BAA0B,kBAAkB;AAAA,kBACzB,aAAa,KAAK,IAAI,CAAC;AAAA,QAC5C;AAAA,MACF;AACA,sBAAgB;AAAA,IAClB,OAAO;AAEL,UAAI,aAAa,WAAW,KAAK,aAAa,CAAC,MAAM,QAAQ;AAE3D,wBAAgB;AAAA,MAClB,OAAO;AACL,cAAM,IAAI;AAAA,UACR,mBAAmB,aAAa,KAAK,IAAI,CAAC;AAAA;AAAA,QAE5C;AAAA,MACF;AAAA,IACF;AAGA,QAAI,kBAAkB,YAAY,kBAAkB,iBAAiB;AACnE,YAAM,cAAc;AACpB,YAAMA,WAAU,gBAAgB,WAAW,mBAAmB;AAC9D,YAAMC,OAAM,aAAa,KAAK,OAAK,EAAE,YAAYD,QAAO;AAExD,UAAI,CAACC,MAAK;AACR,cAAM,IAAI,MAAM,0CAA0C,aAAa,EAAE;AAAA,MAC3E;AAEA,aAAO,MAAM,KAAK,oBAAoB,YAAY,SAAS,QAAQA,MAAK,aAAa,OAAO;AAAA,IAC9F;AAGA,UAAM,YAAY;AAClB,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,UAAU,UAAU,MAAM,OAAO;AACvC,UAAM,MAAM,aAAa,KAAK,OAAK,EAAE,WAAW,WAAW,EAAE,YAAY,OAAO;AAEhF,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,0CAA0C,SAAS,EAAE;AAAA,IACvE;AAIA,UAAM,YAAY,IAAI,UAAU,IAAI;AACpC,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,wCAAwC;AAAA,IAC1D;AACA,UAAM,SAAS,OAAO,SAAS,IAAI;AACnC,SAAK,YAAY,MAAM;AAGvB,QAAI,QAAqB,QAAQ,SAAS;AAG1C,QAAI,QAAQ,YAAY;AACtB,YAAM,WAAW,MAAM,KAAK,WAAW;AACvC,UAAI,SAAS,QAAQ,QAAQ;AAC3B,gBAAQ;AAAA,MACV,WAAW,SAAS,QAAQ,QAAQ;AAClC,gBAAQ;AAAA,MACV,OAAO;AACL,cAAM,IAAI,MAAM,+BAA+B,MAAM,UAAU,SAAS,IAAI,WAAW,SAAS,IAAI,OAAO;AAAA,MAC7G;AAAA,IACF;AAIA,QAAI,UAAU,QAAQ;AACpB,YAAM,WAAW,MAAM,KAAK,WAAW;AACvC,UAAI,SAAS,SAAS,MAAQ;AAC5B,cAAM,IAAI;AAAA,UACR,iEACqB,SAAS,OAAO,QAAQ,CAAC,CAAC;AAAA,QAEjD;AAAA,MACF;AACA,cAAQ,IAAI,iFAAuE;AAAA,IACrF,OAAO;AACL,cAAQ,IAAI,gCAAgC,MAAM,IAAI,KAAK,YAAY;AAAA,IACzE;AAGA,QAAI,cAAc,SAAS,cAAc,eAAe;AACtD,cAAQ,IAAI,mDAAmD;AAC/D,YAAMC,SAAQ,IAAI,SAAS,IAAI;AAC/B,UAAI,CAACA,QAAO;AACV,cAAM,IAAI,MAAM,+CAA+C;AAAA,MACjE;AAEA,YAAM,aAAc,IAAI,OAAe;AACvC,UAAI,CAAC,YAAY;AACf,cAAM,IAAI,MAAM,iFAAiF;AAAA,MACnG;AACA,aAAO,MAAM,KAAK,iBAAiB,YAAY,SAAS,QAAQ;AAAA,QAC9D,IAAIA;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACX,GAAG,OAAO;AAAA,IACZ;AAIA,UAAM,QAAQ,IAAI,SAAS,IAAI;AAC/B,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAGA,UAAM,iBAAkB,IAAI,SAAS,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,OAC5E,EAAE,MAAM,IAAI,MAAM,MAAgB,SAAU,IAAI,MAAM,WAAsB,IAAI,IAChF;AAEJ,UAAM,gBAAgB,MAAM,KAAK,YAAY,OAAO,QAAQ,OAAO,OAAO,cAAc;AAGxF,UAAM,cAAc,MAAM,OAAO,KAAK;AAKtC,UAAM,QAAS,IAAI,SAAS,OAAO,IAAI,UAAU,WAC7C,IAAI,QACJ;AAAA,MACE,MAAO,YAAoB,cAAc;AAAA,MACzC,SAAS;AAAA,IACX;AAEJ,UAAM,UAAU;AAAA,MACd,aAAa;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA;AAAA,MACT,UAAU;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,QACA,mBAAmB,IAAI,qBAAqB;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AACA,UAAM,gBAAgB,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,QAAQ;AAG5E,YAAQ,IAAI,4CAA4C;AACxD,UAAM,kBAAuB,QAAQ,UACjC,EAAE,SAAS,GAAG,OAAO,IACrB,EAAE,SAAS,OAAO;AACtB,QAAI,QAAQ,OAAO;AACjB,sBAAgB,QAAQ,QAAQ;AAAA,IAClC;AACA,UAAM,UAAU,MAAM,MAAM,YAAY;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,CAAC,cAAc,GAAG;AAAA,MACpB;AAAA,MACA,MAAM,KAAK,UAAU,eAAe;AAAA,IACtC,CAAC;AAED,UAAM,SAAS,MAAM,QAAQ,KAAK;AAElC,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,IAAI,MAAM,OAAO,SAAS,0BAA0B;AAAA,IAC5D;AAGA,SAAK,eAAe,MAAM;AAE1B,YAAQ,IAAI,gCAAgC,OAAO,SAAS,UAAU,SAAS,EAAE;AAGjF,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBACZ,YACA,SACA,QACA,eACA,UAAsB,CAAC,GACO;AAE9B,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,eAAe;AAC5D,UAAM,EAAE,oBAAoB,oBAAoB,KAAK,IAAI,MAAM,OAAO,MAAM;AAC5E,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,aAAa;AACpD,UAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,YAAY;AAG7C,UAAM,aAAa,KAAK,WAAY;AACpC,UAAM,UAAU,oBAAoB,UAAU;AAE9C,YAAQ,IAAI,wCAAwC;AACpD,YAAQ,IAAI,uBAAuB,QAAQ,OAAO,EAAE;AAGpD,UAAM,iBAAiB,CAAC,QAAgB,QAA+B;AACrE,YAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,GAAG,GAAG,cAAc,GAAG,CAAC;AAC9D,aAAO,QAAQ,MAAM,CAAC,IAAI;AAAA,IAC5B;AAEA,UAAM,cAAc,eAAe,eAAe,IAAI;AACtD,UAAM,SAAS,eAAe,eAAe,QAAQ;AACrD,UAAM,QAAQ,eAAe,eAAe,OAAO;AACnD,UAAM,aAAa,eAAe,eAAe,SAAS;AAE1D,QAAI,WAAW,SAAS;AACtB,YAAM,IAAI,MAAM,+BAA+B,MAAM,EAAE;AAAA,IACzD;AAEA,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAGA,UAAM,cAAc,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,OAAO;AACtE,UAAM,iBAAiB,KAAK,MAAM,WAAW;AAE7C,UAAM,EAAE,QAAQ,UAAU,WAAW,cAAc,IAAI;AACvD,UAAM,UAAU,eAAe,WAAW;AAC1C,UAAM,gBAAgB,OAAO,MAAM,IAAI;AAEvC,YAAQ,IAAI,wBAAwB,aAAa,OAAO,SAAS,EAAE;AAGnE,SAAK,YAAY,aAAa;AAG9B,YAAQ,IAAI,4CAA4C;AAExD,UAAM,aAAa,EAAE,GAAG,eAAe,UAAU,SAA0B;AAE3E,UAAM,eAAe,mBAAmB;AAAA,MACtC,OAAO;AAAA,MACP,WAAW,KAAK,gCAAgC;AAAA,IAClD,CAAC;AAED,UAAM,eAAe,mBAAmB;AAAA,MACtC;AAAA,MACA,OAAO;AAAA,MACP,WAAW,KAAK,gCAAgC;AAAA,IAClD,CAAC;AAGD,UAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,cAAc;AAAA,MACxD,IAAI;AAAA,MACJ,QAAQ,OAAO,MAAM;AAAA,MACrB,OAAO;AAAA,IACT,CAAC;AAED,YAAQ,IAAI,2BAA2B,MAAM,EAAE;AAG/C,UAAM,aAAa,0BAA0B,EAAE,MAAM,OAAO,CAAC;AAC7D,YAAQ,IAAI,mDAAmD;AAG/D,UAAM,aAAa;AAAA,MACjB,WAAW;AAAA,QACT,IAAI;AAAA,QACJ;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACX;AAAA,MACA,SAAS,EAAE,MAAM,QAAQ,MAAM,OAAO;AAAA,MACtC,QAAQ,kBAAkB,OAAO,IAAI,QAAQ,OAAO;AAAA,IACtD;AAEA,UAAM,gBAAgB,OAAO,KAAK,KAAK,UAAU,UAAU,CAAC,EACzD,SAAS,QAAQ,EACjB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AAGpB,UAAM,YAAY,QAAQ,UACtB,EAAE,SAAS,GAAG,QAAQ,OAAO,iBAAiB,IAC9C,EAAE,SAAS,QAAQ,OAAO,iBAAiB;AAE/C,UAAM,UAAU,MAAM,MAAM,YAAY;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,WAAW,aAAa;AAAA,MAC3C;AAAA,MACA,MAAM,KAAK,UAAU,SAAS;AAAA,IAChC,CAAC;AAED,UAAM,SAAS,MAAM,QAAQ,KAAK;AAElC,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,IAAI,MAAM,OAAO,SAAS,6BAA6B;AAAA,IAC/D;AAGA,SAAK,eAAe,aAAa;AAEjC,YAAQ,IAAI,qBAAqB;AACjC,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,iBACZ,YACA,SACA,QACA,gBAQA,UAAsB,CAAC,GACO;AAC9B,UAAM,EAAE,IAAI,QAAQ,OAAO,WAAW,OAAO,QAAQ,IAAI;AACzD,UAAM,cAAc,MAAM,OAAO,KAAK;AAGtC,UAAM,WAAW,IAAI,qBAAO,gBAAgB,MAAM,GAAG;AACrD,UAAM,YAAY,MAAM,KAAK,eAAe,YAAY,SAAS,SAAS,QAAQ;AAClF,UAAM,iBAAiB,OAAO,KAAK,MAAM,SAAU,MAAM,YAAY,QAAS,CAAC;AAE/E,QAAI,YAAY,gBAAgB;AAE9B,YAAM,gBAAgB,MAAM,SAAS,WAAW,KAAK,OAAQ,OAAO;AACpE,YAAM,gBAAgB,qBAAO,WAAW,QAAQ;AAEhD,UAAI,gBAAgB,eAAe;AACjC,cAAM,YAAY,WAAW,qBAAO,YAAY,aAAa,CAAC,EAAE,QAAQ,CAAC;AACzE,cAAM,YAAY,cAAc;AAEhC,YAAI,WAAW;AACb,gBAAM,IAAI;AAAA,YACR;AAAA;AAAA,mBACoB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,UAI/B;AAAA,QACF,OAAO;AACL,gBAAM,IAAI;AAAA,YACR;AAAA;AAAA,kBACmB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kCAMO,SAAS,cAAc,OAAO;AAAA,UACnE;AAAA,QACF;AAAA,MACF;AAEA,YAAM,IAAI;AAAA,QACR,8BAA8B,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,oCACb,SAAS,cAAc,OAAO;AAAA,MACrE;AAAA,IACF;AAGA,UAAM,YAAY,OAAO,KAAK,MAAM,SAAU,MAAM,YAAY,QAAS,CAAC,EAAE,SAAS;AAGrF,UAAM,SAAS;AAAA,MACb,MAAM,KAAK,OAAQ;AAAA,MACnB;AAAA,MACA,QAAQ;AAAA,MACR,OAAO,YAAY;AAAA,MACnB;AAAA,MACA,OAAO,KAAK,IAAI;AAAA;AAAA,MAChB,UAAU,KAAK,IAAI,IAAI;AAAA;AAAA,IACzB;AAGA,UAAM,SAAS;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,MAAM;AAAA,IACjB;AAGA,UAAM,QAAQ;AAAA,MACZ,eAAe;AAAA,QACb,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,QAChC,EAAE,MAAM,MAAM,MAAM,UAAU;AAAA,QAC9B,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,QAClC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,WAAW,MAAM,SAAS;AAAA,QAClC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,MACtC;AAAA,IACF;AAGA,YAAQ,IAAI,0CAA0C;AACtD,UAAM,YAAY,MAAM,KAAK,OAAQ,cAAc,QAAQ,OAAO,MAAM;AAGxE,UAAM,UAAU,UAAU,MAAM,OAAO;AACvC,UAAM,UAAU;AAAA,MACd,aAAa;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,QACP,QAAQ;AAAA,UACN,GAAG;AAAA,UACH;AAAA,QACF;AAAA,QACA,SAAS,MAAM;AAAA,MACjB;AAAA,MACA,UAAU;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,mBAAmB;AAAA,MACrB;AAAA,IACF;AAEA,UAAM,gBAAgB,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,QAAQ;AAG5E,YAAQ,IAAI,2CAA2C;AACvD,UAAM,iBAAiB,QAAQ,UAC3B,EAAE,SAAS,GAAG,QAAQ,OAAO,UAAU,IACvC,EAAE,SAAS,QAAQ,OAAO,UAAU;AACxC,UAAM,UAAU,MAAM,MAAM,YAAY;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa;AAAA,MACf;AAAA,MACA,MAAM,KAAK,UAAU,cAAc;AAAA,IACrC,CAAC;AAED,UAAM,SAAS,MAAM,QAAQ,KAAK;AAElC,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,IAAI,MAAM,OAAO,SAAS,oBAAoB;AAAA,IACtD;AAGA,SAAK,eAAe,MAAM;AAE1B,YAAQ,IAAI,0CAA0C;AACtD,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,oBACZ,YACA,SACA,QACA,cACA,OACA,UAAsB,CAAC,GACO;AAE9B,UAAM,eAAe,iBAAiB,KAAK,SAAS;AACpD,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF;AAEA,UAAM,SAAS,OAAO,aAAa,MAAM;AACzC,UAAM,aAAa,SAAS;AAG5B,SAAK,YAAY,UAAU;AAE3B,YAAQ,IAAI,wCAAwC,UAAU,OAAO;AAGrE,QAAI,CAAC,aAAa,OAAO;AACvB,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAGA,UAAM,iBAAkB,aAAa,OAAe;AACpD,UAAM,iBAAiB,iBAAiB,IAAI,uBAAU,cAAc,IAAI;AAExE,QAAI,gBAAgB;AAClB,cAAQ,IAAI,2CAA2C;AAAA,IACzD;AAGA,UAAM,kBAAkB,IAAI,uBAAU,aAAa,KAAK;AACxD,UAAM,cAAc,MAAM;AAAA,MACxB,aAAa;AAAA,MACb;AAAA,MACA,OAAO,MAAM;AAAA,MACb;AAAA,MACA;AAAA;AAAA,IACF;AAGA,QAAI,gBAAgB;AAElB,kBAAY,YAAY,YAAY;AAAA,IACtC,OAAO;AAEL,kBAAY,KAAK,YAAY;AAAA,IAC/B;AACA,UAAM,WAAW,YAAY,UAAU,EAAE,sBAAsB,MAAM,CAAC,EAAE,SAAS,QAAQ;AAEzF,YAAQ,IAAI,qDAAqD;AAGjE,UAAM,UAAU,UAAU,WAAW,mBAAmB;AACxD,UAAM,UAAU;AAAA,MACd,aAAa;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,QACP,mBAAmB;AAAA,QACnB,QAAQ,aAAa,UAAU,SAAS;AAAA,QACxC;AAAA,MACF;AAAA,MACA,UAAU;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA,OAAO,aAAa;AAAA,QACpB,QAAQ,aAAa;AAAA,QACrB,OAAO,aAAa;AAAA,QACpB,mBAAmB;AAAA,MACrB;AAAA,IACF;AAEA,UAAM,gBAAgB,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,QAAQ;AAG5E,UAAM,oBAAoB,QAAQ,UAC9B,EAAE,SAAS,GAAG,QAAQ,MAAM,IAC5B,EAAE,SAAS,QAAQ,MAAM;AAC7B,UAAM,UAAU,MAAM,MAAM,YAAY;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa;AAAA,MACf;AAAA,MACA,MAAM,KAAK,UAAU,iBAAiB;AAAA,IACxC,CAAC;AAED,UAAM,SAAS,MAAM,QAAQ,KAAK;AAElC,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,IAAI,MAAM,OAAO,SAAS,uBAAuB;AAAA,IACzD;AAGA,SAAK,eAAe,UAAU;AAE9B,YAAQ,IAAI,6CAA6C;AACzD,QAAI,OAAO,SAAS,aAAa;AAC/B,YAAM,cAAc,UAAU,WAC1B,yBAAyB,OAAO,QAAQ,WAAW,KACnD,yBAAyB,OAAO,QAAQ,WAAW;AACvD,cAAQ,IAAI,2BAA2B,WAAW,EAAE;AAAA,IACtD;AAEA,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eACZ,cACA,SACA,UACiB;AACjB,UAAM,WAAW,IAAI,qBAAO;AAAA,MAC1B;AAAA,MACA,CAAC,2EAA2E;AAAA,MAC5E;AAAA,IACF;AACA,WAAO,MAAM,SAAS,UAAU,KAAK,OAAQ,SAAS,OAAO;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,YACZ,IACA,QACA,OACA,QAAqB,QACrB,gBACqE;AACrE,UAAM,aAAa;AACnB,UAAM,cAAc,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AACpD,UAAM,QAAQ,qBAAO,QAAQ,qBAAO,YAAY,EAAE,CAAC;AAEnD,UAAM,cAAc,MAAM,OAAO,KAAK;AACtC,UAAM,QAAQ,OAAO,KAAK,MAAM,SAAU,MAAM,YAAY,QAAS,CAAC,EAAE,SAAS;AAEjF,UAAM,gBAAsC;AAAA,MAC1C,MAAM,KAAK,OAAQ;AAAA,MACnB;AAAA,MACA;AAAA,MACA,YAAY,WAAW,SAAS;AAAA,MAChC,aAAa,YAAY,SAAS;AAAA,MAClC;AAAA,IACF;AAIA,UAAM,YAAY,gBAAgB,QAAS,YAAoB,eAAe,UAAU,SAAS,aAAa;AAC9G,UAAM,eAAe,gBAAgB,WAAW;AAChD,UAAM,SAAS;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,MAAM;AAAA,MACf,mBAAmB,YAAY;AAAA,IACjC;AAGA,UAAM,QAAQ;AAAA,MACZ,2BAA2B;AAAA,QACzB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,QAChC,EAAE,MAAM,MAAM,MAAM,UAAU;AAAA,QAC9B,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,QACjC,EAAE,MAAM,cAAc,MAAM,UAAU;AAAA,QACtC,EAAE,MAAM,eAAe,MAAM,UAAU;AAAA,QACvC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,MACnC;AAAA,IACF;AAEA,UAAM,YAAY,MAAM,KAAK,OAAQ,cAAc,QAAQ,OAAO,aAAa;AAE/E,WAAO,EAAE,eAAe,UAAU;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,QAAsB;AAExC,QAAI,SAAS,KAAK,OAAO,OAAO,UAAU;AACxC,YAAM,IAAI;AAAA,QACR,WAAW,MAAM,kCAAkC,KAAK,OAAO,OAAO,QAAQ;AAAA,MAChF;AAAA,IACF;AAGA,UAAM,SAAQ,oBAAI,KAAK,GAAE,SAAS,GAAG,GAAG,GAAG,CAAC;AAC5C,QAAI,QAAQ,KAAK,mBAAmB;AAClC,WAAK,gBAAgB;AACrB,WAAK,oBAAoB;AACzB,WAAK,aAAa;AAAA,IACpB;AAGA,QAAI,KAAK,gBAAgB,SAAS,KAAK,OAAO,OAAO,WAAW;AAC9D,YAAM,IAAI;AAAA,QACR,8BAA8B,KAAK,aAAa,OAAO,MAAM,OAAO,KAAK,OAAO,OAAO,SAAS;AAAA,MAClG;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAAsB;AAC3C,SAAK,iBAAiB;AACtB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAIQ,aAA2B;AACjC,UAAM,iBAAa,mBAAK,KAAK,WAAW,aAAa;AACrD,YAAI,uBAAW,UAAU,GAAG;AAC1B,YAAM,cAAU,yBAAa,YAAY,OAAO;AAChD,aAAO,EAAE,GAAG,gBAAgB,GAAG,KAAK,MAAM,OAAO,EAAE;AAAA,IACrD;AACA,WAAO,EAAE,GAAG,eAAe;AAAA,EAC7B;AAAA,EAEQ,aAAmB;AACzB,8BAAU,KAAK,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,UAAM,iBAAa,mBAAK,KAAK,WAAW,aAAa;AACrD,kCAAc,YAAY,KAAK,UAAU,KAAK,QAAQ,MAAM,CAAC,CAAC;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAqB;AAC3B,UAAM,mBAAe,mBAAK,KAAK,WAAW,eAAe;AACzD,YAAI,uBAAW,YAAY,GAAG;AAC5B,UAAI;AACF,cAAM,OAAO,KAAK,UAAM,yBAAa,cAAc,OAAO,CAAC;AAC3D,cAAM,SAAQ,oBAAI,KAAK,GAAE,SAAS,GAAG,GAAG,GAAG,CAAC;AAG5C,YAAI,KAAK,QAAQ,KAAK,SAAS,OAAO;AACpC,eAAK,gBAAgB,KAAK,UAAU;AACpC,eAAK,oBAAoB,KAAK;AAAA,QAChC,OAAO;AAEL,eAAK,gBAAgB;AACrB,eAAK,oBAAoB;AAAA,QAC3B;AAAA,MACF,QAAQ;AAEN,aAAK,gBAAgB;AACrB,aAAK,qBAAoB,oBAAI,KAAK,GAAE,SAAS,GAAG,GAAG,GAAG,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAqB;AAC3B,8BAAU,KAAK,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,UAAM,mBAAe,mBAAK,KAAK,WAAW,eAAe;AACzD,UAAM,OAAO;AAAA,MACX,MAAM,KAAK,sBAAqB,oBAAI,KAAK,GAAE,SAAS,GAAG,GAAG,GAAG,CAAC;AAAA,MAC9D,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK,IAAI;AAAA,IACtB;AACA,kCAAc,cAAc,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,EAC3D;AAAA,EAEQ,aAAgC;AACtC,UAAM,iBAAa,mBAAK,KAAK,WAAW,aAAa;AACrD,YAAI,uBAAW,UAAU,GAAG;AAE1B,UAAI;AACF,cAAM,YAAQ,qBAAS,UAAU;AACjC,cAAM,OAAO,MAAM,OAAO;AAC1B,YAAI,SAAS,KAAO;AAClB,kBAAQ,KAAK,6DAA6D,KAAK,SAAS,CAAC,CAAC,GAAG;AAC7F,kBAAQ,KAAK,0CAA0C;AACvD,oCAAU,YAAY,GAAK;AAAA,QAC7B;AAAA,MACF,SAAS,KAAK;AAAA,MAEd;AAEA,YAAM,cAAU,yBAAa,YAAY,OAAO;AAChD,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,KACL,WACA,SACwC;AACxC,8BAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAGxC,UAAM,SAAS,qBAAO,aAAa;AACnC,UAAM,aAAyB;AAAA,MAC7B,SAAS,OAAO;AAAA,MAChB,YAAY,OAAO;AAAA,MACnB,WAAW,KAAK,IAAI;AAAA,IACtB;AAGA,UAAM,iBAAa,mBAAK,WAAW,aAAa;AAChD,kCAAc,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAG9E,UAAM,SAAuB;AAAA,MAC3B,OAAO,QAAQ;AAAA,MACf,QAAQ;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,WAAW,QAAQ;AAAA,MACrB;AAAA,IACF;AACA,UAAM,iBAAa,mBAAK,WAAW,aAAa;AAChD,kCAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAEzD,WAAO,EAAE,SAAS,OAAO,SAAS,UAAU;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAsE;AAC1E,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,QAAI;AACJ,QAAI;AACF,cAAQ,SAAS,KAAK,OAAO,KAAqB;AAAA,IACpD,QAAQ;AACN,YAAM,IAAI,MAAM,kBAAkB,KAAK,OAAO,KAAK,EAAE;AAAA,IACvD;AAEA,UAAM,WAAW,IAAI,qBAAO,gBAAgB,MAAM,GAAG;AACrD,UAAM,WAAW,CAAC,oDAAoD;AAGtE,UAAM,CAAC,eAAe,aAAa,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,MAClE,SAAS,WAAW,KAAK,OAAO,OAAO;AAAA,MACvC,IAAI,qBAAO,SAAS,MAAM,OAAO,KAAK,SAAS,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAO,OAAO;AAAA,MAChG,IAAI,qBAAO,SAAS,MAAM,OAAO,KAAK,SAAS,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAO,OAAO;AAAA,IAClG,CAAC;AAED,WAAO;AAAA,MACL,MAAM,WAAW,qBAAO,YAAY,aAAa,MAAM,OAAO,KAAK,QAAQ,CAAC;AAAA,MAC5E,MAAM,WAAW,qBAAO,YAAY,aAAa,MAAM,OAAO,KAAK,QAAQ,CAAC;AAAA,MAC5E,QAAQ,WAAW,qBAAO,YAAY,aAAa,CAAC;AAAA,IACtD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAA4K;AAChL,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,UAAM,kBAAkC,CAAC,QAAQ,WAAW,gBAAgB,kBAAkB,OAAO,aAAa;AAClH,UAAM,WAAW,CAAC,oDAAoD;AACtE,UAAM,UAA4J,CAAC;AAGnK,UAAM,cAAc;AAAA,MAClB,SAAS;AAAA,MACT,UAAU;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,IACZ;AAGA,UAAM,QAAQ;AAAA,MACZ,gBAAgB,IAAI,OAAO,cAAc;AACvC,YAAI;AACF,gBAAM,QAAQ,SAAS,SAAS;AAChC,gBAAM,WAAW,IAAI,qBAAO,gBAAgB,MAAM,GAAG;AAErD,cAAI,cAAc,kBAAkB;AAElC,kBAAM,CAAC,eAAe,SAAS,UAAU,SAAS,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,cAC9E,SAAS,WAAW,KAAK,OAAQ,OAAO;AAAA,cACxC,IAAI,qBAAO,SAAS,YAAY,SAAS,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAQ,OAAO;AAAA,cAC3F,IAAI,qBAAO,SAAS,YAAY,UAAU,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAQ,OAAO;AAAA,cAC5F,IAAI,qBAAO,SAAS,YAAY,SAAS,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAQ,OAAO;AAAA,cAC3F,IAAI,qBAAO,SAAS,YAAY,UAAU,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAQ,OAAO;AAAA,YAC9F,CAAC;AAED,oBAAQ,SAAS,IAAI;AAAA,cACnB,MAAM,WAAW,qBAAO,YAAY,SAAS,CAAC,CAAC;AAAA;AAAA,cAC/C,MAAM,WAAW,qBAAO,YAAY,UAAU,CAAC,CAAC;AAAA;AAAA,cAChD,QAAQ,WAAW,qBAAO,YAAY,aAAa,CAAC;AAAA,cACpD,OAAO;AAAA,gBACL,SAAS,WAAW,qBAAO,YAAY,SAAS,CAAC,CAAC;AAAA,gBAClD,UAAU,WAAW,qBAAO,YAAY,UAAU,CAAC,CAAC;AAAA,gBACpD,SAAS,WAAW,qBAAO,YAAY,SAAS,CAAC,CAAC;AAAA,gBAClD,UAAU,WAAW,qBAAO,YAAY,UAAU,CAAC,CAAC;AAAA,cACtD;AAAA,YACF;AAAA,UACF,OAAO;AAEL,kBAAM,CAAC,eAAe,aAAa,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,cAClE,SAAS,WAAW,KAAK,OAAQ,OAAO;AAAA,cACxC,IAAI,qBAAO,SAAS,MAAM,OAAO,KAAK,SAAS,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAQ,OAAO;AAAA,cACjG,IAAI,qBAAO,SAAS,MAAM,OAAO,KAAK,SAAS,UAAU,QAAQ,EAAE,UAAU,KAAK,OAAQ,OAAO;AAAA,YACnG,CAAC;AAED,oBAAQ,SAAS,IAAI;AAAA,cACnB,MAAM,WAAW,qBAAO,YAAY,aAAa,MAAM,OAAO,KAAK,QAAQ,CAAC;AAAA,cAC5E,MAAM,WAAW,qBAAO,YAAY,aAAa,MAAM,OAAO,KAAK,QAAQ,CAAC;AAAA,cAC5E,QAAQ,WAAW,qBAAO,YAAY,aAAa,CAAC;AAAA,YACtD;AAAA,UACF;AAAA,QACF,SAAS,KAAK;AAEZ,kBAAQ,SAAS,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,EAAE;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,WACJ,KACA,UAGI,CAAC,GACS;AACd,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,YAAY;AACpC,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAGA,UAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,eAAe;AAC5D,UAAM,EAAE,oBAAoB,oBAAoB,KAAK,IAAI,MAAM,OAAO,MAAM;AAC5E,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,aAAa;AACpD,UAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,YAAY;AAG7C,UAAM,aAAa,KAAK,WAAW;AACnC,UAAM,UAAU,oBAAoB,UAAU;AAE9C,YAAQ,IAAI,qCAAqC,GAAG,EAAE;AACtD,YAAQ,IAAI,6BAA6B,QAAQ,OAAO,EAAE;AAG1D,UAAM,eAAe,MAAM,MAAM,KAAK;AAAA,MACpC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAG,QAAQ;AAAA,MACb;AAAA,MACA,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI;AAAA,IACtD,CAAC;AAGD,QAAI,aAAa,WAAW,KAAK;AAC/B,UAAI,aAAa,IAAI;AACnB,eAAO,aAAa,KAAK;AAAA,MAC3B;AACA,YAAM,YAAY,MAAM,aAAa,KAAK;AAC1C,YAAM,IAAI,MAAM,mBAAmB,aAAa,MAAM,MAAM,SAAS,EAAE;AAAA,IACzE;AAGA,UAAM,UAAU,aAAa,QAAQ,IAAI,kBAAkB;AAC3D,QAAI,CAAC,WAAW,CAAC,QAAQ,YAAY,EAAE,SAAS,SAAS,GAAG;AAC1D,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAEA,YAAQ,IAAI,kDAAkD;AAG9D,UAAM,iBAAiB,CAAC,QAAgB,QAA+B;AACrE,YAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,GAAG,GAAG,cAAc,GAAG,CAAC;AAC9D,aAAO,QAAQ,MAAM,CAAC,IAAI;AAAA,IAC5B;AAEA,UAAM,cAAc,eAAe,SAAS,IAAI;AAChD,UAAM,SAAS,eAAe,SAAS,QAAQ;AAC/C,UAAM,QAAQ,eAAe,SAAS,OAAO;AAC7C,UAAM,aAAa,eAAe,SAAS,SAAS;AAEpD,QAAI,WAAW,SAAS;AACtB,YAAM,IAAI,MAAM,+BAA+B,MAAM,EAAE;AAAA,IACzD;AAEA,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAGA,UAAM,cAAc,OAAO,KAAK,YAAY,QAAQ,EAAE,SAAS,OAAO;AACtE,UAAM,iBAAiB,KAAK,MAAM,WAAW;AAE7C,YAAQ,IAAI,+BAA+B,cAAc;AAEzD,UAAM,EAAE,QAAQ,UAAU,WAAW,cAAc,IAAI;AACvD,UAAM,UAAU,eAAe,WAAW;AAG1C,YAAQ,IAAI,oDAAoD,OAAO,MAAM;AAC7E,YAAQ,IAAI,sBAAsB,MAAM,SAAS,SAAS,EAAE;AAG5D,UAAM,aAAa,EAAE,GAAG,eAAe,UAAU,SAA0B;AAE3E,UAAM,eAAe,mBAAmB;AAAA,MACtC,OAAO;AAAA,MACP,WAAW,KAAK,gCAAgC;AAAA,IAClD,CAAC;AAED,UAAM,eAAe,mBAAmB;AAAA,MACtC;AAAA,MACA,OAAO;AAAA,MACP,WAAW,KAAK,gCAAgC;AAAA,IAClD,CAAC;AAGD,UAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,cAAc;AAAA,MACxD,IAAI;AAAA,MACJ,QAAQ,OAAO,MAAM;AAAA,MACrB,OAAO;AAAA,IACT,CAAC;AAED,YAAQ,IAAI,gCAAgC,MAAM,EAAE;AAGpD,YAAQ,IAAI,wCAAwC;AACpD,UAAM,aAAa,0BAA0B,EAAE,MAAM,OAAO,CAAC;AAC7D,YAAQ,IAAI,mCAAmC;AAG/C,UAAM,YAAY;AAAA,MAChB,IAAI;AAAA,MACJ;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAEA,UAAM,aAAa;AAAA,MACjB;AAAA,MACA,SAAS,EAAE,MAAM,QAAQ,MAAM,OAAO;AAAA,MACtC,QAAQ,kBAAkB,OAAO,IAAI,QAAQ,OAAO;AAAA,IACtD;AAEA,UAAM,gBAAgB,OAAO,KAAK,KAAK,UAAU,UAAU,CAAC,EACzD,SAAS,QAAQ,EACjB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,EAAE;AAEpB,YAAQ,IAAI,gDAAgD;AAG5D,UAAM,eAAe,MAAM,MAAM,KAAK;AAAA,MACpC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,WAAW,aAAa;AAAA,QACzC,GAAG,QAAQ;AAAA,MACb;AAAA,MACA,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI;AAAA,IACtD,CAAC;AAED,QAAI,CAAC,aAAa,IAAI;AACpB,YAAM,YAAY,MAAM,aAAa,KAAK;AAC1C,YAAM,IAAI,MAAM,gCAAgC,aAAa,MAAM,MAAM,SAAS,EAAE;AAAA,IACtF;AAEA,YAAQ,IAAI,iDAAiD;AAC7D,WAAO,aAAa,KAAK;AAAA,EAC3B;AACF;","names":["import_fs","import_os","import_path","import_web3","bs58","import_web3","import_spl_token","import_web3","network","req","payTo"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts","../../src/client/node/index.ts","../../src/chains/index.ts","../../src/wallet/solana.ts","../../src/chains/solana.ts","../../src/facilitators/solana.ts","../../src/client/core/types.ts","../../src/client/core/chain-map.ts","../../src/client/core/base64.ts","../../src/client/core/eip3009.ts","../../src/client/core/bnb-intent.ts","../../src/client/node/signer.ts"],"sourcesContent":["/**\n * Backwards-compatibility re-export for `src/client/index.ts`.\n *\n * The Node client implementation moved to `./node/index.ts` in 1.6.0 as part\n * of the Web Client work (see docs/WEB-CLIENT-DESIGN.md, Phase 1).\n *\n * This file keeps `import { MoltsPayClient } from 'moltspay/client'` working\n * for existing callers and preserves the published `dist/client/index.*`\n * bundle path declared in package.json exports.\n */\n\nexport * from './node/index.js';\n","/**\n * MoltsPay Client - Pay for AI Agent services\n * \n * Uses x402 protocol for gasless, pay-for-success payments.\n * \n * Usage:\n * const client = new MoltsPayClient(); // Loads from ~/.moltspay/\n * const services = await client.getServices('http://provider:3000');\n * const result = await client.pay('http://provider:3000', 'text-to-video', { prompt: '...' });\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, statSync, chmodSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Wallet, ethers } from 'ethers';\nimport { getChain, type ChainName, type EvmChainName, type TokenSymbol, type ChainConfig } from '../../chains/index.js';\nimport { SOLANA_CHAINS, type SolanaChainName } from '../../chains/solana.js';\nimport { loadSolanaWallet, getSolanaAddress } from '../../wallet/solana.js';\nimport { createSolanaPaymentTransaction } from '../../facilitators/solana.js';\nimport { Connection, PublicKey, Keypair } from '@solana/web3.js';\nimport {\n ClientConfig,\n WalletData,\n ServicesResponse,\n MoltsPayClientOptions,\n} from '../types.js';\nimport {\n X402_VERSION,\n PAYMENT_REQUIRED_HEADER,\n PAYMENT_HEADER,\n type X402PaymentRequirements,\n type EIP3009Authorization,\n networkToChainName as coreNetworkToChainName,\n type ChainName as CoreChainName,\n buildEIP3009TypedData,\n buildBnbIntentTypedData,\n} from '../core/index.js';\nimport type { PaymentSigner } from '../signer.js';\nimport { NodeSigner } from './signer.js';\n\nexport * from '../types.js';\n\nexport interface PayOptions {\n /** Token to pay with (default: USDC, or auto-select based on balance) */\n token?: TokenSymbol;\n /** Auto-select token based on balance (default: false) */\n autoSelect?: boolean;\n /** Chain to pay on */\n chain?: 'base' | 'polygon' | 'base_sepolia' | 'tempo_moderato' | 'bnb' | 'bnb_testnet' | 'solana' | 'solana_devnet';\n /** Send raw data at top level instead of wrapped in { params } */\n rawData?: boolean;\n}\n\n// x402 constants, X402PaymentRequirements, and EIP3009Authorization\n// are re-exported from `../core/index.js` (imported above).\n\nconst DEFAULT_CONFIG: ClientConfig = {\n chain: 'base',\n limits: {\n maxPerTx: 100,\n maxPerDay: 1000,\n },\n};\n\nexport class MoltsPayClient {\n private configDir: string;\n private config: ClientConfig;\n private walletData: WalletData | null = null;\n private wallet: Wallet | null = null;\n private signer: PaymentSigner | null = null;\n private todaySpending: number = 0;\n private lastSpendingReset: number = 0;\n\n constructor(options: MoltsPayClientOptions = {}) {\n this.configDir = options.configDir || join(homedir(), '.moltspay');\n this.config = this.loadConfig();\n this.walletData = this.loadWallet();\n this.loadSpending(); // Load persisted spending data\n\n if (this.walletData) {\n this.wallet = new Wallet(this.walletData.privateKey);\n // Signer abstracts all signing. Solana key is loaded lazily so we don't\n // touch disk for callers that only pay on EVM chains.\n const configDir = this.configDir;\n this.signer = new NodeSigner(this.wallet, {\n getSolanaKeypair: () => loadSolanaWallet(configDir),\n });\n }\n }\n\n /**\n * Check if client is initialized (has wallet)\n */\n get isInitialized(): boolean {\n return this.wallet !== null;\n }\n\n /**\n * Get wallet address\n */\n get address(): string | null {\n return this.wallet?.address || null;\n }\n\n /**\n * Get wallet instance (for direct operations like approvals)\n */\n getWallet(): Wallet | null {\n return this.wallet;\n }\n\n /**\n * Get current config\n */\n getConfig(): ClientConfig {\n return { ...this.config };\n }\n\n /**\n * Update config\n */\n updateConfig(updates: Partial<ClientConfig['limits']>): void {\n if (updates.maxPerTx !== undefined) {\n this.config.limits.maxPerTx = updates.maxPerTx;\n }\n if (updates.maxPerDay !== undefined) {\n this.config.limits.maxPerDay = updates.maxPerDay;\n }\n this.saveConfig();\n }\n\n /**\n * Get services from a provider\n */\n async getServices(serverUrl: string): Promise<ServicesResponse> {\n // Normalize URL - don't append /services if already present\n const normalizedUrl = serverUrl.replace(/\\/(services|api\\/services|registry\\/services)\\/?$/, '');\n \n // Try /services first (standard provider endpoint)\n const endpoints = ['/services', '/api/services', '/registry/services'];\n \n for (const endpoint of endpoints) {\n try {\n const res = await fetch(`${normalizedUrl}${endpoint}`);\n if (!res.ok) continue;\n \n const contentType = res.headers.get('content-type') || '';\n if (!contentType.includes('application/json')) continue;\n \n return await res.json() as ServicesResponse;\n } catch {\n continue;\n }\n }\n \n throw new Error(`Failed to get services: no valid endpoint found at ${normalizedUrl}`);\n }\n\n /**\n * Pay for a service and get the result (x402 protocol)\n * \n * This is GASLESS for the client - server pays gas to claim payment.\n * This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.\n * \n * @param serverUrl - Server URL\n * @param service - Service ID\n * @param params - Service parameters\n * @param options - Payment options (token selection)\n */\n async pay(\n serverUrl: string,\n service: string,\n params: Record<string, any>,\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n if (!this.wallet || !this.walletData) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Step 1: Discover service endpoint\n console.log(`[MoltsPay] Requesting service: ${service}`);\n let executeUrl = `${serverUrl}/execute`; // Default fallback\n \n try {\n const services = await this.getServices(serverUrl);\n const svc = services.services?.find((s: any) => s.id === service);\n if (svc?.endpoint) {\n // Use the endpoint from service discovery (for Cloudflare Workers, etc.)\n executeUrl = `${serverUrl}${svc.endpoint}`;\n console.log(`[MoltsPay] Using service endpoint: ${svc.endpoint}`);\n }\n } catch {\n // Fall back to /execute if service discovery fails\n }\n \n // Build request body - raw mode sends data at top level, standard mode wraps in { params }\n let requestBody: any;\n if (options.rawData) {\n // Raw mode: { service, chain, ...params } - user's data at top level\n requestBody = { service, ...params };\n } else {\n // Standard mode: { service, params } - wrapped format\n requestBody = { service, params };\n }\n if (options.chain) {\n requestBody.chain = options.chain;\n }\n const initialRes = await fetch(executeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n });\n\n // If not 402, check for success or error\n if (initialRes.status !== 402) {\n const data = await initialRes.json() as any;\n if (initialRes.ok && data.result) {\n return data.result;\n }\n throw new Error(data.error || 'Unexpected response');\n }\n\n // Step 2: Detect protocol from 402 response\n // MPP uses WWW-Authenticate header, x402 uses X-Payment-Required header\n const wwwAuthHeader = initialRes.headers.get('www-authenticate');\n const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);\n \n // If WWW-Authenticate with Payment scheme, use MPP flow\n if (wwwAuthHeader && wwwAuthHeader.toLowerCase().includes('payment')) {\n console.log('[MoltsPay] Detected MPP protocol, using Tempo flow...');\n return await this.handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options);\n }\n \n if (!paymentRequiredHeader) {\n throw new Error('Missing payment header (x-payment-required or www-authenticate)');\n }\n\n let requirements: X402PaymentRequirements[];\n try {\n const decoded = Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8');\n const parsed = JSON.parse(decoded);\n \n // Handle both v1 (array) and v2 (object with accepts) formats\n if (Array.isArray(parsed)) {\n // v1 format: direct array of requirements\n requirements = parsed;\n } else if (parsed.accepts && Array.isArray(parsed.accepts)) {\n // v2 format: { x402Version: 2, accepts: [...] }\n requirements = parsed.accepts;\n } else {\n // Single requirement object\n requirements = [parsed];\n }\n } catch {\n throw new Error('Invalid x-payment-required header');\n }\n\n // Get server's accepted chains (uses shared core mapping — same output as 1.5.x)\n const serverChains = requirements\n .map(r => coreNetworkToChainName(r.network))\n .filter((c): c is CoreChainName => c !== null);\n\n // Determine which chain to use\n const userSpecifiedChain = options.chain;\n let selectedChain: string;\n\n if (userSpecifiedChain) {\n // User specified --chain, validate it's accepted by server\n if (!serverChains.includes(userSpecifiedChain)) {\n throw new Error(\n `Server doesn't accept '${userSpecifiedChain}'.\\n` +\n `Server accepts: ${serverChains.join(', ')}`\n );\n }\n selectedChain = userSpecifiedChain;\n } else {\n // No --chain provided\n if (serverChains.length === 1 && serverChains[0] === 'base') {\n // Only default to base if server ONLY accepts base\n selectedChain = 'base';\n } else {\n throw new Error(\n `Server accepts: ${serverChains.join(', ')}\\n` +\n `Please specify: --chain <chain_name>`\n );\n }\n }\n\n // Handle Solana chains separately\n if (selectedChain === 'solana' || selectedChain === 'solana_devnet') {\n const solanaChain = selectedChain as SolanaChainName;\n const network = solanaChain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n const req = requirements.find(r => r.network === network);\n \n if (!req) {\n throw new Error(`Failed to find payment requirement for ${selectedChain}`);\n }\n \n return await this.handleSolanaPayment(executeUrl, service, params, req, solanaChain, options);\n }\n\n // EVM chain handling\n const chainName = selectedChain as EvmChainName;\n const chain = getChain(chainName);\n const network = `eip155:${chain.chainId}`;\n const req = requirements.find(r => r.scheme === 'exact' && r.network === network);\n\n if (!req) {\n throw new Error(`Failed to find payment requirement for ${chainName}`);\n }\n\n // Step 3: Check limits\n // v2 uses 'amount', v1 uses 'maxAmountRequired'\n const amountRaw = req.amount || req.maxAmountRequired;\n if (!amountRaw) {\n throw new Error('Missing amount in payment requirements');\n }\n const amount = Number(amountRaw) / 1e6;\n this.checkLimits(amount);\n\n // Determine which token to use\n let token: TokenSymbol = options.token || 'USDC';\n \n // Auto-select token based on balance if requested\n if (options.autoSelect) {\n const balances = await this.getBalance();\n if (balances.usdc >= amount) {\n token = 'USDC';\n } else if (balances.usdt >= amount) {\n token = 'USDT';\n } else {\n throw new Error(`Insufficient balance: need $${amount}, have ${balances.usdc} USDC / ${balances.usdt} USDT`);\n }\n }\n\n // USDT does not support gasless transfers (no EIP-2612 permit)\n // It requires on-chain approve + transfer, meaning the user pays gas\n if (token === 'USDT') {\n const balances = await this.getBalance();\n if (balances.native < 0.0001) {\n throw new Error(\n `USDT requires ETH for gas (~$0.01 on Base). ` +\n `Your ETH balance: ${balances.native.toFixed(6)} ETH. ` +\n `Please add a small amount of ETH to your wallet, or use USDC (gasless).`\n );\n }\n console.log(`[MoltsPay] ⚠️ USDT requires gas (~$0.01). Proceeding with payment...`);\n } else {\n console.log(`[MoltsPay] Signing payment: $${amount} ${token} (gasless)`);\n }\n\n // BNB chains use intent-based flow (pre-approval + intent signature)\n if (chainName === 'bnb' || chainName === 'bnb_testnet') {\n console.log(`[MoltsPay] Using BNB intent-based payment flow...`);\n const payTo = req.payTo || req.resource;\n if (!payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n // Get spender address from server response (dynamic, not hardcoded)\n const bnbSpender = (req.extra as any)?.bnbSpender;\n if (!bnbSpender) {\n throw new Error('Server did not provide bnbSpender address. Server may not support BNB payments.');\n }\n return await this.handleBNBPayment(executeUrl, service, params, {\n to: payTo,\n amount,\n token,\n chainName,\n chain,\n spender: bnbSpender,\n }, options);\n }\n\n // Step 4: Sign EIP-3009 authorization (GASLESS - just signing)\n // payTo is the recipient address (v2 format)\n const payTo = req.payTo || req.resource; // fallback for v1 compatibility\n if (!payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n \n // Use server's extra field for domain info (contains correct EIP-712 domain for the token on this network)\n const domainOverride = (req.extra && typeof req.extra === 'object' && req.extra.name) \n ? { name: req.extra.name as string, version: (req.extra.version as string) || '2' }\n : undefined;\n \n const authorization = await this.signEIP3009(payTo, amount, chain, token, domainOverride);\n\n // Get token-specific info for accepted field\n const tokenConfig = chain.tokens[token];\n\n // Step 5: Create x402 payment payload (v2 requires scheme, network, payload, AND accepted)\n // Use server's extra field if provided (contains correct EIP-712 domain for the token on this network)\n // Fall back to local config for backward compatibility\n const extra = (req.extra && typeof req.extra === 'object') \n ? req.extra \n : {\n name: (tokenConfig as any).eip712Name || 'USD Coin',\n version: '2',\n };\n \n const payload = {\n x402Version: X402_VERSION,\n scheme: 'exact',\n network,\n payload: authorization, // { authorization: {...}, signature: \"0x...\" }\n accepted: {\n scheme: 'exact',\n network,\n asset: tokenConfig.address,\n amount: amountRaw,\n payTo,\n maxTimeoutSeconds: req.maxTimeoutSeconds || 300,\n extra,\n },\n };\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Step 6: Retry with payment header\n console.log(`[MoltsPay] Sending request with payment...`);\n const paidRequestBody: any = options.rawData\n ? { service, ...params }\n : { service, params };\n if (options.chain) {\n paidRequestBody.chain = options.chain;\n }\n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [PAYMENT_HEADER]: paymentHeader,\n },\n body: JSON.stringify(paidRequestBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Service execution failed');\n }\n\n // Update spending tracking\n this.recordSpending(amount);\n\n console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || 'claimed'}`);\n \n // Support both MoltsPay Server format ({ result: ... }) and direct response format\n return result.result || result;\n }\n\n /**\n * Handle MPP (Machine Payments Protocol) payment flow\n * Called when pay() detects WWW-Authenticate header in 402 response\n */\n private async handleMPPPayment(\n executeUrl: string,\n service: string,\n params: Record<string, any>,\n wwwAuthHeader: string,\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n // Dynamic imports for ESM-only packages\n const { privateKeyToAccount } = await import('viem/accounts');\n const { createWalletClient, createPublicClient, http } = await import('viem');\n const { tempoModerato } = await import('viem/chains');\n const { Actions } = await import('viem/tempo');\n\n // Get private key from wallet data\n const privateKey = this.walletData!.privateKey as `0x${string}`;\n const account = privateKeyToAccount(privateKey);\n\n console.log(`[MoltsPay] Using MPP protocol on Tempo`);\n console.log(`[MoltsPay] Account: ${account.address}`);\n\n // Parse WWW-Authenticate: Payment id=\"...\", method=\"tempo\", request=\"...\"\n const parseAuthParam = (header: string, key: string): string | null => {\n const match = header.match(new RegExp(`${key}=\"([^\"]+)\"`, 'i'));\n return match ? match[1] : null;\n };\n\n const challengeId = parseAuthParam(wwwAuthHeader, 'id');\n const method = parseAuthParam(wwwAuthHeader, 'method');\n const realm = parseAuthParam(wwwAuthHeader, 'realm');\n const requestB64 = parseAuthParam(wwwAuthHeader, 'request');\n\n if (method !== 'tempo') {\n throw new Error(`Unsupported payment method: ${method}`);\n }\n\n if (!requestB64) {\n throw new Error('Missing request in WWW-Authenticate');\n }\n\n // Decode payment request\n const requestJson = Buffer.from(requestB64, 'base64').toString('utf-8');\n const paymentRequest = JSON.parse(requestJson);\n \n const { amount, currency, recipient, methodDetails } = paymentRequest;\n const chainId = methodDetails?.chainId || 42431;\n const amountDisplay = Number(amount) / 1e6;\n\n console.log(`[MoltsPay] Payment: $${amountDisplay} to ${recipient}`);\n\n // Check limits\n this.checkLimits(amountDisplay);\n\n // Execute transfer on Tempo\n console.log(`[MoltsPay] Sending transaction on Tempo...`);\n\n const tempoChain = { ...tempoModerato, feeToken: currency as `0x${string}` };\n \n const publicClient = createPublicClient({\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n const walletClient = createWalletClient({\n account,\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n // TIP-20 transfer\n const txHash = await Actions.token.transfer(walletClient, {\n to: recipient as `0x${string}`,\n amount: BigInt(amount),\n token: currency as `0x${string}`,\n });\n\n console.log(`[MoltsPay] Transaction: ${txHash}`);\n\n // Wait for confirmation\n await publicClient.waitForTransactionReceipt({ hash: txHash });\n console.log(`[MoltsPay] Confirmed! Retrying with credential...`);\n\n // Build credential\n const credential = {\n challenge: {\n id: challengeId,\n realm,\n method: 'tempo',\n intent: 'charge',\n request: paymentRequest,\n },\n payload: { hash: txHash, type: 'hash' },\n source: `did:pkh:eip155:${chainId}:${account.address}`,\n };\n\n const credentialB64 = Buffer.from(JSON.stringify(credential))\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n\n // Retry with credential - respect rawData option\n const retryBody = options.rawData \n ? { service, ...params, chain: 'tempo_moderato' }\n : { service, params, chain: 'tempo_moderato' };\n \n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Payment ${credentialB64}`,\n },\n body: JSON.stringify(retryBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Payment verification failed');\n }\n\n // Update spending tracking\n this.recordSpending(amountDisplay);\n\n console.log(`[MoltsPay] Success!`);\n return result.result || result;\n }\n\n /**\n * Handle BNB Chain payment flow (pre-approval + intent signature)\n * \n * Flow:\n * 1. Check client has approved server wallet (done via `moltspay init`)\n * 2. Sign EIP-712 payment intent (no gas, just signature)\n * 3. Send intent to server\n * 4. Server executes service\n * 5. Server calls transferFrom if successful (pay-for-success)\n */\n private async handleBNBPayment(\n executeUrl: string,\n service: string,\n params: Record<string, any>,\n paymentDetails: {\n to: string;\n amount: number;\n token: TokenSymbol;\n chainName: ChainName;\n chain: ChainConfig;\n spender: string;\n },\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n const { to, amount, token, chainName, chain, spender } = paymentDetails;\n const tokenConfig = chain.tokens[token];\n \n // Check approval status\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);\n const amountWeiCheck = BigInt(Math.floor(amount * (10 ** tokenConfig.decimals)));\n \n if (allowance < amountWeiCheck) {\n // Check if user has enough BNB for gas to approve\n const nativeBalance = await provider.getBalance(this.wallet!.address);\n const minGasBalance = ethers.parseEther('0.0005'); // ~0.0005 BNB minimum for approval\n \n if (nativeBalance < minGasBalance) {\n const nativeBNB = parseFloat(ethers.formatEther(nativeBalance)).toFixed(4);\n const isTestnet = chainName === 'bnb_testnet';\n \n if (isTestnet) {\n throw new Error(\n `❌ Insufficient tBNB for approval transaction\\n\\n` +\n ` Current tBNB: ${nativeBNB}\\n` +\n ` Required: ~0.001 tBNB\\n\\n` +\n ` Get testnet tokens: npx moltspay faucet --chain bnb_testnet\\n` +\n ` (Gives USDC + tBNB for gas)`\n );\n } else {\n throw new Error(\n `❌ Insufficient BNB for approval transaction\\n\\n` +\n ` Current BNB: ${nativeBNB}\\n` +\n ` Required: ~0.001 BNB (~$0.60)\\n\\n` +\n ` To get BNB:\\n` +\n ` • Withdraw from Binance/exchange to your wallet\\n` +\n ` • Most exchanges include BNB dust with withdrawals\\n\\n` +\n ` After funding, run:\\n` +\n ` npx moltspay approve --chain ${chainName} --spender ${spender}`\n );\n }\n }\n \n throw new Error(\n `Insufficient allowance for ${spender.slice(0, 10)}...\\n` +\n `Run: npx moltspay approve --chain ${chainName} --spender ${spender}`\n );\n }\n \n // Convert amount to wei (BNB uses 18 decimals)\n const amountWei = BigInt(Math.floor(amount * (10 ** tokenConfig.decimals))).toString();\n\n // Build PaymentIntent envelope via shared core builder, sign through PaymentSigner.\n const intentNonce = Date.now();\n const intentDeadline = Date.now() + 3600000; // 1 hour\n const envelope = buildBnbIntentTypedData({\n from: this.wallet!.address,\n to,\n amount: amountWei,\n tokenAddress: tokenConfig.address,\n service,\n nonce: intentNonce,\n deadline: intentDeadline,\n chainId: chain.chainId,\n });\n\n console.log(`[MoltsPay] Signing BNB payment intent...`);\n const signature = await this.signer!.signTypedData(envelope);\n const intent = envelope.message;\n\n // Create x402 payment payload with BNB-specific format\n const network = `eip155:${chain.chainId}`;\n const payload = {\n x402Version: 2,\n scheme: 'exact',\n network,\n payload: {\n intent: {\n ...intent,\n signature,\n },\n chainId: chain.chainId,\n },\n accepted: {\n scheme: 'exact',\n network,\n asset: tokenConfig.address,\n amount: amountWei,\n payTo: to,\n maxTimeoutSeconds: 300,\n },\n };\n\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Send request with payment - respect rawData option\n console.log(`[MoltsPay] Sending BNB payment request...`);\n const bnbRequestBody = options.rawData\n ? { service, ...params, chain: chainName }\n : { service, params, chain: chainName };\n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Payment': paymentHeader,\n },\n body: JSON.stringify(bnbRequestBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'BNB payment failed');\n }\n\n // Update spending tracking\n this.recordSpending(amount);\n\n console.log(`[MoltsPay] Success! BNB payment settled.`);\n return result.result || result;\n }\n\n /**\n * Handle Solana payment flow\n * \n * Solana uses SPL token transfers with pay-for-success model:\n * 1. Client creates and signs a transfer transaction\n * 2. Server submits the transaction after service completes\n */\n private async handleSolanaPayment(\n executeUrl: string,\n service: string,\n params: Record<string, any>,\n requirements: X402PaymentRequirements,\n chain: SolanaChainName,\n options: PayOptions = {}\n ): Promise<Record<string, any>> {\n // Load Solana wallet\n const solanaWallet = loadSolanaWallet(this.configDir);\n if (!solanaWallet) {\n throw new Error('No Solana wallet found. Run: npx moltspay init --chain solana_devnet');\n }\n\n const amount = Number(requirements.amount);\n const amountUSDC = amount / 1e6;\n \n // Check limits\n this.checkLimits(amountUSDC);\n\n console.log(`[MoltsPay] Creating Solana payment: $${amountUSDC} USDC`);\n\n // Validate payTo address\n if (!requirements.payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n\n // Check for gasless mode (server pays fees)\n const solanaFeePayer = (requirements.extra as any)?.solanaFeePayer;\n const feePayerPubkey = solanaFeePayer ? new PublicKey(solanaFeePayer) : undefined;\n \n if (feePayerPubkey) {\n console.log(`[MoltsPay] Gasless mode: server pays fees`);\n }\n\n // Create the transfer transaction\n const recipientPubkey = new PublicKey(requirements.payTo);\n const transaction = await createSolanaPaymentTransaction(\n solanaWallet.publicKey,\n recipientPubkey,\n BigInt(amount),\n chain,\n feePayerPubkey // Optional fee payer for gasless mode\n );\n\n // Sign the transaction through PaymentSigner (partial sign in gasless mode).\n // Serializing before signing keeps the Web Client (Phase 4) wallet-adapter\n // flow identical to this one.\n const unsignedBase64 = transaction\n .serialize({ requireAllSignatures: false, verifySignatures: false })\n .toString('base64');\n const signedTx = await this.signer!.signSolanaTransaction!({\n transactionBase64: unsignedBase64,\n partialSign: !!feePayerPubkey,\n });\n\n console.log(`[MoltsPay] Transaction signed, sending to server...`);\n\n // Create x402 payload with Solana-specific format\n const network = chain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n const payload = {\n x402Version: 2,\n scheme: 'exact',\n network,\n payload: {\n signedTransaction: signedTx,\n sender: solanaWallet.publicKey.toBase58(),\n chain,\n },\n accepted: {\n scheme: 'exact',\n network,\n asset: requirements.asset,\n amount: requirements.amount,\n payTo: requirements.payTo,\n maxTimeoutSeconds: 300,\n },\n };\n\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Send request with payment - respect rawData option\n const solanaRequestBody = options.rawData\n ? { service, ...params, chain }\n : { service, params, chain };\n const paidRes = await fetch(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Payment': paymentHeader,\n },\n body: JSON.stringify(solanaRequestBody),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Solana payment failed');\n }\n\n // Update spending tracking\n this.recordSpending(amountUSDC);\n\n console.log(`[MoltsPay] Success! Solana payment settled.`);\n if (result.payment?.transaction) {\n const explorerUrl = chain === 'solana' \n ? `https://solscan.io/tx/${result.payment.transaction}`\n : `https://solscan.io/tx/${result.payment.transaction}?cluster=devnet`;\n console.log(`[MoltsPay] Transaction: ${explorerUrl}`);\n }\n\n return result.result || result;\n }\n\n /**\n * Check ERC20 allowance for a spender\n */\n private async checkAllowance(\n tokenAddress: string,\n spender: string,\n provider: ethers.JsonRpcProvider\n ): Promise<bigint> {\n const contract = new ethers.Contract(\n tokenAddress,\n ['function allowance(address owner, address spender) view returns (uint256)'],\n provider\n );\n return await contract.allowance(this.wallet!.address, spender);\n }\n\n /**\n * Sign EIP-3009 transferWithAuthorization (GASLESS)\n * This only signs - no on-chain transaction, no gas needed.\n * Supports both USDC and USDT.\n *\n * Delegates typed-data construction to `core/eip3009.ts` and the signature\n * itself to `this.signer`. That way Web Client (Phase 4) can reuse the same\n * flow with an EIP-1193 signer without duplicating typed-data layout.\n */\n private async signEIP3009(\n to: string,\n amount: number,\n chain: { chainId: number; tokens: Record<TokenSymbol, { address: string; decimals: number }> },\n token: TokenSymbol = 'USDC',\n domainOverride?: { name: string; version: string }\n ): Promise<{ authorization: EIP3009Authorization; signature: string }> {\n const tokenConfig = chain.tokens[token];\n const value = BigInt(Math.floor(amount * (10 ** tokenConfig.decimals))).toString();\n const nonce = ethers.hexlify(ethers.randomBytes(32));\n\n // EIP-712 domain - use server's domain info if provided (handles mainnet vs testnet differences)\n // Fall back to local token config for backward compatibility.\n const tokenName = domainOverride?.name || (tokenConfig as any).eip712Name || (token === 'USDC' ? 'USD Coin' : 'Tether USD');\n const tokenVersion = domainOverride?.version || '2';\n\n const envelope = buildEIP3009TypedData({\n from: this.wallet!.address,\n to,\n value,\n nonce,\n chainId: chain.chainId,\n tokenAddress: tokenConfig.address,\n tokenName,\n tokenVersion,\n });\n\n const signature = await this.signer!.signTypedData(envelope);\n return { authorization: envelope.message, signature };\n }\n\n /**\n * Check spending limits\n */\n private checkLimits(amount: number): void {\n // Check per-tx limit\n if (amount > this.config.limits.maxPerTx) {\n throw new Error(\n `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`\n );\n }\n\n // Reset daily spending if new day\n const today = new Date().setHours(0, 0, 0, 0);\n if (today > this.lastSpendingReset) {\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n this.saveSpending(); // Persist reset\n }\n\n // Check daily limit\n if (this.todaySpending + amount > this.config.limits.maxPerDay) {\n throw new Error(\n `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`\n );\n }\n }\n\n /**\n * Record spending and persist to disk\n */\n private recordSpending(amount: number): void {\n this.todaySpending += amount;\n this.saveSpending();\n }\n\n // --- Config & Wallet Management ---\n\n private loadConfig(): ClientConfig {\n const configPath = join(this.configDir, 'config.json');\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) };\n }\n return { ...DEFAULT_CONFIG };\n }\n\n private saveConfig(): void {\n mkdirSync(this.configDir, { recursive: true });\n const configPath = join(this.configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n }\n\n /**\n * Load spending data from disk\n */\n private loadSpending(): void {\n const spendingPath = join(this.configDir, 'spending.json');\n if (existsSync(spendingPath)) {\n try {\n const data = JSON.parse(readFileSync(spendingPath, 'utf-8'));\n const today = new Date().setHours(0, 0, 0, 0);\n \n // Only load if it's from today\n if (data.date && data.date === today) {\n this.todaySpending = data.amount || 0;\n this.lastSpendingReset = data.date;\n } else {\n // Data is from a previous day, reset\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n }\n } catch {\n // Ignore parse errors, start fresh\n this.todaySpending = 0;\n this.lastSpendingReset = new Date().setHours(0, 0, 0, 0);\n }\n }\n }\n\n /**\n * Save spending data to disk\n */\n private saveSpending(): void {\n mkdirSync(this.configDir, { recursive: true });\n const spendingPath = join(this.configDir, 'spending.json');\n const data = {\n date: this.lastSpendingReset || new Date().setHours(0, 0, 0, 0),\n amount: this.todaySpending,\n updatedAt: Date.now(),\n };\n writeFileSync(spendingPath, JSON.stringify(data, null, 2));\n }\n\n private loadWallet(): WalletData | null {\n const walletPath = join(this.configDir, 'wallet.json');\n if (existsSync(walletPath)) {\n // POSIX-only: Windows reports synthesized mode bits and chmod can't\n // express NTFS ACLs, so the check is meaningless there.\n if (process.platform !== 'win32') {\n try {\n const stats = statSync(walletPath);\n const mode = stats.mode & 0o777;\n if (mode !== 0o600) {\n console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);\n console.warn(`[MoltsPay] Fixing permissions to 0600...`);\n chmodSync(walletPath, 0o600);\n }\n } catch {\n /* ignored */\n }\n }\n\n const content = readFileSync(walletPath, 'utf-8');\n return JSON.parse(content);\n }\n return null;\n }\n\n /**\n * Initialize a new wallet (called by CLI)\n */\n static init(\n configDir: string,\n options: { chain: string; maxPerTx: number; maxPerDay: number }\n ): { address: string; configDir: string } {\n mkdirSync(configDir, { recursive: true });\n\n // Create wallet\n const wallet = Wallet.createRandom();\n const walletData: WalletData = {\n address: wallet.address,\n privateKey: wallet.privateKey,\n createdAt: Date.now(),\n };\n\n // Save wallet with secure permissions (0o600 = owner read/write only)\n const walletPath = join(configDir, 'wallet.json');\n writeFileSync(walletPath, JSON.stringify(walletData, null, 2), { mode: 0o600 });\n\n // Save config\n const config: ClientConfig = {\n chain: options.chain,\n limits: {\n maxPerTx: options.maxPerTx,\n maxPerDay: options.maxPerDay,\n },\n };\n const configPath = join(configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n return { address: wallet.address, configDir };\n }\n\n /**\n * Get wallet balance (USDC, USDT, and native token) on default chain\n */\n async getBalance(): Promise<{ usdc: number; usdt: number; native: number }> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n let chain;\n try {\n chain = getChain(this.config.chain as EvmChainName);\n } catch {\n throw new Error(`Unknown chain: ${this.config.chain}`);\n }\n\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n const tokenAbi = ['function balanceOf(address) view returns (uint256)'];\n\n // Get all balances in parallel\n const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([\n provider.getBalance(this.wallet.address),\n new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),\n new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address),\n ]);\n\n return {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),\n usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\n }\n\n /**\n * Get wallet balances on all supported chains (Base + Polygon + Tempo)\n */\n async getAllBalances(): Promise<Record<string, { usdc: number; usdt: number; native: number; tempo?: { pathUSD: number; alphaUSD: number; betaUSD: number; thetaUSD: number } }>> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n const supportedChains: EvmChainName[] = ['base', 'polygon', 'base_sepolia', 'tempo_moderato', 'bnb', 'bnb_testnet'];\n const tokenAbi = ['function balanceOf(address) view returns (uint256)'];\n const results: Record<string, { usdc: number; usdt: number; native: number; tempo?: { pathUSD: number; alphaUSD: number; betaUSD: number; thetaUSD: number } }> = {};\n\n // Tempo testnet token addresses\n const tempoTokens = {\n pathUSD: '0x20c0000000000000000000000000000000000000',\n alphaUSD: '0x20c0000000000000000000000000000000000001',\n betaUSD: '0x20c0000000000000000000000000000000000002',\n thetaUSD: '0x20c0000000000000000000000000000000000003',\n };\n\n // Query all chains in parallel\n await Promise.all(\n supportedChains.map(async (chainName) => {\n try {\n const chain = getChain(chainName);\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n\n if (chainName === 'tempo_moderato') {\n // Tempo: fetch all 4 testnet tokens\n const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([\n provider.getBalance(this.wallet!.address),\n new ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet!.address),\n ]);\n\n results[chainName] = {\n usdc: parseFloat(ethers.formatUnits(pathUSD, 6)), // pathUSD as default USDC\n usdt: parseFloat(ethers.formatUnits(alphaUSD, 6)), // alphaUSD as default USDT\n native: parseFloat(ethers.formatEther(nativeBalance)),\n tempo: {\n pathUSD: parseFloat(ethers.formatUnits(pathUSD, 6)),\n alphaUSD: parseFloat(ethers.formatUnits(alphaUSD, 6)),\n betaUSD: parseFloat(ethers.formatUnits(betaUSD, 6)),\n thetaUSD: parseFloat(ethers.formatUnits(thetaUSD, 6)),\n },\n };\n } else {\n // Other chains: fetch USDC and USDT\n const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([\n provider.getBalance(this.wallet!.address),\n new ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet!.address),\n new ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet!.address),\n ]);\n\n results[chainName] = {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),\n usdt: parseFloat(ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\n }\n } catch (err) {\n // If chain query fails, show zeros\n results[chainName] = { usdc: 0, usdt: 0, native: 0 };\n }\n })\n );\n\n return results;\n }\n\n /**\n * Pay for a service using MPP (Machine Payments Protocol)\n * \n * This implements the MPP flow manually for EOA wallets:\n * 1. Request service → get 402 with WWW-Authenticate\n * 2. Parse payment requirements\n * 3. Execute transfer on Tempo chain\n * 4. Retry with transaction hash as credential\n * \n * @param url - Full URL of the MPP-enabled endpoint\n * @param options - Request options (body, headers)\n * @returns Response from the service\n */\n async payWithMPP(\n url: string,\n options: {\n body?: any;\n headers?: Record<string, string>;\n } = {}\n ): Promise<any> {\n if (!this.wallet || !this.walletData) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Dynamic imports for ESM-only packages\n const { privateKeyToAccount } = await import('viem/accounts');\n const { createWalletClient, createPublicClient, http } = await import('viem');\n const { tempoModerato } = await import('viem/chains');\n const { Actions } = await import('viem/tempo');\n\n // Get private key from wallet data\n const privateKey = this.walletData.privateKey as `0x${string}`;\n const account = privateKeyToAccount(privateKey);\n\n console.log(`[MoltsPay] Making MPP request to: ${url}`);\n console.log(`[MoltsPay] Using account: ${account.address}`);\n\n // Step 1: Initial request to get 402 with payment requirements\n const initResponse = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n\n // If not 402, handle directly\n if (initResponse.status !== 402) {\n if (initResponse.ok) {\n return initResponse.json();\n }\n const errorText = await initResponse.text();\n throw new Error(`Request failed (${initResponse.status}): ${errorText}`);\n }\n\n // Step 2: Parse WWW-Authenticate header\n const wwwAuth = initResponse.headers.get('www-authenticate');\n if (!wwwAuth || !wwwAuth.toLowerCase().includes('payment')) {\n throw new Error('No WWW-Authenticate Payment challenge in 402 response');\n }\n\n console.log(`[MoltsPay] Got 402, parsing payment challenge...`);\n\n // Parse WWW-Authenticate: Payment id=\"...\", method=\"tempo\", request=\"...\"\n const parseAuthParam = (header: string, key: string): string | null => {\n const match = header.match(new RegExp(`${key}=\"([^\"]+)\"`, 'i'));\n return match ? match[1] : null;\n };\n\n const challengeId = parseAuthParam(wwwAuth, 'id');\n const method = parseAuthParam(wwwAuth, 'method');\n const realm = parseAuthParam(wwwAuth, 'realm');\n const requestB64 = parseAuthParam(wwwAuth, 'request');\n\n if (method !== 'tempo') {\n throw new Error(`Unsupported payment method: ${method}`);\n }\n\n if (!requestB64) {\n throw new Error('Missing request in WWW-Authenticate');\n }\n\n // Decode payment request\n const requestJson = Buffer.from(requestB64, 'base64').toString('utf-8');\n const paymentRequest = JSON.parse(requestJson);\n \n console.log(`[MoltsPay] Payment request:`, paymentRequest);\n\n const { amount, currency, recipient, methodDetails } = paymentRequest;\n const chainId = methodDetails?.chainId || 42431;\n\n // Step 3: Execute transfer on Tempo\n console.log(`[MoltsPay] Executing transfer on Tempo (chainId: ${chainId})...`);\n console.log(`[MoltsPay] Amount: ${amount}, To: ${recipient}`);\n\n // Create viem client for Tempo (with feeToken for gas-free transactions)\n const tempoChain = { ...tempoModerato, feeToken: currency as `0x${string}` };\n \n const publicClient = createPublicClient({\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n const walletClient = createWalletClient({\n account,\n chain: tempoChain,\n transport: http('https://rpc.moderato.tempo.xyz'),\n });\n\n // Use viem's Tempo Actions for TIP-20 transfer\n const txHash = await Actions.token.transfer(walletClient, {\n to: recipient as `0x${string}`,\n amount: BigInt(amount),\n token: currency as `0x${string}`,\n });\n\n console.log(`[MoltsPay] Transaction sent: ${txHash}`);\n\n // Wait for confirmation\n console.log(`[MoltsPay] Waiting for confirmation...`);\n await publicClient.waitForTransactionReceipt({ hash: txHash });\n console.log(`[MoltsPay] Transaction confirmed!`);\n\n // Step 4: Build credential and retry\n const challenge = {\n id: challengeId,\n realm,\n method: 'tempo',\n intent: 'charge',\n request: paymentRequest,\n };\n\n const credential = {\n challenge,\n payload: { hash: txHash, type: 'hash' },\n source: `did:pkh:eip155:${chainId}:${account.address}`,\n };\n\n const credentialB64 = Buffer.from(JSON.stringify(credential))\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, ''); // base64url without padding\n\n console.log(`[MoltsPay] Retrying with payment credential...`);\n\n // Retry with credential\n const paidResponse = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Payment ${credentialB64}`,\n ...options.headers,\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n\n if (!paidResponse.ok) {\n const errorText = await paidResponse.text();\n throw new Error(`Payment verification failed (${paidResponse.status}): ${errorText}`);\n }\n\n console.log(`[MoltsPay] Payment verified! Service completed.`);\n return paidResponse.json();\n }\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName, EvmChainName, TokenSymbol } from '../types/index.js';\n\nexport const CHAINS: Record<EvmChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n tokens: {\n USDC: {\n address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USD Coin', // EIP-712 domain name\n },\n USDT: {\n address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // deprecated, for backward compat\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-bor-rpc.publicnode.com',\n tokens: {\n USDC: {\n address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',\n decimals: 6,\n symbol: 'USDT',\n eip712Name: '(PoS) Tether USD', // Polygon uses this name\n },\n },\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n tokens: {\n USDC: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USDC', // Testnet USDC uses 'USDC' not 'USD Coin'\n },\n USDT: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Same as USDC on testnet (no official USDT)\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'USDC', // Uses same contract as USDC\n },\n },\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n // ============ Tempo Testnet (Moderato) ============\n tempo_moderato: {\n name: 'Tempo Moderato',\n chainId: 42431,\n rpc: 'https://rpc.moderato.tempo.xyz',\n tokens: {\n // TIP-20 stablecoins on Tempo testnet (from mppx SDK)\n // Note: Tempo uses USD as native gas token, not ETH\n USDC: {\n address: '0x20c0000000000000000000000000000000000000', // pathUSD - primary testnet stablecoin\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'pathUSD',\n },\n USDT: {\n address: '0x20c0000000000000000000000000000000000001', // alphaUSD\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'alphaUSD',\n },\n },\n usdc: '0x20c0000000000000000000000000000000000000',\n explorer: 'https://explore.testnet.tempo.xyz/address/',\n explorerTx: 'https://explore.testnet.tempo.xyz/tx/',\n avgBlockTime: 0.5, // ~500ms finality\n },\n // ============ BNB Chain Testnet ============\n bnb_testnet: {\n name: 'BNB Testnet',\n chainId: 97,\n rpc: 'https://data-seed-prebsc-1-s1.binance.org:8545',\n tokens: {\n // Note: BNB uses 18 decimals for stablecoins (unlike Base/Polygon which use 6)\n // Using official Binance-Peg testnet tokens\n USDC: {\n address: '0x64544969ed7EBf5f083679233325356EbE738930', // Testnet USDC\n decimals: 18,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0x337610d27c682E347C9cD60BD4b3b107C9d34dDd', // Testnet USDT\n decimals: 18,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x64544969ed7EBf5f083679233325356EbE738930',\n explorer: 'https://testnet.bscscan.com/address/',\n explorerTx: 'https://testnet.bscscan.com/tx/',\n avgBlockTime: 3,\n // BNB-specific: requires approval for pay-for-success flow\n requiresApproval: true,\n },\n // ============ BNB Chain Mainnet ============\n bnb: {\n name: 'BNB Smart Chain',\n chainId: 56,\n rpc: 'https://bsc-dataseed.binance.org',\n tokens: {\n // Note: BNB uses 18 decimals for stablecoins\n USDC: {\n address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',\n decimals: 18,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0x55d398326f99059fF775485246999027B3197955',\n decimals: 18,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',\n explorer: 'https://bscscan.com/address/',\n explorerTx: 'https://bscscan.com/tx/',\n avgBlockTime: 3,\n // BNB-specific: requires approval for pay-for-success flow\n requiresApproval: true,\n },\n};\n\n/**\n * Get token address for a chain\n */\nexport function getTokenAddress(chainName: EvmChainName, token: TokenSymbol): string {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n const tokenConfig = chain.tokens[token];\n if (!tokenConfig) {\n throw new Error(`Token ${token} not supported on ${chainName}`);\n }\n return tokenConfig.address;\n}\n\n/**\n * Get token config for a chain\n */\nexport function getTokenConfig(chainName: EvmChainName, token: TokenSymbol) {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n return chain.tokens[token];\n}\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: EvmChainName): ChainConfig {\n const config = CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * List all supported EVM chains\n */\nexport function listChains(): EvmChainName[] {\n return Object.keys(CHAINS) as EvmChainName[];\n}\n\n/**\n * Get chain config by chainId\n */\nexport function getChainById(chainId: number): ChainConfig | undefined {\n return Object.values(CHAINS).find(c => c.chainId === chainId);\n}\n\n/**\n * ERC20 ABI (minimal, only required methods)\n */\nexport const ERC20_ABI = [\n 'function balanceOf(address owner) view returns (uint256)',\n 'function transfer(address to, uint256 amount) returns (bool)',\n 'function approve(address spender, uint256 amount) returns (bool)',\n 'function allowance(address owner, address spender) view returns (uint256)',\n 'function decimals() view returns (uint8)',\n 'function symbol() view returns (string)',\n 'function name() view returns (string)',\n 'function nonces(address owner) view returns (uint256)',\n 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)',\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n 'event Approval(address indexed owner, address indexed spender, uint256 value)',\n];\n\nexport type { ChainConfig, ChainName, EvmChainName, TokenSymbol };\n","/**\n * Solana Wallet Management\n * \n * Separate from EVM wallets - uses ed25519 keypairs.\n * Stored in ~/.moltspay/wallet-solana.json\n */\n\nimport { Keypair, PublicKey, Connection, LAMPORTS_PER_SOL } from '@solana/web3.js';\nimport { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport bs58 from 'bs58';\nimport { getSolanaConnection, getUSDCMint, type SolanaChainName } from '../chains/solana.js';\n\nconst DEFAULT_CONFIG_DIR = join(homedir(), '.moltspay');\nconst SOLANA_WALLET_FILE = 'wallet-solana.json';\n\nexport interface SolanaWalletData {\n publicKey: string; // Base58 encoded\n secretKey: string; // Base58 encoded (should be encrypted in production)\n createdAt: string;\n}\n\n/**\n * Get the path to the Solana wallet file\n */\nexport function getSolanaWalletPath(configDir: string = DEFAULT_CONFIG_DIR): string {\n return join(configDir, SOLANA_WALLET_FILE);\n}\n\n/**\n * Check if Solana wallet exists\n */\nexport function solanaWalletExists(configDir: string = DEFAULT_CONFIG_DIR): boolean {\n return existsSync(getSolanaWalletPath(configDir));\n}\n\n/**\n * Load existing Solana wallet\n */\nexport function loadSolanaWallet(configDir: string = DEFAULT_CONFIG_DIR): Keypair | null {\n const walletPath = getSolanaWalletPath(configDir);\n \n if (!existsSync(walletPath)) {\n return null;\n }\n \n try {\n const data: SolanaWalletData = JSON.parse(readFileSync(walletPath, 'utf-8'));\n const secretKey = bs58.decode(data.secretKey);\n return Keypair.fromSecretKey(secretKey);\n } catch (error) {\n console.error('Failed to load Solana wallet:', error);\n return null;\n }\n}\n\n/**\n * Create new Solana wallet\n */\nexport function createSolanaWallet(configDir: string = DEFAULT_CONFIG_DIR): Keypair {\n // Ensure config directory exists\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n \n const keypair = Keypair.generate();\n const data: SolanaWalletData = {\n publicKey: keypair.publicKey.toBase58(),\n secretKey: bs58.encode(keypair.secretKey),\n createdAt: new Date().toISOString(),\n };\n \n const walletPath = getSolanaWalletPath(configDir);\n writeFileSync(walletPath, JSON.stringify(data, null, 2));\n \n return keypair;\n}\n\n/**\n * Get Solana wallet address (public key as Base58)\n */\nexport function getSolanaAddress(configDir: string = DEFAULT_CONFIG_DIR): string | null {\n const wallet = loadSolanaWallet(configDir);\n return wallet?.publicKey.toBase58() || null;\n}\n\n/**\n * Get SOL balance (native token for gas)\n */\nexport async function getSolanaBalance(\n address: string,\n chain: SolanaChainName\n): Promise<number> {\n const connection = getSolanaConnection(chain);\n const pubkey = new PublicKey(address);\n \n const balance = await connection.getBalance(pubkey);\n return balance / LAMPORTS_PER_SOL;\n}\n\n/**\n * Get USDC balance on Solana\n */\nexport async function getSolanaUSDCBalance(\n address: string,\n chain: SolanaChainName\n): Promise<number> {\n const connection = getSolanaConnection(chain);\n const owner = new PublicKey(address);\n const mint = getUSDCMint(chain);\n \n try {\n const ata = await getAssociatedTokenAddress(mint, owner);\n const account = await getAccount(connection, ata);\n // USDC has 6 decimals on Solana\n return Number(account.amount) / 1e6;\n } catch (error: any) {\n // Account doesn't exist = 0 balance\n if (error.name === 'TokenAccountNotFoundError' || \n error.message?.includes('could not find account')) {\n return 0;\n }\n throw error;\n }\n}\n\n/**\n * Get all Solana balances (SOL + USDC)\n */\nexport async function getSolanaBalances(\n address: string,\n chain: SolanaChainName\n): Promise<{ sol: number; usdc: number }> {\n const [sol, usdc] = await Promise.all([\n getSolanaBalance(address, chain),\n getSolanaUSDCBalance(address, chain),\n ]);\n \n return { sol, usdc };\n}\n\n/**\n * Request SOL airdrop (devnet only)\n */\nexport async function requestSolanaAirdrop(\n address: string,\n chain: SolanaChainName,\n amount: number = 1\n): Promise<string> {\n if (chain !== 'solana_devnet') {\n throw new Error('Airdrop only available on devnet');\n }\n \n const connection = getSolanaConnection(chain);\n const pubkey = new PublicKey(address);\n \n const signature = await connection.requestAirdrop(\n pubkey,\n amount * LAMPORTS_PER_SOL\n );\n \n // Wait for confirmation\n await connection.confirmTransaction(signature, 'confirmed');\n \n return signature;\n}\n\n/**\n * Validate Solana address format\n */\nexport function isValidSolanaAddress(address: string): boolean {\n try {\n new PublicKey(address);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Solana Chain Configuration\n * \n * Solana is NOT an EVM chain - uses different:\n * - Key format: ed25519 (EdDSA) vs secp256k1 (ECDSA)\n * - Address format: Base58 vs 0x hex\n * - Token standard: SPL vs ERC-20\n */\n\nimport { Connection, PublicKey } from '@solana/web3.js';\n\nexport interface SolanaChainConfig {\n name: string;\n cluster: 'mainnet-beta' | 'devnet' | 'testnet';\n rpc: string;\n explorer: string;\n explorerTx: string;\n tokens: {\n USDC: {\n mint: string;\n decimals: number;\n };\n };\n}\n\nexport type SolanaChainName = 'solana' | 'solana_devnet';\n\nexport const SOLANA_CHAINS: Record<SolanaChainName, SolanaChainConfig> = {\n solana: {\n name: 'Solana Mainnet',\n cluster: 'mainnet-beta',\n rpc: 'https://api.mainnet-beta.solana.com',\n explorer: 'https://solscan.io/account/',\n explorerTx: 'https://solscan.io/tx/',\n tokens: {\n USDC: {\n mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // Circle official USDC\n decimals: 6,\n },\n },\n },\n solana_devnet: {\n name: 'Solana Devnet',\n cluster: 'devnet',\n rpc: 'https://api.devnet.solana.com',\n explorer: 'https://solscan.io/account/',\n explorerTx: 'https://solscan.io/tx/',\n tokens: {\n USDC: {\n // Circle's devnet USDC (if not available, we'll deploy our own test token)\n mint: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',\n decimals: 6,\n },\n },\n },\n};\n\n/**\n * Get Solana RPC connection\n */\nexport function getSolanaConnection(chain: SolanaChainName): Connection {\n const config = SOLANA_CHAINS[chain];\n return new Connection(config.rpc, 'confirmed');\n}\n\n/**\n * Get USDC mint public key for a Solana chain\n */\nexport function getUSDCMint(chain: SolanaChainName): PublicKey {\n return new PublicKey(SOLANA_CHAINS[chain].tokens.USDC.mint);\n}\n\n/**\n * Get Solana chain config\n */\nexport function getSolanaChain(name: SolanaChainName): SolanaChainConfig {\n const config = SOLANA_CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported Solana chain: ${name}. Supported: ${Object.keys(SOLANA_CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * Check if a chain name is a Solana chain\n */\nexport function isSolanaChain(chain: string): chain is SolanaChainName {\n return chain === 'solana' || chain === 'solana_devnet';\n}\n\n/**\n * Get explorer URL for a Solana address\n */\nexport function getSolanaExplorerUrl(chain: SolanaChainName, address: string): string {\n const config = SOLANA_CHAINS[chain];\n const clusterParam = chain === 'solana_devnet' ? '?cluster=devnet' : '';\n return `${config.explorer}${address}${clusterParam}`;\n}\n\n/**\n * Get explorer URL for a Solana transaction\n */\nexport function getSolanaTxExplorerUrl(chain: SolanaChainName, signature: string): string {\n const config = SOLANA_CHAINS[chain];\n const clusterParam = chain === 'solana_devnet' ? '?cluster=devnet' : '';\n return `${config.explorerTx}${signature}${clusterParam}`;\n}\n","/**\n * Solana Facilitator\n * \n * Pay-for-success payment settlement for Solana SPL token transfers.\n * Unlike EVM chains, Solana doesn't have a third-party facilitator - \n * we verify and settle directly on-chain.\n * \n * Flow:\n * 1. Client signs a SPL token transfer authorization\n * 2. Server receives the signed transaction\n * 3. Server verifies the signature and amount\n * 4. Server submits the transaction to settle payment\n */\n\nimport { \n Connection, \n PublicKey, \n Transaction,\n VersionedTransaction,\n sendAndConfirmTransaction,\n Keypair,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getAccount,\n createAssociatedTokenAccountInstruction,\n TOKEN_PROGRAM_ID,\n} from '@solana/spl-token';\nimport { \n BaseFacilitator, \n type X402PaymentPayload, \n type X402PaymentRequirements,\n type VerifyResult,\n type SettleResult,\n type HealthCheckResult,\n} from './interface.js';\nimport { SOLANA_CHAINS, type SolanaChainName } from '../chains/solana.js';\n\n/**\n * Solana payment payload structure\n */\nexport interface SolanaPaymentPayload {\n /** Base58 encoded signed transaction */\n signedTransaction: string;\n /** Sender's public key (Base58) */\n sender: string;\n /** Chain: solana or solana_devnet */\n chain: SolanaChainName;\n}\n\n/**\n * Solana Facilitator configuration\n */\nexport interface SolanaFacilitatorConfig {\n /** Optional fee payer keypair for gasless transactions */\n feePayerKeypair?: Keypair;\n}\n\n/**\n * Solana Facilitator for pay-for-success payments\n * \n * Supports gasless mode: if feePayerKeypair is provided, server pays tx fees\n */\nexport class SolanaFacilitator extends BaseFacilitator {\n readonly name = 'solana';\n readonly displayName = 'Solana Direct';\n readonly supportedNetworks = ['solana:mainnet', 'solana:devnet'];\n\n private connections: Map<SolanaChainName, Connection> = new Map();\n private feePayerKeypair?: Keypair;\n\n constructor(config?: SolanaFacilitatorConfig) {\n super();\n this.feePayerKeypair = config?.feePayerKeypair;\n \n // Initialize connections\n for (const [chain, config] of Object.entries(SOLANA_CHAINS)) {\n this.connections.set(\n chain as SolanaChainName, \n new Connection(config.rpc, 'confirmed')\n );\n }\n \n if (this.feePayerKeypair) {\n console.log(`[SolanaFacilitator] Gasless mode enabled. Fee payer: ${this.feePayerKeypair.publicKey.toBase58()}`);\n }\n }\n \n /**\n * Get fee payer public key (for gasless transactions)\n */\n getFeePayerPubkey(): string | null {\n return this.feePayerKeypair?.publicKey.toBase58() || null;\n }\n\n private getConnection(chain: SolanaChainName): Connection {\n const conn = this.connections.get(chain);\n if (!conn) {\n throw new Error(`No connection for chain: ${chain}`);\n }\n return conn;\n }\n\n /**\n * Convert our chain name to network identifier\n */\n static chainToNetwork(chain: SolanaChainName): string {\n return chain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n }\n\n /**\n * Convert network identifier to chain name\n */\n static networkToChain(network: string): SolanaChainName | null {\n if (network === 'solana:mainnet') return 'solana';\n if (network === 'solana:devnet') return 'solana_devnet';\n return null;\n }\n\n async healthCheck(): Promise<HealthCheckResult> {\n const start = Date.now();\n try {\n // Check devnet connection\n const conn = this.getConnection('solana_devnet');\n await conn.getSlot();\n return {\n healthy: true,\n latencyMs: Date.now() - start,\n };\n } catch (error: any) {\n return {\n healthy: false,\n error: error.message,\n };\n }\n }\n\n /**\n * Verify a Solana payment\n * \n * Checks:\n * 1. Transaction is valid and properly signed\n * 2. Transfer instruction matches expected amount and recipient\n */\n async verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult> {\n try {\n const solanaPayload = paymentPayload.payload as SolanaPaymentPayload;\n if (!solanaPayload || !solanaPayload.signedTransaction) {\n return { valid: false, error: 'Missing signed transaction' };\n }\n\n const chain = solanaPayload.chain || 'solana_devnet';\n const chainConfig = SOLANA_CHAINS[chain];\n if (!chainConfig) {\n return { valid: false, error: `Invalid chain: ${chain}` };\n }\n\n // Decode the transaction\n const txBuffer = Buffer.from(solanaPayload.signedTransaction, 'base64');\n let tx: Transaction | VersionedTransaction;\n \n try {\n // Try legacy transaction first\n tx = Transaction.from(txBuffer);\n } catch {\n // Try versioned transaction\n tx = VersionedTransaction.deserialize(txBuffer);\n }\n\n // Verify at least one signature exists (may be partial in gasless mode)\n if (tx instanceof Transaction) {\n // In gasless mode, fee payer signature is added by server\n // Client only signs for token transfer authority\n const hasAnySignature = tx.signatures.some(sig => \n sig.signature && !sig.signature.every(b => b === 0)\n );\n if (!hasAnySignature) {\n return { valid: false, error: 'Transaction not signed' };\n }\n }\n\n // Parse expected values from requirements\n const expectedAmount = BigInt(requirements.amount);\n const expectedRecipient = new PublicKey(requirements.payTo);\n\n // For now, we trust the transaction structure\n // Full verification happens at settlement time\n return {\n valid: true,\n details: {\n chain,\n sender: solanaPayload.sender,\n recipient: requirements.payTo,\n amount: requirements.amount,\n },\n };\n } catch (error: any) {\n return { valid: false, error: error.message };\n }\n }\n\n /**\n * Settle a Solana payment\n * \n * Submits the signed transaction to the network.\n * In gasless mode, adds fee payer signature before submitting.\n */\n async settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult> {\n try {\n const solanaPayload = paymentPayload.payload as SolanaPaymentPayload;\n if (!solanaPayload || !solanaPayload.signedTransaction) {\n return { success: false, error: 'Missing signed transaction' };\n }\n\n const chain = solanaPayload.chain || 'solana_devnet';\n const connection = this.getConnection(chain);\n\n // Decode the transaction\n const txBuffer = Buffer.from(solanaPayload.signedTransaction, 'base64');\n \n let txToSend: Buffer;\n \n try {\n // Try legacy transaction\n const tx = Transaction.from(txBuffer);\n \n // Check if we need to add fee payer signature (gasless mode)\n if (this.feePayerKeypair && tx.feePayer) {\n const feePayerPubkey = this.feePayerKeypair.publicKey.toBase58();\n const txFeePayer = tx.feePayer.toBase58();\n \n if (txFeePayer === feePayerPubkey) {\n // Gasless mode: add fee payer signature\n console.log(`[SolanaFacilitator] Gasless mode: adding fee payer signature`);\n tx.partialSign(this.feePayerKeypair);\n }\n }\n \n txToSend = tx.serialize();\n } catch (e: any) {\n // Fall back to versioned transaction (no gasless support for versioned yet)\n txToSend = txBuffer;\n }\n\n // Send the transaction\n const signature = await connection.sendRawTransaction(txToSend, {\n skipPreflight: false,\n preflightCommitment: 'confirmed',\n });\n\n // Wait for confirmation\n const confirmation = await connection.confirmTransaction(signature, 'confirmed');\n \n if (confirmation.value.err) {\n return {\n success: false,\n error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,\n transaction: signature,\n };\n }\n\n return {\n success: true,\n transaction: signature,\n status: 'confirmed',\n };\n } catch (error: any) {\n return { success: false, error: error.message };\n }\n }\n\n supportsNetwork(network: string): boolean {\n return this.supportedNetworks.includes(network);\n }\n}\n\n/**\n * Create a Solana payment transaction for signing\n * \n * This is called by the client to create the transaction to sign.\n * \n * @param senderPubkey - The sender's public key (token owner)\n * @param recipientPubkey - The recipient's public key\n * @param amount - Amount in token base units\n * @param chain - Solana chain (solana or solana_devnet)\n * @param feePayerPubkey - Optional fee payer public key for gasless transactions\n */\nexport async function createSolanaPaymentTransaction(\n senderPubkey: PublicKey,\n recipientPubkey: PublicKey,\n amount: bigint,\n chain: SolanaChainName,\n feePayerPubkey?: PublicKey,\n connection?: Connection,\n): Promise<Transaction> {\n const chainConfig = SOLANA_CHAINS[chain];\n const conn = connection ?? new Connection(chainConfig.rpc, 'confirmed');\n const mint = new PublicKey(chainConfig.tokens.USDC.mint);\n\n // Determine who pays fees (gasless mode uses server's fee payer)\n const actualFeePayer = feePayerPubkey || senderPubkey;\n\n // Get ATAs\n const senderATA = await getAssociatedTokenAddress(mint, senderPubkey);\n const recipientATA = await getAssociatedTokenAddress(mint, recipientPubkey);\n\n const transaction = new Transaction();\n\n // Check if recipient ATA exists\n try {\n await getAccount(conn, recipientATA);\n } catch {\n // Create ATA for recipient (fee payer pays rent in gasless mode)\n transaction.add(\n createAssociatedTokenAccountInstruction(\n actualFeePayer, // payer (fee payer in gasless mode)\n recipientATA, // ata to create\n recipientPubkey, // owner\n mint // mint\n )\n );\n }\n\n // Add transfer instruction\n transaction.add(\n createTransferCheckedInstruction(\n senderATA, // source\n mint, // mint\n recipientATA, // destination\n senderPubkey, // owner (sender still authorizes the transfer)\n amount, // amount\n chainConfig.tokens.USDC.decimals // decimals\n )\n );\n\n // Get recent blockhash\n const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();\n transaction.recentBlockhash = blockhash;\n transaction.feePayer = actualFeePayer;\n\n return transaction;\n}\n\nexport default SolanaFacilitator;\n","/**\n * Core protocol types — shared between Node and Web clients.\n *\n * These describe x402 / MPP wire formats and EIP-712 typed-data shapes.\n * They MUST NOT import from any Node-only modules.\n */\n\n// ===== x402 protocol constants =====\n\nexport const X402_VERSION = 2;\nexport const PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nexport const PAYMENT_HEADER = 'x-payment';\n\n// ===== x402 payment requirements (server → client) =====\n\nexport interface X402PaymentRequirements {\n scheme: string;\n network: string;\n\n // v2 fields\n amount?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n\n // v1 legacy fields\n maxAmountRequired?: string;\n resource?: string;\n description?: string;\n}\n\n// ===== EIP-3009 TransferWithAuthorization (Base / Polygon / Base Sepolia) =====\n\nexport interface EIP3009Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\n// ===== EIP-2612 Permit (Tempo — pathUSD / AlphaUSD / BetaUSD / ThetaUSD) =====\n\nexport interface EIP2612PermitMessage {\n owner: string;\n spender: string;\n value: string;\n nonce: string;\n deadline: string;\n}\n\n/** Signed permit payload carried inside an x402 `scheme: \"permit\"` payment. */\nexport interface EIP2612PermitPayload {\n owner: string;\n spender: string;\n value: string;\n nonce: string;\n deadline: string;\n v: number;\n r: string;\n s: string;\n}\n\n// ===== BNB payment intent (Phase 3c+ pre-approval flow) =====\n\nexport interface BnbPaymentIntent {\n from: string;\n to: string;\n amount: string;\n token: string;\n service: string;\n nonce: number;\n deadline: number;\n}\n\n// ===== EIP-712 typed-data envelope (runtime-agnostic signing input) =====\n\nexport interface TypedDataDomain {\n name?: string;\n version?: string;\n chainId?: number;\n verifyingContract?: string;\n salt?: string;\n}\n\nexport interface TypedDataField {\n name: string;\n type: string;\n}\n\nexport interface TypedDataEnvelope<TMessage = Record<string, unknown>> {\n domain: TypedDataDomain;\n types: Record<string, readonly TypedDataField[]>;\n primaryType: string;\n message: TMessage;\n}\n","/**\n * Chain identifier mapping — pure, runtime-agnostic.\n *\n * Translates between x402 `network` field values (CAIP-2 style, e.g.\n * `eip155:8453`, `solana:mainnet`) and MoltsPay's internal chain names\n * (e.g. `base`, `solana`, `tempo_moderato`).\n */\n\nexport type ChainName =\n | 'base'\n | 'polygon'\n | 'base_sepolia'\n | 'tempo_moderato'\n | 'bnb'\n | 'bnb_testnet'\n | 'solana'\n | 'solana_devnet';\n\nconst NETWORK_TO_CHAIN: Record<string, ChainName> = {\n 'eip155:8453': 'base',\n 'eip155:137': 'polygon',\n 'eip155:84532': 'base_sepolia',\n 'eip155:42431': 'tempo_moderato',\n 'eip155:56': 'bnb',\n 'eip155:97': 'bnb_testnet',\n 'solana:mainnet': 'solana',\n 'solana:devnet': 'solana_devnet',\n};\n\nconst CHAIN_TO_NETWORK: Record<ChainName, string> = Object.fromEntries(\n Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])\n) as Record<ChainName, string>;\n\n/** Convert an x402 `network` identifier to a MoltsPay chain name, or null if unknown. */\nexport function networkToChainName(network: string): ChainName | null {\n return NETWORK_TO_CHAIN[network] ?? null;\n}\n\n/** Convert a MoltsPay chain name to its x402 `network` identifier. */\nexport function chainNameToNetwork(chain: ChainName): string {\n return CHAIN_TO_NETWORK[chain];\n}\n\n/** Check if an x402 `network` identifier is Solana (SVM). */\nexport function isSolanaNetwork(network: string): boolean {\n return network.startsWith('solana:');\n}\n\n/** List every supported chain name. */\nexport function listSupportedChains(): ChainName[] {\n return Object.keys(CHAIN_TO_NETWORK) as ChainName[];\n}\n","/**\n * Universal base64 — works in both browser and Node without polyfills.\n *\n * Node has `Buffer`, browsers have `btoa`/`atob`. We detect at runtime to\n * avoid pulling a Buffer polyfill into the web bundle.\n */\n\ntype BufferLike = {\n from(input: string | Uint8Array, encoding?: string): { toString(enc: string): string };\n};\n\nconst BufferCtor: BufferLike | undefined =\n (globalThis as { Buffer?: BufferLike }).Buffer;\n\n/** Encode a UTF-8 string as base64 (standard, with padding). */\nexport function encodeBase64(input: string): string {\n if (BufferCtor) {\n return BufferCtor.from(input, 'utf-8').toString('base64');\n }\n // Browser path: btoa requires binary (Latin-1) string; UTF-8 → bytes → binary string.\n const bytes = new TextEncoder().encode(input);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/** Decode a standard base64 string into a UTF-8 string. */\nexport function decodeBase64(input: string): string {\n if (BufferCtor) {\n return BufferCtor.from(input, 'base64').toString('utf-8');\n }\n const binary = atob(input);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/** Decode a base64url string (RFC 4648 §5: `-` and `_` instead of `+` and `/`, optional padding). */\nexport function decodeBase64Url(input: string): string {\n const standard = input.replace(/-/g, '+').replace(/_/g, '/');\n const padded = standard + '='.repeat((4 - (standard.length % 4)) % 4);\n return decodeBase64(padded);\n}\n\n/** Encode a UTF-8 string as base64url (no padding). */\nexport function encodeBase64Url(input: string): string {\n return encodeBase64(input)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/** Decode a base64 string to raw bytes. Used for Solana transactions where the payload is binary. */\nexport function base64ToUint8Array(input: string): Uint8Array {\n if (BufferCtor) {\n const buf = BufferCtor.from(input, 'base64') as unknown as Uint8Array;\n return new Uint8Array(buf);\n }\n const binary = atob(input);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/** Encode raw bytes as a base64 string. */\nexport function uint8ArrayToBase64(bytes: Uint8Array): string {\n if (BufferCtor) {\n return BufferCtor.from(bytes).toString('base64');\n }\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n","/**\n * EIP-3009 TransferWithAuthorization typed-data builder (pure).\n *\n * Used for gasless payments on Base / Polygon / Base Sepolia where the token\n * contract (USDC, USDT variants) implements the `transferWithAuthorization`\n * function selector.\n *\n * The builder is runtime-agnostic: it returns the envelope, and the signer\n * (Node `ethers.Wallet` or Web EIP-1193 provider) does the actual signing.\n */\n\nimport type {\n EIP3009Authorization,\n TypedDataEnvelope,\n} from './types.js';\n\nexport interface BuildEIP3009Args {\n /** Payer address (will become `from`). */\n from: string;\n /** Payee address (will become `to`). */\n to: string;\n /** Token amount in the token's smallest unit (e.g. 6 decimals for USDC → 500000 = $0.50). */\n value: string;\n /** Random 32-byte nonce as 0x-prefixed hex. Caller generates with their environment's CSPRNG. */\n nonce: string;\n /** Unix seconds when the authorization starts being valid. Default 0. */\n validAfter?: string;\n /** Unix seconds when the authorization expires. Default now + 3600. */\n validBefore?: string;\n\n /** EIP-155 chain id. */\n chainId: number;\n /** Token contract address. */\n tokenAddress: string;\n /** EIP-712 domain name as declared on-chain (e.g. \"USD Coin\" for USDC Base). */\n tokenName: string;\n /** EIP-712 domain version. Usually \"2\" for USDC, varies for USDT. */\n tokenVersion: string;\n}\n\nexport const EIP3009_TYPES = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n} as const;\n\nexport function buildEIP3009TypedData(\n args: BuildEIP3009Args\n): TypedDataEnvelope<EIP3009Authorization> {\n const validAfter = args.validAfter ?? '0';\n const validBefore =\n args.validBefore ?? (Math.floor(Date.now() / 1000) + 3600).toString();\n\n const authorization: EIP3009Authorization = {\n from: args.from,\n to: args.to,\n value: args.value,\n validAfter,\n validBefore,\n nonce: args.nonce,\n };\n\n return {\n domain: {\n name: args.tokenName,\n version: args.tokenVersion,\n chainId: args.chainId,\n verifyingContract: args.tokenAddress,\n },\n types: EIP3009_TYPES,\n primaryType: 'TransferWithAuthorization',\n message: authorization,\n };\n}\n","/**\n * BNB PaymentIntent typed-data builder (pure).\n *\n * Used on BNB Smart Chain (mainnet 56, testnet 97) where the user has already\n * approved the MoltsPay spender contract (via `npx moltspay approve` on CLI,\n * or `client.approveBnb()` on Web) and now signs an EIP-712 intent that\n * authorizes a specific spender → payTo transfer for a specific service.\n *\n * The server's spender contract verifies the signature in `transferFrom`-like\n * flow and moves tokens from owner → payTo.\n */\n\nimport type { BnbPaymentIntent, TypedDataEnvelope } from './types.js';\n\nexport interface BuildBnbIntentArgs {\n /** Owner address (payer). */\n from: string;\n /** Recipient address (payTo). */\n to: string;\n /** Amount in token's smallest unit (BNB stablecoins use 18 decimals). */\n amount: string;\n /** Token contract address on BNB chain. */\n tokenAddress: string;\n /** Service ID the intent authorizes payment for. */\n service: string;\n /** Intent nonce — any uint256 value unique per (owner, service). Caller supplies. */\n nonce: number;\n /** Unix milliseconds when the intent expires. */\n deadline: number;\n /** Chain id (56 or 97). */\n chainId: number;\n}\n\nexport const BNB_INTENT_TYPES = {\n PaymentIntent: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'amount', type: 'uint256' },\n { name: 'token', type: 'address' },\n { name: 'service', type: 'string' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n} as const;\n\nexport const BNB_DOMAIN_NAME = 'MoltsPay';\nexport const BNB_DOMAIN_VERSION = '1';\n\nexport function buildBnbIntentTypedData(\n args: BuildBnbIntentArgs\n): TypedDataEnvelope<BnbPaymentIntent> {\n const intent: BnbPaymentIntent = {\n from: args.from,\n to: args.to,\n amount: args.amount,\n token: args.tokenAddress,\n service: args.service,\n nonce: args.nonce,\n deadline: args.deadline,\n };\n\n return {\n domain: {\n name: BNB_DOMAIN_NAME,\n version: BNB_DOMAIN_VERSION,\n chainId: args.chainId,\n },\n types: BNB_INTENT_TYPES,\n primaryType: 'PaymentIntent',\n message: intent,\n };\n}\n","/**\n * NodeSigner — PaymentSigner implementation for Node.js.\n *\n * Wraps:\n * - an ethers.Wallet for EVM signTypedData + sendEvmTransaction\n * - an optional (lazy-loaded) @solana/web3.js Keypair for signSolanaTransaction\n *\n * The Node CLI's `MoltsPayClient` constructs this from the local wallet files\n * in `~/.moltspay/`. Public surface is the same `PaymentSigner` interface that\n * Web signers implement (Phase 4), so `MoltsPayClient.pay()` doesn't branch on\n * runtime.\n */\n\nimport { ethers, Wallet } from 'ethers';\nimport { Transaction, Keypair } from '@solana/web3.js';\nimport type { PaymentSigner } from '../signer.js';\nimport type { TypedDataEnvelope } from '../core/index.js';\nimport { CHAINS } from '../../chains/index.js';\n\nexport interface NodeSignerOptions {\n /** Lazy loader for the Solana Keypair. Resolves to null when no Solana wallet is configured. */\n getSolanaKeypair?: () => Keypair | null;\n}\n\nexport class NodeSigner implements PaymentSigner {\n private readonly evmWallet: Wallet;\n private readonly getSolanaKeypair: () => Keypair | null;\n\n constructor(evmWallet: Wallet, options: NodeSignerOptions = {}) {\n this.evmWallet = evmWallet;\n this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);\n }\n\n async getEvmAddress(): Promise<string> {\n return this.evmWallet.address;\n }\n\n async getSolanaAddress(): Promise<string | null> {\n const kp = this.getSolanaKeypair();\n return kp ? kp.publicKey.toBase58() : null;\n }\n\n async signTypedData<TMessage>(envelope: TypedDataEnvelope<TMessage>): Promise<string> {\n // ethers expects types without the implicit EIP712Domain entry (our envelopes\n // already exclude it) and mutable arrays; core stores them `readonly`, so we\n // shallow-copy each type-list to satisfy the ethers signature.\n const mutableTypes: Record<string, { name: string; type: string }[]> = {};\n for (const [key, fields] of Object.entries(envelope.types)) {\n mutableTypes[key] = [...fields];\n }\n return this.evmWallet.signTypedData(\n envelope.domain,\n mutableTypes,\n envelope.message as Record<string, unknown>\n );\n }\n\n async sendEvmTransaction(args: {\n chainId: number;\n to: string;\n data: string;\n value?: string;\n }): Promise<string> {\n const chain = findChainByChainId(args.chainId);\n if (!chain) {\n throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);\n }\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n const connected = this.evmWallet.connect(provider);\n const tx = await connected.sendTransaction({\n to: args.to,\n data: args.data,\n value: args.value ? BigInt(args.value) : 0n,\n });\n return tx.hash;\n }\n\n async signSolanaTransaction(args: {\n transactionBase64: string;\n partialSign: boolean;\n }): Promise<string> {\n const kp = this.getSolanaKeypair();\n if (!kp) {\n throw new Error('signSolanaTransaction: no Solana wallet configured');\n }\n const tx = Transaction.from(Buffer.from(args.transactionBase64, 'base64'));\n if (args.partialSign) {\n tx.partialSign(kp);\n } else {\n tx.sign(kp);\n }\n return tx.serialize({ requireAllSignatures: false }).toString('base64');\n }\n}\n\n// CHAINS is a Record<ChainName, ChainConfig>; we look up by numeric chainId.\nfunction findChainByChainId(chainId: number): { rpc: string } | undefined {\n for (const cfg of Object.values(CHAINS)) {\n if ((cfg as { chainId?: number }).chainId === chainId) {\n return cfg as { rpc: string };\n }\n }\n return undefined;\n}\n"],"mappings":"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","names":["import_fs","import_os","import_path","import_ethers","import_web3","bs58","import_web3","import_spl_token","import_web3","import_web3","network","req","payTo"]}
|