fix: TxApproval links addresses to the explorer of the transaction's network (closes #631)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

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. A chain id that names no network has no explorer, so its
address lines are drawn without a link and the screen still shows. 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
This commit was merged in pull request #633.
This commit is contained in:
2026-10-20 07:01:41 +02:00
parent ca9cd438d9
commit 5b264f4b3e
4 changed files with 84 additions and 17 deletions
+61 -11
View File
@@ -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.
@@ -107,10 +112,13 @@ function decodedLine(label) {
}
// Open the approval screen, with Ethereum Mainnet active and FROM holding
// `tokenBalances` there, for the site's Sepolia transaction sending
// TRANSFER_DATA to the token. Confirm answers the signed transaction with a
// failure, which leads to the error screen.
async function showSepoliaTransfer(tokenBalances = []) {
// `tokenBalances` there, for the site's transaction sending TRANSFER_DATA to
// the token, on Sepolia unless `chainId` names another chain. Confirm answers
// the signed transaction with a failure, which leads to the error screen.
async function showTokenTransfer(
tokenBalances = [],
chainId = NETWORKS.sepolia.chainId,
) {
globalThis.document = makeDocument();
globalThis.window = { location: { search: "" }, close: () => {} };
globalThis.chrome.runtime = {
@@ -131,7 +139,7 @@ async function showSepoliaTransfer(tokenBalances = []) {
approvedTx: {
type: 2,
from: FROM,
chainId: NETWORKS.sepolia.chainId,
chainId,
nonce: "0x7",
gasLimit: "0x30d40",
maxPriorityFeePerGas: "0x3b9aca00",
@@ -183,7 +191,7 @@ describe("a token tracked on Sepolia only", () => {
});
test("is named on the Contract line and the decoded Token line, with its amount at its scale", async () => {
await showSepoliaTransfer();
await showTokenTransfer();
expect(node("approve-tx-to").innerHTML).toContain(
'<div class="font-bold mb-1">TDOL</div>',
);
@@ -196,7 +204,7 @@ describe("a token tracked on Sepolia only", () => {
});
test("labels the amount on the error screen that follows", async () => {
await showSepoliaTransfer();
await showTokenTransfer();
await confirm();
expect(state.currentView).toBe("error-tx");
expect(node("error-tx-summary").textContent).toBe("1234.5000 TDOL");
@@ -207,7 +215,7 @@ describe("a token tracked on Sepolia only", () => {
// tracks and holds there names nothing on Sepolia.
test("a token tracked and held on mainnet only is not named, and its amount is in base units", async () => {
state.trackedTokens = [{ address: TOKEN, symbol: "MDOL", decimals: 18 }];
await showSepoliaTransfer([
await showTokenTransfer([
{ address: TOKEN, symbol: "MDOL", decimals: 18, balance: "1.0" },
]);
expect(node("approve-tx-to").innerHTML).not.toContain("MDOL");
@@ -218,3 +226,45 @@ test("a token tracked and held on mainnet only is not named, and its amount is i
'<div class="font-bold">1234500000 base units (decimals unknown)</div>',
);
});
// The From and Contract lines, and the decoded Token and Recipient lines.
test("every address links to the Sepolia explorer", async () => {
await showTokenTransfer();
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);
}
});
// Chain id 1337 names no network here, so it has no explorer to link to.
test("a transaction on a chain id that names no network is shown with no address links", async () => {
await showTokenTransfer([], "0x539");
expect(state.currentView).toBe("approve-tx");
expect(node("approve-tx-network").textContent).toBe(
"Unknown network (chain id 1337)",
);
expect(node("approve-tx-from").innerHTML).toContain(FROM);
for (const id of [
"approve-tx-from",
"approve-tx-to",
"approve-tx-decoded-details",
]) {
expect(node(id).innerHTML).not.toContain("href=");
}
});