From d67023e80df9561a1a9c61b863e668d40b98cd78 Mon Sep 17 00:00:00 2001 From: sneak Date: Fri, 27 Feb 2026 16:09:44 +0700 Subject: [PATCH] Show exact amounts and address titles in transaction detail - Display full-precision amount (no 4-decimal truncation) in the transaction detail view, with native quantity (wei/base units) below - Both amount and native quantity are click-copyable - Show wallet/address title above from/to when the address is ours - Update README Display Consistency to document the exception --- README.md | 20 +++++++++---- src/popup/index.html | 6 +++- src/popup/views/transactionDetail.js | 43 +++++++++++++++++++++++----- src/shared/transactions.js | 19 ++++++++++-- 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 39c083d..0ddd3ca 100644 --- a/README.md +++ b/README.md @@ -118,12 +118,20 @@ click it. #### Display Consistency The same data must be formatted identically everywhere it appears. Token and ETH -amounts are always displayed with exactly 4 decimal places (e.g. "1.0500 ETH", -"17.1900 USDT") — in balance lists, transaction lists, transaction details, send -confirmations, and any other context. Timestamps include both an ISO datetime -and a humanized relative age wherever shown. If a formatting rule applies in one -place, it applies in every place. Users should never see the same value rendered -differently on two screens. +amounts are displayed with exactly 4 decimal places (e.g. "1.0500 ETH", "17.1900 +USDT") in balance lists, transaction lists, send confirmations, and approval +screens. Timestamps include both an ISO datetime and a humanized relative age +wherever shown. If a formatting rule applies in one place, it applies in every +place. Users should never see the same value rendered differently on two +screens. + +**Exception — Transaction Detail view:** The transaction detail screen is the +authoritative record of a specific transaction and shows the exact, untruncated +amount with all meaningful decimal places (e.g. "0.00498824598498216 ETH"). It +also shows the native quantity (e.g. "4988245984982160 wei") below it. Both are +click-copyable. Truncating to 4 decimals in summary views is acceptable for +scannability, but the detail view must never discard precision — it is the one +place the user goes to verify exact figures. #### Language & Labeling diff --git a/src/popup/index.html b/src/popup/index.html index 445dc46..647da26 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -843,7 +843,11 @@
Amount
-
+
+
+
From
diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index a3c2056..0ec1dc3 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -6,6 +6,7 @@ const { showView, showFlash, addressDotHtml, + addressTitle, escapeHtml, } = require("./helpers"); const { state } = require("../../shared/state"); @@ -67,15 +68,18 @@ function blockieHtml(address) { return ``; } -function txAddressHtml(address, ensName) { +function txAddressHtml(address, ensName, title) { const blockie = blockieHtml(address); const dot = addressDotHtml(address); const link = `https://etherscan.io/address/${address}`; const extLink = `${EXT_ICON}`; let html = `
${blockie}
`; + if (title) { + html += `
${dot}${escapeHtml(title)}
`; + } if (ensName) { html += - `
${dot}` + + `
${title ? "" : dot}` + copyableHtml(ensName, "") + extLink + `
` + @@ -84,7 +88,7 @@ function txAddressHtml(address, ensName) { `
`; } else { html += - `
${dot}` + + `
${title ? "" : dot}` + copyableHtml(address, "break-all") + extLink + `
`; @@ -105,6 +109,9 @@ function show(tx) { 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, @@ -120,11 +127,33 @@ function render() { const tx = state.viewData.tx; if (!tx) return; $("tx-detail-hash").innerHTML = txHashHtml(tx.hash); - $("tx-detail-from").innerHTML = txAddressHtml(tx.from, tx.fromEns); - $("tx-detail-to").innerHTML = txAddressHtml(tx.to, tx.toEns); - $("tx-detail-value").textContent = tx.value - ? tx.value + " " + tx.symbol + + 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"; diff --git a/src/shared/transactions.js b/src/shared/transactions.js index b9f5805..f926784 100644 --- a/src/shared/transactions.js +++ b/src/shared/transactions.js @@ -27,6 +27,9 @@ function parseTx(tx, addrLower) { // For contract calls, produce a meaningful label instead of "0.0000 ETH" let symbol = "ETH"; let value = formatTxValue(formatEther(rawWei)); + let exactValue = formatEther(rawWei); + let rawAmount = rawWei; + let rawUnit = "wei"; let direction = from.toLowerCase() === addrLower ? "sent" : "received"; let directionLabel = direction === "sent" ? "Sent" : "Received"; if (toIsContract && method && method !== "transfer") { @@ -38,6 +41,9 @@ function parseTx(tx, addrLower) { direction = "contract"; directionLabel = label; value = ""; + exactValue = ""; + rawAmount = ""; + rawUnit = ""; } return { @@ -47,6 +53,9 @@ function parseTx(tx, addrLower) { from: from, to: to, value: value, + exactValue: exactValue, + rawAmount: rawAmount, + rawUnit: rawUnit, valueGwei: Math.floor(Number(BigInt(rawWei) / BigInt(1000000000))), symbol: symbol, direction: direction, @@ -63,17 +72,21 @@ function parseTokenTransfer(tt, addrLower) { const from = tt.from?.hash || ""; const to = tt.to?.hash || ""; const decimals = parseInt(tt.total?.decimals || "18", 10); - const rawValue = tt.total?.value || "0"; + const rawVal = tt.total?.value || "0"; const direction = from.toLowerCase() === addrLower ? "sent" : "received"; + const sym = tt.token?.symbol || "?"; return { hash: tt.transaction_hash, blockNumber: tt.block_number, timestamp: Math.floor(new Date(tt.timestamp).getTime() / 1000), from: from, to: to, - value: formatTxValue(formatUnits(rawValue, decimals)), + value: formatTxValue(formatUnits(rawVal, decimals)), + exactValue: formatUnits(rawVal, decimals), + rawAmount: rawVal, + rawUnit: sym + " base units (10^-" + decimals + ")", valueGwei: null, - symbol: tt.token?.symbol || "?", + symbol: sym, direction: direction, directionLabel: direction === "sent" ? "Sent" : "Received", isError: false,