Skip to main content
هذه الصفحة مُترجَمة آليًا بواسطة الذكاء الاصطناعي. النسخة الإنجليزية هي المرجع المعتمد.عرض النسخة الإنجليزية →
CPI (“cross-program invocation” أو الاستدعاء بين البرامج) هي الآلية التي يستدعي بها برنامج Solana برنامجًا آخر. تأتي معظم برامج Raydium مع صناديق غلاف CPI مبنية على Anchor تجعل موقع الاستدعاء يبدو وكأنه استدعاء دالة مكتوبة بشكل صحيح، مع هياكل حسابات لها أسماء حقول تم التحقق منها ومساعدات cpi::<ix>(). تتناول هذه الصفحة النمط العام مرة واحدة، ثم الاختلافات لكل برنامج. للحصول على أمثلة TypeScript قابلة للتشغيل، انظر صفحة code-demos من فصل كل منتج.

أي نمط ينطبق على أي برنامج

إذا كنت تدمج CPMM أو CLMM أو LaunchLab، اقرأ النمط العام أولاً، ثم انتقل إلى قسم برنامجك للحصول على قائمة الحسابات وأي اختلافات. Farm v6 و AMM v4 مختلفان بما يكفي لضمان قراءة أقسامهما بشكل مستقل.

تبعيات Cargo

يجب أن يطابق مفتاح التبعية اسم [package] الخاص بمستودع الهدف بالضبط، مع الشرطات المضمنة. لا يعامل Cargo raydium_cp_swap كمكافئ لـ raydium-cp-swap عند حل تبعية git.
branch = "master" يتتبع أحدث مصدر منشور؛ ثبّت على rev = "<commit>" محدد إذا كنت بحاجة إلى بناء قابل للتكرار. يُنصح به بمجرد تجاوزك مرحلة النماذج الأولية، لأن تغيير تخطيط الحساب على master سيكسر بناءك بدون تحذير. علم cpi يجعل الصناديق تُترجم إلى سطح CPI فقط (هياكل الحسابات + المستدعيات) بدلاً من البرنامج الكامل، لذا يبقى ملفك الثنائي صغيرًا. يجب أن يطابق anchor-lang / anchor-spl ما يثبّته صندوق الهدف، واعتبارًا من 2026-09 الصناديق العامة Raydium لا تتفق:
لا يمكنك الاعتماد على كلا الصندوقين من برنامج واحد الآن. كل منهما يثبّت Anchor بـ =، لذا سيضطر Cargo إلى ربط نسختين غير متوافقتين من سمات Anchor في ملف ثنائي واحد، والبناء يفشل. إذا كان برنامجك يستدعي CPI إلى كل من CPMM و CLMM، فيجب عليك إما تقسيمه إلى برنامجين، أو إسقاط صندوق CPI المكتوب لأحدهما وترميز تلك التعليمات يدويًا (النمط الموضح لـ AMM v4 يعمل مع أي برنامج). أعد التحقق من ملفي Cargo.toml قبل البدء — من المتوقع أن يتم حل هذا عندما ينتقل CLMM إلى Anchor 1.x.
غيّر Anchor 1.0 شيئين يلمسهما كل موقع استدعاء CPI. إذا كنت تنقل تكاملاً عاملاً من 0.3x:
  • CpiContext::new يأخذ Pubkey، وليس AccountInfo. CpiContext::new(ctx.accounts.cpmm_program.to_account_info(), accts) يصبح CpiContext::new(*ctx.accounts.cpmm_program.key, accts). نفس الشيء لـ new_with_signer. حقل الهيكل الآن program_id: Pubkey.
  • Context له عمر واحد، وليس أربعة. Context<'_, '_, 'info, 'info, MyProxySwap<'info>> يصبح Context<'info, MyProxySwap<'info>>.
