> 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/vi/huong-dan-cho-nha-phat-trien/call-evm-from-solana.md).

# Gọi EVM từ Solana

Cho phép một ví Solana điều khiển các hợp đồng EVM trên Rome — không cần khóa Ethereum. Ví ký, và giao dịch của nó đi lên Solana.

Chiều ngược của [Gọi Solana từ EVM](/vi/huong-dan-cho-nha-phat-trien/call-solana-from-evm.md): một ví Solana (ví dụ Solflare) gọi trực tiếp bất kỳ hợp đồng EVM nào trên Rome, với **không cần khóa Ethereum**. Đây là cách [Aerarium](/vi/ung-dung-tren-rome/apps.md) cho phép người dùng thuần Solana dùng chung các hợp đồng với người dùng EVM.

Cách gọi một lần là của SDK `submitRomeTxV1`, trong đó proxy sẽ tạo một giao dịch Solana v1 cho lời gọi; trang này giải thích cơ chế.

## Địa chỉ tổng hợp

Danh tính EVM của một ví Solana là **địa chỉ tổng hợp** — 20 byte cuối của `keccak256` của khóa công khai Solana 32 byte của nó:

```
synthetic_evm_address = keccak256(solana_pubkey)[12:]
```

Đây là `msg.sender` khi ví gọi một hợp đồng, và là danh tính ổn định mà nonce và trạng thái của nó nằm dưới đó. Cách suy ra này được ghim trong giao thức, nên một ví luôn ánh xạ tới cùng một địa chỉ.

```javascript
import { syntheticAddress } from "@rome-protocol/sdk";
const from = syntheticAddress(solanaPubkey); // 0x… 20 byte
```

## Synthetic là một lớp chuyển tiếp — nó không nắm giữ token khi ở trạng thái nghỉ

Số dư có thể chi tiêu của người dùng thuần Solana nằm trong **ví Solana** (dưới dạng SPL token), được phản ánh 1:1 ở phía EVM dưới dạng **wrapper ERC20SPL** (ví dụ `wUSDC`). Synthetic không giữ gì khi ở trạng thái nghỉ, nên giá trị chảy *qua* nó — mọi bước đều được ví Solana ký:

1. **Cấp quyền (một lần).** PDA external-auth của một synthetic mới toanh không tồn tại cho đến khi `create_pda` được chạy — và các chuyển khoản bên dưới được ký bởi PDA đó. `submitRomeTxSolanaLane` tự động cấp nó trong lần dùng đầu tiên (hoặc gọi `provisionSynthetic` để có bước "Kích hoạt" rõ ràng; kiểm tra bằng `isSyntheticProvisioned`). Với `submitRomeTxV1`, hãy kiểm tra `isSyntheticProvisioned` và gửi `buildCreatePdaCall` qua nó một lần.
2. **Chặng nạp vốn (ví → synthetic).** Chuyển token từ ATA của ví vào ATA của synthetic — `ActivateAta` bước, được tạo bởi `buildFundLeg`. Giờ thì `wrapper.balanceOf(synthetic)` đọc số dư đó, nên synthetic chi tiêu nó như một ERC-20 thông thường (`chuyển` / `transferFrom`) — **không phải** như native `msg.value`, thứ mà nó không nắm giữ.
3. **Các lời gọi (`DoTxUnsigned`).** Synthetic chạy (các) giao dịch EVM — ví dụ: `approve` rồi một vault `deposit` kéo thông qua `transferFrom`.
4. **Chặng quét (synthetic → ví).** Sau khi một thao tác withdraw / borrow / claim đưa token vào synthetic, `buildSweepLeg` đẩy chúng trở lại ATA của chính ví người dùng (`HelperProgram.transfer_spl`), nên synthetic ròng về 0.

