CLMM pools

Tapp's Concentrated Liquidity Market Maker (CLMM) is an advanced pool type modelled on Uniswap v3. Liquidity providers choose a price range (a pair of "ticks") and only trade within that range earns fees. The logic lives in the clmm::clmm Move module.

This document walks through the main data types and entry functions so integrators can understand how CLMM pools operate.

Key data types

Config

  • authority – address allowed to manage global settings.

  • pending_authority – temporary storage when transferring authority.

  • fee_amount_tick_spacing – mapping from fee tier to tick spacing.

Pool

A pool tracks its price and liquidity discretely across ticks:

struct Pool has key, store {
    pool_addr: address,
    asset_a: address,
    asset_b: address,
    tick_spacing: u64,
    fee_rate: u64,
    reserve_a: u64,
    reserve_b: u64,
    liquidity: u128,
    current_sqrt_price: u128,
    current_tick_index: I64,
    fee_growth_global_a: u128,
    fee_growth_global_b: u128,
    tick_indexes: SimpleMap<u64, BitVector>,
    ticks: BigOrderedMap<I64, Tick>,
    positions: BigOrderedMap<u64, Position>,
    position_index: u64,
}

Position

Holds the liquidity amount supplied between a lower and upper tick index and the fees owed to that position.

Tick

Represents a single price step. Each tick stores the sqrt_price, liquidity net values when crossing the tick and fee growth data used for fee accounting.

Creating a pool

create_pool(pool_signer, assets, fee, rest_args, creator) deploys a new pool resource. assets contains the two coin types; fee must match a supported fee tier. rest_args encodes the initial square‑root price. The tick spacing is looked up from fee_amount_tick_spacing.

A PoolCreated event records the pool address and initial parameters.

Adding liquidity

Liquidity can be added in two ways:

  • add_liquidity(pool_signer, position_idx, stream, creator)stream encodesdelta_liquidity, max_amount_a, max_amount_b, lower_tick_idx andupper_tick_idx.

  • add_liquidity_fix_token(pool_signer, position_idx, stream, creator) – the stream encodes a fixed amount of A or B to deposit and whether the amount is in token A or token B.

If position_idx is none, the function opens a new position via open_position. Otherwise it modifies the existing position. The pool calculates how much of each token must be deposited based on the current price and tick range, updates liquidity and emits LiquidityAdded.

Returned values are the actual token amounts deposited and optionally the newly created position index.

Removing liquidity

remove_liquidity(pool_signer, position_idx, stream, creator) burns part of a position's liquidity. The stream provides delta_liquidity, min_amount_a andmin_amount_b. Fees earned by the position are first collected withinternal_collect_fee. If after removing liquidity the position balance reaches zero it is closed. A LiquidityRemoved event logs the withdrawal.

The function returns the withdrawn token amounts (including fees) and optionally the index of any position that was burned.

Swapping tokens

swap(pool_signer, stream, creator) performs trades against the pool. The stream encodes:

  1. a2btrue to swap A for B, false for B → A.

  2. fixed_amount_in – indicates whether amount is the exact input or the desired output.

  3. amount – amount being swapped.

  4. amount_limit – slippage limit depending on fixed_amount_in.

  5. sqrt_price – limit price expressed as the square‑root price X64.

The pool iterates across ticks using swap_in_pool, updating the current price and liquidity along the path. A Swapped event contains the final amounts and post‑swap price.

Fees

Every swap accrues fees in proportion to the traded amount. Global accumulatorsfee_growth_global_a and fee_growth_global_b track the total fee growth per unit of liquidity. When adding or removing liquidity the pool updates a position's fee data so providers can later claim their earned fees viacollect_fee.

Utility functions

The module exposes several helpers for off‑chain queries:

  • current_sqrt_price(address)

  • liquidity(address)

  • reserve_a(address) and reserve_b(address)

  • current_tick_idx(address)

  • fee_rate(address)

  • get_positions(address) and get_position(address, index)

  • calculate_swap_result(address, a2b, fixed_amount_in, amount)

These assist front‑ends in showing pool state and estimating trade outcomes.

Events

CLMM emits many events to make indexing simple:

  • PoolCreated

  • LiquidityAdded

  • LiquidityRemoved

  • Swapped

  • OpenPositionEvent

  • ClosePositionEvent

  • CollectFeeEvent

  • CollectProtocolFeeEvent

Security checks

The Move code contains numerous assertions that enforce valid tick ranges, prevent overflows when updating liquidity and fees, and ensure the swap logic cannot violate pool invariants. Transactions that violate these conditions abort, protecting both LPs and traders.


Concentrated liquidity pools provide fine‑grained control over where liquidity is active and can achieve greater capital efficiency than constant product pools. Understanding the module internals helps builders integrate with Tapp's CLMM and visualise liquidity positions accurately.

Last updated