Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1146ffd12b |
No files matched your search
@@ -1260,26 +1260,25 @@ runs (Transaction Decoding below gives what the router holds). The router
|
|||||||
unwraps only the WETH it holds, so WETH that step paid to the user, or to anyone
|
unwraps only the WETH it holds, so WETH that step paid to the user, or to anyone
|
||||||
else, stays WETH: a swap that pays the router WETH, then a swap that pays the
|
else, stays WETH: a swap that pays the router WETH, then a swap that pays the
|
||||||
user WETH, then an unwrap, shows WETH with the second swap's minimum. The
|
user WETH, then an unwrap, shows WETH with the second swap's minimum. The
|
||||||
minimum reads in ETH when that step paid all its WETH to the router and no step
|
minimum reads in ETH only when that step paid all its WETH to the router and
|
||||||
between it and the `UNWRAP_WETH` is a `TRANSFER`, `PAY_PORTION` or
|
every step between it and the `UNWRAP_WETH` is one the decoder read that cannot
|
||||||
`PAY_PORTION_FULL_PRECISION` of WETH to anyone but the router, or a `WRAP_ETH`
|
pay out WETH the router holds, add to it, or wrap the router's ETH: a
|
||||||
into the router. Such a step may pay out WETH the router holds, or put other
|
`PERMIT2_PERMIT`, a `BALANCE_CHECK_ERC20`, or a `SWEEP`, `TRANSFER`,
|
||||||
WETH in place of WETH an earlier step paid out. With such a step, or when that
|
`PAY_PORTION` or `PAY_PORTION_FULL_PRECISION` of a token other than WETH.
|
||||||
step paid only part of its WETH to the router, as a V4 step does that `TAKE`s
|
Otherwise the swap shows ETH with no minimum, as it does after a V4 step that
|
||||||
some of its WETH to the user and the rest to the router, or paid the router WETH
|
`TAKE`s some of its WETH to the user and the rest to the router, or that pays
|
||||||
beside a payout the decoder cannot read, the swap shows ETH with no minimum: the
|
the router WETH beside a payout the decoder cannot read, and with a WETH
|
||||||
decoder does not count how much of that step's minimum reaches the
|
interface fee paid by `PAY_PORTION` before the `UNWRAP_WETH`: the decoder does
|
||||||
`UNWRAP_WETH`, nor read the `UNWRAP_WETH`'s own minimum. After an `UNWRAP_WETH`
|
not count how much of that step's minimum reaches the `UNWRAP_WETH`, nor read
|
||||||
that pays the output side's WETH to the router as ETH, a `WRAP_ETH` of
|
the `UNWRAP_WETH`'s own minimum. When that `UNWRAP_WETH` pays the router, which
|
||||||
`CONTRACT_BALANCE` to anyone but the router, while the router still holds ETH,
|
then holds the ETH, any later step but those listed may wrap the ETH and pay it
|
||||||
pays the output out as WETH again: the swap shows WETH with no minimum, as the
|
out as WETH: after one, the swap shows WETH with no minimum. A later step that
|
||||||
decoder does not count what steps between the two pay out of that ETH. A later
|
sets the output side sets it anew. Otherwise `Token Out` and `Min. received`
|
||||||
step that sets the output side sets it anew. Otherwise `Token Out` and
|
keep the output side's own token and figure, whether the swap was paid in ETH or
|
||||||
`Min. received` keep the output side's own token and figure, whether the swap
|
in a token: a V2 exact-out swap that buys USDC and then unwraps the WETH it did
|
||||||
was paid in ETH or in a token: a V2 exact-out swap that buys USDC and then
|
not spend shows USDC. The token permission warning on the signature screen has
|
||||||
unwraps the WETH it did not spend shows USDC. The token permission warning on
|
its own amount wording, including `Unknown`; the SignApproval section below
|
||||||
the signature screen has its own amount wording, including `Unknown`; the
|
describes it.
|
||||||
SignApproval section below describes it.
|
|
||||||
|
|
||||||
The swap's `Deadline` line is the router's deadline as a UTC date and time, e.g.
|
The swap's `Deadline` line is the router's deadline as a UTC date and time, e.g.
|
||||||
`2026-02-27 08:25:51`. A JavaScript date reaches only to 275760-09-13 00:00:00
|
`2026-02-27 08:25:51`. A JavaScript date reaches only to 275760-09-13 00:00:00
|
||||||
|
|||||||
+65
-53
@@ -1221,17 +1221,20 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
let hasUnwrapWeth = false;
|
let hasUnwrapWeth = false;
|
||||||
// Set when an UNWRAP_WETH pays out, as ETH, the output side's WETH,
|
// Set when an UNWRAP_WETH pays out, as ETH, the output side's WETH,
|
||||||
// which the router holds; cleared whenever the output side is set
|
// which the router holds; cleared whenever the output side is set
|
||||||
// again, or a WRAP_ETH pays it out as WETH again (see WRAP_ETH).
|
// again, or a later step may pay that ETH out as WETH (see after the
|
||||||
// `unwrappedIntoRouter` says whether that UNWRAP_WETH paid the ETH to
|
// loop). `unwrapIntoRouterStep` is the index of that UNWRAP_WETH when
|
||||||
// the router, which then holds it.
|
// it paid the ETH to the router, which then holds it, otherwise null.
|
||||||
let outputUnwrapped = false;
|
let outputUnwrapped = false;
|
||||||
let unwrappedIntoRouter = false;
|
let unwrapIntoRouterStep = null;
|
||||||
// Set when a step after the one that set the output side may pay out
|
// The index of the step that set the output side.
|
||||||
// WETH the router holds, or add to it: a TRANSFER, PAY_PORTION or
|
let outputStep = -1;
|
||||||
// PAY_PORTION_FULL_PRECISION of WETH to anyone but the router, or a
|
// The index of the last step that may change the router's WETH: pay
|
||||||
// WRAP_ETH into the router. Cleared whenever the output side is set
|
// out WETH the router holds, add to it, or wrap the router's ETH.
|
||||||
// again.
|
// Every step may, but one this decoder read that is a PERMIT2_PERMIT,
|
||||||
let routerWethChanged = false;
|
// a BALANCE_CHECK_ERC20, or a SWEEP, TRANSFER, PAY_PORTION or
|
||||||
|
// PAY_PORTION_FULL_PRECISION of a token other than WETH (see
|
||||||
|
// keepsRouterWeth in the loop).
|
||||||
|
let lastWethStep = -1;
|
||||||
// Where the step that set the output side paid its output: the
|
// 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
|
// 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,
|
// TAKE_PORTION of it in a V4 step, a TAKE_ALL as paid to the sender,
|
||||||
@@ -1274,14 +1277,14 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
if (!present(token) && !present(amount)) return;
|
if (!present(token) && !present(amount)) return;
|
||||||
setInput(token, amount);
|
setInput(token, amount);
|
||||||
};
|
};
|
||||||
const setOutput = (token, amount, recipients) => {
|
const setOutput = (step, token, amount, recipients) => {
|
||||||
if (!present(token) && !present(amount)) return;
|
if (!present(token) && !present(amount)) return;
|
||||||
outputToken = present(token) ? token : null;
|
outputToken = present(token) ? token : null;
|
||||||
minOutput = present(amount) ? amount : null;
|
minOutput = present(amount) ? amount : null;
|
||||||
outputEstablished = true;
|
outputEstablished = true;
|
||||||
outputUnwrapped = false;
|
outputUnwrapped = false;
|
||||||
outputRecipients = recipients;
|
outputRecipients = recipients;
|
||||||
routerWethChanged = false;
|
outputStep = step;
|
||||||
};
|
};
|
||||||
|
|
||||||
// What the steps take from the sender beyond the amount of the step
|
// What the steps take from the sender beyond the amount of the step
|
||||||
@@ -1431,6 +1434,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
? COMMAND_NAMES[command]
|
? COMMAND_NAMES[command]
|
||||||
: "Command 0x" + cmdId.toString(16).padStart(2, "0"),
|
: "Command 0x" + cmdId.toString(16).padStart(2, "0"),
|
||||||
);
|
);
|
||||||
|
// Set by a branch below that reads a step that cannot change the
|
||||||
|
// router's WETH (see lastWethStep).
|
||||||
|
let keepsRouterWeth = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (command === "EXECUTE_SUB_PLAN") {
|
if (command === "EXECUTE_SUB_PLAN") {
|
||||||
@@ -1462,6 +1468,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
allowedToFail,
|
allowedToFail,
|
||||||
beforeEveryTake: !tookBefore,
|
beforeEveryTake: !tookBefore,
|
||||||
};
|
};
|
||||||
|
keepsRouterWeth = true;
|
||||||
} else {
|
} else {
|
||||||
stepsUnread.push(i);
|
stepsUnread.push(i);
|
||||||
}
|
}
|
||||||
@@ -1517,6 +1524,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// what the user receives: it sets no side, and the
|
// what the user receives: it sets no side, and the
|
||||||
// step says so.
|
// step says so.
|
||||||
commandNames[i] += " (of another address)";
|
commandNames[i] += " (of another address)";
|
||||||
|
keepsRouterWeth = true;
|
||||||
} else {
|
} else {
|
||||||
// The router passes this check whenever the owner
|
// The router passes this check whenever the owner
|
||||||
// holds at least minBalance, so a zero one guarantees
|
// holds at least minBalance, so a zero one guarantees
|
||||||
@@ -1527,8 +1535,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// does. It pays nobody.
|
// does. It pays nobody.
|
||||||
const minBalance = allowedToFail ? 0n : b.minBalance;
|
const minBalance = allowedToFail ? 0n : b.minBalance;
|
||||||
if (!(minBalance === 0n && present(minOutput))) {
|
if (!(minBalance === 0n && present(minOutput))) {
|
||||||
setOutput(b.token, minBalance, []);
|
setOutput(i, b.token, minBalance, []);
|
||||||
}
|
}
|
||||||
|
keepsRouterWeth = true;
|
||||||
}
|
}
|
||||||
} else if (command === "V3_SWAP_EXACT_IN") {
|
} else if (command === "V3_SWAP_EXACT_IN") {
|
||||||
const s = decodeV3SwapExactIn(inputs[i]);
|
const s = decodeV3SwapExactIn(inputs[i]);
|
||||||
@@ -1541,7 +1550,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// Always update output: in multi-step swaps (V3 → V4),
|
// Always update output: in multi-step swaps (V3 → V4),
|
||||||
// the last swap step determines the final output token
|
// the last swap step determines the final output token
|
||||||
// and minimum received amount.
|
// and minimum received amount.
|
||||||
setOutput(s.tokenOut, s.amountOutMin, [s.recipient]);
|
setOutput(i, s.tokenOut, s.amountOutMin, [s.recipient]);
|
||||||
if (!s.payerIsUser) {
|
if (!s.payerIsUser) {
|
||||||
spendFromRouter(s.tokenIn, s.amountIn);
|
spendFromRouter(s.tokenIn, s.amountIn);
|
||||||
}
|
}
|
||||||
@@ -1561,7 +1570,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else if (s.payerIsUser) {
|
} else if (s.payerIsUser) {
|
||||||
addUserPaid(s.tokenIn, s.amountIn);
|
addUserPaid(s.tokenIn, s.amountIn);
|
||||||
}
|
}
|
||||||
setOutput(s.tokenOut, s.amountOutMin, [s.recipient]);
|
setOutput(i, s.tokenOut, s.amountOutMin, [s.recipient]);
|
||||||
if (!s.payerIsUser) {
|
if (!s.payerIsUser) {
|
||||||
spendFromRouter(s.tokenIn, s.amountIn);
|
spendFromRouter(s.tokenIn, s.amountIn);
|
||||||
}
|
}
|
||||||
@@ -1592,7 +1601,7 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
} else if (s.payerIsUser) {
|
} else if (s.payerIsUser) {
|
||||||
addUserPaid(s.tokenIn, s.amountInMax);
|
addUserPaid(s.tokenIn, s.amountInMax);
|
||||||
}
|
}
|
||||||
setOutput(s.tokenOut, s.amountOut, [s.recipient]);
|
setOutput(i, s.tokenOut, s.amountOut, [s.recipient]);
|
||||||
if (!isHop(i, s.tokenOut, s.recipient)) {
|
if (!isHop(i, s.tokenOut, s.recipient)) {
|
||||||
addPayee(s.recipient, s.tokenOut);
|
addPayee(s.recipient, s.tokenOut);
|
||||||
}
|
}
|
||||||
@@ -1614,6 +1623,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
if (!isRouter(s.recipient)) {
|
if (!isRouter(s.recipient)) {
|
||||||
heldByRouter.delete(s.token.toLowerCase());
|
heldByRouter.delete(s.token.toLowerCase());
|
||||||
}
|
}
|
||||||
|
keepsRouterWeth = !WETH_ADDRESSES.includes(
|
||||||
|
s.token.toLowerCase(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
stepsUnread.push(i);
|
stepsUnread.push(i);
|
||||||
@@ -1651,12 +1663,9 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
) {
|
) {
|
||||||
heldByRouter.delete(t.token.toLowerCase());
|
heldByRouter.delete(t.token.toLowerCase());
|
||||||
}
|
}
|
||||||
if (
|
keepsRouterWeth = !WETH_ADDRESSES.includes(
|
||||||
WETH_ADDRESSES.includes(t.token.toLowerCase()) &&
|
t.token.toLowerCase(),
|
||||||
!isRouter(t.recipient)
|
);
|
||||||
) {
|
|
||||||
routerWethChanged = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
payeeUnread = true;
|
payeeUnread = true;
|
||||||
stepsUnread.push(i);
|
stepsUnread.push(i);
|
||||||
@@ -1693,24 +1702,6 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
w.amount,
|
w.amount,
|
||||||
);
|
);
|
||||||
addPayee(w.recipient);
|
addPayee(w.recipient);
|
||||||
// After an UNWRAP_WETH paid the output side's WETH to
|
|
||||||
// the router as ETH, a WRAP_ETH of all the router's
|
|
||||||
// ETH to anyone else pays it out as WETH again. This
|
|
||||||
// decoder does not count what steps between the two
|
|
||||||
// paid out of that ETH, so the output side keeps no
|
|
||||||
// figure.
|
|
||||||
if (
|
|
||||||
outputUnwrapped &&
|
|
||||||
unwrappedIntoRouter &&
|
|
||||||
w.amount === CONTRACT_BALANCE &&
|
|
||||||
!isRouter(w.recipient) &&
|
|
||||||
heldByRouter.has(
|
|
||||||
"0x0000000000000000000000000000000000000000",
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
outputUnwrapped = false;
|
|
||||||
minOutput = null;
|
|
||||||
}
|
|
||||||
// CONTRACT_BALANCE wraps all the router's ETH.
|
// CONTRACT_BALANCE wraps all the router's ETH.
|
||||||
if (w.amount === CONTRACT_BALANCE) {
|
if (w.amount === CONTRACT_BALANCE) {
|
||||||
heldByRouter.delete(
|
heldByRouter.delete(
|
||||||
@@ -1721,7 +1712,6 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// amount is known when the step states it and the
|
// amount is known when the step states it and the
|
||||||
// router held no WETH before.
|
// router held no WETH before.
|
||||||
if (isRouter(w.recipient) && w.amount > 0n) {
|
if (isRouter(w.recipient) && w.amount > 0n) {
|
||||||
routerWethChanged = true;
|
|
||||||
const weth = WETH.get(chainId);
|
const weth = WETH.get(chainId);
|
||||||
heldByRouter.set(
|
heldByRouter.set(
|
||||||
weth,
|
weth,
|
||||||
@@ -1801,14 +1791,19 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
outputEstablished = true;
|
outputEstablished = true;
|
||||||
outputUnwrapped = false;
|
outputUnwrapped = false;
|
||||||
outputRecipients = takeRecipients;
|
outputRecipients = takeRecipients;
|
||||||
routerWethChanged = false;
|
outputStep = i;
|
||||||
} else if (v4) {
|
} else if (v4) {
|
||||||
// A step with no swap action sets only the sides its
|
// A step with no swap action sets only the sides its
|
||||||
// SETTLE, SETTLE_ALL, TAKE or TAKE_ALL names:
|
// SETTLE, SETTLE_ALL, TAKE or TAKE_ALL names:
|
||||||
// setInputOnce() and setOutput() leave alone a side
|
// setInputOnce() and setOutput() leave alone a side
|
||||||
// whose two halves are both unstated.
|
// whose two halves are both unstated.
|
||||||
setInputOnce(v4.tokenIn, amountIn);
|
setInputOnce(v4.tokenIn, amountIn);
|
||||||
setOutput(v4.tokenOut, v4.amountOutMin, takeRecipients);
|
setOutput(
|
||||||
|
i,
|
||||||
|
v4.tokenOut,
|
||||||
|
v4.amountOutMin,
|
||||||
|
takeRecipients,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Every V4 step, with a swap action or without, takes
|
// Every V4 step, with a swap action or without, takes
|
||||||
// from the sender what the user pays it (an amount an
|
// from the sender what the user pays it (an amount an
|
||||||
@@ -1837,13 +1832,13 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
// that step paid to the user, or to anyone else, stays
|
// that step paid to the user, or to anyone else, stays
|
||||||
// WETH. When that step also paid some of its output
|
// WETH. When that step also paid some of its output
|
||||||
// elsewhere or made a payout this decoder did not read,
|
// elsewhere or made a payout this decoder did not read,
|
||||||
// or a later step may have paid out WETH the router holds
|
// or a step between the two may have changed the
|
||||||
// or added to it (see routerWethChanged), the WETH
|
// router's WETH (see lastWethStep), the WETH unwrapped
|
||||||
// unwrapped may be only part of the output side's, or
|
// may be only part of the output side's, or none of it.
|
||||||
// none of it. This decoder does not count how much of
|
// This decoder does not count how much of that step's
|
||||||
// that step's minimum then reaches the UNWRAP_WETH, nor
|
// minimum then reaches the UNWRAP_WETH, nor read the
|
||||||
// read the UNWRAP_WETH's own minimum, so the output side
|
// UNWRAP_WETH's own minimum, so the output side keeps no
|
||||||
// keeps no figure.
|
// figure.
|
||||||
const u = decodeUnwrapWeth(inputs[i]);
|
const u = decodeUnwrapWeth(inputs[i]);
|
||||||
const toRouter = (r) => present(r) && isRouter(r);
|
const toRouter = (r) => present(r) && isRouter(r);
|
||||||
if (
|
if (
|
||||||
@@ -1853,11 +1848,11 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
outputRecipients.some(toRouter)
|
outputRecipients.some(toRouter)
|
||||||
) {
|
) {
|
||||||
outputUnwrapped = true;
|
outputUnwrapped = true;
|
||||||
unwrappedIntoRouter =
|
unwrapIntoRouterStep =
|
||||||
u !== null && isRouter(u.recipient);
|
u !== null && isRouter(u.recipient) ? i : null;
|
||||||
if (
|
if (
|
||||||
!outputRecipients.every(toRouter) ||
|
!outputRecipients.every(toRouter) ||
|
||||||
routerWethChanged
|
lastWethStep > outputStep
|
||||||
) {
|
) {
|
||||||
minOutput = null;
|
minOutput = null;
|
||||||
}
|
}
|
||||||
@@ -1898,6 +1893,23 @@ function decode(data, toAddress, chainId, sources, fromAddress) {
|
|||||||
if (allowedToFail) {
|
if (allowedToFail) {
|
||||||
commandNames[i] += " (allowed to fail)";
|
commandNames[i] += " (allowed to fail)";
|
||||||
}
|
}
|
||||||
|
if (!keepsRouterWeth) {
|
||||||
|
lastWethStep = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After an UNWRAP_WETH that paid the output side's WETH to the router
|
||||||
|
// as ETH, a later step that may change the router's WETH (see
|
||||||
|
// lastWethStep) may wrap that ETH and pay it out as WETH. The output
|
||||||
|
// side then reads as WETH with no figure, as this decoder does not
|
||||||
|
// count how much of that ETH each later step pays out.
|
||||||
|
if (
|
||||||
|
outputUnwrapped &&
|
||||||
|
unwrapIntoRouterStep !== null &&
|
||||||
|
lastWethStep > unwrapIntoRouterStep
|
||||||
|
) {
|
||||||
|
outputUnwrapped = false;
|
||||||
|
minOutput = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Amount line, and the lines for what the steps take beyond it.
|
// The Amount line, and the lines for what the steps take beyond it.
|
||||||
|
|||||||
@@ -747,11 +747,11 @@ describe("an UNWRAP_WETH makes the output ETH only when the router holds it", ()
|
|||||||
});
|
});
|
||||||
|
|
||||||
// https://git.eeqj.de/sneak/AutistMask/issues/642: a step between the swap and
|
// https://git.eeqj.de/sneak/AutistMask/issues/642: a step between the swap and
|
||||||
// the UNWRAP_WETH that pays out part of the router's WETH, or a WRAP_ETH into
|
// the UNWRAP_WETH may pay out the router's WETH or add to it, and a step after
|
||||||
// the router after the swap's WETH was paid out, may leave less of the swap's
|
// an UNWRAP_WETH into the router may wrap its ETH and pay it out as WETH. Each
|
||||||
// WETH to unwrap than its minimum; an UNWRAP_WETH into the router, then a
|
// read as ETH with the swap's minimum. A step the decoder read that is a
|
||||||
// WRAP_ETH of all its ETH to the user, pays the user WETH. Each read as ETH
|
// PERMIT2_PERMIT, a BALANCE_CHECK_ERC20, or a SWEEP, TRANSFER, PAY_PORTION or
|
||||||
// with the swap's minimum.
|
// PAY_PORTION_FULL_PRECISION of a token other than WETH does neither.
|
||||||
describe("what reaches the UNWRAP_WETH of the swap's WETH", () => {
|
describe("what reaches the UNWRAP_WETH of the swap's WETH", () => {
|
||||||
const NO_MINIMUM_TO_USER = PAID_TO_USER.filter(
|
const NO_MINIMUM_TO_USER = PAID_TO_USER.filter(
|
||||||
(l) => l !== "Min. received",
|
(l) => l !== "Min. received",
|
||||||
@@ -787,6 +787,61 @@ describe("what reaches the UNWRAP_WETH of the swap's WETH", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
{
|
||||||
|
kind: "a SWEEP whose recipient cannot be read",
|
||||||
|
step: withDirtyWord(sweep(WETH, OTHER), 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "a V4 SETTLE of 0.5 WETH from the router",
|
||||||
|
step: v4Swap([
|
||||||
|
0x0b,
|
||||||
|
coder.encode(
|
||||||
|
["address", "uint256", "bool"],
|
||||||
|
[WETH, ONE_WETH / 2n, false],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "a bridge deposit of 0.5 WETH",
|
||||||
|
step: acrossDeposit(USER, USER, WETH, ONE_WETH / 2n),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "a PERMIT2_TRANSFER_FROM of 0.5 WETH into the router",
|
||||||
|
step: permit2TransferFrom(WETH, ADDRESS_THIS, ONE_WETH / 2n),
|
||||||
|
},
|
||||||
|
])("$kind between them reads as ETH with no minimum", ({ step }) => {
|
||||||
|
const result = decode(
|
||||||
|
execute(swapToWeth(ADDRESS_THIS), step, unwrapWeth(MSG_SENDER)),
|
||||||
|
);
|
||||||
|
expect(result.name).toBe("Swap USDT → ETH");
|
||||||
|
expect(detail(result, "Min. received")).toBeUndefined();
|
||||||
|
expect(detail(result, "Min. paid out")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
{
|
||||||
|
kind: "a PERMIT2_PERMIT between them",
|
||||||
|
steps: [permit2Permit(USDT, USDT_2000), unwrapWeth(MSG_SENDER)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "a BALANCE_CHECK_ERC20 between them",
|
||||||
|
steps: [balanceCheck(USDC, 1n, OTHER), unwrapWeth(MSG_SENDER)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "a SWEEP of USDC between them",
|
||||||
|
steps: [sweep(USDC, MSG_SENDER), unwrapWeth(MSG_SENDER)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "an UNWRAP_WETH into the router, then a SWEEP of its ETH,",
|
||||||
|
steps: [unwrapWeth(ADDRESS_THIS), sweep(ZERO, MSG_SENDER)],
|
||||||
|
},
|
||||||
|
])("$kind reads as ETH with the swap's minimum", ({ steps }) => {
|
||||||
|
const result = decode(execute(swapToWeth(ADDRESS_THIS), ...steps));
|
||||||
|
expect(result.name).toBe("Swap USDT → ETH");
|
||||||
|
expect(detail(result, "Min. received").value).toBe("1.0000 ETH");
|
||||||
|
});
|
||||||
|
|
||||||
// The SWEEP pays the user the swap's WETH, so the UNWRAP_WETH unwraps the
|
// The SWEEP pays the user the swap's WETH, so the UNWRAP_WETH unwraps the
|
||||||
// WRAP_ETH's.
|
// WRAP_ETH's.
|
||||||
test("a SWEEP of the swap's WETH to the user, then a WRAP_ETH into the router, reads as ETH with no minimum", () => {
|
test("a SWEEP of the swap's WETH to the user, then a WRAP_ETH into the router, reads as ETH with no minimum", () => {
|
||||||
@@ -814,6 +869,20 @@ describe("what reaches the UNWRAP_WETH of the swap's WETH", () => {
|
|||||||
expect(labels(result)).toEqual(NO_MINIMUM_TO_USER);
|
expect(labels(result)).toEqual(NO_MINIMUM_TO_USER);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The user receives 0.5 WETH and the rest of the swap's output as ETH.
|
||||||
|
test("an UNWRAP_WETH into the router, then a WRAP_ETH of 0.5 ETH to the user, reads as WETH with no minimum", () => {
|
||||||
|
const result = decode(
|
||||||
|
execute(
|
||||||
|
swapToWeth(ADDRESS_THIS),
|
||||||
|
unwrapWeth(ADDRESS_THIS),
|
||||||
|
wrapEth(MSG_SENDER, ONE_WETH / 2n),
|
||||||
|
sweep(ZERO, MSG_SENDER),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(result.name).toBe("Swap USDT → WETH");
|
||||||
|
expect(labels(result)).toEqual(NO_MINIMUM_TO_USER);
|
||||||
|
});
|
||||||
|
|
||||||
// The UNWRAP_WETH pays the user the swap's WETH as ETH; the WRAP_ETH wraps
|
// The UNWRAP_WETH pays the user the swap's WETH as ETH; the WRAP_ETH wraps
|
||||||
// the ETH an earlier V4 step paid the router.
|
// the ETH an earlier V4 step paid the router.
|
||||||
test("an UNWRAP_WETH to the user, then a WRAP_ETH of all the router's ETH to the user, reads as ETH", () => {
|
test("an UNWRAP_WETH to the user, then a WRAP_ETH of all the router's ETH to the user, reads as ETH", () => {
|
||||||
|
|||||||
Reference in new issue
Block a user