Ana içeriğe atla

Documentation Index

Fetch the complete documentation index at: https://docs.raydium.io/llms.txt

Use this file to discover all available pages before exploring further.

Bu sayfa yapay zekâ tarafından otomatik olarak çevrilmiştir. İngilizce sürüm esas alınır.İngilizce sürümü görüntüle →
Sürüm notu. Tüm demolar, Solana mainnet-beta üzerinde @raydium-io/raydium-sdk-v2@0.2.42-alpha ile 2026-04 tarihinde doğrulanmış şekilde çalışır. Program ID’leri SDK aracılığıyla reference/program-addresses sayfasından alınmaktadır.

Kurulum

npm install @raydium-io/raydium-sdk-v2 @solana/web3.js @solana/spl-token bn.js decimal.js
Bu sayfadaki her demo, raydium-sdk-V2-demo/src/clmm dizinindeki bir dosyayla birebir örtüşmektedir; GitHub bağlantısı her bölümün yanında yer almaktadır. Başlangıç yapılandırması, demo deposunun config.ts.template dosyasını (kaynak) takip eder — basit olmayan entegrasyonlar için önerilen ayar disableFeatureCheck: true şeklindedir:
import { Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import fs from "node:fs";

const connection = new Connection(process.env.RPC_URL ?? clusterApiUrl("mainnet-beta"));
const owner = Keypair.fromSecretKey(
  new Uint8Array(JSON.parse(fs.readFileSync(process.env.KEYPAIR!, "utf8"))),
);
const raydium = await Raydium.load({
  owner,
  connection,
  cluster: "mainnet",
  disableFeatureCheck: true,
  blockhashCommitment: "finalized",
});
export const txVersion = TxVersion.V0;

CLMM pool oluşturma

Kaynak: src/clmm/createPool.ts
import { PublicKey } from "@solana/web3.js";
import { CLMM_PROGRAM_ID } from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";
import Decimal from "decimal.js";

const mintA = await raydium.token.getTokenInfo(
  new PublicKey("So11111111111111111111111111111111111111112"));   // wSOL
const mintB = await raydium.token.getTokenInfo(
  new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"));   // USDC

const ammConfigs = await raydium.api.getClmmConfigs();
const ammConfig  = ammConfigs.find((c) => c.index === 1)!;        // 0.05% tier

const initialPrice = new Decimal(160);        // 160 USDC per SOL
const { execute, extInfo } = await raydium.clmm.createPool({
  programId: CLMM_PROGRAM_ID,
  mint1:     mintA,
  mint2:     mintB,
  ammConfig,
  initialPrice,
  startTime: new BN(0),
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Pool:", extInfo.address.id.toBase58(), "tx:", txId);
SDK şu işlemleri gerçekleştirir:
  • mint1/mint2 çiftini türetme öncesinde bayt sırasına göre sıralar.
  • sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64) değerini hesaplar.
  • observation ve tick_array_bitmap_extension hesaplarını oluşturur.
  • ammConfig tarafından tanımlanan pool oluşturma ücretini öder.

Seçilen aralıkta pozisyon açma

Kaynak: src/clmm/createPosition.ts
import { PoolUtils, TickUtils } from "@raydium-io/raydium-sdk-v2";

const poolId = new PublicKey("<POOL_ID>");
const { poolInfo, poolKeys, rpcData } = await raydium.clmm.getPoolInfoFromRpc(poolId);

// Choose a price range. Here: ±10% of current.
const currentPrice = new Decimal(poolInfo.price);
const lowerPrice   = currentPrice.mul(0.9);
const upperPrice   = currentPrice.mul(1.1);

// Snap to valid ticks for this pool's tick_spacing.
const { tick: tickLower } = TickUtils.getPriceAndTick({
  poolInfo, price: lowerPrice, baseIn: true,
});
const { tick: tickUpper } = TickUtils.getPriceAndTick({
  poolInfo, price: upperPrice, baseIn: true,
});

// How much of each token to deposit.
const inputAmount = new BN(10_000_000);  // 0.01 SOL
const inputMint   = poolInfo.mintA.address;

const res = PoolUtils.getLiquidityAmountOutFromAmountIn({
  poolInfo,
  slippage: 0.01,
  inputA: true,
  tickUpper,
  tickLower,
  amount: inputAmount,
  add: true,
  amountHasFee: true,
  epochInfo: await raydium.fetchEpochInfo(),
});

const { execute } = await raydium.clmm.openPositionFromBase({
  poolInfo,
  poolKeys,
  tickUpper,
  tickLower,
  base: "MintA",
  ownerInfo: { useSOLBalance: true },
  baseAmount: inputAmount,
  otherAmountMax: res.amountSlippageB.amount,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Position opened, tx:", txId);
SDK, aralığın hangi tick dizilerine dokunduğunu otomatik olarak hesaplar ve başlatılmamış olanlar varsa InitTickArray talimatlarını işleme ekler.

Mevcut bir pozisyonda likidite artırma

Kaynak: src/clmm/increaseLiquidity.ts
const positionNftMint = new PublicKey("<POSITION_NFT_MINT>");

const positionAccount = await raydium.clmm.getPositionInfo({
  nftMint: positionNftMint,
});

const { execute } = await raydium.clmm.increasePositionFromBase({
  poolInfo,
  poolKeys,
  ownerPosition: positionAccount,
  base: "MintA",
  baseAmount: new BN(5_000_000),
  otherAmountMax: new BN(1_000_000_000),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });

Likidite azaltma (ve aynı anda ücret toplama)

Kaynak: src/clmm/decreaseLiquidity.ts ve src/clmm/closePosition.ts
const { execute } = await raydium.clmm.decreaseLiquidity({
  poolInfo,
  poolKeys,
  ownerPosition: positionAccount,
  liquidity: positionAccount.liquidity.divn(2),   // halve
  amountMinA: new BN(0),
  amountMinB: new BN(0),
  closePosition: false,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
Yalnızca ücret toplamak için decreaseLiquidity fonksiyonunu liquidity = new BN(0) ile çağırın. Talimatın yan etkisi olarak tokens_fees_owed_{0,1} hesaplanır ve bu değerler hesaptan çıkarılır. Likidite ve ücretleri sıfırladıktan sonra pozisyonu tamamen kapatmak için son decreaseLiquidity çağrısında closePosition: true parametresini geçirin. SDK, ClosePosition talimatını ekler ve NFT’yi yakar.

Ödül toplama

Kaynak: src/clmm/harvestAllRewards.ts
const { execute } = await raydium.clmm.harvestAllRewards({
  ownerInfo: { useSOLBalance: true },
  allPoolInfo: { [poolInfo.id]: poolInfo },
  allPositions: { [poolInfo.id]: [positionAccount] },
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
harvestAllRewards, iletilen her pool’daki tüm pozisyonları tarar, CollectReward (ve varsa UpdateRewardInfos) talimatlarını toplu hâle getirir, gerekirse bunları birden fazla işleme böler.

Swap

Kaynak: src/clmm/swap.ts
import { PoolUtils } from "@raydium-io/raydium-sdk-v2";

const amountIn = new BN(10_000_000);
const baseIn   = true;               // swap A (SOL) → B (USDC)
const slippage = 0.005;

const { minAmountOut, remainingAccounts, priceImpact } =
  PoolUtils.computeAmountOutFormat({
    poolInfo,
    tickArrayCache: await raydium.clmm.fetchTickArrays({ poolInfo }),
    amountIn,
    tokenOut: poolInfo.mintB,
    slippage,
    epochInfo: await raydium.fetchEpochInfo(),
  });

const { execute } = await raydium.clmm.swap({
  poolInfo,
  poolKeys,
  inputMint: new PublicKey(poolInfo.mintA.address),
  amountIn,
  amountOutMin: minAmountOut.amount,
  observationId: poolKeys.observationId,
  remainingAccounts,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
computeAmountOutFormat, zincir üstü programla aynı mantığı kullanarak tick haritasını zincir dışında işler ve şunları döndürür:
  • beklenen çıkış miktarı,
  • slippage sonrası minimum çıkış miktarı,
  • gerçek swap’ın dokunacağı tick dizisi hesaplarının listesi (remainingAccounts).
Simülasyondan dönen remainingAccounts değerini her zaman iletin: çok az hesap gönderirseniz swap, yürütme sırasında TickArrayNotFound hatasıyla geri döner; güncel olmayan hesaplar gönderirseniz hesaplama gücü boşa harcanır.

Özelleştirilebilir CLMM pool oluşturma

createCustomizablePool, pool oluşturma sırasında dinamik ücret ve tek taraflı ücret seçeneklerini açığa çıkaran yeni giriş noktasıdır. createPool ile aynı yapıyı alır, üç ek alan barındırır:
import { CLMM_PROGRAM_ID, CollectFeeOn } from "@raydium-io/raydium-sdk-v2";

const dynamicFeeConfigs = await raydium.api.getClmmDynamicConfigs();    // GET /main/clmm-dynamic-config
const dynamicFeeConfig  = dynamicFeeConfigs.find((c) => c.index === 0); // pick a calibration tier

const { execute, extInfo } = await raydium.clmm.createCustomizablePool({
  programId: CLMM_PROGRAM_ID,
  mint1:     mintA,
  mint2:     mintB,
  ammConfig,
  initialPrice,
  startTime: new BN(0),
  // New fields:
  collectFeeOn:        CollectFeeOn.Token1Only,   // 0 = FromInput, 1 = Token0Only, 2 = Token1Only
  enableDynamicFee:    true,
  dynamicFeeConfigId:  dynamicFeeConfig?.id,      // omit when enableDynamicFee is false
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
console.log("Customizable pool:", extInfo.address.id.toBase58());
createPool, varsayılan ücret, limit order yok ve dinamik ücret yok senaryosu için çalışmaya devam eder. Üç yeni seçenekten herhangi birine ihtiyaç duyduğunuzda createCustomizablePool kullanın. Zincir üstü hesap listesi için products/clmm/instructions sayfasına bakın.

Limit emirleri

Bir limit emri, kullanıcının girişini tek bir tick’te park eder ve bir swap o tick’i geçtiğinde FIFO sırasıyla doldurulur. Çıktılar, emir kapandığında sahibinin ATA’sına aktarılır; doldurulan emrin sahibinin çevrimiçi olması gerekmez.

Limit emri açma

import { TickUtils } from "@raydium-io/raydium-sdk-v2";

const limitConfigs = await raydium.api.getClmmLimitOrderConfigs(); // GET /main/clmm-limit-order-config
const limitConfig  = limitConfigs.find((c) => c.poolId === poolInfo.id);

// Limit price MUST be quantized to tick_spacing.
const targetPrice = new Decimal(180);                              // sell SOL at 180 USDC
const { tick: limitTick } = TickUtils.getPriceAndTick({
  poolInfo, price: targetPrice, baseIn: true,
});

const { execute } = await raydium.clmm.openLimitOrder({
  poolInfo,
  poolKeys,
  limitOrderConfig: limitConfig,
  inputMint: poolInfo.mintA.address,         // selling SOL
  inputAmount: new BN(50_000_000),           // 0.05 SOL
  tick: limitTick,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Limit order opened, tx:", txId);
SDK, LimitOrderState PDA’sını (pool, owner, tick, nonce) kombinasyonundan türetir, (pool, owner) başına LimitOrderNonce değerini artırır ve emri söz konusu tick’teki FIFO kuyruğuna ekler.

Açık bir emrin miktarını artırma / azaltma

await raydium.clmm.increaseLimitOrder({
  poolInfo,
  poolKeys,
  limitOrderId: <LIMIT_ORDER_PUBKEY>,
  addAmount: new BN(20_000_000),
  txVersion: TxVersion.V0,
}).then((b) => b.execute({ sendAndConfirm: true }));

await raydium.clmm.decreaseLimitOrder({
  poolInfo,
  poolKeys,
  limitOrderId: <LIMIT_ORDER_PUBKEY>,
  removeAmount: new BN(10_000_000),
  txVersion: TxVersion.V0,
}).then((b) => b.execute({ sendAndConfirm: true }));
decreaseLimitOrder yalnızca emrin doldurulmamış kısmından miktar çıkarabilir; doldurulmuş kısım kapanışa kadar kilitli kalır. Emir tamamen dolmuşsa her iki talimat da InvalidOrderPhase hatasıyla geri döner.

Dolmuş bir emri kapatma

await raydium.clmm.settleLimitOrder({
  poolInfo,
  poolKeys,
  limitOrderId: <LIMIT_ORDER_PUBKEY>,
  txVersion: TxVersion.V0,
}).then((b) => b.execute({ sendAndConfirm: true }));
settleLimitOrder, emrin unfilled_ratio_x64 değerini kohort takipçisiyle karşılaştırır, doldurulan çıktıyı hesaplar ve sahibinin ATA’sına aktarır. Sahibi bu işlemi kendisi yapabileceği gibi zincir dışı bir operasyonel bekçi olan limit_order_admin de sahibi adına çağırabilir; çıktı her durumda sahibine gider. Tamamen kapanmış emirleri kira geri kazanımı için kapatmak amacıyla closeLimitOrder (tekli) veya closeAllLimitOrder (toplu) kullanın. Aynı anda birden fazla emri kapatmak için settleAllLimitOrder, v0 işlemine sığabilecek kadar çok SettleLimitOrder çağrısını tek seferde paketler.

Bir cüzdanın açık emirlerini listeleme (zincir dışı)

// API helper. See api-reference/temp-api-v1.
const active = await fetch(
  `https://temp-api-v1.raydium.io/limit-order/order/list?wallet=<your-wallet-pubkey>`,
).then((r) => r.json());
Aktif emirler uç noktası, doldurulmamış ve kısmen doldurulmuş emirleri tek bir yanıtta döndürür (totalAmount / filledAmount / pendingSettle alanları aşamaları birbirinden ayırır). Kapatılmış emir geçmişi için /limit-order/history/order/list-by-user?wallet=… (cüzdan bazında, nextPageId ile sayfalanmış) adresini kullanın; belirli bir emrin tam olay kaydı için ise /limit-order/history/event/list-by-pda?pda=… adresine başvurun.

Rust CPI şablonu

use anchor_lang::prelude::*;
use raydium_amm_v3::cpi;
use raydium_amm_v3::program::AmmV3;
use raydium_amm_v3::cpi::accounts::SwapV2;

#[derive(Accounts)]
pub struct ProxyClmmSwap<'info> {
    #[account(mut)]
    pub payer: Signer<'info>,
    /// CHECK:
    pub amm_config: UncheckedAccount<'info>,
    #[account(mut)]
    /// CHECK:
    pub pool_state: UncheckedAccount<'info>,
    #[account(mut)]
    /// CHECK:
    pub input_token_account: UncheckedAccount<'info>,
    #[account(mut)]
    /// CHECK:
    pub output_token_account: UncheckedAccount<'info>,
    #[account(mut)]
    /// CHECK:
    pub input_vault: UncheckedAccount<'info>,
    #[account(mut)]
    /// CHECK:
    pub output_vault: UncheckedAccount<'info>,
    #[account(mut)]
    /// CHECK:
    pub observation_state: UncheckedAccount<'info>,
    /// CHECK:
    pub token_program: UncheckedAccount<'info>,
    /// CHECK:
    pub token_program_2022: UncheckedAccount<'info>,
    /// CHECK:
    pub memo_program: UncheckedAccount<'info>,
    /// CHECK:
    pub input_vault_mint: UncheckedAccount<'info>,
    /// CHECK:
    pub output_vault_mint: UncheckedAccount<'info>,
    pub clmm_program: Program<'info, AmmV3>,
    // `remaining_accounts` carries the tick_array and bitmap_extension accounts.
}

pub fn proxy_swap(
    ctx: Context<ProxyClmmSwap>,
    amount: u64,
    other_amount_threshold: u64,
    sqrt_price_limit_x64: u128,
    is_base_input: bool,
) -> Result<()> {
    let cpi_accounts = SwapV2 {
        payer:                 ctx.accounts.payer.to_account_info(),
        amm_config:            ctx.accounts.amm_config.to_account_info(),
        pool_state:            ctx.accounts.pool_state.to_account_info(),
        input_token_account:   ctx.accounts.input_token_account.to_account_info(),
        output_token_account:  ctx.accounts.output_token_account.to_account_info(),
        input_vault:           ctx.accounts.input_vault.to_account_info(),
        output_vault:          ctx.accounts.output_vault.to_account_info(),
        observation_state:     ctx.accounts.observation_state.to_account_info(),
        token_program:         ctx.accounts.token_program.to_account_info(),
        token_program_2022:    ctx.accounts.token_program_2022.to_account_info(),
        memo_program:          ctx.accounts.memo_program.to_account_info(),
        input_vault_mint:      ctx.accounts.input_vault_mint.to_account_info(),
        output_vault_mint:     ctx.accounts.output_vault_mint.to_account_info(),
    };
    let cpi_ctx = CpiContext::new(ctx.accounts.clmm_program.to_account_info(), cpi_accounts)
        .with_remaining_accounts(ctx.remaining_accounts.to_vec());
    cpi::swap_v2(cpi_ctx, amount, other_amount_threshold, sqrt_price_limit_x64, is_base_input)
}
SwapV2 için kalan hesapların sırası:
[tick_array_bitmap_extension?, tick_array_0, tick_array_1, …]
Swap uzantıya hiç ihtiyaç duymuyorsa atlanabilir; aksi takdirde ilk kalan hesap olarak yer almalıdır.

Sık yapılan hatalar

  • Tick uç noktalarının aralık dışı olmasıInvalidTickIndex. Her zaman TickUtils.getPriceAndTick aracılığıyla hizalayın.
  • SwapV2’ye yeterli tick dizisi sağlanmamasıTickArrayNotFound. Tam listeyi almak için computeAmountOutFormat kullanın.
  • Bitmap uzantısı olmadan tam aralıklı pozisyon → uzantı PDA’sının yazılabilir olması gerekir; SDK bunu otomatik olarak yönetir.
  • sqrt_price_x64 ile price karıştırılması → bu noktada 2 katlık bir karışıklık oldukça sorunlu sonuçlar doğurur. Şüphe duyduğunuzda SDK’nın bunu insan tarafından okunabilir bir fiyattan hesaplamasına izin verin.
  • Ödülleri çok sık toplamak → her toplama işlemi bir işlem maliyeti doğurur. Birden fazla pozisyon için harvestAllRewards ile toplu yapın.
  • Kira hâlâ mint’e borçluyken NFT’yi yakmakClosePosition aynı zamanda NFT mint ve ATA’yı da kapatır; bunları ayrı ayrı kapatmaya çalışırsanız program geri döner.
  • Aralık dışı bir tick’te limit emri açmakInvalidTickIndex. Her zaman TickUtils.getPriceAndTick ile nicelendirin.
  • Tamamen dolmuş bir emirde decreaseLimitOrder çağırmakInvalidOrderPhase. Bunun yerine settleLimitOrder ve ardından closeLimitOrder kullanın.
  • enableDynamicFee: true iletirken dynamicFeeConfigId unutmakCreateCustomizablePool işlemi InvalidDynamicFeeConfigParams hatasıyla geri döner. Ya dinamik ücreti kapatın ya da /main/clmm-dynamic-config adresinden bir yapılandırma seçin.

Sonraki adımlar

Kaynaklar: