Version banner. All TypeScript demos target
@raydium-io/raydium-sdk-v2@0.2.64-alpha; they were last executed against 0.2.42-alpha (2026-04) and their call signatures re-checked against the 0.2.64-alpha source on 2026-09-09, against Solana mainnet-beta. The Rust CPI skeleton at the end targets raydium-clmm on the chore/upgrade-anchor branch, Anchor 1.0.2, the same pin as the CPMM page, so the two can live in one crate. master still pins 0.32.1. Program IDs come from reference/program-addresses via the SDK.Setup
raydium-sdk-V2-demo/src/clmm; the GitHub link sits next to each section. Bootstrap follows the demo repo’s config.ts.template (source) — disableFeatureCheck: true is the recommended setting for any non-trivial integration:
Create a CLMM pool
Source:src/clmm/createPool.ts
- Sorts
mint1/mint2by byte order before derivation. - Computes
sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64). - Creates the
observationandtick_array_bitmap_extensionaccounts. - Pays the pool-creation fee defined by
ammConfig.
Open a position in a chosen range
Source:src/clmm/createPosition.ts
OpenPosition* allocates a missing tick array itself, at the payer’s expense.
Increase liquidity on an existing position
Source:src/clmm/increaseLiquidity.ts
Decrease liquidity (and collect fees at the same time)
Source:src/clmm/decreaseLiquidity.ts and src/clmm/closePosition.ts
decreaseLiquidity with liquidity = new BN(0). The instruction’s side-effect is settling token_fees_owed_{0,1} and reward_amount_owed and transferring them out — this is the only way to collect either.
To close the position entirely after zeroing liquidity and fees, pass ownerInfo: { closePosition: true } on the final decreaseLiquidity call. The SDK appends ClosePosition and burns the NFT.
For a direct Anchor client, keep the declared accounts unchanged and append the pool:
poolId on every close. CLMM reads it only when positionNftAccount is frozen, which keeps one client path compatible with old and new positions.
Collect reward(s)
Source:src/clmm/harvestAllRewards.ts
harvestAllRewards walks every position on every pool passed in, batches the zero-liquidity DecreaseLiquidity calls that settle fees and rewards (plus any UpdateRewardInfos), and splits them across transactions if needed.
Swap
Source:src/clmm/swap.ts
amountCalculated) plus the exact account list the swap will touch (accounts).
Always pass the remainingAccounts the simulation returns: too few and the swap reverts mid-walk with NotEnoughTickArrayAccount; stale ones just waste compute.
PoolUtils.computeAmountOutFormat still exists, but it needs a ComputeClmmPoolInfo (the
computePoolInfo from getPoolInfoFromRpc, not an API pool object) plus two more required
arguments — tickarrayBitmapExtension and blockTimestamp — and there is no
raydium.clmm.fetchTickArrays method (fetchTickArrays is a free function; the module-level
helpers are PoolUtils.fetchMultiplePoolTickArrays and the tickData / tickArrays returned by
getPoolInfoFromRpc).Create a customizable CLMM pool
createCustomizablePool is the entry point that exposes the dynamic-fee and single-sided-fee toggles at pool-creation time. It takes createPool’s shape plus two additions:
createPool continues to work for the default-fee, no-dynamic-fee path. Use createCustomizablePool whenever you need either knob. See products/clmm/instructions for the on-chain account list.
Limit orders
A limit order parks user input at a single tick and is filled FIFO when a swap crosses that tick. Outputs are pushed to the owner’s ATA at settle time; the owner does not need to be online to be filled.Open a limit order
LimitOrderState PDA from (owner, nonce PDA, order nonce), bumps the per-wallet LimitOrderNonce, and inserts the order into the FIFO cohort at that tick.
Increase / decrease an open order
decreaseLimitOrder can only remove from the unfilled portion of the order; the filled portion is locked until settlement. Both instructions revert with InvalidOrderPhase if the order has already been fully filled.
Settle a filled order
settleLimitOrder reads the order’s unfilled_ratio_x64 against the cohort tracker, computes the filled output, and transfers it to the owner’s ATA. The owner can call this themselves; limit_order_admin (an off-chain operational keeper) can also call it on the owner’s behalf — the output still goes to the owner.
For closing fully-settled orders to recover rent, use closeLimitOrder (single) or closeAllLimitOrder (batch). For settling many at once, settleAllLimitOrder packs as many SettleLimitOrder calls as fit into a v0 tx.
List a wallet’s parked orders (off-chain)
totalAmount / filledAmount / pendingSettle distinguish the phases). For closed-order history use /limit-order/history/order/list-by-user?wallet=… (per-wallet, paginated by nextPageId); for the full event log of a specific order use /limit-order/history/event/list-by-pda?pda=….
Rust CPI skeleton
SwapV2:
Common pitfalls
- Off-spacing tick endpoints →
TickAndSpacingNotMatch. Always snap viaTickUtil.getPriceAndTick(singularTickUtil). - Not enough tick arrays supplied in
SwapV2→NotEnoughTickArrayAccount. Take the list fromswapInternal(...).accounts. - Full-range position without the bitmap extension → the extension PDA must be writable; the SDK handles this automatically.
- Mistaking
sqrt_price_x64forprice→ a factor-of-2 confusion here is particularly painful. When in doubt, let the SDK compute it from a human-readable price. - Collecting rewards too eagerly → each collect is a zero-liquidity
DecreaseLiquidityand costs one transaction. Batch viaharvestAllRewardsacross many positions, and remember itsexecuteneeds{ sequentially: true }. - Closing NFT accounts yourself →
ClosePositionburns the NFT and closes its ATA. It also closes a Token-2022 NFT mint; a classic SPL Token mint remains at supply zero because that program cannot close mints. Do not close supported accounts separately or the instruction will revert. - Opening a limit order at a non-spaced tick →
TickAndSpacingNotMatch. Always quantize via the exportedgetOrderTickhelper. - Calling
decreaseLimitOrderon a fully-filled order →InvalidOrderPhase. UsesettleLimitOrderthencloseLimitOrderinstead. - Expecting an
enableDynamicFeeflag → there is none. OmittingdynamicFeeConfigsimply creates a static-fee pool, silently and with no error. If you wanted dynamic fees, pass the config account’sPublicKey, picked from/main/clmm-dynamic-config.
Where to go next
sdk-api/typescript-sdk— complete SDK surface.sdk-api/rest-api— quote and pool-metadata endpoints.user-flows/create-clmm-pool— non-code walkthrough.integration-guides/aggregator— routing CLMM as part of a path.

