fix(swap-vm): single-sided fromPriceBounds throws at max bound#78
Merged
krboktv merged 2 commits intoJul 12, 2026
Merged
Conversation
…max bound PriceRange.fromPriceBounds throws 'maxPrice should be >= spotPrice' for a valid token1-only composition (WBTC/USDT, range 58946-63752): integer truncation in computeLiquidityAndPrice lands the derived sqrtSpot slightly above sqrtPmax. The mirror token0-only case at the min bound passes. Co-authored-by: Kirill <krboktv@gmail.com>
… bound For single-sided compositions the spot price is a range bound by construction (reserve0 depletes at max, reserve1 at min), so derive it directly instead of running computeLiquidityAndPrice, whose integer truncation could land the implied sqrt spot just outside the bounds and trip the PriceRange constructor assert. Also reject the degenerate both-reserves-zero input with a clear error. Co-authored-by: Kirill <krboktv@gmail.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Fixes the asymmetric edge case in
PriceRange.fromPriceBoundsreported in 1inch/dapp-ui#3938 (found while fixing DAPP-4462, 1inch/dapp-ui#3954), together with regression tests that reproduce it.The bug
For a valid single-sided composition at the max bound,
fromPriceBoundsthrewmaxPrice should be >= spotPrice, while the mirror case at the min bound round-tripped fine.Repro (WBTC 8dp / USDT 6dp, range 58,946 → 63,752 USDT per WBTC):
reserveLt = 0.442142 WBTC, reserveGt = 0→ implied spot 58,946.000012 ✅ (min side OK, but off by rounding dust)reserveLt = 0, reserveGt = 28,207 USDT→ throws ❌In exact arithmetic the implied spot equals the max bound (
amount0 = 0), but integer truncation incomputeLiquidityAndPrice(virtualLt = L / sqrtPmaxrounds down) lands the derivedsqrtSpot~3.4e-10 (relative) abovesqrtPmax, and thePriceRangeconstructor assert rejects the legitimate composition.The fix
For single-sided compositions the spot is a bound by construction (
bLt = L·(√Pmax − √Pspot)/… = 0 ⟺ spot = max;bGt = L·(√Pspot − √Pmin) = 0 ⟺ spot = min), sofromPriceBoundsnow short-circuits:reserve0 == 0→spotPrice = maxPricereserve1 == 0→spotPrice = minPriceat least one reserve must be positiveassert (previously a crypticmulDiv: division by zerofrom deep inside the math)No math is run for these cases, so no truncation dust — the spot is exactly the bound on both sides now (the min side previously came out at 58,946.000012 instead of 58,946). The constructor assert stays untouched. The both-reserves-positive path is unchanged.
Tests
Added under
fromPriceBounds > single-sided compositions:58946)63752) — this was the throwing caseat least one reserve must be positiveLint and type-check pass.