> 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-almtwryn/dual-lane-app.md).

# أنشئ تطبيقًا ثنائي المسار

أحد **تطبيق ثنائي المسار** هو عقد Solidity واحد يستخدمه كلٌّ من **MetaMask (EVM)** المستخدم و **Phantom (Solana)** يستخدمانه مباشرة — العقد نفسه، والحالة نفسها، وكلٌّ بالمحفظة التي لديه بالفعل. تشرح هذه الصفحة *بالضبط* ما يحدث في كل خطوة، على كل جانب.

المثال عبارة عن **خزنة**: أنت `تودع` USDC ثم لاحقًا `تسحب` إياه. "Stake / unstake" و"supply / redeem" و"tip / claim" لها البنية نفسها.

## ما تكتبه — خزنة ERC-20 قياسية

على Rome، يظهر USDC الخاص بمستخدم Solana على جانب EVM كـ **رمز ERC-20** — غلاف SPL لذلك الإصدار (مثلًا `wUSDC`). لذا فإن عقدك خزنة رمزية عادية: يسحب الرموز باستخدام `transferFrom` ويعيدها باستخدام `transfer`. لا شيء خاص بـRome:

```solidity
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract Vault {
    IERC20 public immutable token;                 // غلاف wUSDC
    mapping(address => uint256) public balanceOf;
    constructor(IERC20 _token) { token = _token; }

    function deposit(uint256 amount) external {
        require(token.transferFrom(msg.sender, address(this), amount));
        balanceOf[msg.sender] += amount;
    }
    function withdraw(uint256 amount) external {
        balanceOf[msg.sender] -= amount;
        require(token.transfer(msg.sender, amount));
    }
}
```

> **لماذا ERC-20 وليس `قابلاً للدفع` / `msg.value`?** الرصيد القابل للإنفاق لدى مستخدم Solana هو **حساب رمز SPL في محفظته**، ويظهر واحدًا لواحد على هيئة غلاف ERC-20 هذا — *وليس* الرصيد الأصلي على EVM. لذا يمكن لمستخدم Solana دائمًا تمويل `transferFrom`، لكن `deposit() payable` سيتطلب قيمة أصلية لا يمتلكونها ساكنة. ابنِ حول الرمز، وسيعمل المساران بالطريقة نفسها.

