moltspay 0.5.4 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +0 -124
  2. package/dist/cdp/index.d.mts +111 -0
  3. package/dist/cdp/index.d.ts +111 -0
  4. package/dist/cdp/index.js +30655 -0
  5. package/dist/cdp/index.js.map +1 -0
  6. package/dist/cdp/index.mjs +30631 -0
  7. package/dist/cdp/index.mjs.map +1 -0
  8. package/dist/chains/index.d.mts +1 -1
  9. package/dist/chains/index.d.ts +1 -1
  10. package/dist/cli/index.js +990 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/index.mjs +967 -0
  13. package/dist/cli/index.mjs.map +1 -0
  14. package/dist/client/index.d.mts +134 -0
  15. package/dist/client/index.d.ts +134 -0
  16. package/dist/client/index.js +331 -0
  17. package/dist/client/index.js.map +1 -0
  18. package/dist/client/index.mjs +296 -0
  19. package/dist/client/index.mjs.map +1 -0
  20. package/dist/createWallet-D53qu7ie.d.mts +77 -0
  21. package/dist/createWallet-D53qu7ie.d.ts +77 -0
  22. package/dist/index-Dg8n6wdW.d.mts +32 -0
  23. package/dist/index-Dg8n6wdW.d.ts +32 -0
  24. package/dist/index.d.mts +6 -1483
  25. package/dist/index.d.ts +6 -1483
  26. package/dist/index.js +31039 -4254
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +31042 -4203
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/server/index.d.mts +120 -0
  31. package/dist/server/index.d.ts +120 -0
  32. package/dist/server/index.js +418 -0
  33. package/dist/server/index.js.map +1 -0
  34. package/dist/server/index.mjs +393 -0
  35. package/dist/server/index.mjs.map +1 -0
  36. package/dist/wallet/index.d.mts +3 -451
  37. package/dist/wallet/index.d.ts +3 -451
  38. package/dist/wallet/index.js +5 -1021
  39. package/dist/wallet/index.js.map +1 -1
  40. package/dist/wallet/index.mjs +16 -1015
  41. package/dist/wallet/index.mjs.map +1 -1
  42. package/package.json +19 -19
  43. package/dist/cli.js +0 -1984
  44. package/dist/cli.js.map +0 -1
  45. package/dist/cli.mjs +0 -1969
  46. package/dist/cli.mjs.map +0 -1
  47. package/dist/guide/index.d.mts +0 -39
  48. package/dist/guide/index.d.ts +0 -39
  49. package/dist/guide/index.js +0 -181
  50. package/dist/guide/index.js.map +0 -1
  51. package/dist/guide/index.mjs +0 -152
  52. package/dist/guide/index.mjs.map +0 -1
  53. package/dist/index-CyFg9s2m.d.mts +0 -161
  54. package/dist/index-CyFg9s2m.d.ts +0 -161
  55. package/dist/orders/index.d.mts +0 -97
  56. package/dist/orders/index.d.ts +0 -97
  57. package/dist/orders/index.js +0 -162
  58. package/dist/orders/index.js.map +0 -1
  59. package/dist/orders/index.mjs +0 -136
  60. package/dist/orders/index.mjs.map +0 -1
  61. package/dist/permit/index.d.mts +0 -49
  62. package/dist/permit/index.d.ts +0 -49
  63. package/dist/permit/index.js +0 -273
  64. package/dist/permit/index.js.map +0 -1
  65. package/dist/permit/index.mjs +0 -246
  66. package/dist/permit/index.mjs.map +0 -1
  67. /package/dist/{cli.d.mts → cli/index.d.mts} +0 -0
  68. /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/verify/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * MoltsPay Server - Payment infrastructure for AI Agents\n * \n * Usage:\n * const server = new MoltsPayServer('./moltspay.services.json');\n * server.skill('text-to-video', async (params) => { ... });\n * server.listen(3000);\n */\n\nimport { readFileSync } from 'fs';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { verifyPayment } from '../verify/index.js';\nimport {\n ServicesManifest,\n ServiceConfig,\n SkillFunction,\n RegisteredSkill,\n Charge,\n ChargeStatus,\n PaymentRequest,\n MoltsPayServerOptions,\n} from './types.js';\n\nexport * from './types.js';\n\nfunction generateChargeId(): string {\n return 'ch_' + Math.random().toString(36).substring(2, 15);\n}\n\nexport class MoltsPayServer {\n private manifest: ServicesManifest;\n private skills: Map<string, RegisteredSkill> = new Map();\n private charges: Map<string, Charge> = new Map();\n private options: MoltsPayServerOptions;\n\n constructor(servicesPath: string, options: MoltsPayServerOptions = {}) {\n // Load services manifest\n const content = readFileSync(servicesPath, 'utf-8');\n this.manifest = JSON.parse(content) as ServicesManifest;\n \n this.options = {\n port: options.port || 3000,\n host: options.host || '0.0.0.0',\n chargeExpirySecs: options.chargeExpirySecs || 300, // 5 minutes\n };\n\n console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);\n console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);\n console.log(`[MoltsPay] Wallet: ${this.manifest.provider.wallet}`);\n }\n\n /**\n * Register a skill handler for a service\n */\n skill(serviceId: string, handler: SkillFunction): this {\n const config = this.manifest.services.find(s => s.id === serviceId);\n if (!config) {\n throw new Error(`Service '${serviceId}' not found in manifest`);\n }\n\n this.skills.set(serviceId, {\n id: serviceId,\n config,\n handler,\n });\n\n console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);\n return this;\n }\n\n /**\n * Start the server\n */\n listen(port?: number): void {\n const p = port || this.options.port!;\n \n const server = createServer((req, res) => this.handleRequest(req, res));\n \n server.listen(p, this.options.host, () => {\n console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);\n console.log(`[MoltsPay] Endpoints:`);\n console.log(` GET /services - List available services`);\n console.log(` POST /pay - Create payment & execute service`);\n console.log(` POST /verify - Verify payment & get result`);\n console.log(` GET /status/:id - Check charge status`);\n });\n }\n\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url || '/', `http://${req.headers.host}`);\n const path = url.pathname;\n const method = req.method || 'GET';\n\n // CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n if (method === 'GET' && path === '/services') {\n return this.handleGetServices(res);\n }\n\n if (method === 'POST' && path === '/pay') {\n const body = await this.readBody(req);\n return this.handlePay(body, res);\n }\n\n if (method === 'POST' && path === '/verify') {\n const body = await this.readBody(req);\n return this.handleVerify(body, res);\n }\n\n if (method === 'GET' && path.startsWith('/status/')) {\n const chargeId = path.replace('/status/', '');\n return this.handleStatus(chargeId, res);\n }\n\n // Not found\n this.sendJson(res, 404, { error: 'Not found' });\n } catch (err: any) {\n console.error('[MoltsPay] Error:', err);\n this.sendJson(res, 500, { error: err.message || 'Internal error' });\n }\n }\n\n /**\n * GET /services - List available services\n */\n private handleGetServices(res: ServerResponse): void {\n const services = this.manifest.services.map(s => ({\n id: s.id,\n name: s.name,\n description: s.description,\n price: s.price,\n currency: s.currency,\n input: s.input,\n output: s.output,\n available: this.skills.has(s.id),\n }));\n\n this.sendJson(res, 200, {\n provider: this.manifest.provider,\n services,\n });\n }\n\n /**\n * POST /pay - Create payment request\n * Body: { service: string, params: object }\n */\n private handlePay(body: any, res: ServerResponse): void {\n const { service, params } = body;\n\n if (!service) {\n return this.sendJson(res, 400, { error: 'Missing service' });\n }\n\n const skill = this.skills.get(service);\n if (!skill) {\n return this.sendJson(res, 404, { error: `Service '${service}' not found or not registered` });\n }\n\n // Validate required params\n for (const [key, field] of Object.entries(skill.config.input)) {\n if (field.required && (!params || params[key] === undefined)) {\n return this.sendJson(res, 400, { error: `Missing required param: ${key}` });\n }\n }\n\n // Create charge\n const chargeId = generateChargeId();\n const now = Date.now();\n const charge: Charge = {\n id: chargeId,\n service,\n params: params || {},\n amount: skill.config.price,\n currency: skill.config.currency,\n status: 'pending',\n createdAt: now,\n expiresAt: now + (this.options.chargeExpirySecs! * 1000),\n };\n\n this.charges.set(chargeId, charge);\n\n // Return payment request\n const paymentRequest: PaymentRequest = {\n chargeId,\n service,\n amount: charge.amount,\n currency: charge.currency,\n wallet: this.manifest.provider.wallet,\n chain: this.manifest.provider.chain,\n expiresAt: charge.expiresAt,\n };\n\n this.sendJson(res, 402, {\n message: 'Payment required',\n payment: paymentRequest,\n });\n }\n\n /**\n * POST /verify - Verify payment and execute skill\n * Body: { chargeId: string, txHash: string }\n */\n private async handleVerify(body: any, res: ServerResponse): Promise<void> {\n const { chargeId, txHash } = body;\n\n if (!chargeId || !txHash) {\n return this.sendJson(res, 400, { error: 'Missing chargeId or txHash' });\n }\n\n const charge = this.charges.get(chargeId);\n if (!charge) {\n return this.sendJson(res, 404, { error: 'Charge not found' });\n }\n\n // Check expiry\n if (Date.now() > charge.expiresAt) {\n charge.status = 'expired';\n return this.sendJson(res, 400, { error: 'Charge expired' });\n }\n\n // Check if already paid\n if (charge.status === 'completed') {\n return this.sendJson(res, 200, {\n status: 'completed',\n result: charge.result,\n });\n }\n\n try {\n const verification = await verifyPayment({\n txHash,\n expectedTo: this.manifest.provider.wallet,\n expectedAmount: charge.amount,\n chain: this.manifest.provider.chain,\n });\n\n if (!verification.verified) {\n charge.status = 'failed';\n return this.sendJson(res, 400, {\n error: 'Payment verification failed',\n reason: verification.error,\n });\n }\n\n // Payment verified - update charge\n charge.status = 'paid';\n charge.txHash = txHash;\n charge.paidAt = Date.now();\n\n // Execute skill\n const skill = this.skills.get(charge.service)!;\n console.log(`[MoltsPay] Executing skill: ${charge.service}`);\n \n const result = await skill.handler(charge.params);\n \n charge.status = 'completed';\n charge.result = result;\n charge.completedAt = Date.now();\n\n this.sendJson(res, 200, {\n status: 'completed',\n chargeId,\n txHash,\n result,\n });\n } catch (err: any) {\n console.error('[MoltsPay] Skill execution error:', err);\n charge.status = 'failed';\n this.sendJson(res, 500, {\n error: 'Skill execution failed',\n message: err.message,\n });\n }\n }\n\n /**\n * GET /status/:chargeId - Check charge status\n */\n private handleStatus(chargeId: string, res: ServerResponse): void {\n const charge = this.charges.get(chargeId);\n if (!charge) {\n return this.sendJson(res, 404, { error: 'Charge not found' });\n }\n\n this.sendJson(res, 200, {\n chargeId: charge.id,\n service: charge.service,\n amount: charge.amount,\n currency: charge.currency,\n status: charge.status,\n txHash: charge.txHash,\n result: charge.status === 'completed' ? charge.result : undefined,\n createdAt: charge.createdAt,\n expiresAt: charge.expiresAt,\n });\n }\n\n private async readBody(req: IncomingMessage): Promise<any> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', chunk => body += chunk);\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(new Error('Invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n }\n\n private sendJson(res: ServerResponse, status: number, data: any): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(data, null, 2));\n }\n}\n","/**\n * On-chain Payment Verification Module\n */\n\nimport { ethers } from 'ethers';\nimport { getChain, getChainById, type ChainConfig, type ChainName } from '../chains';\n\n// ERC20 Transfer event signature\nconst TRANSFER_EVENT_TOPIC = ethers.id('Transfer(address,address,uint256)');\n\nexport interface VerifyPaymentParams {\n txHash: string;\n expectedAmount: number;\n expectedTo?: string;\n chain?: string | number;\n}\n\nexport interface VerifyPaymentResult {\n verified: boolean;\n amount?: number;\n from?: string;\n to?: string;\n txHash?: string;\n blockNumber?: number;\n error?: string;\n}\n\n/**\n * Verify on-chain payment\n */\nexport async function verifyPayment(params: VerifyPaymentParams): Promise<VerifyPaymentResult> {\n const { txHash, expectedAmount, expectedTo } = params;\n \n // Get chain config\n let chain: ChainConfig | undefined;\n try {\n if (typeof params.chain === 'number') {\n chain = getChainById(params.chain);\n } else {\n chain = getChain((params.chain || 'base') as ChainName);\n }\n if (!chain) {\n return { verified: false, error: `Unsupported chain: ${params.chain}` };\n }\n } catch (e) {\n return { verified: false, error: `Unsupported chain: ${params.chain}` };\n }\n\n try {\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n \n // Get transaction receipt\n const receipt = await provider.getTransactionReceipt(txHash);\n \n if (!receipt) {\n return { verified: false, error: 'Transaction not found or not confirmed' };\n }\n\n if (receipt.status !== 1) {\n return { verified: false, error: 'Transaction failed' };\n }\n\n // Parse Transfer event\n const usdcAddress = chain.usdc?.toLowerCase();\n if (!usdcAddress) {\n return { verified: false, error: `Chain ${chain.name} USDC address not configured` };\n }\n\n for (const log of receipt.logs) {\n // Check if USDC contract\n if (log.address.toLowerCase() !== usdcAddress) {\n continue;\n }\n\n // Check if Transfer event\n if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {\n continue;\n }\n\n // Parse Transfer event params\n const from = '0x' + log.topics[1].slice(-40);\n const to = '0x' + log.topics[2].slice(-40);\n const amountRaw = BigInt(log.data);\n const amount = Number(amountRaw) / 1e6; // USDC 6 decimals\n\n // Verify recipient address\n if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {\n continue;\n }\n\n // Verify amount\n if (amount < expectedAmount) {\n return {\n verified: false,\n error: `Insufficient amount: received ${amount} USDC, expected ${expectedAmount} USDC`,\n amount,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n // Verification successful\n return {\n verified: true,\n amount,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n return { verified: false, error: 'No USDC transfer found' };\n\n } catch (e: any) {\n return { verified: false, error: e.message || String(e) };\n }\n}\n\n/**\n * Get transaction status\n */\nexport async function getTransactionStatus(\n txHash: string,\n chain: string | number = 'base'\n): Promise<{\n status: 'pending' | 'confirmed' | 'failed' | 'not_found';\n blockNumber?: number;\n confirmations?: number;\n}> {\n let chainConfig: ChainConfig | undefined;\n try {\n chainConfig = typeof chain === 'number' ? getChainById(chain) : getChain(chain as ChainName);\n if (!chainConfig) return { status: 'not_found' };\n } catch {\n return { status: 'not_found' };\n }\n\n try {\n const provider = new ethers.JsonRpcProvider(chainConfig.rpc);\n const receipt = await provider.getTransactionReceipt(txHash);\n\n if (!receipt) {\n // Check if in pending pool\n const tx = await provider.getTransaction(txHash);\n if (tx) {\n return { status: 'pending' };\n }\n return { status: 'not_found' };\n }\n\n const currentBlock = await provider.getBlockNumber();\n const confirmations = currentBlock - receipt.blockNumber;\n\n if (receipt.status === 1) {\n return {\n status: 'confirmed',\n blockNumber: receipt.blockNumber,\n confirmations,\n };\n } else {\n return {\n status: 'failed',\n blockNumber: receipt.blockNumber,\n };\n }\n } catch {\n return { status: 'not_found' };\n }\n}\n\n/**\n * Wait for transaction confirmation\n */\nexport async function waitForTransaction(\n txHash: string,\n chain: string | number = 'base',\n confirmations = 1,\n timeoutMs = 60000\n): Promise<VerifyPaymentResult & { confirmed: boolean }> {\n let chainConfig: ChainConfig | undefined;\n try {\n chainConfig = typeof chain === 'number' ? getChainById(chain) : getChain(chain as ChainName);\n if (!chainConfig) {\n return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };\n }\n } catch (e) {\n return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };\n }\n\n const provider = new ethers.JsonRpcProvider(chainConfig.rpc);\n \n try {\n const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);\n \n if (!receipt) {\n return { verified: false, confirmed: false, error: 'Timeout waiting' };\n }\n\n if (receipt.status !== 1) {\n return { verified: false, confirmed: true, error: 'Transaction failed' };\n }\n\n return {\n verified: true,\n confirmed: true,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n } catch (e: any) {\n return { verified: false, confirmed: false, error: e.message || String(e) };\n }\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName } from '../types/index.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\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-rpc.com',\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n ethereum: {\n name: 'Ethereum',\n chainId: 1,\n rpc: 'https://eth.llamarpc.com',\n usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n explorer: 'https://etherscan.io/address/',\n explorerTx: 'https://etherscan.io/tx/',\n avgBlockTime: 12,\n },\n\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n sepolia: {\n name: 'Sepolia',\n chainId: 11155111,\n rpc: 'https://rpc.sepolia.org',\n usdc: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n explorer: 'https://sepolia.etherscan.io/address/',\n explorerTx: 'https://sepolia.etherscan.io/tx/',\n avgBlockTime: 12,\n },\n};\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: ChainName): 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 chains\n */\nexport function listChains(): ChainName[] {\n return Object.keys(CHAINS) as ChainName[];\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 };\n"],"mappings":"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","names":[]}
@@ -0,0 +1,393 @@
1
+ // src/server/index.ts
2
+ import { readFileSync } from "fs";
3
+ import { createServer } from "http";
4
+
5
+ // src/verify/index.ts
6
+ import { ethers } from "ethers";
7
+
8
+ // src/chains/index.ts
9
+ var CHAINS = {
10
+ // ============ Mainnet ============
11
+ base: {
12
+ name: "Base",
13
+ chainId: 8453,
14
+ rpc: "https://mainnet.base.org",
15
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
16
+ explorer: "https://basescan.org/address/",
17
+ explorerTx: "https://basescan.org/tx/",
18
+ avgBlockTime: 2
19
+ },
20
+ polygon: {
21
+ name: "Polygon",
22
+ chainId: 137,
23
+ rpc: "https://polygon-rpc.com",
24
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
25
+ explorer: "https://polygonscan.com/address/",
26
+ explorerTx: "https://polygonscan.com/tx/",
27
+ avgBlockTime: 2
28
+ },
29
+ ethereum: {
30
+ name: "Ethereum",
31
+ chainId: 1,
32
+ rpc: "https://eth.llamarpc.com",
33
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
34
+ explorer: "https://etherscan.io/address/",
35
+ explorerTx: "https://etherscan.io/tx/",
36
+ avgBlockTime: 12
37
+ },
38
+ // ============ Testnet ============
39
+ base_sepolia: {
40
+ name: "Base Sepolia",
41
+ chainId: 84532,
42
+ rpc: "https://sepolia.base.org",
43
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
44
+ explorer: "https://sepolia.basescan.org/address/",
45
+ explorerTx: "https://sepolia.basescan.org/tx/",
46
+ avgBlockTime: 2
47
+ },
48
+ sepolia: {
49
+ name: "Sepolia",
50
+ chainId: 11155111,
51
+ rpc: "https://rpc.sepolia.org",
52
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
53
+ explorer: "https://sepolia.etherscan.io/address/",
54
+ explorerTx: "https://sepolia.etherscan.io/tx/",
55
+ avgBlockTime: 12
56
+ }
57
+ };
58
+ function getChain(name) {
59
+ const config = CHAINS[name];
60
+ if (!config) {
61
+ throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(", ")}`);
62
+ }
63
+ return config;
64
+ }
65
+ function getChainById(chainId) {
66
+ return Object.values(CHAINS).find((c) => c.chainId === chainId);
67
+ }
68
+
69
+ // src/verify/index.ts
70
+ var TRANSFER_EVENT_TOPIC = ethers.id("Transfer(address,address,uint256)");
71
+ async function verifyPayment(params) {
72
+ const { txHash, expectedAmount, expectedTo } = params;
73
+ let chain;
74
+ try {
75
+ if (typeof params.chain === "number") {
76
+ chain = getChainById(params.chain);
77
+ } else {
78
+ chain = getChain(params.chain || "base");
79
+ }
80
+ if (!chain) {
81
+ return { verified: false, error: `Unsupported chain: ${params.chain}` };
82
+ }
83
+ } catch (e) {
84
+ return { verified: false, error: `Unsupported chain: ${params.chain}` };
85
+ }
86
+ try {
87
+ const provider = new ethers.JsonRpcProvider(chain.rpc);
88
+ const receipt = await provider.getTransactionReceipt(txHash);
89
+ if (!receipt) {
90
+ return { verified: false, error: "Transaction not found or not confirmed" };
91
+ }
92
+ if (receipt.status !== 1) {
93
+ return { verified: false, error: "Transaction failed" };
94
+ }
95
+ const usdcAddress = chain.usdc?.toLowerCase();
96
+ if (!usdcAddress) {
97
+ return { verified: false, error: `Chain ${chain.name} USDC address not configured` };
98
+ }
99
+ for (const log of receipt.logs) {
100
+ if (log.address.toLowerCase() !== usdcAddress) {
101
+ continue;
102
+ }
103
+ if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
104
+ continue;
105
+ }
106
+ const from = "0x" + log.topics[1].slice(-40);
107
+ const to = "0x" + log.topics[2].slice(-40);
108
+ const amountRaw = BigInt(log.data);
109
+ const amount = Number(amountRaw) / 1e6;
110
+ if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
111
+ continue;
112
+ }
113
+ if (amount < expectedAmount) {
114
+ return {
115
+ verified: false,
116
+ error: `Insufficient amount: received ${amount} USDC, expected ${expectedAmount} USDC`,
117
+ amount,
118
+ from,
119
+ to,
120
+ txHash,
121
+ blockNumber: receipt.blockNumber
122
+ };
123
+ }
124
+ return {
125
+ verified: true,
126
+ amount,
127
+ from,
128
+ to,
129
+ txHash,
130
+ blockNumber: receipt.blockNumber
131
+ };
132
+ }
133
+ return { verified: false, error: "No USDC transfer found" };
134
+ } catch (e) {
135
+ return { verified: false, error: e.message || String(e) };
136
+ }
137
+ }
138
+
139
+ // src/server/index.ts
140
+ function generateChargeId() {
141
+ return "ch_" + Math.random().toString(36).substring(2, 15);
142
+ }
143
+ var MoltsPayServer = class {
144
+ manifest;
145
+ skills = /* @__PURE__ */ new Map();
146
+ charges = /* @__PURE__ */ new Map();
147
+ options;
148
+ constructor(servicesPath, options = {}) {
149
+ const content = readFileSync(servicesPath, "utf-8");
150
+ this.manifest = JSON.parse(content);
151
+ this.options = {
152
+ port: options.port || 3e3,
153
+ host: options.host || "0.0.0.0",
154
+ chargeExpirySecs: options.chargeExpirySecs || 300
155
+ // 5 minutes
156
+ };
157
+ console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);
158
+ console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);
159
+ console.log(`[MoltsPay] Wallet: ${this.manifest.provider.wallet}`);
160
+ }
161
+ /**
162
+ * Register a skill handler for a service
163
+ */
164
+ skill(serviceId, handler) {
165
+ const config = this.manifest.services.find((s) => s.id === serviceId);
166
+ if (!config) {
167
+ throw new Error(`Service '${serviceId}' not found in manifest`);
168
+ }
169
+ this.skills.set(serviceId, {
170
+ id: serviceId,
171
+ config,
172
+ handler
173
+ });
174
+ console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);
175
+ return this;
176
+ }
177
+ /**
178
+ * Start the server
179
+ */
180
+ listen(port) {
181
+ const p = port || this.options.port;
182
+ const server = createServer((req, res) => this.handleRequest(req, res));
183
+ server.listen(p, this.options.host, () => {
184
+ console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);
185
+ console.log(`[MoltsPay] Endpoints:`);
186
+ console.log(` GET /services - List available services`);
187
+ console.log(` POST /pay - Create payment & execute service`);
188
+ console.log(` POST /verify - Verify payment & get result`);
189
+ console.log(` GET /status/:id - Check charge status`);
190
+ });
191
+ }
192
+ async handleRequest(req, res) {
193
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
194
+ const path = url.pathname;
195
+ const method = req.method || "GET";
196
+ res.setHeader("Access-Control-Allow-Origin", "*");
197
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
198
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
199
+ if (method === "OPTIONS") {
200
+ res.writeHead(204);
201
+ res.end();
202
+ return;
203
+ }
204
+ try {
205
+ if (method === "GET" && path === "/services") {
206
+ return this.handleGetServices(res);
207
+ }
208
+ if (method === "POST" && path === "/pay") {
209
+ const body = await this.readBody(req);
210
+ return this.handlePay(body, res);
211
+ }
212
+ if (method === "POST" && path === "/verify") {
213
+ const body = await this.readBody(req);
214
+ return this.handleVerify(body, res);
215
+ }
216
+ if (method === "GET" && path.startsWith("/status/")) {
217
+ const chargeId = path.replace("/status/", "");
218
+ return this.handleStatus(chargeId, res);
219
+ }
220
+ this.sendJson(res, 404, { error: "Not found" });
221
+ } catch (err) {
222
+ console.error("[MoltsPay] Error:", err);
223
+ this.sendJson(res, 500, { error: err.message || "Internal error" });
224
+ }
225
+ }
226
+ /**
227
+ * GET /services - List available services
228
+ */
229
+ handleGetServices(res) {
230
+ const services = this.manifest.services.map((s) => ({
231
+ id: s.id,
232
+ name: s.name,
233
+ description: s.description,
234
+ price: s.price,
235
+ currency: s.currency,
236
+ input: s.input,
237
+ output: s.output,
238
+ available: this.skills.has(s.id)
239
+ }));
240
+ this.sendJson(res, 200, {
241
+ provider: this.manifest.provider,
242
+ services
243
+ });
244
+ }
245
+ /**
246
+ * POST /pay - Create payment request
247
+ * Body: { service: string, params: object }
248
+ */
249
+ handlePay(body, res) {
250
+ const { service, params } = body;
251
+ if (!service) {
252
+ return this.sendJson(res, 400, { error: "Missing service" });
253
+ }
254
+ const skill = this.skills.get(service);
255
+ if (!skill) {
256
+ return this.sendJson(res, 404, { error: `Service '${service}' not found or not registered` });
257
+ }
258
+ for (const [key, field] of Object.entries(skill.config.input)) {
259
+ if (field.required && (!params || params[key] === void 0)) {
260
+ return this.sendJson(res, 400, { error: `Missing required param: ${key}` });
261
+ }
262
+ }
263
+ const chargeId = generateChargeId();
264
+ const now = Date.now();
265
+ const charge = {
266
+ id: chargeId,
267
+ service,
268
+ params: params || {},
269
+ amount: skill.config.price,
270
+ currency: skill.config.currency,
271
+ status: "pending",
272
+ createdAt: now,
273
+ expiresAt: now + this.options.chargeExpirySecs * 1e3
274
+ };
275
+ this.charges.set(chargeId, charge);
276
+ const paymentRequest = {
277
+ chargeId,
278
+ service,
279
+ amount: charge.amount,
280
+ currency: charge.currency,
281
+ wallet: this.manifest.provider.wallet,
282
+ chain: this.manifest.provider.chain,
283
+ expiresAt: charge.expiresAt
284
+ };
285
+ this.sendJson(res, 402, {
286
+ message: "Payment required",
287
+ payment: paymentRequest
288
+ });
289
+ }
290
+ /**
291
+ * POST /verify - Verify payment and execute skill
292
+ * Body: { chargeId: string, txHash: string }
293
+ */
294
+ async handleVerify(body, res) {
295
+ const { chargeId, txHash } = body;
296
+ if (!chargeId || !txHash) {
297
+ return this.sendJson(res, 400, { error: "Missing chargeId or txHash" });
298
+ }
299
+ const charge = this.charges.get(chargeId);
300
+ if (!charge) {
301
+ return this.sendJson(res, 404, { error: "Charge not found" });
302
+ }
303
+ if (Date.now() > charge.expiresAt) {
304
+ charge.status = "expired";
305
+ return this.sendJson(res, 400, { error: "Charge expired" });
306
+ }
307
+ if (charge.status === "completed") {
308
+ return this.sendJson(res, 200, {
309
+ status: "completed",
310
+ result: charge.result
311
+ });
312
+ }
313
+ try {
314
+ const verification = await verifyPayment({
315
+ txHash,
316
+ expectedTo: this.manifest.provider.wallet,
317
+ expectedAmount: charge.amount,
318
+ chain: this.manifest.provider.chain
319
+ });
320
+ if (!verification.verified) {
321
+ charge.status = "failed";
322
+ return this.sendJson(res, 400, {
323
+ error: "Payment verification failed",
324
+ reason: verification.error
325
+ });
326
+ }
327
+ charge.status = "paid";
328
+ charge.txHash = txHash;
329
+ charge.paidAt = Date.now();
330
+ const skill = this.skills.get(charge.service);
331
+ console.log(`[MoltsPay] Executing skill: ${charge.service}`);
332
+ const result = await skill.handler(charge.params);
333
+ charge.status = "completed";
334
+ charge.result = result;
335
+ charge.completedAt = Date.now();
336
+ this.sendJson(res, 200, {
337
+ status: "completed",
338
+ chargeId,
339
+ txHash,
340
+ result
341
+ });
342
+ } catch (err) {
343
+ console.error("[MoltsPay] Skill execution error:", err);
344
+ charge.status = "failed";
345
+ this.sendJson(res, 500, {
346
+ error: "Skill execution failed",
347
+ message: err.message
348
+ });
349
+ }
350
+ }
351
+ /**
352
+ * GET /status/:chargeId - Check charge status
353
+ */
354
+ handleStatus(chargeId, res) {
355
+ const charge = this.charges.get(chargeId);
356
+ if (!charge) {
357
+ return this.sendJson(res, 404, { error: "Charge not found" });
358
+ }
359
+ this.sendJson(res, 200, {
360
+ chargeId: charge.id,
361
+ service: charge.service,
362
+ amount: charge.amount,
363
+ currency: charge.currency,
364
+ status: charge.status,
365
+ txHash: charge.txHash,
366
+ result: charge.status === "completed" ? charge.result : void 0,
367
+ createdAt: charge.createdAt,
368
+ expiresAt: charge.expiresAt
369
+ });
370
+ }
371
+ async readBody(req) {
372
+ return new Promise((resolve, reject) => {
373
+ let body = "";
374
+ req.on("data", (chunk) => body += chunk);
375
+ req.on("end", () => {
376
+ try {
377
+ resolve(body ? JSON.parse(body) : {});
378
+ } catch {
379
+ reject(new Error("Invalid JSON"));
380
+ }
381
+ });
382
+ req.on("error", reject);
383
+ });
384
+ }
385
+ sendJson(res, status, data) {
386
+ res.writeHead(status, { "Content-Type": "application/json" });
387
+ res.end(JSON.stringify(data, null, 2));
388
+ }
389
+ };
390
+ export {
391
+ MoltsPayServer
392
+ };
393
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/verify/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * MoltsPay Server - Payment infrastructure for AI Agents\n * \n * Usage:\n * const server = new MoltsPayServer('./moltspay.services.json');\n * server.skill('text-to-video', async (params) => { ... });\n * server.listen(3000);\n */\n\nimport { readFileSync } from 'fs';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { verifyPayment } from '../verify/index.js';\nimport {\n ServicesManifest,\n ServiceConfig,\n SkillFunction,\n RegisteredSkill,\n Charge,\n ChargeStatus,\n PaymentRequest,\n MoltsPayServerOptions,\n} from './types.js';\n\nexport * from './types.js';\n\nfunction generateChargeId(): string {\n return 'ch_' + Math.random().toString(36).substring(2, 15);\n}\n\nexport class MoltsPayServer {\n private manifest: ServicesManifest;\n private skills: Map<string, RegisteredSkill> = new Map();\n private charges: Map<string, Charge> = new Map();\n private options: MoltsPayServerOptions;\n\n constructor(servicesPath: string, options: MoltsPayServerOptions = {}) {\n // Load services manifest\n const content = readFileSync(servicesPath, 'utf-8');\n this.manifest = JSON.parse(content) as ServicesManifest;\n \n this.options = {\n port: options.port || 3000,\n host: options.host || '0.0.0.0',\n chargeExpirySecs: options.chargeExpirySecs || 300, // 5 minutes\n };\n\n console.log(`[MoltsPay] Loaded ${this.manifest.services.length} services from ${servicesPath}`);\n console.log(`[MoltsPay] Provider: ${this.manifest.provider.name}`);\n console.log(`[MoltsPay] Wallet: ${this.manifest.provider.wallet}`);\n }\n\n /**\n * Register a skill handler for a service\n */\n skill(serviceId: string, handler: SkillFunction): this {\n const config = this.manifest.services.find(s => s.id === serviceId);\n if (!config) {\n throw new Error(`Service '${serviceId}' not found in manifest`);\n }\n\n this.skills.set(serviceId, {\n id: serviceId,\n config,\n handler,\n });\n\n console.log(`[MoltsPay] Registered skill: ${serviceId} ($${config.price} ${config.currency})`);\n return this;\n }\n\n /**\n * Start the server\n */\n listen(port?: number): void {\n const p = port || this.options.port!;\n \n const server = createServer((req, res) => this.handleRequest(req, res));\n \n server.listen(p, this.options.host, () => {\n console.log(`[MoltsPay] Server listening on http://${this.options.host}:${p}`);\n console.log(`[MoltsPay] Endpoints:`);\n console.log(` GET /services - List available services`);\n console.log(` POST /pay - Create payment & execute service`);\n console.log(` POST /verify - Verify payment & get result`);\n console.log(` GET /status/:id - Check charge status`);\n });\n }\n\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url || '/', `http://${req.headers.host}`);\n const path = url.pathname;\n const method = req.method || 'GET';\n\n // CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n if (method === 'GET' && path === '/services') {\n return this.handleGetServices(res);\n }\n\n if (method === 'POST' && path === '/pay') {\n const body = await this.readBody(req);\n return this.handlePay(body, res);\n }\n\n if (method === 'POST' && path === '/verify') {\n const body = await this.readBody(req);\n return this.handleVerify(body, res);\n }\n\n if (method === 'GET' && path.startsWith('/status/')) {\n const chargeId = path.replace('/status/', '');\n return this.handleStatus(chargeId, res);\n }\n\n // Not found\n this.sendJson(res, 404, { error: 'Not found' });\n } catch (err: any) {\n console.error('[MoltsPay] Error:', err);\n this.sendJson(res, 500, { error: err.message || 'Internal error' });\n }\n }\n\n /**\n * GET /services - List available services\n */\n private handleGetServices(res: ServerResponse): void {\n const services = this.manifest.services.map(s => ({\n id: s.id,\n name: s.name,\n description: s.description,\n price: s.price,\n currency: s.currency,\n input: s.input,\n output: s.output,\n available: this.skills.has(s.id),\n }));\n\n this.sendJson(res, 200, {\n provider: this.manifest.provider,\n services,\n });\n }\n\n /**\n * POST /pay - Create payment request\n * Body: { service: string, params: object }\n */\n private handlePay(body: any, res: ServerResponse): void {\n const { service, params } = body;\n\n if (!service) {\n return this.sendJson(res, 400, { error: 'Missing service' });\n }\n\n const skill = this.skills.get(service);\n if (!skill) {\n return this.sendJson(res, 404, { error: `Service '${service}' not found or not registered` });\n }\n\n // Validate required params\n for (const [key, field] of Object.entries(skill.config.input)) {\n if (field.required && (!params || params[key] === undefined)) {\n return this.sendJson(res, 400, { error: `Missing required param: ${key}` });\n }\n }\n\n // Create charge\n const chargeId = generateChargeId();\n const now = Date.now();\n const charge: Charge = {\n id: chargeId,\n service,\n params: params || {},\n amount: skill.config.price,\n currency: skill.config.currency,\n status: 'pending',\n createdAt: now,\n expiresAt: now + (this.options.chargeExpirySecs! * 1000),\n };\n\n this.charges.set(chargeId, charge);\n\n // Return payment request\n const paymentRequest: PaymentRequest = {\n chargeId,\n service,\n amount: charge.amount,\n currency: charge.currency,\n wallet: this.manifest.provider.wallet,\n chain: this.manifest.provider.chain,\n expiresAt: charge.expiresAt,\n };\n\n this.sendJson(res, 402, {\n message: 'Payment required',\n payment: paymentRequest,\n });\n }\n\n /**\n * POST /verify - Verify payment and execute skill\n * Body: { chargeId: string, txHash: string }\n */\n private async handleVerify(body: any, res: ServerResponse): Promise<void> {\n const { chargeId, txHash } = body;\n\n if (!chargeId || !txHash) {\n return this.sendJson(res, 400, { error: 'Missing chargeId or txHash' });\n }\n\n const charge = this.charges.get(chargeId);\n if (!charge) {\n return this.sendJson(res, 404, { error: 'Charge not found' });\n }\n\n // Check expiry\n if (Date.now() > charge.expiresAt) {\n charge.status = 'expired';\n return this.sendJson(res, 400, { error: 'Charge expired' });\n }\n\n // Check if already paid\n if (charge.status === 'completed') {\n return this.sendJson(res, 200, {\n status: 'completed',\n result: charge.result,\n });\n }\n\n try {\n const verification = await verifyPayment({\n txHash,\n expectedTo: this.manifest.provider.wallet,\n expectedAmount: charge.amount,\n chain: this.manifest.provider.chain,\n });\n\n if (!verification.verified) {\n charge.status = 'failed';\n return this.sendJson(res, 400, {\n error: 'Payment verification failed',\n reason: verification.error,\n });\n }\n\n // Payment verified - update charge\n charge.status = 'paid';\n charge.txHash = txHash;\n charge.paidAt = Date.now();\n\n // Execute skill\n const skill = this.skills.get(charge.service)!;\n console.log(`[MoltsPay] Executing skill: ${charge.service}`);\n \n const result = await skill.handler(charge.params);\n \n charge.status = 'completed';\n charge.result = result;\n charge.completedAt = Date.now();\n\n this.sendJson(res, 200, {\n status: 'completed',\n chargeId,\n txHash,\n result,\n });\n } catch (err: any) {\n console.error('[MoltsPay] Skill execution error:', err);\n charge.status = 'failed';\n this.sendJson(res, 500, {\n error: 'Skill execution failed',\n message: err.message,\n });\n }\n }\n\n /**\n * GET /status/:chargeId - Check charge status\n */\n private handleStatus(chargeId: string, res: ServerResponse): void {\n const charge = this.charges.get(chargeId);\n if (!charge) {\n return this.sendJson(res, 404, { error: 'Charge not found' });\n }\n\n this.sendJson(res, 200, {\n chargeId: charge.id,\n service: charge.service,\n amount: charge.amount,\n currency: charge.currency,\n status: charge.status,\n txHash: charge.txHash,\n result: charge.status === 'completed' ? charge.result : undefined,\n createdAt: charge.createdAt,\n expiresAt: charge.expiresAt,\n });\n }\n\n private async readBody(req: IncomingMessage): Promise<any> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', chunk => body += chunk);\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(new Error('Invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n }\n\n private sendJson(res: ServerResponse, status: number, data: any): void {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(data, null, 2));\n }\n}\n","/**\n * On-chain Payment Verification Module\n */\n\nimport { ethers } from 'ethers';\nimport { getChain, getChainById, type ChainConfig, type ChainName } from '../chains';\n\n// ERC20 Transfer event signature\nconst TRANSFER_EVENT_TOPIC = ethers.id('Transfer(address,address,uint256)');\n\nexport interface VerifyPaymentParams {\n txHash: string;\n expectedAmount: number;\n expectedTo?: string;\n chain?: string | number;\n}\n\nexport interface VerifyPaymentResult {\n verified: boolean;\n amount?: number;\n from?: string;\n to?: string;\n txHash?: string;\n blockNumber?: number;\n error?: string;\n}\n\n/**\n * Verify on-chain payment\n */\nexport async function verifyPayment(params: VerifyPaymentParams): Promise<VerifyPaymentResult> {\n const { txHash, expectedAmount, expectedTo } = params;\n \n // Get chain config\n let chain: ChainConfig | undefined;\n try {\n if (typeof params.chain === 'number') {\n chain = getChainById(params.chain);\n } else {\n chain = getChain((params.chain || 'base') as ChainName);\n }\n if (!chain) {\n return { verified: false, error: `Unsupported chain: ${params.chain}` };\n }\n } catch (e) {\n return { verified: false, error: `Unsupported chain: ${params.chain}` };\n }\n\n try {\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n \n // Get transaction receipt\n const receipt = await provider.getTransactionReceipt(txHash);\n \n if (!receipt) {\n return { verified: false, error: 'Transaction not found or not confirmed' };\n }\n\n if (receipt.status !== 1) {\n return { verified: false, error: 'Transaction failed' };\n }\n\n // Parse Transfer event\n const usdcAddress = chain.usdc?.toLowerCase();\n if (!usdcAddress) {\n return { verified: false, error: `Chain ${chain.name} USDC address not configured` };\n }\n\n for (const log of receipt.logs) {\n // Check if USDC contract\n if (log.address.toLowerCase() !== usdcAddress) {\n continue;\n }\n\n // Check if Transfer event\n if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {\n continue;\n }\n\n // Parse Transfer event params\n const from = '0x' + log.topics[1].slice(-40);\n const to = '0x' + log.topics[2].slice(-40);\n const amountRaw = BigInt(log.data);\n const amount = Number(amountRaw) / 1e6; // USDC 6 decimals\n\n // Verify recipient address\n if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {\n continue;\n }\n\n // Verify amount\n if (amount < expectedAmount) {\n return {\n verified: false,\n error: `Insufficient amount: received ${amount} USDC, expected ${expectedAmount} USDC`,\n amount,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n // Verification successful\n return {\n verified: true,\n amount,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n return { verified: false, error: 'No USDC transfer found' };\n\n } catch (e: any) {\n return { verified: false, error: e.message || String(e) };\n }\n}\n\n/**\n * Get transaction status\n */\nexport async function getTransactionStatus(\n txHash: string,\n chain: string | number = 'base'\n): Promise<{\n status: 'pending' | 'confirmed' | 'failed' | 'not_found';\n blockNumber?: number;\n confirmations?: number;\n}> {\n let chainConfig: ChainConfig | undefined;\n try {\n chainConfig = typeof chain === 'number' ? getChainById(chain) : getChain(chain as ChainName);\n if (!chainConfig) return { status: 'not_found' };\n } catch {\n return { status: 'not_found' };\n }\n\n try {\n const provider = new ethers.JsonRpcProvider(chainConfig.rpc);\n const receipt = await provider.getTransactionReceipt(txHash);\n\n if (!receipt) {\n // Check if in pending pool\n const tx = await provider.getTransaction(txHash);\n if (tx) {\n return { status: 'pending' };\n }\n return { status: 'not_found' };\n }\n\n const currentBlock = await provider.getBlockNumber();\n const confirmations = currentBlock - receipt.blockNumber;\n\n if (receipt.status === 1) {\n return {\n status: 'confirmed',\n blockNumber: receipt.blockNumber,\n confirmations,\n };\n } else {\n return {\n status: 'failed',\n blockNumber: receipt.blockNumber,\n };\n }\n } catch {\n return { status: 'not_found' };\n }\n}\n\n/**\n * Wait for transaction confirmation\n */\nexport async function waitForTransaction(\n txHash: string,\n chain: string | number = 'base',\n confirmations = 1,\n timeoutMs = 60000\n): Promise<VerifyPaymentResult & { confirmed: boolean }> {\n let chainConfig: ChainConfig | undefined;\n try {\n chainConfig = typeof chain === 'number' ? getChainById(chain) : getChain(chain as ChainName);\n if (!chainConfig) {\n return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };\n }\n } catch (e) {\n return { verified: false, confirmed: false, error: `Unsupported chain: ${chain}` };\n }\n\n const provider = new ethers.JsonRpcProvider(chainConfig.rpc);\n \n try {\n const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);\n \n if (!receipt) {\n return { verified: false, confirmed: false, error: 'Timeout waiting' };\n }\n\n if (receipt.status !== 1) {\n return { verified: false, confirmed: true, error: 'Transaction failed' };\n }\n\n return {\n verified: true,\n confirmed: true,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n } catch (e: any) {\n return { verified: false, confirmed: false, error: e.message || String(e) };\n }\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName } from '../types/index.js';\n\nexport const CHAINS: Record<ChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\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-rpc.com',\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n ethereum: {\n name: 'Ethereum',\n chainId: 1,\n rpc: 'https://eth.llamarpc.com',\n usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n explorer: 'https://etherscan.io/address/',\n explorerTx: 'https://etherscan.io/tx/',\n avgBlockTime: 12,\n },\n\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n sepolia: {\n name: 'Sepolia',\n chainId: 11155111,\n rpc: 'https://rpc.sepolia.org',\n usdc: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n explorer: 'https://sepolia.etherscan.io/address/',\n explorerTx: 'https://sepolia.etherscan.io/tx/',\n avgBlockTime: 12,\n },\n};\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: ChainName): 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 chains\n */\nexport function listChains(): ChainName[] {\n return Object.keys(CHAINS) as ChainName[];\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 };\n"],"mappings":"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","names":[]}