Files
AutistMask/src/popup/views/transactionDetail.js
user 2244b52f5f
All checks were successful
check / check (push) Successful in 22s
fix: rules audit items 1,3,6 (closes #1)
2026-02-27 02:18:45 -08:00

152 lines
4.8 KiB
JavaScript

// Transaction detail view — shows full details for a single transaction.
// Shared by addressDetail and addressToken via ctx.showTransactionDetail().
const {
$,
showView,
showFlash,
addressDotHtml,
addressTitle,
escapeHtml,
isoDate,
timeAgo,
} = require("./helpers");
const { state } = require("../../shared/state");
const makeBlockie = require("ethereum-blockies-base64");
const EXT_ICON =
`<span style="display:inline-block;width:10px;height:10px;margin-left:4px;vertical-align:middle">` +
`<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5">` +
`<path d="M4.5 1.5H2a.5.5 0 00-.5.5v8a.5.5 0 00.5.5h8a.5.5 0 00.5-.5V7.5"/>` +
`<path d="M7 1.5h3.5V5M7 5.5L10.5 1.5"/>` +
`</svg></span>`;
let ctx;
function copyableHtml(text, extraClass) {
const cls =
"underline decoration-dashed cursor-pointer" +
(extraClass ? " " + extraClass : "");
return `<span class="${cls}" data-copy="${escapeHtml(text)}">${escapeHtml(text)}</span>`;
}
function blockieHtml(address) {
const src = makeBlockie(address);
return `<img src="${src}" width="48" height="48" style="image-rendering:pixelated;border-radius:50%;display:inline-block">`;
}
function txAddressHtml(address, ensName, title) {
const blockie = blockieHtml(address);
const dot = addressDotHtml(address);
const link = `https://etherscan.io/address/${address}`;
const extLink = `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
let html = `<div class="mb-1">${blockie}</div>`;
if (title) {
html += `<div class="flex items-center font-bold">${dot}${escapeHtml(title)}</div>`;
}
if (ensName) {
html +=
`<div class="flex items-center">${title ? "" : dot}` +
copyableHtml(ensName, "") +
extLink +
`</div>` +
`<div class="break-all">` +
copyableHtml(address, "break-all") +
`</div>`;
} else {
html +=
`<div class="flex items-center">${title ? "" : dot}` +
copyableHtml(address, "break-all") +
extLink +
`</div>`;
}
return html;
}
function txHashHtml(hash) {
const link = `https://etherscan.io/tx/${hash}`;
const extLink = `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
return copyableHtml(hash, "break-all") + extLink;
}
function show(tx) {
state.viewData = {
tx: {
hash: tx.hash,
from: tx.from,
to: tx.to,
value: tx.value,
exactValue: tx.exactValue || tx.value,
rawAmount: tx.rawAmount || "",
rawUnit: tx.rawUnit || "",
symbol: tx.symbol,
timestamp: tx.timestamp,
isError: tx.isError,
fromEns: tx.fromEns || null,
toEns: tx.toEns || null,
directionLabel: tx.directionLabel || null,
},
};
render();
}
function render() {
const tx = state.viewData.tx;
if (!tx) return;
$("tx-detail-hash").innerHTML = txHashHtml(tx.hash);
const fromTitle = addressTitle(tx.from, state.wallets);
const toTitle = addressTitle(tx.to, state.wallets);
$("tx-detail-from").innerHTML = txAddressHtml(
tx.from,
tx.fromEns,
fromTitle,
);
$("tx-detail-to").innerHTML = txAddressHtml(tx.to, tx.toEns, toTitle);
// Exact amount (full precision, copyable)
const exactStr = tx.exactValue
? tx.exactValue + " " + tx.symbol
: tx.directionLabel + " " + tx.symbol;
$("tx-detail-value").innerHTML = copyableHtml(exactStr, "font-bold");
// Native quantity (raw integer, copyable)
const nativeEl = $("tx-detail-native");
if (tx.rawAmount && tx.rawUnit) {
const nativeStr = tx.rawAmount + " " + tx.rawUnit;
nativeEl.innerHTML = copyableHtml(nativeStr, "");
nativeEl.parentElement.classList.remove("hidden");
} else {
nativeEl.innerHTML = "";
nativeEl.parentElement.classList.add("hidden");
}
$("tx-detail-time").textContent =
isoDate(tx.timestamp) + " (" + timeAgo(tx.timestamp) + ")";
$("tx-detail-status").textContent = tx.isError ? "Failed" : "Success";
showView("transaction");
document
.getElementById("view-transaction")
.querySelectorAll("[data-copy]")
.forEach((el) => {
el.onclick = () => {
navigator.clipboard.writeText(el.dataset.copy);
showFlash("Copied!");
};
});
}
function init(_ctx) {
ctx = _ctx;
$("btn-tx-back").addEventListener("click", () => {
if (state.selectedToken) {
ctx.showAddressToken();
} else {
ctx.showAddressDetail();
}
});
}
module.exports = { init, show, render };