Mainnet indexer fix + retire testnet copy
- chain.js: derive the chain tip as the MAX height across the RPC pool and scan getLogs against that same synced node. Load-balanced public RPCs (publicnode) intermittently answer eth_blockNumber from a replica lagging thousands of blocks behind, which stalled the mainnet scan (latest < lastBlock, 0 events) and could skip freshly-mined events. This unblocks the live ledger + the purchase-confirmation email (both driven by the event scan). - disclaimer: replace the "rehearsal/testnet" section with a live-on-Polygon real-money notice (it's live now). - Point the dead Amoy fallbacks (purchase email tx link, contract-page source link) at Polygon mainnet / the verified mainnet contract. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -189,15 +189,36 @@ function saveState() {
|
||||
fs.renameSync(tmp, STATE_FILE);
|
||||
} catch (e) { console.error('chain state save failed', e.message); }
|
||||
}
|
||||
// Most-synced tip across the RPC pool. Public load-balanced nodes (publicnode)
|
||||
// sometimes answer eth_blockNumber from a replica lagging thousands of blocks
|
||||
// behind — which stalls the scan (latest < lastBlock) and, worse, could let a
|
||||
// getLogs from a lagging node skip freshly-mined events for good. Take the MAX
|
||||
// height and remember which node reported it, so we scan against a node we know
|
||||
// is synced to that height.
|
||||
async function bestTip() {
|
||||
const urls = getConfig().rpcs;
|
||||
let best = { block: 0, url: urls[0] };
|
||||
await Promise.all(urls.map(async u => {
|
||||
try { const b = parseInt(await rpcOnce(u, 'eth_blockNumber', []), 16);
|
||||
if (b > best.block) best = { block: b, url: u }; } catch (e) {}
|
||||
}));
|
||||
return best;
|
||||
}
|
||||
async function tail() {
|
||||
if (busy) return; busy = true;
|
||||
try {
|
||||
const latest = parseInt(await rpc('eth_blockNumber', []), 16);
|
||||
const tip = await bestTip();
|
||||
const latest = tip.block;
|
||||
while (state.lastBlock < latest) {
|
||||
const from = state.lastBlock + 1;
|
||||
const to = Math.min(from + CHUNK - 1, latest);
|
||||
const logs = await rpc('eth_getLogs', [{ address: getConfig().contract,
|
||||
fromBlock: '0x' + from.toString(16), toBlock: '0x' + to.toString(16) }]);
|
||||
// scan against the node we confirmed is synced to `latest`; fall back to
|
||||
// the rotating pool only if that specific node errors on this range.
|
||||
let logs;
|
||||
try { logs = await rpcOnce(tip.url, 'eth_getLogs', [{ address: getConfig().contract,
|
||||
fromBlock: '0x' + from.toString(16), toBlock: '0x' + to.toString(16) }]); }
|
||||
catch (e) { logs = await rpc('eth_getLogs', [{ address: getConfig().contract,
|
||||
fromBlock: '0x' + from.toString(16), toBlock: '0x' + to.toString(16) }]); }
|
||||
for (const lg of logs) {
|
||||
const ev = decodeLog(lg);
|
||||
if (!ev) continue;
|
||||
|
||||
Reference in New Issue
Block a user