1 Commits
Author SHA1 Message Date
sneak 5dc24d2d57 fix: a WRAP_ETH says where its ETH goes when no swap step spends it (closes #747)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run
The `Moved to` line of a WRAP_ETH into the router now reads `The router`,
unless a later swap step pays from that WETH.

A later WRAP_ETH of CONTRACT_BALANCE to anyone but the router now gets a
`Moved from the sender` line reading `All the ETH the transaction carries`,
and `Moved to` names the recipient. It gets no lines after an earlier step
paid ETH to the router, because Uniswap's routes pay back unspent ETH this way.

README states the new rule.

Model: opus-5-5
2026-11-03 13:18:49 +00:00
5 changed files with 96 additions and 162 deletions
+12 -16
View File
@@ -471,9 +471,7 @@ When "Back" returns to a screen that this page load has not drawn yet,
Home is shown instead. "Back" onto a screen already drawn only unhides it, so
unsaved input in Settings survives. Home is drawn again on every "Back". When a
balance refresh returns while AddressDetail or AddressToken is showing, that
screen draws its balances again. When a refresh returns while Send is showing,
Send draws its From address and current balance again. Its inputs and dropdown
stay as the user left them.
screen draws its balances again.
A network switch, or a change to "Use Blockscout", cuts the stack at the first
screen that cannot be drawn from saved state. The cut also drops AddressToken
@@ -1414,19 +1412,17 @@ when both tokens are known. The lines are:
the sender that the `Amount` line does not include. The same three lines show
what a `PERMIT2_TRANSFER_FROM` moves and where. A `WRAP_ETH` can pay its WETH
to a recipient other than the router. When its amount gets these lines, its
`Moved to` line names that recipient, or reads `The sender`. For a `WRAP_ETH`
into the router, `Moved to` reads `The router`. A `WRAP_ETH` after the step
that sets `Token In` takes the amount it states from the sender. When it pays
anyone but the router, that amount gets these lines. When it pays the router,
that amount joins the `Amount` line if `Token In` is ETH, and gets these lines
if not. A later `WRAP_ETH` of `CONTRACT_BALANCE` to anyone but the router gets
these lines. It gets none after an `UNWRAP_WETH` into the router, or while the
router holds ETH from a V4 step. When it gets them, its
`Moved from the sender` line reads `All the ETH the transaction carries`. ETH
that the sender pays a V4 step or a `WRAP_ETH` of a stated amount can get
these three lines when the router may hold ETH from an earlier step. When the
decoder knows the amount, its `Moved from the sender` line reads `Up to` and
the amount.
`Moved to` line names that recipient, or reads `The sender`. A `WRAP_ETH`
after the step that sets `Token In` takes the amount it states from the
sender. When it pays anyone but the router, that amount gets these lines. When
it pays the router, that amount joins the `Amount` line if `Token In` is ETH,
and gets these lines if not. A later `WRAP_ETH` of `CONTRACT_BALANCE` to
anyone but the router gets these lines, unless an earlier step paid ETH to the
router. Its `Moved from the sender` line reads
`All the ETH the transaction carries`. ETH that the sender pays a V4 step or a
`WRAP_ETH` of a stated amount can get these three lines when the router may
hold ETH from an earlier step. When the decoder knows the amount, its
`Moved from the sender` line reads `Up to` and the amount.
- `Permit2 allowance`, `Allowance token` and `Allowance spender`: each allowance
that a `PERMIT2_PERMIT` or `PERMIT2_PERMIT_BATCH` grants, unless the permit's
amount already stands as the `Amount` figure.
+2 -5
View File
@@ -107,10 +107,8 @@ let refreshTimer = null;
// tracked token list stays as it is.
//
// Once the save is done, the wallet list is drawn again. So are the balances
// on AddressDetail or AddressToken when that screen is up. When Send is up, its
// From address and current balance are drawn again, and its inputs and dropdown
// stay as the user left them. No other screen is drawn again, DeleteAddress and
// its balance lines included.
// on AddressDetail or AddressToken when that screen is up. No other screen is
// drawn again, DeleteAddress and its balance lines included.
async function doRefreshAndRender() {
if (refreshInFlight) {
refreshAgain = true;
@@ -166,7 +164,6 @@ async function doRefreshAndRender() {
renderWalletList();
if (state.currentView === "address") addressDetail.renderBalances();
if (state.currentView === "address-token") addressToken.renderBalance();
if (state.currentView === "send") send.updateSendBalance();
} catch (e) {
// No caller waits for this function, so it must never reject. An
// unhandled rejection reports nothing. The save inside it reports its
+61 -37
View File
@@ -1273,10 +1273,12 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
// ERC-20 token from the router takes none of that token from the
// sender.
let inputPaidByUser = false;
// The recipient of a WRAP_ETH that set the input side, as written;
// otherwise null. That WRAP_ETH pays its WETH there (see the Moved to
// lines).
// The recipient of a WRAP_ETH that set the input side, as written,
// when it is not the router; otherwise null. That WRAP_ETH pays its
// WETH there, not to a swap step (see the Moved to lines).
let wrapEthRecipient = null;
// The index of the WRAP_ETH that set the input side, or null.
let wrapEthStep = null;
// The last PERMIT2_PERMIT this decoder read before a step set the
// input side, or null (see permitStands after the loop).
let permit = null;
@@ -1317,6 +1319,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
// other than WETH, and not of ETH to a WETH contract, which pays the
// router WETH (see keepsRouterWeth in the loop). None of those can.
let lastWethStep = -1;
// The index of the last swap step paid from WETH the router holds, a
// V4 SETTLE included, or -1 (see spendFromRouter()). Only such a step
// spends the WETH of an earlier WRAP_ETH into the router.
let lastRouterWethSwapStep = -1;
// Where the step that set the output side paid its output: the
// recipient of a swap step, as written, or of each TAKE, TAKE_ALL and
// TAKE_PORTION of it in a V4 step, a TAKE_ALL as paid to the sender,
@@ -1377,9 +1383,11 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
// After the loop each joins the Amount line or gets lines of its own
// (see permitStands). A take with `atMost` set may take less than
// `amount`: it never joins the Amount line, and its line reads
// "Up to" and the amount. A take with a `recipient` is the ETH of a
// later WRAP_ETH. If that recipient is not the router, the take never
// joins the Amount line either: its Moved to line names that recipient.
// "Up to" and the amount. A take with a `recipient`, the ETH of a
// later WRAP_ETH that pays anyone but the router, never joins the
// Amount line either: its Moved to line names that recipient. The
// take of a later WRAP_ETH of a stated amount has that step's index as
// `wrapStep`.
const userPaid = [];
// A swap step the user pays for (`payerIsUser` true) takes `amount`
// of `token` from the sender, or at most that when `atMost` is set. A
@@ -1454,17 +1462,20 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
const r = recipient.toLowerCase();
return r === MSG_SENDER || r === sender;
};
// A swap step or V4 SETTLE pays `amount` of `token` from the router's
// own balance. CONTRACT_BALANCE pays all of it. Any other quantity
// counts against the amount a WRAP_ETH stated, and the steps pay all
// of it once their amounts reach that one: a swap paid in ETH wraps
// what its swap steps then spend. An amount that is not a quantity,
// such as the null decodeV4Swap() gives a SETTLE whose payment it
// cannot tell, or one of a token whose amount is not known, pays out
// nothing this decoder can see.
const spendFromRouter = (token, amount) => {
// A swap step or V4 SETTLE at `step` pays `amount` of `token` from the
// router's own balance. CONTRACT_BALANCE pays all of it. Any other
// quantity counts against the amount a WRAP_ETH stated, and the steps
// pay all of it once their amounts reach that one: a swap paid in ETH
// wraps what its swap steps then spend. An amount that is not a
// quantity, such as the null decodeV4Swap() gives a SETTLE whose
// payment it cannot tell, or one of a token whose amount is not known,
// pays out nothing this decoder can see.
const spendFromRouter = (step, token, amount) => {
const t = token.toLowerCase();
const held = heldByRouter.get(t);
if (t === WETH.get(chainId) && heldByRouter.has(t)) {
lastRouterWethSwapStep = step;
}
if (amount === CONTRACT_BALANCE) {
heldByRouter.delete(t);
} else if (typeof held === "bigint" && typeof amount === "bigint") {
@@ -1750,7 +1761,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
[s.recipient],
);
if (!s.payerIsUser) {
spendFromRouter(s.tokenIn, s.amountIn);
spendFromRouter(i, s.tokenIn, s.amountIn);
}
if (!isHop(i, s.tokenOut, s.recipient)) {
addPayee(s.recipient, s.tokenOut);
@@ -1779,7 +1790,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
[s.recipient],
);
if (!s.payerIsUser) {
spendFromRouter(s.tokenIn, s.amountIn);
spendFromRouter(i, s.tokenIn, s.amountIn);
}
if (!isHop(i, s.tokenOut, s.recipient)) {
addPayee(s.recipient, s.tokenOut);
@@ -1795,8 +1806,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
) {
// Buys exactly amountOut and spends at most amountInMax.
// Paid from the router, it spends an amount the calldata
// does not state, so it spends nothing spendFromRouter()
// could count.
// does not state, so spendFromRouter() has none to count.
hasV2OrV3ExactOut = true;
const s =
command === "V2_SWAP_EXACT_OUT"
@@ -1818,6 +1828,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
]),
[s.recipient],
);
if (!s.payerIsUser) {
spendFromRouter(i, s.tokenIn, null);
}
if (!isHop(i, s.tokenOut, s.recipient)) {
addPayee(s.recipient, s.tokenOut);
}
@@ -1915,7 +1928,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
w.amount,
);
inputPaidByUser = true;
wrapEthRecipient = w.recipient;
wrapEthStep = i;
if (!isRouter(w.recipient)) {
wrapEthRecipient = w.recipient;
}
} else if (
w.amount > 0n &&
w.amount !== CONTRACT_BALANCE
@@ -1930,7 +1946,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
atMost: heldByRouter.has(
"0x0000000000000000000000000000000000000000",
),
recipient: w.recipient,
recipient: isRouter(w.recipient)
? null
: w.recipient,
wrapStep: i,
});
} else if (
w.amount === CONTRACT_BALANCE &&
@@ -1941,11 +1960,10 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
)
) {
// It pays the recipient all the ETH the transaction
// carries. It gets no lines after an UNWRAP_WETH
// into the router, or while the router holds ETH
// from a V4 step: Uniswap's routes pay back what
// their swap did not spend this way, and carry no
// ETH.
// carries. After an earlier step paid ETH to the
// router there is no line: Uniswap's routes pay back
// what their swap did not spend this way, and carry
// no ETH.
userPaid.push({
token: "0x0000000000000000000000000000000000000000",
amount: TRANSACTION_ETH,
@@ -2019,7 +2037,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
// Each SETTLE paid from the router spends the amount
// decodeV4Swap() says it pays.
for (const s of v4.settledFromRouter) {
spendFromRouter(s.currency, s.amount);
spendFromRouter(i, s.currency, s.amount);
}
for (const t of v4.takes) {
if (!isHop(i, t.currency, t.recipient)) {
@@ -2290,10 +2308,11 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
permit.beforeEveryTake &&
!stepsUnread.some((step) => step > permit.step);
// What the steps take that the Amount line does not give, as
// { token, amount, atMost, recipient }. A null token is a step this
// decoder did not read (see spentUnread()), or a token it does not
// know. `atMost` is as in `userPaid`. Only the take of a WRAP_ETH has
// a recipient (see wrapEthRecipient and userPaid).
// { token, amount, atMost, recipient, wrapStep }. A null token is a
// step this decoder did not read (see spentUnread()), or a token it
// does not know. `atMost` is as in `userPaid`. Only the take of a
// WRAP_ETH that pays elsewhere has a recipient (see wrapEthRecipient
// and userPaid). Only the take of a WRAP_ETH has a `wrapStep`.
const spentFromSender = [];
let total = null;
// The takes from the sender that `total` adds up, each a quantity, in
@@ -2318,6 +2337,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
token: inputToken,
amount: inputAmount,
recipient: wrapEthRecipient,
wrapStep: wrapEthStep,
});
}
if (hasFirstTake && !present(inputAmount)) {
@@ -2330,6 +2350,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
token: inputToken,
amount: inputAmount,
recipient: wrapEthRecipient,
wrapStep: wrapEthStep,
});
}
if (permitStands) inputToken = permit.token;
@@ -2341,7 +2362,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
typeof total === "bigint" &&
typeof t.amount === "bigint" &&
!t.atMost &&
(!present(t.recipient) || isRouter(t.recipient))
!present(t.recipient)
) {
total += t.amount;
inTotal.push(t);
@@ -2522,8 +2543,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
// of the lines below for a PERMIT2_TRANSFER_FROM. An amount this
// decoder does not know is said so, and a step it did not read gets
// that one line alone. A take with no recipient goes to the swap step
// that spends it. A take with a recipient goes to that recipient:
// the router, the sender as `MSG_SENDER`, or an address.
// that spends it. The take of a WRAP_ETH into the router goes to the
// router, unless a later swap step spends its WETH. A take with a
// recipient goes to that recipient; `MSG_SENDER` means the sender.
for (const s of spentFromSender) {
details.push({
label: "Moved from the sender",
@@ -2545,10 +2567,12 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
if (!present(s.recipient)) {
details.push({
label: "Moved to",
value: "The swap step that spends it",
value:
present(s.wrapStep) &&
s.wrapStep > lastRouterWethSwapStep
? "The router"
: "The swap step that spends it",
});
} else if (isRouter(s.recipient)) {
details.push({ label: "Moved to", value: "The router" });
} else if (s.recipient.toLowerCase() === MSG_SENDER) {
details.push({ label: "Moved to", value: "The sender" });
} else {
-61
View File
@@ -19,17 +19,12 @@
// its ETH line reads the same, and Send states it as unknown
// (https://git.eeqj.de/sneak/AutistMask/issues/736).
//
// A refresh that returns while Send is up draws Send's balance line again, and
// leaves what the user entered as it is
// (https://git.eeqj.de/sneak/AutistMask/issues/748).
//
// Driven through the real popup over a stored profile on Ethereum Mainnet,
// with "Show tracked tokens with zero balance" on, as it is by default.
const { HDNodeWallet, Mnemonic } = require("ethers");
const {
ADDRESS,
bootPopup,
cleanupPopup,
unversionedValidProfile,
@@ -436,62 +431,6 @@ test("Send for the new token reads its balance as unknown before any refresh has
await finish([MILLION]);
});
test("Send for the new token reads the fetched balance once the refresh returns, without being reopened", async () => {
const page = await bootOnAddress();
const finish = holdNextRefresh(page);
await addNewToken(page);
await openToken(page, NEW_TOKEN.toLowerCase());
await page.click("btn-address-token-send");
expect(page.text("send-balance")).toBe("Current balance: unknown (NEW)");
await finish([MILLION]);
expect(page.visibleViews()).toEqual(["send"]);
expect(page.text("send-balance")).toBe("Current balance: 1000000.0000 NEW");
});
test("a refresh that returns while Send is up draws an unread ETH balance again and leaves the inputs alone", async () => {
const page = await bootPopup(profileNotRead());
// The dropdown's first option, which a browser selects by itself.
page.node("send-token").value = "ETH";
await page.click("btn-main-send");
expect(page.text("send-balance")).toBe("Current balance: unknown (ETH)");
const recipient = "0x" + "dd".repeat(20);
page.node("send-to").value = recipient;
page.node("send-amount").value = "0.5";
// Stands for the options on the screen, which a refresh leaves as they are.
page.node("send-token").innerHTML = "options on the screen";
// Stands for the From address from before the refresh.
page.node("send-from").innerHTML = "From address before the refresh";
await refreshOnce(page, []);
expect(page.visibleViews()).toEqual(["send"]);
expect(page.text("send-balance")).toBe("Current balance: 2.0000 ETH");
expect(page.node("send-from").innerHTML).toContain(ADDRESS);
expect(page.value("send-to")).toBe(recipient);
expect(page.value("send-amount")).toBe("0.5");
expect(page.value("send-token")).toBe("ETH");
expect(page.node("send-token").innerHTML).toBe("options on the screen");
});
test("a refresh that returns while Send is up keeps the token picked in the dropdown and draws its new balance", async () => {
const page = await bootOnDerivingWallet();
const dropdown = page.node("send-token");
dropdown.value = "ETH";
await page.click("btn-main-send");
dropdown.value = NEW_TOKEN;
for (const fn of dropdown.listeners.change) await fn();
expect(page.text("send-balance")).toBe("Current balance: 1000000.0000 NEW");
page.node("send-amount").value = "0.5";
await refreshOnce(page, [{ ...MILLION, balance: "3000000.0" }]);
expect(page.text("send-balance")).toBe("Current balance: 3000000.0000 NEW");
expect(page.value("send-token")).toBe(NEW_TOKEN);
expect(page.value("send-amount")).toBe("0.5");
});
test("a token added by contract address on SettingsAddToken reads quantity unknown until the refresh returns", async () => {
const page = await bootPopup(unversionedValidProfile());
expect(page.visibleViews()).toEqual(["main"]);
+21 -43
View File
@@ -2475,10 +2475,8 @@ describe("uniswap decoder", () => {
// After an earlier step that may leave ETH in the router, the ETH that
// the user pays a V4 step reads as a maximum, on lines of its own.
// Every row reads at least the ETH that can leave the wallet. The row
// marked "reads as on next" reads the same on next. The row with a
// SETTLE_ALL of at most 3 ETH read that ETH as not known on next. On
// next every other row added that ETH to the Amount line, or read it as
// an exact amount.
// marked "reads as on next" reads the same on next. On next every other
// row added that ETH to the Amount line, or read it as an exact amount.
//
// ADDRESS_THIS is the router on every release.
const ADDRESS_THIS = "0x0000000000000000000000000000000000000002";
@@ -2917,10 +2915,10 @@ describe("uniswap decoder", () => {
expect(linesBetweenTokens(result)).toEqual(lines);
});
// https://git.eeqj.de/sneak/AutistMask/issues/747 — a WRAP_ETH into the
// router pays its WETH to the router, whichever step then takes it. On
// next that Moved to line read The swap step that spends it in every
// row.
// https://git.eeqj.de/sneak/AutistMask/issues/747 — the WETH of a
// WRAP_ETH into the router goes to a swap step only when a later swap
// step paid from the router spends it. On next every row not marked
// "reads as on next" read The swap step that spends it.
const SWAP_STEP = "The swap step that spends it";
// Each pays 1 WETH from the router, for USDC, to the user.
const V3_WETH_FROM_ROUTER = coder.encode(
@@ -3005,30 +3003,24 @@ describe("uniswap decoder", () => {
],
lines: [
["Amount", "600.0000 USDT"],
...wrapLines("The router"),
...wrapLines(SWAP_STEP),
...wrapLines("The router"),
],
},
{
kind: "a V3 exact-in of 600 USDT, a WRAP_ETH of 1 ETH into the router, then a V3 exact-in paid from the router's WETH",
kind: "a V3 exact-in of 600 USDT, a WRAP_ETH of 1 ETH into the router, then a V3 exact-in paid from the router's WETH, reads as on next",
commands: [0x00, 0x0b, 0x00],
inputs: [V3_600, WRAP_INTO_ROUTER, V3_WETH_FROM_ROUTER],
lines: [
["Amount", "600.0000 USDT"],
...wrapLines("The router"),
],
lines: [["Amount", "600.0000 USDT"], ...wrapLines(SWAP_STEP)],
},
{
kind: "the same with a V2 exact-in paid from the router's WETH",
kind: "the same with a V2 exact-in paid from the router's WETH, reads as on next",
commands: [0x00, 0x0b, 0x08],
inputs: [V3_600, WRAP_INTO_ROUTER, V2_WETH_FROM_ROUTER],
lines: [
["Amount", "600.0000 USDT"],
...wrapLines("The router"),
],
lines: [["Amount", "600.0000 USDT"], ...wrapLines(SWAP_STEP)],
},
{
kind: "the same with a V3 exact-out paid from the router's WETH",
kind: "the same with a V3 exact-out paid from the router's WETH, reads as on next",
commands: [0x00, 0x0b, 0x01],
inputs: [
V3_600,
@@ -3045,17 +3037,14 @@ describe("uniswap decoder", () => {
["Amount", "Up to 600.0000 USDT"],
["Moved from the sender", "Up to 1.0000 ETH"],
["Token moved", "ETH"],
["Moved to", "The router"],
["Moved to", SWAP_STEP],
],
},
{
kind: "the same with a V4 swap that the router's WETH settles",
kind: "the same with a V4 swap that the router's WETH settles, reads as on next",
commands: [0x00, 0x0b, 0x10],
inputs: [V3_600, WRAP_INTO_ROUTER, V4_WETH_FROM_ROUTER],
lines: [
["Amount", "600.0000 USDT"],
...wrapLines("The router"),
],
lines: [["Amount", "600.0000 USDT"], ...wrapLines(SWAP_STEP)],
},
{
kind: "a WRAP_ETH of 1 ETH into the router that sets the input side, a SWEEP of that WETH to another address, then an EXECUTE_SUB_PLAN",
@@ -3067,11 +3056,11 @@ describe("uniswap decoder", () => {
],
},
{
kind: "a WRAP_ETH of 1 ETH into the router that sets the input side, a V3 exact-in paid from the router's WETH, then an EXECUTE_SUB_PLAN",
kind: "a WRAP_ETH of 1 ETH into the router that sets the input side, a V3 exact-in paid from the router's WETH, then an EXECUTE_SUB_PLAN, reads as on next",
commands: [0x0b, 0x00, 0x21],
inputs: [WRAP_INTO_ROUTER, V3_WETH_FROM_ROUTER, SUB_PLAN],
lines: [
...wrapLines("The router"),
...wrapLines(SWAP_STEP),
["Moved from the sender", UNDECODED],
],
},
@@ -3089,7 +3078,7 @@ describe("uniswap decoder", () => {
],
},
{
kind: "the same with a V3 exact-in paid from the router's WETH after the WRAP_ETH",
kind: "the same with a V3 exact-in paid from the router's WETH after the WRAP_ETH, reads as on next",
commands: [0x0a, 0x0b, 0x00, 0x00],
inputs: [
encodePermit2(USDT_ADDR, 5000000000n, ROUTER_ADDR),
@@ -3097,10 +3086,7 @@ describe("uniswap decoder", () => {
V3_WETH_FROM_ROUTER,
V3_600,
],
lines: [
["Amount", "5000.0000 USDT"],
...wrapLines("The router"),
],
lines: [["Amount", "5000.0000 USDT"], ...wrapLines(SWAP_STEP)],
},
])("$kind", ({ commands, inputs, lines }) => {
const result = decodeSteps(commands, inputs);
@@ -3110,10 +3096,8 @@ describe("uniswap decoder", () => {
// https://git.eeqj.de/sneak/AutistMask/issues/747 — a later WRAP_ETH
// of CONTRACT_BALANCE wraps all the ETH the router holds. Unless an
// earlier step paid ETH to the router, that is the ETH the transaction
// carries. On next no line gave it. Into the router it gets no lines.
// It gets none after an UNWRAP_WETH into the router, or while the
// router holds ETH from a V4 step: Uniswap's own routes pay back what
// their swap did not spend this way, and carry no ETH. In the rows
// carries. On next no line gave it. Uniswap's own routes pay back what
// their swap did not spend this way, and carry no ETH, so in the rows
// marked "reads as on next" that WRAP_ETH gets no lines.
const WRAP_ALL_TO_OTHER = encodeWrapEth(OTHER_ADDR, CONTRACT_BALANCE);
const WRAP_ALL_TO_SENDER = encodeWrapEth(MSG_SENDER, CONTRACT_BALANCE);
@@ -3197,12 +3181,6 @@ describe("uniswap decoder", () => {
inputs: [WRAP_INTO_ROUTER, WRAP_ALL_TO_OTHER],
lines: [["Amount", "1.0000 ETH"], ...allEthLines(OTHER_ADDR)],
},
{
kind: "a V3 exact-in of 600 USDT, then a WRAP_ETH of CONTRACT_BALANCE into the router, reads as on next",
commands: [0x00, 0x0b],
inputs: [V3_600, encodeWrapEth(ADDRESS_THIS, CONTRACT_BALANCE)],
lines: [["Amount", "600.0000 USDT"]],
},
{
kind: "a V3 exact-in of 600 USDT, an UNWRAP_WETH to the sender, then a WRAP_ETH of CONTRACT_BALANCE to another address",
commands: [0x00, 0x0c, 0x0b],