1 Commits
Author SHA1 Message Date
clawbot f943ca45fa 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:47:26 +00:00
3 changed files with 55 additions and 5 deletions
+7 -3
View File
@@ -1235,14 +1235,18 @@ only when no earlier step set it. It finds each side by three rules:
is their sum; in any other case the step gives no `Amount`. The decoder reads
no amount from an exact-out action, and an open delta is not a stated amount.
Its `Min. received` is found the same way, from the `amountOutMin` of the
actions that buy the output currency. When the user pays the step any of the
input currency, its `Amount` is what the user pays instead (see Transaction
Decoding).
actions that buy the output currency.
3. If any swap action in the step could not be read, only `SETTLE`,
`SETTLE_ALL`, `TAKE` and `TAKE_ALL` name its sides, and the step gives no
`Amount` or `Min. received`. A side they did not name is unknown, and the
step sets it as unknown with no figure.
Under each of these rules, when the user pays the step any of the input
currency, its `Amount` is instead what the user pays it of that currency when
the decoder knows that amount, and otherwise there is none (see Transaction
Decoding). So a step whose USDT-for-USDC swap action could not be read, with a
`SETTLE` of 400 USDT paid by the user, reads `Amount 400.0000 USDT`.
A V4 step with no swap action sets only the sides its `SETTLE`, `SETTLE_ALL`,
`TAKE` or `TAKE_ALL` names.
+4 -2
View File
@@ -903,8 +903,10 @@ function totalAmount(amounts) {
// input side from it only when no earlier step set it. A side it sets that
// these rules leave unknown is set unknown, with no figure. A step with no
// swap action sets only the sides SETTLE, SETTLE_ALL, TAKE and TAKE_ALL
// name. When the user pays the step any of Token In (`paidByUser` below),
// decode() takes that, not the Amount of rule 2, as the step's amount.
// name. Under each of the three rules, rule 3 included, when the user pays
// the step any of Token In (`paidByUser` below), decode() takes what the user
// pays it of Token In as the step's amount in place of the rules' Amount, or
// no amount when this decoder cannot tell what that is.
//
// `takes` is every TAKE, TAKE_ALL and TAKE_PORTION, as the currency it takes
// and its recipient as written, MSG_SENDER for a TAKE_ALL. TAKE_PORTION takes
+44
View File
@@ -1645,6 +1645,50 @@ describe("uniswap decoder", () => {
]);
});
// A step whose swap action cannot be read states no amount of its
// own, but what the user pays it of its input currency is still its
// amount. On next it had no Amount line.
test("a V4 step whose swap action cannot be read, with a SETTLE of 400 USDT by the user, shows the 400 USDT", () => {
// USDT for USDC exact-in, in the newer shape the decoder cannot
// read (see V4_SWAP_ACTION_SHAPES).
const unreadSwap = coder.encode(
[`tuple(${POOL_KEY},bool,uint128,uint128,uint256,bytes)`],
[
[
[USDC_ADDR, USDT_ADDR, 100, 1, ZERO_ADDR],
false, // currency1 (USDT) for currency0 (USDC)
USDT_400,
390000000n,
0n,
"0x",
],
],
);
const result = decodeSteps(
[0x10],
[
encodeV4Swap(
new Uint8Array([
V4_SWAP_EXACT_IN_SINGLE_ID,
V4_SETTLE_ID,
V4_TAKE_ALL_ID,
]),
[
unreadSwap,
settle(USDT_ADDR, USDT_400, true),
encodeV4TakeAll(USDC_ADDR, 0n),
],
),
],
);
expect(detail(result, "Steps").value).toBe(
"V4 Swap (could not be decoded)",
);
expect(linesBetweenTokens(result)).toEqual([
["Amount", "400.0000 USDT"],
]);
});
// A step that may take from the sender what the decoder did not read
// leaves no Amount figure, which may be too low, and one line says so.
const UNREAD_SWAP = V4_SWAP_ACTION_SHAPES[0].newer;