على جانب العميل، anchor-client’s RequestBuilder::instructions() الآن يعيد Vec<Instruction> بدلاً من Result<Vec<Instruction>> (أسقط ?)، و CommitmentConfig انتقل خارج solana-sdk — خذه من anchor_client بدلاً من ذلك. spl-associated-token-account 8.0 يعيد تصدير مساعداته من صندوق spl-associated-token-account-interface الجديد. get_associated_token_address و ID لا تزال قابلة للوصول في جذر الصندوق (spl_associated_token_account::{get_associated_token_address, ID})، لكن مساعدات العنوان مهجورة هناك — فضّل الاعتماد على spl-associated-token-account-interface مباشرة واستيراد spl_associated_token_account_interface::address::get_associated_token_address و spl_associated_token_account_interface::program::ID. لاحظ ::address و ::program هي وحدات من صندوق الواجهة؛ spl_associated_token_account::address::… لا تُحل.
للحصول على أمثلة CPI عاملة تربط هياكل الحسابات من البداية إلى النهاية، انظر raydium-io/raydium-cpi-example (يغطي AMM v4 و CPMM و CLMM). أحدث فرع لها هو anchor-0.31.0 — لا توجد فرع Anchor 1.x حتى الآن، لذا تعامل مع هذا المستودع كمرجع لـ ربط هيكل الحسابات، وليس لدبابيس الإصدار التي تفرضها هذه الصفحة.

النمط العام لـ Anchor CPI

يمشي هذا القسم عبر CPMM من البداية إلى النهاية كمثال معمول به: هيكل Accounts، CpiContext، cpi::<ix>(). يتبع CLMM الشكل المتطابق، مع قائمة حسابات مختلفة ومتطلب remaining-accounts. يتبع LaunchLab نفس الميكانيكا لكن قائمة حساباته تحمل عدة حسابات بدون مكافئ CPMM/CLMM (global_config، platform_config، event_authority، program)، لذا تعامل معها كنفس النمط، وليس نفس الشكل. انظر قسم كل برنامج الخاص به بدلاً من افتراض أن قائمة الحسابات من هذا الشرح تنتقل مباشرة.

بناء قائمة الحسابات

كل CPI من Raydium يتطلب هيكل Accounts في البرنامج الاستدعاء. حقوله هي أي حسابات يحتاجها تعليمك، مع محققات على مستوى الحقل؛ ترتيب إعلانهم لا يجب أن يطابق ترتيب حساب التعليمات الخاص بـ Raydium، لأن عميلك المُنشأ من IDL يعالجهم بالاسم، وليس بالموضع:
معظم حسابات جانب Raydium هي UncheckedAccount لأن المستدعى (Raydium) يمتلك التحقق. برنامجك الاستدعاء يتحقق فقط بشكل صارم من الحسابات التي تمتلكها، مثل ATAs المستخدم و PDAs الخاصة بك. تعليق /// CHECK: يقمع تحذير Anchor حول الفحوصات المفقودة. الاستثناء الوحيد من جانب Raydium هو cpmm_program نفسه: إنه البرنامج الذي يتم استدعاؤه وليس حساب بيانات يتحقق منه Raydium داخليًا، لذا يُكتب كـ Program<T> ويحصل على فحص العنوان التلقائي لـ Anchor بدلاً من فحص /// CHECK: يدوي. هذا الشكل الذي يحتوي على UncheckedAccount في الغالب، حيث يتحقق Raydium من حساباته الخاصة، هو نفسه لـ CLMM و LaunchLab. يفترض هذا المثال أن كلا الـ mints كلاسيكي SPL Token؛ إذا كان أي جانب يمكن أن يكون mint Token-2022، أضف حقل token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> وأمرره كـ input_token_program/output_token_program لذلك الجانب في استدعاء CPI أدناه بدلاً من token_program.

بناء استدعاء CPI

ينشئ Anchor مساعدًا واحدًا لكل تعليمة، جنبًا إلى جنب مع هيكل حسابات CPI (cpi::accounts::Swap، مستعار CpmmSwap أدناه). بخلاف هيكل MyProxySwap الخاص بك أعلاه، أسماء حقول هذا وترتيبه ثابتان بواسطة IDL الخاص بـ raydium-cp-swap ويجب أن يطابقا بالضبط:
cpi::swap_base_input يُنشأ من IDL؛ قائمة حجته تعكس قائمة حجة تعليمة Anchor. كل برنامج Raydium مبني على Anchor مؤكد (CPMM و CLMM و LaunchLab) ينشئ مساعدات cpi::<ix>() الخاصة به بنفس الطريقة، مع اسم الدالة يطابق اسم التعليمة بـ snake_case. ما إذا كان هذا يمتد إلى Farm v6 غير مؤكد؛ انظر قسمه.

بذور الموقّع (CPI موقّع بـ PDA)

عندما يوقّع برنامجك CPI نيابة عن PDA (شائع للأقبية والضمانات، إلخ)، استخدم CpiContext::new_with_signer:
يجب أن تطابق بذور الموقّع اشتقاق PDA. لأي حساب يُمرر كـ authority (أو دور موقّع مشابه)، يتحقق وقت تشغيل Solana من أن PDA يوقّع عبر هذه البذور.

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

