From 9d0e2eab22a6766b3669574ed08c5322ef54c788 Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 20 Oct 2026 02:22:48 +0000 Subject: [PATCH] fix: TxApproval links addresses to the explorer of the transaction's network (closes #631) TxApproval built its From, Contract and decoded-action address links for the active network, so a Sepolia transaction reviewed with mainnet active linked to the mainnet explorer. approvalAddressHtml() now takes the transaction's chain id and passes it to renderAddressHtml(), as SuccessTx and ErrorTx do. SignApproval and SiteApproval still link for the active network. The walletDefects fixture's numeric chain id, which names no network, is now the hex string the background sends. Model: opus-5-5 --- README.md | 2 ++ src/popup/views/approval.js | 21 ++++++++++++----- tests/approvalTokenNetwork.test.js | 36 +++++++++++++++++++++++++++--- tests/walletDefects.test.js | 2 +- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 418bba9..7f6847b 100644 --- a/README.md +++ b/README.md @@ -2395,6 +2395,8 @@ view would leave a wallet one click from deletion. - Raw data: full calldata displayed inline (shown if present) - Password input and an error line - "Confirm" / "Reject" buttons +- **Behavior**: Every etherscan link on the screen opens the explorer of the + network the transaction was populated on, whichever network is active. - **Transitions**: - "Confirm" (correct password) → decrypts and signs the transaction it was shown, exactly as shown, hands the signed transaction to the background to diff --git a/src/popup/views/approval.js b/src/popup/views/approval.js index 6911927..6003fdd 100644 --- a/src/popup/views/approval.js +++ b/src/popup/views/approval.js @@ -58,9 +58,11 @@ const { notify, runtimeApi, sendMessage } = require("../../shared/browserApi"); const erc20Iface = new Interface(ERC20_ABI); -function approvalAddressHtml(address) { +// The link opens the explorer of the network with `chainId`, or of the active +// network when none is given. +function approvalAddressHtml(address, chainId) { const title = addressTitle(address, state.wallets); - return renderAddressHtml(address, { title }); + return renderAddressHtml(address, { title, chainId }); } // The amount line for a decoded ERC-20 call. With a known scale it is the @@ -338,8 +340,14 @@ function showTxApproval(details) { }; } + // Every address link on this screen opens the explorer of the network the + // transaction is for, which the Network line names, whichever network is + // active. $("approve-tx-origin").textContent = details.origin; - $("approve-tx-from").innerHTML = approvalAddressHtml(details.approvedFrom); + $("approve-tx-from").innerHTML = approvalAddressHtml( + details.approvedFrom, + approvedTx.chainId, + ); // Show token symbol next to contract address if known on the network the // transaction is for @@ -349,7 +357,7 @@ function showTxApproval(details) { if (symbol) { toHtml += `
${escapeHtml(symbol)}
`; } - toHtml += approvalAddressHtml(toAddr); + toHtml += approvalAddressHtml(toAddr, approvedTx.chainId); $("approve-tx-to").innerHTML = toHtml; } else { $("approve-tx-to").innerHTML = escapeHtml(CONTRACT_CREATION_TEXT); @@ -388,7 +396,10 @@ function showTxApproval(details) { if (d.isToken) { detailsHtml += `
${escapeHtml(tokenLabel(d.address, approvedTx.chainId) || "Unknown token")}
`; } - detailsHtml += approvalAddressHtml(d.address); + detailsHtml += approvalAddressHtml( + d.address, + approvedTx.chainId, + ); } else { detailsHtml += `
${escapeHtml(d.value)}
`; } diff --git a/tests/approvalTokenNetwork.test.js b/tests/approvalTokenNetwork.test.js index 20ed1dc..62bbf51 100644 --- a/tests/approvalTokenNetwork.test.js +++ b/tests/approvalTokenNetwork.test.js @@ -1,11 +1,16 @@ // The approval screen for a site's Sepolia token transfer while Ethereum -// Mainnet is active (https://git.eeqj.de/sneak/AutistMask/issues/629). +// Mainnet is active. // // The screen named the token, and scaled its amount, from the active network's // tracked tokens and balances, so a token the wallet tracks on Sepolia read as // an unknown token with its amount in base units, and the screens after it -// labelled the amount `?`. It now uses what the wallet knows of the tokens on -// the network the transaction is for. +// labelled the amount `?` (https://git.eeqj.de/sneak/AutistMask/issues/629). +// It now uses what the wallet knows of the tokens on the network the +// transaction is for. +// +// Its address links opened the active network's explorer +// (https://git.eeqj.de/sneak/AutistMask/issues/631). They now open the +// explorer of the network the transaction is for. // // Driven through the real approval screen, password and Confirm included, // against a minimal DOM stub in the shape tests/swapStatusAmount.test.js uses. @@ -218,3 +223,28 @@ test("a token tracked and held on mainnet only is not named, and its amount is i '
1234500000 base units (decimals unknown)
', ); }); + +// The From and Contract lines, and the decoded Token and Recipient lines. +test("every address links to the Sepolia explorer", async () => { + await showSepoliaTransfer(); + const sepolia = NETWORKS.sepolia.explorerUrl; + expect(node("approve-tx-from").innerHTML).toContain( + `href="${sepolia}/address/${FROM}"`, + ); + expect(node("approve-tx-to").innerHTML).toContain( + `href="${sepolia}/address/${TOKEN}"`, + ); + expect(decodedLine("Token")).toContain( + `href="${sepolia}/address/${TOKEN}"`, + ); + expect(decodedLine("Recipient")).toContain( + `href="${sepolia}/address/${RECIPIENT}"`, + ); + for (const id of [ + "approve-tx-from", + "approve-tx-to", + "approve-tx-decoded-details", + ]) { + expect(node(id).innerHTML).not.toContain(NETWORKS.mainnet.explorerUrl); + } +}); diff --git a/tests/walletDefects.test.js b/tests/walletDefects.test.js index d5e9ea5..fe1c389 100644 --- a/tests/walletDefects.test.js +++ b/tests/walletDefects.test.js @@ -475,7 +475,7 @@ describe("every way to a signature or the private key refuses a defective wallet to: RECIPIENT, value: "0x0", data: "0x", - chainId: 1, + chainId: "0x1", nonce: 0, gasLimit: "21000", maxFeePerGas: "1000000000",