For the complete documentation index, see llms.txt. This page is also available as Markdown.

Contract interface reference

Full reference of the concentrated liquidity pool smart contract interface: position management, state getters, fees, swaps and rewards.

This page is the canonical public reference for the concentrated liquidity pool contract interface. Functions are grouped by where they live: the router (shared Aquarius entry point) and the pool contract (one per concentrated pool). Signatures are given in Rust/Soroban notation; the e: Env argument is implicit in every Soroban contract call and is not passed by the client.

Read-only getters and estimate_* functions don't change state — call them through transaction simulation, no signature or fees required. Estimates enforce the same validation as the real calls: on invalid input the simulation fails with the corresponding error code (for example, PositionNotFound, AllCoinsRequired, OutMinNotSatisfied when amounts round to zero liquidity) instead of returning a value — handle simulation errors accordingly.

Router functions

The router address is listed in Prerequisites & basics. Token vectors must always be sorted by contract ID.

init_concentrated_pool

Creates a concentrated pool for a token pair, or returns the existing one for that fee tier.

fn init_concentrated_pool(
    e: Env,
    user: Address,
    tokens: Vec<Address>,
    fee: u32,
) -> (BytesN<32>, Address);
  • user — the account creating the pool (pays the pool creation fee in AQUA if one is configured).

  • tokens — the two token addresses, sorted.

  • fee — one of 10 (0.1%), 30 (0.3%), 100 (1.0%). Tick spacing is derived automatically: 20, 60 or 200 respectively.

Returns the pool index hash and the pool contract address.

Pool discovery

get_pools returns all pools for a pair (all types). Concentrated pools report pool_type: "concentrated" in get_info, along with fee and tick_spacing. See the Get pools info example.

Router-level deposit, withdraw, swaps, rewards

The generic router functions documented in Router & pool contractsdeposit, withdraw, swap_chained, claim — all work with concentrated pools. A router deposit into a concentrated pool opens a full-range position (see position model). For ranged positions, call the pool contract directly.

Pool contract: position management

deposit_position

Opens a new position or adds liquidity to an existing one with the same range.

  • sender — position owner; authorizes the token transfers.

  • tick_lower, tick_upper — range boundaries; must be multiples of the pool's tick spacing, tick_lower < tick_upper, within ±887272.

  • desired_amounts[amount0, amount1] in raw units (token order = sorted order). These are maximums you authorize, not exact spends: the contract computes the maximum liquidity purchasable at the current price, pulls what it needs and refunds the excess within the same transaction.

  • min_liquidity — slippage guard on liquidity units (not token amounts): the transaction fails with OutMinNotSatisfied if the minted liquidity is below this value.

Returns (actual_amounts, minted_liquidity) — the amounts actually spent and the liquidity minted.

Special cases:

  • If the current price is outside the range, only one token is required — pass 0 for the other. Which one depends on the side: a range entirely above the current price takes only token0, entirely below — only token1. Passing only the wrong-side token yields zero liquidity and fails with OutMinNotSatisfied.

  • The first deposit into an empty pool initializes the pool price from amount1 / amount0. It must include both tokens (AllCoinsRequired otherwise), and the tick derived from that ratio must fall inside the deposit range (TickOutOfBounds otherwise).

estimate_deposit_position

Simulation counterpart of deposit_position — same math, no transfers.

withdraw_position

