الانتقال إلى المحتوى الرئيسي

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 ممكناً. تتيح PDAs لبرنامج “امتلاك” عناوين حتمية دون مفاتيح خاصة — هكذا تعمل سلطات وخزائن حمامات السباحة. تتيح CPIs لبرنامج واحد استدعاء آخر — هكذا يقوم Raydium بمبادلة الرموز عبر برنامج SPL Token وكيف يقوم المدمجون بدمج Raydium في تدفقاتهم الخاصة. كلاهما يستحق الفهم قبل قراءة كود Raydium.

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

العنوان المشتق من البرنامج هو مفتاح عام يتميز بـ:
  • أنه لا يقع على منحنى ed25519 (لا يوجد مفتاح خاص له).
  • يُشتق بشكل حتمي من معرّف برنامج ومجموعة من البذور.
  • يمكن التوقيع عليه من قبل فقط البرنامج المشتق منه، عبر invoke_signed.
كل سلطة حمام سباحة في Raydium، وكل حساب حالة حمام سباحة، وكل خزينة، وكل حالة مزرعة — كلها PDAs.

الاشتقاق

يتم حساب PDA بهاش معرّف البرنامج مع البذور، ثم العثور على بايت “نتوء” يفرض النتيجة خارج المنحنى. أول نتوء (عادة يبدأ من 255 وينخفض) ينتج عنه عنوان خارج منحنى هو الفائز؛ هذا هو النتوء الأساسي.
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"] + bumpAMM 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 بحيث يمكن للمعاملات اللاحقة تمريره وتخطي حلقة الاشتقاق (المكلفة):
#[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.

invoke مقابل invoke_signed

يوفر وقت تشغيل Solana بدائل CPI اثنتين:
  • invoke: استدعاء برنامج آخر؛ يرث البرنامج المستدعى الموقّعين من المعاملة الخارجية.
  • invoke_signed: استدعاء برنامج آخر بالنيابة عن PDA؛ يتحقق وقت التشغيل من بذور PDA ويصرح بالتوقيع.
invoke_signed هو السحر الذي يتيح للبرامج الاحتفاظ بالسلطة على الحسابات دون إدارة المفاتيح الخاصة.

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

خزينة حمام السباحة هي حساب رمز يكون مسؤولها هو PDA من برنامج حمام السباحة. لنقل الرموز أثناء المبادلة، يجب على برنامج حمام السباحة التوقيع كـ PDA:
// بذور سلطة حمام السباحة
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:
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 للمثال الكامل للضمان.

حد عمق CPI

يحدّ Solana عمق CPI في 4 مستويات. تعليمة المستوى الأعلى للمعاملة تُحسب كعمق 0؛ كل استدعاء CPI يزيد العمق. التأثير العملي: swap الخاص بـ Raydium بالفعل يستخدم مستوى 1-2 من CPI (Raydium → SPL Token). مدمج يستدعي Raydium يستخدم 2. إذا تم استدعاء هذا المدمج من قبل مدمج آخر، فهو 3. المستوى الرابع هو الحد. معظم الترتيبات تبقى تحت هذا بسهولة، لكن التداخل العميق (المجمّع → الموجّه → Raydium → الخطاف) يمكن أن يصطدم به. صمّم بشكل مسطح بدلاً من العمق.

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

عندما تحتاج تعليمة Raydium إلى عدد متغير من الحسابات (مثلاً، مبادلة CLMM عبر عدد غير معروف من مصفوفات التجزئة)، يتم تمرير الحسابات الإضافية كـ حسابات متبقية — مضافة إلى قائمة الحسابات الثابتة، وتفسيرها حسب الموضع. يستخدم SwapV2 الخاص بـ CPMM الحسابات المتبقية لحسابات البرامج الخطافة الإضافية المطلوبة. يجلب العملاء الحسابات المطلوبة ويضيفونها:
const swapIx = await raydium.cpmm.swap({
  /* ... */
  // تتعامل SDK مع الحسابات المتبقية تلقائياً
});
على مستوى CPI، يجب على المدمجين إعادة توجيه الحسابات المتبقية من خلال تعليماتهم الخاصة:
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

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 المرتبطة دون جولة.

مؤشرات

المصادر: