moltspay 0.9.5 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -45
- package/dist/cdp/index.d.mts +1 -1
- package/dist/cdp/index.d.ts +1 -1
- package/dist/cdp/index.js +63 -0
- package/dist/cdp/index.js.map +1 -1
- package/dist/cdp/index.mjs +63 -0
- package/dist/cdp/index.mjs.map +1 -1
- package/dist/chains/index.d.mts +9 -5
- package/dist/chains/index.d.ts +9 -5
- package/dist/chains/index.js +85 -0
- package/dist/chains/index.js.map +1 -1
- package/dist/chains/index.mjs +83 -0
- package/dist/chains/index.mjs.map +1 -1
- package/dist/cli/index.js +201 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +201 -38
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +18 -3
- package/dist/client/index.d.ts +18 -3
- package/dist/client/index.js +112 -15
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +112 -15
- package/dist/client/index.mjs.map +1 -1
- package/dist/{index-Dg8n6wdW.d.mts → index-B3v8IWjM.d.mts} +11 -1
- package/dist/{index-Dg8n6wdW.d.ts → index-B3v8IWjM.d.ts} +11 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +203 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +203 -42
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +19 -1
- package/dist/server/index.d.ts +19 -1
- package/dist/server/index.js +71 -19
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +71 -19
- package/dist/server/index.mjs.map +1 -1
- package/dist/verify/index.d.mts +7 -0
- package/dist/verify/index.d.ts +7 -0
- package/dist/verify/index.js +83 -8
- package/dist/verify/index.js.map +1 -1
- package/dist/verify/index.mjs +83 -8
- package/dist/verify/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +16 -8
- package/dist/wallet/index.d.ts +16 -8
- package/dist/wallet/index.js +114 -18
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +114 -18
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
- package/schemas/moltspay.services.schema.json +13 -3
package/dist/verify/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/verify/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\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":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/verify/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * On-chain Payment Verification Module\n */\n\nimport { ethers } from 'ethers';\nimport { getChain, getChainById, type ChainConfig, type ChainName, type TokenSymbol } 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 /** Expected token (if not specified, accepts both USDC and USDT) */\n expectedToken?: TokenSymbol;\n}\n\nexport interface VerifyPaymentResult {\n verified: boolean;\n amount?: number;\n token?: TokenSymbol;\n from?: string;\n to?: string;\n txHash?: string;\n blockNumber?: number;\n error?: string;\n}\n\n/**\n * Verify on-chain payment\n * Supports both USDC and USDT transfers\n */\nexport async function verifyPayment(params: VerifyPaymentParams): Promise<VerifyPaymentResult> {\n const { txHash, expectedAmount, expectedTo, expectedToken } = 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 // Build map of accepted token addresses\n const tokenAddresses: Record<string, TokenSymbol> = {};\n \n if (!expectedToken || expectedToken === 'USDC') {\n tokenAddresses[chain.tokens.USDC.address.toLowerCase()] = 'USDC';\n }\n if (!expectedToken || expectedToken === 'USDT') {\n tokenAddresses[chain.tokens.USDT.address.toLowerCase()] = 'USDT';\n }\n\n if (Object.keys(tokenAddresses).length === 0) {\n return { verified: false, error: `No token addresses configured for ${chain.name}` };\n }\n\n for (const log of receipt.logs) {\n const logAddress = log.address.toLowerCase();\n \n // Check if this is one of our accepted tokens\n const detectedToken = tokenAddresses[logAddress];\n if (!detectedToken) {\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 tokenConfig = chain.tokens[detectedToken];\n const amount = Number(amountRaw) / (10 ** tokenConfig.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} ${detectedToken}, expected ${expectedAmount}`,\n amount,\n token: detectedToken,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n // Verification successful\n return {\n verified: true,\n amount,\n token: detectedToken,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n const tokenList = expectedToken ? expectedToken : 'USDC/USDT';\n return { verified: false, error: `No ${tokenList} 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, TokenSymbol } 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 tokens: {\n USDC: {\n address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n decimals: 6,\n symbol: 'USDT',\n },\n },\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // deprecated, for backward compat\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-rpc.com',\n tokens: {\n USDC: {\n address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 tokens: {\n USDC: {\n address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 tokens: {\n USDC: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Same as USDC on testnet (no official USDT)\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 tokens: {\n USDC: {\n address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', // Same as USDC on testnet\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 token address for a chain\n */\nexport function getTokenAddress(chainName: ChainName, token: TokenSymbol): string {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n const tokenConfig = chain.tokens[token];\n if (!tokenConfig) {\n throw new Error(`Token ${token} not supported on ${chainName}`);\n }\n return tokenConfig.address;\n}\n\n/**\n * Get token config for a chain\n */\nexport function getTokenConfig(chainName: ChainName, token: TokenSymbol) {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n return chain.tokens[token];\n}\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: 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, TokenSymbol };\n"],"mappings":"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","names":[]}
|
package/dist/verify/index.mjs
CHANGED
|
@@ -8,7 +8,20 @@ var CHAINS = {
|
|
|
8
8
|
name: "Base",
|
|
9
9
|
chainId: 8453,
|
|
10
10
|
rpc: "https://mainnet.base.org",
|
|
11
|
+
tokens: {
|
|
12
|
+
USDC: {
|
|
13
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
14
|
+
decimals: 6,
|
|
15
|
+
symbol: "USDC"
|
|
16
|
+
},
|
|
17
|
+
USDT: {
|
|
18
|
+
address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
19
|
+
decimals: 6,
|
|
20
|
+
symbol: "USDT"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
11
23
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
24
|
+
// deprecated, for backward compat
|
|
12
25
|
explorer: "https://basescan.org/address/",
|
|
13
26
|
explorerTx: "https://basescan.org/tx/",
|
|
14
27
|
avgBlockTime: 2
|
|
@@ -17,6 +30,18 @@ var CHAINS = {
|
|
|
17
30
|
name: "Polygon",
|
|
18
31
|
chainId: 137,
|
|
19
32
|
rpc: "https://polygon-rpc.com",
|
|
33
|
+
tokens: {
|
|
34
|
+
USDC: {
|
|
35
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
36
|
+
decimals: 6,
|
|
37
|
+
symbol: "USDC"
|
|
38
|
+
},
|
|
39
|
+
USDT: {
|
|
40
|
+
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
41
|
+
decimals: 6,
|
|
42
|
+
symbol: "USDT"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
20
45
|
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
21
46
|
explorer: "https://polygonscan.com/address/",
|
|
22
47
|
explorerTx: "https://polygonscan.com/tx/",
|
|
@@ -26,6 +51,18 @@ var CHAINS = {
|
|
|
26
51
|
name: "Ethereum",
|
|
27
52
|
chainId: 1,
|
|
28
53
|
rpc: "https://eth.llamarpc.com",
|
|
54
|
+
tokens: {
|
|
55
|
+
USDC: {
|
|
56
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
57
|
+
decimals: 6,
|
|
58
|
+
symbol: "USDC"
|
|
59
|
+
},
|
|
60
|
+
USDT: {
|
|
61
|
+
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
62
|
+
decimals: 6,
|
|
63
|
+
symbol: "USDT"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
29
66
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
30
67
|
explorer: "https://etherscan.io/address/",
|
|
31
68
|
explorerTx: "https://etherscan.io/tx/",
|
|
@@ -36,6 +73,19 @@ var CHAINS = {
|
|
|
36
73
|
name: "Base Sepolia",
|
|
37
74
|
chainId: 84532,
|
|
38
75
|
rpc: "https://sepolia.base.org",
|
|
76
|
+
tokens: {
|
|
77
|
+
USDC: {
|
|
78
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
79
|
+
decimals: 6,
|
|
80
|
+
symbol: "USDC"
|
|
81
|
+
},
|
|
82
|
+
USDT: {
|
|
83
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
84
|
+
// Same as USDC on testnet (no official USDT)
|
|
85
|
+
decimals: 6,
|
|
86
|
+
symbol: "USDT"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
39
89
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
40
90
|
explorer: "https://sepolia.basescan.org/address/",
|
|
41
91
|
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
@@ -45,6 +95,19 @@ var CHAINS = {
|
|
|
45
95
|
name: "Sepolia",
|
|
46
96
|
chainId: 11155111,
|
|
47
97
|
rpc: "https://rpc.sepolia.org",
|
|
98
|
+
tokens: {
|
|
99
|
+
USDC: {
|
|
100
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
101
|
+
decimals: 6,
|
|
102
|
+
symbol: "USDC"
|
|
103
|
+
},
|
|
104
|
+
USDT: {
|
|
105
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
106
|
+
// Same as USDC on testnet
|
|
107
|
+
decimals: 6,
|
|
108
|
+
symbol: "USDT"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
48
111
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
49
112
|
explorer: "https://sepolia.etherscan.io/address/",
|
|
50
113
|
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
@@ -65,7 +128,7 @@ function getChainById(chainId) {
|
|
|
65
128
|
// src/verify/index.ts
|
|
66
129
|
var TRANSFER_EVENT_TOPIC = ethers.id("Transfer(address,address,uint256)");
|
|
67
130
|
async function verifyPayment(params) {
|
|
68
|
-
const { txHash, expectedAmount, expectedTo } = params;
|
|
131
|
+
const { txHash, expectedAmount, expectedTo, expectedToken } = params;
|
|
69
132
|
let chain;
|
|
70
133
|
try {
|
|
71
134
|
if (typeof params.chain === "number") {
|
|
@@ -88,12 +151,20 @@ async function verifyPayment(params) {
|
|
|
88
151
|
if (receipt.status !== 1) {
|
|
89
152
|
return { verified: false, error: "Transaction failed" };
|
|
90
153
|
}
|
|
91
|
-
const
|
|
92
|
-
if (!
|
|
93
|
-
|
|
154
|
+
const tokenAddresses = {};
|
|
155
|
+
if (!expectedToken || expectedToken === "USDC") {
|
|
156
|
+
tokenAddresses[chain.tokens.USDC.address.toLowerCase()] = "USDC";
|
|
157
|
+
}
|
|
158
|
+
if (!expectedToken || expectedToken === "USDT") {
|
|
159
|
+
tokenAddresses[chain.tokens.USDT.address.toLowerCase()] = "USDT";
|
|
160
|
+
}
|
|
161
|
+
if (Object.keys(tokenAddresses).length === 0) {
|
|
162
|
+
return { verified: false, error: `No token addresses configured for ${chain.name}` };
|
|
94
163
|
}
|
|
95
164
|
for (const log of receipt.logs) {
|
|
96
|
-
|
|
165
|
+
const logAddress = log.address.toLowerCase();
|
|
166
|
+
const detectedToken = tokenAddresses[logAddress];
|
|
167
|
+
if (!detectedToken) {
|
|
97
168
|
continue;
|
|
98
169
|
}
|
|
99
170
|
if (log.topics.length < 3 || log.topics[0] !== TRANSFER_EVENT_TOPIC) {
|
|
@@ -102,15 +173,17 @@ async function verifyPayment(params) {
|
|
|
102
173
|
const from = "0x" + log.topics[1].slice(-40);
|
|
103
174
|
const to = "0x" + log.topics[2].slice(-40);
|
|
104
175
|
const amountRaw = BigInt(log.data);
|
|
105
|
-
const
|
|
176
|
+
const tokenConfig = chain.tokens[detectedToken];
|
|
177
|
+
const amount = Number(amountRaw) / 10 ** tokenConfig.decimals;
|
|
106
178
|
if (expectedTo && to.toLowerCase() !== expectedTo.toLowerCase()) {
|
|
107
179
|
continue;
|
|
108
180
|
}
|
|
109
181
|
if (amount < expectedAmount) {
|
|
110
182
|
return {
|
|
111
183
|
verified: false,
|
|
112
|
-
error: `Insufficient amount: received ${amount}
|
|
184
|
+
error: `Insufficient amount: received ${amount} ${detectedToken}, expected ${expectedAmount}`,
|
|
113
185
|
amount,
|
|
186
|
+
token: detectedToken,
|
|
114
187
|
from,
|
|
115
188
|
to,
|
|
116
189
|
txHash,
|
|
@@ -120,13 +193,15 @@ async function verifyPayment(params) {
|
|
|
120
193
|
return {
|
|
121
194
|
verified: true,
|
|
122
195
|
amount,
|
|
196
|
+
token: detectedToken,
|
|
123
197
|
from,
|
|
124
198
|
to,
|
|
125
199
|
txHash,
|
|
126
200
|
blockNumber: receipt.blockNumber
|
|
127
201
|
};
|
|
128
202
|
}
|
|
129
|
-
|
|
203
|
+
const tokenList = expectedToken ? expectedToken : "USDC/USDT";
|
|
204
|
+
return { verified: false, error: `No ${tokenList} transfer found` };
|
|
130
205
|
} catch (e) {
|
|
131
206
|
return { verified: false, error: e.message || String(e) };
|
|
132
207
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/verify/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\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":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/verify/index.ts","../../src/chains/index.ts"],"sourcesContent":["/**\n * On-chain Payment Verification Module\n */\n\nimport { ethers } from 'ethers';\nimport { getChain, getChainById, type ChainConfig, type ChainName, type TokenSymbol } 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 /** Expected token (if not specified, accepts both USDC and USDT) */\n expectedToken?: TokenSymbol;\n}\n\nexport interface VerifyPaymentResult {\n verified: boolean;\n amount?: number;\n token?: TokenSymbol;\n from?: string;\n to?: string;\n txHash?: string;\n blockNumber?: number;\n error?: string;\n}\n\n/**\n * Verify on-chain payment\n * Supports both USDC and USDT transfers\n */\nexport async function verifyPayment(params: VerifyPaymentParams): Promise<VerifyPaymentResult> {\n const { txHash, expectedAmount, expectedTo, expectedToken } = 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 // Build map of accepted token addresses\n const tokenAddresses: Record<string, TokenSymbol> = {};\n \n if (!expectedToken || expectedToken === 'USDC') {\n tokenAddresses[chain.tokens.USDC.address.toLowerCase()] = 'USDC';\n }\n if (!expectedToken || expectedToken === 'USDT') {\n tokenAddresses[chain.tokens.USDT.address.toLowerCase()] = 'USDT';\n }\n\n if (Object.keys(tokenAddresses).length === 0) {\n return { verified: false, error: `No token addresses configured for ${chain.name}` };\n }\n\n for (const log of receipt.logs) {\n const logAddress = log.address.toLowerCase();\n \n // Check if this is one of our accepted tokens\n const detectedToken = tokenAddresses[logAddress];\n if (!detectedToken) {\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 tokenConfig = chain.tokens[detectedToken];\n const amount = Number(amountRaw) / (10 ** tokenConfig.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} ${detectedToken}, expected ${expectedAmount}`,\n amount,\n token: detectedToken,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n // Verification successful\n return {\n verified: true,\n amount,\n token: detectedToken,\n from,\n to,\n txHash,\n blockNumber: receipt.blockNumber,\n };\n }\n\n const tokenList = expectedToken ? expectedToken : 'USDC/USDT';\n return { verified: false, error: `No ${tokenList} 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, TokenSymbol } 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 tokens: {\n USDC: {\n address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n decimals: 6,\n symbol: 'USDT',\n },\n },\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // deprecated, for backward compat\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-rpc.com',\n tokens: {\n USDC: {\n address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 tokens: {\n USDC: {\n address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 tokens: {\n USDC: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Same as USDC on testnet (no official USDT)\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 tokens: {\n USDC: {\n address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',\n decimals: 6,\n symbol: 'USDC',\n },\n USDT: {\n address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', // Same as USDC on testnet\n decimals: 6,\n symbol: 'USDT',\n },\n },\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 token address for a chain\n */\nexport function getTokenAddress(chainName: ChainName, token: TokenSymbol): string {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n const tokenConfig = chain.tokens[token];\n if (!tokenConfig) {\n throw new Error(`Token ${token} not supported on ${chainName}`);\n }\n return tokenConfig.address;\n}\n\n/**\n * Get token config for a chain\n */\nexport function getTokenConfig(chainName: ChainName, token: TokenSymbol) {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n return chain.tokens[token];\n}\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: 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, TokenSymbol };\n"],"mappings":"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","names":[]}
|
package/dist/wallet/index.d.mts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { C as ChainName, a as ChainConfig, W as WalletBalance, T as TransferResult } from '../index-
|
|
1
|
+
import { C as ChainName, a as ChainConfig, W as WalletBalance, T as TokenSymbol, c as TransferResult } from '../index-B3v8IWjM.mjs';
|
|
2
2
|
export { C as CreateWalletOptions, a as CreateWalletResult, W as WalletData, c as createWallet, g as getWalletAddress, l as loadWallet, w as walletExists } from '../createWallet-D53qu7ie.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Wallet - Basic Custody Wallet
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
8
|
-
* - Query balance
|
|
9
|
-
* - Send USDC
|
|
8
|
+
* - Query balance (USDC, USDT, ETH)
|
|
9
|
+
* - Send token transfers (USDC or USDT)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
interface WalletConfig {
|
|
@@ -20,22 +20,30 @@ declare class Wallet {
|
|
|
20
20
|
readonly address: string;
|
|
21
21
|
private wallet;
|
|
22
22
|
private provider;
|
|
23
|
-
private
|
|
23
|
+
private tokenContracts;
|
|
24
24
|
constructor(config?: WalletConfig);
|
|
25
25
|
/**
|
|
26
|
-
* Get wallet balance
|
|
26
|
+
* Get wallet balance for all tokens
|
|
27
27
|
*/
|
|
28
28
|
getBalance(): Promise<WalletBalance>;
|
|
29
29
|
/**
|
|
30
|
-
* Send USDC
|
|
30
|
+
* Send token transfer (USDC or USDT)
|
|
31
|
+
* @param to - recipient address
|
|
32
|
+
* @param amount - amount to send
|
|
33
|
+
* @param token - token to send (default: USDC)
|
|
31
34
|
*/
|
|
32
|
-
transfer(to: string, amount: number): Promise<TransferResult>;
|
|
35
|
+
transfer(to: string, amount: number, token?: TokenSymbol): Promise<TransferResult>;
|
|
33
36
|
/**
|
|
34
37
|
* Get ETH balance
|
|
35
38
|
*/
|
|
36
39
|
getEthBalance(): Promise<string>;
|
|
37
40
|
/**
|
|
38
|
-
* Get
|
|
41
|
+
* Get token balance
|
|
42
|
+
* @param token - token symbol (default: USDC)
|
|
43
|
+
*/
|
|
44
|
+
getTokenBalance(token?: TokenSymbol): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use getTokenBalance('USDC') instead
|
|
39
47
|
*/
|
|
40
48
|
getUsdcBalance(): Promise<string>;
|
|
41
49
|
}
|
package/dist/wallet/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { C as ChainName, a as ChainConfig, W as WalletBalance, T as TransferResult } from '../index-
|
|
1
|
+
import { C as ChainName, a as ChainConfig, W as WalletBalance, T as TokenSymbol, c as TransferResult } from '../index-B3v8IWjM.js';
|
|
2
2
|
export { C as CreateWalletOptions, a as CreateWalletResult, W as WalletData, c as createWallet, g as getWalletAddress, l as loadWallet, w as walletExists } from '../createWallet-D53qu7ie.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Wallet - Basic Custody Wallet
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
8
|
-
* - Query balance
|
|
9
|
-
* - Send USDC
|
|
8
|
+
* - Query balance (USDC, USDT, ETH)
|
|
9
|
+
* - Send token transfers (USDC or USDT)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
interface WalletConfig {
|
|
@@ -20,22 +20,30 @@ declare class Wallet {
|
|
|
20
20
|
readonly address: string;
|
|
21
21
|
private wallet;
|
|
22
22
|
private provider;
|
|
23
|
-
private
|
|
23
|
+
private tokenContracts;
|
|
24
24
|
constructor(config?: WalletConfig);
|
|
25
25
|
/**
|
|
26
|
-
* Get wallet balance
|
|
26
|
+
* Get wallet balance for all tokens
|
|
27
27
|
*/
|
|
28
28
|
getBalance(): Promise<WalletBalance>;
|
|
29
29
|
/**
|
|
30
|
-
* Send USDC
|
|
30
|
+
* Send token transfer (USDC or USDT)
|
|
31
|
+
* @param to - recipient address
|
|
32
|
+
* @param amount - amount to send
|
|
33
|
+
* @param token - token to send (default: USDC)
|
|
31
34
|
*/
|
|
32
|
-
transfer(to: string, amount: number): Promise<TransferResult>;
|
|
35
|
+
transfer(to: string, amount: number, token?: TokenSymbol): Promise<TransferResult>;
|
|
33
36
|
/**
|
|
34
37
|
* Get ETH balance
|
|
35
38
|
*/
|
|
36
39
|
getEthBalance(): Promise<string>;
|
|
37
40
|
/**
|
|
38
|
-
* Get
|
|
41
|
+
* Get token balance
|
|
42
|
+
* @param token - token symbol (default: USDC)
|
|
43
|
+
*/
|
|
44
|
+
getTokenBalance(token?: TokenSymbol): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use getTokenBalance('USDC') instead
|
|
39
47
|
*/
|
|
40
48
|
getUsdcBalance(): Promise<string>;
|
|
41
49
|
}
|
package/dist/wallet/index.js
CHANGED
|
@@ -38,7 +38,20 @@ var CHAINS = {
|
|
|
38
38
|
name: "Base",
|
|
39
39
|
chainId: 8453,
|
|
40
40
|
rpc: "https://mainnet.base.org",
|
|
41
|
+
tokens: {
|
|
42
|
+
USDC: {
|
|
43
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
44
|
+
decimals: 6,
|
|
45
|
+
symbol: "USDC"
|
|
46
|
+
},
|
|
47
|
+
USDT: {
|
|
48
|
+
address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
|
49
|
+
decimals: 6,
|
|
50
|
+
symbol: "USDT"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
41
53
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
54
|
+
// deprecated, for backward compat
|
|
42
55
|
explorer: "https://basescan.org/address/",
|
|
43
56
|
explorerTx: "https://basescan.org/tx/",
|
|
44
57
|
avgBlockTime: 2
|
|
@@ -47,6 +60,18 @@ var CHAINS = {
|
|
|
47
60
|
name: "Polygon",
|
|
48
61
|
chainId: 137,
|
|
49
62
|
rpc: "https://polygon-rpc.com",
|
|
63
|
+
tokens: {
|
|
64
|
+
USDC: {
|
|
65
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
66
|
+
decimals: 6,
|
|
67
|
+
symbol: "USDC"
|
|
68
|
+
},
|
|
69
|
+
USDT: {
|
|
70
|
+
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
71
|
+
decimals: 6,
|
|
72
|
+
symbol: "USDT"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
50
75
|
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
51
76
|
explorer: "https://polygonscan.com/address/",
|
|
52
77
|
explorerTx: "https://polygonscan.com/tx/",
|
|
@@ -56,6 +81,18 @@ var CHAINS = {
|
|
|
56
81
|
name: "Ethereum",
|
|
57
82
|
chainId: 1,
|
|
58
83
|
rpc: "https://eth.llamarpc.com",
|
|
84
|
+
tokens: {
|
|
85
|
+
USDC: {
|
|
86
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
87
|
+
decimals: 6,
|
|
88
|
+
symbol: "USDC"
|
|
89
|
+
},
|
|
90
|
+
USDT: {
|
|
91
|
+
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
92
|
+
decimals: 6,
|
|
93
|
+
symbol: "USDT"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
59
96
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
60
97
|
explorer: "https://etherscan.io/address/",
|
|
61
98
|
explorerTx: "https://etherscan.io/tx/",
|
|
@@ -66,6 +103,19 @@ var CHAINS = {
|
|
|
66
103
|
name: "Base Sepolia",
|
|
67
104
|
chainId: 84532,
|
|
68
105
|
rpc: "https://sepolia.base.org",
|
|
106
|
+
tokens: {
|
|
107
|
+
USDC: {
|
|
108
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
109
|
+
decimals: 6,
|
|
110
|
+
symbol: "USDC"
|
|
111
|
+
},
|
|
112
|
+
USDT: {
|
|
113
|
+
address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
114
|
+
// Same as USDC on testnet (no official USDT)
|
|
115
|
+
decimals: 6,
|
|
116
|
+
symbol: "USDT"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
69
119
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
70
120
|
explorer: "https://sepolia.basescan.org/address/",
|
|
71
121
|
explorerTx: "https://sepolia.basescan.org/tx/",
|
|
@@ -75,6 +125,19 @@ var CHAINS = {
|
|
|
75
125
|
name: "Sepolia",
|
|
76
126
|
chainId: 11155111,
|
|
77
127
|
rpc: "https://rpc.sepolia.org",
|
|
128
|
+
tokens: {
|
|
129
|
+
USDC: {
|
|
130
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
131
|
+
decimals: 6,
|
|
132
|
+
symbol: "USDC"
|
|
133
|
+
},
|
|
134
|
+
USDT: {
|
|
135
|
+
address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
136
|
+
// Same as USDC on testnet
|
|
137
|
+
decimals: 6,
|
|
138
|
+
symbol: "USDT"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
78
141
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
79
142
|
explorer: "https://sepolia.etherscan.io/address/",
|
|
80
143
|
explorerTx: "https://sepolia.etherscan.io/tx/",
|
|
@@ -109,7 +172,7 @@ var Wallet = class {
|
|
|
109
172
|
address;
|
|
110
173
|
wallet;
|
|
111
174
|
provider;
|
|
112
|
-
|
|
175
|
+
tokenContracts;
|
|
113
176
|
constructor(config = {}) {
|
|
114
177
|
this.chain = config.chain || "base_sepolia";
|
|
115
178
|
this.chainConfig = getChain(this.chain);
|
|
@@ -121,42 +184,63 @@ var Wallet = class {
|
|
|
121
184
|
this.provider = new import_ethers.ethers.JsonRpcProvider(rpcUrl);
|
|
122
185
|
this.wallet = new import_ethers.ethers.Wallet(privateKey, this.provider);
|
|
123
186
|
this.address = this.wallet.address;
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
187
|
+
this.tokenContracts = {
|
|
188
|
+
USDC: new import_ethers.ethers.Contract(
|
|
189
|
+
this.chainConfig.tokens.USDC.address,
|
|
190
|
+
ERC20_ABI,
|
|
191
|
+
this.wallet
|
|
192
|
+
),
|
|
193
|
+
USDT: new import_ethers.ethers.Contract(
|
|
194
|
+
this.chainConfig.tokens.USDT.address,
|
|
195
|
+
ERC20_ABI,
|
|
196
|
+
this.wallet
|
|
197
|
+
)
|
|
198
|
+
};
|
|
129
199
|
}
|
|
130
200
|
/**
|
|
131
|
-
* Get wallet balance
|
|
201
|
+
* Get wallet balance for all tokens
|
|
132
202
|
*/
|
|
133
203
|
async getBalance() {
|
|
134
|
-
const [ethBalance, usdcBalance] = await Promise.all([
|
|
204
|
+
const [ethBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
135
205
|
this.provider.getBalance(this.address),
|
|
136
|
-
this.
|
|
206
|
+
this.tokenContracts.USDC.balanceOf(this.address),
|
|
207
|
+
this.tokenContracts.USDT.balanceOf(this.address)
|
|
137
208
|
]);
|
|
138
209
|
return {
|
|
139
210
|
address: this.address,
|
|
140
211
|
eth: import_ethers.ethers.formatEther(ethBalance),
|
|
141
212
|
usdc: (Number(usdcBalance) / 1e6).toFixed(2),
|
|
213
|
+
usdt: (Number(usdtBalance) / 1e6).toFixed(2),
|
|
142
214
|
chain: this.chain
|
|
143
215
|
};
|
|
144
216
|
}
|
|
145
217
|
/**
|
|
146
|
-
* Send USDC
|
|
218
|
+
* Send token transfer (USDC or USDT)
|
|
219
|
+
* @param to - recipient address
|
|
220
|
+
* @param amount - amount to send
|
|
221
|
+
* @param token - token to send (default: USDC)
|
|
147
222
|
*/
|
|
148
|
-
async transfer(to, amount) {
|
|
223
|
+
async transfer(to, amount, token = "USDC") {
|
|
149
224
|
try {
|
|
150
225
|
to = import_ethers.ethers.getAddress(to);
|
|
151
|
-
const
|
|
152
|
-
const
|
|
226
|
+
const tokenContract = this.tokenContracts[token];
|
|
227
|
+
const tokenConfig = this.chainConfig.tokens[token];
|
|
228
|
+
if (!tokenContract || !tokenConfig) {
|
|
229
|
+
return {
|
|
230
|
+
success: false,
|
|
231
|
+
error: `Token ${token} not supported on ${this.chain}`
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const decimals = tokenConfig.decimals;
|
|
235
|
+
const amountWei = BigInt(Math.floor(amount * 10 ** decimals));
|
|
236
|
+
const balance = await tokenContract.balanceOf(this.address);
|
|
153
237
|
if (BigInt(balance) < amountWei) {
|
|
154
238
|
return {
|
|
155
239
|
success: false,
|
|
156
|
-
error: `Insufficient
|
|
240
|
+
error: `Insufficient ${token} balance: ${Number(balance) / 10 ** decimals} < ${amount}`
|
|
157
241
|
};
|
|
158
242
|
}
|
|
159
|
-
const tx = await
|
|
243
|
+
const tx = await tokenContract.transfer(to, amountWei);
|
|
160
244
|
const receipt = await tx.wait();
|
|
161
245
|
if (receipt.status === 1) {
|
|
162
246
|
return {
|
|
@@ -165,6 +249,7 @@ var Wallet = class {
|
|
|
165
249
|
from: this.address,
|
|
166
250
|
to,
|
|
167
251
|
amount,
|
|
252
|
+
token,
|
|
168
253
|
gas_used: Number(receipt.gasUsed),
|
|
169
254
|
block_number: receipt.blockNumber,
|
|
170
255
|
explorer_url: `${this.chainConfig.explorerTx}${tx.hash}`
|
|
@@ -173,12 +258,14 @@ var Wallet = class {
|
|
|
173
258
|
return {
|
|
174
259
|
success: false,
|
|
175
260
|
tx_hash: tx.hash,
|
|
261
|
+
token,
|
|
176
262
|
error: "Transaction reverted"
|
|
177
263
|
};
|
|
178
264
|
}
|
|
179
265
|
} catch (error) {
|
|
180
266
|
return {
|
|
181
267
|
success: false,
|
|
268
|
+
token,
|
|
182
269
|
error: error.message
|
|
183
270
|
};
|
|
184
271
|
}
|
|
@@ -191,11 +278,20 @@ var Wallet = class {
|
|
|
191
278
|
return import_ethers.ethers.formatEther(balance);
|
|
192
279
|
}
|
|
193
280
|
/**
|
|
194
|
-
* Get
|
|
281
|
+
* Get token balance
|
|
282
|
+
* @param token - token symbol (default: USDC)
|
|
283
|
+
*/
|
|
284
|
+
async getTokenBalance(token = "USDC") {
|
|
285
|
+
const tokenContract = this.tokenContracts[token];
|
|
286
|
+
const tokenConfig = this.chainConfig.tokens[token];
|
|
287
|
+
const balance = await tokenContract.balanceOf(this.address);
|
|
288
|
+
return (Number(balance) / 10 ** tokenConfig.decimals).toFixed(2);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* @deprecated Use getTokenBalance('USDC') instead
|
|
195
292
|
*/
|
|
196
293
|
async getUsdcBalance() {
|
|
197
|
-
|
|
198
|
-
return (Number(balance) / 1e6).toFixed(2);
|
|
294
|
+
return this.getTokenBalance("USDC");
|
|
199
295
|
}
|
|
200
296
|
};
|
|
201
297
|
|