انشره باستخدام Foundry أو Hardhat، مع توجيه المُنشئ إلى عنوان الغلاف لرمزك (من [السجل](https://github.com/rome-protocol/rome-registry)). على Rome، رمز الغاز هو USDC، لذا تحتاج إلى رصيد USDC صغير للغاز كي تنشر.

## المساران

| المسار     | المحفظة                | كيف يستدعيه التطبيق                              |
| ---------- | ---------------------- | ------------------------------------------------ |
| **EVM**    | MetaMask (مفتاح EVM)   | `submitRomeTx` — عملية الكتابة القياسية في Rome  |
| **Solana** | Phantom (مفتاح Solana) | `submitRomeTxSolanaLane` — لا حاجة إلى مفتاح EVM |

مسار EVM عادي. بقية هذه الصفحة هي **مسار Solana** — النصف المثير للاهتمام.

## الفكرة الأساسية: الهوية الاصطناعية مجرد وسيط تمرير

هوية EVM لمستخدم Solana هي **العنوان الاصطناعي** — `keccak256(solana_pubkey)[12:]`. إنه `msg.sender` في العقد، لكنه **لا يحتفظ بأي شيء أثناء السكون.** أموال المستخدم موجودة في **محفظة Solana** (بصفتها SPL USDC)، وعلى جانب EVM فإن الرصيد نفسه هو ما `wUSDC.balanceOf(synthetic)` يقرأه. تتدفق القيمة *عبر* الهوية الاصطناعية:

* **للداخل** (الإيداع): حساب الرمز في المحفظة → حساب الرمز الاصطناعي → العقد (عبر `transferFrom`).
* **للخارج** (السحب): العقد → حساب الرمز الاصطناعي → حساب الرمز في المحفظة.

تعود الهوية الاصطناعية إلى الصفر بعد كل دورة ذهاب وعودة.

## مرة واحدة: التفعيل (تهيئة الهوية الاصطناعية)

الحساب على السلسلة لهوية اصطناعية جديدة لا يوجد حتى تنشئه. عند أول مرة يتصرف فيها مستخدم Solana، تكون هويته الاصطناعية **مُهيأة** بـ `create_pda` استدعاء — وبعد ذلك، يمكن استدعاءات نقل القيمة (ERC-20 `transferFrom`، وعمليّة الكنس) أن توقّعها.

`submitRomeTxSolanaLane` يفعل هذا **تلقائيًا عند أول استخدام** (`autoProvision` مفعّل افتراضيًا). إذا كنت تفضل عرض شاشة "التفعيل" صراحةً (إعداد حساب لمرة واحدة)، فافعل ذلك بنفسك:

```javascript
import { provisionSynthetic, isSyntheticProvisioned } from "@rome-protocol/sdk";

const deps = { connection, proxyUrl, programId, chainId, payer: wallet.publicKey, signTransaction: wallet.signTransaction };
if (!(await isSyntheticProvisioned(connection, programId, synthetic))) {
  await provisionSynthetic(deps); // استدعاء create_pda واحد؛ ثم أرسل عمليات الكتابة مع autoProvision: false
}
```

## ما يحتاجه كل جانب

|                             | المتطلبات                        | السبب                                                          |
| --------------------------- | -------------------------------- | -------------------------------------------------------------- |
| **مستخدم Solana (Phantom)** | **SOL** (قليلًا)                 | يدفع رسوم معاملة Solana في كل معاملة على المسار                |
|                             | **USDC كرمز SPL** في محفظته      | القيمة التي يودعونها (وتظهر على جانب EVM كـ `wUSDC`)           |
| **مستخدم EVM (MetaMask)**   | **USDC** كـرصيد الغاز في Rome    | غاز + قيمة؛ ويزيدونه عبر **جسر USDC** إلى Rome (لا يوجد صنبور) |
| **أنت (الباني)**            | محفظة فيها **USDC** غاز على Rome | لنشر العقد                                                     |

## القيمة الداخلة — الإيداع (خطوة بخطوة)

لدى مستخدم Solana ‏SOL + USDC في محفظة Phantom الخاصة به. كل معاملة على المسار تكون **موقعة بواسطة Phantom** — المحفظة توقعها وترسلها إلى Solana RPC (يُستخدم الوكيل فقط لاكتشاف الحسابات):

1. **مرحلة التمويل** — `buildFundLeg(...)` → `submitSolanaInstructions(...)`. ينشئ حساب رمز USDC الخاص بالهوية الاصطناعية (إن لزم) ويشغّل **`ActivateAta`**، ناقلًا `amount` من USDC من حساب الرمز في المحفظة **إلى حساب الرمز الخاص بالهوية الاصطناعية**. الآن `wUSDC.balanceOf(synthetic)` يظهر ذلك الرصيد.
2. **الموافقة** — `submitRomeTxSolanaLane({ to: wUSDC, data: approve(vault, amount) })`. يتيح للخزنة سحب الرموز. *(عادةً هذا هو أول استدعاء على المسار، لذا تُهيَّأ الهوية الاصطناعية تلقائيًا هنا.)*
3. **إيداع** — `submitRomeTxSolanaLane({ to: vault, data: deposit(amount) })`. تنفذ الخزنة `transferFrom(synthetic, vault, amount)` — ينتقل USDC من الهوية الاصطناعية إلى الخزنة، ويُقيد على عنوان الهوية الاصطناعية.

**الأثر الصافي:** انتقل USDC **محفظة Phantom → (الهوية الاصطناعية) → الخزنة.**

```javascript
import { syntheticAddress, buildFundLeg, submitSolanaInstructions, submitRomeTxSolanaLane } from "@rome-protocol/sdk";
import { encodeFunctionData, erc20Abi } from "viem";

const synthetic = syntheticAddress(wallet.publicKey);
const deps = { connection, proxyUrl, programId, chainId, payer: wallet.publicKey, signTransaction: wallet.signTransaction };

// 1) fund leg — wallet USDC → synthetic token account (Phantom signs)
await submitSolanaInstructions(
  buildFundLeg({ programId, chainId, mint: usdcMint, amount: depositAmount, wallet: wallet.publicKey, synthetic }),
  { connection, feePayer: wallet.publicKey, signTransaction: wallet.signTransaction },
);

// 2) approve the vault (first lane call → synthetic auto-provisioned)
await submitRomeTxSolanaLane(deps, { to: wUSDC, data: encodeFunctionData({ abi: erc20Abi, functionName: "approve", args: [vault, depositAmount] }) });

// 3) deposit — the vault pulls via transferFrom
await submitRomeTxSolanaLane(deps, { to: vault, data: encodeFunctionData({ abi, functionName: "deposit", args: [depositAmount] }) });
```

## القيمة الخارجة — السحب (خطوة بخطوة)

الآن يسحب المستخدم. وهي أيضًا موقعة بواسطة Phantom:

1. **سحب** — `submitRomeTxSolanaLane({ to: vault, data: withdraw(amount) })`. `vault.withdraw` تنفذ `transfer(synthetic, amount)` — ينتقل USDC من الخزنة عائدًا إلى **الخاص بالهوية الاصطناعية** حساب الرمز.
2. **مرحلة الكنس** — `buildSweepLeg(...)` يعطيك `HelperProgram.transfer_spl` الاستدعاء + الحسابات؛ شغِّله (أنشئ حساب الرمز في المحفظة إن لزم، ثم `DoTxUnsigned` إلى precompile الخاص بـ Helper) لنقل USDC من الهوية الاصطناعية **إلى محفظة Solana الخاصة بالمستخدم**. وتعود الهوية الاصطناعية إلى الصفر.

**الأثر الصافي:** انتقل USDC **الخزنة → (الهوية الاصطناعية) → محفظة Phantom الخاصة بالمستخدم.** لا يبقى شيء عالقًا.

```javascript
import { buildSweepLeg } from "@rome-protocol/sdk";

// 1) withdraw — vault returns USDC to the synthetic's token account
await submitRomeTxSolanaLane(deps, { to: vault, data: encodeFunctionData({ abi, functionName: "withdraw", args: [amount] }) });

// 2) sweep leg — synthetic token account → the user's own wallet token account
const sweep = buildSweepLeg({ programId, mint: usdcMint, amount, wallet: wallet.publicKey, synthetic });
await submitSolanaInstructions([sweep.ensureWalletAtaIx], { connection, feePayer: wallet.publicKey, signTransaction: wallet.signTransaction });
await submitRomeTxSolanaLane(deps, { to: sweep.helperTo, data: sweep.calldata, extraAccounts: sweep.extraAccounts });
```

## المآخذ — كلها يعالجها SDK

هذه هي الأشياء التي تخطئ فيها معاملة مسار Solana المبنية يدويًا؛ `submitRomeTxSolanaLane` ويقوم بها نيابةً عنك:

* **التهيئة.** يجب إنشاء حساب هوية اصطناعية جديدة (`create_pda`) قبل أي استدعاء ينقل قيمة، وإلا فلن تستطيع توقيع النقل. مفعّل تلقائيًا عند أول استخدام؛ أوقفه باستخدام `autoProvision: false` + `provisionSynthetic`.
* **استخدم الغلاف، لا `msg.value`.** رصيد مستخدم Solana هو حساب الرمز SPL الخاص به، ويظهر على هيئة غلاف ERC-20 — انقله باستخدام `transfer` / `transferFrom`، وليس بالقيمة الأصلية أبدًا.
* **ComputeBudget.** يحتاج EVM الخاص بـRome إلى حد CU مرتفع (\~1.35M) وإطار heap كبير (\~250 KB). أما القيم الافتراضية لـSolana عند 200K-CU / 32-KB فتفشل.
* **محفظة الخزانة.** تدفع عملية التنفيذ رسمًا صغيرًا إلى حساب الخزانة لكل سلسلة؛ ويغفله اكتشاف الحسابات، لذا يضيفه SDK.
* **إلى أين يُرسل.** المحفظة **توقّع معاملة Solana وترسلها إلى Solana RPC** — وليس إلى الوكيل. يُستخدم الوكيل فقط لاكتشاف الحسابات. على السلسلة، يستنتج البرنامج `msg.sender` انطلاقًا من موقِّع Solana.
* **الغاز هو USDC.** لا يوجد صنبور — جسّر USDC إلى الداخل (انظر [الحصول على التمويل](/ar/almward/faucets.md)).

## التطبيق نفسه من MetaMask

يستدعي مستخدم EVM العقد نفسه مع `submitRomeTx` — أدوات EVM قياسية، والغاز بعملة USDC. وما يزالون `يوافقون` ثم `تودع` (ERC-20 كالمعتاد)، من دون مرحلتي التمويل/الكنس (رموزهم موجودة بالفعل على عنوان EVM الخاص بهم). يشترك المستخدمان في نفس `balanceOf` الحالة.

## ما التالي

* [استدعاء EVM من Solana](/ar/adlh-almtwryn/call-evm-from-solana.md) — تفاصيل آليات مسار Solana
* [استدعاء Solana من EVM](/ar/adlh-almtwryn/call-solana-from-evm.md) — الاتجاه الآخر (CPI)
* [الحصول على التمويل](/ar/almward/faucets.md) — USDC كرمز الغاز؛ جسّره إلى الداخل


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.rome.builders/ar/adlh-almtwryn/dual-lane-app.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
