From 718d7f2d0e8320e7b8a27f7a55b0b16ec4af315e Mon Sep 17 00:00:00 2001 From: martbost Date: Sun, 6 Sep 2026 05:37:29 -0500 Subject: [PATCH] Built-in transaction viewer: every ledger row verifiable at /tx/, explorer link when configured Co-Authored-By: Claude Fable 5 --- chain.js | 2 +- chatbot.js | 2 +- public/assets/common.js | 2 +- public/assets/ledger.js | 2 +- public/assets/tx.js | 42 +++++++++++++++++++++++++++++++++++++++++ public/contract.html | 8 ++++---- public/index.html | 10 +++++----- public/ledger.html | 8 ++++---- public/my.html | 10 +++++----- public/tx.html | 39 ++++++++++++++++++++++++++++++++++++++ public/view.html | 4 ++-- server.js | 21 +++++++++++++++++++-- 12 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 public/assets/tx.js create mode 100644 public/tx.html diff --git a/chain.js b/chain.js index 9434c16..468a9dc 100644 --- a/chain.js +++ b/chain.js @@ -229,4 +229,4 @@ function init(opts) { } module.exports = { init, getConfig, reloadConfig, memberIdByAccount, memberCount, member, - product, productCount, quoteWei, creditBalance, catalog, recentEvents, totals, rpc }; + product, productCount, quoteWei, creditBalance, catalog, recentEvents, totals, rpc, decodeLog }; diff --git a/chatbot.js b/chatbot.js index 05e969a..18e8750 100644 --- a/chatbot.js +++ b/chatbot.js @@ -59,7 +59,7 @@ FACTS: - Every purchase is split by an immutable smart contract in the same transaction: 50% direct sponsor, 20% level 2, 10% level 3, 20% platform. No withdrawals exist; money lands in members' own wallets instantly. - Qualification: level 1 open to all; 2 buyers of $20+ unlock level 2; 5 unlock level 3. Unqualified shares pass up the sponsor line, checking up to 25 positions, else the platform receives them. Qualification cannot be bought and never expires. - Every member gets a share link immediately (site code, /join/); the contract locks a buyer to their sponsor at the buyer's FIRST purchase, so members should switch on payouts before their referrals buy. -- Transparency: live ledger at /ledger streams every chain event with verify links; plain-language contract review at /contract; the verified source is public. +- Transparency: live ledger at /ledger streams every chain event, and EVERY transaction row carries a clickable "verify" link — to the public block explorer when the chain has one, or to the built-in transaction viewer at /tx/ (block, time, from/to, value, gas, decoded events, read live from the node). Plain-language contract review at /contract; the verified source is public. - The contract cannot be paused, upgraded, or drained. The operator can only manage the ad catalog ($1-$500 bounds, 24h price timelock) and rotate keys. RULES: diff --git a/public/assets/common.js b/public/assets/common.js index bf61149..8638280 100644 --- a/public/assets/common.js +++ b/public/assets/common.js @@ -92,7 +92,7 @@ window.IAP = (function () { div.innerHTML = '' + describeEvent(ev, c) + '' + (c.explorer ? 'verify ↗' - : '' + ev.tx.slice(0, 12) + '…'); + : 'verify ↗'); // built-in viewer when the chain has no public explorer return div; } // Render one served ad into #. Silent if no inventory. diff --git a/public/assets/ledger.js b/public/assets/ledger.js index aa44c0e..431f76f 100644 --- a/public/assets/ledger.js +++ b/public/assets/ledger.js @@ -2,7 +2,7 @@ (async function () { await IAP.renderNav('ledger'); const c = await IAP.getConfig(); - IAP.$('contractLink').href = c.explorer + '/address/' + c.contract; + IAP.$('contractLink').href = c.explorer ? c.explorer + '/address/' + c.contract : '/contract'; const feed = IAP.$('feed'); const { events } = await (await fetch('/api/feed?n=150')).json(); diff --git a/public/assets/tx.js b/public/assets/tx.js new file mode 100644 index 0000000..31b4fea --- /dev/null +++ b/public/assets/tx.js @@ -0,0 +1,42 @@ +// Transaction viewer: renders one tx from /api/tx/ (the server-side RPC +// relay, so the browser never talks to the chain directly and CSP stays 'self'). +// This is the "verify it yourself" page for chains without a public explorer; +// when the config carries an explorer URL, feed links point there instead. +(async function () { + await IAP.renderNav('ledger'); + const c = await IAP.getConfig(); + const hash = location.pathname.split('/').pop(); + IAP.$('txHash').textContent = hash; + IAP.$('txChain').textContent = 'on ' + (c.chainName || 'the settlement chain'); + let r = null; + try { r = await (await fetch('/api/tx/' + hash)).json(); } catch (e) {} + const stat = IAP.$('txStatus'); + if (!r || !r.found) { + stat.textContent = 'not found'; + stat.className = 'badge amber'; + IAP.$('txHint').hidden = false; + return; + } + const ok = r.status === '0x1'; + stat.textContent = ok ? '✓ confirmed' : '✗ reverted'; + stat.className = 'badge' + (ok ? '' : ' amber'); + const hx = v => { try { return parseInt(v, 16); } catch (e) { return 0; } }; + const rows = [ + ['Block', '#' + hx(r.blockNumber).toLocaleString()], + ['Time', r.ts ? new Date(hx(r.ts) * 1000).toLocaleString() : 'pending'], + ['From', r.from || ''], + ['To (contract)', r.to || ''], + ['Value sent', IAP.fmtPol(r.valueWei || '0') + ' POL'], + ['Gas used', hx(r.gasUsed).toLocaleString()] + ]; + const tb = IAP.$('txTable'); + tb.hidden = false; + tb.querySelector('tbody').innerHTML = rows.map(x => + '' + x[0] + '' + + '' + String(x[1]).replace(/[&<>]/g, '') + '').join(''); + if (r.events && r.events.length) { + IAP.$('evCard').hidden = false; + const feed = IAP.$('evFeed'); + for (const ev of r.events) feed.appendChild(IAP.feedRow(ev, c)); + } +})(); diff --git a/public/contract.html b/public/contract.html index a40972f..168b080 100644 --- a/public/contract.html +++ b/public/contract.html @@ -5,7 +5,7 @@ The contract | InstantAdPay - +
@@ -129,8 +129,8 @@
Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.
- - - + + + diff --git a/public/index.html b/public/index.html index 6597460..93a6996 100644 --- a/public/index.html +++ b/public/index.html @@ -5,7 +5,7 @@ InstantAdPay: advertise and earn, locked in code - + @@ -409,9 +409,9 @@ - - - - + + + + diff --git a/public/ledger.html b/public/ledger.html index 5567845..0f6acef 100644 --- a/public/ledger.html +++ b/public/ledger.html @@ -5,7 +5,7 @@ Live ledger | InstantAdPay - +
@@ -25,8 +25,8 @@
- - - + + + diff --git a/public/my.html b/public/my.html index 3297089..9d77c02 100644 --- a/public/my.html +++ b/public/my.html @@ -4,7 +4,7 @@ Member area | InstantAdPay - + @@ -399,9 +399,9 @@ - - - - + + + + diff --git a/public/tx.html b/public/tx.html new file mode 100644 index 0000000..7eef6b3 --- /dev/null +++ b/public/tx.html @@ -0,0 +1,39 @@ + + + + +Transaction | InstantAdPay + + + + + +
+
+
+

One transaction, fully public

+

Pulled straight from the chain this site settles on. Every field below is read from the node, + not from our database.

+
+
+

looking it up… +

+

+
+ +
+ +
+ +

← Back to the live ledger · Read the contract review

+
+
+ + + + diff --git a/public/view.html b/public/view.html index 1c66394..88ecc26 100644 --- a/public/view.html +++ b/public/view.html @@ -4,7 +4,7 @@ Viewing ad — InstantAdPay - +