> ## 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.

# عروض كود CLMM

> أمثلة TypeScript شاملة من البداية إلى النهاية: إنشاء pool من نوع CLMM، فتح مركز ضمن نطاق سعري محدد، تعديل السيولة، تنفيذ swap، وتحصيل الرسوم والمكافآت.

<Info>
  **هذه الصفحة مُترجَمة آليًا بواسطة الذكاء الاصطناعي. النسخة الإنجليزية هي المرجع المعتمد.**

  [عرض النسخة الإنجليزية →](/products/clmm/code-demos)
</Info>

<Info>
  **لافتة الإصدار.** جميع العروض التوضيحية تستهدف `@raydium-io/raydium-sdk-v2@0.2.42-alpha` على شبكة Solana mainnet-beta، تم التحقق منها في أبريل 2026. تأتي معرّفات البرامج من [`reference/program-addresses`](/ar/reference/program-addresses) عبر SDK.
</Info>

## الإعداد

```bash theme={null}
npm install @raydium-io/raydium-sdk-v2 @solana/web3.js @solana/spl-token bn.js decimal.js
```

كل عرض توضيحي في هذه الصفحة يعكس ملفًا موجودًا في [`raydium-sdk-V2-demo/src/clmm`](https://github.com/raydium-io/raydium-sdk-V2-demo/tree/master/src/clmm)؛ ورابط GitHub مذكور بجانب كل قسم. يتبع الإعداد الأولي ملف `config.ts.template` الخاص بمستودع العروض التوضيحية ([المصدر](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/config.ts.template)) — يُنصح باستخدام `disableFeatureCheck: true` في أي تكامل غير بسيط:

```ts theme={null}
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;
```

## إنشاء pool من نوع CLMM

المصدر: [`src/clmm/createPool.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/createPool.ts)

```ts theme={null}
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 بما يلي:

* ترتيب `mint1`/`mint2` وفق الترتيب البايتي قبل الاشتقاق.
* حساب `sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64)`.
* إنشاء حسابَي `observation` و`tick_array_bitmap_extension`.
* دفع رسوم إنشاء pool المحددة في `ammConfig`.

## فتح مركز ضمن نطاق سعري محدد

المصدر: [`src/clmm/createPosition.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/createPosition.ts)

```ts theme={null}
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 تلقائيًا مصفوفات الـ tick التي يمسّها النطاق المحدد، ويضم تعليمات `InitTickArray` إذا كانت أي منها غير مُهيَّأة.

## زيادة السيولة على مركز قائم

المصدر: [`src/clmm/increaseLiquidity.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/increaseLiquidity.ts)

```ts theme={null}
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 });
```

## تقليص السيولة (مع تحصيل الرسوم في آنٍ واحد)

المصدر: [`src/clmm/decreaseLiquidity.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/decreaseLiquidity.ts) و[`src/clmm/closePosition.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/closePosition.ts)

```ts theme={null}
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 });
```

**لتحصيل الرسوم فقط**، استدعِ `decreaseLiquidity` مع تمرير `liquidity = new BN(0)`. الأثر الجانبي للتعليمة هو تسوية `tokens_fees_owed_{0,1}` وتحويلها خارجًا.

لإغلاق المركز كليًا بعد تصفير السيولة والرسوم، مرّر `closePosition: true` في آخر استدعاء لـ`decreaseLiquidity`. سيضيف SDK تعليمة `ClosePosition` ويحرق NFT.

## تحصيل المكافآت

المصدر: [`src/clmm/harvestAllRewards.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/harvestAllRewards.ts)

```ts theme={null}
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` على كل مركز في كل pool يُمرَّر إليها، وتجمع تعليمات `CollectReward` (وأي `UpdateRewardInfos`) في دُفعات، وتوزعها على معاملات متعددة عند الحاجة.

## Swap (مبادلة)