Burns liquidity from a position and transfers principal plus all accrued swap fees to the owner. This applies to partial withdrawals too: any withdraw_position call, regardless of amount, always sweeps the position's entire accrued fee balance along with the withdrawn principal — there is no way to withdraw principal while leaving fees unclaimed. Withdrawing the full liquidity closes the position.

  • amount — liquidity to burn (up to the position's total; see get_position).

  • min_amounts[min0, min1] slippage guards checked against the total payout (principal + fees).

Returns the paid-out token amounts. Withdrawals are always available — this function has no kill-switch.

estimate_withdraw_position

Like withdraw_position, the returned amounts include the position's accrued fees on top of the principal.

Pool contract: swap fees

All return [amount0, amount1]. Claiming emits a claim_fees event with the owner and token addresses as topics.

Pool contract: state getters

Relevant types:

tokens_owed_* in get_position only reflects fees settled during past interactions with the position. For the live pending total, use get_position_fees.

Enumerating initialized ticks

To reconstruct the pool's liquidity distribution (for example, for a chart), you need every initialized tick — a tick referenced by at least one position boundary (liquidity_gross > 0 in TickInfo). Two strategies:

  • Simple: call get_tick_bounds and scan get_ticks_batch over multiples of the tick spacing between the bounds. Fine for pools with a modest active range.

  • Sparse (bitmap): ticks are indexed in a two-level bitmap. Ticks are grouped into chunks of 16: chunk_pos = floor((tick / tick_spacing) / 16). Each bit of a 256-bit word flags a chunk containing at least one initialized tick: word_pos = chunk_pos >> 8, bit index chunk_pos & 255 (bit 0 = least significant). Scan words with get_chunk_bitmap_batch, then read only the flagged chunks' ticks via get_ticks_batch.

Given active liquidity at the current tick (get_active_liquidity) and the liquidity_net deltas of initialized ticks, liquidity at any tick is reconstructed by walking outward from the current tick and applying the deltas: add liquidity_net when crossing a tick left-to-right, subtract when right-to-left.

Pool contract: swaps

Concentrated pools implement the standard Aquarius pool swap interface, so direct-pool swaps work exactly like the Executing swaps through specific pool example:

in_idx/out_idx are the token indices in the sorted pair (0 or 1).

Pool contract: AQUA rewards

Remember: positions accrue rewards only while in rangetick_lower <= current_tick < tick_upper — and the in-range check is checkpoint-based: the reward weight is snapshotted when the owner interacts with the pool (deposit, withdraw, claim) and is not re-evaluated between interactions as the price moves. A position that left its range keeps accruing at the stale weight until the owner's next action, and one that re-entered its range earns nothing until then — perform an action (for example, claim) after the price crosses a range boundary to re-sync. Note: this checkpoint behavior is specific to the current contract version and may change in future versions.

The same operations are available through the router with (tokens, pool_index) arguments. Calling claim with nothing accrued succeeds and pays zero — it is a no-op, not an error. Reward claiming can be temporarily paused by protocol admins in emergencies; principal withdrawal can not.

Events

Events emitted by the pool contract, for indexers and monitoring (topics listed first, then the data payload):

Event
Topics (after the event name)
Data

deposit_liquidity

token0, token1

liquidity, amount0, amount1

withdraw_liquidity

token0, token1

liquidity, amount0, amount1

position_update

user

tick_lower, tick_upper, liquidity_delta

pool_state

sqrt_price_x96, tick, active_liquidity

claim_fees

owner, token0, token1

amount0, amount1

claim_reward

reward_token, user

amount

trade

token_in, token_out, trader

sold_amount, bought_amount, fee

update_reserves

reserve0, reserve1

Correlation notes for indexers: a swap emits trade, update_reserves and pool_state together; withdraw_position additionally emits claim_fees when it auto-sweeps the position's accrued fees.

Common errors

Code
Error
Meaning

201

PoolAlreadyInitialized

Pool init attempted twice

205

DepositKilled

Deposits are paused by the protocol

2002

TokensNotSorted

Token vector not sorted by contract ID

2101

InvalidTickRange

Malformed tick range

2107

TickOutOfBounds

Tick outside allowed bounds (for example, first-deposit price outside the deposit range)

2109

TickNotSpacedCorrectly

Boundary is not a multiple of the tick spacing

2110

TickLowerNotLessThanUpper

tick_lower >= tick_upper

2111

TickLowerTooLow

Below MIN_TICK

2112

TickUpperTooHigh

Above MAX_TICK

2118

PositionNotFound

No position for (owner, range)

2119

TooManyPositions

More than 20 positions per user per pool

2121

InsufficientLiquidity

Withdraw amount exceeds position liquidity; or the pool cannot fill a swap (exact-output cannot produce out_amount, exact-input cannot move at all)

Generic validation errors (zero amounts, slippage guards like OutMinNotSatisfied) are shared with other Aquarius pool types.

Last updated