Fixes #358, as settled there: the V4 swap actions stay pinned to the parameter structs deployed on mainnet and Sepolia.
The issue expected the newer v4-periphery structs, which add minHopPriceX36, to fail to decode. ethers read them as the deployed ones instead, and the newer multi-hop exact-in struct showed its hop-price array's offset as Amount.
The four swap-action decodes in src/shared/uniswap.js now go through decodeExactly(), which accepts a decode only when encoding it again gives back the same bytes. A swap action that fails is listed as V4 Swap (could not be decoded) with no amounts, and a side its SETTLE or TAKE did not name reads Unknown (could not be decoded), never another step's token or figure, nor ETH from an UNWRAP_WETH.
The wait and error screens showed the transaction's ETH value as the amount of a swap with no Amount line, such as 0.0000 WETH. They now say Unknown (could not be decoded), with no currency. This also covers a V4 exact-out step, which did the same on next. A swap whose Token In is ETH (native) still shows that ETH value, since it is what the swap pays.
The README records the pinned shape and the wordings.
Judgement call: the strict decode goes beyond marking the fallthrough; without it the marker never fires for the shape the issue is about.
Not done: an unreadable V4_SWAP input as a whole, and failed V2, V3, SETTLE or TAKE decodes, are still skipped silently.
Model: opus-5-5
Fixes https://git.eeqj.de/sneak/AutistMask/issues/358, as settled there: the V4 swap actions stay pinned to the parameter structs deployed on mainnet and Sepolia.
The issue expected the newer `v4-periphery` structs, which add `minHopPriceX36`, to fail to decode. ethers read them as the deployed ones instead, and the newer multi-hop exact-in struct showed its hop-price array's offset as `Amount`.
The four swap-action decodes in `src/shared/uniswap.js` now go through `decodeExactly()`, which accepts a decode only when encoding it again gives back the same bytes. A swap action that fails is listed as `V4 Swap (could not be decoded)` with no amounts, and a side its `SETTLE` or `TAKE` did not name reads `Unknown (could not be decoded)`, never another step's token or figure, nor ETH from an `UNWRAP_WETH`.
The wait and error screens showed the transaction's ETH value as the amount of a swap with no `Amount` line, such as `0.0000 WETH`. They now say `Unknown (could not be decoded)`, with no currency. This also covers a V4 exact-out step, which did the same on `next`. A swap whose `Token In` is `ETH (native)` still shows that ETH value, since it is what the swap pays.
The README records the pinned shape and the wordings.
Judgement call: the strict decode goes beyond marking the fallthrough; without it the marker never fires for the shape the issue is about.
Not done: an unreadable `V4_SWAP` input as a whole, and failed V2, V3, `SETTLE` or `TAKE` decodes, are still skipped silently.
Model: opus-5-5
src/shared/uniswap.js, the Token In and Token Out fallbacks in decode() (lines 671 and 734), next to the new step marker (line 596). When a V4 swap action is not in the deployed shape and no SETTLE/TAKE names the currencies, for example a swap action followed by SETTLE_ALL and TAKE_ALL, the screen shows V4 Swap (could not be decoded) and also says both tokens are "Unknown (not named in the calldata)". The calldata did name them, in the action that was not read, so those two lines present "could not decode" as "nothing there", which #358 rules out. For the newer single-pool structs this is new: on next they showed the right tokens. Acceptable: when a swap action went unread and a side is left without a token, that line says the currency could not be read, not that the calldata did not name it, with a test for a newer-shape swap action followed by SETTLE_ALL and TAKE_ALL.
Judgement call: the cases the PR lists as not done (an unreadable V4_SWAP input as a whole; failed V2, V3, SETTLE or TAKE decodes) are outside the issue's definition of done, since none of them is a V4 swap struct whose shape changed upstream.
Judgement call: a deployed-shape swap action with extra bytes appended is now shown as not decoded, although the router would run it; taken as the intended effect of pinning the shape.
Model: opus-5-5
**FAIL** (`needs-rework`)
1. `src/shared/uniswap.js`, the Token In and Token Out fallbacks in `decode()` (lines 671 and 734), next to the new step marker (line 596). When a V4 swap action is not in the deployed shape and no `SETTLE`/`TAKE` names the currencies, for example a swap action followed by `SETTLE_ALL` and `TAKE_ALL`, the screen shows `V4 Swap (could not be decoded)` and also says both tokens are "Unknown (not named in the calldata)". The calldata did name them, in the action that was not read, so those two lines present "could not decode" as "nothing there", which https://git.eeqj.de/sneak/AutistMask/issues/358 rules out. For the newer single-pool structs this is new: on `next` they showed the right tokens. Acceptable: when a swap action went unread and a side is left without a token, that line says the currency could not be read, not that the calldata did not name it, with a test for a newer-shape swap action followed by `SETTLE_ALL` and `TAKE_ALL`.
Judgement call: the cases the PR lists as not done (an unreadable `V4_SWAP` input as a whole; failed V2, V3, `SETTLE` or `TAKE` decodes) are outside the issue's definition of done, since none of them is a V4 swap struct whose shape changed upstream.
Judgement call: a deployed-shape swap action with extra bytes appended is now shown as not decoded, although the router would run it; taken as the intended effect of pinning the shape.
Model: opus-5-5
A token line left without a currency after a swap action went unread now reads Unknown (could not be decoded), not Unknown (not named in the calldata). A new test runs each newer-shape swap action followed by SETTLE_ALL and TAKE_ALL, and the existing unreadable-action test in tests/uniswapUndeterminedTokenIn.test.js now expects the new wording.
Model: opus-5-5
1. A token line left without a currency after a swap action went unread now reads `Unknown (could not be decoded)`, not `Unknown (not named in the calldata)`. A new test runs each newer-shape swap action followed by `SETTLE_ALL` and `TAKE_ALL`, and the existing unreadable-action test in `tests/uniswapUndeterminedTokenIn.test.js` now expects the new wording.
Model: opus-5-5
src/shared/uniswap.js, the V4 swap branch of decode() (lines 606 and 617). A V4 step whose swap action could not be read, and which has no TAKE (or no SETTLE), sets nothing on that side, so a neighbouring step's token and figure take its place. A V3 swap of USDT to WETH followed by a newer-shape V4 swap of WETH to USDC that ends in TAKE_ALL is titled Swap USDT → WETH, with Token Out WETH and the V3 step's Min. received, although the user receives USDC. The mirror case, a newer-shape V4 step with SETTLE_ALL and then a V3 step, names the V3 step's input token and amount as what is paid. On next both transactions showed the right tokens. The screen now names a wrong asset in the very shape #358 is about. Acceptable: a V4 step with an unread swap action leaves a side it did not name undetermined, so that line reads Unknown (could not be decoded) and no other step's figure is shown beside it, with tests for a V3 step before and after such a V4 step.
Judgement call: this is in scope although the PR lists an unreadable V4_SWAP input as not done, because here only the swap action, in the changed upstream shape, is unread.
Model: opus-5-5
**FAIL** (`needs-rework`)
The earlier finding is resolved.
1. `src/shared/uniswap.js`, the V4 swap branch of `decode()` (lines 606 and 617). A V4 step whose swap action could not be read, and which has no `TAKE` (or no `SETTLE`), sets nothing on that side, so a neighbouring step's token and figure take its place. A V3 swap of USDT to WETH followed by a newer-shape V4 swap of WETH to USDC that ends in `TAKE_ALL` is titled `Swap USDT → WETH`, with `Token Out` WETH and the V3 step's `Min. received`, although the user receives USDC. The mirror case, a newer-shape V4 step with `SETTLE_ALL` and then a V3 step, names the V3 step's input token and amount as what is paid. On `next` both transactions showed the right tokens. The screen now names a wrong asset in the very shape https://git.eeqj.de/sneak/AutistMask/issues/358 is about. Acceptable: a V4 step with an unread swap action leaves a side it did not name undetermined, so that line reads `Unknown (could not be decoded)` and no other step's figure is shown beside it, with tests for a V3 step before and after such a V4 step.
Judgement call: this is in scope although the PR lists an unreadable `V4_SWAP` input as not done, because here only the swap action, in the changed upstream shape, is unread.
Model: opus-5-5
A V4 step whose swap action could not be read now sets both sides itself: a side its SETTLE or TAKE did not name reads Unknown (could not be decoded), and no other step's token or figure is shown for it. Tests run each newer-shape swap action after a V3 step (ending in TAKE_ALL) and before one (with SETTLE_ALL).
Judgement call: an UNWRAP_WETH after such a step no longer turns its unknown Token Out into ETH, with a test.
Model: opus-5-5
1. A V4 step whose swap action could not be read now sets both sides itself: a side its `SETTLE` or `TAKE` did not name reads `Unknown (could not be decoded)`, and no other step's token or figure is shown for it. Tests run each newer-shape swap action after a V3 step (ending in `TAKE_ALL`) and before one (with `SETTLE_ALL`).
Judgement call: an `UNWRAP_WETH` after such a step no longer turns its unknown `Token Out` into ETH, with a test.
Model: opus-5-5
src/popup/views/approval.js, the details showTxApproval() builds for the later screens (lines 262 to 299), reached through the new branch in decode() in src/shared/uniswap.js (line 599). When a V4 swap action is not read and its SETTLE names the token paid, the approval screen rightly drops the Amount line, but the wait, success and error screens then show the transaction's ETH value in that token: a swap of 1 WETH in the newer single-pool exact-in shape reads 0.0000 WETH there. On next the same transaction read 1.0000 WETH on those screens. That is a figure the decoder refused to state, shown as "nothing paid", which #358 rules out, and it contradicts the PR body's "its amounts are not shown". Acceptable: when a swap's amount was not read, those screens show no figure in the swap's input token (for example, they say the amount could not be decoded), with a test. A V4 exact-out step already reaches the same fallback on next; fixing it in the same change is not scope creep.
PR body: 274 words, over the limit of about 250. Acceptable: about 250 words or fewer, including whatever the fix for item 1 adds.
Model: opus-5-5
**FAIL** (`needs-rework`)
1. `src/popup/views/approval.js`, the details `showTxApproval()` builds for the later screens (lines 262 to 299), reached through the new branch in `decode()` in `src/shared/uniswap.js` (line 599). When a V4 swap action is not read and its `SETTLE` names the token paid, the approval screen rightly drops the `Amount` line, but the wait, success and error screens then show the transaction's ETH value in that token: a swap of 1 WETH in the newer single-pool exact-in shape reads `0.0000 WETH` there. On `next` the same transaction read `1.0000 WETH` on those screens. That is a figure the decoder refused to state, shown as "nothing paid", which https://git.eeqj.de/sneak/AutistMask/issues/358 rules out, and it contradicts the PR body's "its amounts are not shown". Acceptable: when a swap's amount was not read, those screens show no figure in the swap's input token (for example, they say the amount could not be decoded), with a test. A V4 exact-out step already reaches the same fallback on `next`; fixing it in the same change is not scope creep.
2. PR body: 274 words, over the limit of about 250. Acceptable: about 250 words or fewer, including whatever the fix for item 1 adds.
Model: opus-5-5
clawbot
changed title from fix: a V4 swap action not in the deployed shape is shown as not decoded to fix: a V4 swap action not in the deployed shape is shown as not decoded (closes #358)2026-10-16 01:56:25 +02:00
The wait and error screens now read Unknown (could not be decoded), with no currency, for any swap the approval screen showed no Amount for, V4 exact-out steps included. tests/swapStatusAmount.test.js drives each case through Confirm to those screens and to the success screen, which already showed the decoded lines in place of an amount.
The PR body is now under 250 words.
Judgement call: a swap whose input token is also unknown, such as a newer-shape swap action with SETTLE_ALL, showed 0.0000 ETH there; it gets the same wording, which is why no currency follows it.
Judgement call: a swap paid in native ETH with no Amount line now reads the same instead of the transaction's ETH value.
Model: opus-5-5
1. The wait and error screens now read `Unknown (could not be decoded)`, with no currency, for any swap the approval screen showed no `Amount` for, V4 exact-out steps included. `tests/swapStatusAmount.test.js` drives each case through Confirm to those screens and to the success screen, which already showed the decoded lines in place of an amount.
2. The PR body is now under 250 words.
Judgement call: a swap whose input token is also unknown, such as a newer-shape swap action with `SETTLE_ALL`, showed `0.0000 ETH` there; it gets the same wording, which is why no currency follows it.
Judgement call: a swap paid in native ETH with no `Amount` line now reads the same instead of the transaction's ETH value.
Model: opus-5-5
The findings of the three earlier reviews stay resolved.
src/popup/views/approval.js, the new check in showTxApproval() (lines 301 to 312). For a swap paid in native ETH that has no Amount line, the wait and error screens now read Unknown (could not be decoded) where they used to show the ETH the transaction sends. That figure is not read from the calldata. It is the transaction's own value, the same one the approval screen's value line shows, and for a swap paid in ETH it is what the user pays (for an exact-out swap, the most they pay). The change also reaches a transaction where nothing failed to decode: a V4 exact-out swap in the deployed shape, paid in ETH, whose steps read V4 Swap, showed 1.0000 ETH on those screens on next and now says its amount could not be decoded. Acceptable: when the swap's Token In line is ETH (native), those screens keep showing the transaction's ETH value in the network's currency, as on next. Unknown (could not be decoded) stays for a swap paid in a token, or in a currency the decoder could not establish. The README's WaitTx and ErrorTx entries say so, and a test covers a swap paid in ETH.
Judgement call: a swap whose input currency is unknown keeps the new wording even when the transaction sends ETH, because that ETH is not known to be what the swap pays.
Model: opus-5-5
**FAIL** (`needs-rework`)
The findings of the three earlier reviews stay resolved.
1. `src/popup/views/approval.js`, the new check in `showTxApproval()` (lines 301 to 312). For a swap paid in native ETH that has no `Amount` line, the wait and error screens now read `Unknown (could not be decoded)` where they used to show the ETH the transaction sends. That figure is not read from the calldata. It is the transaction's own value, the same one the approval screen's value line shows, and for a swap paid in ETH it is what the user pays (for an exact-out swap, the most they pay). The change also reaches a transaction where nothing failed to decode: a V4 exact-out swap in the deployed shape, paid in ETH, whose steps read `V4 Swap`, showed `1.0000 ETH` on those screens on `next` and now says its amount could not be decoded. Acceptable: when the swap's `Token In` line is `ETH (native)`, those screens keep showing the transaction's ETH value in the network's currency, as on `next`. `Unknown (could not be decoded)` stays for a swap paid in a token, or in a currency the decoder could not establish. The README's WaitTx and ErrorTx entries say so, and a test covers a swap paid in ETH.
Judgement call: a swap whose input currency is unknown keeps the new wording even when the transaction sends ETH, because that ETH is not known to be what the swap pays.
Model: opus-5-5
The four V4 swap actions are read only as the structs deployed on mainnet
and Sepolia. ethers read the newer v4-periphery structs, which add
minHopPriceX36, as the deployed ones, and the newer multi-hop exact-in
struct showed the offset of its hop-price array as the swap amount. A
decode now counts only when encoding it again gives back the same bytes.
A swap action that fails that marks its step "V4 Swap (could not be
decoded)", and a side its SETTLE or TAKE did not name reads "Unknown
(could not be decoded)". The wait and error screens no longer show the
ETH value as the amount of a swap whose amount was not read, unless the
swap is paid in native ETH.
Model: opus-5-5
When the swap's Token In is ETH (native), the wait and error screens again show the transaction's ETH value in the network's currency. The README's WaitTx and ErrorTx entries say so, and tests/swapStatusAmount.test.js covers a deployed-shape exact-out swap and a newer-shape swap action, each paid in ETH.
The PR body no longer has the judgement call about swaps paid in native ETH losing their ETH value.
Model: opus-5-5
1. When the swap's `Token In` is `ETH (native)`, the wait and error screens again show the transaction's ETH value in the network's currency. The README's WaitTx and ErrorTx entries say so, and `tests/swapStatusAmount.test.js` covers a deployed-shape exact-out swap and a newer-shape swap action, each paid in ETH.
The PR body no longer has the judgement call about swaps paid in native ETH losing their ETH value.
Model: opus-5-5
PASS: the change meets the definition of done in #358, and the findings of the four earlier reviews stay resolved.
Judgement call: a BALANCE_CHECK_ERC20 with a zero minBalance after a V4 step whose swap action could not be read shows None (no minimum guaranteed), as the README's balance-check rule says. I did not raise it: it understates what the swap guarantees rather than overstating it, and a V4 exact-out step already gives the same result on next.
Model: opus-5-5
**PASS**: the change meets the definition of done in https://git.eeqj.de/sneak/AutistMask/issues/358, and the findings of the four earlier reviews stay resolved.
Judgement call: a `BALANCE_CHECK_ERC20` with a zero `minBalance` after a V4 step whose swap action could not be read shows `None (no minimum guaranteed)`, as the README's balance-check rule says. I did not raise it: it understates what the swap guarantees rather than overstating it, and a V4 exact-out step already gives the same result on `next`.
Model: opus-5-5
clawbot
merged commit bad6494e43 into next2026-10-16 03:00:33 +02:00
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.
Fixes #358, as settled there: the V4 swap actions stay pinned to the parameter structs deployed on mainnet and Sepolia.
The issue expected the newer
v4-peripherystructs, which addminHopPriceX36, to fail to decode. ethers read them as the deployed ones instead, and the newer multi-hop exact-in struct showed its hop-price array's offset asAmount.The four swap-action decodes in
src/shared/uniswap.jsnow go throughdecodeExactly(), which accepts a decode only when encoding it again gives back the same bytes. A swap action that fails is listed asV4 Swap (could not be decoded)with no amounts, and a side itsSETTLEorTAKEdid not name readsUnknown (could not be decoded), never another step's token or figure, nor ETH from anUNWRAP_WETH.The wait and error screens showed the transaction's ETH value as the amount of a swap with no
Amountline, such as0.0000 WETH. They now sayUnknown (could not be decoded), with no currency. This also covers a V4 exact-out step, which did the same onnext. A swap whoseToken InisETH (native)still shows that ETH value, since it is what the swap pays.The README records the pinned shape and the wordings.
Judgement call: the strict decode goes beyond marking the fallthrough; without it the marker never fires for the shape the issue is about.
Not done: an unreadable
V4_SWAPinput as a whole, and failed V2, V3,SETTLEorTAKEdecodes, are still skipped silently.Model: opus-5-5
6e00d52f89to0c88dcbda9FAIL (
needs-rework)src/shared/uniswap.js, the Token In and Token Out fallbacks indecode()(lines 671 and 734), next to the new step marker (line 596). When a V4 swap action is not in the deployed shape and noSETTLE/TAKEnames the currencies, for example a swap action followed bySETTLE_ALLandTAKE_ALL, the screen showsV4 Swap (could not be decoded)and also says both tokens are "Unknown (not named in the calldata)". The calldata did name them, in the action that was not read, so those two lines present "could not decode" as "nothing there", which #358 rules out. For the newer single-pool structs this is new: onnextthey showed the right tokens. Acceptable: when a swap action went unread and a side is left without a token, that line says the currency could not be read, not that the calldata did not name it, with a test for a newer-shape swap action followed bySETTLE_ALLandTAKE_ALL.Judgement call: the cases the PR lists as not done (an unreadable
V4_SWAPinput as a whole; failed V2, V3,SETTLEorTAKEdecodes) are outside the issue's definition of done, since none of them is a V4 swap struct whose shape changed upstream.Judgement call: a deployed-shape swap action with extra bytes appended is now shown as not decoded, although the router would run it; taken as the intended effect of pinning the shape.
Model: opus-5-5
0c88dcbda9toec1254204cUnknown (could not be decoded), notUnknown (not named in the calldata). A new test runs each newer-shape swap action followed bySETTLE_ALLandTAKE_ALL, and the existing unreadable-action test intests/uniswapUndeterminedTokenIn.test.jsnow expects the new wording.Model: opus-5-5
FAIL (
needs-rework)The earlier finding is resolved.
src/shared/uniswap.js, the V4 swap branch ofdecode()(lines 606 and 617). A V4 step whose swap action could not be read, and which has noTAKE(or noSETTLE), sets nothing on that side, so a neighbouring step's token and figure take its place. A V3 swap of USDT to WETH followed by a newer-shape V4 swap of WETH to USDC that ends inTAKE_ALLis titledSwap USDT → WETH, withToken OutWETH and the V3 step'sMin. received, although the user receives USDC. The mirror case, a newer-shape V4 step withSETTLE_ALLand then a V3 step, names the V3 step's input token and amount as what is paid. Onnextboth transactions showed the right tokens. The screen now names a wrong asset in the very shape #358 is about. Acceptable: a V4 step with an unread swap action leaves a side it did not name undetermined, so that line readsUnknown (could not be decoded)and no other step's figure is shown beside it, with tests for a V3 step before and after such a V4 step.Judgement call: this is in scope although the PR lists an unreadable
V4_SWAPinput as not done, because here only the swap action, in the changed upstream shape, is unread.Model: opus-5-5
ec1254204ctof938950161SETTLEorTAKEdid not name readsUnknown (could not be decoded), and no other step's token or figure is shown for it. Tests run each newer-shape swap action after a V3 step (ending inTAKE_ALL) and before one (withSETTLE_ALL).Judgement call: an
UNWRAP_WETHafter such a step no longer turns its unknownToken Outinto ETH, with a test.Model: opus-5-5
FAIL (
needs-rework)src/popup/views/approval.js, the detailsshowTxApproval()builds for the later screens (lines 262 to 299), reached through the new branch indecode()insrc/shared/uniswap.js(line 599). When a V4 swap action is not read and itsSETTLEnames the token paid, the approval screen rightly drops theAmountline, but the wait, success and error screens then show the transaction's ETH value in that token: a swap of 1 WETH in the newer single-pool exact-in shape reads0.0000 WETHthere. Onnextthe same transaction read1.0000 WETHon those screens. That is a figure the decoder refused to state, shown as "nothing paid", which #358 rules out, and it contradicts the PR body's "its amounts are not shown". Acceptable: when a swap's amount was not read, those screens show no figure in the swap's input token (for example, they say the amount could not be decoded), with a test. A V4 exact-out step already reaches the same fallback onnext; fixing it in the same change is not scope creep.PR body: 274 words, over the limit of about 250. Acceptable: about 250 words or fewer, including whatever the fix for item 1 adds.
Model: opus-5-5
f938950161to08fa969cd6fix: a V4 swap action not in the deployed shape is shown as not decodedto fix: a V4 swap action not in the deployed shape is shown as not decoded (closes #358)Unknown (could not be decoded), with no currency, for any swap the approval screen showed noAmountfor, V4 exact-out steps included.tests/swapStatusAmount.test.jsdrives each case through Confirm to those screens and to the success screen, which already showed the decoded lines in place of an amount.Judgement call: a swap whose input token is also unknown, such as a newer-shape swap action with
SETTLE_ALL, showed0.0000 ETHthere; it gets the same wording, which is why no currency follows it.Judgement call: a swap paid in native ETH with no
Amountline now reads the same instead of the transaction's ETH value.Model: opus-5-5
FAIL (
needs-rework)The findings of the three earlier reviews stay resolved.
src/popup/views/approval.js, the new check inshowTxApproval()(lines 301 to 312). For a swap paid in native ETH that has noAmountline, the wait and error screens now readUnknown (could not be decoded)where they used to show the ETH the transaction sends. That figure is not read from the calldata. It is the transaction's own value, the same one the approval screen's value line shows, and for a swap paid in ETH it is what the user pays (for an exact-out swap, the most they pay). The change also reaches a transaction where nothing failed to decode: a V4 exact-out swap in the deployed shape, paid in ETH, whose steps readV4 Swap, showed1.0000 ETHon those screens onnextand now says its amount could not be decoded. Acceptable: when the swap'sToken Inline isETH (native), those screens keep showing the transaction's ETH value in the network's currency, as onnext.Unknown (could not be decoded)stays for a swap paid in a token, or in a currency the decoder could not establish. The README's WaitTx and ErrorTx entries say so, and a test covers a swap paid in ETH.Judgement call: a swap whose input currency is unknown keeps the new wording even when the transaction sends ETH, because that ETH is not known to be what the swap pays.
Model: opus-5-5
08fa969cd6to130ecd9bbeToken InisETH (native), the wait and error screens again show the transaction's ETH value in the network's currency. The README's WaitTx and ErrorTx entries say so, andtests/swapStatusAmount.test.jscovers a deployed-shape exact-out swap and a newer-shape swap action, each paid in ETH.The PR body no longer has the judgement call about swaps paid in native ETH losing their ETH value.
Model: opus-5-5
PASS: the change meets the definition of done in #358, and the findings of the four earlier reviews stay resolved.
Judgement call: a
BALANCE_CHECK_ERC20with a zerominBalanceafter a V4 step whose swap action could not be read showsNone (no minimum guaranteed), as the README's balance-check rule says. I did not raise it: it understates what the swap guarantees rather than overstating it, and a V4 exact-out step already gives the same result onnext.Model: opus-5-5