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",