بعض تعليمات Raydium تأخذ حسابات متبقية، قائمة بطول متغير مُلحقة بعد الحسابات الثابتة. لا تتحقق مساعدات CPI الخاصة بـ Anchor من الحسابات المتبقية؛ مررها عبر .with_remaining_accounts(...):
الترتيب مهم دائمًا، لأن برنامج المستقبل يكرر الحسابات المتبقية بالترتيب الذي تمررها. ترتيبان مؤكدان:
  • CLMM SwapV2: مصفوفات التجزئة، مرتبة اتجاهيًا.
  • Farm v6: أزواج (reward_vault, user_reward_ata)، لكن فقط من تدفق المكافأة الثاني فصاعدًا؛ انظر Farm v6 لما يظهره فك تشفير معاملة حقيقية.

تطبيق النمط: CLMM

يتبع SwapV2 النمط العام أعلاه مع قائمة حسابات مختلفة ومتطلب remaining-accounts لمصفوفات التجزئة. وحدة #[program] الخاصة بالصندوق تُسمى raydium_clmm، وهي أيضًا مسار Rust use الخاص بها.
هيكل حسابات CPI يُسمى SwapSingleV2، وليس SwapV2. SwapV2 هو اسم التعليمة على السلسلة الفعلي.
احسب قائمة مصفوفة التجزئة بنفس الطريقة التي يفعلها SDK، عبر اقتباس مقابل حالة المجموعة الحالية، بدلاً من تخمين عدد ثابت؛ مبادلة تتجاوز المصفوفات التي مررتها تعود مع TickArrayNotFound (انظر products/clmm/instructions للجدول الكامل للحسابات وقائمة الأخطاء). مررها في اتجاه المسار السعري: أول مصفوفة في اتجاه المبادلة أولاً.

تطبيق النمط: LaunchLab

LaunchLab مبني على Anchor و IDL-منشور: raydium_launchpad/raydium_launchpad.json في مستودع raydium-idl العام. معرّف البيانات الوصفية الداخلية لـ IDL هذا هو raydium_launchpad، اسم تقني للبرنامج الأساسي، وليس اسم بديل للمنتج. بخلاف CPMM و CLMM، مع ذلك، مصدر البرنامج الخاص به غير متاح للعموم (انظر reference/program-addresses). لا توجد تبعية git = "..." للإشارة إلى Cargo، ولا مصدر للتأكد من ما قد يكون مسار Rust use الفعلي للصندوق. أنشئ ربط من IDL المنشور باستخدام ماكرو declare_program! الخاص بـ Anchor. احفظ JSON الخاص بـ IDL كـ idls/raydium_launchpad.json في صندوقك (يبحث Cargo عن مجلد idls/ نسبة إلى CARGO_MANIFEST_DIR)، ثم declare_program!(raydium_launchpad); ينشئ هياكل raydium_launchpad::cpi::accounts::<Ix> ودوال cpi::<ix>() مباشرة من IDL، بدون مصدر برنامج مطلوب. اسم هيكل الحسابات المُنشأ هو دائمًا اسم التعليمة بـ PascalCase (buy_exact_in → BuyExactIn)، وأسماء الحقول تطابق أسماء الحسابات الخاصة بـ IDL بالضبط، قائمة الحسابات نفسها المستخدمة بالفعل في MyProxyBuy أدناه. يتبع شكل CPI النمط العام. قائمة الحسابات والحجج أدناه تأتي من تعليمة buy_exact_in الخاصة بـ IDL على السلسلة، وليس من products/launchlab/instructions.mdx:
بعد التخرج، البرنامج الهدف هو CPMM أو AMM v4 حسب pool_state.migrate_type، والذي تقول products/launchlab/accounts.mdx أنه يُعيّن في وقت Initialize. قائمة حسابات CPI الخاصة بك يجب أن تكون مُعدّة لأي منهما، أو تحتاج إلى قراءة migrate_type من PoolState أولاً والتفرع.

نشر الأخطاء

كل برنامج Raydium مبني على Anchor يعيد enum أخطاء خاص به؛ Anchor يلفه، لذا يرى برنامجك الاستدعاء Err(ProgramError::Custom(code)). للتعامل مع أخطاء محددة:
استبدل نوع الخطأ ذا الصلة للبرنامج الذي تستدعيه (raydium_clmm::error::ErrorCode لـ CLMM، وهكذا). أرقام رموز الأخطاء مستقرة وفقًا لسياسة IDL (sdk-api/anchor-idl)، لذا يمكنك الاختبار مقابل رموز محددة بمقارنة القيمة الرقمية. جداول الأخطاء الكاملة: CPMM، CLMM، AMM v4 و Farm v6 و LaunchLab.

