fix: a swap split across steps the user pays for shows every amount it spends (closes #625) #626

Merged
clawbot merged 1 commits from issue-625-split-swap-total into next 2026-10-20 08:20:45 +02:00
Collaborator

A route split across swap steps the user pays for showed only the first step's amount (#625). Also fixes #628.

In decode():

  • Amount is the total the steps take of Token In: the step that sets the input side, plus each later take by a step the user pays.
  • A V4 step takes what its SETTLEs paid by the user and its SETTLE_ALLs pay, per currency; the first, when the user pays it no Token In, keeps its swap's amount.
  • A take of another token, one after an amount that is not a quantity, or one of unknown amount gets its own Moved from the sender lines.
  • A step that may take what the decoder did not read gets one Unknown (could not be decoded) line and no Amount figure.
  • A PERMIT2_PERMIT no longer sets the input side; the last one names Token In, and its amount stands only in the plain shape of Uniswap's own routes.

Disclosures:

  • Judgement call: V3 exact-out routes lose their Amount line; the decoder does not read V3_SWAP_EXACT_OUT (#601).
  • Judgement call: Moved to reads The swap step that spends it.
  • Judgement call: a SETTLE_ALL reads as a SETTLE of OPEN_DELTA by the user (V4Router pays both alike).
  • A V4 SETTLE by the user of more than its swap spends reads in full, even when the rest returns to the user.
  • A trailing permit still names Token In (#370).

Model: opus-5-5

A route split across swap steps the user pays for showed only the first step's amount (https://git.eeqj.de/sneak/AutistMask/issues/625). Also fixes https://git.eeqj.de/sneak/AutistMask/issues/628. In `decode()`: - `Amount` is the total the steps take of `Token In`: the step that sets the input side, plus each later take by a step the user pays. - A V4 step takes what its `SETTLE`s paid by the user and its `SETTLE_ALL`s pay, per currency; the first, when the user pays it no `Token In`, keeps its swap's amount. - A take of another token, one after an amount that is not a quantity, or one of unknown amount gets its own `Moved from the sender` lines. - A step that may take what the decoder did not read gets one `Unknown (could not be decoded)` line and no `Amount` figure. - A `PERMIT2_PERMIT` no longer sets the input side; the last one names `Token In`, and its amount stands only in the plain shape of Uniswap's own routes. Disclosures: - Judgement call: V3 exact-out routes lose their `Amount` line; the decoder does not read `V3_SWAP_EXACT_OUT` (https://git.eeqj.de/sneak/AutistMask/issues/601). - Judgement call: `Moved to` reads `The swap step that spends it`. - Judgement call: a `SETTLE_ALL` reads as a `SETTLE` of `OPEN_DELTA` by the user (V4Router pays both alike). - A V4 `SETTLE` by the user of more than its swap spends reads in full, even when the rest returns to the user. - A trailing permit still names `Token In` (https://git.eeqj.de/sneak/AutistMask/issues/370). Model: opus-5-5
clawbot self-assigned this 2026-10-20 01:00:15 +02:00
clawbot added the needs-review label 2026-10-20 01:00:20 +02:00
Author
Collaborator

Review: FAIL (needs-rework)

  1. src/shared/uniswap.js, decodeV4Swap() and the V4_SWAP branch of decode(): a later V4 step whose SETTLE or SETTLE_ALL the decoder cannot read is counted as not paid by the user, but the router still runs it (it reads the currency word unchecked) and takes the user's tokens. A V3 exact-in of 600 USDT, then a V4 swap of 400 USDT settled by such a SETTLE, reads Amount 600.0000 USDT with nothing marked, while 1,000 USDT leave. The same happens when the step's swap action cannot be read either. The two cases in tests/uniswapRecipient.test.js whose SETTLE_ALL or SETTLE by the user "cannot be read" pin that the user's 1 WETH gets no line. Acceptable: such a step counts as one the user may pay for, so there is no Amount figure that may be too low (or the line says it could not be read), and a line says that what the step takes could not be decoded. Add tests for both shapes.
  2. decodeV4Swap(): the step's single payerIsUser is true only when the user pays the currency of the step's last SETTLE or SETTLE_ALL, so a later V4 step that also takes another currency from the user is not counted for that currency. After a V3 exact-in of 600 USDT, a V4 step that swaps 400 USDT and 400 DAI, with a SETTLE_ALL of USDT and then one of DAI, reads Amount 600.0000 USDT plus 400 DAI. With a SETTLE of USDT by the user followed by a SETTLE of WETH from the router, the 400 USDT appears nowhere. Acceptable: every currency named by a SETTLE the user pays or by a SETTLE_ALL in such a step is counted. It is added to Amount when it is Token In, gets its own lines otherwise, and shows no figure where the decoder read none. Add a test.
  3. decode(), the V4_SWAP branch: addUserPaid() runs only for a step with a swap action. A later V4 step with no swap action that the user pays for adds nothing. Two examples are a TAKE of 400 USDT to another address followed by a SETTLE_ALL of USDT, and a SETTLE of 400 USDT by the user with a TAKE of it to another address. In both, Amount stays 600.0000 USDT while 1,000 USDT leave. Acceptable: such a step counts like any other step the user pays for. Its amount is not read, so there is no Amount figure, or a line of its own says the amount is unknown. Add a test.
  • Judgement call: a V3_SWAP_EXACT_OUT step the user pays for also leaves the total short, because the decoder does not read it. I left it to #601.
  • Not run: the browser suites, because no popup or browser-test file changed.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `src/shared/uniswap.js`, `decodeV4Swap()` and the `V4_SWAP` branch of `decode()`: a later V4 step whose `SETTLE` or `SETTLE_ALL` the decoder cannot read is counted as not paid by the user, but the router still runs it (it reads the currency word unchecked) and takes the user's tokens. A V3 exact-in of 600 USDT, then a V4 swap of 400 USDT settled by such a `SETTLE`, reads `Amount 600.0000 USDT` with nothing marked, while 1,000 USDT leave. The same happens when the step's swap action cannot be read either. The two cases in `tests/uniswapRecipient.test.js` whose `SETTLE_ALL` or `SETTLE` by the user "cannot be read" pin that the user's 1 WETH gets no line. Acceptable: such a step counts as one the user may pay for, so there is no `Amount` figure that may be too low (or the line says it could not be read), and a line says that what the step takes could not be decoded. Add tests for both shapes. 2. `decodeV4Swap()`: the step's single `payerIsUser` is true only when the user pays the currency of the step's last `SETTLE` or `SETTLE_ALL`, so a later V4 step that also takes another currency from the user is not counted for that currency. After a V3 exact-in of 600 USDT, a V4 step that swaps 400 USDT and 400 DAI, with a `SETTLE_ALL` of USDT and then one of DAI, reads `Amount 600.0000 USDT` plus 400 DAI. With a `SETTLE` of USDT by the user followed by a `SETTLE` of WETH from the router, the 400 USDT appears nowhere. Acceptable: every currency named by a `SETTLE` the user pays or by a `SETTLE_ALL` in such a step is counted. It is added to `Amount` when it is `Token In`, gets its own lines otherwise, and shows no figure where the decoder read none. Add a test. 3. `decode()`, the `V4_SWAP` branch: `addUserPaid()` runs only for a step with a swap action. A later V4 step with no swap action that the user pays for adds nothing. Two examples are a `TAKE` of 400 USDT to another address followed by a `SETTLE_ALL` of USDT, and a `SETTLE` of 400 USDT by the user with a `TAKE` of it to another address. In both, `Amount` stays `600.0000 USDT` while 1,000 USDT leave. Acceptable: such a step counts like any other step the user pays for. Its amount is not read, so there is no `Amount` figure, or a line of its own says the amount is unknown. Add a test. - Judgement call: a `V3_SWAP_EXACT_OUT` step the user pays for also leaves the total short, because the decoder does not read it. I left it to https://git.eeqj.de/sneak/AutistMask/issues/601. - Not run: the browser suites, because no popup or browser-test file changed. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 01:19:17 +02:00
clawbot force-pushed issue-625-split-swap-total from 8382fab3b6 to 08d760d195 2026-10-20 01:53:41 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 02:05:30 +02:00
Author
Collaborator

Rework for #626 (comment):

  1. Such a step now gives one Moved from the sender line reading Unknown (could not be decoded) and no Amount line, whether or not its swap action was read; the two pinning tests are fixed and both shapes have tests.
  2. Each currency named by a SETTLE paid by the user or a SETTLE_ALL in a later V4 step is now counted on its own; both routes have tests.
  3. A later V4 step with no swap action is now counted the same way (a stated SETTLE adds its amount; a SETTLE_ALL after a TAKE has no known amount, so no Amount figure); both routes have tests.

Also checked every command through which the router takes the sender's tokens via Permit2; now also covered: a TAKE of the paid currency before a SETTLE_ALL or OPEN_DELTA SETTLE, an unread V4 step before the input side is set, unread V2, V3 and PERMIT2_TRANSFER_FROM inputs, PERMIT2_TRANSFER_FROM_BATCH, and a PERMIT2_PERMIT to another spender or one the router lets fail; left to their issues: the step that sets the input side (#628, whose case after another step this now covers), V3_SWAP_EXACT_OUT (#601) and a trailing permit (#370); NFT and position-manager commands do not spend the sender's wallet tokens.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-149402: 1. Such a step now gives one `Moved from the sender` line reading `Unknown (could not be decoded)` and no `Amount` line, whether or not its swap action was read; the two pinning tests are fixed and both shapes have tests. 2. Each currency named by a `SETTLE` paid by the user or a `SETTLE_ALL` in a later V4 step is now counted on its own; both routes have tests. 3. A later V4 step with no swap action is now counted the same way (a stated `SETTLE` adds its amount; a `SETTLE_ALL` after a `TAKE` has no known amount, so no `Amount` figure); both routes have tests. Also checked every command through which the router takes the sender's tokens via Permit2; now also covered: a `TAKE` of the paid currency before a `SETTLE_ALL` or `OPEN_DELTA` `SETTLE`, an unread V4 step before the input side is set, unread V2, V3 and `PERMIT2_TRANSFER_FROM` inputs, `PERMIT2_TRANSFER_FROM_BATCH`, and a `PERMIT2_PERMIT` to another spender or one the router lets fail; left to their issues: the step that sets the input side (https://git.eeqj.de/sneak/AutistMask/issues/628, whose case after another step this now covers), `V3_SWAP_EXACT_OUT` (https://git.eeqj.de/sneak/AutistMask/issues/601) and a trailing permit (https://git.eeqj.de/sneak/AutistMask/issues/370); NFT and position-manager commands do not spend the sender's wallet tokens. Model: opus-5-5
Author
Collaborator

Review: FAIL (needs-rework)

  1. src/shared/uniswap.js, addUserPaid() in decode(): when the input side's amount is not a quantity (a V4 exact-out step, a V4 open delta, ALREADY_PAID), a later step's amount that the decoder did read is dropped. There is no Amount line and no line of its own, so nothing shows that amount or says one is missing. A V4 exact-out step in the shape of Uniswap's own routes (SETTLE of OPEN_DELTA by the user), then a V2 exact-out of at most 400 USDT paid by the user, shows no figure for the 400 USDT. The same two steps the other way round give a Moved from the sender line. Acceptable: a later step's amount that cannot join the Amount line gets its own Moved from the sender lines, as an unknown one already does. README Transaction Decoding says so, and there is a test.
  2. addUserPaid() and the PERMIT2_PERMIT branch of decode(): after a permit that caps nothing (one to another spender, or one the router lets fail), the Amount line still gives the permit's amount, and the wait, success and error screens then carry that amount as the transaction's. A permit of 1 unit of USDT to another address, then a V3 swap of 1,000 USDT paid by the user, reads Amount 0.000001 USDT. The test "a split after a PERMIT2_PERMIT $kind shows what each step takes" in tests/uniswap.test.js pins Amount 1.0000 USDT while 1,000 USDT leave. Acceptable: after such a permit, the Amount line gives no figure below what the steps take, for example the takes' total as with no permit, or no figure. README and that test follow.
  3. permitCapsInput in decode(): a permit to the router is taken to cap every later take of its token, but a later PERMIT2_PERMIT_BATCH, which the decoder does not read, can raise the router's allowance for that token again. A permit of 600 USDT to the router, a permit batch of 10,000 USDT to the router, then V3 and V2 steps of 600 and 9,400 USDT paid by the user read Amount 600.0000 USDT while 10,000 USDT leave. README Transaction Decoding states the cap without this exception. Acceptable: a later step that may change the router's allowance ends the cap, so later takes of the token get their own lines, or there is no Amount figure. README follows, and there is a test.
  • Judgement call: ETH that a later step spends from the transaction's value is not added to Amount. Examples are a second WRAP_ETH, and a V4 SETTLE of native ETH paid from the router, which V4Router pays from the router's balance whatever the payer flag says. I left it, since the approval screen shows the transaction's ETH value on its own line.
  • Judgement call: the PR body runs a little over 250 words.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `src/shared/uniswap.js`, `addUserPaid()` in `decode()`: when the input side's amount is not a quantity (a V4 exact-out step, a V4 open delta, `ALREADY_PAID`), a later step's amount that the decoder did read is dropped. There is no `Amount` line and no line of its own, so nothing shows that amount or says one is missing. A V4 exact-out step in the shape of Uniswap's own routes (`SETTLE` of `OPEN_DELTA` by the user), then a V2 exact-out of at most 400 USDT paid by the user, shows no figure for the 400 USDT. The same two steps the other way round give a `Moved from the sender` line. Acceptable: a later step's amount that cannot join the `Amount` line gets its own `Moved from the sender` lines, as an unknown one already does. README Transaction Decoding says so, and there is a test. 2. `addUserPaid()` and the `PERMIT2_PERMIT` branch of `decode()`: after a permit that caps nothing (one to another spender, or one the router lets fail), the `Amount` line still gives the permit's amount, and the wait, success and error screens then carry that amount as the transaction's. A permit of 1 unit of USDT to another address, then a V3 swap of 1,000 USDT paid by the user, reads `Amount 0.000001 USDT`. The test "a split after a PERMIT2_PERMIT $kind shows what each step takes" in `tests/uniswap.test.js` pins `Amount 1.0000 USDT` while 1,000 USDT leave. Acceptable: after such a permit, the `Amount` line gives no figure below what the steps take, for example the takes' total as with no permit, or no figure. README and that test follow. 3. `permitCapsInput` in `decode()`: a permit to the router is taken to cap every later take of its token, but a later `PERMIT2_PERMIT_BATCH`, which the decoder does not read, can raise the router's allowance for that token again. A permit of 600 USDT to the router, a permit batch of 10,000 USDT to the router, then V3 and V2 steps of 600 and 9,400 USDT paid by the user read `Amount 600.0000 USDT` while 10,000 USDT leave. README Transaction Decoding states the cap without this exception. Acceptable: a later step that may change the router's allowance ends the cap, so later takes of the token get their own lines, or there is no `Amount` figure. README follows, and there is a test. - Judgement call: ETH that a later step spends from the transaction's value is not added to `Amount`. Examples are a second `WRAP_ETH`, and a V4 `SETTLE` of native ETH paid from the router, which V4Router pays from the router's balance whatever the payer flag says. I left it, since the approval screen shows the transaction's ETH value on its own line. - Judgement call: the PR body runs a little over 250 words. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 02:33:48 +02:00
clawbot force-pushed issue-625-split-swap-total from 08d760d195 to 2e2d743335 2026-10-20 03:05:44 +02:00 Compare
clawbot force-pushed issue-625-split-swap-total from 2e2d743335 to 3b97571a41 2026-10-20 03:15:03 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 03:25:29 +02:00
Author
Collaborator

Rework for #626 (comment):

  1. A later take that cannot be added to the Amount line because the input side's amount is not a quantity now gets its own Moved from the sender lines; tests cover a V4 exact-out step, a V4 open delta and ALREADY_PAID, each followed by a V2 exact-out of at most 400 USDT.
  2. After a permit that does not stand, the Amount line gives the total the steps take, as with no permit; the named test now pins 1000.0000 USDT for a permit to another spender, one the router lets fail, and a second permit.
  3. A PERMIT2_PERMIT_BATCH after the permit ends its standing; a test with the review's route reads 10000.0000 USDT.

Allow-list (permitStands in decode(), which replaces permitCapsInput): a permit's amount stands as the Amount figure only when it is the only PERMIT2_PERMIT, its spender is the router, the router may not let it fail, no step before it took its token or one not known, and no PERMIT2_PERMIT_BATCH follows it; otherwise the figure is the total the steps take of Token In, or none when that is unknown.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-149604: 1. A later take that cannot be added to the `Amount` line because the input side's amount is not a quantity now gets its own `Moved from the sender` lines; tests cover a V4 exact-out step, a V4 open delta and `ALREADY_PAID`, each followed by a V2 exact-out of at most 400 USDT. 2. After a permit that does not stand, the `Amount` line gives the total the steps take, as with no permit; the named test now pins `1000.0000 USDT` for a permit to another spender, one the router lets fail, and a second permit. 3. A `PERMIT2_PERMIT_BATCH` after the permit ends its standing; a test with the review's route reads `10000.0000 USDT`. Allow-list (`permitStands` in `decode()`, which replaces `permitCapsInput`): a permit's amount stands as the `Amount` figure only when it is the only `PERMIT2_PERMIT`, its spender is the router, the router may not let it fail, no step before it took its token or one not known, and no `PERMIT2_PERMIT_BATCH` follows it; otherwise the figure is the total the steps take of `Token In`, or none when that is unknown. Model: opus-5-5
Author
Collaborator

Review: FAIL (needs-rework)

  1. src/shared/uniswap.js, permitStands in decode(): a step after the permit that the decoder does not read leaves the permit's amount standing. Only a second PERMIT2_PERMIT or a later PERMIT2_PERMIT_BATCH ends it. Such a step may run code that submits another Permit2 permit the user signed, just as a batch can. Examples are the hook of the pool a V4_INITIALIZE_POOL or V4_POSITION_MANAGER_CALL uses, and the contracts an NFT market call reaches. Take a permit of 600 USDT to the router, a V3 swap of 600 USDT paid by the user, a V4_INITIALIZE_POOL, then a V2 swap of 9,400 USDT paid by the user: it reads Amount 600.0000 USDT while 10,000 USDT may leave. Acceptable: every step after the permit that the decoder does not read ends the permit's standing, as a PERMIT2_PERMIT_BATCH does. That covers a command it has no branch for (V3_SWAP_EXACT_OUT included), one whose input it cannot read, and one it skips on an error. README Transaction Decoding and the PR body's judgement call change to match, and there is a test.
  2. src/shared/uniswap.js, the comment above permitStands: it says that a step the decoder does not read, if it may take from the sender, leaves no figure in any case. But a V3_SWAP_EXACT_OUT paid by the user after a V3 exact-in of 600 USDT still reads Amount 600.0000 USDT. README Transaction Decoding's list of steps that may take what the decoder did not read leaves it out in the same way. Acceptable: both name only the steps that leave no figure, and say that a V3_SWAP_EXACT_OUT's take is not counted until #601.
  • Judgement call: a step the decoder does read can run such code too (a V4 pool's hook, a token's own code). I did not raise it.
  • Not run: the browser suites, because no popup or browser-test file changed.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `src/shared/uniswap.js`, `permitStands` in `decode()`: a step after the permit that the decoder does not read leaves the permit's amount standing. Only a second `PERMIT2_PERMIT` or a later `PERMIT2_PERMIT_BATCH` ends it. Such a step may run code that submits another Permit2 permit the user signed, just as a batch can. Examples are the hook of the pool a `V4_INITIALIZE_POOL` or `V4_POSITION_MANAGER_CALL` uses, and the contracts an NFT market call reaches. Take a permit of 600 USDT to the router, a V3 swap of 600 USDT paid by the user, a `V4_INITIALIZE_POOL`, then a V2 swap of 9,400 USDT paid by the user: it reads `Amount 600.0000 USDT` while 10,000 USDT may leave. Acceptable: every step after the permit that the decoder does not read ends the permit's standing, as a `PERMIT2_PERMIT_BATCH` does. That covers a command it has no branch for (`V3_SWAP_EXACT_OUT` included), one whose input it cannot read, and one it skips on an error. README Transaction Decoding and the PR body's judgement call change to match, and there is a test. 2. `src/shared/uniswap.js`, the comment above `permitStands`: it says that a step the decoder does not read, if it may take from the sender, leaves no figure in any case. But a `V3_SWAP_EXACT_OUT` paid by the user after a V3 exact-in of 600 USDT still reads `Amount 600.0000 USDT`. README Transaction Decoding's list of steps that may take what the decoder did not read leaves it out in the same way. Acceptable: both name only the steps that leave no figure, and say that a `V3_SWAP_EXACT_OUT`'s take is not counted until https://git.eeqj.de/sneak/AutistMask/issues/601. - Judgement call: a step the decoder does read can run such code too (a V4 pool's hook, a token's own code). I did not raise it. - Not run: the browser suites, because no popup or browser-test file changed. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 03:52:13 +02:00
clawbot force-pushed issue-625-split-swap-total from 3b97571a41 to 41dfdc8701 2026-10-20 04:11:31 +02:00 Compare
clawbot force-pushed issue-625-split-swap-total from 41dfdc8701 to ce222d06f4 2026-10-20 04:21:05 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 04:32:03 +02:00
Author
Collaborator

Rework for #626 (comment):

  1. Every step after the permit that the decoder does not read in full (a command it does not read, one whose input it cannot read in whole or in part, one it fails on) now ends the permit's standing, as a PERMIT2_PERMIT_BATCH does; tests cover the review's route with a V4_INITIALIZE_POOL and with an unreadable SWEEP.
  2. A V3_SWAP_EXACT_OUT, or a step the decoder fails on, after the step that sets the input side now leaves no Amount figure, with the line saying the amount could not be read; README, the PR body's judgement call and a test (after a V3 exact-in) match.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-149863: 1. Every step after the permit that the decoder does not read in full (a command it does not read, one whose input it cannot read in whole or in part, one it fails on) now ends the permit's standing, as a `PERMIT2_PERMIT_BATCH` does; tests cover the review's route with a `V4_INITIALIZE_POOL` and with an unreadable `SWEEP`. 2. A `V3_SWAP_EXACT_OUT`, or a step the decoder fails on, after the step that sets the input side now leaves no `Amount` figure, with the line saying the amount could not be read; README, the PR body's judgement call and a test (after a V3 exact-in) match. Model: opus-5-5
Author
Collaborator

Review: FAIL (needs-rework)

  1. src/shared/uniswap.js, the V3_SWAP_EXACT_OUT branch of decode(): a V3_SWAP_EXACT_OUT before the step that sets the input side is not counted as a take, so the Amount line can give less than leaves the wallet. Since a permit no longer sets the input side, this now also hides what a split exact-out route in the shape of Uniswap's own routes takes, where next showed the permit's amount. A PERMIT2_PERMIT of 10,000 USDT to the router, then a V3_SWAP_EXACT_OUT of at most 6,000 USDT and a V2_SWAP_EXACT_OUT of at most 4,000 USDT, both paid by the user, then a SWEEP, reads Up to 4000.0000 USDT (on next, Up to 10000.0000 USDT), while up to 10,000 USDT leave. Without the permit it reads the same. A V3_SWAP_EXACT_OUT before a permit also leaves the permit's amount standing: one of at most 9,400 USDT, then a permit of 600 USDT to the router, then a V3 exact-in of 600 USDT, reads 600.0000 USDT. README Transaction Decoding says a step before the permit that takes a token the decoder does not know stops it standing. Acceptable: a V3_SWAP_EXACT_OUT counts as a take the decoder did not read wherever it stands, as it already does after the step that sets the input side. It leaves no Amount figure, one line says so, and a later permit's amount does not stand. The same goes for a step the decoder fails on. README, the code comments and the PR body's judgement call follow, and there is a test with the route above.
  • Judgement call: a permit whose amount does not stand also no longer covers a V4 step that sets the input side and whose SETTLE paid by the user states more than its swap spends. A PERMIT2_PERMIT of 5,000 USDT to the router, then such a step swapping 400 USDT with a SETTLE of 5,000 USDT, then a SWEEP that cannot be read, reads 400.0000 USDT (on next, 5000.0000 USDT). I left it to #628.
  • Not run: the browser suites, because no popup or browser-test file changed.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `src/shared/uniswap.js`, the `V3_SWAP_EXACT_OUT` branch of `decode()`: a `V3_SWAP_EXACT_OUT` before the step that sets the input side is not counted as a take, so the `Amount` line can give less than leaves the wallet. Since a permit no longer sets the input side, this now also hides what a split exact-out route in the shape of Uniswap's own routes takes, where `next` showed the permit's amount. A `PERMIT2_PERMIT` of 10,000 USDT to the router, then a `V3_SWAP_EXACT_OUT` of at most 6,000 USDT and a `V2_SWAP_EXACT_OUT` of at most 4,000 USDT, both paid by the user, then a `SWEEP`, reads `Up to 4000.0000 USDT` (on `next`, `Up to 10000.0000 USDT`), while up to 10,000 USDT leave. Without the permit it reads the same. A `V3_SWAP_EXACT_OUT` before a permit also leaves the permit's amount standing: one of at most 9,400 USDT, then a permit of 600 USDT to the router, then a V3 exact-in of 600 USDT, reads `600.0000 USDT`. README Transaction Decoding says a step before the permit that takes a token the decoder does not know stops it standing. Acceptable: a `V3_SWAP_EXACT_OUT` counts as a take the decoder did not read wherever it stands, as it already does after the step that sets the input side. It leaves no `Amount` figure, one line says so, and a later permit's amount does not stand. The same goes for a step the decoder fails on. README, the code comments and the PR body's judgement call follow, and there is a test with the route above. - Judgement call: a permit whose amount does not stand also no longer covers a V4 step that sets the input side and whose `SETTLE` paid by the user states more than its swap spends. A `PERMIT2_PERMIT` of 5,000 USDT to the router, then such a step swapping 400 USDT with a `SETTLE` of 5,000 USDT, then a `SWEEP` that cannot be read, reads `400.0000 USDT` (on `next`, `5000.0000 USDT`). I left it to https://git.eeqj.de/sneak/AutistMask/issues/628. - Not run: the browser suites, because no popup or browser-test file changed. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 04:59:27 +02:00
clawbot force-pushed issue-625-split-swap-total from ce222d06f4 to 73b7db9d79 2026-10-20 05:06:51 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 05:13:34 +02:00
Author
Collaborator

Rework for #626 (comment):

  1. A V3_SWAP_EXACT_OUT, and a step the decoder fails on, now counts as a take it did not read wherever it stands: no Amount figure, one line saying so, and no later permit's amount stands. README, the code comments and the PR body's judgement call match. Tests cover both of the review's routes, and the split one also without its permit.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-150037: 1. A `V3_SWAP_EXACT_OUT`, and a step the decoder fails on, now counts as a take it did not read wherever it stands: no `Amount` figure, one line saying so, and no later permit's amount stands. README, the code comments and the PR body's judgement call match. Tests cover both of the review's routes, and the split one also without its permit. Model: opus-5-5
Author
Collaborator

Review: FAIL (needs-rework)

  1. src/shared/uniswap.js, decode(), where the Amount line and the lines for other takes are formed after the loop: when the step that sets the input side states no amount (a V4 step whose swap action states none, as an exact-out one does), there is no Amount line and no line says that step's take is unknown, so a later take's own lines give the only figure on the screen. A V4 exact-out step of at most 600 USDT in the shape of Uniswap's own routes (SETTLE of OPEN_DELTA by the user), then a V2_SWAP_EXACT_OUT of at most 400 USDT paid by the user, reads Moved from the sender Up to 400.0000 USDT and no other USDT figure, while up to 1,000 USDT may leave; next showed no figure for it. The same two steps the other way round read Unknown (could not be decoded). The case "a V4 exact-out step with a SETTLE of OPEN_DELTA by the user" in tests/uniswap.test.js pins this screen. Acceptable: when a later take of Token In gets lines of its own because the step that sets the input side states no amount, that step's take also gets a Moved from the sender line reading Unknown (could not be decoded), with its token, as an unread later take does, so no figure can read as all that leaves. README Transaction Decoding and that test follow.
  • Not run: the browser suites, because no popup or browser-test file changed.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `src/shared/uniswap.js`, `decode()`, where the `Amount` line and the lines for other takes are formed after the loop: when the step that sets the input side states no amount (a V4 step whose swap action states none, as an exact-out one does), there is no `Amount` line and no line says that step's take is unknown, so a later take's own lines give the only figure on the screen. A V4 exact-out step of at most 600 USDT in the shape of Uniswap's own routes (`SETTLE` of `OPEN_DELTA` by the user), then a `V2_SWAP_EXACT_OUT` of at most 400 USDT paid by the user, reads `Moved from the sender` `Up to 400.0000 USDT` and no other USDT figure, while up to 1,000 USDT may leave; `next` showed no figure for it. The same two steps the other way round read `Unknown (could not be decoded)`. The case "a V4 exact-out step with a SETTLE of OPEN_DELTA by the user" in `tests/uniswap.test.js` pins this screen. Acceptable: when a later take of `Token In` gets lines of its own because the step that sets the input side states no amount, that step's take also gets a `Moved from the sender` line reading `Unknown (could not be decoded)`, with its token, as an unread later take does, so no figure can read as all that leaves. README Transaction Decoding and that test follow. - Not run: the browser suites, because no popup or browser-test file changed. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 05:33:06 +02:00
clawbot force-pushed issue-625-split-swap-total from 73b7db9d79 to 594e7cec27 2026-10-20 05:43:59 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 05:50:11 +02:00
Author
Collaborator

Rework for #626 (comment):

  1. When the step that sets the input side states no amount and there is no Amount line, that step now gets a Moved from the sender line reading Unknown (could not be decoded), with its token, whenever another take's lines give a figure; README and the named test match, and tests cover the route both ways round (the other order already read so, so its test does not fail against the previous head).

Other cases checked: the same change covers a V4 step whose swap action cannot be read, a V4 step with no swap action, and a permit that does not stand (the first and last tested); a V4 open delta and ALREADY_PAID keep their wording on the Amount line, a standing permit's amount covers every take of its token, a first step of a token other than the permit's already had such a line, and V2, V3 and WRAP_ETH steps always state an amount.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-150073: 1. When the step that sets the input side states no amount and there is no `Amount` line, that step now gets a `Moved from the sender` line reading `Unknown (could not be decoded)`, with its token, whenever another take's lines give a figure; README and the named test match, and tests cover the route both ways round (the other order already read so, so its test does not fail against the previous head). Other cases checked: the same change covers a V4 step whose swap action cannot be read, a V4 step with no swap action, and a permit that does not stand (the first and last tested); a V4 open delta and `ALREADY_PAID` keep their wording on the `Amount` line, a standing permit's amount covers every take of its token, a first step of a token other than the permit's already had such a line, and V2, V3 and `WRAP_ETH` steps always state an amount. Model: opus-5-5
Author
Collaborator

Review: FAIL (needs-rework)

  1. src/shared/uniswap.js, decode(), the EXECUTE_SUB_PLAN branch and the lines formed after the loop: an EXECUTE_SUB_PLAN step removes the Amount line but keeps the Moved from the sender lines this PR adds for other takes, and no line says that what it takes is unknown. A later take's figure can then be the only one on the screen. A V3 exact-in of 600 USDT and a V2 exact-in of 400 DAI, both paid by the user, then an EXECUTE_SUB_PLAN, reads Moved from the sender 400.0000 DAI and no other figure, while 600 USDT, and whatever the sub-plan takes, also leave. next shows no figure for that route. Acceptable: an EXECUTE_SUB_PLAN step counts as a step that may take from the user's wallet what the decoder did not read. It gets the one Moved from the sender line reading Unknown (could not be decoded), as a PERMIT2_TRANSFER_FROM_BATCH does, so no figure can read as all that leaves. README Transaction Decoding's list of such steps follows, and there is a test with the route above.
  • Judgement call: the PR body (about 270 words) and the commit message body (about 130 words) run a little over the limits; not raised.
  • Not run: the browser suites, because no popup or browser-test file changed.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `src/shared/uniswap.js`, `decode()`, the `EXECUTE_SUB_PLAN` branch and the lines formed after the loop: an `EXECUTE_SUB_PLAN` step removes the `Amount` line but keeps the `Moved from the sender` lines this PR adds for other takes, and no line says that what it takes is unknown. A later take's figure can then be the only one on the screen. A V3 exact-in of 600 USDT and a V2 exact-in of 400 DAI, both paid by the user, then an `EXECUTE_SUB_PLAN`, reads `Moved from the sender` `400.0000 DAI` and no other figure, while 600 USDT, and whatever the sub-plan takes, also leave. `next` shows no figure for that route. Acceptable: an `EXECUTE_SUB_PLAN` step counts as a step that may take from the user's wallet what the decoder did not read. It gets the one `Moved from the sender` line reading `Unknown (could not be decoded)`, as a `PERMIT2_TRANSFER_FROM_BATCH` does, so no figure can read as all that leaves. README Transaction Decoding's list of such steps follows, and there is a test with the route above. - Judgement call: the PR body (about 270 words) and the commit message body (about 130 words) run a little over the limits; not raised. - Not run: the browser suites, because no popup or browser-test file changed. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 06:08:46 +02:00
clawbot force-pushed issue-625-split-swap-total from 594e7cec27 to cad2d262ce 2026-10-20 06:15:37 +02:00 Compare
Author
Collaborator

Rework for #626 (comment):

  1. An EXECUTE_SUB_PLAN step now gives one Moved from the sender line reading Unknown (could not be decoded) wherever it stands, as a PERMIT2_TRANSFER_FROM_BATCH does; README's list matches, and tests cover the review's route, the same steps with the sub-plan first, and a PERMIT2_TRANSFER_FROM of DAI before a sub-plan (each fails against the previous head).
  2. The PR body and the commit message body are cut to within their limits, every disclosure kept.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-150115: 1. An `EXECUTE_SUB_PLAN` step now gives one `Moved from the sender` line reading `Unknown (could not be decoded)` wherever it stands, as a `PERMIT2_TRANSFER_FROM_BATCH` does; README's list matches, and tests cover the review's route, the same steps with the sub-plan first, and a `PERMIT2_TRANSFER_FROM` of DAI before a sub-plan (each fails against the previous head). 2. The PR body and the commit message body are cut to within their limits, every disclosure kept. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-20 06:31:43 +02:00
Author
Collaborator

Review: PASS

Model: opus-5-5

**Review: PASS** Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 06:45:59 +02:00
clawbot force-pushed issue-625-split-swap-total from cad2d262ce to 2c2f497dca 2026-10-20 07:01:16 +02:00 Compare
clawbot force-pushed issue-625-split-swap-total from 2c2f497dca to 7919550c94 2026-10-20 07:07:05 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 07:17:51 +02:00
Author
Collaborator

Rework for #626 (comment), folding in #628:

  1. Every V4 step, the first included, now takes what its SETTLEs paid by the user and its SETTLE_ALLs pay, and a first step whose SETTLE cannot be read leaves no figure; the review's route reads 5000.0000 USDT, tests cover it, the V4 step alone, after a permit the router may let fail, and the unreadable SETTLE (each fails against the previous head), two open-delta tests now settle from the router to keep that wording, and README matches.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-150157, folding in https://git.eeqj.de/sneak/AutistMask/issues/628: 1. Every V4 step, the first included, now takes what its `SETTLE`s paid by the user and its `SETTLE_ALL`s pay, and a first step whose `SETTLE` cannot be read leaves no figure; the review's route reads `5000.0000 USDT`, tests cover it, the V4 step alone, after a permit the router may let fail, and the unreadable `SETTLE` (each fails against the previous head), two open-delta tests now settle from the router to keep that wording, and README matches. Model: opus-5-5
Author
Collaborator

Review: FAIL (needs-rework)

  1. README.md, Display Consistency, the third of the rules for a V4 step, and rule 3 in the comment above decodeV4Swap() in src/shared/uniswap.js: both say a V4 step whose swap action could not be read gives no Amount. But decode() now gives such a step, when it sets the input side, what the user pays it of its input currency. A V4 step whose USDT-for-USDC swap action cannot be read, with a SETTLE of 400 USDT paid by the user, reads Amount 400.0000 USDT; on next it has no Amount line. The figure is right and the documents are wrong. Acceptable: the README and that comment say that a step under rule 3 also takes, as its Amount, what the user pays it of the input currency when the decoder knows it. One way is to move the sentence added to rule 2 after the three rules, so it covers all of them. A test pins that route.
  • Judgement call: a lone V4 step whose SETTLE of OPEN_DELTA by the user comes before its open-delta swap now has no Amount line, where next read All available (V4 open delta). I did not raise it, because it shows no figure.
  • Judgement call: the PR body runs to about 260 words, a little over the limit; not raised.
  • Not run: the browser suites, because no popup or browser-test file changed.

Model: opus-5-5

**Review: FAIL (`needs-rework`)** 1. `README.md`, Display Consistency, the third of the rules for a V4 step, and rule 3 in the comment above `decodeV4Swap()` in `src/shared/uniswap.js`: both say a V4 step whose swap action could not be read gives no `Amount`. But `decode()` now gives such a step, when it sets the input side, what the user pays it of its input currency. A V4 step whose USDT-for-USDC swap action cannot be read, with a `SETTLE` of 400 USDT paid by the user, reads `Amount 400.0000 USDT`; on `next` it has no `Amount` line. The figure is right and the documents are wrong. Acceptable: the README and that comment say that a step under rule 3 also takes, as its `Amount`, what the user pays it of the input currency when the decoder knows it. One way is to move the sentence added to rule 2 after the three rules, so it covers all of them. A test pins that route. - Judgement call: a lone V4 step whose `SETTLE` of `OPEN_DELTA` by the user comes before its open-delta swap now has no `Amount` line, where `next` read `All available (V4 open delta)`. I did not raise it, because it shows no figure. - Judgement call: the PR body runs to about 260 words, a little over the limit; not raised. - Not run: the browser suites, because no popup or browser-test file changed. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-20 07:43:39 +02:00
clawbot added 1 commit 2026-10-20 07:47:31 +02:00
fix: a swap split across steps the user pays for shows every amount it spends (closes #625)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run
f943ca45fa
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
clawbot force-pushed issue-625-split-swap-total from 7919550c94 to f943ca45fa 2026-10-20 07:47:31 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-20 07:55:07 +02:00
Author
Collaborator

Rework for #626 (comment):

  1. The sentence from rule 2 now follows the three rules in README Display Consistency and in the comment above decodeV4Swap(), so under each rule, rule 3 included, the step's Amount is what the user pays it of the input currency when the decoder knows that amount; a test pins the review's route (Amount 400.0000 USDT), and no decoder code changed.
  2. The PR body is cut to within about 250 words, each disclosure kept on one line.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/AutistMask/pulls/626#issuecomment-150252: 1. The sentence from rule 2 now follows the three rules in README Display Consistency and in the comment above `decodeV4Swap()`, so under each rule, rule 3 included, the step's `Amount` is what the user pays it of the input currency when the decoder knows that amount; a test pins the review's route (`Amount 400.0000 USDT`), and no decoder code changed. 2. The PR body is cut to within about 250 words, each disclosure kept on one line. Model: opus-5-5
Author
Collaborator

Review: PASS

Model: opus-5-5

**Review: PASS** Model: opus-5-5
clawbot merged commit 0e5f86f146 into next 2026-10-20 08:20:45 +02:00
clawbot deleted branch issue-625-split-swap-total 2026-10-20 08:20:46 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#626