Add copy-on-click and etherscan links to transaction detail page
Some checks failed
check / check (push) Has been cancelled

From address, to address, and tx hash are now clickable to copy.
Each also has a small box-arrow icon linking to the corresponding
etherscan page. ENS names and full addresses are both copyable.
This commit is contained in:
2026-02-26 15:34:28 +07:00
parent 69311f4444
commit 6c4b2753d9

View File

@@ -182,27 +182,46 @@ function etherscanTxLink(hash) {
return `https://etherscan.io/tx/${hash}`;
}
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>`;
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 txDetailAddressHtml(address) {
const ensName = ensNameMap.get(address) || null;
const dot = addressDotHtml(address);
const link = etherscanAddressLink(address);
const extLink = `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
if (ensName) {
return (
dot +
`<a href="${link}" target="_blank" class="underline decoration-dashed">${escapeHtml(ensName)}</a>` +
`<div class="break-all"><a href="${link}" target="_blank" class="underline decoration-dashed">${escapeHtml(address)}</a></div>`
copyableHtml(ensName, "") +
extLink +
`<div class="break-all">` +
copyableHtml(address, "break-all") +
`</div>`
);
}
return (
dot +
`<a href="${link}" target="_blank" class="underline decoration-dashed break-all">${escapeHtml(address)}</a>`
);
return dot + copyableHtml(address, "break-all") + extLink;
}
function txDetailHashHtml(hash) {
const link = etherscanTxLink(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 showTxDetail(tx) {
const txLink = etherscanTxLink(tx.hash);
$("tx-detail-hash").innerHTML =
`<a href="${txLink}" target="_blank" class="underline decoration-dashed">${escapeHtml(tx.hash)}</a>`;
$("tx-detail-hash").innerHTML = txDetailHashHtml(tx.hash);
$("tx-detail-from").innerHTML = txDetailAddressHtml(tx.from);
$("tx-detail-to").innerHTML = txDetailAddressHtml(tx.to);
$("tx-detail-value").textContent = tx.value + " " + tx.symbol;
@@ -210,6 +229,17 @@ function showTxDetail(tx) {
isoDate(tx.timestamp) + " (" + timeAgo(tx.timestamp) + ")";
$("tx-detail-status").textContent = tx.isError ? "Failed" : "Success";
showView("transaction");
// Attach copy handlers
document
.getElementById("view-transaction")
.querySelectorAll("[data-copy]")
.forEach((el) => {
el.onclick = () => {
navigator.clipboard.writeText(el.dataset.copy);
showFlash("Copied!");
};
});
}
function init(ctx) {