From b64f9b56cc8d3330bdf6cdf36e62dce80a7ab742 Mon Sep 17 00:00:00 2001 From: sneak Date: Fri, 27 Feb 2026 12:54:42 +0700 Subject: [PATCH] Show contract calls as "Approve USDT" instead of "0.0000 ETH" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contract interactions (approve, swap, etc.) now display the method name and token symbol instead of the meaningless 0 ETH value. Blockscout provides the method name and whether the target is a contract — parseTx uses these plus TOKEN_BY_ADDRESS to produce labels like "Approve USDT" or "Swap LINK". Added directionLabel field to parsed transactions so renderers don't need to know about the sent/received/contract distinction. Also: clicking a transaction on the home screen now opens the transaction detail view instead of navigating to the address detail view. --- src/popup/views/addressDetail.js | 6 +++-- src/popup/views/addressToken.js | 6 +++-- src/popup/views/home.js | 11 ++++++---- src/popup/views/transactionDetail.js | 5 ++++- src/shared/transactions.js | 33 +++++++++++++++++++++++----- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js index e1c20cf..26ed95d 100644 --- a/src/popup/views/addressDetail.js +++ b/src/popup/views/addressDetail.js @@ -186,8 +186,10 @@ function renderTransactions(txs) { for (const tx of txs) { const counterparty = tx.direction === "sent" ? tx.to : tx.from; const ensName = ensNameMap.get(counterparty) || null; - const dirLabel = tx.direction === "sent" ? "Sent" : "Received"; - const amountStr = escapeHtml(tx.value + " " + tx.symbol); + const dirLabel = tx.directionLabel; + const amountStr = tx.value + ? escapeHtml(tx.value + " " + tx.symbol) + : escapeHtml(tx.symbol); const maxAddr = Math.max(10, 36 - Math.max(0, amountStr.length - 10)); const displayAddr = ensName || truncateMiddle(counterparty, maxAddr); const addrStr = escapeHtml(displayAddr); diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js index 43e4161..8b3d78f 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -211,8 +211,10 @@ function renderTransactions(txs) { for (const tx of txs) { const counterparty = tx.direction === "sent" ? tx.to : tx.from; const ensName = ensNameMap.get(counterparty) || null; - const dirLabel = tx.direction === "sent" ? "Sent" : "Received"; - const amountStr = escapeHtml(tx.value + " " + tx.symbol); + const dirLabel = tx.directionLabel; + const amountStr = tx.value + ? escapeHtml(tx.value + " " + tx.symbol) + : escapeHtml(tx.symbol); const maxAddr = Math.max(10, 36 - Math.max(0, amountStr.length - 10)); const displayAddr = ensName || truncateMiddle(counterparty, maxAddr); const addrStr = escapeHtml(displayAddr); diff --git a/src/popup/views/home.js b/src/popup/views/home.js index 13aa6f6..d57690b 100644 --- a/src/popup/views/home.js +++ b/src/popup/views/home.js @@ -134,8 +134,10 @@ function renderHomeTxList(ctx) { let i = 0; for (const tx of homeTxs) { const counterparty = tx.direction === "sent" ? tx.to : tx.from; - const dirLabel = tx.direction === "sent" ? "Sent" : "Received"; - const amountStr = escapeHtml(tx.value + " " + tx.symbol); + const dirLabel = tx.directionLabel; + const amountStr = tx.value + ? escapeHtml(tx.value + " " + tx.symbol) + : escapeHtml(tx.symbol); const maxAddr = Math.max(10, 36 - Math.max(0, amountStr.length - 10)); const displayAddr = truncateMiddle(counterparty, maxAddr); const addrStr = escapeHtml(displayAddr); @@ -155,7 +157,7 @@ function renderHomeTxList(ctx) { row.addEventListener("click", () => { const idx = parseInt(row.dataset.tx, 10); const tx = homeTxs[idx]; - // Find which wallet/address this tx belongs to and navigate + // Set selectedWallet/selectedAddress so back navigation works for (let wi = 0; wi < state.wallets.length; wi++) { for ( let ai = 0; @@ -169,7 +171,8 @@ function renderHomeTxList(ctx) { ) { state.selectedWallet = wi; state.selectedAddress = ai; - ctx.showAddressDetail(); + state.selectedToken = null; + ctx.showTransactionDetail(tx); return; } } diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index 6c4fb8a..a3c2056 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -110,6 +110,7 @@ function show(tx) { isError: tx.isError, fromEns: tx.fromEns || null, toEns: tx.toEns || null, + directionLabel: tx.directionLabel || null, }, }; render(); @@ -121,7 +122,9 @@ function render() { $("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.symbol; + $("tx-detail-value").textContent = tx.value + ? tx.value + " " + tx.symbol + : tx.directionLabel + " " + tx.symbol; $("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 71a7203..b9f5805 100644 --- a/src/shared/transactions.js +++ b/src/shared/transactions.js @@ -8,7 +8,7 @@ const { formatEther, formatUnits } = require("ethers"); const { log, debugFetch } = require("./log"); -const { KNOWN_SYMBOLS } = require("./tokenList"); +const { KNOWN_SYMBOLS, TOKEN_BY_ADDRESS } = require("./tokenList"); function formatTxValue(val) { const parts = val.split("."); @@ -22,21 +22,40 @@ function parseTx(tx, addrLower) { const to = tx.to?.hash || ""; const rawWei = tx.value || "0"; const toIsContract = tx.to?.is_contract || false; + const method = tx.method || null; + + // For contract calls, produce a meaningful label instead of "0.0000 ETH" + let symbol = "ETH"; + let value = formatTxValue(formatEther(rawWei)); + let direction = from.toLowerCase() === addrLower ? "sent" : "received"; + let directionLabel = direction === "sent" ? "Sent" : "Received"; + if (toIsContract && method && method !== "transfer") { + const token = TOKEN_BY_ADDRESS.get(to.toLowerCase()); + if (token) { + symbol = token.symbol; + } + const label = method.charAt(0).toUpperCase() + method.slice(1); + direction = "contract"; + directionLabel = label; + value = ""; + } + return { hash: tx.hash, blockNumber: tx.block_number, timestamp: Math.floor(new Date(tx.timestamp).getTime() / 1000), from: from, to: to, - value: formatTxValue(formatEther(rawWei)), + value: value, valueGwei: Math.floor(Number(BigInt(rawWei) / BigInt(1000000000))), - symbol: "ETH", - direction: from.toLowerCase() === addrLower ? "sent" : "received", + symbol: symbol, + direction: direction, + directionLabel: directionLabel, isError: tx.status !== "ok", contractAddress: null, holders: null, isContractCall: toIsContract, - method: tx.method || null, + method: method, }; } @@ -45,6 +64,7 @@ function parseTokenTransfer(tt, addrLower) { const to = tt.to?.hash || ""; const decimals = parseInt(tt.total?.decimals || "18", 10); const rawValue = tt.total?.value || "0"; + const direction = from.toLowerCase() === addrLower ? "sent" : "received"; return { hash: tt.transaction_hash, blockNumber: tt.block_number, @@ -54,7 +74,8 @@ function parseTokenTransfer(tt, addrLower) { value: formatTxValue(formatUnits(rawValue, decimals)), valueGwei: null, symbol: tt.token?.symbol || "?", - direction: from.toLowerCase() === addrLower ? "sent" : "received", + direction: direction, + directionLabel: direction === "sent" ? "Sent" : "Received", isError: false, contractAddress: ( tt.token?.address_hash ||