Phần tiếp theo mô tả thứ mà bản cũ hơn `submitRomeTxSolanaLane` tạo ở phía client. `submitRomeTxV1` thay vào đó cho proxy soạn một giao dịch v1, không cần khám phá tài khoản, ComputeBudget hay ví kho bạc trong mã của bạn; xem [các endpoint external-V1](#the-external-v1-endpoints) bên dưới.

## Cách lời gọi được tạo

`submitRomeTxSolanaLane` thực hiện tất cả những việc này. Cơ chế:

1. **Tạo một giao dịch EIP-1559 chưa ký** với `from` = địa chỉ synthetic — không có chữ ký secp256k1.
2. **Khám phá các tài khoản** — gọi [`rome_emulateCallAccounts`](/vi/tai-lieu-tham-khao/json-rpc.md) với `from`, `vào`, `data`, **và `value`** (khi lời gọi là payable). Việc truyền `value` rất quan trọng: proxy mô phỏng *thực* lời gọi, nên mọi ghi trạng thái phụ thuộc vào giá trị — ví dụ một ô mapping mới — đều được cấp phát và tài khoản Solana tương ứng được trả về. Bỏ qua nó thì tài khoản đó sẽ thiếu, và giao dịch sẽ thất bại với *"instruction modified data of a read-only account."*
3. **Ghép giao dịch Solana:** hai **ComputeBudget** instruction (EVM của Rome cần một giới hạn CU cao hơn ≈1,35M và một heap frame lớn ≈250 KB — mặc định của Solana sẽ lỗi) + **`DoTxUnsigned`** instruction (RLP chưa ký + các tài khoản đã khám phá) + **ví kho bạc** (quá trình thực thi trả cho nó một khoản phí nhỏ; bước khám phá bỏ qua nó, nên SDK sẽ gắn thêm).
4. **Ví ký và gửi.** Các **Ví Solana ký giao dịch Solana (Ed25519) và gửi nó tới RPC của Solana — không phải tới proxy.** Môi trường chạy của Solana xác minh chữ ký; trên chuỗi, chương trình suy ra `msg.sender` từ signer đó. Thẩm quyền là chữ ký Solana, không phải chữ ký Ethereum. (Proxy chỉ được dùng cho mô phỏng/khám phá ở bước 2.)

PDA của người dùng (`["EXTERNAL_AUTHORITY", synthetic_address]`) sở hữu các tài khoản token của họ và ký các CPI SPL thay mặt họ — vì vậy một người dùng Solana có thể supply, borrow, swap và LP hoàn toàn từ ví Solana của mình.

## Các endpoint external-V1

`submitRomeTxV1` là cách gọi một lần: nó yêu cầu proxy tạo một giao dịch v1 đã soạn, kiểm tra nó, để ví ký và gửi nó tới RPC của Solana, đồng thời chuyển tiếp qua proxy khi lời gọi không khớp. Proxy cung cấp cùng một làn như các phương thức RPC, nên bất kỳ client nào cũng có thể dùng mà không cần SDK (danh sách đầy đủ trong [Hỗ trợ JSON-RPC](/vi/tai-lieu-tham-khao/json-rpc.md)):

* `rome_composeUnsignedAtomicV1(rlp, pubkey, signature)` — một giao dịch Solana v1; ví ký nó và ứng dụng phát nó lên RPC Solana. Bị từ chối khi lời gọi không vừa trong một giao dịch nguyên tử.
* `rome_composeUnsignedIterativeV1(rlp, pubkey, signature)` — mọi chặng của lần thực thi (nguyên tử nếu vừa, nếu không thì các chặng lặp); ví ký từng chặng, `rome_submitSignedV1Plan` phát chúng. Các giới hạn của một kế hoạch dài nằm trong [Các lệnh gọi được chuyển tiếp và lệnh gọi dài](#relayed-calls-and-long-calls).
* `rome_relayUnsignedV1(rlp, pubkey, signature)` — một chữ ký ví, payer của proxy điều khiển các chặng và trả về mã băm giao dịch EVM; synthetic trả gas Rome, nên hãy nạp cho nó một lần bằng `rome_composeExternalDepositV1`.

**Chữ ký ủy quyền.** `rlp` là phần thân EIP-1559 chưa ký gồm 9 trường thuần túy. Ví ký, bằng khóa Ed25519 của nó, hoặc chữ ký thô 32 byte `keccak256(0x02 ‖ rlp)` hoặc thông điệp dễ đọc

```
Rome authorization\n0x<64 lowercase hex chars of that hash>
```

Cả hai đều được chấp nhận; dạng dễ đọc là thứ mà ví hiển thị rõ ràng trong `signMessage` lời nhắc. Chương trình tính lại hash từ phần thân mà nó thực thi và yêu cầu thông điệp đã ký phải chính xác là một trong hai dạng, nên một chữ ký trên một phần thân không bao giờ có thể ủy quyền cho phần thân khác, và nonce của phần thân ngăn việc phát lại.

## Các lệnh gọi được chuyển tiếp và lệnh gọi dài

Một số lời gọi không vừa trong một giao dịch Solana. Phần này nói về điều gì xảy ra khi đó, và các giới hạn áp dụng.

### Khi bộ soạn nguyên tử từ chối

`rome_composeUnsignedAtomicV1` từ chối một lời gọi không vừa trong một giao dịch nguyên tử. `submitRomeTxV1` rồi chuyển tiếp lời gọi qua proxy với **cùng** chữ ký ủy quyền, nên ví không bị hỏi lần thứ hai. Kết quả có `route: "relay"` và không có `chữ ký`. Truyền `relayOnRefusal: false` để nhận sự từ chối dưới dạng lỗi, hoặc tự gọi `relayV1` để chuyển tiếp có chủ đích.

Ba điều cần biết về một lời gọi được chuyển tiếp:

* **Synthetic trả gas.** Trên tuyến chuyển tiếp, proxy gửi và thanh toán cho các giao dịch Solana, và lời gọi được trả bằng token gas bản địa của chuỗi từ số dư của synthetic. Số dư phải ít nhất `gasLimit × maxFeePerGas`. `fundSyntheticGas(deps, amountWei)` nạp thêm từ tài khoản token của chính ví đối với token gas của chuỗi (USDC trên Hadrian và Rubicon); ví ký một giao dịch v1 thuần túy, và cần một ít SOL để trả phí Solana. `amountWei` nằm trong wei 18 chữ số thập phân của chuỗi và phải là bội số của 10^12, nếu không proxy sẽ từ chối. Không dùng SDK, `rome_composeExternalDepositV1` tạo ra cùng giao dịch đó.
* **Một revert không ném lỗi.** Một lời gọi được chuyển tiếp mà revert ở phía EVM vẫn được giải quyết bình thường, với `receipt.status === "0x0"`. Hãy kiểm tra `receipt?.status === "0x1"` trước khi coi lời gọi là hoàn tất. `biên nhận` là `null` nếu biên nhận không xuất hiện kịp thời.
* **`relayOnRefusal: false` là một lựa chọn được thực hiện trong SDK, không phải một khóa.** Proxy đã giữ chữ ký ủy quyền cho payload đó và có thể chạy nó theo một trong hai tuyến, tối đa `gasLimit × maxFeePerGas`.

```javascript
const res = await submitRomeTxV1(deps, { to, data, value });
if (res.route === "relay" && res.receipt?.status !== "0x1") {
  // một lời gọi được chuyển tiếp nhưng đã revert vẫn được giải quyết; nó không ném lỗi
}
```

### Lời gọi dài

Một lời gọi cần nhiều hơn một giao dịch sẽ chạy như một phiên lặp: chương trình thực thi nhiều bước EVM nhất có thể trong mỗi giao dịch Solana và lưu trạng thái giữa các giao dịch. Có hai cách điều khiển nó từ một ví Solana:

* **Chuyển tiếp nó.** `rome_relayUnsignedV1` chỉ cần một chữ ký ví và payer của proxy gửi mọi chặng. Synthetic trả gas, như trên.
* **Tự ký từng chặng.** `rome_composeUnsignedIterativeV1` trả về mọi chặng với một ô chữ ký trống, ví ký từng chặng, và `rome_submitSignedV1Plan` phát chúng theo thứ tự. Mỗi chặng là một giao dịch Solana riêng để ví ký, nên một ví chỉ ký từng giao dịch một sẽ hiện lời nhắc một lần cho mỗi chặng, bên cạnh chữ ký ủy quyền. Ví trả phí Solana cho từng chặng.

Các giới hạn:

| Giới hạn                                                 | Giá trị          | Nguồn gốc                                                                                                      |
| -------------------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------- |
| Số giao dịch trong một `rome_submitSignedV1Plan` lời gọi | 64               | Proxy từ chối một kế hoạch dài hơn                                                                             |
| Số chặng mà bộ soạn lặp sẽ lập kế hoạch                  | 128              | Giới hạn trong bộ soạn lặp của Rust Rome SDK, cũng ràng buộc kế hoạch `rome_composeUnsignedIterativeV1` trả về |
| Số lần lặp trong một phiên                               | 160              | Chương trình Rome EVM; vượt quá đó thì phiên ngừng tiếp tục, giải phóng các khóa tài khoản và kết thúc         |
| Kích thước giao dịch Solana và số tài khoản              | 4 KB, 64 địa chỉ | Mỗi chặng là một giao dịch Solana v1                                                                           |

Hầu hết các lời gọi thực tế hoàn tất với ít chặng hơn nhiều so với các giới hạn này. Giới hạn 64 giao dịch trên một kế hoạch do ví ký là giới hạn bạn sẽ gặp đầu tiên. Một lời gọi cần nhiều chặng hơn thế không thể được phát qua `rome_submitSignedV1Plan`.

Một lời gọi dài không thể tạo CPI trên legacy-track. Các lời gọi làm thay đổi trạng thái trên các precompile legacy (`0xff…08`, `0xff…09` và `0x42…16`) bị từ chối trong một phiên lặp với `CpiProhibitedInIterativeTx`. Việc đọc trạng thái Solana và các precompile cached-track (`0xff…04`, `0xff…05`, `0xff…06`, `0xff…0b`) hoạt động. Một lời gọi tạo CPI legacy-track phải vừa trong một giao dịch nguyên tử, bất kể làn nào gửi nó. Xem [Ràng buộc](/vi/cac-khai-niem-cot-loi/constraints.md) và [Ngân sách tính toán](/vi/cac-khai-niem-cot-loi/compute-budget.md).

## Các triển khai tham chiếu

* **Aerarium** — điều khiển Compound v3 (supply / borrow) từ một ví Solana qua `DoTxUnsigned`. Đây là tham chiếu cho làn giao dịch legacy cũ hơn được mô tả ở trên, không phải cho `submitRomeTxV1`.
* **Rome DEX** không phải là một ví dụ của trang này: phía Solana của nó là một chương trình Solana gốc mà ví ký trực tiếp, và phía EVM của nó dùng chung cùng một pool.

Aerarium không cần cầu nối hay ví thứ hai — địa chỉ synthetic biến một khóa Solana thành một tài khoản EVM hạng nhất. Với mẫu v1, hãy theo [Xây dựng một ứng dụng hai làn](/vi/huong-dan-cho-nha-phat-trien/dual-lane-app.md).

## Tiếp theo là gì

* [Gọi Solana từ EVM](/vi/huong-dan-cho-nha-phat-trien/call-solana-from-evm.md) — chiều ngược lại, qua CPI
* [Tương tác token](/vi/cac-khai-niem-cot-loi/token-interop.md) — cách số dư được chia sẻ giữa EVM và Solana


---

# 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/vi/huong-dan-cho-nha-phat-trien/call-evm-from-solana.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.
