// Shared page runtime: site config, nav, formatting. Zero dependencies.
window.IAP = (function () {
let config = null;
const $ = id => document.getElementById(id);
async function getConfig() {
if (!config) config = await (await fetch('/api/config')).json();
return config;
}
function fmtPol(wei) {
const s = BigInt(wei).toString().padStart(19, '0');
const whole = s.slice(0, -18) || '0';
const frac = s.slice(-18, -12).replace(/0+$/, '');
return whole + (frac ? '.' + frac : '');
}
const fmtUsd = cents => '$' + (cents / 100).toFixed(2);
function status(msg, cls) {
let el = $('status');
if (!el) { el = document.createElement('div'); el.id = 'status'; document.body.appendChild(el); }
el.textContent = msg; el.className = cls || ''; el.hidden = false;
clearTimeout(status._t);
if (cls === 'ok') status._t = setTimeout(() => { el.hidden = true; }, 6000);
}
async function renderNav(active) {
const c = await getConfig();
const nav = document.createElement('nav');
nav.innerHTML = '
';
document.body.prepend(nav);
if (c.rehearsal) {
const b = document.createElement('div');
b.className = 'rehearsal';
b.innerHTML = 'Testnet rehearsal โ running on ' + c.chainName + '. Purchases use valueless test POL while we prove every payout in public.';
document.body.prepend(b);
}
const a = nav.querySelector('[data-p="' + active + '"]');
if (a) a.style.color = 'var(--ink)';
refreshNavWallet();
}
async function refreshNavWallet() {
try {
const me = await (await fetch('/api/me')).json();
const el = $('navWallet');
if (!el) return;
if (me.signedIn) {
el.innerHTML = (me.memberId ? 'member #' + me.memberId + ' ' : '')
+ '' + me.address.slice(0, 6) + 'โฆ' + me.address.slice(-4) + '';
} else {
el.innerHTML = 'Sign in';
}
return me;
} catch (e) { return null; }
}
function describeEvent(ev, c) {
const pol = w => fmtPol(w) + ' POL';
switch (ev.type) {
case 'Purchase': return '๐งพ member #' + ev.buyerId + ' bought package #' + ev.productId
+ ' (' + fmtUsd(ev.priceCents) + ') for ' + pol(ev.paidWei) + ' โ +' + ev.creditAmount.toLocaleString() + ' credits';
case 'TierPaid': return '๐ธ level ' + ev.tier + ' payout โ member #' + ev.recipientId + ': ' + pol(ev.amountWei)
+ (ev.hops ? ' (passed up ' + ev.hops + ')' : '');
case 'PassedUp': return 'โท level ' + ev.tier + ' passed over #' + ev.skippedId + ' (' + ev.reason + ')';
case 'AdminPaid': return '๐ platform fee settled: ' + pol(ev.amountWei);
case 'BuyerCounted': return 'โญ member #' + ev.sponsorId + ' now has ' + ev.newCount + ' qualifying buyer(s)';
case 'MemberActivated': return '๐ค member #' + ev.id + ' activated a payout wallet';
case 'AwardPaid': return '๐ award: ' + pol(ev.amountWei) + ' โ member #' + ev.toId;
case 'CreditsConsumed': return '๐ฃ member #' + ev.memberId + ' ran ads: โ' + ev.amount.toLocaleString() + ' credits';
case 'PriceCached': return '๐ฎ oracle price refreshed';
case 'FallbackPriceUsed': return '๐ฎ cached price bridged an oracle gap';
default: return 'ยท ' + ev.type;
}
}
function feedRow(ev, c) {
const div = document.createElement('div');
div.className = 'row t-' + ev.type;
div.innerHTML = '' + describeEvent(ev, c) + ''
+ 'verify โ';
return div;
}
return { getConfig, fmtPol, fmtUsd, status, renderNav, refreshNavWallet, describeEvent, feedRow, $ };
})();