> 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/zh/kai-fa-zhe-zhi-nan/call-solana-from-evm.md).

# 从 EVM 调用 Solana

Rome 的预编译使 Solidity 合约能够直接调用 Solana 程序。本指南涵盖其工作机制。

## 前提条件

* 来自公开仓库的 Rome Solidity 接口 [`rome-solidity`](https://github.com/rome-protocol/rome-solidity) 仓库（npm 发布待定）— 预编译接口位于 [`contracts/interface.sol`](https://github.com/rome-protocol/rome-solidity/blob/master/contracts/interface.sol)
* 已部署的 Rome 合约（参见 [部署 Solidity](/zh/kai-fa-zhe-zhi-nan/deploy-solidity.md))

将每个接口绑定到其预编译地址：

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

ICrossProgramInvocation constant CpiProgram    = ICrossProgramInvocation(0xFF00000000000000000000000000000000000008);
ISystemProgram          constant SystemProgram = ISystemProgram(0xFF00000000000000000000000000000000000007);
IHelperProgram          constant Helper        = IHelperProgram(0xFF00000000000000000000000000000000000009);
```

## CpiProgram 预编译

`CpiProgram` (`0xFF…08`）会分发 CPI（`invoke` / `invoke_signed`）以及跨状态读取快捷方法（`account_info`, `account_data_at`, `account_u64_at`, `account_lamports`, `pdas_batch_derive`):

```solidity
// 调用一个 Solana 程序
CpiProgram.invoke(programId, accounts, instructionData);

// 使用 PDA 签名调用（你的合约作为 PDA 签名）
CpiProgram.invoke_signed(programId, accounts, data, seeds);
```

## 转账 lamports

对于来自调用者 PDA 的简单 SOL/lamports 和 SPL 转账，请使用 HelperProgram 预编译——无需手工构建 CPI：

```solidity
contract Transfers {
    IHelperProgram constant Helper = IHelperProgram(0xFF00000000000000000000000000000000000009);

    // 向某个 EVM 地址的 PDA 转账 lamports
    function transferSol(address to, uint64 lamports) external {
        Helper.transfer_lamports(to, lamports);
    }

    // 从调用者的 PDA 转移一个 SPL 代币
    function transferSpl(address to, uint64 tokens, bytes32 mint) external {
        Helper.transfer_spl(to, tokens, mint);
    }

    // 为某个 mint 创建调用者的 ATA
    function createAta(bytes32 mint) external {
        Helper.create_ata(msg.sender, mint);
    }
}
```

`transfer_spl` 有多个重载版本（包括用于 `transferFrom` 流程的委托变体）；准确签名请参见 `interface.sol`。在缓存路径上，改用 `ISplCached` (`0xff…05`) / `IAssociatedSplCached` (`0xff…06`）——一个合约应始终使用同一路径。

## 读取账户数据

通过 CpiProgram 读取快捷方法读取任意 Solana 账户的数据：

```solidity
(
    uint64 lamports,
    bytes32 owner,
    bool isSigner,
    bool isWritable,
    bool executable,
    bytes memory data
) = CpiProgram.account_info(accountPubkey);
```

## PDA 派生

通过 System 预编译从 Solidity 查找程序派生地址：

```solidity
ISystemProgram.Seed[] memory seeds = new ISystemProgram.Seed[](2);
seeds[0] = ISystemProgram.Seed("my-program-seed");
seeds[1] = ISystemProgram.Seed(abi.encodePacked(someValue));

(bytes32 pda, uint8 bump) = SystemProgram.find_program_address(targetProgramId, seeds);
```

## Base58 转换

在以下之间转换 `bytes32` 和 base58（Solana 的地址格式）：

```solidity
bytes memory base58Str = SystemProgram.bytes32_to_base58(pubkey);
bytes32 pubkey = SystemProgram.base58_to_bytes32(base58Bytes);
```

## 调用自定义 Solana 程序

要调用任何 Solana 程序，请自行构建账户列表和指令数据：

```solidity
contract CustomCPI {
    ICrossProgramInvocation constant CpiProgram = ICrossProgramInvocation(0xFF00000000000000000000000000000000000008);
    bytes32 constant MY_PROGRAM = 0x0000000000000000000000000000000000000000000000000000000000000000; // 你的 Solana 程序 ID

    function callMyProgram(bytes32 account1, bytes32 account2, bytes calldata ixData) external {
        ICrossProgramInvocation.AccountMeta[] memory accounts = new ICrossProgramInvocation.AccountMeta[](2);
        accounts[0] = ICrossProgramInvocation.AccountMeta(account1, false, true);
        accounts[1] = ICrossProgramInvocation.AccountMeta(account2, false, false);

        CpiProgram.invoke(MY_PROGRAM, accounts, ixData);
    }
}
```

## 关键限制

1. **所有账户都必须预先声明。** Solana 交易必须包含 CPI 将要触及的每一个账户——CPI 内部无法动态发现账户。
2. **CPI 深度限制：4 层。** Rome EVM → 你的目标 → 目标的调用 → 再多一层。请规划好调用深度。
3. **Solana 公钥是 `bytes32`,** 不是 20 字节的以太坊地址。
4. **指令数据是原始字节** 其格式应符合目标程序的预期（通常为 Borsh、little-endian）。

## 下一步

* **在真实应用中查看** — [rome-dex](https://github.com/rome-protocol/rome-dex) （双轨 AMM）， [cardo](https://github.com/rome-protocol/cardo) （CPI 路由至 Meteora / Marinade / Mango / Jupiter），以及 [aerarium](https://github.com/rome-protocol/aerarium) 在生产环境中从 Solidity 调用 Solana。
* [从 Solana 调用 EVM](/zh/kai-fa-zhe-zhi-nan/call-evm-from-solana.md) — 反向流程：通过 Solana 钱包驱动 EVM 合约
* [代币互操作](/zh/he-xin-gai-nian/token-interop.md) — ERC-20 和 SPL 代币如何协同工作
* [限制](/zh/he-xin-gai-nian/constraints.md) — 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 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/zh/kai-fa-zhe-zhi-nan/call-solana-from-evm.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.
