Built-in transaction viewer: every ledger row verifiable at /tx/<hash>, explorer link when configured
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -262,10 +262,26 @@ const server = http.createServer(async (req, res) => {
|
||||
}
|
||||
m = /^\/api\/tx\/(0x[0-9a-fA-F]{64})$/.exec(p);
|
||||
if (m && req.method === 'GET') {
|
||||
// receipt relay so the browser never talks to the RPC directly (CSP stays 'self')
|
||||
// tx relay so the browser never talks to the RPC directly (CSP stays 'self').
|
||||
// Serves both the wallet's waitTx poll (found/status) and the built-in
|
||||
// /tx/<hash> viewer (full details + decoded events).
|
||||
try {
|
||||
const r = await chain.rpc('eth_getTransactionReceipt', [m[1]]);
|
||||
return json(res, 200, r ? { found: true, status: r.status, blockNumber: r.blockNumber } : { found: false });
|
||||
if (!r) return json(res, 200, { found: false });
|
||||
const out = { found: true, status: r.status, blockNumber: r.blockNumber, gasUsed: r.gasUsed };
|
||||
try {
|
||||
const t = await chain.rpc('eth_getTransactionByHash', [m[1]]);
|
||||
if (t) { out.from = t.from; out.to = t.to; out.valueWei = BigInt(t.value || '0x0').toString(); }
|
||||
} catch (e) {}
|
||||
try {
|
||||
const blk = await chain.rpc('eth_getBlockByNumber', [r.blockNumber, false]);
|
||||
if (blk) out.ts = blk.timestamp;
|
||||
} catch (e) {}
|
||||
try {
|
||||
out.events = await attachNames((r.logs || []).map(chain.decodeLog).filter(Boolean)
|
||||
.map(ev => Object.assign(ev, { tx: m[1] })));
|
||||
} catch (e) { out.events = []; }
|
||||
return json(res, 200, out);
|
||||
} catch (e) { return json(res, 200, { found: false, rpcError: true }); }
|
||||
}
|
||||
if (p === '/api/sponsor' && req.method === 'GET') {
|
||||
@@ -681,6 +697,7 @@ const server = http.createServer(async (req, res) => {
|
||||
if (/^\/view\/[a-f0-9]{32}$/.test(p)) return sendFile(res, path.join(PUBLIC_DIR, 'view.html'));
|
||||
m = /^\/uploads\/([a-z0-9]{24}\.(?:png|jpg|webp|gif|mp4|webm))$/.exec(p);
|
||||
if (m) return sendFile(res, path.join(UPLOADS_DIR, m[1]));
|
||||
if (/^\/tx\/0x[0-9a-fA-F]{64}$/.test(p)) return sendFile(res, path.join(PUBLIC_DIR, 'tx.html'));
|
||||
const safe = path.normalize(p).replace(/^([.\\/])+/, '');
|
||||
const file = path.join(PUBLIC_DIR, safe);
|
||||
if (file.startsWith(PUBLIC_DIR) && fs.existsSync(file) && fs.statSync(file).isFile()) return sendFile(res, file);
|
||||
|
||||
Reference in New Issue
Block a user