# 代币互操作

Rome EVM 通过单一状态模型桥接 ERC-20 代币和 SPL 代币。本页说明代币如何在 EVM 和 Solana 之间运作。

## 单一状态模型

与传统桥不同，Rome 不会在一条链上锁定代币，并在另一条链上铸造包装副本。相反，Rome EVM 上的 ERC-20 代币是 **透明包装器** 覆盖在 Solana 上底层 SPL 代币账户之上。

```
┌──────────────────────────────────┐
│ Rome EVM                         │
│                                  │
│   ERC-20 “rUSDC”                 │
│   ┌────────────────────────┐     │
│   │ balanceOf(user)        │─────┼──► 直接从 SPL ATA 读取
│   │ transfer(to, amount)   │─────┼──► 通过预编译执行 SPL 转账
│   │ totalSupply()          │─────┼──► 读取 SPL 铸币供应量
│   └────────────────────────┘     │
│                                  │
└──────────────────────────────────┘
                 │
                 │ 相同的底层数据
                 ↓
┌──────────────────────────────────┐
│ Solana                           │
│                                  │
│   SPL 代币账户（ATA）        │
│   所有者：用户的 PDA              │
│   铸币：USDC（Circle 原生）     │
│   数量：1000000（= 1 USDC）    │
│                                  │
└──────────────────────────────────┘
```

**这意味着：**

* 无桥接延迟——ERC-20 余额就是 SPL 余额
* 无流动性碎片化——两端的 DeFi 看到的是同一批代币
* 无桥风险——不存在可被利用的独立托管账户

## ERC20SPL：包装合约

`SPL_ERC20` 是标准包装合约，为一个 SPL 代币铸币提供完整的 ERC-20 接口：

```solidity
import {SPL_ERC20} from "@rome-protocol/solidity-sdk/contracts/token/ERC20SPL.sol";

// 包装器从用户的 SPL ATA 读取余额
uint256 balance = wrapper.balanceOf(userAddress);

// 转账通过 SPL Token 预编译执行
wrapper.transfer(recipient, amount);
```

**其底层工作方式：**

* `balanceOf()` → 推导用户的 ATA（关联代币账户）→ 从 Solana 读取余额
* `transfer()` → 调用 SPL Token 预编译（`0xff...05`）→ 在 Solana 上移动代币
* `approve()` / `allowance()` → 使用 EVM 存储（标准 ERC-20 模式），因为 SPL 原生不支持 EVM 风格的授权额度
* `totalSupply()` → 从 SPL 铸币账户读取

## ERC20SPLFactory

工厂合约可为任何 SPL 代币部署包装器：

```solidity
import {ERC20SPLFactory} from "@rome-protocol/solidity-sdk/contracts/token/ERC20SPLFactory.sol";

ERC20SPLFactory factory = ERC20SPLFactory(0xfd21da046c282e1d36cc45e46d9599cff5742f2b);

// 为一个 SPL 铸币部署包装器（从 Metaplex 元数据加载名称/符号）
address wrapper = factory.add_spl_token_with_metadata(splMintPubkey);

// 或手动指定名称/符号
address wrapper = factory.add_spl_token_no_metadata(splMintPubkey, "USD Coin", "USDC");
```

**工厂地址（devnet）：** `0xfd21da046c282e1d36cc45e46d9599cff5742f2b`

## 代币注册表

该 `TokenRegistry` 提供对已批准 SPL 代币的管理员控制注册，并附带跨链元数据：

```solidity
import {TokenRegistry, TokenOrigin} from "@rome-protocol/solidity-sdk/contracts/token/TokenRegistry.sol";

// 注册一个原生 SPL 代币
registry.registerToken(
    splMint,
    TokenOrigin.NativeSPL,
    bytes32(0),   // 无外部地址
    0             // 无外部链
);

// 注册一个带有跨链元数据的 Wormhole 包装代币
registry.registerToken(
    wormholeMint,
    TokenOrigin.WormholeWrapped,
    externalTokenAddress,  // 源链上的原始代币
    2                       // 以太坊的 Wormhole 链 ID
);
```

该注册表确保每个资产映射到单一的规范 SPL 铸币——防止多个 USDC 表示形式导致流动性碎片化。

## 存款 / 提款流程

### 将 SPL 存入 → EVM

1. 用户将 SPL 代币转入桥接金库
2. 桥为用户在 Rome EVM 上创建 ATA（如果不存在）
3. ERC-20 包装器变为激活状态——用户可在 MetaMask 中看到余额

### 从 EVM 提取 → SPL

1. 用户调用 Withdraw 预编译（`0x42...16`）在 Rome EVM 上
2. 预编译执行 SPL 转账，将代币从用户的 PDA 转回其 Solana 钱包
3. SPL 代币会出现在用户的 Solana 钱包中

## PDA 推导

每个 EVM 地址都会映射到一个 Solana PDA，该 PDA 拥有其代币账户：

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

// 获取某个 EVM 地址对应的 Solana PDA
bytes32 userPda = RomeEVMAccount.pda(msg.sender);

// 获取用户针对特定铸币的 ATA
bytes32 ata = AssociatedSplToken.create_associated_token_account(userPda, mintPubkey);
```

## 关键模式

### 从 Solidity 读取 SPL 余额

```solidity
// 读取原始 SPL 代币账户状态
ISplToken.Account memory account = SplToken.account_state(tokenAccountPubkey);
uint64 balance = account.amount;
uint8 decimals = SplToken.decimals_eq(mintPubkey, 9); // 验证小数位
```

### 通过 SPL 预编译转移代币

```solidity
// 直接进行 SPL 转账（比 ERC-20 包装器更底层）
SplToken.transfer(recipientAta, mintPubkey, amount);
```

## Gas 代币

每条 Rome EVM 链都有自己的 Gas 代币——在链注册时选择的任意 SPL 代币：

* **RSOL** — 默认 Gas 代币（包装的 SOL）
* 自定义代币——任意 SPL 代币，通过 Meteora DAMM V1 池定价

Gas 代币是 SPL 代币的 ERC-20 表示形式。在 EVM 内部的 Gas 支付中，Transfer Hooks 不会触发。

## 约束

* SPL 代币数量是 `uint64` ——最大值为 18,446,744,073,709,551,615
* 新 SPL 铸币的默认小数位：9
* ERC-20 包装器符号在每个工厂内必须全局唯一
* 授权额度使用 EVM 存储（而非 Solana delegates）

## 下一步

* [Transfer Hooks](/zh/he-xin-gai-nian/transfer-hooks.md) — Token-2022 转账钩子中的 EVM 逻辑
* [代币包装指南](https://github.com/rome-protocol/docs/blob/main/developer-guides/token-wrapping.md) — 部署你自己的 ERC-20 包装器
* [合约地址](/zh/can-kao/contract-addresses.md) — 已部署的工厂地址


---

# Agent Instructions: 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:

```
GET https://docs.rome.builders/zh/he-xin-gai-nian/token-interop.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
