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

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.

The contract-level scripts at the bottom cover other languages; per-function signatures live in the contract interface reference. The concentrated surface is beta: the contracts are under an external audit.

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.

Copy the full code Python
Copy the full code JavaScript

Last updated