ميزانية الحساب في CPIs المركبة

لكل إطار CPI نفقات عامة، واستهلاك CU الخاص بالمستدعى يتراكم فوق ملكك، لذا معاملة تستدعي Raydium من داخل برنامجك تحتاج إلى ميزانية حساب صريحة بدلاً من الاعتماد على الحد الافتراضي 200k CU.
مقاس، وليس مقدّر. swap_base_input الخاص بـ CPMM على mainnet يستهلك ~23,000 CU في برنامج CPMM نفسه — تم أخذ عينات في 2026-09-09 عبر ثماني مبادلات حية على مجموعة عالية الحجم (22,721–23,052)، مقروءة من سطر السجل Program CPMMoo8… consumed N of M compute units. للمقارنة: مبادلة AMM v4 ~26,000؛ CLMM swap ~41,000؛ CLMM swap_v2 ~48,000 (43,838–52,887)، ترتفع مع كل عبور تجزئة.نسخة سابقة من هذه الصفحة أبلغت عن ~47,700 CU لـ CPI proxy-swap. كان هذا الرقم المعاملة الكاملة (computeUnitsConsumed)، والذي يشمل برنامجك الخاص والإطار CPI وأي إعداد ATA — وليس تكلفة المستدعى. كلاهما مفيد، لكنهما ليسا نفس الرقم، لذا قارن مثل مع مثل. قس معاملتك الخاصة بدلاً من ميزانية من أي رقم.
CPIs الخاصة بـ CLMM و LaunchLab تكلف أكثر (CLMM بشكل خاص يمشي مصفوفات تجزئة إضافية عبر remaining_accounts، مضيفًا CU لكل مصفوفة)، لكن فقط الرقم الخاص بـ CPMM أعلاه هو قيمة مقاسة. اضبط دائمًا تعليمة ComputeBudgetProgram::set_compute_unit_limit(...) صريحة بحجم من قياسك الخاص، وليس رقمًا منسوخًا من التوثيق، لأن حد 200k CU الافتراضي سيستنزف بصمت وتكاليف كل تعليمة تتحول مع ترقية البرامج.

AMM v4: بناء التعليمة اليدوي

AMM v4 يسبق Anchor وليس لديه صندوق CPI، مما يجعله البرنامج الوحيد في هذا المستند الذي لا يتبع النمط العام أعلاه. بناء Instruction يدويًا:
انظر products/amm-v4/code-demos للحصول على قائمة الحسابات الكاملة.

Farm v6

استخدم TS SDK إذا كان هذا خيارًا لتكاملك. raydium.farm.deposit(...) (انظر products/farm-staking/code-demos) يتم ممارسته من قبل عروض توضيحية حقيقية ولا يعتمد على ما إذا كان صندوق Rust Anchor موجودًا لهذا البرنامج.
Farm v6 لا يوفر مسار CPI Anchor. لا يوجد صندوق raydium_farm_v6 على crates.io، لا مستودع مصدر عام، ولا IDL على السلسلة — البرنامج ليس لديه حساب anchor:idl قديم ولا إدخال في برنامج Program Metadata (انظر sdk-api/anchor-idl). تعامل معه كبرنامج غير Anchor وبناء تعليماته يدويًا، كما هو موضح أدناه.
إذا كنت بحاجة إلى Rust CPI على أي حال، على سبيل المثال التكوين من برنامج آخر على السلسلة، بناء Instruction يدويًا، بنفس الطريقة كـ AMM v4: اشتق قائمة الحسابات الحقيقية ومميزات التعليمات بشكل مستقل، على سبيل المثال بفك تشفير تخطيطات TypeScript الخاصة بـ SDK (raydium-sdk-V2’s farm module)، فك تشفير معاملات حقيقية مباشرة (انظر أدناه)، أو تفريغ وفك تجميع البرنامج المنشور. لشكل التعليمة بدون حجة متسق مع استدعاء harvest أو claim، ترتيب الحساب الحقيقي هو بادئة ثابتة (token_program، حساب حالة المزرعة، PDA سلطة الخزان، أول خزان مكافأة لـ PDA، PDA ثانية، المستدعي، و ATA المستدعي لأول رمز مكافأة)، متبوعًا بأزواج (reward_vault_i, user_reward_ata_i) في remaining_accounts لكل تدفق مكافأة بعد الأول. اتفاقية الاقتران حقيقية، لكنها تبدأ فقط في تدفق المكافأة الثاني: خزان وـ ATA تدفق المكافأة الأول ثابتة، وليست مجاورة لبعضها البعض، وليست جزءًا من remaining_accounts على الإطلاق.

