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

# PDAs والـ CPIs

> العناوين المشتقة من البرنامج والاستدعاء البرمجي بين البرامج — البدائل الأساسيتان اللتان يستخدمهما Raydium لإدارة الحالة بشكل حتمي والتركيب مع برامج أخرى. البذور والنتوءات والاستدعاء مقابل استدعاء_موقّع والأنماط التي يتبعها كود Raydium.

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

  [عرض النسخة الإنجليزية →](/solana-fundamentals/pdas-and-cpis)
</Info>

<Info>
  **PDAs** (العناوين المشتقة من البرنامج) و**CPIs** (الاستدعاء البرمجي بين البرامج) هما البدائل الأساسيتان اللتان تجعلان Raydium ممكناً. تتيح PDAs لبرنامج "امتلاك" عناوين حتمية دون مفاتيح خاصة — هكذا تعمل سلطات وخزائن حمامات السباحة. تتيح CPIs لبرنامج واحد استدعاء آخر — هكذا يقوم Raydium بمبادلة الرموز عبر برنامج SPL Token وكيف يقوم المدمجون بدمج Raydium في تدفقاتهم الخاصة. كلاهما يستحق الفهم قبل قراءة كود Raydium.
</Info>

## PDAs: العناوين بدون مفاتيح

**العنوان المشتق من البرنامج** هو مفتاح عام يتميز بـ:

* أنه لا يقع على منحنى ed25519 (لا يوجد مفتاح خاص له).
* يُشتق بشكل حتمي من معرّف برنامج ومجموعة من البذور.
* يمكن التوقيع عليه من قبل *فقط* البرنامج المشتق منه، عبر `invoke_signed`.

كل سلطة حمام سباحة في Raydium، وكل حساب حالة حمام سباحة، وكل خزينة، وكل حالة مزرعة — كلها PDAs.

### الاشتقاق

يتم حساب PDA بهاش معرّف البرنامج مع البذور، ثم العثور على بايت "نتوء" يفرض النتيجة خارج المنحنى. أول نتوء (عادة يبدأ من 255 وينخفض) ينتج عنه عنوان خارج منحنى هو الفائز؛ هذا هو **النتوء الأساسي**.

```ts theme={null}
import { PublicKey } from "@solana/web3.js";

const [poolAuthority, bump] = PublicKey.findProgramAddressSync(
  [Buffer.from("authority"), poolId.toBuffer()],
  CPMM_PROGRAM_ID,
);
```

يمكن أن تكون البذور أي شيء — سلاسل نصية، مفاتيح عامة أخرى، قيم `u64` كبايتات little-endian. الاتفاقية في Raydium هي بادئة يمكن قراءتها من البشر متبوعة بمعرّفات فريدة.

### أنماط PDA في Raydium

PDAs الشائعة في برامج Raydium:

| PDA                       | البذور                                    | البرنامج |
| ------------------------- | ----------------------------------------- | -------- |
| سلطة AMM (AMM v4)         | `[b"amm authority"]` + bump               | AMM v4   |
| حالة حمام السباحة (CPMM)  | `[b"pool", amm_config, mint_a, mint_b]`   | CPMM     |
| خزينة حمام السباحة (CPMM) | `[b"pool_vault", pool, mint]`             | CPMM     |
| السلطة (CPMM)             | `[b"vault_and_lp_mint_auth_seed"]`        | CPMM     |
| حالة حمام السباحة (CLMM)  | `[b"pool", amm_config, mint_0, mint_1]`   | CLMM     |
| مصفوفة التجزئة (CLMM)     | `[b"tick_array", pool, start_tick_index]` | CLMM     |
| الملاحظة (CLMM)           | `[b"observation", pool]`                  | CLMM     |
| الموضع الشخصي (CLMM)      | `[b"position", position_nft_mint]`        | CLMM     |
| حالة المزرعة (Farm v6)    | `[b"pool_farm_state", farm_id]`           | Farm v6  |
| دفتر المستخدم (Farm v6)   | `[b"user_ledger", farm, user]`            | Farm v6  |

