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.
- package/README.md +0 -124
- package/dist/cdp/index.d.mts +111 -0
- package/dist/cdp/index.d.ts +111 -0
- package/dist/cdp/index.js +30655 -0
- package/dist/cdp/index.js.map +1 -0
- package/dist/cdp/index.mjs +30631 -0
- package/dist/cdp/index.mjs.map +1 -0
- package/dist/chains/index.d.mts +1 -1
- package/dist/chains/index.d.ts +1 -1
- package/dist/cli/index.js +990 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +967 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/client/index.d.mts +134 -0
- package/dist/client/index.d.ts +134 -0
- package/dist/client/index.js +331 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +296 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/createWallet-D53qu7ie.d.mts +77 -0
- package/dist/createWallet-D53qu7ie.d.ts +77 -0
- package/dist/index-Dg8n6wdW.d.mts +32 -0
- package/dist/index-Dg8n6wdW.d.ts +32 -0
- package/dist/index.d.mts +6 -1483
- package/dist/index.d.ts +6 -1483
- package/dist/index.js +31039 -4254
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31042 -4203
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +120 -0
- package/dist/server/index.d.ts +120 -0
- package/dist/server/index.js +418 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +393 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/wallet/index.d.mts +3 -451
- package/dist/wallet/index.d.ts +3 -451
- package/dist/wallet/index.js +5 -1021
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +16 -1015
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +19 -19
- package/dist/cli.js +0 -1984
- package/dist/cli.js.map +0 -1
- package/dist/cli.mjs +0 -1969
- package/dist/cli.mjs.map +0 -1
- package/dist/guide/index.d.mts +0 -39
- package/dist/guide/index.d.ts +0 -39
- package/dist/guide/index.js +0 -181
- package/dist/guide/index.js.map +0 -1
- package/dist/guide/index.mjs +0 -152
- package/dist/guide/index.mjs.map +0 -1
- package/dist/index-CyFg9s2m.d.mts +0 -161
- package/dist/index-CyFg9s2m.d.ts +0 -161
- package/dist/orders/index.d.mts +0 -97
- package/dist/orders/index.d.ts +0 -97
- package/dist/orders/index.js +0 -162
- package/dist/orders/index.js.map +0 -1
- package/dist/orders/index.mjs +0 -136
- package/dist/orders/index.mjs.map +0 -1
- package/dist/permit/index.d.mts +0 -49
- package/dist/permit/index.d.ts +0 -49
- package/dist/permit/index.js +0 -273
- package/dist/permit/index.js.map +0 -1
- package/dist/permit/index.mjs +0 -246
- package/dist/permit/index.mjs.map +0 -1
- /package/dist/{cli.d.mts → cli/index.d.mts} +0 -0
- /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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,gBAA6B;AAC7B,kBAA8D;;;ACN9D,oBAAuB;;;ACEhB,IAAM,SAAyC;AAAA;AAAA,EAEpD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,MAAM;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,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAAA,EACA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAAA;AAAA,EAGA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,MAAM;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,MAAM;AAAA,IACN,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AACF;AAKO,SAAS,SAAS,MAA8B;AACrD,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;AAYO,SAAS,aAAa,SAA0C;AACrE,SAAO,OAAO,OAAO,MAAM,EAAE,KAAK,OAAK,EAAE,YAAY,OAAO;AAC9D;;;ADxEA,IAAM,uBAAuB,qBAAO,GAAG,mCAAmC;AAsB1E,eAAsB,cAAc,QAA2D;AAC7F,QAAM,EAAE,QAAQ,gBAAgB,WAAW,IAAI;AAG/C,MAAI;AACJ,MAAI;AACF,QAAI,OAAO,OAAO,UAAU,UAAU;AACpC,cAAQ,aAAa,OAAO,KAAK;AAAA,IACnC,OAAO;AACL,cAAQ,SAAU,OAAO,SAAS,MAAoB;AAAA,IACxD;AACA,QAAI,CAAC,OAAO;AACV,aAAO,EAAE,UAAU,OAAO,OAAO,sBAAsB,OAAO,KAAK,GAAG;AAAA,IACxE;AAAA,EACF,SAAS,GAAG;AACV,WAAO,EAAE,UAAU,OAAO,OAAO,sBAAsB,OAAO,KAAK,GAAG;AAAA,EACxE;AAEA,MAAI;AACF,UAAM,WAAW,IAAI,qBAAO,gBAAgB,MAAM,GAAG;AAGrD,UAAM,UAAU,MAAM,SAAS,sBAAsB,MAAM;AAE3D,QAAI,CAAC,SAAS;AACZ,aAAO,EAAE,UAAU,OAAO,OAAO,yCAAyC;AAAA,IAC5E;AAEA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,EAAE,UAAU,OAAO,OAAO,qBAAqB;AAAA,IACxD;AAGA,UAAM,cAAc,MAAM,MAAM,YAAY;AAC5C,QAAI,CAAC,aAAa;AAChB,aAAO,EAAE,UAAU,OAAO,OAAO,SAAS,MAAM,IAAI,+BAA+B;AAAA,IACrF;AAEA,eAAW,OAAO,QAAQ,MAAM;AAE9B,UAAI,IAAI,QAAQ,YAAY,MAAM,aAAa;AAC7C;AAAA,MACF;AAGA,UAAI,IAAI,OAAO,SAAS,KAAK,IAAI,OAAO,CAAC,MAAM,sBAAsB;AACnE;AAAA,MACF;AAGA,YAAM,OAAO,OAAO,IAAI,OAAO,CAAC,EAAE,MAAM,GAAG;AAC3C,YAAM,KAAK,OAAO,IAAI,OAAO,CAAC,EAAE,MAAM,GAAG;AACzC,YAAM,YAAY,OAAO,IAAI,IAAI;AACjC,YAAM,SAAS,OAAO,SAAS,IAAI;AAGnC,UAAI,cAAc,GAAG,YAAY,MAAM,WAAW,YAAY,GAAG;AAC/D;AAAA,MACF;AAGA,UAAI,SAAS,gBAAgB;AAC3B,eAAO;AAAA,UACL,UAAU;AAAA,UACV,OAAO,iCAAiC,MAAM,mBAAmB,cAAc;AAAA,UAC/E;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,aAAa,QAAQ;AAAA,QACvB;AAAA,MACF;AAGA,aAAO;AAAA,QACL,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa,QAAQ;AAAA,MACvB;AAAA,IACF;AAEA,WAAO,EAAE,UAAU,OAAO,OAAO,yBAAyB;AAAA,EAE5D,SAAS,GAAQ;AACf,WAAO,EAAE,UAAU,OAAO,OAAO,EAAE,WAAW,OAAO,CAAC,EAAE;AAAA,EAC1D;AACF;;;AD9FA,SAAS,mBAA2B;AAClC,SAAO,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE;AAC3D;AAEO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EACA,SAAuC,oBAAI,IAAI;AAAA,EAC/C,UAA+B,oBAAI,IAAI;AAAA,EACvC;AAAA,EAER,YAAY,cAAsB,UAAiC,CAAC,GAAG;AAErE,UAAM,cAAU,wBAAa,cAAc,OAAO;AAClD,SAAK,WAAW,KAAK,MAAM,OAAO;AAElC,SAAK,UAAU;AAAA,MACb,MAAM,QAAQ,QAAQ;AAAA,MACtB,MAAM,QAAQ,QAAQ;AAAA,MACtB,kBAAkB,QAAQ,oBAAoB;AAAA;AAAA,IAChD;AAEA,YAAQ,IAAI,qBAAqB,KAAK,SAAS,SAAS,MAAM,kBAAkB,YAAY,EAAE;AAC9F,YAAQ,IAAI,wBAAwB,KAAK,SAAS,SAAS,IAAI,EAAE;AACjE,YAAQ,IAAI,sBAAsB,KAAK,SAAS,SAAS,MAAM,EAAE;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAmB,SAA8B;AACrD,UAAM,SAAS,KAAK,SAAS,SAAS,KAAK,OAAK,EAAE,OAAO,SAAS;AAClE,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,YAAY,SAAS,yBAAyB;AAAA,IAChE;AAEA,SAAK,OAAO,IAAI,WAAW;AAAA,MACzB,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,gCAAgC,SAAS,MAAM,OAAO,KAAK,IAAI,OAAO,QAAQ,GAAG;AAC7F,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAqB;AAC1B,UAAM,IAAI,QAAQ,KAAK,QAAQ;AAE/B,UAAM,aAAS,0BAAa,CAAC,KAAK,QAAQ,KAAK,cAAc,KAAK,GAAG,CAAC;AAEtE,WAAO,OAAO,GAAG,KAAK,QAAQ,MAAM,MAAM;AACxC,cAAQ,IAAI,yCAAyC,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE;AAC7E,cAAQ,IAAI,uBAAuB;AACnC,cAAQ,IAAI,6CAA6C;AACzD,cAAQ,IAAI,sDAAsD;AAClE,cAAQ,IAAI,iDAAiD;AAC7D,cAAQ,IAAI,0CAA0C;AAAA,IACxD,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,cAAc,KAAsB,KAAoC;AACpF,UAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,IAAI,EAAE;AAChE,UAAM,OAAO,IAAI;AACjB,UAAM,SAAS,IAAI,UAAU;AAG7B,QAAI,UAAU,+BAA+B,GAAG;AAChD,QAAI,UAAU,gCAAgC,oBAAoB;AAClE,QAAI,UAAU,gCAAgC,cAAc;AAE5D,QAAI,WAAW,WAAW;AACxB,UAAI,UAAU,GAAG;AACjB,UAAI,IAAI;AACR;AAAA,IACF;AAEA,QAAI;AACF,UAAI,WAAW,SAAS,SAAS,aAAa;AAC5C,eAAO,KAAK,kBAAkB,GAAG;AAAA,MACnC;AAEA,UAAI,WAAW,UAAU,SAAS,QAAQ;AACxC,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,eAAO,KAAK,UAAU,MAAM,GAAG;AAAA,MACjC;AAEA,UAAI,WAAW,UAAU,SAAS,WAAW;AAC3C,cAAM,OAAO,MAAM,KAAK,SAAS,GAAG;AACpC,eAAO,KAAK,aAAa,MAAM,GAAG;AAAA,MACpC;AAEA,UAAI,WAAW,SAAS,KAAK,WAAW,UAAU,GAAG;AACnD,cAAM,WAAW,KAAK,QAAQ,YAAY,EAAE;AAC5C,eAAO,KAAK,aAAa,UAAU,GAAG;AAAA,MACxC;AAGA,WAAK,SAAS,KAAK,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,IAChD,SAAS,KAAU;AACjB,cAAQ,MAAM,qBAAqB,GAAG;AACtC,WAAK,SAAS,KAAK,KAAK,EAAE,OAAO,IAAI,WAAW,iBAAiB,CAAC;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,KAA2B;AACnD,UAAM,WAAW,KAAK,SAAS,SAAS,IAAI,QAAM;AAAA,MAChD,IAAI,EAAE;AAAA,MACN,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,OAAO,EAAE;AAAA,MACT,UAAU,EAAE;AAAA,MACZ,OAAO,EAAE;AAAA,MACT,QAAQ,EAAE;AAAA,MACV,WAAW,KAAK,OAAO,IAAI,EAAE,EAAE;AAAA,IACjC,EAAE;AAEF,SAAK,SAAS,KAAK,KAAK;AAAA,MACtB,UAAU,KAAK,SAAS;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAU,MAAW,KAA2B;AACtD,UAAM,EAAE,SAAS,OAAO,IAAI;AAE5B,QAAI,CAAC,SAAS;AACZ,aAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAAA,IAC7D;AAEA,UAAM,QAAQ,KAAK,OAAO,IAAI,OAAO;AACrC,QAAI,CAAC,OAAO;AACV,aAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,YAAY,OAAO,gCAAgC,CAAC;AAAA,IAC9F;AAGA,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,OAAO,KAAK,GAAG;AAC7D,UAAI,MAAM,aAAa,CAAC,UAAU,OAAO,GAAG,MAAM,SAAY;AAC5D,eAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,2BAA2B,GAAG,GAAG,CAAC;AAAA,MAC5E;AAAA,IACF;AAGA,UAAM,WAAW,iBAAiB;AAClC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAiB;AAAA,MACrB,IAAI;AAAA,MACJ;AAAA,MACA,QAAQ,UAAU,CAAC;AAAA,MACnB,QAAQ,MAAM,OAAO;AAAA,MACrB,UAAU,MAAM,OAAO;AAAA,MACvB,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,WAAW,MAAO,KAAK,QAAQ,mBAAoB;AAAA,IACrD;AAEA,SAAK,QAAQ,IAAI,UAAU,MAAM;AAGjC,UAAM,iBAAiC;AAAA,MACrC;AAAA,MACA;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,QAAQ,KAAK,SAAS,SAAS;AAAA,MAC/B,OAAO,KAAK,SAAS,SAAS;AAAA,MAC9B,WAAW,OAAO;AAAA,IACpB;AAEA,SAAK,SAAS,KAAK,KAAK;AAAA,MACtB,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,aAAa,MAAW,KAAoC;AACxE,UAAM,EAAE,UAAU,OAAO,IAAI;AAE7B,QAAI,CAAC,YAAY,CAAC,QAAQ;AACxB,aAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,6BAA6B,CAAC;AAAA,IACxE;AAEA,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX,aAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAAA,IAC9D;AAGA,QAAI,KAAK,IAAI,IAAI,OAAO,WAAW;AACjC,aAAO,SAAS;AAChB,aAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,IAC5D;AAGA,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,KAAK,SAAS,KAAK,KAAK;AAAA,QAC7B,QAAQ;AAAA,QACR,QAAQ,OAAO;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,eAAe,MAAM,cAAc;AAAA,QACvC;AAAA,QACA,YAAY,KAAK,SAAS,SAAS;AAAA,QACnC,gBAAgB,OAAO;AAAA,QACvB,OAAO,KAAK,SAAS,SAAS;AAAA,MAChC,CAAC;AAED,UAAI,CAAC,aAAa,UAAU;AAC1B,eAAO,SAAS;AAChB,eAAO,KAAK,SAAS,KAAK,KAAK;AAAA,UAC7B,OAAO;AAAA,UACP,QAAQ,aAAa;AAAA,QACvB,CAAC;AAAA,MACH;AAGA,aAAO,SAAS;AAChB,aAAO,SAAS;AAChB,aAAO,SAAS,KAAK,IAAI;AAGzB,YAAM,QAAQ,KAAK,OAAO,IAAI,OAAO,OAAO;AAC5C,cAAQ,IAAI,+BAA+B,OAAO,OAAO,EAAE;AAE3D,YAAM,SAAS,MAAM,MAAM,QAAQ,OAAO,MAAM;AAEhD,aAAO,SAAS;AAChB,aAAO,SAAS;AAChB,aAAO,cAAc,KAAK,IAAI;AAE9B,WAAK,SAAS,KAAK,KAAK;AAAA,QACtB,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,SAAS,KAAU;AACjB,cAAQ,MAAM,qCAAqC,GAAG;AACtD,aAAO,SAAS;AAChB,WAAK,SAAS,KAAK,KAAK;AAAA,QACtB,OAAO;AAAA,QACP,SAAS,IAAI;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,UAAkB,KAA2B;AAChE,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX,aAAO,KAAK,SAAS,KAAK,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAAA,IAC9D;AAEA,SAAK,SAAS,KAAK,KAAK;AAAA,MACtB,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO,WAAW,cAAc,OAAO,SAAS;AAAA,MACxD,WAAW,OAAO;AAAA,MAClB,WAAW,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,SAAS,KAAoC;AACzD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,OAAO;AACX,UAAI,GAAG,QAAQ,WAAS,QAAQ,KAAK;AACrC,UAAI,GAAG,OAAO,MAAM;AAClB,YAAI;AACF,kBAAQ,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC;AAAA,QACtC,QAAQ;AACN,iBAAO,IAAI,MAAM,cAAc,CAAC;AAAA,QAClC;AAAA,MACF,CAAC;AACD,UAAI,GAAG,SAAS,MAAM;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEQ,SAAS,KAAqB,QAAgB,MAAiB;AACrE,QAAI,UAAU,QAAQ,EAAE,gBAAgB,mBAAmB,CAAC;AAC5D,QAAI,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,EACvC;AACF;","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":[]}
|