diff --git a/chain.js b/chain.js index b6e6c7d..4dc72b6 100644 --- a/chain.js +++ b/chain.js @@ -55,6 +55,8 @@ function loadState() { } // income rows saved before desc-classification existed: re-snapshot to backfill if (state.payouts.some(p => p.kind === 'income' && !p.desc)) state.snapshotAt = 0; + // one-time true-up: earlier tail code didn't maintain directCount/matrix slots live + if (!state.migratedLiveCounts) { state.snapshotAt = 0; state.migratedLiveCounts = true; } } function saveState() { try { @@ -221,7 +223,10 @@ async function processRange(fromBlock, toBlock) { if (l.topics[0] === T_REGISTERED) { const id = topicInt(l.topics[1]); if (!state.members[id]) { - state.members[id] = { account: topicAddr(l.topics[2]), referrerId: topicInt(l.topics[3]), tier: wInt(l.data, 0), joinedAt: ts }; + state.members[id] = { account: topicAddr(l.topics[2]), referrerId: topicInt(l.topics[3]), tier: wInt(l.data, 0), joinedAt: ts, level: 1, directCount: 0, earnedPol: 0 }; + // keep the referrer's qualification progress live between snapshots + const ref = state.members[topicInt(l.topics[3])]; + if (ref) ref.directCount = (ref.directCount || 0) + 1; newIds.push(id); emit({ type: 'registered', id, referrerId: topicInt(l.topics[3]), tierName: tierName(wInt(l.data, 0)), tx, ts }); } @@ -249,13 +254,22 @@ async function processRange(fromBlock, toBlock) { if (twinIdx >= 0) state.payouts[twinIdx] = p; else { state.payouts.push(p); state.totals.count++; state.totals.pol = +(state.totals.pol + p.pol).toFixed(6); + const rcpt = state.members[p.toId]; + if (rcpt) rcpt.earnedPol = +((rcpt.earnedPol || 0) + p.pol).toFixed(6); emit({ type: 'payout', kind: p.kind, toId: p.toId, fromId: p.fromId, pol: p.pol, levelName: levelName(p.level), upgrade: p.upgrade, tx, ts }); } } } for (const id of newIds) { - try { const m = await fetchMember(id); if (m) Object.assign(state.members[id], { uplineId: m.uplineId, level: m.level }); } - catch (e) { /* picked up by next snapshot */ } + try { + const m = await fetchMember(id); + if (m) { + Object.assign(state.members[id], { uplineId: m.uplineId, level: m.level }); + // wire the new member into the parent's matrix slots so trees update live + const par = state.members[m.uplineId]; + if (par) { if (!par.l) par.l = id; else if (!par.r && par.l !== id) par.r = id; } + } + } catch (e) { /* picked up by next snapshot */ } } state.payouts.sort((a, b) => (a.ts || 0) - (b.ts || 0)); if (state.payouts.length > KEEP_PAYOUTS) state.payouts = state.payouts.slice(-KEEP_PAYOUTS);