// 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)); } })();