Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66cae2e593 |
@@ -418,10 +418,6 @@ Each group of these assertions records a coverage key and a final case demands
|
|||||||
the exact set, so a section that silently stopped running reddens the suite
|
the exact set, so a section that silently stopped running reddens the suite
|
||||||
instead of quietly shrinking it.
|
instead of quietly shrinking it.
|
||||||
|
|
||||||
It also covers the red banner on the Settings screen: switching the network
|
|
||||||
there to Sepolia must put up the `[TESTNET]` banner, and switching back to
|
|
||||||
mainnet must take it down, both while Settings is still on screen.
|
|
||||||
|
|
||||||
It also covers the confirmation screen, for both a native ETH send and an ERC-20
|
It also covers the confirmation screen, for both a native ETH send and an ERC-20
|
||||||
send: Send disabled while the fee estimate is in flight, enabled once it lands,
|
send: Send disabled while the fee estimate is in flight, enabled once it lands,
|
||||||
the fee block quoting the expected cost and the reserve separately, the distinct
|
the fee block quoting the expected cost and the reserve separately, the distinct
|
||||||
@@ -3118,23 +3114,21 @@ Currently supported:
|
|||||||
may pay an address went unread. When no step pays anyone, a single line reads
|
may pay an address went unread. When no step pays anyone, a single line reads
|
||||||
`Unknown (not named in the calldata)`. Wherever there is a `Paid to` line, the
|
`Unknown (not named in the calldata)`. Wherever there is a `Paid to` line, the
|
||||||
minimum reads `Min. paid out`. After the `Amount` line (after `Token In` when
|
minimum reads `Min. paid out`. After the `Amount` line (after `Token In` when
|
||||||
there is none), each `PERMIT2_TRANSFER_FROM` but one into the router, in step
|
there is none), each `PERMIT2_TRANSFER_FROM`, in step order, gives three
|
||||||
order, gives three lines: `Moved from the sender`, the amount it moves, for
|
lines: `Moved from the sender`, the amount it moves, for example
|
||||||
example `1000000.0000 USDT`; `Token moved`, giving the token's address as the
|
`1000000.0000 USDT`; `Token moved`, giving the token's address as the
|
||||||
`Token In` line does; and `Moved to`, which reads
|
`Token In` line does; and `Moved to`, which reads `The router` for a move into
|
||||||
`The V2 pair the next swap spends` for a hop and `The sender` for `address(1)`
|
the router, `The V2 pair the next swap spends` for a hop, and `The sender` for
|
||||||
on every router but `UniversalRouterV1`, and otherwise gives the address. A
|
`address(1)` on every router but `UniversalRouterV1`, and otherwise gives the
|
||||||
move into the router needs none of them, because its `Paid to` line and
|
address. These lines change neither the `Paid to` lines nor the minimum's
|
||||||
`Token moved into the router` line already give the amount, the token and
|
label. A step goes unread when its recipient does not decode as an address, or
|
||||||
where it goes, and so its amount is shown once. These lines change neither the
|
when it is a command the decoder does not read: every command other than
|
||||||
`Paid to` lines nor the minimum's label. A step goes unread when its recipient
|
`PERMIT2_PERMIT`, `PERMIT2_TRANSFER_FROM`, `V2_SWAP_EXACT_IN`,
|
||||||
does not decode as an address, or when it is a command the decoder does not
|
`V2_SWAP_EXACT_OUT`, `V3_SWAP_EXACT_IN`, `V4_SWAP`, `SWEEP`, `TRANSFER`,
|
||||||
read: every command other than `PERMIT2_PERMIT`, `PERMIT2_TRANSFER_FROM`,
|
`PAY_PORTION`, `PAY_PORTION_FULL_PRECISION`, `ACROSS_V4_DEPOSIT_V3`,
|
||||||
`V2_SWAP_EXACT_IN`, `V2_SWAP_EXACT_OUT`, `V3_SWAP_EXACT_IN`, `V4_SWAP`,
|
`WRAP_ETH`, `UNWRAP_WETH` and `BALANCE_CHECK_ERC20` (`V3_SWAP_EXACT_OUT`,
|
||||||
`SWEEP`, `TRANSFER`, `PAY_PORTION`, `PAY_PORTION_FULL_PRECISION`,
|
`PERMIT2_TRANSFER_FROM_BATCH`, `EXECUTE_SUB_PLAN` and the NFT commands among
|
||||||
`ACROSS_V4_DEPOSIT_V3`, `WRAP_ETH`, `UNWRAP_WETH` and `BALANCE_CHECK_ERC20`
|
them), or a type its release names no command for.
|
||||||
(`V3_SWAP_EXACT_OUT`, `PERMIT2_TRANSFER_FROM_BATCH`, `EXECUTE_SUB_PLAN` and
|
|
||||||
the NFT commands among them), or a type its release names no command for.
|
|
||||||
|
|
||||||
### Non-Goals Forever
|
### Non-Goals Forever
|
||||||
|
|
||||||
|
|||||||
@@ -128,8 +128,7 @@ function showView(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create or update the debug/insecure warning banner.
|
// Create or update the debug/insecure warning banner.
|
||||||
// Called on every view switch, after the settings debug toggle changes, and
|
// Called on every view switch and after the settings debug toggle changes.
|
||||||
// after a network switch in Settings.
|
|
||||||
// The banner is shown when the compile-time DEBUG constant is true OR when
|
// The banner is shown when the compile-time DEBUG constant is true OR when
|
||||||
// the user has enabled runtime debug mode via the settings easter egg, OR
|
// the user has enabled runtime debug mode via the settings easter egg, OR
|
||||||
// when the active network is a testnet.
|
// when the active network is a testnet.
|
||||||
|
|||||||
@@ -343,13 +343,11 @@ function init(ctx) {
|
|||||||
// switch request.
|
// switch request.
|
||||||
notify({ type: "AUTISTMASK_NETWORK_CHANGED", chainId: net.chainId });
|
notify({ type: "AUTISTMASK_NETWORK_CHANGED", chainId: net.chainId });
|
||||||
resetRenderedViews();
|
resetRenderedViews();
|
||||||
// Settings itself, and the testnet banner above it, are brought up
|
// Settings itself is brought up to date for the new network here.
|
||||||
// to date for the new network here.
|
|
||||||
markViewRendered("settings");
|
markViewRendered("settings");
|
||||||
$("settings-rpc").value = state.rpcUrl;
|
$("settings-rpc").value = state.rpcUrl;
|
||||||
$("settings-blockscout").value = state.blockscoutUrl;
|
$("settings-blockscout").value = state.blockscoutUrl;
|
||||||
renderTrackedTokens();
|
renderTrackedTokens();
|
||||||
updateDebugBanner(state.currentView);
|
|
||||||
showFlash("Switched to " + net.name + ".");
|
showFlash("Switched to " + net.name + ".");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1186,12 +1186,11 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// router, as { token, amount }. Later steps may leave part of it
|
// router, as { token, amount }. Later steps may leave part of it
|
||||||
// there: nothing here checks that they spend all of it.
|
// there: nothing here checks that they spend all of it.
|
||||||
const movedIntoRouter = [];
|
const movedIntoRouter = [];
|
||||||
// What each PERMIT2_TRANSFER_FROM not into the router moves from the
|
// What each PERMIT2_TRANSFER_FROM moves from the sender, in step
|
||||||
// sender, in step order, whoever counts as paid, as
|
// order, whoever counts as paid, as { token, recipient, amount, to }:
|
||||||
// { token, recipient, amount, to }: `to` says where it goes, the
|
// `to` says where it goes, the sender, the router or the V2 pair the
|
||||||
// sender or the V2 pair the next swap spends, and is null when it
|
// next swap spends, and is null when it goes to `recipient` as
|
||||||
// goes to `recipient` as written. A move into the router is in
|
// written.
|
||||||
// `movedIntoRouter`, whose lines give its amount already.
|
|
||||||
const movedFromSender = [];
|
const movedFromSender = [];
|
||||||
// Each token a step paid to the router that no later step paid out
|
// Each token a step paid to the router that no later step paid out
|
||||||
// or spent in full, by its address, lowercased, ETH as the zero
|
// or spent in full, by its address, lowercased, ETH as the zero
|
||||||
@@ -1327,6 +1326,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
movedFromSender.push({ ...p, to: null });
|
movedFromSender.push({ ...p, to: null });
|
||||||
} else if (isRouter(p.recipient)) {
|
} else if (isRouter(p.recipient)) {
|
||||||
movedIntoRouter.push(p);
|
movedIntoRouter.push(p);
|
||||||
|
movedFromSender.push({ ...p, to: "The router" });
|
||||||
} else if (isHop(i, p.token, p.recipient)) {
|
} else if (isHop(i, p.token, p.recipient)) {
|
||||||
movedFromSender.push({
|
movedFromSender.push({
|
||||||
...p,
|
...p,
|
||||||
|
|||||||
@@ -1824,65 +1824,6 @@ test("the Settings assertions above all ran (#229)", async (env) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ------------------------------------------------ testnet banner (#511)
|
|
||||||
|
|
||||||
// This suite runs a release build, so the red banner at the top reads
|
|
||||||
// "[TESTNET]" on Sepolia and is absent on mainnet. A network switch in
|
|
||||||
// Settings must change it while Settings is still on screen; it used to
|
|
||||||
// change only when the next screen came up
|
|
||||||
// (https://git.eeqj.de/sneak/AutistMask/issues/511).
|
|
||||||
|
|
||||||
// The banner's text, or null when there is none, once the switch to `net`
|
|
||||||
// has finished. The switch's flash is the last thing its handler does, and
|
|
||||||
// showView() clears the flash, so the banner read while that flash is up is
|
|
||||||
// the one the switch left on the screen it was made from.
|
|
||||||
async function bannerAfterSwitch(page, net) {
|
|
||||||
await page.waitForFunction(
|
|
||||||
(text) => document.getElementById("flash-msg").textContent === text,
|
|
||||||
"Switched to " + net.name + ".",
|
|
||||||
);
|
|
||||||
return page.evaluate(() => {
|
|
||||||
const banner = document.getElementById("debug-banner");
|
|
||||||
return {
|
|
||||||
text: banner === null ? null : banner.textContent,
|
|
||||||
onSettings: !document
|
|
||||||
.getElementById("view-settings")
|
|
||||||
.classList.contains("hidden"),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
test("a network switch in Settings shows and removes the testnet banner at once (#511)", async (env) => {
|
|
||||||
const { mainnet, sepolia } = NETWORKS;
|
|
||||||
await openSettings(env.page);
|
|
||||||
assert(
|
|
||||||
(await env.page.locator("#debug-banner").count()) === 0,
|
|
||||||
"a banner is up on mainnet before the switch",
|
|
||||||
);
|
|
||||||
|
|
||||||
await env.page.selectOption("#settings-network", "sepolia");
|
|
||||||
let after = await bannerAfterSwitch(env.page, sepolia);
|
|
||||||
assert(after.onSettings, "Settings left the screen on the switch");
|
|
||||||
assert(
|
|
||||||
after.text === "[TESTNET]",
|
|
||||||
"after the switch to Sepolia, Settings shows the banner " +
|
|
||||||
JSON.stringify(after.text) +
|
|
||||||
', expected "[TESTNET]"',
|
|
||||||
);
|
|
||||||
|
|
||||||
await env.page.selectOption("#settings-network", "mainnet");
|
|
||||||
after = await bannerAfterSwitch(env.page, mainnet);
|
|
||||||
assert(after.onSettings, "Settings left the screen on the switch back");
|
|
||||||
assert(
|
|
||||||
after.text === null,
|
|
||||||
"after the switch back to mainnet, Settings still shows the banner " +
|
|
||||||
JSON.stringify(after.text),
|
|
||||||
);
|
|
||||||
|
|
||||||
await env.page.click("#btn-settings-back");
|
|
||||||
await visible(env.page, "#view-main");
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------------------------------------------ dust threshold (#233)
|
// ------------------------------------------------ dust threshold (#233)
|
||||||
|
|
||||||
// The popup size README documents the UI as designed for. Pages in this
|
// The popup size README documents the UI as designed for. Pages in this
|
||||||
|
|||||||
@@ -1167,14 +1167,6 @@ describe("each transfer command's recipient", () => {
|
|||||||
expect(detail(result, "Min. received").value).toBe("1.0000 WETH");
|
expect(detail(result, "Min. received").value).toBe("1.0000 WETH");
|
||||||
}
|
}
|
||||||
|
|
||||||
// A move to the From address as written shows that address, not The
|
|
||||||
// sender.
|
|
||||||
expect(detail(decode(movedThenSwapped(USER)), "Moved to")).toEqual({
|
|
||||||
label: "Moved to",
|
|
||||||
value: USER,
|
|
||||||
address: USER,
|
|
||||||
});
|
|
||||||
|
|
||||||
const toOther = decode(movedThenSwapped(OTHER));
|
const toOther = decode(movedThenSwapped(OTHER));
|
||||||
expect(labels(toOther)).toEqual(withMove(PAID_ELSEWHERE));
|
expect(labels(toOther)).toEqual(withMove(PAID_ELSEWHERE));
|
||||||
expect(paidTo(toOther)).toEqual([OTHER]);
|
expect(paidTo(toOther)).toEqual([OTHER]);
|
||||||
@@ -1200,6 +1192,9 @@ describe("each transfer command's recipient", () => {
|
|||||||
"Protocol",
|
"Protocol",
|
||||||
"Token In",
|
"Token In",
|
||||||
"Amount",
|
"Amount",
|
||||||
|
"Moved from the sender",
|
||||||
|
"Token moved",
|
||||||
|
"Moved to",
|
||||||
"Token Out",
|
"Token Out",
|
||||||
"Paid to",
|
"Paid to",
|
||||||
"Token moved into the router",
|
"Token moved into the router",
|
||||||
@@ -1317,9 +1312,8 @@ describe("each transfer command's recipient", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// https://git.eeqj.de/sneak/AutistMask/issues/611: after the swap's own
|
// https://git.eeqj.de/sneak/AutistMask/issues/611: after the swap's own
|
||||||
// Amount, each PERMIT2_TRANSFER_FROM but one into the router gives the amount
|
// Amount, each PERMIT2_TRANSFER_FROM gives the amount of the sender's tokens
|
||||||
// of the sender's tokens it moves, the token, and where it goes, whoever
|
// it moves, the token, and where it goes, whoever counts as paid.
|
||||||
// counts as paid.
|
|
||||||
describe("what each PERMIT2_TRANSFER_FROM moves", () => {
|
describe("what each PERMIT2_TRANSFER_FROM moves", () => {
|
||||||
// The WETH/USDC pair on Ethereum Mainnet, as its V2 factory reports it.
|
// The WETH/USDC pair on Ethereum Mainnet, as its V2 factory reports it.
|
||||||
const PAIR = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc";
|
const PAIR = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc";
|
||||||
@@ -1389,9 +1383,7 @@ describe("what each PERMIT2_TRANSFER_FROM moves", () => {
|
|||||||
expect(detail(result, "Min. received").value).toBe("1990.0000 USDC");
|
expect(detail(result, "Min. received").value).toBe("1990.0000 USDC");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Its Paid to and Token moved into the router lines already say what a
|
test("2,000 USDT moved into the router, then a swap of 1 USDT from the router's balance", () => {
|
||||||
// move into the router moves, so it gets no lines after Amount.
|
|
||||||
test("2,000 USDT moved into the router, then a swap of 1 USDT from the router's balance, shows the 2,000 USDT once", () => {
|
|
||||||
const result = decode(
|
const result = decode(
|
||||||
execute(
|
execute(
|
||||||
permit2TransferFrom(USDT, ADDRESS_THIS, USDT_2000),
|
permit2TransferFrom(USDT, ADDRESS_THIS, USDT_2000),
|
||||||
@@ -1399,11 +1391,10 @@ describe("what each PERMIT2_TRANSFER_FROM moves", () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(detail(result, "Amount").value).toBe("1.0000 USDT");
|
expect(detail(result, "Amount").value).toBe("1.0000 USDT");
|
||||||
expect(afterAmount(result)).toEqual([]);
|
expect(afterAmount(result)).toEqual(
|
||||||
|
moved("2000.0000 USDT", USDT, "USDT", movedTo("The router")),
|
||||||
|
);
|
||||||
expect(paidTo(result)).toEqual([movedIntoRouter("2000.0000 USDT")]);
|
expect(paidTo(result)).toEqual([movedIntoRouter("2000.0000 USDT")]);
|
||||||
expect(
|
|
||||||
result.details.filter((d) => d.value.includes("2000.0000 USDT")),
|
|
||||||
).toHaveLength(1);
|
|
||||||
expect(detail(result, "Min. paid out").value).toBe("1.0000 WETH");
|
expect(detail(result, "Min. paid out").value).toBe("1.0000 WETH");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2930,11 +2921,7 @@ describe("the screens show what a PERMIT2_TRANSFER_FROM moves", () => {
|
|||||||
])("the $screen screen", async ({ show }) => {
|
])("the $screen screen", async ({ show }) => {
|
||||||
const html = await show(millionToOther);
|
const html = await show(millionToOther);
|
||||||
expect(html).toContain("1000000.0000 USDT");
|
expect(html).toContain("1000000.0000 USDT");
|
||||||
// Each line is drawn in a div of its own; the Paid to line names
|
expect(html).toContain("Moved to");
|
||||||
// OTHER too, so the Moved to line's own div is checked.
|
expect(html).toContain('data-copy="' + OTHER + '"');
|
||||||
const movedToLine = html
|
|
||||||
.split('<div class="mb-2">')
|
|
||||||
.find((line) => line.includes(">Moved to<"));
|
|
||||||
expect(movedToLine).toContain('data-copy="' + OTHER + '"');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user