从 EVM 调用 Solana

Rome 的 CPI 预编译允许 Solidity 合约直接调用任何 Solana 程序。本指南介绍其工作机制。

前置条件

  • 已安装 Rome Solidity SDK: npm install @rome-protocol/solidity-sdk

  • 一个已部署的 Rome 合约(见 部署 Solidity)

CPI 预编译

位于 0xFF00000000000000000000000000000000000008 提供两个函数:

import {CpiProgram} from "@rome-protocol/solidity-sdk/contracts/core/Precompiles.sol";

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

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

基础示例:转移 SOL

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {SystemProgram, CpiProgram} from "@rome-protocol/solidity-sdk/contracts/core/Precompiles.sol";
import {RomeEVMAccount} from "@rome-protocol/solidity-sdk/contracts/core/RomeEVMAccount.sol";

contract SolTransfer {
    function transferSol(bytes32 recipient, uint64 lamports) external {
        // 获取发送方的 PDA
        bytes32 senderPda = RomeEVMAccount.pda(msg.sender);

        // 构建 System Program 转账指令
        ICrossProgramInvocation.AccountMeta[] memory accounts = new ICrossProgramInvocation.AccountMeta[](2);
        accounts[0] = ICrossProgramInvocation.AccountMeta(senderPda, true, true);   // 发送方(签名者,可写)
        accounts[1] = ICrossProgramInvocation.AccountMeta(recipient, false, true);   // 接收方(可写)

        // System Program 转账指令(变体 2,小端 u64 金额)
        bytes memory data = abi.encodePacked(uint32(2), lamports);

        CpiProgram.invoke(SystemProgram.program_id(), accounts, data);
    }
}

读取账户数据

CPI 预编译还允许你读取任意 Solana 账户的数据:

使用 SPL Token

使用 SPL Token 预编译执行常见代币操作:

PDA 推导

在 Solidity 中查找程序派生地址:

Base58 转换

在 bytes32 与 base58(Solana 的地址格式)之间转换:

调用自定义 Solana 程序

要调用任意 Solana 程序:

关键限制

  1. 所有账户都必须预先声明。 Solana 交易必须包含 CPI 将要触及的每一个账户。在 CPI 内部无法动态发现账户。

  2. CPI 深度限制:4 层。 Rome EVM → 你的目标 → 目标的调用 → 再一层。请仔细规划调用深度。

  3. Solana 公钥是 bytes32。 所有地址都是 32 字节的 Solana 公钥,而不是 20 字节的 Ethereum 地址。

  4. 指令数据是原始字节。 你需要按照目标程序期望的格式对指令数据进行编码(通常为 Borsh 编码,采用小端序)。

下一步

最后更新于

这有帮助吗?