A V4 step with several swap actions shows the first one's output as what the user receives #563

Open
opened 2026-10-16 03:01:42 +02:00 by clawbot · 2 comments
Collaborator

Seen by the review of #537.

Where

decodeV4Swap() in src/shared/uniswap.js. Each swap action sets the output token (takeToken) and amountOutMin only when nothing has set them yet, so the first swap action in a V4_SWAP command wins. A TAKE overrides the output token, but TAKE_ALL (action 0x0f) and SETTLE_ALL (0x0c), which carry the currency the same way, are not read at all.

What happens

A connected site sends a Universal Router execute() whose V4_SWAP holds two single-pool exact-in swap actions, USDT to WETH and then WETH to USDC, followed by SETTLE_ALL (USDT) and TAKE_ALL (USDC). The router runs both swaps and pays the user USDC. The approval screen reads Swap USDT → WETH, with Token Out WETH and the first hop's minimum as Min. received. The screen names an asset the user does not receive, and a minimum that does not apply to what they do receive.

What should happen

README, Transaction Decoding: the approval screen states what the transaction does. The output side of a V4 step is the currency the step ends with: the one its TAKE or TAKE_ALL names, or, when it names none, the output of the last swap action. Its input side is the currency its SETTLE or SETTLE_ALL names, or the input of the first swap action.

Severity: major

Definition of done

  • A V4 step whose swap actions are chained shows the first action's input as Token In and the last action's output as Token Out, with that last action's minimum as Min. received.
  • SETTLE_ALL and TAKE_ALL name the step's input and output currency as SETTLE and TAKE do.
  • Tests: two chained single-pool exact-in actions ending in SETTLE_ALL and TAKE_ALL; the same ending in SETTLE and TAKE.

Model: opus-5-5

Seen by the review of https://git.eeqj.de/sneak/AutistMask/pulls/537. ## Where `decodeV4Swap()` in `src/shared/uniswap.js`. Each swap action sets the output token (`takeToken`) and `amountOutMin` only when nothing has set them yet, so the first swap action in a `V4_SWAP` command wins. A `TAKE` overrides the output token, but `TAKE_ALL` (action `0x0f`) and `SETTLE_ALL` (`0x0c`), which carry the currency the same way, are not read at all. ## What happens A connected site sends a Universal Router `execute()` whose `V4_SWAP` holds two single-pool exact-in swap actions, USDT to WETH and then WETH to USDC, followed by `SETTLE_ALL` (USDT) and `TAKE_ALL` (USDC). The router runs both swaps and pays the user USDC. The approval screen reads `Swap USDT → WETH`, with `Token Out` WETH and the first hop's minimum as `Min. received`. The screen names an asset the user does not receive, and a minimum that does not apply to what they do receive. ## What should happen README, Transaction Decoding: the approval screen states what the transaction does. The output side of a V4 step is the currency the step ends with: the one its `TAKE` or `TAKE_ALL` names, or, when it names none, the output of the last swap action. Its input side is the currency its `SETTLE` or `SETTLE_ALL` names, or the input of the first swap action. Severity: major ## Definition of done - [ ] A V4 step whose swap actions are chained shows the first action's input as `Token In` and the last action's output as `Token Out`, with that last action's minimum as `Min. received`. - [ ] `SETTLE_ALL` and `TAKE_ALL` name the step's input and output currency as `SETTLE` and `TAKE` do. - [ ] Tests: two chained single-pool exact-in actions ending in `SETTLE_ALL` and `TAKE_ALL`; the same ending in `SETTLE` and `TAKE`. Model: opus-5-5
clawbot self-assigned this 2026-10-16 03:01:42 +02:00
Author
Collaborator

Plan: the change stays inside decodeV4Swap() in src/shared/uniswap.js; no new file. While walking the actions, keep the input side from the first swap action and let each later swap action overwrite the output currency and amountOutMin, so after the loop they belong to the last swap action. SETTLE_ALL (0x0c, (address currency, uint256 maxAmount)) is read like SETTLE and names the input currency; TAKE_ALL (0x0f, (address currency, uint256 minAmount)) is read like TAKE and names the output currency. A currency named by SETTLE/SETTLE_ALL or TAKE/TAKE_ALL wins over one read from a swap action, wherever in the step it appears. Exact-out actions follow the same rule with the roles swapped (input from the first, output and amount from the last), if the decoder reads them today. Tests go in tests/uniswap.test.js or a new tests/uniswapV4Chained.test.js: two chained single-pool exact-in actions ending in SETTLE_ALL and TAKE_ALL, the same ending in SETTLE and TAKE, each showing the first action's input as Token In, the last action's output as Token Out and the last action's minimum as Min. received. This unit and #520 both change src/shared/uniswap.js and land one after the other.

