moltspay 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +35 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +35 -19
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.js +11 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +11 -3
- package/dist/client/index.mjs.map +1 -1
- package/dist/index.js +35 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +35 -13
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +24 -10
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +24 -17
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * MoltsPay Client - Pay for AI Agent services\n * \n * Uses x402 protocol for gasless, pay-for-success payments.\n * \n * Usage:\n * const client = new MoltsPayClient(); // Loads from ~/.moltspay/\n * const services = await client.getServices('http://provider:3000');\n * const result = await client.pay('http://provider:3000', 'text-to-video', { prompt: '...' });\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Wallet, ethers } from 'ethers';\nimport { getChain, type ChainName } from '../chains/index.js';\nimport {\n ClientConfig,\n WalletData,\n ServicesResponse,\n MoltsPayClientOptions,\n} from './types.js';\n\nexport * from './types.js';\n\n// x402 constants\nconst X402_VERSION = 2;\nconst PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nconst PAYMENT_HEADER = 'x-payment';\n\ninterface X402PaymentRequirements {\n scheme: string;\n network: string;\n // v2 fields\n amount?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n // v1 fields (legacy)\n maxAmountRequired?: string;\n resource?: string;\n description?: string;\n}\n\ninterface EIP3009Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\nconst DEFAULT_CONFIG: ClientConfig = {\n chain: 'base',\n limits: {\n maxPerTx: 100,\n maxPerDay: 1000,\n },\n};\n\nexport class MoltsPayClient {\n private configDir: string;\n private config: ClientConfig;\n private walletData: WalletData | null = null;\n private wallet: Wallet | null = null;\n private todaySpending: number = 0;\n private lastSpendingReset: number = 0;\n\n constructor(options: MoltsPayClientOptions = {}) {\n this.configDir = options.configDir || join(homedir(), '.moltspay');\n this.config = this.loadConfig();\n this.walletData = this.loadWallet();\n \n if (this.walletData) {\n this.wallet = new Wallet(this.walletData.privateKey);\n }\n }\n\n /**\n * Check if client is initialized (has wallet)\n */\n get isInitialized(): boolean {\n return this.wallet !== null;\n }\n\n /**\n * Get wallet address\n */\n get address(): string | null {\n return this.wallet?.address || null;\n }\n\n /**\n * Get current config\n */\n getConfig(): ClientConfig {\n return { ...this.config };\n }\n\n /**\n * Update config\n */\n updateConfig(updates: Partial<ClientConfig['limits']>): void {\n if (updates.maxPerTx !== undefined) {\n this.config.limits.maxPerTx = updates.maxPerTx;\n }\n if (updates.maxPerDay !== undefined) {\n this.config.limits.maxPerDay = updates.maxPerDay;\n }\n this.saveConfig();\n }\n\n /**\n * Get services from a provider\n */\n async getServices(serverUrl: string): Promise<ServicesResponse> {\n const res = await fetch(`${serverUrl}/services`);\n if (!res.ok) {\n throw new Error(`Failed to get services: ${res.statusText}`);\n }\n return res.json() as Promise<ServicesResponse>;\n }\n\n /**\n * Pay for a service and get the result (x402 protocol)\n * \n * This is GASLESS for the client - server pays gas to claim payment.\n * This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.\n */\n async pay(\n serverUrl: string,\n service: string,\n params: Record<string, any>\n ): Promise<Record<string, any>> {\n if (!this.wallet || !this.walletData) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Step 1: Make initial request without payment\n console.log(`[MoltsPay] Requesting service: ${service}`);\n const initialRes = await fetch(`${serverUrl}/execute`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service, params }),\n });\n\n // If not 402, check for success or error\n if (initialRes.status !== 402) {\n const data = await initialRes.json() as any;\n if (initialRes.ok && data.result) {\n return data.result;\n }\n throw new Error(data.error || 'Unexpected response');\n }\n\n // Step 2: Parse payment requirements from 402 response\n const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);\n if (!paymentRequiredHeader) {\n throw new Error('Missing x-payment-required header');\n }\n\n let requirements: X402PaymentRequirements[];\n try {\n const decoded = Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8');\n const parsed = JSON.parse(decoded);\n \n // Handle both v1 (array) and v2 (object with accepts) formats\n if (Array.isArray(parsed)) {\n // v1 format: direct array of requirements\n requirements = parsed;\n } else if (parsed.accepts && Array.isArray(parsed.accepts)) {\n // v2 format: { x402Version: 2, accepts: [...] }\n requirements = parsed.accepts;\n } else {\n // Single requirement object\n requirements = [parsed];\n }\n } catch {\n throw new Error('Invalid x-payment-required header');\n }\n\n // Find matching requirement for our chain\n const chain = getChain(this.config.chain as ChainName);\n const network = `eip155:${chain.chainId}`;\n const req = requirements.find(r => r.scheme === 'exact' && r.network === network);\n \n if (!req) {\n throw new Error(`No matching payment option for ${network}`);\n }\n\n // Step 3: Check limits\n // v2 uses 'amount', v1 uses 'maxAmountRequired'\n const amountRaw = req.amount || req.maxAmountRequired;\n if (!amountRaw) {\n throw new Error('Missing amount in payment requirements');\n }\n const amount = Number(amountRaw) / 1e6;\n this.checkLimits(amount);\n\n console.log(`[MoltsPay] Signing payment: $${amount} USDC (gasless)`);\n\n // Step 4: Sign EIP-3009 authorization (GASLESS - just signing)\n // payTo is the recipient address (v2 format)\n const payTo = req.payTo || req.resource; // fallback for v1 compatibility\n if (!payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n const authorization = await this.signEIP3009(payTo, amount, chain);\n\n // Step 5: Create x402 payment payload\n const payload = {\n x402Version: X402_VERSION,\n scheme: 'exact',\n network,\n payload: authorization,\n };\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Step 6: Retry with payment header\n console.log(`[MoltsPay] Sending request with payment...`);\n const paidRes = await fetch(`${serverUrl}/execute`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [PAYMENT_HEADER]: paymentHeader,\n },\n body: JSON.stringify({ service, params }),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Service execution failed');\n }\n\n // Update spending tracking\n this.recordSpending(amount);\n\n console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || 'claimed'}`);\n \n return result.result;\n }\n\n /**\n * Sign EIP-3009 transferWithAuthorization (GASLESS)\n * This only signs - no on-chain transaction, no gas needed.\n */\n private async signEIP3009(\n to: string,\n amount: number,\n chain: { chainId: number; usdc: string }\n ): Promise<{ authorization: EIP3009Authorization; signature: string }> {\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600; // 1 hour\n const nonce = ethers.hexlify(ethers.randomBytes(32));\n const value = BigInt(Math.floor(amount * 1e6)).toString();\n\n const authorization: EIP3009Authorization = {\n from: this.wallet!.address,\n to,\n value,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n };\n\n // EIP-712 domain for USDC\n const domain = {\n name: 'USD Coin',\n version: '2',\n chainId: chain.chainId,\n verifyingContract: chain.usdc,\n };\n\n // EIP-3009 types\n const types = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n };\n\n const signature = await this.wallet!.signTypedData(domain, types, authorization);\n\n return { authorization, signature };\n }\n\n /**\n * Check spending limits\n */\n private checkLimits(amount: number): void {\n // Check per-tx limit\n if (amount > this.config.limits.maxPerTx) {\n throw new Error(\n `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`\n );\n }\n\n // Reset daily spending if new day\n const today = new Date().setHours(0, 0, 0, 0);\n if (today > this.lastSpendingReset) {\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n }\n\n // Check daily limit\n if (this.todaySpending + amount > this.config.limits.maxPerDay) {\n throw new Error(\n `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`\n );\n }\n }\n\n /**\n * Record spending\n */\n private recordSpending(amount: number): void {\n this.todaySpending += amount;\n }\n\n // --- Config & Wallet Management ---\n\n private loadConfig(): ClientConfig {\n const configPath = join(this.configDir, 'config.json');\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) };\n }\n return { ...DEFAULT_CONFIG };\n }\n\n private saveConfig(): void {\n mkdirSync(this.configDir, { recursive: true });\n const configPath = join(this.configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n }\n\n private loadWallet(): WalletData | null {\n const walletPath = join(this.configDir, 'wallet.json');\n if (existsSync(walletPath)) {\n const content = readFileSync(walletPath, 'utf-8');\n return JSON.parse(content);\n }\n return null;\n }\n\n /**\n * Initialize a new wallet (called by CLI)\n */\n static init(\n configDir: string,\n options: { chain: string; maxPerTx: number; maxPerDay: number }\n ): { address: string; configDir: string } {\n mkdirSync(configDir, { recursive: true });\n\n // Create wallet\n const wallet = Wallet.createRandom();\n const walletData: WalletData = {\n address: wallet.address,\n privateKey: wallet.privateKey,\n createdAt: Date.now(),\n };\n\n // Save wallet\n const walletPath = join(configDir, 'wallet.json');\n writeFileSync(walletPath, JSON.stringify(walletData, null, 2));\n\n // Save config\n const config: ClientConfig = {\n chain: options.chain,\n limits: {\n maxPerTx: options.maxPerTx,\n maxPerDay: options.maxPerDay,\n },\n };\n const configPath = join(configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n return { address: wallet.address, configDir };\n }\n\n /**\n * Get wallet balance\n */\n async getBalance(): Promise<{ usdc: number; native: number }> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n let chain;\n try {\n chain = getChain(this.config.chain as ChainName);\n } catch {\n throw new Error(`Unknown chain: ${this.config.chain}`);\n }\n\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n\n // Get native balance\n const nativeBalance = await provider.getBalance(this.wallet.address);\n\n // Get USDC balance\n const usdcAbi = ['function balanceOf(address) view returns (uint256)'];\n const usdc = new ethers.Contract(chain.usdc, usdcAbi, provider);\n const usdcBalance = await usdc.balanceOf(this.wallet.address);\n\n return {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\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":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * MoltsPay Client - Pay for AI Agent services\n * \n * Uses x402 protocol for gasless, pay-for-success payments.\n * \n * Usage:\n * const client = new MoltsPayClient(); // Loads from ~/.moltspay/\n * const services = await client.getServices('http://provider:3000');\n * const result = await client.pay('http://provider:3000', 'text-to-video', { prompt: '...' });\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\nimport { Wallet, ethers } from 'ethers';\nimport { getChain, type ChainName } from '../chains/index.js';\nimport {\n ClientConfig,\n WalletData,\n ServicesResponse,\n MoltsPayClientOptions,\n} from './types.js';\n\nexport * from './types.js';\n\n// x402 constants\nconst X402_VERSION = 2;\nconst PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nconst PAYMENT_HEADER = 'x-payment';\n\ninterface X402PaymentRequirements {\n scheme: string;\n network: string;\n // v2 fields\n amount?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n // v1 fields (legacy)\n maxAmountRequired?: string;\n resource?: string;\n description?: string;\n}\n\ninterface EIP3009Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\nconst DEFAULT_CONFIG: ClientConfig = {\n chain: 'base',\n limits: {\n maxPerTx: 100,\n maxPerDay: 1000,\n },\n};\n\nexport class MoltsPayClient {\n private configDir: string;\n private config: ClientConfig;\n private walletData: WalletData | null = null;\n private wallet: Wallet | null = null;\n private todaySpending: number = 0;\n private lastSpendingReset: number = 0;\n\n constructor(options: MoltsPayClientOptions = {}) {\n this.configDir = options.configDir || join(homedir(), '.moltspay');\n this.config = this.loadConfig();\n this.walletData = this.loadWallet();\n \n if (this.walletData) {\n this.wallet = new Wallet(this.walletData.privateKey);\n }\n }\n\n /**\n * Check if client is initialized (has wallet)\n */\n get isInitialized(): boolean {\n return this.wallet !== null;\n }\n\n /**\n * Get wallet address\n */\n get address(): string | null {\n return this.wallet?.address || null;\n }\n\n /**\n * Get current config\n */\n getConfig(): ClientConfig {\n return { ...this.config };\n }\n\n /**\n * Update config\n */\n updateConfig(updates: Partial<ClientConfig['limits']>): void {\n if (updates.maxPerTx !== undefined) {\n this.config.limits.maxPerTx = updates.maxPerTx;\n }\n if (updates.maxPerDay !== undefined) {\n this.config.limits.maxPerDay = updates.maxPerDay;\n }\n this.saveConfig();\n }\n\n /**\n * Get services from a provider\n */\n async getServices(serverUrl: string): Promise<ServicesResponse> {\n const res = await fetch(`${serverUrl}/services`);\n if (!res.ok) {\n throw new Error(`Failed to get services: ${res.statusText}`);\n }\n return res.json() as Promise<ServicesResponse>;\n }\n\n /**\n * Pay for a service and get the result (x402 protocol)\n * \n * This is GASLESS for the client - server pays gas to claim payment.\n * This is PAY-FOR-SUCCESS - payment only claimed if service succeeds.\n */\n async pay(\n serverUrl: string,\n service: string,\n params: Record<string, any>\n ): Promise<Record<string, any>> {\n if (!this.wallet || !this.walletData) {\n throw new Error('Client not initialized. Run: npx moltspay init');\n }\n\n // Step 1: Make initial request without payment\n console.log(`[MoltsPay] Requesting service: ${service}`);\n const initialRes = await fetch(`${serverUrl}/execute`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ service, params }),\n });\n\n // If not 402, check for success or error\n if (initialRes.status !== 402) {\n const data = await initialRes.json() as any;\n if (initialRes.ok && data.result) {\n return data.result;\n }\n throw new Error(data.error || 'Unexpected response');\n }\n\n // Step 2: Parse payment requirements from 402 response\n const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);\n if (!paymentRequiredHeader) {\n throw new Error('Missing x-payment-required header');\n }\n\n let requirements: X402PaymentRequirements[];\n try {\n const decoded = Buffer.from(paymentRequiredHeader, 'base64').toString('utf-8');\n const parsed = JSON.parse(decoded);\n \n // Handle both v1 (array) and v2 (object with accepts) formats\n if (Array.isArray(parsed)) {\n // v1 format: direct array of requirements\n requirements = parsed;\n } else if (parsed.accepts && Array.isArray(parsed.accepts)) {\n // v2 format: { x402Version: 2, accepts: [...] }\n requirements = parsed.accepts;\n } else {\n // Single requirement object\n requirements = [parsed];\n }\n } catch {\n throw new Error('Invalid x-payment-required header');\n }\n\n // Find matching requirement for our chain\n const chain = getChain(this.config.chain as ChainName);\n const network = `eip155:${chain.chainId}`;\n const req = requirements.find(r => r.scheme === 'exact' && r.network === network);\n \n if (!req) {\n throw new Error(`No matching payment option for ${network}`);\n }\n\n // Step 3: Check limits\n // v2 uses 'amount', v1 uses 'maxAmountRequired'\n const amountRaw = req.amount || req.maxAmountRequired;\n if (!amountRaw) {\n throw new Error('Missing amount in payment requirements');\n }\n const amount = Number(amountRaw) / 1e6;\n this.checkLimits(amount);\n\n console.log(`[MoltsPay] Signing payment: $${amount} USDC (gasless)`);\n\n // Step 4: Sign EIP-3009 authorization (GASLESS - just signing)\n // payTo is the recipient address (v2 format)\n const payTo = req.payTo || req.resource; // fallback for v1 compatibility\n if (!payTo) {\n throw new Error('Missing payTo address in payment requirements');\n }\n const authorization = await this.signEIP3009(payTo, amount, chain);\n\n // Step 5: Create x402 payment payload (v2 format requires 'accepted')\n const payload = {\n x402Version: X402_VERSION,\n payload: authorization,\n // v2 requires 'accepted' field with the requirements being fulfilled\n accepted: {\n scheme: 'exact',\n network,\n asset: req.asset || chain.usdc,\n amount: amountRaw,\n payTo,\n maxTimeoutSeconds: req.maxTimeoutSeconds || 300,\n extra: req.extra || { name: 'USD Coin', version: '2' },\n },\n };\n const paymentHeader = Buffer.from(JSON.stringify(payload)).toString('base64');\n\n // Step 6: Retry with payment header\n console.log(`[MoltsPay] Sending request with payment...`);\n const paidRes = await fetch(`${serverUrl}/execute`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [PAYMENT_HEADER]: paymentHeader,\n },\n body: JSON.stringify({ service, params }),\n });\n\n const result = await paidRes.json() as any;\n\n if (!paidRes.ok) {\n throw new Error(result.error || 'Service execution failed');\n }\n\n // Update spending tracking\n this.recordSpending(amount);\n\n console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || 'claimed'}`);\n \n return result.result;\n }\n\n /**\n * Sign EIP-3009 transferWithAuthorization (GASLESS)\n * This only signs - no on-chain transaction, no gas needed.\n */\n private async signEIP3009(\n to: string,\n amount: number,\n chain: { chainId: number; usdc: string }\n ): Promise<{ authorization: EIP3009Authorization; signature: string }> {\n const validAfter = 0;\n const validBefore = Math.floor(Date.now() / 1000) + 3600; // 1 hour\n const nonce = ethers.hexlify(ethers.randomBytes(32));\n const value = BigInt(Math.floor(amount * 1e6)).toString();\n\n const authorization: EIP3009Authorization = {\n from: this.wallet!.address,\n to,\n value,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n };\n\n // EIP-712 domain for USDC\n const domain = {\n name: 'USD Coin',\n version: '2',\n chainId: chain.chainId,\n verifyingContract: chain.usdc,\n };\n\n // EIP-3009 types\n const types = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n };\n\n const signature = await this.wallet!.signTypedData(domain, types, authorization);\n\n return { authorization, signature };\n }\n\n /**\n * Check spending limits\n */\n private checkLimits(amount: number): void {\n // Check per-tx limit\n if (amount > this.config.limits.maxPerTx) {\n throw new Error(\n `Amount $${amount} exceeds max per transaction ($${this.config.limits.maxPerTx})`\n );\n }\n\n // Reset daily spending if new day\n const today = new Date().setHours(0, 0, 0, 0);\n if (today > this.lastSpendingReset) {\n this.todaySpending = 0;\n this.lastSpendingReset = today;\n }\n\n // Check daily limit\n if (this.todaySpending + amount > this.config.limits.maxPerDay) {\n throw new Error(\n `Would exceed daily limit ($${this.todaySpending} + $${amount} > $${this.config.limits.maxPerDay})`\n );\n }\n }\n\n /**\n * Record spending\n */\n private recordSpending(amount: number): void {\n this.todaySpending += amount;\n }\n\n // --- Config & Wallet Management ---\n\n private loadConfig(): ClientConfig {\n const configPath = join(this.configDir, 'config.json');\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, 'utf-8');\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) };\n }\n return { ...DEFAULT_CONFIG };\n }\n\n private saveConfig(): void {\n mkdirSync(this.configDir, { recursive: true });\n const configPath = join(this.configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(this.config, null, 2));\n }\n\n private loadWallet(): WalletData | null {\n const walletPath = join(this.configDir, 'wallet.json');\n if (existsSync(walletPath)) {\n const content = readFileSync(walletPath, 'utf-8');\n return JSON.parse(content);\n }\n return null;\n }\n\n /**\n * Initialize a new wallet (called by CLI)\n */\n static init(\n configDir: string,\n options: { chain: string; maxPerTx: number; maxPerDay: number }\n ): { address: string; configDir: string } {\n mkdirSync(configDir, { recursive: true });\n\n // Create wallet\n const wallet = Wallet.createRandom();\n const walletData: WalletData = {\n address: wallet.address,\n privateKey: wallet.privateKey,\n createdAt: Date.now(),\n };\n\n // Save wallet\n const walletPath = join(configDir, 'wallet.json');\n writeFileSync(walletPath, JSON.stringify(walletData, null, 2));\n\n // Save config\n const config: ClientConfig = {\n chain: options.chain,\n limits: {\n maxPerTx: options.maxPerTx,\n maxPerDay: options.maxPerDay,\n },\n };\n const configPath = join(configDir, 'config.json');\n writeFileSync(configPath, JSON.stringify(config, null, 2));\n\n return { address: wallet.address, configDir };\n }\n\n /**\n * Get wallet balance\n */\n async getBalance(): Promise<{ usdc: number; native: number }> {\n if (!this.wallet) {\n throw new Error('Client not initialized');\n }\n\n let chain;\n try {\n chain = getChain(this.config.chain as ChainName);\n } catch {\n throw new Error(`Unknown chain: ${this.config.chain}`);\n }\n\n const provider = new ethers.JsonRpcProvider(chain.rpc);\n\n // Get native balance\n const nativeBalance = await provider.getBalance(this.wallet.address);\n\n // Get USDC balance\n const usdcAbi = ['function balanceOf(address) view returns (uint256)'];\n const usdc = new ethers.Contract(chain.usdc, usdcAbi, provider);\n const usdcBalance = await usdc.balanceOf(this.wallet.address);\n\n return {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, 6)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\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":[]}
|
package/dist/index.js
CHANGED
|
@@ -30392,20 +30392,34 @@ var USDC_DOMAIN = {
|
|
|
30392
30392
|
version: "2"
|
|
30393
30393
|
};
|
|
30394
30394
|
function loadEnvFiles() {
|
|
30395
|
-
|
|
30396
|
-
|
|
30397
|
-
|
|
30398
|
-
|
|
30399
|
-
|
|
30400
|
-
|
|
30401
|
-
|
|
30402
|
-
|
|
30403
|
-
|
|
30395
|
+
const envPaths = [
|
|
30396
|
+
path.join(process.cwd(), ".env"),
|
|
30397
|
+
path.join(process.env.HOME || "", ".moltspay", ".env")
|
|
30398
|
+
];
|
|
30399
|
+
for (const envPath of envPaths) {
|
|
30400
|
+
if ((0, import_fs.existsSync)(envPath)) {
|
|
30401
|
+
try {
|
|
30402
|
+
const content = (0, import_fs.readFileSync)(envPath, "utf-8");
|
|
30403
|
+
for (const line of content.split("\n")) {
|
|
30404
|
+
const trimmed = line.trim();
|
|
30405
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
30406
|
+
const eqIndex = trimmed.indexOf("=");
|
|
30407
|
+
if (eqIndex === -1) continue;
|
|
30408
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
30409
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
30410
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
30411
|
+
value = value.slice(1, -1);
|
|
30412
|
+
}
|
|
30413
|
+
if (!process.env[key]) {
|
|
30414
|
+
process.env[key] = value;
|
|
30415
|
+
}
|
|
30416
|
+
}
|
|
30404
30417
|
console.log(`[MoltsPay] Loaded config from ${envPath}`);
|
|
30405
30418
|
break;
|
|
30419
|
+
} catch (err) {
|
|
30420
|
+
console.warn(`[MoltsPay] Failed to load ${envPath}:`, err);
|
|
30406
30421
|
}
|
|
30407
30422
|
}
|
|
30408
|
-
} catch {
|
|
30409
30423
|
}
|
|
30410
30424
|
}
|
|
30411
30425
|
function getCDPConfig() {
|
|
@@ -30986,9 +31000,17 @@ var MoltsPayClient = class {
|
|
|
30986
31000
|
const authorization = await this.signEIP3009(payTo, amount, chain2);
|
|
30987
31001
|
const payload = {
|
|
30988
31002
|
x402Version: X402_VERSION2,
|
|
30989
|
-
|
|
30990
|
-
|
|
30991
|
-
|
|
31003
|
+
payload: authorization,
|
|
31004
|
+
// v2 requires 'accepted' field with the requirements being fulfilled
|
|
31005
|
+
accepted: {
|
|
31006
|
+
scheme: "exact",
|
|
31007
|
+
network,
|
|
31008
|
+
asset: req.asset || chain2.usdc,
|
|
31009
|
+
amount: amountRaw,
|
|
31010
|
+
payTo,
|
|
31011
|
+
maxTimeoutSeconds: req.maxTimeoutSeconds || 300,
|
|
31012
|
+
extra: req.extra || { name: "USD Coin", version: "2" }
|
|
31013
|
+
}
|
|
30992
31014
|
};
|
|
30993
31015
|
const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
30994
31016
|
console.log(`[MoltsPay] Sending request with payment...`);
|