Chain index keeps the full event history (was a 600-event window); one-time rescan from the deploy block
Hugh's Earnings page showed no payouts, referrals or purchases: /api/my/activity read the last 600 events and the window had moved past his Sept 9-10 activity. Ten other readers (dashboard earned total, leaderboard, holding-tank own-buy check, admin member view, growth snapshot, P&L, burner match) treated the same list as complete. KEEP_EVENTS 600 -> 250000; chain.rescan() rebuilds from deployBlock without re-firing onEvent (no repeat Telegram/email), keeps real ts for known events and estimates ts by block height for backfilled ones; runs once at boot when a filled window is found; admin routes POST /api/admin/chain/rescan and GET /api/admin/chain/status. State file only rewritten when events changed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -656,7 +656,7 @@ async function emailOnEvent(ev) {
|
||||
const PCT = { 1: 50, 2: 20, 3: 10 };
|
||||
// the Purchase event of the same tx is already indexed (it precedes every payout log), so the
|
||||
// dollar side of any share is that purchase's price times the tier percentage
|
||||
const purchaseOf = tx => { try { return chain.recentEvents(600).find(e => e.tx === tx && e.type === 'Purchase'); } catch (e) { return null; } };
|
||||
const purchaseOf = tx => { try { return chain.recentEvents(1e9).find(e => e.tx === tx && e.type === 'Purchase'); } catch (e) { return null; } };
|
||||
const usdShare = (pur, pct) => pur ? (' (about ' + ('$' + (pur.priceCents * pct / 10000).toFixed(2)).replace(/\.00$/, '') + ')') : '';
|
||||
const txUrlOf = tx => { const cc = chain.getConfig(); return (cc.explorer ? cc.explorer.replace(/\/+$/, '') : 'https://polygonscan.com') + '/tx/' + tx; };
|
||||
if (ev.type === 'Purchase') {
|
||||
@@ -1213,7 +1213,7 @@ const server = http.createServer(async (req, res) => {
|
||||
out.credits = bal.total; out.creditedCredits = bal.credited; out.earnedCredits = bal.earned; out.inCampaigns = bal.inCampaigns; out.availableCredits = bal.available;
|
||||
} catch (e) { out.chainReadError = true; }
|
||||
let earned = 0n, n = 0;
|
||||
for (const ev of chain.recentEvents(600)) {
|
||||
for (const ev of chain.recentEvents(1e9)) { // whole history, not the last 600
|
||||
if ((ev.type === 'TierPaid' && ev.recipientId === memberId) || (ev.type === 'AwardPaid' && ev.toId === memberId)) {
|
||||
earned += BigInt(ev.amountWei); n += 1;
|
||||
}
|
||||
@@ -2353,12 +2353,12 @@ const server = http.createServer(async (req, res) => {
|
||||
if (!s) return json(res, 401, { error: 'Sign in first.' });
|
||||
const id = s.memberId || await auth.refreshMemberId(s);
|
||||
if (!id) return json(res, 200, { memberId: 0, earnings: [], purchases: [], referrals: [] });
|
||||
const evs = chain.recentEvents(600);
|
||||
const evs = chain.recentEvents(1e9); // whole history (was the last 600 events: older members saw three empty boxes)
|
||||
return json(res, 200, {
|
||||
memberId: id,
|
||||
earnings: await attachNames(evs.filter(e => (e.type === 'TierPaid' && e.recipientId === id) || (e.type === 'AwardPaid' && e.toId === id))),
|
||||
purchases: await attachNames(evs.filter(e => e.type === 'Purchase' && e.buyerId === id)),
|
||||
referrals: await attachNames(evs.filter(e => (e.type === 'MemberActivated' && e.sponsorId === id) || (e.type === 'BuyerCounted' && e.sponsorId === id)))
|
||||
earnings: await attachNames(evs.filter(e => (e.type === 'TierPaid' && e.recipientId === id) || (e.type === 'AwardPaid' && e.toId === id)).slice(0, 300)),
|
||||
purchases: await attachNames(evs.filter(e => e.type === 'Purchase' && e.buyerId === id).slice(0, 300)),
|
||||
referrals: await attachNames(evs.filter(e => (e.type === 'MemberActivated' && e.sponsorId === id) || (e.type === 'BuyerCounted' && e.sponsorId === id)).slice(0, 300))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2554,6 +2554,14 @@ const server = http.createServer(async (req, res) => {
|
||||
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
|
||||
return json(res, 200, await audit.run());
|
||||
}
|
||||
if (p === '/api/admin/chain/rescan' && req.method === 'POST') { // rebuild the full event history from the deploy block
|
||||
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
|
||||
return json(res, 200, await chain.rescan());
|
||||
}
|
||||
if (p === '/api/admin/chain/status' && req.method === 'GET') {
|
||||
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
|
||||
return json(res, 200, { events: chain.eventCount(), historyComplete: chain.historyComplete(), totals: chain.totals() });
|
||||
}
|
||||
if (p === '/api/admin/reports' && req.method === 'GET') {
|
||||
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
|
||||
return json(res, 200, { reports: await reports.list(200) });
|
||||
|
||||
Reference in New Issue
Block a user