المصدر: [`src/clmm/swap.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/clmm/swap.ts)

```ts theme={null}
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` خريطة الـ tick خارج السلسلة باستخدام المنطق ذاته المعتمد في البرنامج على السلسلة، وتُعيد:

* الكمية المتوقعة ناتجةً عن العملية،
* الحد الأدنى للكمية الناتجة بعد احتساب slippage،
* قائمة بحسابات مصفوفات tick التي ستمسّها عملية swap الفعلية (`remainingAccounts`).

احرص دائمًا على تمرير `remainingAccounts` المُعادة من المحاكاة: إن كانت أقل من اللازم، ستعود العملية بخطأ `TickArrayNotFound` في منتصف المسير؛ وإن كانت قديمة، فهذا هدر لموارد الحساب.

## إنشاء pool من نوع CLMM قابل للتخصيص

`createCustomizablePool` هو نقطة الدخول الجديدة التي تتيح ضبط الرسوم الديناميكية وخيار الرسوم أحادية الجانب عند إنشاء pool. يأخذ نفس معاملات `createPool` مع ثلاثة إضافات:

```ts theme={null}
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` عملها للمسار الافتراضي الذي لا يحتاج رسومًا ديناميكية أو أوامر حد أو تخصيصًا إضافيًا. استخدم `createCustomizablePool` كلما احتجت إلى أي من الخيارات الثلاثة الجديدة. راجع [`products/clmm/instructions`](/ar/products/clmm/instructions) للاطلاع على قائمة الحسابات على السلسلة.

## أوامر الحد (Limit orders)

يضع أمر الحد مدخلات المستخدم عند tick واحد ويُنفَّذ وفق مبدأ FIFO عندما تعبر عملية swap ذلك الـ tick. تُرسَل المخرجات إلى ATA الخاص بالمالك عند وقت التسوية؛ ولا يحتاج المالك أن يكون متصلًا بالشبكة لتنفيذ أمره.

### فتح أمر حد

```ts theme={null}
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 الـ PDA الخاص بـ`LimitOrderState` من `(pool, owner, tick, nonce)`، ويزيد `LimitOrderNonce` الخاص بكل زوج (pool, owner)، ويدرج الأمر في مجموعة FIFO عند ذلك الـ tick.

### زيادة / تقليص أمر مفتوح

```ts theme={null}
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` سحب إلا من الجزء **غير المنفَّذ** من الأمر؛ أما الجزء المنفَّذ فيظل مقيدًا حتى وقت التسوية. تعود كلتا التعليمتين بخطأ `InvalidOrderPhase` إذا كان الأمر قد اكتمل تنفيذه بالكامل.

### تسوية أمر مكتمل التنفيذ

```ts theme={null}
await raydium.clmm.settleLimitOrder({
  poolInfo,
  poolKeys,
  limitOrderId: <LIMIT_ORDER_PUBKEY>,
  txVersion: TxVersion.V0,
}).then((b) => b.execute({ sendAndConfirm: true }));
```

تقرأ `settleLimitOrder` النسبة `unfilled_ratio_x64` الخاصة بالأمر مقابل متتبع المجموعة، وتحسب المخرجات المنفَّذة، ثم تحولها إلى ATA المالك. يمكن للمالك استدعاء هذه الدالة بنفسه؛ كما يمكن لـ`limit_order_admin` (وهو حارس تشغيلي خارج السلسلة) استدعاؤها نيابةً عن المالك — والمخرجات تذهب إلى المالك في كلتا الحالتين.

لإغلاق الأوامر المُسوَّاة بالكامل واسترداد الإيجار، استخدم `closeLimitOrder` (لأمر واحد) أو `closeAllLimitOrder` (دُفعي). ولتسوية عدة أوامر دفعةً واحدة، تحشو `settleAllLimitOrder` أكبر عدد ممكن من استدعاءات `SettleLimitOrder` في معاملة واحدة من نوع v0.

### عرض الأوامر المعلّقة لمحفظة ما (خارج السلسلة)

```ts theme={null}
// 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());
```

تُعيد نقطة نهاية الأوامر النشطة كلًا من الأوامر غير المنفَّذة والمنفَّذة جزئيًا في حمولة واحدة (تُميّز الحقول `totalAmount` / `filledAmount` / `pendingSettle` بين المراحل المختلفة). للاطلاع على سجل الأوامر المُغلقة، استخدم `/limit-order/history/order/list-by-user?wallet=…` (مقسّم بالصفحات عبر `nextPageId`)؛ وللاطلاع على سجل أحداث أمر بعينه، استخدم `/limit-order/history/event/list-by-pda?pda=…`.

## هيكل CPI بلغة Rust

```rust theme={null}
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`:

```
[tick_array_bitmap_extension?, tick_array_0, tick_array_1, …]
```

إن لم تحتج عملية swap إلى الامتداد، أغفله؛ وإلا فهو أول حساب في قائمة الحسابات المتبقية.

## الأخطاء الشائعة

* **تحديد نقاط tick لا تتوافق مع التباعد** ← `InvalidTickIndex`. احرص دائمًا على المحاذاة عبر `TickUtils.getPriceAndTick`.
* **تمرير عدد غير كافٍ من مصفوفات tick في `SwapV2`** ← `TickArrayNotFound`. استخدم `computeAmountOutFormat` للحصول على القائمة الكاملة.
* **إنشاء مركز بنطاق كامل دون إضافة امتداد bitmap** ← يجب أن يكون PDA الامتداد قابلًا للكتابة؛ يتولى SDK هذا تلقائيًا.
* **الخلط بين `sqrt_price_x64` والسعر** ← هذا الالتباس يُفضي إلى خطأ عامل 2 يصعب اكتشافه. عند الشك، دع SDK يحسب السعر انطلاقًا من قيمة يقرأها الإنسان.
* **تحصيل المكافآت بشكل متكرر جدًا** ← كل تحصيل يكلف معاملة. استخدم `harvestAllRewards` لدمج عدة مراكز في دُفعة واحدة.
* **حرق NFT ولا يزال الإيجار مستحقًا على الـ mint** ← تُغلق `ClosePosition` أيضًا mint الـ NFT وحساب ATA الخاص به؛ لا تُغلقهما منفصلَين وإلا سيعود البرنامج بخطأ.
* **فتح أمر حد عند tick لا يتوافق مع التباعد** ← `InvalidTickIndex`. احرص دائمًا على التكميم عبر `TickUtils.getPriceAndTick`.
* **استدعاء `decreaseLimitOrder` على أمر اكتمل تنفيذه** ← `InvalidOrderPhase`. استخدم `settleLimitOrder` ثم `closeLimitOrder` عوضًا عن ذلك.
* **نسيان `dynamicFeeConfigId` مع تمرير `enableDynamicFee: true`** ← سيعود `CreateCustomizablePool` بخطأ `InvalidDynamicFeeConfigParams`. إما أن تُعطّل الرسوم الديناميكية، أو تختار إعدادًا من `/main/clmm-dynamic-config`.

## الخطوات التالية

* [`sdk-api/typescript-sdk`](/ar/sdk-api/typescript-sdk) — الواجهة الكاملة لـ SDK.
* [`sdk-api/rest-api`](/ar/sdk-api/rest-api) — نقاط نهاية الأسعار وبيانات pool.
* [`user-flows/create-clmm-pool`](/ar/user-flows/create-clmm-pool) — شرح تفصيلي بدون كود.
* [`integration-guides/aggregator`](/ar/integration-guides/aggregator) — توجيه CLMM ضمن مسار تجميعي.

المصادر:

* [Raydium SDK v2](https://github.com/raydium-io/raydium-sdk-V2)
* [عروض Raydium SDK v2 التوضيحية](https://github.com/raydium-io/raydium-sdk-V2-demo)
* [`raydium-io/raydium-clmm`](https://github.com/raydium-io/raydium-clmm)
