For the complete documentation index, see llms.txt. This page is also available as Markdown.

构建双通道应用

双通道应用的分步演示——同一个 Solidity 合约同时供 MetaMask 用户和 Phantom(Solana)用户使用,并详细说明每一步具体发生了什么。

A 双通道应用 是一个 Solidity 合约,同时供一个 MetaMask(EVM) 用户和一个 Phantom(Solana) 用户可直接使用——同一个合约、同一份状态,每个人都用自己已有的钱包。本页将逐步讲解 准确地 在每一步、在双方各自一侧会发生什么。

示例是一个很小的 金库:你 存入 USDC,之后再 提取 它。“质押 / 取消质押”、“供应 / 赎回”、“打赏 / 领取”都是同一类模式。

你要编写的是——一个标准的 ERC-20 金库

在 Rome 上,Solana 用户的 USDC 在 EVM 侧会表现为普通的 ERC-20 代币 ——该铸币的 SPL 包装代币(例如 wUSDC)。因此你的合约就是一个普通的代币金库:它通过 transferFrom 拉取代币,并通过 transfer返还代币。没有任何 Rome 特有的内容:

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,而不是 payable / msg.value? Solana 用户可支配的余额是他们的 钱包中的 SPL 代币账户,它会一对一地体现为这个 ERC-20 包装代币—— 不是 EVM 原生余额。因此,Solana 用户总是可以为一个 transferFrom注资,但一个 deposit() payable 则需要他们并未持有的原生资产。围绕代币来设计,两条通道就能以相同方式工作。

使用 Foundry 或 Hardhat 部署它,并将构造函数指向你的代币对应的包装地址(来自 注册表)。在 Rome 上,gas 代币是 USDC,因此你需要少量 USDC gas 余额才能部署。

两条通道

通道
钱包
应用如何调用它

EVM

MetaMask(一个 EVM 密钥)

submitRomeTx ——标准的 Rome 写入

Solana

Phantom(一个 Solana 密钥)

submitRomeTxSolanaLane ——不需要 EVM 密钥

EVM 通道很普通。本页其余部分讲的是 Solana 通道 ——有意思的另一半。

核心思想:synthetic 只是一个中转通道

Solana 用户的 EVM 身份就是他们的 synthetic 地址 —— keccak256(solana_pubkey)[12:]。它是他们的 msg.sender 在合约中的身份,但 它静态时不持有任何资产。 用户的资金保存在他们的 Solana 钱包 (以 SPL USDC 形式),而在 EVM 侧,同一余额就是 wUSDC.balanceOf(synthetic) 读取到的值。价值流动 经过 synthetic:

  • (存款):钱包代币账户 → synthetic 代币账户 → 合约(通过 transferFrom).

  • (提取):合约 → synthetic 代币账户 → 钱包代币账户。

每次往返后,synthetic 都会净额归零。

一次性操作:激活(为 synthetic 提供账户)

全新的 synthetic 链上账户在你创建之前并不存在。Solana 用户第一次操作时,他们的 synthetic 会被 预置create_pda 调用——之后,任何移动价值的调用(ERC-20 transferFrom,以及 sweep)都可以由它签名。

submitRomeTxSolanaLane 会自动执行此操作 在首次使用时自动 (autoProvision 默认开启)。如果你更希望展示一个明确的“激活”界面(一次性的账户设置),可以自行实现:

双方各自需要什么

需要
原因

Solana 用户(Phantom)

SOL (少量)

为每笔通道交易支付 Solana 交易费用

作为 SPL 代币的 USDC 在他们的钱包中

他们存入的价值(在 EVM 侧显示为 wUSDC)

EVM 用户(MetaMask)

USDC 作为他们的 Rome gas 余额

gas + 价值;他们通过以下方式补充: 桥接 USDC 进入 Rome(没有水龙头)

你(构建者)

一个带有 USDC Rome 上 gas 的钱包

用于部署合约

价值流入——存款(逐步说明)

Solana 用户的 Phantom 钱包中有 SOL + USDC。每笔通道交易都是 由 Phantom 签名 ——钱包对其签名并发送到 Solana RPC(代理仅用于发现账户):

  1. 资金注入步骤 —— buildFundLeg(...)submitSolanaInstructions(...)。创建 synthetic 的 USDC 代币账户(如有需要),并执行 ActivateAta,将 amount 的 USDC 从钱包的代币账户 转入 synthetic 的。现在 wUSDC.balanceOf(synthetic) 会显示该余额。

  2. 批准 —— submitRomeTxSolanaLane({ to: wUSDC, data: approve(vault, amount) })。允许金库拉取代币。 (这通常是第一次通道调用,因此 synthetic 会在此自动预置。)

  3. 存款 —— submitRomeTxSolanaLane({ to: vault, data: deposit(amount) })。金库执行 transferFrom(synthetic, vault, amount) ——USDC 从 synthetic 转入金库,并记入 synthetic 的地址。

净效应: USDC 经由 Phantom 钱包 →(synthetic)→ 金库。

价值流出——提取(逐步说明)

现在用户提取。仍由 Phantom 签名:

  1. 提取 —— submitRomeTxSolanaLane({ to: vault, data: withdraw(amount) }). vault.withdraw 执行 transfer(synthetic, amount) ——USDC 从金库重新转入 synthetic 的 代币账户。

  2. 清扫步骤 —— buildSweepLeg(...) 会为你提供 HelperProgram.transfer_spl 调用及相关账户;执行它(如有需要先创建钱包的代币账户,然后通过 DoTxUnsigned 向 Helper 预编译合约发起)将 USDC 从 synthetic 转回用户自己的 Solana 钱包。synthetic 再次净额归零。

净效应: USDC 经由 金库 →(synthetic)→ 用户的 Phantom 钱包。 没有任何资产被滞留。

注意事项——全部由 SDK 处理

这些是手工构建的 Solana 通道交易容易出错的地方; submitRomeTxSolanaLane SDK 会替你处理:

  • 预置。 全新的 synthetic 账户必须先创建(create_pda)才能进行任何价值移动调用,否则它无法对转账签名。首次使用时自动执行;如需关闭,请使用 autoProvision: false + provisionSynthetic.

  • 花费包装代币,而不是 msg.value. Solana 用户的余额就是他们的 SPL 代币账户,并以 ERC-20 包装代币的形式呈现——请使用 transfer / transferFrom进行转移,切勿使用原生资产。

  • ComputeBudget。 Rome 的 EVM 需要更高的 CU 上限(约 135 万)和更大的堆帧(约 250 KB)。Solana 默认的 20 万 CU / 32 KB 会失败。

  • Treasury 钱包。 执行会向每条链的 treasury 账户支付少量费用;账户发现不会包含它,因此 SDK 会自动附加。

  • 发送到哪里。 钱包 对 Solana 交易签名并将其发送到 Solana RPC ——不是发送给代理。代理仅用于账户发现。在链上,程序会从 msg.sender Solana 签名者派生。

  • Gas 以 USDC 计。 没有水龙头——请桥接 USDC 进来(参见 获取资金).

来自 MetaMask 的同一应用

EVM 用户使用以下方式调用相同的合约: submitRomeTx ——标准 EVM 工具,gas 以 USDC 计。他们仍然需要 批准 然后 存入 (与普通 ERC-20 相同),不需要资金注入/清扫步骤(他们的代币本来就位于其 EVM 地址)。两类用户共享同一份 balanceOf 状态。

下一步

最后更新于

这有帮助吗?