The swap decoder, src/shared/uniswap.js. It checks only the swap's output token for being left in the router; the user's input is never checked the same way.
What happens
A V4 step whose SETTLE takes more of the user's tokens than its swap spends, with the excess taken to the router (TAKE or TAKE_PORTION to address(2)), reads Min. received like a normal swap. The excess stays in the router, where anyone can sweep it in a later transaction, and the SETTLE amount is never shown. Likewise, WRAP_ETH into the router with no later step spending it reads cleanly (its amount does show).
What should happen
The approval screen never presents a swap as complete while what it took from the user may stay in the router. When a step moves the user's tokens or ETH into the router and the decoder cannot see later steps spend or return all of it, the minimum is not called received, or the screen shows what the step takes.
Severity: major
Definition of done
A V4 SETTLE larger than its swap spends, with the excess taken to the router, does not read Min. received; nor does a WRAP_ETH into the router that nothing spends.
Nor does a route that leaves an intermediate token in the router (seen by the review of #604: a swap paying WETH to the router, a 1 wei TRANSFER of it to the WETH/USDC pair, then a V2 swap of what the pair already holds paid to the user; or a second swap of 1 wei from the router). The check covers any token the router is left holding, not only the output token.
Normal routes read as before.
Tests for both shapes and a normal route of each kind.
Model: opus-5-5
Seen by the review of https://git.eeqj.de/sneak/AutistMask/pulls/604.
## Where
The swap decoder, `src/shared/uniswap.js`. It checks only the swap's output token for being left in the router; the user's input is never checked the same way.
## What happens
A V4 step whose `SETTLE` takes more of the user's tokens than its swap spends, with the excess taken to the router (`TAKE` or `TAKE_PORTION` to `address(2)`), reads `Min. received` like a normal swap. The excess stays in the router, where anyone can sweep it in a later transaction, and the `SETTLE` amount is never shown. Likewise, `WRAP_ETH` into the router with no later step spending it reads cleanly (its amount does show).
## What should happen
The approval screen never presents a swap as complete while what it took from the user may stay in the router. When a step moves the user's tokens or ETH into the router and the decoder cannot see later steps spend or return all of it, the minimum is not called received, or the screen shows what the step takes.
Severity: major
## Definition of done
- [ ] A V4 `SETTLE` larger than its swap spends, with the excess taken to the router, does not read `Min. received`; nor does a `WRAP_ETH` into the router that nothing spends.
- [ ] Nor does a route that leaves an intermediate token in the router (seen by the review of https://git.eeqj.de/sneak/AutistMask/pulls/604: a swap paying WETH to the router, a 1 wei `TRANSFER` of it to the WETH/USDC pair, then a V2 swap of what the pair already holds paid to the user; or a second swap of 1 wei from the router). The check covers any token the router is left holding, not only the output token.
- [ ] Normal routes read as before.
- [ ] Tests for both shapes and a normal route of each kind.
Model: opus-5-5
clawbot
self-assigned this 2026-10-19 12:44:18 +02:00
Plan: one PR to next, built on the decoder's existing check for an output token left in the router: when a step moves the user's tokens or ETH into the router and later steps are not seen to spend or return all of it, for any token, the minimum is not called received. Tests for the SETTLE excess, the unspent WRAP_ETH and the intermediate-token route, each failing on next without the fix, plus normal routes reading as before. The other open units on src/shared/uniswap.js wait until this lands.
Model: opus-5-5
Plan: one PR to `next`, built on the decoder's existing check for an output token left in the router: when a step moves the user's tokens or ETH into the router and later steps are not seen to spend or return all of it, for any token, the minimum is not called received. Tests for the `SETTLE` excess, the unspent `WRAP_ETH` and the intermediate-token route, each failing on `next` without the fix, plus normal routes reading as before. The other open units on `src/shared/uniswap.js` wait until this lands.
Model: opus-5-5
Beyond the plan: a swap paid in ETH wraps an exact amount that its swap steps then spend from the router by exact amounts, so for the WETH of a WRAP_ETH the decoder keeps the amount it states and counts those steps against it. Without that, the most common ETH-paid route would stop reading Min. received. No other amount is followed; any other token in the router counts as spent only by a step that spends all of it.
Model: opus-5-5
PR: https://git.eeqj.de/sneak/AutistMask/pulls/618.
Beyond the plan: a swap paid in ETH wraps an exact amount that its swap steps then spend from the router by exact amounts, so for the WETH of a `WRAP_ETH` the decoder keeps the amount it states and counts those steps against it. Without that, the most common ETH-paid route would stop reading `Min. received`. No other amount is followed; any other token in the router counts as spent only by a step that spends all of it.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Seen by the review of #604.
Where
The swap decoder,
src/shared/uniswap.js. It checks only the swap's output token for being left in the router; the user's input is never checked the same way.What happens
A V4 step whose
SETTLEtakes more of the user's tokens than its swap spends, with the excess taken to the router (TAKEorTAKE_PORTIONtoaddress(2)), readsMin. receivedlike a normal swap. The excess stays in the router, where anyone can sweep it in a later transaction, and theSETTLEamount is never shown. Likewise,WRAP_ETHinto the router with no later step spending it reads cleanly (its amount does show).What should happen
The approval screen never presents a swap as complete while what it took from the user may stay in the router. When a step moves the user's tokens or ETH into the router and the decoder cannot see later steps spend or return all of it, the minimum is not called received, or the screen shows what the step takes.
Severity: major
Definition of done
SETTLElarger than its swap spends, with the excess taken to the router, does not readMin. received; nor does aWRAP_ETHinto the router that nothing spends.TRANSFERof it to the WETH/USDC pair, then a V2 swap of what the pair already holds paid to the user; or a second swap of 1 wei from the router). The check covers any token the router is left holding, not only the output token.Model: opus-5-5
Plan: one PR to
next, built on the decoder's existing check for an output token left in the router: when a step moves the user's tokens or ETH into the router and later steps are not seen to spend or return all of it, for any token, the minimum is not called received. Tests for theSETTLEexcess, the unspentWRAP_ETHand the intermediate-token route, each failing onnextwithout the fix, plus normal routes reading as before. The other open units onsrc/shared/uniswap.jswait until this lands.Model: opus-5-5
PR: #618.
Beyond the plan: a swap paid in ETH wraps an exact amount that its swap steps then spend from the router by exact amounts, so for the WETH of a
WRAP_ETHthe decoder keeps the amount it states and counts those steps against it. Without that, the most common ETH-paid route would stop readingMin. received. No other amount is followed; any other token in the router counts as spent only by a step that spends all of it.Model: opus-5-5