clmm_swap_x96

Exact swap across initialized ticks, as the Uniswap v3 and v4 swap loop: each step ends at the next listed tick or the tick-bitmap word edge (256 x tick_spacing), whichever is first, with clmm_swap_step_x96 math; crossing adds liquidity_net moving up, subtracts it moving down. Stops at the amount, price limit or list end; never assumes liquidity past listed ticks. Offline: caller reads state and ticks. No hooks, dynamic fees or protocol fee split. List sorted once per call.

Example

Runs locally

-- Sell 100 WETH (currency1) into a USDC/WETH 0.05% pool state with its next initialized ticks
WITH s AS (SELECT clmm_swap_x96('1617879584705704368092559623168785'::UINT256, 198495, 10, 5100467488167303535::UINT256,
  [{'tick': 198500, 'liquidity_net': 3690971406824959::HUGEINT}, {'tick': 198510, 'liquidity_net': -12894930357576102::HUGEINT},
   {'tick': 198520, 'liquidity_net': -139898289234390568::HUGEINT}],
  -(parse_units('100', 18)::INT256), 500::UBIGINT, false) AS swap)
SELECT format_units((swap).amount_out, 6) AS usdc_out, (swap).tick AS tick_after, (swap).ticks_crossed AS crossed,
  (swap).complete::VARCHAR AS complete FROM s;
-- => [{"usdc_out":"239460.080613","tick_after":198515,"crossed":2,"complete":"true"}]

API reference

clmm_swap_x96(UINT256, INTEGER, INTEGER, UINT256, STRUCT(tick INTEGER, liquidity_net HUGEINT)[], INT256, UBIGINT, BOOLEAN) #

Exact swap across initialized ticks, as the Uniswap v3 and v4 swap loop: each step ends at the next listed tick or the tick-bitmap word edge (256 x tick_spacing), whichever is first, with clmm_swap_step_x96 math; crossing adds liquidity_net moving up, subtracts it moving down. Stops at the amount, price limit or list end; never assumes liquidity past listed ticks. Offline: caller reads state and ticks. No hooks, dynamic fees or protocol fee split. List sorted once per call.

Inputs

NameTypeUse
sqrt_price_x96UINT256

UINT256 slot0 sqrtPriceX96.

requiredpositional
tickINTEGER

INTEGER slot0 tick: the price's tick, or one below when the price sits on a tick; anything else errors.

requiredpositional
tick_spacingINTEGER

INTEGER pool tick spacing in [1, 32767]; listed ticks must be its multiples.

requiredpositional
liquidityUINT256

UINT256 in-range liquidity, at most 2^128 - 1.

requiredpositional
ticksSTRUCT(tick INTEGER, liquidity_net HUGEINT)[]

STRUCT(tick INTEGER, liquidity_net HUGEINT)[], any order, distinct ticks: initialized ticks the swap may cross, e.g. from tick bitmap set bits and ticks(t). Swap never runs past the last one.

requiredpositional

Returns

Name Type
swap STRUCT(amount_in UINT256, amount_out UINT256, fee_amount UINT256, sqrt_price_x96 UINT256, tick INTEGER, liquidity UINT256, ticks_crossed INTEGER, complete BOOLEAN)

Multi-tick swap. STRUCT, raw units: amount_in includes fee_amount; amount_out; sqrt_price_x96, tick, liquidity after; ticks_crossed counts listed ticks crossed; complete false when the list ran out before the amount or limit. On a last partial exact-input step fee_amount splits as v4 SwapMath; v3 can count a few more units as fee, with the same amount_in. NULL input returns NULL.

clmm_swap_x96(UINT256, INTEGER, INTEGER, UINT256, STRUCT(tick INTEGER, liquidity_net HUGEINT)[], INT256, UBIGINT, BOOLEAN, UINT256) #

Exact swap across initialized ticks, as the Uniswap v3 and v4 swap loop: each step ends at the next listed tick or the tick-bitmap word edge (256 x tick_spacing), whichever is first, with clmm_swap_step_x96 math; crossing adds liquidity_net moving up, subtracts it moving down. Stops at the amount, price limit or list end; never assumes liquidity past listed ticks. Offline: caller reads state and ticks. No hooks, dynamic fees or protocol fee split. List sorted once per call. Limit form gives depth to a price: amount_in - fee_amount is fee-free.

Inputs

NameTypeUse
sqrt_price_x96UINT256

UINT256 slot0 sqrtPriceX96.

requiredpositional
tickINTEGER

INTEGER slot0 tick: the price's tick, or one below when the price sits on a tick; anything else errors.

requiredpositional
tick_spacingINTEGER

INTEGER pool tick spacing in [1, 32767]; listed ticks must be its multiples.

requiredpositional
liquidityUINT256

UINT256 in-range liquidity, at most 2^128 - 1.

requiredpositional
ticksSTRUCT(tick INTEGER, liquidity_net HUGEINT)[]

STRUCT(tick INTEGER, liquidity_net HUGEINT)[], any order, distinct ticks: initialized ticks the swap may cross, e.g. from tick bitmap set bits and ticks(t). Swap never runs past the last one.

requiredpositional

Returns

Name Type
swap STRUCT(amount_in UINT256, amount_out UINT256, fee_amount UINT256, sqrt_price_x96 UINT256, tick INTEGER, liquidity UINT256, ticks_crossed INTEGER, complete BOOLEAN)

Multi-tick swap. STRUCT, raw units: amount_in includes fee_amount; amount_out; sqrt_price_x96, tick, liquidity after; ticks_crossed counts listed ticks crossed; complete false when the list ran out before the amount or limit. On a last partial exact-input step fee_amount splits as v4 SwapMath; v3 can count a few more units as fee, with the same amount_in. NULL input returns NULL.

Overload examples

Runs locally

-- WETH to move the same pool state's price up to tick 198510: a large exact input stopped by a price limit
WITH s AS (SELECT clmm_swap_x96('1617879584705704368092559623168785'::UINT256, 198495, 10, 5100467488167303535::UINT256,
  [{'tick': 198500, 'liquidity_net': 3690971406824959::HUGEINT}, {'tick': 198510, 'liquidity_net': -12894930357576102::HUGEINT},
   {'tick': 198520, 'liquidity_net': -139898289234390568::HUGEINT}],
  -(parse_units('1000', 18)::INT256), 500::UBIGINT, false, clmm_tick_to_sqrt_price_x96(198510)) AS swap)
SELECT format_units((swap).amount_in, 18) AS weth_in, format_units((swap).fee_amount, 18) AS weth_fee,
  (swap).complete::VARCHAR AS complete FROM s;
-- => [{"weth_in":"73.907515200447971145","weth_fee":"0.036953757600223987","complete":"true"}]

Guidance

Uniswap v4-compatible local math boundary

These generic helpers follow Uniswap v4 core TickMath, SqrtPriceMath, SwapMath, and TickBitmap conventions. Q64.96 square-root price means sqrt(currency1 / currency0) * 2^96, using raw token units.

  • Use them as deterministic evidence about one hydrated pool state, price range, bitmap word, or swap step.
  • They do not fetch state or bitmap words, resolve dynamic fees or hooks, apply slippage policy, select routes, produce calldata, or return an executable quote. clmm_swap_x96 crosses only the initialized ticks the caller lists.
  • Pin onchain reads before use. The six-argument swap-step fee denominator and the bitmap result diagnostics are Determica extensions, not Uniswap v4 interfaces.

Runs locally

WITH bounds AS (
  SELECT
    clmm_tick_to_sqrt_price_x96(-60) AS sqrt_price_a_x96,
    clmm_tick_to_sqrt_price_x96(60) AS sqrt_price_b_x96
)
SELECT clmm_amount0_delta_x96(
  sqrt_price_a_x96,
  sqrt_price_b_x96,
  1000000::UINT256,
  false
) AS amount0
FROM bounds;