Managing positions: code examples
Code examples for the full concentrated liquidity position lifecycle: reading pool state, opening a position, monitoring it, claiming fees and rewards, and withdrawing.
This page walks through the full lifecycle of a concentrated liquidity position with the official SDKs (0.3.0 or later). Amounts and liquidity always come from the contract's own estimators — the SDK converts prices to ticks with exact integer math and derives every slippage guard from a quote, never from client-side arithmetic.
Step 1. Set up and find a concentrated pool
from stellar_sdk import Keypair
from aquarius import AquariusClient, Asset, XLM
AQUA = Asset.classic("AQUA", "GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA")
aqua = AquariusClient(network="mainnet", signer=Keypair.from_secret("S..."))
pool = next(p for p in aqua.pools_for_pair(XLM, AQUA) if p.type == "concentrated")
print(pool.slot0()) # Slot0(sqrt_price_x96=..., tick=...)
print(pool.tick_spacing()) # 20 (0.1% pool), 60 (0.3%) or 200 (1.0%)import { Keypair } from "@stellar/stellar-sdk";
import { AquariusClient, Asset, XLM } from "@aquariusdefi/sdk";
const AQUA = Asset.classic("AQUA", "GBNZILSTVQZ4R7IKQDGHYGY2QXL5QOFJYQMXPKWRRM5PAV7Y4M67AQUA");
const aqua = new AquariusClient({ network: "mainnet", signer: Keypair.fromSecret("S...") });
const pool = (await aqua.pools.forPair(XLM, AQUA)).find(p => p.type === "concentrated");
console.log(await pool.slot0()); // { sqrtPriceX96: ..., tick: ... }
console.log(await pool.tickSpacing()); // 20 (0.1% pool), 60 (0.3%) or 200 (1.0%)Step 2. Convert your price range to ticks
Prices are token1 per token0 in base units. tick_from_price is integer-exact and identical across both SDKs; snap_tick aligns a tick to the pool's spacing. Passing price_range to the estimator in the next step performs both conversions for you:
from aquarius import price_at_tick, snap_tick, tick_from_price
spacing = pool.tick_spacing()
tick = pool.current_tick()
# Range: 5% below to 5% above the current price
lower = snap_tick(tick_from_price(price_at_tick(tick) * 0.95), spacing)
upper = snap_tick(tick_from_price(price_at_tick(tick) * 1.05), spacing, up=True)import { priceAtTick, snapTick, tickFromPrice } from "@aquariusdefi/sdk";
const spacing = await pool.tickSpacing();
const tick = await pool.currentTick();
// Range: 5% below to 5% above the current price
const lower = snapTick(tickFromPrice(priceAtTick(tick) * 0.95), spacing);
const upper = snapTick(tickFromPrice(priceAtTick(tick) * 1.05), spacing, { up: true });Step 3. Estimate and open the position
Deposits are two-phase: estimate_position_deposit quotes through the contract's own estimator, then execute runs the deposit with a minimum-liquidity guard derived from that quote. The contract pulls the desired amounts and refunds whatever the range does not need:
A position is the key (owner, tick_lower, tick_upper) — no NFTs, merged on re-deposit, at most 20 ranges per account.
Step 4. List your positions and check if they are in range
position_range_status matches the contract's active-liquidity rule exactly: the range is half-open [tick_lower, tick_upper), so a position sitting at tick_upper is already out and earning nothing. "Below" means the price is under the range and the position sits entirely in the first sorted token; "above" — entirely in the second:
Step 5. Check and claim swap fees
Fees accrue per position while it is in range. Withdrawals auto-claim the position's fees, so explicit claims are for harvesting without touching the principal:
Step 6. Claim AQUA rewards
Reward accrual and claims work the same as for every other pool type:
Step 7. Withdraw the position
Partial or full — pass the liquidity to remove, or nothing for the whole position. Per-token minimums come from a simulation of this exact withdrawal, reduced by slippage; the payout includes the position's accrued fees:
Contract-level scripts (any language)
The scripts below run the same lifecycle against the pool contract directly, with the simulate/invoke helpers spelled out — the reference for languages without an official SDK. Provide the secret key of a funded account and the address of a concentrated pool.
Last updated