يمكن للمستخدمين والمدمجين حساب هذه دون جلب أي شيء — بالنظر إلى المدخلات العامة (معرّف حمام السباحة، معرّف المزرعة، مفتاح المستخدم)، فإن PDA حتمي.

### النتوء الأساسي

على الرغم من أنه يمكن في الواقع أن تكون هناك نتوءات متعددة تنتج عناوين خارج منحنى، تستخدم برامج Raydium دائماً **النتوء الأساسي** (الموجود بالتناقص من 255). يتم تخزينه في بيانات حساب PDA بحيث يمكن للمعاملات اللاحقة تمريره وتخطي حلقة الاشتقاق (المكلفة):

```rust theme={null}
#[account]
pub struct PoolState {
    pub bump: [u8; 1],
    // ... بقية حالة حمام السباحة
}
```

في المعاملات اللاحقة، يتم قراءة النتوء من حالة حمام السباحة بدلاً من إعادة الحساب.

## CPIs: استدعاء برامج أخرى

**الاستدعاء البرمجي بين البرامج** يتيح لبرنامج استدعاء تعليمات برنامج آخر بشكل مباشر ضمن معاملة واحدة. يستخدم Raydium CPIs على نطاق واسع:

* تستدعي تعليمات المبادلة برنامج SPL Token لنقل الرموز.
* يستدعي CLMM Metaplex لصك رمز موضع NFT.
* ينادي إنشاء حمام السباحة برنامج النظام لتخصيص الحسابات.
* يستدعي Farm v6 SPL Token لنقل المكافآت.

يستخدم المدمجون أيضاً CPIs للاستدعاء *إلى* Raydium — هكذا تعمل استراتيجيات الخزائن وبروتوكولات LP ذات النفوذ والمركبات التلقائية. انظر [`integration-guides/cpi-integration`](/ar/integration-guides/cpi-integration).

### invoke مقابل invoke\_signed

يوفر وقت تشغيل Solana بدائل CPI اثنتين:

* **`invoke`**: استدعاء برنامج آخر؛ يرث البرنامج المستدعى الموقّعين من المعاملة الخارجية.
* **`invoke_signed`**: استدعاء برنامج آخر *بالنيابة عن PDA*؛ يتحقق وقت التشغيل من بذور PDA ويصرح بالتوقيع.

`invoke_signed` هو السحر الذي يتيح للبرامج الاحتفاظ بالسلطة على الحسابات دون إدارة المفاتيح الخاصة.

### مثال: Raydium تحويل من خزينة حمام السباحة

خزينة حمام السباحة هي حساب رمز يكون مسؤولها هو PDA من برنامج حمام السباحة. لنقل الرموز أثناء المبادلة، يجب على برنامج حمام السباحة التوقيع كـ PDA:

```rust theme={null}
// بذور سلطة حمام السباحة
let authority_seeds: &[&[u8]] = &[
    b"vault_and_lp_mint_auth_seed",
    &[authority_bump],
];
let signer_seeds: &[&[&[u8]]] = &[authority_seeds];

// بناء سياق CPI
let cpi_accounts = Transfer {
    from:      input_vault.to_account_info(),
    to:        user_ata.to_account_info(),
    authority: pool_authority.to_account_info(),
};
let cpi_program = token_program.to_account_info();
let cpi_ctx     = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds);

// التنفيذ
token::transfer(cpi_ctx, amount)?;
```

يرى وقت التشغيل أن `invoke_signed` يُستدعى من برنامج CPMM، ويتحقق من أن `vault_and_lp_mint_auth_seed + bump` يُشتق إلى عنوان `pool_authority` عند هاشه مع معرّف برنامج CPMM، ويسمح بتوقيع السلطة على نقل الرمز. لا مفتاح خاص متورط.

### مثال: مدمج يستدعي Raydium CPMM

يمكن لبرنامج مدمج (مثلاً، الضمان) استدعاء `swap_base_input` في Raydium عبر CPI:

