Files
instantadpay/missed.js
T
martbost c00ec64cf8 Missed-payout notice shows the whole chain, one message per purchase
Marty (2026-09-19): show every missed qualification so the whole chain is visible. The old
notice fired once per passed-up tier and showed only that tier. AdminPaid is the last event of
every purchase transaction (119 of 119 in the index), so the notice now fires there, gathers
every PassedUp and TierPaid of the tx, and missed.js composes one message: each tier's walk,
every person passed over with their buyer count, who finally caught it (or that the company
kept it), and the total POL that walked past how many people on that one sale.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 13:14:12 -05:00

54 lines
3.0 KiB
JavaScript

// The missed-payout notice, whole chain in one message (Marty, 2026-09-19).
//
// A purchase pays three tiers up the sponsor line. When a tier's rightful recipient is not
// qualified, the contract passes that share up until it finds someone who is, or hands it to
// the company. Every hop is an on-chain event: PassedUp (who was skipped, and why) and TierPaid
// (who finally got it, and how many hops up). Until now the Telegram notice reported one tier
// per message; this composes ONE message per purchase that shows every tier's walk, every
// person passed, and what it added up to. The point is that the people passed over see, in
// numbers, exactly what qualifying would have paid them.
//
// Pure: takes the events of one transaction plus a name resolver; returns the text or null.
'use strict';
const SHARE = { 1: 0.5, 2: 0.2, 3: 0.1 }; // tier shares of the purchase, for a tier nobody caught
const NEED = { 2: 2, 3: 5 }; // qualifying buyers a tier requires
function fmtPol(wei) {
try { return (Number(BigInt(String(wei)) / 10n ** 14n) / 1e4).toFixed(4); } catch (e) { return '0'; }
}
// txEvents: every indexed event sharing the tx. who(id) -> '#id @name'. had(id, tier) -> qualifying
// buyers that member had at the time (a number) or null when unknown.
function composeMissed(txEvents, who, had, site) {
const buy = txEvents.find(e => e.type === 'Purchase');
const skips = txEvents.filter(e => e.type === 'PassedUp');
if (!buy || !skips.length) return null;
const price = '$' + Math.round((buy.priceCents || 0) / 100);
const lines = ['\u{1F62C} <b>Missed payouts</b> on ' + who(buy.buyerId) + "'s " + price + ' purchase:'];
const passed = new Set(); let walked = 0;
for (const tier of [2, 3]) {
const sk = skips.filter(s => Number(s.tier) === tier);
if (!sk.length) continue;
const paid = txEvents.find(e => e.type === 'TierPaid' && Number(e.tier) === tier);
const pol = paid ? Number(fmtPol(paid.amountWei)) : Number(fmtPol(BigInt(String(buy.paidWei || 0)) * BigInt(Math.round(SHARE[tier] * 100)) / 100n));
walked += pol;
const hops = sk.map(s => {
passed.add(s.skippedId);
let why;
if (s.reason === 'unqualified') { const n = had(s.skippedId, tier); why = (n == null ? 'not qualified' : n + ' of ' + NEED[tier] + ' qualifying buyers'); }
else if (s.reason === 'send-failed') why = 'wallet refused the payment';
else why = String(s.reason || 'passed');
return who(s.skippedId) + ' (' + why + ')';
});
const end = paid ? 'paid to ' + who(paid.recipientId) : 'nobody above was qualified, so the company kept it';
lines.push('Level ' + tier + ' · <b>' + pol.toFixed(4) + ' POL</b>: ' + hops.join(' → ') + ' → ' + end);
}
const n = passed.size;
lines.push('<b>' + walked.toFixed(4) + ' POL walked past ' + n + ' ' + (n === 1 ? 'person' : 'people') + ' on one ' + price + ' sale.</b>');
lines.push('Qualify before the next one: <a href="' + site + '">' + site.replace(/^https:\/\//, '') + '</a>');
return lines.join('\n');
}
module.exports = { composeMissed, fmtPol };