fix(web): preserve correct tickSpacing when migrating legacy feeTier URL parameter (#7939)#8028
Open
Kropiunig wants to merge 1 commit into
Open
Conversation
…URL parameter
The URL migration registered as V1 in apps/web/src/features/Liquidity/parsers/migrations.ts
converts the deprecated `feeTier` query parameter into the new `fee` FeeData object.
For any feeTier value that is not one of the standard V3 fee amounts present in
`TICK_SPACINGS` (1, 200, 300, 400, 500, 3000, 10000), the previous logic
silently fell back to `TICK_SPACINGS[FeeAmount.MEDIUM]` (60):
const tickSpacing = TICK_SPACINGS[feeTierNumber as FeeAmount] || TICK_SPACINGS[FeeAmount.MEDIUM]
This caused legacy links pointing at custom V4 fee tiers (e.g. `feeTier=1000`,
common for dynamic-fee hooks on Base) to land on the create-position page with
a tickSpacing of 60 instead of the actual pool tickSpacing (20 for fee=1000),
fracturing liquidity into a parallel non-existent pool. The issue is reported
in Uniswap#7939 and reproduces in the deprecated URL format used by
Uniswap#7940.
The fix uses the same formula already adopted elsewhere in the codebase
(`calculateTickSpacingFromFeeAmount` in features/Liquidity/utils/feeTiers.ts):
const tickSpacing =
TICK_SPACINGS[feeTierNumber as FeeAmount] ??
calculateTickSpacingFromFeeAmount(feeTierNumber)
For every standard fee tier the lookup short-circuits, so behavior is
unchanged. For custom fee tiers the SDK-canonical mathematical default is
returned instead of MEDIUM, matching what `FeeTierSearchModal` and
`usePoolData` use when constructing the same FeeData object from a custom
fee amount.
Adds apps/web/src/features/Liquidity/parsers/migrations.test.ts covering:
- All 7 standard V3 fee tiers (regression guard)
- Custom V4 fee tiers (1000, 1500, 2000, 2500, 4000, 7500, 250, 333, 50, 1)
- The specific regression: feeTier=1000 must not coerce to tickSpacing=60
- isDynamic propagation, missing isDynamic, empty feeTier, currency migrations
- Combined fee + currency migrations with deduplicated clearParams
Fixes Uniswap#7939
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
apps/web/src/features/Liquidity/parsers/migrations.tssilently coerces any unrecognized legacyfeeTierURL parameter totickSpacing=60, so historic links and bookmarks for custom V4 fee tiers (e.g.feeTier=1000on Base) route users to a non-existent parallel pool when they click "Add Liquidity". This PR replaces the wrong fallback with the SDK-canonical formula already used elsewhere in the codebase, and adds full coverage for both the regression and the existing standard-tier behavior.Root cause
When the URL parameter format changed (
feeTier=X&isDynamic=Y→fee={...}), a V1 migration was added to convert the deprecated parameters into the newfeeobject. The original migration code:TICK_SPACINGSfrom@uniswap/v3-sdkonly contains entries for the seven standard V3 fee amounts:LOWEST (100),LOW_200 (200),LOW_300 (300),LOW_400 (400),LOW (500),MEDIUM (3000),HIGH (10000).feeAmount— which is the entire space of custom V4 fee tiers and hook-based pools — the lookup returnsundefined, and the||fallback collapses toTICK_SPACINGS[FeeAmount.MEDIUM] = 60.Concrete user impact (from #7939 / #7940 reproductions on Base):
tickSpacingfeeTier=1000feeTier=1000feeTier=2500Because tick spacing is part of the V4 pool key, this misrouted users into a parallel non-existent pool (or, for the V3-style "fee + tickSpacing" pair, broke range validation downstream).
Fix
Use the same
calculateTickSpacingFromFeeAmounthelper thatFeeTierSearchModal.tsx,feeTiers.test.ts, and the wider liquidity feature already use as the SDK-canonical mapping from a fee amount to its default tick spacing:Behavioral characteristics:
Standard V3 fee tiers — the lookup succeeds, the helper is never called, behavior is byte-identical to before.
Custom fee tiers — the same formula already shipped in
apps/web/src/features/Liquidity/utils/feeTiers.tsis used, matching whatFeeTierSearchModalinjects when a user creates a new fee tier:So a legacy URL with
feeTier=1000now producestickSpacing=20, matching what the same fee amount would produce when typed into the fee-tier search modal.The
??(nullish coalescing) instead of||is intentional:TICK_SPACINGS[FeeAmount.LOWEST] === 1, which is falsy under||(it isn't — 1 is truthy — but the same module also relies on this distinction for theFeeAmount.LOW_200andLOW_300cases that historically were 2/3 in older v3-sdk versions).??is more defensible against future SDK changes.Test coverage
Adds
apps/web/src/features/Liquidity/parsers/migrations.test.ts(new file, 27 tests) covering:TICK_SPACINGS[feeAmount].feeTier=1000 → tickSpacing=20reproduction from the issue.expect(result?.updatedParams.fee?.tickSpacing).not.toBe(60)forfeeTier=1000.isDynamicflag handling — preserved for both standard and custom tiers, including the legacy URL case where the flag is omitted.feeTierreturnsnull(no migration emitted).migrateCurrencycoverage — lowercasecurrencya/currencybmigration paths, including the "do not overwrite when canonical key is already set" rule.clearParams.Run output:
Existing
feeTiers.test.ts(21 tests) continues to pass, confirming no impact on the sharedcalculateTickSpacingFromFeeAmounthelper or its callers. The existing migration test insideuseLiquidityUrlState.test.ts(handles feeTier and isDynamic, line 554) usesfeeTier='500'(a standard tier), so the new code path returns 10 just like before and that test still passes.Notes
migrations.tsexplains the SDK-formula choice and the historical fallback for future readers.??chosen over||deliberately to avoid masking a legitimatetickSpacingof0if a future SDK change ever produces one.Fixes #7939