```rust theme={null}
use raydium_cpmm::cpi::{self, accounts::Swap};

let cpi_accounts = Swap {
    payer:                order.to_account_info(),      // PDA، سيقوم بالتوقيع
    authority:            pool_authority_info,
    amm_config:           amm_config_info,
    pool_state:           pool_info,
    input_token_account:  order_input_ata,
    output_token_account: order_output_ata,
    input_vault:          input_vault_info,
    output_vault:         output_vault_info,
    input_token_program:  input_token_program_info,
    output_token_program: output_token_program_info,
    input_token_mint:     input_mint_info,
    output_token_mint:    output_mint_info,
    observation_state:    observation_info,
};

let seeds = &[b"order", user.key.as_ref(), &[order.bump]];
let cpi_ctx = CpiContext::new_with_signer(
    cpmm_program.to_account_info(),
    cpi_accounts,
    &[seeds],
);

cpi::swap_base_input(cpi_ctx, amount_in, min_out)?;
```

هذا هو نمط التكامل الأساسي — انظر [`integration-guides/cpi-integration`](/ar/integration-guides/cpi-integration) للمثال الكامل للضمان.

## حد عمق CPI

يحدّ Solana عمق CPI في **4 مستويات**. تعليمة المستوى الأعلى للمعاملة تُحسب كعمق 0؛ كل استدعاء CPI يزيد العمق.

التأثير العملي: swap الخاص بـ Raydium بالفعل يستخدم مستوى 1-2 من CPI (Raydium → SPL Token). مدمج يستدعي Raydium يستخدم 2. إذا تم استدعاء هذا المدمج من قبل مدمج آخر، فهو 3. المستوى الرابع هو الحد.

معظم الترتيبات تبقى تحت هذا بسهولة، لكن التداخل العميق (المجمّع → الموجّه → Raydium → الخطاف) يمكن أن يصطدم به. صمّم بشكل مسطح بدلاً من العمق.

## الحسابات المتبقية

عندما تحتاج تعليمة Raydium إلى عدد متغير من الحسابات (مثلاً، مبادلة CLMM عبر عدد غير معروف من مصفوفات التجزئة)، يتم تمرير الحسابات الإضافية كـ **حسابات متبقية** — مضافة إلى قائمة الحسابات الثابتة، وتفسيرها حسب الموضع.

يستخدم `SwapV2` الخاص بـ CPMM الحسابات المتبقية لحسابات البرامج الخطافة الإضافية المطلوبة. يجلب العملاء الحسابات المطلوبة ويضيفونها:

```ts theme={null}
const swapIx = await raydium.cpmm.swap({
  /* ... */
  // تتعامل SDK مع الحسابات المتبقية تلقائياً
});
```

على مستوى CPI، يجب على المدمجين إعادة توجيه الحسابات المتبقية من خلال تعليماتهم الخاصة:

```rust theme={null}
pub struct Swap<'info> {
    // ... الحسابات الثابتة
    // بالإضافة إلى الحسابات المتبقية التي يتم إعادة توجيهها عبر ctx.remaining_accounts
}

// إعادة توجيه remaining_accounts إلى CPI
cpi::swap_base_input(
    cpi_ctx.with_remaining_accounts(ctx.remaining_accounts.to_vec()),
    amount_in,
    min_out,
)?;
```

## مخاطر PDA

### البذور الخاطئة → العنوان الخاطئ

خطأ حيث تكون البذور بترتيب خاطئ أو ترميز خاطئ أو تتضمن/تستثني بايت إضافي ينتج بصمت PDA مختلف. تفشل المعاملة بطريقة غامضة (يحاول البرنامج قراءة حساب غير موجود). اختبر دائماً اشتقاق البذور مقابل قيم ذهبية معروفة.

### عدم تخزين النتوء

إذا أعدت اشتقاق النتوء في كل معاملة، فإنك تدفع الحساب لحلقة الاشتقاق. خزّن النتوء الأساسي في بيانات PDA واقرأه من هناك.

### الخلط بين النتوء الأساسي وغير الأساسي

النتوءات غير الأساسية (إذا وجد أحد واحداً ينتج عنه خارج منحنى) يُسمح بها بواسطة `invoke_signed` لكن برامج Raydium ترفضها عبر `assert_eq!(bump, canonical_bump)`. إذا حاول شخص ما المطالبة بـ PDA مع نتوء غير أساسي، تفشل tx.

### تمرير PDA كموقّع عندما لا تكون البرنامج المالك

فقط البرنامج الذي معرّفه في اشتقاق PDA يمكنه `invoke_signed` ببذوره. إذا حاولت، وقت التشغيل يرفض.

## مخاطر CPI

### نسيان إعادة توجيه `remaining_accounts`

إذا كانت تعليمتك الخارجية تمرر حسابات خطاف نقل في `remaining_accounts` لكن CPI إلى Raydium لا تعيد توجيهها، فإن Raydium تفشل لأنها لا تستطيع العثور على حسابات الخطاف. ادرج دائماً `with_remaining_accounts` في CPIs التي تحتاجها.

### عدم تطابق علامات الكتابة

يجب أيضاً أن يكون الحساب الذي تعليمتك الخارجية تشير إليه كقابل للكتابة قابل للكتابة في استدعاء CPI إذا كان البرنامج المستدعى ينوي الكتابة عليه. عدم التطابق → رفض وقت التشغيل.

### عدم الأخذ في الاعتبار الإيجار

CPI إلى برنامج ينشئ حساباً (مثلاً، إنشاء ATA) يتطلب من الدافع أن يملك SOL كافياً للإيجار. فحوصات الإيجار الفاشلة تظهر كأخطاء غامضة.

## مثال عملي: حساب PDAs Raydium CPMM

```ts theme={null}
import { PublicKey } from "@solana/web3.js";

const CPMM_PROGRAM_ID = new PublicKey("CPMMoo8L3F4NbTegBCKVNunggL7H1Zpdmwpwh8KMoZ0F");

function computeCpmmPdas(ammConfig, mintA, mintB) {
  const [poolState, poolBump] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("pool"),
      ammConfig.toBuffer(),
      mintA.toBuffer(),
      mintB.toBuffer(),
    ],
    CPMM_PROGRAM_ID,
  );

  const [authority] = PublicKey.findProgramAddressSync(
    [Buffer.from("vault_and_lp_mint_auth_seed")],
    CPMM_PROGRAM_ID,
  );

  const [vaultA] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), mintA.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  const [vaultB] = PublicKey.findProgramAddressSync(
    [Buffer.from("pool_vault"), poolState.toBuffer(), mintB.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  const [observation] = PublicKey.findProgramAddressSync(
    [Buffer.from("observation"), poolState.toBuffer()],
    CPMM_PROGRAM_ID,
  );

  return { poolState, authority, vaultA, vaultB, observation, poolBump };
}
```

هذا بالضبط ما تفعله Raydium SDK تحت الغطاء عندما تستدعي `getPoolInfoFromRpc({ poolId })` — يشتق PDAs المرتبطة دون جولة.

## مؤشرات

* [`solana-fundamentals/account-model`](/ar/solana-fundamentals/account-model) — كيف تناسب PDAs نموذج الحساب.
* [`solana-fundamentals/programs-and-anchor`](/ar/solana-fundamentals/programs-and-anchor) — مساعدات Anchor لإعلان PDAs.
* [`integration-guides/cpi-integration`](/ar/integration-guides/cpi-integration) — بناء التكاملات التي تستدعي Raydium عبر CPI.
* [`sdk-api/rust-cpi`](/ar/sdk-api/rust-cpi) — أنواع Rust CPI الخاصة بـ Raydium.

المصادر:

* [Solana PDA docs](https://docs.solana.com/developing/programming-model/calling-between-programs).
* [Anchor PDA & CPI docs](https://www.anchor-lang.com/docs/pdas-and-cpis).
