// node backfill-receives.js [--dry-run]: for every stored entry whose funding sources lack `receives`/`last_at` // (entries recorded before 2026-09-22), re-read the address's receive history before received_at from the node and // add the per-transfer list to the matching source. Never changes amount_raw or first_at; a source whose re-read // amount differs from the stored one is logged and left without receives. Idempotent. const path = require('path'); const L = require('./ladder'); const DATA = process.env.DATA_DIR || path.join(__dirname, 'data'); const ENTRIES = path.join(DATA, 'entries.json'); const RPC = process.env.NANO_RPC || 'http://127.0.0.1:7076'; const dry = process.argv.includes('--dry-run'); async function rpc(body) { const r = await fetch(RPC, { method: 'POST', body: JSON.stringify(body) }); return r.json(); } (async () => { const all = L.readJson(ENTRIES, {}); let n = 0, skipped = 0; for (const [rid, byAddr] of Object.entries(all)) for (const [addr, e] of Object.entries(byAddr)) { if (!e.funding || !Array.isArray(e.funding.sources) || !e.funding.sources.length) continue; if (e.funding.sources.every(s => Array.isArray(s.receives))) continue; const fresh = await L.fundingSources(rpc, addr, Date.parse(e.received_at)); const by = Object.fromEntries(fresh.sources.map(s => [s.account, s])); for (const s of e.funding.sources) { if (Array.isArray(s.receives)) continue; const f = by[s.account]; if (!f) { console.log('round', rid, addr.slice(0, 16), 'source', s.account.slice(0, 16), 'not found on re-read; left as is'); skipped++; continue; } if (f.amount_raw !== s.amount_raw) { console.log('round', rid, addr.slice(0, 16), 'source', s.account.slice(0, 16), 'amount differs', s.amount_raw, 'vs', f.amount_raw, '; left as is'); skipped++; continue; } s.last_at = f.last_at; s.receives = f.receives; n++; console.log('round', rid, addr.slice(0, 16), 'source', s.account.slice(0, 16), '->', f.receives.length, 'receive(s), last', f.last_at); } e.funding.receives_backfilled_at = new Date().toISOString(); } if (!dry && n) L.writeJsonAtomic(ENTRIES, all); console.log(dry ? 'dry run,' : 'wrote', n, 'source(s) backfilled,', skipped, 'skipped'); })();