const { JsonRpcProvider } = require('ethers'); const { readDb, writeDb } = require('../storage'); const { getEthereumRpcUrls, createRoundRobinPicker } = require('../utils/rpc'); function startEvmWatcher({ notifyMerchant }) { const rpcUrls = getEthereumRpcUrls(); if (!rpcUrls.length) { console.warn('[evmWatcher] No Ethereum RPC URLs configured. Skipping watcher.'); return; } const pollInterval = Number(process.env.TX_POLL_INTERVAL_MS || 180000); const maxPerCycle = Number(process.env.MAX_TX_CHECK_PER_CYCLE || 20); const minConfirmations = Number(process.env.MIN_CONFIRMATIONS || 1); const pickRpcUrl = createRoundRobinPicker(rpcUrls); let running = false; async function poll() { if (running) return; running = true; try { const db = await readDb(); const pending = db.txs.filter((tx) => tx.network === 'evm:ethereum' && tx.status === 'pending'); if (pending.length === 0) { running = false; return; } const rpcUrl = pickRpcUrl(); const provider = new JsonRpcProvider(rpcUrl); const latestBlock = await provider.getBlockNumber(); const now = new Date().toISOString(); const confirmedEvents = []; for (const tx of pending.slice(0, maxPerCycle)) { const receipt = await provider.getTransactionReceipt(tx.txHash); tx.lastCheckedAt = now; if (!receipt) continue; const confirmations = latestBlock - receipt.blockNumber + 1; tx.confirmations = confirmations; tx.updatedAt = now; if (receipt.status === 1 && confirmations >= minConfirmations) { tx.status = 'confirmed'; const order = db.orders.find((item) => item.id === tx.orderId); if (order) { order.status = 'paid'; order.updatedAt = now; confirmedEvents.push({ order, tx }); } } else if (receipt.status === 0) { tx.status = 'failed'; } } await writeDb(db); for (const event of confirmedEvents) { await notifyMerchant(event.order, event.tx); } } catch (error) { console.error('[evmWatcher] poll error', error.message); } finally { running = false; } } poll(); setInterval(poll, pollInterval); console.log(`[evmWatcher] started with interval ${pollInterval}ms`); } module.exports = { startEvmWatcher };