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 أيضاً نقل مساعداته: get_associated_token_address الآن تحت ::address، ومعرّف البرنامج هو ::program::ID.
للحصول على أمثلة CPI عاملة تربط هياكل الحسابات من البداية إلى النهاية، انظر raydium-io/raydium-cpi-example (يغطي AMM v4 و CPMM و CLMM).

النمط العام لـ 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 المنشور باستخدام ماكرو Anchor declare_program!. احفظ 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_inBuyExactIn)، وأسماء الحقول تطابق أسماء الحسابات في 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.
نقطة بيانات واحدة مقاسة، وليست معيار. تقدير قاعدة الإبهام الشائع لـ CPMM swap CPI (تقريباً 1,500 CU CPI overhead + 150,000 CU للمبادلة نفسها + 10,000 CU لتحديث الملاحظة، ~161,500 CU إجمالي) يبالغ في الاستخدام الفعلي بهامش كبير. مبادلة CPI حقيقية (my_proxy_swap تستدعي swap_base_input، mints SPL-token، مجموعة ثنائية الرمز تم إنشاؤها للتو) استهلكت ~47,700 CU إجمالي، مقروءة من connection.getTransaction(...).meta.computeUnitsConsumed، تقريباً ثلث هذا التقدير. تعامل مع هذا كنقطة بيانات واحدة من شكل مجموعة واحد وتكوين mint واحد، وليس مواصفة. قس معاملتك الخاصة بدلاً من وضع ميزانية من أي رقم.
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 موجوداً لهذا البرنامج.
حالة Anchor CPI الخاصة بـ Farm v6 غير مؤكدة، والأدلة المتاحة تناقضها. لا يوجد IDL عام أو مصدر لـ Farm v6، مما يناقض ادعاء products/farm-staking/code-demos.mdx بوجود صندوق CPI Anchor (raydium_farm_v6) مع هيكل حسابات Deposit.
إذا كنت بحاجة إلى Rust CPI بغض النظر، على سبيل المثال التركيب من برنامج آخر على السلسلة، بناء Instruction يدويًا، بنفس الطريقة كـ AMM v4: اشتق قائمة الحسابات الحقيقية ومميزات التعليمات بشكل مستقل، على سبيل المثال بفك تشفير تخطيطات TypeScript الخاصة بـ SDK (raydium-sdk-V2’s farm module)، فك تشفير معاملات حقيقية مباشرة (انظر أدناه)، أو تفريغ وفك تجميع البرنامج المنشور. لشكل التعليمة بدون حجة المتسق مع استدعاء حصاد أو مطالبة، ترتيب الحساب الحقيقي هو بادئة ثابتة (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) بين إنشاء المجموعة والمبادلة الأولى كافٍ. هذا خاص بالاختبار؛ إنسان يشغّل أمرين منفصلين يدويين لن يلاحظ عادة، لأن الكتابة وبدء العملية تأكل بالفعل أكثر من ثانية.

مؤشرات

المصادر: