# 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.

Canonical HTML: <https://determica.com/docs/functions/clmm_swap_x96>

- Kind: scalar function
- Category: [Fixed-point and AMM math](/docs/category/fixed-point-and-amm-math.md)
- Tags: Clmm Math, Deterministic, Offline, Types

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.

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

```sql
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.

- Kind: scalar
- Execution context: Offline
- Behavior: Transform
- Execution modes: Offline
- Input shape: Scalar Arguments
- Planning contract: Runtime
- Cardinality: One Per Input
- Determinism: Deterministic
- Return shape: Scalar
- Schema stability: Fixed
- Stability: Stable
- Side effects: None documented

### Inputs

- `sqrt_price_x96` (UINT256; required; positional) — UINT256 slot0 sqrtPriceX96.
- `tick` (INTEGER; required; positional) — INTEGER slot0 tick: the price's tick, or one below when the price sits on a tick; anything else errors.
- `tick_spacing` (INTEGER; required; positional) — INTEGER pool tick spacing in [1, 32767]; listed ticks must be its multiples.
- `liquidity` (UINT256; required; positional) — UINT256 in-range liquidity, at most 2^128 - 1.
- `ticks` (STRUCT(tick INTEGER, liquidity_net HUGEINT)[]; required; positional) — 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.
- `amount_specified` (INT256; required; positional) — INT256 raw units: negative is exact input, positive exact output.
- `fee_pips` (UBIGINT; required; positional) — UBIGINT fee per 1,000,000 (500 = 0.05%); below 1,000,000 for exact output. For v4 with a protocol fee, pass the Swap event's fee (LP and protocol fee together).
- `zero_for_one` (BOOLEAN; required; positional) — BOOLEAN true sells currency0, lowers price; false sells currency1.

### Returns

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.

- `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.

### Examples

_Local SQL · Local_

```sql
-- 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"}]
```

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

```sql
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.

- Kind: scalar
- Execution context: Offline
- Behavior: Transform
- Execution modes: Offline
- Input shape: Scalar Arguments
- Planning contract: Runtime
- Cardinality: One Per Input
- Determinism: Deterministic
- Return shape: Scalar
- Schema stability: Fixed
- Stability: Stable
- Side effects: None documented

### Inputs

- `sqrt_price_x96` (UINT256; required; positional) — UINT256 slot0 sqrtPriceX96.
- `tick` (INTEGER; required; positional) — INTEGER slot0 tick: the price's tick, or one below when the price sits on a tick; anything else errors.
- `tick_spacing` (INTEGER; required; positional) — INTEGER pool tick spacing in [1, 32767]; listed ticks must be its multiples.
- `liquidity` (UINT256; required; positional) — UINT256 in-range liquidity, at most 2^128 - 1.
- `ticks` (STRUCT(tick INTEGER, liquidity_net HUGEINT)[]; required; positional) — 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.
- `amount_specified` (INT256; required; positional) — INT256 raw units: negative is exact input, positive exact output.
- `fee_pips` (UBIGINT; required; positional) — UBIGINT fee per 1,000,000 (500 = 0.05%); below 1,000,000 for exact output. For v4 with a protocol fee, pass the Swap event's fee (LP and protocol fee together).
- `zero_for_one` (BOOLEAN; required; positional) — BOOLEAN true sells currency0, lowers price; false sells currency1.
- `sqrt_price_limit_x96` (UINT256; required; positional) — UINT256 Q64.96 stop price: below current when zero_for_one, else above; strictly inside TickMath bounds.

### Returns

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.

- `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.

### Examples

_Local SQL · Local_

```sql
-- 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.

```sql
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;
```

## Related functions

- [clmm_swap_step_x96](/docs/functions/clmm_swap_step_x96.md) — See Also
- [evm_set_bits](/docs/functions/evm_set_bits.md) — Usually Before
- [clmm_tick_to_sqrt_price_x96](/docs/functions/clmm_tick_to_sqrt_price_x96.md) — Usually Before
- [format_units](/docs/functions/format_units.md) — Usually After
