'
+ + '
Referral contest
Leaderboard: who is selling.
Ranked by ad packages sold to other people (your own positions never count). Read from the chain, updated live. Weeks run Monday to Sunday, Central time.
';
+ h += '
' + week.label + ' ' + fmtD(week.start) + ' to Sunday
' + (week.prize ? '
Weekly prizes: ' + esc(week.prize) + '
' : '') + table(week);
+ h += '
' + month.label + '
' + (month.prize ? '
Monthly prizes: ' + esc(month.prize) + '
' : '') + table(month);
+ h += '
All time
' + table(all);
+ const w = winners();
+ if (w.length) h += '
Past winners
' + w.slice(0, 12).map(x => '
' + (x.kind === 'week' ? 'Week of ' : 'Month of ') + fmtD(x.start) + ': ' + (x.top[0] ? esc(x.top[0].name) + ' (' + x.top[0].sales + ' sold, $' + x.top[0].usd + ')' : 'no sales') + (x.granted && x.granted.length ? ' · awarded: ' + x.granted.map(g => esc(g.name) + ' +' + g.credits.toLocaleString()).join(', ') : (x.prize ? ' · prize ' + esc(x.prize) : '')) + '
').join('');
+ h += '
Sales are $ of packages bought by members you directly sponsor. Prizes are advertising credits or packages, never cash. No income is guaranteed.
';
+ h += '
';
+ return h;
+}
+module.exports = { init, view, compute, renderPage, rolloverTick, winners, periodBounds };
diff --git a/public/assets/admin.js b/public/assets/admin.js
index c317593..6ed60c9 100644
--- a/public/assets/admin.js
+++ b/public/assets/admin.js
@@ -757,7 +757,7 @@
}));
// site settings: key / value rows; booleans as checkboxes, numbers stay numbers
- const SITE_META = { siteName: 'Site name', tagline: 'Tagline', rehearsal: 'Testnet rehearsal banner', defaultSponsorId: 'Fallback sponsor (member #)', walletConnectProjectId: 'WalletConnect project id', moonpayPublicKey: 'MoonPay public key', telegramBotToken: 'Telegram proof feed: bot token', telegramChatId: 'Telegram proof feed: chat id', telegramTopicId: 'Telegram proof feed: topic id (optional)', telegramEvents: 'Telegram proof feed: events (payouts | payouts+purchases | all)', telegramCtaUrl: 'Telegram proof feed: join link under each post', telegramEchoChatId: 'Telegram echo (shared payments topic): chat id', telegramEchoTopicId: 'Telegram echo: topic id', telegramEchoEvents: 'Telegram echo: events (payouts | payouts+purchases | all)', legacyCreditsAdvertiser: 'Legacy welcome credits: former advertisers', legacyCreditsEarner: 'Legacy welcome credits: former earners', pnlFixedMonthlyUsd: 'P&L: fixed monthly cost (USD)' };
+ const SITE_META = { siteName: 'Site name', tagline: 'Tagline', rehearsal: 'Testnet rehearsal banner', defaultSponsorId: 'Fallback sponsor (member #)', walletConnectProjectId: 'WalletConnect project id', moonpayPublicKey: 'MoonPay public key', telegramBotToken: 'Telegram proof feed: bot token', telegramChatId: 'Telegram proof feed: chat id', telegramTopicId: 'Telegram proof feed: topic id (optional)', telegramEvents: 'Telegram proof feed: events (payouts | payouts+purchases | all)', telegramCtaUrl: 'Telegram proof feed: join link under each post', leaderboardWeeklyPrize: 'Leaderboard: weekly prize text (optional; blank shows the credit ladder)', leaderboardMonthlyPrize: 'Leaderboard: monthly prize text (optional)', leaderboardWeeklyCredits: 'Leaderboard: weekly credits for 1st,2nd,3rd… (e.g. 1000,500,250; blank = none)', leaderboardMonthlyCredits: 'Leaderboard: monthly credits for 1st,2nd,3rd… (e.g. 5000,2500,1000)', leaderboardAnnounceGeneral: 'Leaderboard: announce winners in the main group too (1/0)', telegramEchoChatId: 'Telegram echo (shared payments topic): chat id', telegramEchoTopicId: 'Telegram echo: topic id', telegramEchoEvents: 'Telegram echo: events (payouts | payouts+purchases | all)', legacyCreditsAdvertiser: 'Legacy welcome credits: former advertisers', legacyCreditsEarner: 'Legacy welcome credits: former earners', pnlFixedMonthlyUsd: 'P&L: fixed monthly cost (USD)' };
function drawSite() {
const wrap = $('siteForm');
wrap.innerHTML = Object.entries(siteObj).map(([k, v]) => '' + esc(SITE_META[k] || humanize(k)) + ''
diff --git a/public/assets/common.js b/public/assets/common.js
index 68eabc5..0c192ff 100644
--- a/public/assets/common.js
+++ b/public/assets/common.js
@@ -53,7 +53,7 @@ window.IAP = (function () {
f.style.cssText = 'border-top:1px solid var(--line);margin-top:48px;padding:26px 22px;text-align:center;color:var(--muted);font-size:13px';
f.innerHTML = '
© ' + new Date().getFullYear() + ' InstantAdPay
'
+ '
';
document.body.appendChild(f);
}
diff --git a/public/assets/my.js b/public/assets/my.js
index a53f41c..c994ec7 100644
--- a/public/assets/my.js
+++ b/public/assets/my.js
@@ -363,6 +363,7 @@
// site-wide activity (not about me): joins, tank, adoptions, purchases, payouts, qualifications
if (ev.type === 'Joined') { communityToast('👋 ' + ev.name + ' just joined' + (ev.tank ? ' and is waiting for a sponsor in the holding tank' : '')); liveRefresh(); return; }
if (ev.type === 'Adopted') { communityToast('🤝 ' + ev.sponsor + ' picked up ' + ev.member + ' from the holding tank'); liveRefresh(); return; }
+ if (ev.type === 'Contest') { communityToast('🏆 ' + ev.winner + ' won the ' + (ev.kind === 'week' ? 'weekly' : 'monthly') + ' referral contest'); return; }
if (ev.type === 'Badge') { communityToast('🏆 ' + ev.member + ' unlocked ' + ev.label); return; }
if (ev.type === 'Released') { communityToast('🪣 ' + ev.sponsor + ' returned ' + ev.member + ' to the holding tank'); liveRefresh(); return; }
const mine = MYID && (ev.recipientId === MYID || ev.toId === MYID || ev.sponsorId === MYID || ev.skippedId === MYID || ev.buyerId === MYID);
@@ -457,9 +458,23 @@
$('newsCard').addEventListener('click', () => { try { localStorage.setItem('iap.news.seen', r.latest || ''); } catch (e) {} $('newsList').querySelectorAll('span[style*="mint"]').forEach(s => { if (s.textContent === '●') s.remove(); }); }, { once: true });
} catch (e) {}
}
+ // Leaderboard card: top 5 this week + your own rank + the prize (Marty, 2026-09-14)
+ async function loadLeaderboard() {
+ if (!$('lbCard')) return;
+ try {
+ const r = await (await fetch('/api/leaderboard?period=week')).json();
+ const esc = t => String(t == null ? '' : t).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
+ let h = r.prize ? '
Prize this week: ' + esc(r.prize) + '
' : '';
+ h += r.top.length ? '
' + r.top.slice(0, 5).map(x => '| ' + x.rank + ' | ' + esc(x.name) + ' | ' + x.sales + ' sold · $' + x.usd + ' |
').join('') + '
'
+ : '
No packages sold yet this week. First sale takes the top spot.
';
+ h += r.me ? '
You: #' + r.me.rank + ' · ' + r.me.sales + ' sold · $' + r.me.usd + '
' : '
You: no sales yet this week. Sales are $20+ packages bought by people you sponsor.
';
+ h += '
Full leaderboard: week, month, all time →
';
+ $('lbList').innerHTML = h; $('lbCard').hidden = false;
+ } catch (e) {}
+ }
async function loadDashboard() {
try {
- loadNews();
+ loadNews(); loadLeaderboard();
const d = await (await fetch('/api/my/dashboard')).json();
if (d.error) return;
chatSync(d);
diff --git a/public/my.html b/public/my.html
index 45f3de9..517024c 100644
--- a/public/my.html
+++ b/public/my.html
@@ -258,6 +258,8 @@