No-payout positions (siteConfig.noPayoutIds, default 25): linkage only. Buy-from picker disables them, purchases from them refused, joins routed away from them and their upline chain

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-14 10:30:58 -05:00
parent e699513636
commit 1fad5bdab2
5 changed files with 24 additions and 4 deletions
+5 -2
View File
@@ -1822,6 +1822,7 @@
}
// ── linked positions (Qualified Start) ──
const short = a => a ? a.slice(0, 6) + '…' + a.slice(-4) : '';
let noPayoutAddrs = new Set(); // positions the site refuses to buy from (see siteConfig.noPayoutIds)
async function loadPositions(me) {
try {
const r = await (await fetch('/api/my/positions')).json();
@@ -1829,7 +1830,7 @@
const list = r.positions || [];
const rows = list.map((p, i) => '<div class="lin-row"><span class="nm">Position ' + (i + 2) + ' <span class="mono">' + short(p.address) + '</span></span>'
+ '<span class="id">' + (p.memberId ? '#' + p.memberId : 'not on-chain yet') + '</span>'
+ '<span class="earn' + (p.counted ? ' on' : '') + '">' + (p.counted ? 'counts as a qualifying buyer' : p.memberId ? 'registered, buy $20+ to count' : 'buy a $20+ package to register it') + '</span>'
+ '<span class="earn' + (p.counted ? ' on' : '') + '">' + (p.noPayout ? 'linkage only: no purchases from this position (payouts would reach a retired wallet)' : p.counted ? 'counts as a qualifying buyer' : p.memberId ? 'registered, buy $20+ to count' : 'buy a $20+ package to register it') + '</span>'
+ '<span class="dt">' + (p.credits || 0).toLocaleString() + ' credits' + (p.balanceWei != null ? ' · ' + (Number(BigInt(p.balanceWei) / 10n ** 14n) / 10000).toLocaleString(undefined, { maximumFractionDigits: 2 }) + ' POL' : '') + '</span>'
+ (!p.memberId ? '<button class="btn sec small" type="button" data-unlink="' + p.address + '">Unlink</button>' : '')
+ '</div>').join('');
@@ -1857,7 +1858,8 @@
if (sel) {
const keep = sel.value;
sel.innerHTML = '<option value="main">Main wallet ' + (r.main && r.main.address ? short(r.main.address) : '(link it first)') + (r.main && r.main.memberId ? ' · #' + r.main.memberId : '') + '</option>'
+ list.map((p, i) => '<option value="' + p.address + '">Position ' + (i + 2) + ' ' + short(p.address) + (p.memberId ? ' · #' + p.memberId : ' · not registered yet') + (p.counted ? ' · counted' : '') + '</option>').join('');
+ list.map((p, i) => '<option value="' + p.address + '"' + (p.noPayout ? ' disabled' : '') + '>Position ' + (i + 2) + ' ' + short(p.address) + (p.memberId ? ' · #' + p.memberId : ' · not registered yet') + (p.noPayout ? ' · linkage only' : p.counted ? ' · counted' : '') + '</option>').join('');
noPayoutAddrs = new Set(list.filter(p => p.noPayout).map(p => p.address.toLowerCase())); if (r.main && r.main.noPayout && r.main.address) noPayoutAddrs.add(r.main.address.toLowerCase());
if (keep && [...sel.options].some(o => o.value === keep)) sel.value = keep;
$('buyFromWrap').hidden = !list.length;
}
@@ -1915,6 +1917,7 @@
const fromSel = $('buyFrom');
const fromPos = (fromSel && !$('buyFromWrap').hidden && fromSel.value && fromSel.value !== 'main') ? fromSel.value.toLowerCase() : null;
if (fromPos && !meNow.memberId) { IAP.status('Switch on payouts for your main wallet first (Wallet tab), so this position can register under you.', 'bad'); return; }
if (noPayoutAddrs.has(fromPos || String(meNow.address || '').toLowerCase())) { IAP.status('This position is kept for linkage only. A purchase from it would pay a retired wallet. Buy from your main wallet or another position.', 'bad'); return; }
if (!fromPos && !meNow.address) {
IAP.status('Link your wallet first — one quick signature…');
await IAPWallet.signIn();