1 Commits
Author SHA1 Message Date
sneak 628bc07c27 fix: a V4 SETTLE of ETH paid from the router reads as moved from the sender (closes #682)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
V4Router pays a SETTLE of ETH from the router's own ETH, whichever payer
it names. The router holds the transaction's value. So a SETTLE of ETH
that names the router as payer now counts as paid by the user.

When an earlier step left ETH in the router, such a SETTLE gets lines of
its own. Its line reads "Up to" and its amount, and that amount never
joins the Amount total. When the decoder does not know the SETTLE's
amount, as for CONTRACT_BALANCE, its line reads "Unknown (could not be
decoded)", also on the step that sets the input side.

One existing row in tests/uniswapRecipient.test.js now expects the ETH
lines. Its SETTLE spends all the router's ETH, the transaction's value
included.

Model: opus-5-5
2026-10-27 05:18:43 +00:00
+38 -38
View File
@@ -3522,44 +3522,44 @@ Currently supported:
none): one of a token other than `Token In`; one of `Token In` after an amount none): one of a token other than `Token In`; one of `Token In` after an amount
that is not a quantity (a V4 open delta, `ALREADY_PAID`, which keep their that is not a quantity (a V4 open delta, `ALREADY_PAID`, which keep their
wording on the `Amount` line, or no stated amount, as of a V4 exact-out step); wording on the `Amount` line, or no stated amount, as of a V4 exact-out step);
an ETH take whose line reads `Up to` (see above); and one whose amount the one of `Token In` that ETH an earlier step left in the router may pay part of;
decoder does not know. When there is no `Amount` line, each take the total and one whose amount the decoder does not know. When there is no `Amount`
counts gets them too, so that every amount the decoder reads is shown: the line, each take the total counts gets them too, so that every amount the
amount the step that sets the input side states, when it is a quantity and the decoder reads is shown: the amount the step that sets the input side states,
user pays that step (a V2 or V3 step whose `payerIsUser` is true, a V4 step when it is a quantity and the user pays that step (a V2 or V3 step whose
the user pays some of its input token, or a `WRAP_ETH`, whose ETH comes from `payerIsUser` is true, a V4 step the user pays some of its input token, or a
the transaction's value), and each later take of `Token In` added to it. A `WRAP_ETH`, whose ETH comes from the transaction's value), and each later take
step that sets the input side gets none when the user pays it none of its of `Token In` added to it. A step that sets the input side gets none when the
input token. A `PERMIT2_PERMIT` the decoder cannot read, then V3 exact-ins of user pays it none of its input token. A `PERMIT2_PERMIT` the decoder cannot
600 USDT and 400 DAI paid by the user, give lines for both amounts. When the read, then V3 exact-ins of 600 USDT and 400 DAI paid by the user, give lines
step that sets the input side states no amount, the user pays it some of its for both amounts. When the step that sets the input side states no amount, the
input token, and there is no `Amount` line, that step gets them too whenever user pays it some of its input token, and there is no `Amount` line, that step
another take's lines give a figure, or a `PERMIT2_TRANSFER_FROM` of its token gets them too whenever another take's lines give a figure, or a
gives one, on the lines below or, into the router, on its `Paid to` line, so `PERMIT2_TRANSFER_FROM` of its token gives one, on the lines below or, into
that figure cannot read as all that leaves. When it states no amount and the the router, on its `Paid to` line, so that figure cannot read as all that
user pays it none of its input token, that step never gets them, with a permit leaves. When it states no amount and the user pays it none of its input token,
or without. They come in step order, except that the step that sets the input that step never gets them, with a permit or without. They come in step order,
side comes first when it has them, followed, when there is no `Amount` line, except that the step that sets the input side comes first when it has them,
by the other takes the total counts: `Moved from the sender`, its amount, followed, when there is no `Amount` line, by the other takes the total counts:
worded as the `Amount` line words one, or `Unknown (could not be decoded)` `Moved from the sender`, its amount, worded as the `Amount` line words one, or
when the decoder does not know it; `Token moved`, giving the token's address `Unknown (could not be decoded)` when the decoder does not know it;
as the `Token In` line does; and `Moved to`, which reads `Token moved`, giving the token's address as the `Token In` line does; and
`The swap step that spends it`. A token the decoder does not know gives the `Moved to`, which reads `The swap step that spends it`. A token the decoder
`Moved from the sender` line alone. A step may also take from the user's does not know gives the `Moved from the sender` line alone. A step may also
wallet what the decoder did not read: a V4 step whose input or one of whose take from the user's wallet what the decoder did not read: a V4 step whose
`SETTLE`s or `SETTLE_ALL`s it cannot read, which the router still runs; a V2 input or one of whose `SETTLE`s or `SETTLE_ALL`s it cannot read, which the
or V3 exact-in or exact-out step or a `PERMIT2_TRANSFER_FROM` whose input it router still runs; a V2 or V3 exact-in or exact-out step or a
cannot read, such as a V3 step whose path is shorter than one token address or `PERMIT2_TRANSFER_FROM` whose input it cannot read, such as a V3 step whose
has bytes after its last hop; every `PERMIT2_TRANSFER_FROM_BATCH`; every path is shorter than one token address or has bytes after its last hop; every
`EXECUTE_SUB_PLAN`, wherever it stands; and every step it fails on. Each such `PERMIT2_TRANSFER_FROM_BATCH`; every `EXECUTE_SUB_PLAN`, wherever it stands;
step gives one `Moved from the sender` line among those, reading and every step it fails on. Each such step gives one `Moved from the sender`
`Unknown (could not be decoded)`. That line, or any line for a token the line among those, reading `Unknown (could not be decoded)`. That line, or any
decoder does not know, leaves no `Amount` line, whose figure might be too low, line for a token the decoder does not know, leaves no `Amount` line, whose
and such a step before a `PERMIT2_PERMIT` keeps the permit's amount from figure might be too low, and such a step before a `PERMIT2_PERMIT` keeps the
standing. Then each `PERMIT2_TRANSFER_FROM` but one into the router, in step permit's amount from standing. Then each `PERMIT2_TRANSFER_FROM` but one into
order, gives three lines: `Moved from the sender`, the amount it moves, for the router, in step order, gives three lines: `Moved from the sender`, the
example `1000000.0000 USDT`; `Token moved`, giving the token's address as the amount it moves, for example `1000000.0000 USDT`; `Token moved`, giving the
`Token In` line does; and `Moved to`, which reads token's address as the `Token In` line does; and `Moved to`, which reads
`The V2 pair the next swap spends` for a hop and `The sender` for `address(1)` `The V2 pair the next swap spends` for a hop and `The sender` for `address(1)`
on every router but `UniversalRouterV1`, and otherwise gives the address. A on every router but `UniversalRouterV1`, and otherwise gives the address. A
move into the router needs none of them, because its `Paid to` line and move into the router needs none of them, because its `Paid to` line and