fix: a swap's unknown first take shows beside a PERMIT2_TRANSFER_FROM, only when the user pays it (closes #634)
decode() now records whether the user pays the V4 step that sets the input side any of its input token, and decides once, after the loop, whether that step counts as a take from the sender. A stated amount counts whoever pays it; with none stated, the step counts only when the user pays it. Paid from the router, it gets no Moved from the sender lines, with a PERMIT2_PERMIT of another token or without. When it counts and states no amount, its Unknown (could not be decoded) line also shows beside a PERMIT2_TRANSFER_FROM of its token, whose figure is on that move's own lines or, into the router, on its Paid to line. README Transaction Decoding says the same. Model: opus-5-5
This commit was merged in pull request #639.
This commit is contained in:
@@ -2318,6 +2318,24 @@ describe("uniswap decoder", () => {
|
||||
encodeV4TakeAll(WETH_ADDR, 0n),
|
||||
],
|
||||
);
|
||||
// The same step with its SETTLE of OPEN_DELTA paid from the router.
|
||||
const V4_EXACT_OUT_600_FROM_ROUTER = encodeV4Swap(
|
||||
new Uint8Array([
|
||||
V4_SWAP_EXACT_OUT_SINGLE_ID,
|
||||
V4_SETTLE_ID,
|
||||
V4_TAKE_ALL_ID,
|
||||
]),
|
||||
[
|
||||
encodeV4ExactOutSingle(
|
||||
USDT_ADDR,
|
||||
WETH_ADDR,
|
||||
ONE_WETH,
|
||||
USDT_600,
|
||||
),
|
||||
settle(USDT_ADDR, 0n, false),
|
||||
encodeV4TakeAll(WETH_ADDR, 0n),
|
||||
],
|
||||
);
|
||||
test.each([
|
||||
{
|
||||
kind: "a V4 exact-out step with a SETTLE of OPEN_DELTA by the user",
|
||||
@@ -2325,6 +2343,13 @@ describe("uniswap decoder", () => {
|
||||
first: V4_EXACT_OUT_600,
|
||||
lines: [...UNKNOWN_USDT, ...UP_TO_400_USDT],
|
||||
},
|
||||
// Paid from the router, the step takes nothing from the wallet.
|
||||
{
|
||||
kind: "a V4 exact-out step with a SETTLE of OPEN_DELTA from the router",
|
||||
commands: [0x10, 0x09],
|
||||
first: V4_EXACT_OUT_600_FROM_ROUTER,
|
||||
lines: UP_TO_400_USDT,
|
||||
},
|
||||
// Settled by the user, the step's amount would be what that
|
||||
// SETTLE pays, which the decoder cannot state here.
|
||||
{
|
||||
@@ -2370,6 +2395,36 @@ describe("uniswap decoder", () => {
|
||||
},
|
||||
);
|
||||
|
||||
// https://git.eeqj.de/sneak/AutistMask/issues/634 — after a
|
||||
// PERMIT2_PERMIT of another token, the V4 exact-out step paid from
|
||||
// the router still takes nothing from the wallet, so it gets no lines
|
||||
// of its own. On next it got lines saying its amount is not known.
|
||||
const ONE_DAI_PERMIT = encodePermit2(DAI_ADDR, 10n ** 18n, ROUTER_ADDR);
|
||||
test.each([
|
||||
{
|
||||
kind: "a PERMIT2_PERMIT of 1 DAI, then a V4 exact-out step of at most 600 USDT paid from the router",
|
||||
commands: [0x0a, 0x10],
|
||||
inputs: [ONE_DAI_PERMIT, V4_EXACT_OUT_600_FROM_ROUTER],
|
||||
lines: [["Amount", "1.0000 DAI"]],
|
||||
},
|
||||
{
|
||||
kind: "a PERMIT2_PERMIT of 1 DAI, a V4 exact-out step of at most 600 USDT paid from the router, then a V2 exact-out of at most 400 USDT the user pays for",
|
||||
commands: [0x0a, 0x10, 0x09],
|
||||
inputs: [
|
||||
ONE_DAI_PERMIT,
|
||||
V4_EXACT_OUT_600_FROM_ROUTER,
|
||||
V2_EXACT_OUT_400,
|
||||
],
|
||||
lines: [["Amount", "Up to 1.0000 DAI"], ...UP_TO_400_USDT],
|
||||
},
|
||||
])(
|
||||
"$kind, gives the V4 step no lines of its own",
|
||||
({ commands, inputs, lines }) => {
|
||||
const result = decodeSteps(commands, inputs);
|
||||
expect(linesBetweenTokens(result)).toEqual(lines);
|
||||
},
|
||||
);
|
||||
|
||||
// The V4 exact-out route above the other way round, where the later
|
||||
// take's unknown amount leaves no figure at all, and other ways the
|
||||
// step that sets the input side states no amount.
|
||||
@@ -2423,6 +2478,98 @@ describe("uniswap decoder", () => {
|
||||
},
|
||||
);
|
||||
|
||||
// https://git.eeqj.de/sneak/AutistMask/issues/634 — a
|
||||
// PERMIT2_TRANSFER_FROM of the token the V4 exact-out step spends,
|
||||
// before or after it, shows its own figure, so the step's amount is
|
||||
// shown as not known beside it, and the 400 USDT cannot read as all
|
||||
// that leaves. On next only the 400 USDT was shown. Into the router,
|
||||
// the 400 USDT is on its Paid to line.
|
||||
const transfer = (token, recipient, amount) =>
|
||||
coder.encode(
|
||||
["address", "address", "uint160"],
|
||||
[token, recipient, amount],
|
||||
);
|
||||
const MOVED_400_USDT = [
|
||||
["Moved from the sender", "400.0000 USDT"],
|
||||
["Token moved", "USDT (" + USDT_ADDR + ")"],
|
||||
["Moved to", OTHER_ADDR],
|
||||
];
|
||||
test.each([
|
||||
{
|
||||
kind: "a V4 exact-out step of at most 600 USDT, then a PERMIT2_TRANSFER_FROM of 400 USDT to another address",
|
||||
commands: [0x10, 0x02],
|
||||
inputs: [
|
||||
V4_EXACT_OUT_600,
|
||||
transfer(USDT_ADDR, OTHER_ADDR, USDT_400),
|
||||
],
|
||||
lines: [...UNKNOWN_USDT, ...MOVED_400_USDT],
|
||||
},
|
||||
{
|
||||
kind: "a PERMIT2_TRANSFER_FROM of 400 USDT to another address, then a V4 exact-out step of at most 600 USDT",
|
||||
commands: [0x02, 0x10],
|
||||
inputs: [
|
||||
transfer(USDT_ADDR, OTHER_ADDR, USDT_400),
|
||||
V4_EXACT_OUT_600,
|
||||
],
|
||||
lines: [...UNKNOWN_USDT, ...MOVED_400_USDT],
|
||||
},
|
||||
{
|
||||
kind: "a V4 exact-out step of at most 600 USDT, then a PERMIT2_TRANSFER_FROM of 400 USDT into the router",
|
||||
commands: [0x10, 0x02],
|
||||
inputs: [
|
||||
V4_EXACT_OUT_600,
|
||||
transfer(USDT_ADDR, ROUTER_ADDR, USDT_400),
|
||||
],
|
||||
lines: UNKNOWN_USDT,
|
||||
},
|
||||
{
|
||||
kind: "a PERMIT2_TRANSFER_FROM of 400 USDT into the router, then a V4 exact-out step of at most 600 USDT",
|
||||
commands: [0x02, 0x10],
|
||||
inputs: [
|
||||
transfer(USDT_ADDR, ROUTER_ADDR, USDT_400),
|
||||
V4_EXACT_OUT_600,
|
||||
],
|
||||
lines: UNKNOWN_USDT,
|
||||
},
|
||||
])(
|
||||
"$kind, paid by the user, shows that the step's amount is not known",
|
||||
({ commands, inputs, lines }) => {
|
||||
const result = decodeSteps(commands, inputs);
|
||||
expect(linesBetweenTokens(result)).toEqual(lines);
|
||||
},
|
||||
);
|
||||
|
||||
// Paid from the router, the V4 exact-out step takes nothing from the
|
||||
// wallet, so the moves give all that leaves.
|
||||
test("a PERMIT2_TRANSFER_FROM of 400 USDT into the router, one of 10 USDT to another address, then a V4 exact-out step paid from the router, shows only the moves", () => {
|
||||
const result = decodeSteps(
|
||||
[0x02, 0x02, 0x10],
|
||||
[
|
||||
transfer(USDT_ADDR, ROUTER_ADDR, USDT_400),
|
||||
transfer(USDT_ADDR, OTHER_ADDR, 10000000n),
|
||||
V4_EXACT_OUT_600_FROM_ROUTER,
|
||||
],
|
||||
);
|
||||
expect(linesBetweenTokens(result)).toEqual([
|
||||
["Moved from the sender", "10.0000 USDT"],
|
||||
["Token moved", "USDT (" + USDT_ADDR + ")"],
|
||||
["Moved to", OTHER_ADDR],
|
||||
]);
|
||||
});
|
||||
|
||||
// A move of another token says nothing of how much USDT leaves.
|
||||
test("a V4 exact-out step of at most 600 USDT, then a PERMIT2_TRANSFER_FROM of 400 DAI, shows only the DAI moved", () => {
|
||||
const result = decodeSteps(
|
||||
[0x10, 0x02],
|
||||
[V4_EXACT_OUT_600, transfer(DAI_ADDR, OTHER_ADDR, DAI_400)],
|
||||
);
|
||||
expect(linesBetweenTokens(result)).toEqual([
|
||||
["Moved from the sender", "400.0000 DAI"],
|
||||
["Token moved", "DAI (" + DAI_ADDR + ")"],
|
||||
["Moved to", OTHER_ADDR],
|
||||
]);
|
||||
});
|
||||
|
||||
// A V2 exact-out step makes every amount the swap steps spend a maximum,
|
||||
// the total included.
|
||||
test("a split with a V2 exact-out step the user pays for shows the total as a maximum", () => {
|
||||
|
||||
Reference in New Issue
Block a user