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:
a2b
–true
to swap A for B,false
for B → A.fixed_amount_in
– indicates whetheramount
is the exact input or the desired output.amount
– amount being swapped.amount_limit
– slippage limit depending onfixed_amount_in
.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)
andreserve_b(address)
current_tick_idx(address)
fee_rate(address)
get_positions(address)
andget_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