> For the complete documentation index, see [llms.txt](https://docs.rome.builders/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rome.builders/ar/adlh-almtw-ryn/integrate-solana-program.md).

# دمج برنامج سولانا

دمج برنامج Solana مع Rome EVM — من هو الموقّع في Solana، وكيف تصل القيمة إلى برنامجك وتعود منه، وقاعدة المسار الواحد، والذرّي مقابل التكراري، وتوفير الحسابات.

هذا الدليل مخصص لفريق برنامج على Solana يريد أن يستخدم مستخدمو Rome EVM برنامجه — برنامج خصوصية أو إقراض أو خزينة ينبغي أن يحتفظ بالقيمة لحسابات EVM. وهو يبني على [استدعِ Solana من EVM](/ar/adlh-almtw-ryn/call-solana-from-evm.md) (آليات الـ precompile) ويجيب عن الأسئلة التي تظهر بمجرد أن يوجد برنامج فعلي في الطرف الآخر: *من* يوقّع، *كيف* تدخل الرموز وتخرج، و *أي* القيود تشكّل التصميم.

التدفق المستخدم في هذا الدليل: ينقل حساب EVM عملات USDC إلى برنامجك («shield» أو «deposit»)، ويحتفظ برنامجك بها لذلك المستخدم، ثم يفرج عنها لاحقًا («unshield» أو «withdraw»). استبدل الأفعال بأفعالك الخاصة.

## 1. لكل عنوان EVM هوية Solana واحدة

يشتق Rome حساب Solana لكل عنوان EVM — للمستخدمين والعقود على حد سواء — ويُسمّى **السلطة الخارجية**:

```
external_auth(addr) = find_program_address(["EXTERNAL_AUTHORITY", addr_h160], rome_evm_program_id)
```

وهو يملك حسابات الرموز المرتبطة بالعنوان (وهذا هو المكان الذي `wUSDC.balanceOf(addr)` يقرأ منه) وهو المفتاح الذي يوقّع عمليات CPI المنفذة نيابةً عن ذلك العنوان. يرى برنامجك مفتاح Solana عامًا بطول 32 بايت؛ ولا يرى H160 مطلقًا.

**يُحسم من يوقّع بحسب من يستدعي الـ precompile:**

| يستدعي الـ precompile                                               | موقّع Solana                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| تطبيق **العقد**، مع `CALL`                                          | الـ **الخاص بالعقد** السلطة الخارجية (`external_auth(contract)`)، إضافةً إلى أي PDAs فرعية مملحة للعقد يطلبها                                                                                                                                                                                                                                                                                                                                                                                                                      |
| تطبيق **المستخدم**، في معاملة تُرسل مباشرة إلى عنوان الـ precompile | الـ **الخاصة بالمستخدم** السلطة الخارجية                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| عقد عبر `DELEGATECALL`                                              | تُرفض السلطة المجردة (`DelegatecallOwnerAuthority`). `invoke` من دون موقّع، و `invoke_signed` مع PDAs فرعية مملحة، تظل تعمل — لكن تلك الـ PDAs الفرعية تقع ضمن نطاق عنوان العقد الجاري تشغيله (`["EXTERNAL_AUTHORITY", caller, running_contract, salt]`), لذلك `pda_with_salt` لا يشتقها. ومن HelperProgram لا تمر إلا محددات الإنشاء والتمويل (`create_ata*`, `create_pda*`, `swap_gas_to_lamports`, `init_spl_mint`, `create_and_init_mint`)؛ وهي مستثناة لأن هدفها مرتبط بالمستدعي أو ممول من المشغّل، لا لأنها لا توقّع أي شيء |

والنتيجة هي قاعدة ERC-20 التي تعرفها بالفعل: **لا يمكن للعقد نقل أصول المستخدم بمفرده.** يتصرف بهويته الخاصة، ويمنحه المستخدم الصلاحيات أولًا. بالنسبة إلى الرموز التي يجب أن تتحرك على جانب Solana، تكون تلك المنحة تفويض SPL على حساب رموز المستخدم؛ أما ERC-20 الخاص بالغلاف `approve` فهو مجرد سماح على جانب EVM تحترمه تحويلات الغلاف نفسه. هكذا تعمل أغلفة رموز Rome نفسها، وهكذا سيعمل المهايئ الخاص بك (القسم 3).

PDAs الفرعية المملحة: `invoke_signed` يأخذ قائمة من `bytes32` الأملاح، ويوقّع Rome باستخدام `["EXTERNAL_AUTHORITY", caller_h160, salt]` لكل منها (مع إلحاق bump؛ ويكون المستدعي هو العقد عند الاستدعاء المباشر). `HelperProgram.pda_with_salt(addr, salt)` يعيد العنوان من Solidity. استخدم ملحًا واحدًا لكل مستخدم (مثلًا عنوان المستخدم) عندما يريد برنامجك حساب مالك متميزًا لكل مركز.

التمويل: لا يلزم أن توجد PDA سلطة خارجية لكي تملك ATA (`create_ata` متكرر بأمان، ويدفع دافع رسوم Solana للمعاملة الإيجار). يجب أن توجد وتحتفظ بـ lamports فقط عندما تموّل حسابات تنشئها تعليمتك. `HelperProgram.create_pda(addr)` ينشئها كحساب System عادي `CreateAccount`، لذا استدعه مرة واحدة؛ فهو ليس متكررًا بأمان. `swap_gas_to_lamports(n)` يضيف إلى PDA العنوان `n` lamports من دافع رسوم Solana للمعاملة، ويدفع المستدعي ثمنها بغاز Rome: يُضاف أثر كل CPI في المسار القديم على رصيد دافع الرسوم إلى رسوم المعاملة ويُحمّل على المستخدم بسعر الغاز، بالطريقة نفسها التي يُحمّل بها إيجار الحسابات التي ينشئها CPI. تُوصَف خطوة التجهيز لمسار Solana في [استدعِ EVM من Solana](/ar/adlh-almtw-ryn/call-evm-from-solana.md)؛ ويجهز العقد PDA الخاصة به في المُنشئ أو عند أول استدعاء.

## 2. ثلاث طرق للاحتفاظ بمركز المستخدم

**أ. عقد مهايئ (موصى به لمهايئ قابل لإعادة الاستخدام).** يقف عقد Solidity واحد أمام برنامجك. يمنح المستخدم سلطة المهايئ تفويض SPL على حساب رموزه؛ ويسحب المهايئ الرموز إلى حساب رموز يملكه، ويستدعي برنامجك بصفته هو، ويؤشر مركز المستخدم داخل برنامجك بواسطة PDA فرعية لكل مستخدم أو بواسطة معرّف يمرره في بيانات التعليمة. وعند الخروج، يطلب من برنامجك الإفراج إلى حساب رموز المستخدم نفسه. المهايئ هو هوية Solana الوحيدة التي يحتاج برنامجك إلى معرفتها. المنحة هي تفويض SPL، وليست ERC-20 `approve`؛ يشرح القسم 3 السبب والتكلفة على المستخدم.

**ب. المستخدم بصفته موقّع Solana.** ترسل الواجهة الأمامية معاملة من حساب EVM الخاص بالمستخدم **مباشرة إلى precompile الخاص بـ CpiProgram** (`to = 0xff…08`، وcalldata = `invoke` أو `invoke_signed`المشفّرة وفق ABI)، لذا توقّع السلطة الخارجية للمستخدم نفسه ويؤشر برنامجك المركز بها. لا يوجد عقد مهايئ ولا تفويض، لكن كل إجراء يكون معاملة calldata معتمة في المحفظة، وأي شيء يحتاجه التدفق بجانب CPI (مثل ATA أو تحويل سابق) يمثل معاملة منفصلة. يناسب هذا المنح لمرة واحدة ( `approve_spl` الخاصة بالمستخدم إلى غلاف) والبرامج البسيطة، وليس التدفقات متعددة الخطوات. [Cardo](https://github.com/rome-protocol/cardo) يستخدم هذا الشكل: ترسل واجهته الأمامية `invoke` مباشرة إلى الـ precompile من محفظة المستخدم.

**ج. موجّه بلا وصاية.** عقد لا يحتفظ أبدًا برموز المستخدم. يمنح المستخدم سلطة الموجّه تفويض SPL على حساب رموزه الخاص؛ وينفذ الموجّه CPI إلى برنامجك بتلك السلطة بوصفها سلطة التحويل، فتنتقل الرموز من حساب رموز المستخدم إلى حساب برنامجك (أو عائدةً إلى المستخدم)، ولا يحتفظ الموجّه بشيء بين الاستدعاءات. يشتق الموجّه كل حساب من جانب المستخدم من `msg.sender`، لذا لا يمكن توجيه منحة مستخدم إلى حساب مستخدم آخر، ويستدعي برنامجًا ثابتًا واحدًا بأشكال تعليمات ثابتة بدلًا من التصرف كـ `invoke` ممرر عام. إن `RomeDexRouter` في [rome-dex](https://github.com/rome-protocol/rome-dex) مكتوب بهذه الطريقة؛ كما أن مسار EVM الخاص بـ rome-dex يقدم أيضًا المسار المباشر الخام `invoke` بالشكل ب. يحتاج إلى المنحة نفسها مثل أ، مع تكلفة المفوّض الواحد نفسها (القسم 3)، ومن دون خطوة سحب.

يتبع بقية هذا الدليل أ؛ ويستخدم ج المنحة نفسها وقاعدة التوقيع نفسها.

## 3. إدخال الرموز وإخراجها

USDC على Rome هو mint واحد من SPL يُرى من جانبين: `wUSDC` غلاف ERC-20 على جانب EVM وATA الخاصة بالسلطة الخارجية على جانب Solana. بالنسبة إلى عنوان المستخدم نفسه، يمثل الاثنان الرصيد نفسه: `wUSDC.balanceOf(user)` يقرأ ATA الخاصة بالمستخدم. لا توجد خطوة جسر أو تغليف بينهما.

**رصيد غلاف العقد ليس حساب رموز يتحكم فيه العقد.** ينشر المصنع `SPL_ERC20_cached` لـ mint عادي، وHadrian's `wUSDC` واحد منها. لا يستطيع هذا الغلاف التوقيع لعقد، لذا عندما `wUSDC.transferFrom(user, adapter, amount)` أو `transfer` يرسل الرموز إلى عقد، تنتقل رموز SPL إلى **حساب ضمان الرموز الخاص بالغلاف** (`escrow_ata()`), ويسجل الغلاف المبلغ لكل عقد في تخزينه. `wUSDC.balanceOf(adapter)` يعيد ذلك السجل، وهو دفتر أستاذ ضمان، وليس رصيد حساب رموز مملوك بواسطة `external_auth(adapter)`. لا يستطيع المهايئ تسليم تلك الرموز إلى برنامجك في CPI؛ إذ لا يمكنها الخروج إلا عبر الغلاف مجددًا. لذلك، فإن مهايئًا يمول نفسه باستخدام `transferFrom` ليس لديه شيء يمكنه إنفاقه على جانب Solana.

المسار الذي يعمل هو تفويض SPL:

* **المنح.** يرسل المستخدم `approve_spl(adapter, amount, mint)` كمعاملة **مباشرة إلى HelperProgram** (`to = 0xff…09`)، حيث `mint` هو الخاص بالغلاف `mint_id()`. توقّع سلطة المستخدم نفسها، ويكون المفوّض الذي تعيّنه على حساب رموز المستخدم هو السلطة الخارجية للمهايئ.
* **إدخال.** الخاص بالمهايئ `deposit(amount)` يسحب باستخدام التحميل الزائد للمفوّض `HelperProgram.transfer_spl(srcAta, dstAta, amount, mint)`، مع ATA المستخدم كمصدر وATA الخاصة بالمهايئ (`HelperProgram.ata(address(this), mint)`، التي تُنشأ مرة واحدة باستخدام `create_ata`) كوجهة. توقّع سلطة المهايئ بوصفها المفوّض. أصبحت الرموز الآن في حساب رموز مملوك بواسطة `external_auth(adapter)`، وهو الحساب الذي يسميه CPI الخاص بك؛ والموقّع والمصدر تابعان للمهايئ نفسه، لذا لا يلزم إذن إضافي. لا تظهر في `wUSDC.balanceOf(adapter)`.
* **إخراج.** تنقل تعليمة السحب في برنامجك إلى **الخاصة بالمستخدم** ATA، `HelperProgram.ata(user, mint)`. لا يتطلب الاستلام توقيعًا من المستخدم، ويظهر الرصيد فورًا بوصفه `wUSDC.balanceOf(user)`. إذا كان من المحتمل ألا تكون ATA موجودة بعد، فأنشئها أولًا باستخدام `HelperProgram.create_ata(user, mint)` (متكرر بأمان).

**لحساب الرموز مفوّض واحد.** تحل المنحة أعلاه محل منحة الغلاف نفسه على ذلك الحساب. وإلى أن يمنح المستخدم الغلاف مجددًا، فإن كل `wUSDC` `transfer` أو `transferFrom` من عنوانه يتراجع بخطأ SPL `0x4`. بعد أي تدفق يستخدم هذه المنحة، استدعِ `wUSDC.isEnabled(user)`؛ وعندما يعيد `false`، اطلب من المستخدم إرسال `approve_spl(wrapper, 2^64-1, mint)` إلى HelperProgram مرة أخرى، وهي المنحة القصوى نفسها التي بُني الغلاف عليها (انظر [لمرة واحدة: امنح الغلاف](/ar/adlh-almtw-ryn/dual-lane-app.md#one-time-grant-the-wrapper)). ستؤدي المنحة المحددة بسقف إلى كسر عمليات `wUSDC` التحويل العادية للمستخدم مرة أخرى عند انخفاضها، و `isEnabled` يبقى `true` ما دام أي جزء منها متبقيًا، لذا لن يعرف التطبيق أن يطلب مرة أخرى.

للتحقق من الغلاف الذي يملكه mint، اقرأ الخاص بالمصنع `wrapper_kind_by_mint(mint)` (عنوان المصنع موجود في `contracts.json` الخاصة بالسلسلة). إن [السجل](https://github.com/rome-protocol/rome-registry)). `1` هو الغلاف المخزن مؤقتًا الذي يصفه هذا القسم. `2` هو غلاف CPI المباشر الذي ينشره المصنع لـ Token-2022 mint مع خطاف تحويل مفعّل؛ ولا يغطيه هذا الدليل. إن `tokens.json` الخاص بالسجل يسرد عنوان الغلاف وmint لكن لا يبين نوعه.

## 4. مسار واحد لكل عقد، ولكل معاملة

لدى Rome عائلتان من precompiles تغيران حالة Solana، و **تستخدم المعاملة واحدة منهما فقط** — يرفض وقت التشغيل معاملة تمزج الاستدعاءات المعدلة من كلتيهما، حتى عبر العقود.

| المسار          | Precompiles                                                                                         | موضع تطبيق الآثار                                                              | يعمل في التنفيذ التكراري                                                 |
| --------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| **قديم**        | `CpiProgram` `0xff…08` (`invoke`, `invoke_signed`), `HelperProgram` `0xff…09`, `سحب` `0x42…16`      | فورًا، باعتباره CPI حقيقيًا داخل خطوة EVM                                      | **لا** — يفشل CPI قديم في معاملة تكرارية مع `CpiProhibitedInIterativeTx` |
| **مخزن مؤقتًا** | `SplCached` `0xff…05`, `ASplCached` `0xff…06`, `SystemCached` `0xff…04`, `WithdrawCached` `0xff…0b` | تُرحّل في طبقة تراكب وتُنفذ كـ CPIs عند نهاية المعاملة؛ ويتخلص EVM revert منها | نعم                                                                      |

ما يعنيه ذلك لتكامل البرنامج:

* **استدعاء تعليمات برنامجك الخاص يقتصر على المسار القديم.** لا يحتوي المسار المخزن مؤقتًا على `invoke`عام؛ إذ يغطي تحويلات SPL وإنشاء ATA وعمليات برنامج النظام وسحب الغاز.
* لذا يجب أن تكون خطوة تمويل المهايئ من المسار القديم أيضًا إذا نُفذت في المعاملة نفسها التي تضم CPI. إن الخاص بالغلاف المخزن مؤقتًا `transferFrom` هو استدعاء من المسار المخزن مؤقتًا، لذلك لا يمكنه مشاركة معاملة مع CPI الخاص بك (وكما يبين القسم 3، فإنه سيضيف رصيدًا إلى دفتر ضمان فقط). اسحب باستخدام `HelperProgram.transfer_spl(userAta, adapterAta, amount, mint)` بدلًا من ذلك، بعد أن يجعل المستخدم المهايئ مفوّض SPL (`approve_spl`، يرسله المستخدم مباشرة إلى HelperProgram). يكون السحب وCPI كلاهما من المسار القديم، لذا يعملان في معاملة واحدة.
* **أبقِ الغلاف خارج تلك المعاملة.** لا تستدعِ دوال الغلاف المعدّلة (`wUSDC.transfer`, `transferFrom`) من المهايئ في المعاملة نفسها التي تضم CPI قديمًا. تحقق من `isEnabled(user)` والأرصدة من الواجهة الأمامية باستخدام `eth_call`.
* **ويجب أن تكون معاملة CPI ذرية.** هذا هو القيد الذي يقع التصميم تحته.

عمليات القراءة خارج القاعدة: `account_info`, `account_data_at`, `account_u64_at`, `account_lamports`, `pdas_batch_derive` و `HelperProgram.user_balance` / `allowance_of` هي قراءات لحسابات محمّلة مسبقًا (دون CPI)، ومسموح بها في أي من المسارين وفي نمطي التنفيذ كليهما. ضع فحوصاتك المسبقة عليها. قاعدة ترتيب واحدة: بعد تنفيذ طفرة مخزنة مؤقتًا في معاملة، تُحظر قراءات المسار القديم لبقية المعاملة — اقرأ أولًا، ثم عدّل.

## 5. الذرية هي الميزانية

معاملة Rome الذرية هي معاملة Solana واحدة: نحو **1.4M وحدة حوسبة**، وقائمة حسابات من **64 عنوانًا** وحجم على السلك قدره **4 كيلوبايت** (معاملات Solana v1). يشترك برنامج Rome وCPI الخاص بك في تلك الميزانية. يستهلك تحويل الغلاف نحو 170K CU على Hadrian وتضيف تعليمتك تكلفتها الخاصة؛ قِس باستخدام الخاص بإيصال Solana `computeUnitsConsumed`، وليس EVM `gasUsed`.

عندما تقرر المحاكاة أن المعاملة لا تتسع، يشغلها Rome **تكراريًا** عبر عدة معاملات Solana، ويفشل عندئذ CPI قديم بداخلها. اجعل كلًا من الإيداع والسحب يقتصر على حركة رموز واحدة إضافة إلى CPI واحد إلى برنامجك، واجعل Solidity المحيط بهما صغيرًا. قسّم أي شيء أكبر إلى معاملتين للمستخدم.

عمق CPI: يسمح Solana بأربعة مستويات ويستخدم Rome واحدًا، لذا تعمل تعليمتك في المستوى 2 مع مستويين تحته (برنامجك ← SPL Token يترك مستوى واحدًا).

## 6. تجهيز الحسابات

لا يتلقى برنامجك حساباته من Solidity وقت التشغيل. يقوم الوكيل **بمحاكاة** كل معاملة قبل إرسالها، ويسجل كل حساب Solana لمسته عمليات CPI، ويضعها في معاملة Solana. يسمي المهايئ حسابات في `invoke_signed` قائمة الحسابات؛ وتثبت المحاكاة أنها قابلة للحل. تترتب على ذلك ثلاثة أمور:

* يجب أن يكون كل حساب تلمسه تعليمتك قابلًا للاشتقاق من مدخلات يملكها المهايئ (PDA الخاصة به وPDAs الفرعية، ومعرّف برنامجك، وmints، وعنوان المستخدم). `SystemProgram.find_program_address` و `CpiProgram.pdas_batch_derive` ينفذان الاشتقاقات في Solidity.
* يجب أن يظل الحساب غير الموجود بعد (مركز جديد أو ATA جديدة) مسمى في قائمة الحسابات؛ فعنوانه هو ما يصل إلى معاملة Solana.
* يجب أن يبقى كل ما تلمسه المعاملة — بما في ذلك حسابات Rome الخاصة بالمستخدم والمهايئ والغلاف — تحت 64 عنوانًا. لن يتسع تصميم يلمس عشرة أسواق في استدعاء واحد.

بالنسبة إلى مسار محفظة Solana، `rome_emulateCallAccounts` يكشف الاكتشاف نفسه للعميل؛ وتستخدمه SDK.

حسابان لا يجوز لـ CPI تسميتهما مطلقًا: دافع رسوم Solana الذي يرسل المعاملة (باستثناء داخل تعليمة تحويل System عادية) وبرنامج Rome نفسه. يُرفض CPI عائد إلى برنامج Rome.

## 7. إيداع وسحب، في Solidity

مخطط أدنى لمهايئ على المسار القديم. أرسل المستخدم مسبقًا `approve_spl(adapter, amount, mint)` إلى HelperProgram (القسم 3). `PRIVACY_PROGRAM` هو معرّف برنامجك؛ `encodeDeposit` / `encodeWithdraw` ينشئان بيانات تعليمتك (Borsh، بترتيب little-endian)؛ وتتبع بيانات تعريف الحسابات تخطيط تعليمتك.

```solidity
import {ICrossProgramInvocation, IHelperProgram}
    from "@rome-protocol/rome-solidity/contracts/interface.sol";

contract PrivacyAdapter {
    ICrossProgramInvocation constant Cpi    = ICrossProgramInvocation(0xFF00000000000000000000000000000000000008);
    IHelperProgram          constant Helper = IHelperProgram(0xFF00000000000000000000000000000000000009);
    bytes32 constant SPL_TOKEN = 0x06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9; // TokenkegQ...

    bytes32 immutable PRIVACY_PROGRAM;
    bytes32 immutable USDC_MINT;

    constructor(bytes32 program, bytes32 mint) {
        PRIVACY_PROGRAM = program; USDC_MINT = mint;
        Helper.create_pda(address(this));                 // هوية Solana الخاصة بالمهايئ
        Helper.create_ata(address(this), USDC_MINT);      // وحساب USDC يملكه
    }

    function _salt(address user) internal pure returns (bytes32) { return bytes32(uint256(uint160(user))); }

    function deposit(uint64 amount) external {
        bytes32 self       = Helper.pda(address(this));                   // الموقّع: سلطة المهايئ
        bytes32 adapterAta = Helper.ata(address(this), USDC_MINT);
        bytes32 position   = Helper.pda_with_salt(address(this), _salt(msg.sender)); // PDA فرعية لكل مستخدم، توقّع أيضًا

        // اسحب من حساب رموز المستخدم. توقّع سلطة المهايئ بصفتها مفوّض SPL
        // الذي منحه المستخدم؛ وهذا استدعاء من المسار القديم، مثل CPI أدناه.
        Helper.transfer_spl(Helper.ata(msg.sender, USDC_MINT), adapterAta, amount, USDC_MINT);

        ICrossProgramInvocation.AccountMeta[] memory accs = new ICrossProgramInvocation.AccountMeta[](5);
        accs[0] = ICrossProgramInvocation.AccountMeta(self,       true,  true);
        accs[1] = ICrossProgramInvocation.AccountMeta(position,   true,  true);
        accs[2] = ICrossProgramInvocation.AccountMeta(adapterAta, false, true);                          // المصدر: مملوك لسلطة المهايئ
        accs[3] = ICrossProgramInvocation.AccountMeta(_vaultFor(position), false, true);                 // خزينة برنامجك
        accs[4] = ICrossProgramInvocation.AccountMeta(SPL_TOKEN, false, false);

        bytes32[] memory salts = new bytes32[](1);
        salts[0] = _salt(msg.sender);
        Cpi.invoke_signed(PRIVACY_PROGRAM, accs, encodeDeposit(amount), salts);
    }

    function withdraw(uint64 amount) external {
        bytes32 position = Helper.pda_with_salt(address(this), _salt(msg.sender));
        bytes32 userAta  = Helper.ata(msg.sender, USDC_MINT);            // الوجهة: حساب المستخدم نفسه
        // ... الشكل نفسه: ينقل برنامجك `amount` من الخزينة إلى userAta، موقّعًا بواسطة `position`
        Cpi.invoke_signed(PRIVACY_PROGRAM, accs, encodeWithdraw(amount), salts);
    }
}
```

لا يستدعي المهايئ أبدًا `wUSDC` عقد. إن السحب استدعاء HelperProgram ينفق تفويض المستخدم، وتوجد الرموز في حساب رموز `external_auth(adapter)` يملكه، ولهذا يستطيع CPI إنفاقها.

ملاحظات عن الموقّع. يوقّع Rome لنوعين فقط من الحسابات: سلطة العقد المستدعي المجردة (`Helper.pda(address(this))`), التي توقّع دون إدراجها في `salts`، وPDA فرعية مملحة واحدة لكل إدخال في `salts`. يجب أن يظهر كل ملح تسرده في قائمة الحسابات بصفته موقّعًا، وإلا يفشل الاستدعاء مع `signer account calculated from the provided seed was not found`. أي حساب آخر معلم بصفته موقّعًا، بما فيه سلطة المستخدم نفسه، لا يحصل على seed من Rome، ويرفض وقت تشغيل Solana عملية CPI. تأتي صلاحيات المستخدم على رموزه من التفويض، وليس أبدًا من توقيع المهايئ بوصفه المستخدم.

تفويض دون سحب: إذا كان ينبغي لبرنامجك أن ينفق مباشرة من حساب رموز المستخدم، يرسل المستخدم `HelperProgram.approve_spl_raw_delegate(userAta, yourProgramPda, amount, mint, decimals)` مباشرة إلى HelperProgram، مع تسمية PDA الخاصة ببرنامجك بوصفها مفوّض SPL. يستخدم جسر Rome هذا لـ Wormhole. تنطبق هنا أيضًا قاعدة المفوّض الواحد من القسم 3: إذ تحل المنحة محل منحة الغلاف.

## 8. الاختبار

* انشر على **هادريان** (الشبكة التجريبية، السلسلة 200010) أولاً؛ العناوين وRPC موجودة في [عناوين العقود](/ar/almrja/contract-addresses.md) و [السجل](https://github.com/rome-protocol/rome-registry).
* `eth_call` و `eth_estimateGas` يُشغّل نفس المحاكاة التي يستخدمها الوكيل للإرسال، لذا فإن أي CPI يفشل هناك سيفشل على السلسلة للسبب نفسه، وتحمل بيانات الإرجاع الخطأ الأساسي في سولانا (`خطأ استدعاء غير EVM: …` في سجل برنامج سولانا).
* اقرأ جانب سولانا من معاملة EVM التي تم تنفيذها باستخدام `rome_solanaTxForEvmTx(hash)`؛ إيصال سولانا هو المكان الذي توجد فيه وحدات CU وسجلات برنامجك.
* أي CPI يعمل في `eth_call` ولكنه يُرفض عند الإرسال يكون عادة قد تجاوز حدًا تُبلّغ عنه المحاكاة: حد 64 عنوانًا، أو ميزانية CU الذرّية (أصبحت المعاملة تكرارية)، أو قاعدة التتبع.

## 9. المرجع

* [مرجع البرنامج المسبق](/ar/almrja/precompiles.md) — كل محدِّد على كل precompile، مع فئة الإرسال وقاعدة delegatecall.
* [القيود](/ar/almfahym-alasasyh/constraints.md) — الحدود أعلاه في جدول واحد.
* [التوافق التشغيلي للرموز](/ar/almfahym-alasasyh/token-interop.md) — الأغلفة، السكوكات المعيارية، الإيداع والسحب.
* التطبيقات المرجعية في المستودعات العامة: [cardo](https://github.com/rome-protocol/cardo) (يستدعي المستخدم precompile الخاص بـ CPI مباشرةً، الشكل B)، [rome-dex](https://github.com/rome-protocol/rome-dex) (مباشر `invoke` بالإضافة إلى موجّه من دون وصاية، الشكل C)، و [aerarium](https://github.com/rome-protocol/aerarium) (تطبيق إقراض من Compound III فوق سوق مشترك واحد؛ لا يستدعي برامج سولانا من Solidity (لا يوجد CPI)، لذا فهو ليس مثالًا على محوِّل).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.rome.builders/ar/adlh-almtw-ryn/integrate-solana-program.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
