diff --git a/chain.js b/chain.js index 34872b2..1981ec2 100644 --- a/chain.js +++ b/chain.js @@ -521,6 +521,52 @@ function getOwnerUpgradeNeeds(ids) { return { ready: true, needs }; } +// Payment-routing / leak map: simulates the contract's _payUpline routing for +// every member below `rootId` on their NEXT upgrade, classifying where the POL +// lands — a position you own, a teammate inside your org, an outsider above the +// org (a true LEAK), or fees. Answers "is money escaping my org?". +function getOrgRouting(rootId, ownerIds) { + if (!state || !state.snapshotAt || !costs) return { ready: false }; + const M = state.members; + const CROOT = 1; // contract root position (#1); payments passing it go to fees + const owned = new Set((ownerIds || []).map(Number)); + const lvl = id => (M[id] && M[id].level) || 1; + const catcher = (fromId, li) => { + let up = M[fromId] && M[fromId].uplineId; + for (let i = 0; i < 16; i++) { + if (!up || up === CROOT) return null; + const u = M[up]; if (!u) return null; + if (i < li) { up = u.uplineId; continue; } + if ((u.level || 1) > li && (u.directCount || 0) >= 2) return up; + up = u.uplineId; + } + return null; + }; + const inOrg = id => { let up = M[id] && M[id].uplineId, g = 0; while (up && up !== CROOT && g++ < 48) { if (up === rootId) return true; up = M[up].uplineId; } return false; }; + const ids = Object.keys(M).map(Number).filter(id => id !== rootId && inOrg(id)); + let ownedPol = 0, teamPol = 0, leakPol = 0, feePol = 0; + const leaks = []; + for (const id of ids) { + const L = lvl(id); if (L >= 8) continue; + const li = L - 1; + const tier = (M[id].tier === 2) ? 2 : 1; + const amt = (costs.up[tier] || [])[li] || 0; + if (!amt) continue; + const c = catcher(id, li); + if (c == null) feePol += amt; + else if (owned.has(c)) ownedPol += amt; + else if (inOrg(c)) teamPol += amt; + else { leakPol += amt; leaks.push({ from: id, to: c, toLevel: lvl(c), amt: +amt.toFixed(2) }); } + } + leaks.sort((a, b) => b.amt - a.amt); + return { + ready: true, rootId, members: ids.length, + ownedPol: +ownedPol.toFixed(2), teamPol: +teamPol.toFixed(2), + leakPol: +leakPol.toFixed(2), feePol: +feePol.toFixed(2), + leaks: leaks.slice(0, 40) + }; +} + // How an organization rooted at `rootId` stacks up against the whole contract — // share of members and share of all POL paid. Computed from in-memory state. function getOrgShare(rootId) { @@ -559,4 +605,4 @@ async function getIncome(id) { }; } -module.exports = { startIndexer, getPayoutsPublic, verifyMember, memberLookup, memberPublic, getIncome, getOwnerUpgradeNeeds, getOrgShare, getMatrixTree, isInTeam, CONTRACT }; +module.exports = { startIndexer, getPayoutsPublic, verifyMember, memberLookup, memberPublic, getIncome, getOwnerUpgradeNeeds, getOrgRouting, getOrgShare, getMatrixTree, isInTeam, CONTRACT }; diff --git a/public/admin.html b/public/admin.html index 37a4923..cb61f25 100644 --- a/public/admin.html +++ b/public/admin.html @@ -5,7 +5,7 @@
First-touch source per visitor session (referring domain or utm_source). Funnel: bridge page → start page → join click.
| Source | Bridge views | Start views | Training views | Invite views | Join clicks | Start → Join |
|---|
New members who confirmed their purchase on the start page. Each one was posted to your Hermes Telegram chat — add them to the rotation.
| When | Name / Handle | New ID | Joined under | Source | On-chain |
|---|
How your team — rooted at your top ID — stacks up against the entire Crypto Team Build Network smart contract. Live on-chain.
Every payment received by your own positions, live from the contract. Comma-separated IDs — saved for next time.
Every payment received by your own positions, live from the contract. Comma-separated IDs — saved for next time.
Enter an RM Circle ID to read its registration, lineage, and every payment it has received — live from the smart contract.
The entire on-chain matrix — who landed where, with tier, level, directs, and earnings per position. Click a position to drill down.
Next-upgrade routing for ${rt.members} members below #${rt.rootId} — where each imminent upgrade payment lands. "Team" = teammates earning (good). "Leaking" = escaping to outsiders above you (often structural, not fixable by upgrading).
`+ + (rt.leaks&&rt.leaks.length? + `| Member's next upgrade | Escapes to (outside) | Amount |
|---|---|---|
| #${l.from} | #${l.to} (L${l.toLevel}) | ${fmt(l.amt)} POL |
| When (UTC) | To | From | For | Amount |
|---|---|---|---|---|
| ${date(r.ts)} | #${r.toId} | #${r.fromId} | ${esc(r.desc||'')} | ${fmt(r.pol)} POL |
${d.rows.length} payment${d.rows.length===1?'':'s'} across your positions, newest first.
`:'