اختبار تدفق CPI

يتطلب dev المحلي توفر برامج Raydium في مدقق الاختبار المحلي الخاص بك. ثلاثة خيارات:
  1. anchor test مع استنساخ البرنامج. يسحب bytecode mainnet المنشور إلى مدقق الاختبار المحلي الخاص بك؛ انظر استنساخ البرامج إلى مدقق محلي أدناه لتكوين Anchor.toml وشيئين يعثران على اختبارات إنشاء المجموعة بشكل خاص.
  2. Devnet. ينشر Raydium معظم البرامج إلى devnet، لكن في معرفات برامج مختلفة عن mainnet لكل برنامج (CPMM و CLMM و AMM v4 و Stable AMM و LaunchLab لكل منها عنوان devnet مميز؛ انظر جدول Devnet في reference/program-addresses). لا يتم نشر Farm v3/v5/v6 بشكل موثوق على devnet؛ API المباشر (https://api-v3-devnet.raydium.io/main/info) لديه الصورة الحالية. إذا استخدمت ثوابت DEVNET_PROGRAM_ID المدمجة في raydium_clmm (أو ما يعادلها للبرامج الأخرى)، لا تفترض أن معرف mainnet يعمل أيضًا على devnet. قم بتشغيل anchor test --provider.cluster devnet للوصول إلى الكود المباشر بمجرد حصولك على العناوين الصحيحة.
  3. نشر محلي. استنسخ مستودعات Raydium (CPMM و CLMM؛ مصدر LaunchLab غير متاح لهذا الخيار) و anchor deploy إلى مدقق محلي. يضيف نفقات عامة لدورة الاختبار لكن يسمح لك بتعديل المستدعى للتصحيح.
قم بالتشغيل باستخدام anchor test، أو anchor build أولاً و anchor test --skip-build بعد ذلك إذا كنت تكرر على ملف الاختبار بدون تغيير البرنامج.

استنساخ البرامج إلى مدقق محلي

يعمل هذا بمعرف البرنامج بغض النظر عما إذا كان مصدر البرنامج عامًا، لذا يستنسخ LaunchLab بنفس الطريقة CPMM و CLMM حتى لو لم يكن مصدره متاحًا. reference/program-addresses هو مصدر الحقيقة لكل عنوان هنا.
استنساخ البرنامج ليس كافيًا إذا كان اختبارك أيضًا ينشئ مجموعة (بدلاً من المبادلة مقابل واحدة موجودة بالفعل). تعليمة initialize الخاصة بـ CPMM تتحقق من حسابات amm_config و create_pool_fee الخاصة بها مقابل بيانات حقيقية على السلسلة، لذا تحتاج إلى استنساخ تلك أيضًا، أو initialize يفشل تمامًا. لـ CPMM بشكل خاص: استنسخ AmmConfig لفئة الرسوم التي تريدها (احصل على عنوانها من GET https://api-v3.raydium.io/main/cpmm-config، الفهرس 0 هو فئة 0.25%) و حساب رمز مستقبل الرسوم، تم التحقق من قبل العنوان الدقيق، وليس تم إنشاؤه على الطاير، لذا يجب أن يكون موجودًا بالفعل.
مجموعة أنشأها اختبارك للتو ليست قابلة للمبادلة في نفس اللحظة. initialize الخاص بـ CPMM يتجاوز بصمت open_time مطلوب ليس بدقة في المستقبل (if open_time <= block_timestamp { open_time = block_timestamp + 1 })، لذا حتى startTime: 0 (“افتح فورًا،” حسب SDK) يترك فجوة حقيقية ≥1 ثانية قبل أن تقبل المجموعة المبادلات. اختبار ينشئ مجموعة ويبادل ضدها بدون تأخير سيضرب NotApproved. await قصير (1–2s) بين إنشاء المجموعة والمبادلة الأولى كافٍ. هذا خاص بالاختبار؛ إنسان يشغل أمرين منفصلين يدويًا لن يلاحظ عادة، لأن الكتابة وبدء العملية تأكل بالفعل أكثر من ثانية.

مؤشرات

المصادر: