1 Commits
Author SHA1 Message Date
clawbot 2c2f497dca fix: a swap split across steps the user pays for shows every amount it spends (closes #625)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
The Amount line gave only the first swap step's amount. It now gives the
total the steps take of Token In: the amount of the step that sets the
input side plus each later take of it by a step the user pays. A V4
step, the first included, takes what its SETTLEs paid by the user state.
A take of another token, one after an amount that is not a quantity, or
one of unknown amount gets lines of its own. A step that may take what
the decoder did not read gets one line saying so and leaves no figure.
A PERMIT2_PERMIT names Token In; its amount stands only in the plain
shape of Uniswap's own routes.

Also closes #628.

Model: opus-5-5
2026-10-20 05:01:09 +00:00
4 changed files with 17 additions and 84 deletions
-2
View File
@@ -2392,8 +2392,6 @@ 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
+5 -20
View File
@@ -58,15 +58,9 @@ const { notify, runtimeApi, sendMessage } = require("../../shared/browserApi");
const erc20Iface = new Interface(ERC20_ABI);
// The link opens the explorer of the network with `chainId`, or of the active
// network when none is given. A chain id that names no network has no
// explorer, so the address is drawn without a link.
function approvalAddressHtml(address, chainId) {
function approvalAddressHtml(address) {
const title = addressTitle(address, state.wallets);
if (chainId && !networkByChainId(chainId)) {
return renderAddressHtml(address, { title, noLink: true });
}
return renderAddressHtml(address, { title, chainId });
return renderAddressHtml(address, { title });
}
// The amount line for a decoded ERC-20 call. With a known scale it is the
@@ -344,14 +338,8 @@ 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,
approvedTx.chainId,
);
$("approve-tx-from").innerHTML = approvalAddressHtml(details.approvedFrom);
// Show token symbol next to contract address if known on the network the
// transaction is for
@@ -361,7 +349,7 @@ function showTxApproval(details) {
if (symbol) {
toHtml += `<div class="font-bold mb-1">${escapeHtml(symbol)}</div>`;
}
toHtml += approvalAddressHtml(toAddr, approvedTx.chainId);
toHtml += approvalAddressHtml(toAddr);
$("approve-tx-to").innerHTML = toHtml;
} else {
$("approve-tx-to").innerHTML = escapeHtml(CONTRACT_CREATION_TEXT);
@@ -400,10 +388,7 @@ function showTxApproval(details) {
if (d.isToken) {
detailsHtml += `<div class="font-bold">${escapeHtml(tokenLabel(d.address, approvedTx.chainId) || "Unknown token")}</div>`;
}
detailsHtml += approvalAddressHtml(
d.address,
approvedTx.chainId,
);
detailsHtml += approvalAddressHtml(d.address);
} else {
detailsHtml += `<div class="font-bold">${escapeHtml(d.value)}</div>`;
}
+11 -61
View File
@@ -1,16 +1,11 @@
// The approval screen for a site's Sepolia token transfer while Ethereum
// Mainnet is active.
// Mainnet is active (https://git.eeqj.de/sneak/AutistMask/issues/629).
//
// 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 `?` (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.
// labelled the amount `?`. It now uses what the wallet knows of the tokens on
// 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.
@@ -112,13 +107,10 @@ function decodedLine(label) {
}
// Open the approval screen, with Ethereum Mainnet active and FROM holding
// `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,
) {
// `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 = []) {
globalThis.document = makeDocument();
globalThis.window = { location: { search: "" }, close: () => {} };
globalThis.chrome.runtime = {
@@ -139,7 +131,7 @@ async function showTokenTransfer(
approvedTx: {
type: 2,
from: FROM,
chainId,
chainId: NETWORKS.sepolia.chainId,
nonce: "0x7",
gasLimit: "0x30d40",
maxPriorityFeePerGas: "0x3b9aca00",
@@ -191,7 +183,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 showTokenTransfer();
await showSepoliaTransfer();
expect(node("approve-tx-to").innerHTML).toContain(
'<div class="font-bold mb-1">TDOL</div>',
);
@@ -204,7 +196,7 @@ describe("a token tracked on Sepolia only", () => {
});
test("labels the amount on the error screen that follows", async () => {
await showTokenTransfer();
await showSepoliaTransfer();
await confirm();
expect(state.currentView).toBe("error-tx");
expect(node("error-tx-summary").textContent).toBe("1234.5000 TDOL");
@@ -215,7 +207,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 showTokenTransfer([
await showSepoliaTransfer([
{ address: TOKEN, symbol: "MDOL", decimals: 18, balance: "1.0" },
]);
expect(node("approve-tx-to").innerHTML).not.toContain("MDOL");
@@ -226,45 +218,3 @@ 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=");
}
});
+1 -1
View File
@@ -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: "0x1",
chainId: 1,
nonce: 0,
gasLimit: "21000",
maxFeePerGas: "1000000000",