1 Commits
Author SHA1 Message Date
sneak 6792a1f9b6 fix: a V4 step of chained swap actions shows what the user receives (closes #563)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
A V4 step's input side is what SETTLE or SETTLE_ALL names, else the one
currency its swap actions spend and never buy; its output side is what
TAKE or TAKE_ALL names, else the one they buy and never spend. Amount
and Min. received come from those actions, summed where several state
one. An unread swap action leaves the sides to SETTLE, SETTLE_ALL, TAKE
and TAKE_ALL, with no figure. A step with a swap action always sets the
output side, sets the input side only when no earlier step set it, and
sets a side its rules leave unknown as unknown with no figure.
SWAP_EXACT_OUT is now read as spending its first path entry, not its
last.

Model: opus-5-5
2026-10-19 00:06:36 +00:00
2 changed files with 37 additions and 30 deletions
+21 -18
View File
@@ -1168,11 +1168,13 @@ read:
- `All available (V4 open delta)`: the swap's `Amount` line, when the amount it
shows is a V4 exact-in `amountIn` of zero. V4 reads that zero as "use the
whole open delta", so the calldata states no quantity. The line shows the
amount of one step that names an input token or amount: the last
`PERMIT2_PERMIT` step if there is one, otherwise the first V2 or V3 exact-in,
V2 exact-out, `WRAP_ETH` or V4 swap step. A V2 exact-out step gives its
`amountInMax`, and a V4 swap step the `amountIn` of the swap action that
spends its input currency, as described below.
amount of the step that sets the input side: the last `PERMIT2_PERMIT` step if
there is one, otherwise the first V2 or V3 exact-in, V2 exact-out, `WRAP_ETH`
or V4 swap step that names an input token or amount. A V4 step with a swap
action sets the input side when no earlier step set it, even when it names
neither, as described below. A V2 exact-out step gives its `amountInMax`, and
a V4 swap step the `amountIn` of the swap action that spends its input
currency.
- `Whatever an earlier step sent to the pair (V2 already paid)`: the swap's
`Amount` line, when the amount it shows is a V2 exact-in `amountIn` of zero.
The router reads that zero as "the pair already holds the input tokens": the
@@ -1194,25 +1196,26 @@ The swap's `Token In` and `Token Out` lines name a currency, not an amount; each
reads `Unknown (not named in the calldata)` when the decoder found no token for
that side, or `Unknown (could not be decoded)` instead when a V4 swap action in
the transaction could not be read (see Transaction Decoding below). A V4 step
with a swap action sets both sides as a swap step does, following three rules:
with a swap action always sets the output side, and sets the input side only
when no earlier step set it. It finds each side by three rules:
1. Its input side is the currency its `SETTLE` or `SETTLE_ALL` names, or else
the one currency that some swap action in the step spends and none buys. Its
output side is the currency its `TAKE` or `TAKE_ALL` names, or else the one
currency that some swap action buys and none spends. If there is not exactly
one such currency, that side is unknown, and no other step's token or figure
is shown for it. A step that buys WETH with USDT and USDC with that WETH,
with nothing naming its sides, reads `Swap USDT → USDC`.
2. `Amount` is the `amountIn` of the swap action that spends the input currency.
If several exact-in actions spend it, each with a stated amount, it is their
sum; in any other case there is no `Amount` line. The decoder reads no amount
from an exact-out action, and an open delta is not a stated amount.
`Min. received` is found the same way, from the `amountOutMin` of the actions
that buy the output currency.
one such currency, that side is unknown, and the step sets it as unknown with
no figure. A step that buys WETH with USDT and USDC with that WETH, with
nothing naming its sides, reads `Swap USDT → USDC`.
2. Its `Amount` is the `amountIn` of the swap action that spends the input
currency. If several exact-in actions spend it, each with a stated amount, it
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.
3. If any swap action in the step could not be read, only `SETTLE`,
`SETTLE_ALL`, `TAKE` and `TAKE_ALL` name its sides, and there is no `Amount`
or `Min. received` line. A side they did not name is unknown, and no other
step's token or figure is shown for it.
`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.
A V4 step with no swap action sets only the sides its `SETTLE`, `SETTLE_ALL`,
`TAKE` or `TAKE_ALL` names.
+16 -12
View File
@@ -442,10 +442,11 @@ function totalAmount(amounts) {
// the action's amountOutMin as Min. received.
//
// `hasSwapAction` is true when the step has a swap action, read or not.
// decode() then sets both sides from this step, a side these rules leave
// unknown included, so no other step's token or figure shows for it. A step
// with no swap action sets only the sides SETTLE, SETTLE_ALL, TAKE and
// TAKE_ALL name.
// decode() then always sets the output side from this step, and sets the
// 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.
function decodeV4Swap(input) {
try {
const d = coder.decode(["bytes", "bytes[]"], input);
@@ -595,8 +596,11 @@ function decode(data, toAddress, sources) {
//
// The input side is fixed by the first hop that states either half, the
// output side by the last, because the final leg is what the user
// receives. A half the establishing hop did not state stays null and
// the line says so, rather than being filled in from a different hop.
// receives. A V4 step with a swap action sets a side even when it
// states neither half: the output side always, and the input side
// when no earlier hop set it. A half the establishing hop did not
// state stays null and the line says so, rather than being filled in
// from a different hop.
const setInput = (token, amount) => {
inputToken = present(token) ? token : null;
inputAmount = present(amount) ? amount : null;
@@ -689,12 +693,12 @@ function decode(data, toAddress, sources) {
commandNames[i] += " (could not be decoded)";
}
if (v4 && v4.hasSwapAction) {
// A step with a swap action sets both sides as a swap
// step does, the input when no earlier step set it and
// the output always, as the last swap step wins. Each
// side is set as a pair, so a side decodeV4Swap() left
// unknown is set unknown with no figure, rather than
// left to another step's token and figure. A token
// A step with a swap action always sets the output
// side, as the last swap step wins, and sets the
// input side only when no earlier step set it. Each
// side it sets is set as a pair, even when the step
// states neither half, so a side decodeV4Swap() left
// unknown is set unknown with no figure. A token
// stated without a figure, or a figure without a
// token, likewise never sits beside an earlier step's
// other half.