Model: opus-5-5

Plan: the change stays inside `decodeV4Swap()` in `src/shared/uniswap.js`; no new file. While walking the actions, keep the input side from the first swap action and let each later swap action overwrite the output currency and `amountOutMin`, so after the loop they belong to the last swap action. `SETTLE_ALL` (`0x0c`, `(address currency, uint256 maxAmount)`) is read like `SETTLE` and names the input currency; `TAKE_ALL` (`0x0f`, `(address currency, uint256 minAmount)`) is read like `TAKE` and names the output currency. A currency named by `SETTLE`/`SETTLE_ALL` or `TAKE`/`TAKE_ALL` wins over one read from a swap action, wherever in the step it appears. Exact-out actions follow the same rule with the roles swapped (input from the first, output and amount from the last), if the decoder reads them today. Tests go in `tests/uniswap.test.js` or a new `tests/uniswapV4Chained.test.js`: two chained single-pool exact-in actions ending in `SETTLE_ALL` and `TAKE_ALL`, the same ending in `SETTLE` and `TAKE`, each showing the first action's input as `Token In`, the last action's output as `Token Out` and the last action's minimum as `Min. received`. This unit and https://git.eeqj.de/sneak/AutistMask/issues/520 both change `src/shared/uniswap.js` and land one after the other. Model: opus-5-5
Author
Collaborator

Revised plan, replacing the first: four reviews of #566 have each found a step whose swap actions are written in an order that a line-up by position gets wrong. A step's sides do not depend on the order its actions are written in, so the decoder stops using order.

In decodeV4Swap() in src/shared/uniswap.js, each readable swap action gives the currency it spends and the currency it buys (decodeV4SwapAction() as on the PR). Then:

  • Token In is the currency SETTLE or SETTLE_ALL names; otherwise the one currency that some swap action spends and none buys. Token Out is the currency TAKE or TAKE_ALL names; otherwise the one currency that some swap action buys and none spends. If there is not exactly one such currency, that side is unknown.
  • Amount is the amountIn of the swap action that spends Token In, read as today. 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. Min. received is the same for amountOutMin and the actions that buy Token Out.
  • If any swap action in the step cannot be read, only SETTLE, SETTLE_ALL, TAKE and TAKE_ALL name the sides, and there is no Amount or Min. received line.

The line-up of exact-out actions in front of exact-in ones goes. The README passage and the comment above decodeV4Swap() state these three rules and nothing about order. Tests: chained exact-in actions; chained exact-out actions written from either end; a step mixing the two kinds; two exact-in actions spending the same currency (summed); a step with an unread swap action; each with and without SETTLE_ALL/TAKE_ALL.

Model: opus-5-5

Revised plan, replacing the first: four reviews of https://git.eeqj.de/sneak/AutistMask/pulls/566 have each found a step whose swap actions are written in an order that a line-up by position gets wrong. A step's sides do not depend on the order its actions are written in, so the decoder stops using order. In `decodeV4Swap()` in `src/shared/uniswap.js`, each readable swap action gives the currency it spends and the currency it buys (`decodeV4SwapAction()` as on the PR). Then: - **Token In** is the currency `SETTLE` or `SETTLE_ALL` names; otherwise the one currency that some swap action spends and none buys. **Token Out** is the currency `TAKE` or `TAKE_ALL` names; otherwise the one currency that some swap action buys and none spends. If there is not exactly one such currency, that side is unknown. - **Amount** is the `amountIn` of the swap action that spends Token In, read as today. 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. **Min. received** is the same for `amountOutMin` and the actions that buy Token Out. - If any swap action in the step cannot be read, only `SETTLE`, `SETTLE_ALL`, `TAKE` and `TAKE_ALL` name the sides, and there is no Amount or Min. received line. The line-up of exact-out actions in front of exact-in ones goes. The README passage and the comment above `decodeV4Swap()` state these three rules and nothing about order. Tests: chained exact-in actions; chained exact-out actions written from either end; a step mixing the two kinds; two exact-in actions spending the same currency (summed); a step with an unread swap action; each with and without `SETTLE_ALL`/`TAKE_ALL`. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#563