Crossing condition
The contract defines a crossing as:sqrt_price_limit.
The target is already clamped to the price limit:
- A limit before the next initialized tick stops inside the range and does not cross.
- A limit exactly at the next initialized tick crosses if the step reaches it.
- A limit beyond the next initialized tick allows the same crossing and continued traversal, subject to remaining input and iteration count.
State inconsistency without equality crossing
Suppose the crossing predicate instead required:[lower, T) and an upward one_for_zero swap that reaches T exactly at its price limit.
The resulting state could be:
liquidity_net was not applied. This is ghost active liquidity.
If the LP then removed the full position, the normal range check would see current_tick == tick_upper and remove zero from pool liquidity. Once the boundary ticks were unlinked, the phantom amount could remain without a position backing it.
Half-open position ranges
The active-range rule is:- A position is active at its lower tick.
- A position is inactive at its upper tick.
- Moving upward across an upper tick removes that range’s liquidity.
- Moving downward across a lower tick removes that range’s liquidity in the opposite direction.
State transition by direction
LetT be the crossed initialized tick and net be its signed liquidity_net.
Upward, one_for_zero
Downward, zero_for_one
T - 1 convention represents the state immediately below the crossed tick.
Fee growth at the boundary
The crossing flips both token fee-growth-outside accumulators:u128 limbs. The step’s LP fee is added to the applicable global accumulator before the flip.
This ordering assigns the boundary step to the liquidity that was active while the price moved to the tick. The next step begins with the post-cross active set.
The transition combines exact-limit crossing with the Q128 fee-growth layout. Clients must use the two-limb Q128 fields when applying the crossing update.
Price-limit preservation
The crossing updates tick, liquidity, pointers, and fee-growth state while preserving the caller’s price cap. After exact-limit arrival, the next iteration sees a next-tick target that is clamped back to the same current price. Its price interval is zero, so it cannot consume additional swap input or produce output beyond the limit. Completing the crossing does not change these boundary-step values:- End square-root price
- Step input consumed
- Step output
- Step fee
- Unspent input at the limit
Quoter state update
An off-chain simulator should persist all post-cross fields even when the user limit equals the tick price:- Current tick
- Active liquidity
- Next initialized tick below
- Next initialized tick above
- Both fee-growth-outside values on the crossed tick
- Applicable global Q128 fee growth
- Protocol-fee accumulator
Edge cases
Insufficient input
If the step stops before the initialized tick,new_sqrt_price != target_tick_sqrt_price, so there is no crossing.