> 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/ja/ji-ben-gai-nian/token-interop.md).

# Token Interop

RomeではERC-20トークンとSPLトークンが同じ基盤アカウントである仕組み。

Rome は ERC-20 トークンとその基盤となる SPL トークンを 1 つの共有アカウントとして表します。このページでは、EVM と Solana をまたぐトークンの仕組みを説明します。

## 共有ステートモデル

Rome では、片側でトークンをロックし、もう片側でラップ版をミントすることはありません。Rome 上の ERC-20 トークンは **透過的なラッパー** Solana 上の SPL トークンアカウントの上にあるもので、ERC-20 の残高は *は* SPL の残高です。

<figure><img src="https://3990464069-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F671PKS8ZQSNqtoC8PGvz%2Fuploads%2Fgit-blob-65af9700f322d308e2cb17da3d107f87b51201a7%2Ftoken-interop.svg?alt=media" alt="An ERC-20 wrapper over the same SPL token account on Solana"><figcaption></figcaption></figure>

* ブリッジ遅延なし — ERC-20 の残高は SPL の残高そのもの
* 流動性の断片化なし — 両側の DeFi は同じトークンを参照
* ブリッジリスクなし — 悪用可能な別個のエスクローは存在しない

> 以下の import パスでは `@rome-protocol/rome-solidity`を使用します。このパッケージは npm にはありません。公開 [`rome-solidity`](https://github.com/rome-protocol/rome-solidity) repo から `npm install github:rome-protocol/rome-solidity`、または `forge install rome-protocol/rome-solidity` リマッピング付きでインストールしてください。ラッパーとファクトリのコントラクトも Foundry では OpenZeppelin を必要とします（ [Solidityコントラクトのデプロイ](/ja/gaido/deploy-solidity.md#using-the-rome-solidity-sdk)を参照）。事前コンパイルのインターフェースは [`contracts/interface.sol`](https://github.com/rome-protocol/rome-solidity/blob/master/contracts/interface.sol).

## ラッパーコントラクト

`SPL_ERC20` （およびそのキャッシュ済みトラック版 `SPL_ERC20_cached`これは、武装した transfer hook を持つものを除くすべての mint に対してファクトリがデプロイします。詳細は [Token-2022 mint](#token-2022-mints)）は、SPL mint 上で完全な ERC-20 インターフェースを提供します:

* `balanceOf()` — Solana からユーザーの ATA 残高を読み取ります（コントラクトアドレスの場合はラッパー自身の台帳を読み取ります。以下を参照）
* `transfer()` / `transferFrom()` — Solana 上でトークンを移動します
* `approve()` / `allowance()` — EVM ストレージに保持される、spender ごとの ERC-20 の allowance です（SPL トークンアカウントには EVM 風の allowance はありません）。これは下記の 1 回限りの付与とは別物です。ユーザーのトークンを動かすには、ラッパーがそのユーザーの SPL デリゲートとして機能し、ユーザーはウォレットごとに 1 回だけその権利を与えます
* `totalSupply()` — SPL mint の供給量を読み取ります

コントラクトは 1 回限りの付与を送信できないため、コントラクトのトークンはラッパー自身のトークンアカウントに保持され、ラッパーの台帳で追跡されます。 `balanceOf(contract)` はその台帳を読み取ります。

## ファクトリ

`ERC20SPLFactory` は、任意の SPL mint に対してラッパーをデプロイします:

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

// ラッパーをデプロイし、name/symbol を mint の Token-2022 メタデータまたは Metaplex から読み込みます
address wrapper = factory.add_spl_token_with_metadata(splMint);

// あるいは name/symbol を手動で指定します
address wrapper = factory.add_spl_token_no_metadata(splMint, "USD Coin", "USDC");
```

ライブのファクトリアドレスは、レジストリの `live` エントリ（v0.2.0）から取得します: Hadrian `0x37c13e4ff21fb914eb24014dcbd0f8dbeaa31078`. 以前のファクトリは依然オンチェーンにありますが、 `非推奨` または `廃止済み` 内にあります [registry](https://github.com/rome-protocol/rome-registry/tree/main/chains)としてマークされています。使用しないでください。レジストリが真実のソースなので、コードに書き写す代わりに、そこからライブアドレスを読み取ってください（ [であるものを選んでください。](/ja/rifarensu/contract-addresses.md#reading-the-contracts-list)を参照）。

## 標準ミント

オンチェーンの token-registry コントラクトは存在しません。標準ラッパーと gas/bridge トークンはオフチェーンの [`rome-protocol/registry`](https://github.com/rome-protocol/rome-registry)で管理されます。パーミッションレスのラッパーは `add_spl_token_no_metadata` で作成されたものが、オンチェーンの `TokenCreated` イベントから検出されます。これにより、各資産は流動性を断片化せずに、単一の標準 SPL mint にマップされます。

## Solidity からの SPL 操作

ユーザーの PDA 署名付き SPL プリミティブには、 **HelperProgram** 事前コンパイル（`0xFF…09`）を使ってください — ATA 作成、SPL 転送、gas↔lamports 変換:

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

IHelperProgram helper = IHelperProgram(0xFF00000000000000000000000000000000000009);

helper.create_ata(user, mint);              // ミントに対するユーザーの ATA を作成
helper.transfer_spl(to, tokens, mint);      // 呼び出し元の PDA から SPL を転送
```

`transfer_spl` には複数のオーバーロードがあります（ `transferFrom` のフロー向けの delegate 版を含む）。正確なシグネチャは `interface.sol` を参照してください。キャッシュ済みトラックでは、同等の操作は `ISplCached` (`0xFF…05`と `IAssociatedSplCached` (`0xFF…06`にあります。コントラクトは 1 つのトラックを一貫して使用します。

## ウォレットごとの一回限りの許可

ラッパーはユーザーのトークンを、 **SPL デリゲート** として移動します。したがってウォレットは、ラッパーが何かを移動できるようになる前に、ラッパーごとに 1 回その権利を付与する必要があります。付与は Helper 事前コンパイルへの 1 回の呼び出しです（`0xff00000000000000000000000000000000000009`):

```solidity
helper.approve_spl(wrapper, type(uint64).max, mint);   // approve_spl(wrapper, 2^64-1, mint)
```

`approve_spl` はユーザー自身のアカウントによって署名されるため、送信するのはユーザーであり、ラッパーやあなたのコントラクトではありません。mint は `mint_id()` ラッパー上にあります。

* **必要な対象。** それがトークンを動かしたい各ラッパーごとに、すべての EVM ウォレットと、各 Solana ユーザーのシンセティックアドレス。コントラクトがトークンを引き出す場合は `transferFrom` ラッパーに権利を付与したユーザーに対してのみ機能します。
* **確認方法。** を呼び出します。 `isEnabled(user)` ラッパー上で。これは `true` を返し、付与が設定されると
* **付与がない場合の見た目。** 転送は SPL エラー `0x4` (owner mismatch) で revert します。
* **トークンアカウントごとに 1 つの delegate。** SPL トークンアカウントには delegate が 1 つしかありません。ブリッジやルーターのように、同じトークンアカウントに対して別のプログラムへ付与すると、ラッパーの付与は置き換えられ、そのユーザーが再度付与するまでラッパーはそのユーザーに対して機能しなくなります。 `isEnabled` 以前の付与がまだ残っていると仮定するのではなく、転送前に

その [双方向アプリガイド](/ja/gaido/dual-lane-app.md#one-time-grant-the-wrapper) には両方のレーンの完全なコードがあります。

## 入金と出金

* **EVM へ** — SPL 側はユーザーの PDA 所有の ATA にクレジットし、ERC-20 ラッパーは即座に残高へ反映します。クロスチェーンの inbound 転送は、ブリッジ上のユーザー署名付き承認によってトラストレスに決済されます。
* **gas を gas トークンにラップする** — 次を呼び出します `Withdraw` 事前コンパイル（`0x42…16`). `withdraw_to_ata(wei)` はネイティブ gas をバーンし、同等量を gas トークン自身の decimals で呼び出し元のトークンアカウントにクレジットします。すると gas トークンのラッパー（たとえば wUSDC）がそれを残高として表示します。 `withdraw_to_pda(wei)` は呼び出し元の PDA に lamports を支払い、gas が SOL のチェーンにのみ適用されます。gas に USDC を使う Hadrian と Rubicon では revert するので、そこで使うのは `withdraw_to_ata` です。金額は 18 桁の gas 単位で、gas トークン単位の整数倍でなければなりません。USDC gas のチェーンでは 10^12 の倍数（6 桁単位 1 つ）でなければ、呼び出しは revert します。 `HelperProgram.deposit_from_ata(wei)` は逆方向に動作し、呼び出し元のトークンアカウント内の gas トークンをネイティブ gas に戻します。こちらも同じ倍数ルールです。これらは gas トークンの変換であり、他のトークンの退出経路ではありません。
* **Solana への出金、その他のトークン** — ラッパー自身の残高はすでに SPL の残高なので、それを外へ移すのは SPL 転送です:
  * `HelperProgram.transfer_spl_to_signer(amount, mint)` は、トランザクションに署名した Solana ウォレットのトークンアカウントへ、呼び出し元のトークンを送ります。これは Solana ウォレットのユーザーに対する戻り側です。
  * `RomeBridgeWithdraw.bridgeOutToSolana(solanaRecipient, amount, mint)` はそれらを任意の Solana ウォレットに送ります。これにはブリッジコントラクトに対するユーザーの付与が必要です（`approve_spl(bridge, …)`、上の 1-delegate の注記を参照）。また、受信者のトークンアカウントが存在している必要があります; `ensureRecipientAta(solanaRecipient, mint)` がそれを作成します。
  * `RomeBridgeWithdraw` また、CCTP（`burnUSDC`）で USDC を別チェーンへバーンし、Wormhole（`burnETH`, `burnToWormhole`).

ライブの `RomeBridgeWithdraw` アドレスをレジストリから取得してください。

## ガストークン

各チェーンには独自の gas トークンがあります — 価格は Meteora DAMM プール（v1 または v2、設定可能）で決まる任意の SPL トークンです。公開チェーン（Rubicon と Hadrian）は USDC を使います。Hadrian ではテスト USDC、Rubicon では本物の USDC です。共通のデフォルト gas トークンは存在しません。

## Token-2022 mint

ファクトリは Token-2022 mint もラップします。mint とは何かを Helper 事前コンパイルに問い合わせるために `mint_info(mint)` （セレクタ `0xe24bf5d4`）を使っており、答えに基づいてラッパーを選びます。 `mint_info` は 5 つの値を返します:

| 値              | 意味                                                             |
| -------------- | -------------------------------------------------------------- |
| `tokenProgram` | mint を所有するトークンプログラム（従来の SPL Token プログラムまたは Token-2022）         |
| `decimals`     | mint の decimals                                                |
| `hookProgram`  | transfer-hook プログラム。フックがない場合、またはフックが存在していても armed でない場合は 0     |
| `feeBps`       | transfer fee を basis points で表したもの。存在しない場合、または 0 に設定されている場合は 0 |
| `extensions`   | mint が持つ Token-2022 拡張のビットマップ。拡張タイプ N に対してビット N                |

mint が得るもの:

| ミント                                                          | ラッパー                                                                      | `wrapper_kind_by_mint` |
| ------------------------------------------------------------ | ------------------------------------------------------------------------- | ---------------------- |
| 通常の mint、フックのない Token-2022 mint、またはフックは存在するが armed ではない mint | キャッシュ済みラッパー（`SPL_ERC20_cached`)                                           | `1`                    |
| armed transfer hook を持つ Token-2022 mint                      | フック付きラッパー（`SPL_ERC20_Token2022Hooked`）、ファクトリの `hooked_wrapper_deployer()` | `2`                    |

transfer fee を持つ mint にはキャッシュ済みラッパーが割り当てられ、その `Transfer` イベントは、送信された量ではなく、手数料差し引き後に到着した量を報告します。ファクトリからその mint が受け取った種類は `wrapper_kind_by_mint(mint)`で確認できます。ライブの Hadrian ファクトリでは USDC、ETH、SOL の各ラッパーはすべて `1`.

拒否されるもの:

* armed transfer hook を持つ mint は、通常のラッパーのいずれかを取得できません。 `SPL_ERC20` または `SPL_ERC20_cached` に直接デプロイすると `ArmedTransferHookUnsupported(mint, hookProgram)`で revert します。ファクトリ経由では、このケースは代わりにフック付きラッパーへ送られます。
* SPL mint ではないアドレスが `mint_info` を呼び出すと、次で revert します `Invalid SPL mint account: <address>`.
* すでにラッパーがある mint は、次で revert します `Token exists`、そしてすでに使われているシンボルは `Token with symbol exists`.
* `add_spl_token_with_metadata` はまず mint 自身の Token-2022 メタデータから name と symbol を読み取り、次に Metaplex から読み取ります。mint にどちらもなければ、次で revert します `Mint asserts no name: use add_spl_token_no_metadata`.

フック付きラッパーの挙動:

* Token-2022 はすべての転送でフックを実行し、そのフックには転送に依存するアカウントが必要です。ラッパーはそれを算出できないため、呼び出し元は mint の extra-account-metas リストから完全なアカウント一覧を解決して渡します。ラッパーはその一覧の固定部分（フックプログラムとその検証アカウント）を確認し、変更せずにそのまま渡します。
* 通常の `transfer`, `transferFrom` および `bridgeOutToSolana` を呼び出すと、次で revert します `HookAccountPlanRequired`。 `transferWithHookAccounts`, `transferFromWithHookAccounts` および `bridgeOutToSolanaWithHookAccounts`を使ってください。これらはアカウント一覧を追加引数として受け取ります。
* ラッパーのデプロイ後に mint のフックプログラムが変わると、転送は `HookConfigurationChanged`.
* で revert します。これは直接のクロスプログラム呼び出し、すなわちレガシートラックでトークンを移動し、キャッシュ済みトラックは使用しません。コントラクトは 1 つのトラックを一貫して使い、レガシートラックでの呼び出しは原子的実行でのみ動作するため、フック付きラッパーを使う呼び出しは 1 つの原子トランザクションに収める必要があります。
* コントラクト残高用の台帳は保持しません。コントラクトに送られたトークンはコントラクト自身のトークンアカウントに着地し、コントラクトは次の `approve_spl` ラッパー自体への付与
* と同じ [1 回限りの付与](#one-time-grant-per-wallet) が必要です。

## 制約

* SPL トークン量は `uint64` (最大 18,446,744,073,709,551,615)
* ERC-20 の allowance（`approve` / `allowance`）は EVM ストレージです。ラッパーはユーザーの SPL デリゲートとしてトークンを動かします。ユーザーはラッパーごとに 1 回それを付与します（ [ウォレットごとの一回限りの許可](#one-time-grant-per-wallet))
* ERC-20 ラッパーのシンボルはファクトリごとに一意でなければなりません

## 次は

* [Contract Addresses](/ja/rifarensu/contract-addresses.md) — 事前コンパイルとチェーンごとのアドレス
* [EVMからSolanaを呼ぶ](/ja/gaido/call-solana-from-evm.md) — Solidity からの CPI と SPL 操作


---

# 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/ja/ji-ben-gai-nian/token-interop.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.
