diff --git a/chain.js b/chain.js index 3cd6f1e..0c12733 100644 --- a/chain.js +++ b/chain.js @@ -57,6 +57,9 @@ function loadState() { 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; } + // one-time re-snapshot: repair members pre-cached by verifyMember whose + // registration the tail skipped (#134 missing placement, #41 stuck at 1/2) + if (!state.migratedPreIndexFix) { state.snapshotAt = 0; state.migratedPreIndexFix = true; } } function saveState() { try { @@ -275,8 +278,10 @@ async function processRange(fromBlock, toBlock) { const key = tx.slice(2, 12) + ':' + hexInt(l.logIndex); 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, level: 1, directCount: 0, earnedPol: 0 }; + const existing = state.members[id]; + if (!existing || existing.preIndexed) { + state.members[id] = { ...(existing || {}), account: topicAddr(l.topics[2]), referrerId: topicInt(l.topics[3]), tier: wInt(l.data, 0), joinedAt: ts, level: (existing && existing.level) || 1, directCount: (existing && existing.directCount) || 0, earnedPol: (existing && existing.earnedPol) || 0 }; + delete state.members[id].preIndexed; // 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; @@ -411,7 +416,11 @@ function getPayoutsPublic(offset = 0, limit = 40) { async function verifyMember(id) { const m = await fetchMember(id); if (!m) return { registered: false }; - if (state && !state.members[id]) state.members[id] = { account: m.account, referrerId: m.referrerId, uplineId: m.uplineId, tier: m.tier, level: m.level, joinedAt: m.joinedAt }; + // preIndexed: provisional cache — the log tail must still process this + // member's registration event (referrer directCount++ etc.). Without the + // flag, an instant verify (join-now auto-submission) races the tail and the + // registration gets skipped as a duplicate (bit us with #134/#41, 2026-08-19). + if (state && !state.members[id]) state.members[id] = { account: m.account, referrerId: m.referrerId, uplineId: m.uplineId, tier: m.tier, level: m.level, joinedAt: m.joinedAt, preIndexed: true }; return { registered: true, ...m, tierName: tierName(m.tier), levelName: levelName(m.level) }; }