Announce each payout once, the same way registrations and upgrades already are
Marty saw #49's 4,971.22 POL Gen-4 pass-up from #222 posted twice in the payments feed. The money is fine: the transaction has one transfer log to #49's address and one contract payout event, 4971.219256933542 POL, paid once. Only the announcement doubled. Payouts were the one event type without the durable announce-once guard. registered and upgraded are keyed into state.announced — 4,000 entries, persisted with the rest of the index state. Payouts instead leaned on state.payouts, which is a rolling window trimmed to KEEP_PAYOUTS (400), and the announcement was emitted BEFORE that state was written. So a restart in the gap between announcing and persisting, or any re-read of the same block, announced the payment again — while an upgrade in the very same transaction was correctly suppressed. That asymmetry is exactly what Marty reported: the payout line twice, #222's Apex upgrade once. The existing state.payouts check stays as the first line of defence; this adds the durable one behind it, keyed on tx+logIndex, which identifies the on-chain event exactly. Not caused by today's deploy — that container started at 14:10 UTC and the payout landed at 13:40 UTC. Checked before blaming it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -352,10 +352,21 @@ async function processRange(fromBlock, toBlock) {
|
||||
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);
|
||||
// Payouts were the ONE event type without the durable announce-once guard.
|
||||
// registered/upgraded are keyed into `state.announced` (4,000 entries, persisted);
|
||||
// payouts leaned on `state.payouts` instead — a rolling window trimmed to
|
||||
// KEEP_PAYOUTS (400) — and the announcement went out BEFORE that state was saved.
|
||||
// So a restart in the gap between announcing and persisting, or any re-read of the
|
||||
// same block, announced the payment a second time, while an upgrade in the very same
|
||||
// transaction was correctly suppressed. That is exactly the shape Marty reported on
|
||||
// 2026-09-18: #49's 4,971.22 POL pass-up posted twice, #222's Apex upgrade once.
|
||||
// The key is already tx+logIndex, so it identifies the on-chain event exactly.
|
||||
if (announceOnce('pay:' + key)) {
|
||||
emit({ type: 'payout', kind: p.kind, toId: p.toId, fromId: p.fromId, pol: p.pol, levelName: levelName(p.kind === 'upline' ? p.level + 1 : p.level), upgrade: p.upgrade, gen: p.kind === 'upline' ? genBetween(p.fromId, p.toId) : undefined, tx, ts });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const id of newIds) {
|
||||
try {
|
||||
const m = await fetchMember(id);
|
||||
|
||||
Reference in New Issue
Block a user