> 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/romenoapuri/bloom/building.md).

# Bloom 上での構築

このガイドが答えるのは1つの問いです： **ビルダーはこれをどうやって自分で行うのか？** Solidityの実世界資産コントラクトを取り上げ、それをRomeチェーン上で許可制トークンとして立ち上げ、ブリッジも第2トークンも使わずにSolanaウォレットとEVMウォレットの両方に開放し、独自のKYC/コンプライアンス判定を組み込む。

対象読者は、このアプローチを評価するか採用する開発者です。Bloomは全体を通しての実例であり、そのライブ資産 **ARCV（「Mineral Vault I」）** がHadrian（`200010`)上にあり、以下の各手順の実例です。また、そのデプロイ領収書（`deployments/200010.tokens/ARCV.json`）には、この方法が生成する正確な3つのコントラクトと8つのトランザクションが記録されています。

ここで資産コントラクトを変更することはありません。BloomはPlumeのArcフレームワークを **変更せずに** （ベンダー提供のツリーは `contracts/`にあり、 `plumenetwork/contracts` と同一のバイト列で、固定箇所は `NOTICE`にあります）。Rome側のコントラクトは `ArcTokenFactoryV2`だけで、これに1つの関数が追加されています。作業の本質は、 *どのように* あなたがデプロイし *どのように* あなたがコントラクトを操作することにあり、変更することではありません。

> **方法の概要。** 許可制トークンは **3つの** デプロイ済みコントラクトです。デプロイは **段階的に** — 1回のトランザクションにつき1コントラクト — 行われます。1回のファクトリー呼び出しではRomeのトランザクションごとのアカウント上限を超えるからです。そのプロキシは **来歴ロックされたアーティファクト** から来なければならず、そうでない場合オンチェーン登録は失敗して閉じます。コンプライアンスはアドレスごとに1つの **ブール値**で、あなたのKYCプロセスが承認した者によって書き込まれます。そして同じデプロイは、Solanaウォレットからも **合成送信者** を介してオンチェーンで導出され到達可能です。ブリッジもラップトークンも第2の許可リストも不要です。

## 前提条件

> **始める前に。** このガイドで使う発行者向けツールはまだ公開されていません。これにはスクリプト（`deploy-infra.ts`, `create-token.ts` などの残り）と、 `contracts/` および `rome-contracts/` ツリー、さらにそこに記載されたデプロイ領収書が含まれます。発行者はRomeへのオンボーディングを通じてこのツールを受け取ります。それまでは、このガイドを方法と各スクリプトの役割の説明として読んでください。

参照用ツールは `scripts/` ディレクトリにあります（オンボーディング時に提供されます。依存関係は `npm install`でインストールしてください）。各コマンドはチェーンの情報をハードコードせずレジストリから解決するため、別のRomeチェーンへ切り替えてもコード変更ではなく環境変更で済みます：

```bash
export PRIVATE_KEY=…       # 対象チェーン上の資金供給済みデプロイ／発行者キー
export CHAIN_ID=200010     # チェーンを選択
export REGISTRY_ROOT=…     # Romeチェーンレジストリのチェックアウト
# Solanaレーンのフローにはこれも必要です：
export SOLANA_KEYPAIR=…    # 資金供給済みSolanaキーペアJSONへのパス（手数料支払者）
```

RomeチェーンのガスはUSDC建てです。デプロイ全体のコストはdevnetでおよそ10〜15ネイティブ単位です。鍵は環境変数からのみ取得し、リポジトリには置きません。デプロイ前にコントラクトを一度ビルドしてください。なぜならアーティファクトは **必ず** から取得しなければならず、 `rome-contracts/out` （手順3を参照）：

```bash
cd contracts       && forge build --via-ir     # ベンダー提供のArcスイート
cd rome-contracts  && forge build --via-ir     # ArcTokenFactoryV2 + 標準プロキシ
```

***

## 手順1 — 許可制トークンとは何かを理解する *は* ここで

許可制のBloomトークンは1つのコントラクトではありません。 **3つのデプロイ済みコントラクト**と、チェーンごとにopsが一度だけデプロイし、すべての発行者が再利用する4つの共有コントラクトです。

**トークンごとの3つのコントラクト** — 1つの資産はちょうど次のものを所有します：

| コントラクト                       | ソース                                                         | 内容                                                       |
| ---------------------------- | ----------------------------------------------------------- | -------------------------------------------------------- |
| `ArcTokenProxy`              | `contracts/src/proxy/ArcTokenProxy.sol`                     | トークン本体 — 共有 `ArcToken` 実装を指すUUPSプロキシ。残高、保有者、ロールはここにあります。 |
| `WhitelistRestrictions`      | `contracts/src/restrictions/WhitelistRestrictions.sol`      | 許可リストモジュール。アドレスごとに1つのブール値で、資産がゲートされている間はすべての送金で強制されます。   |
| `YieldBlacklistRestrictions` | `contracts/src/restrictions/YieldBlacklistRestrictions.sol` | 利回りモジュール。保有を変更せずに、配当分配からアドレスを除外します。                      |

**共有される、チェーンごとに1つの4つのコントラクト** （によってデプロイされる `deploy-infra.ts`):

| コントラクト               | ロール                                |
| -------------------- | ---------------------------------- |
| `RestrictionsRouter` | モジュール種別レジストリ — チェーン配線。opsが一度設定します。 |
| `ArcTokenFactoryV2`  | 登録 + 標準プロキシのコードハッシュ（手順2〜3）。        |
| `ArcToken` （実装）      | すべてのトークンがプロキシする、共有で再利用可能な資産ロジック。   |
| `ArcTokenPurchase`   | すべてのトークンと発行者が共有する1つの販売窓口（手順4）。     |
| wUSDC                | 現金側 — 利回り通貨と販売購入通貨。                |

送金制限と利回り制限は **意図的に別モジュール**です。保有者は資産を保持したまま収益を禁止でき、これが実際の制裁や裁判所命令の仕組みです。

この「3つのコントラクト」という事実は、検証において重要です。 `verify-deployments.ts` および `register-asset.ts` の両方が、トークンがデプロイ済みの3コントラクトに解決されることを前提としています。以前、手作業でトークンを検証したとき、チェーン全体で7つを見落としていました（すべての利回りモジュールと3つのホワイトリストモジュール）。

ライフサイクルには3つの段階と一方通行の門があります：

```
draft     プロキシは生きているがモジュール未接続 — 送金は何もゲートされない
  ↓
ungated   モジュール接続済み、transfersAllowed = true   （WhitelistRestrictions.initialize がこれを設定）
  ↓  供給をミントし、許可リストを構築               [可逆]
gated     setTransfersAllowed(false)                [一方通行の門 — 供給は確定する]
```

ゲーティングが供給に対して一方通行なのは、mint と burn が `address(0)` を相手先として扱い、 `address(0)` は決して許可リストに載せられないからです。したがって、一度ゲートされると新しい供給は二度とミントできません。それが許可制資産が保有者に約束する保証です。

***

## 手順2 — 1回のトランザクションではなく、必ず段階的にデプロイする

Arcはワンショットの `の代わりに` を提供しており、プロキシと両方のモジュールをデプロイし、1回の呼び出しで配線します。 **Romeではそれを使えません。** そのトランザクションには **77個のSolanaアカウントロック**が必要で、Romeの1トランザクションあたりの上限は **62ロック**です。調整しても、最適化パスを通しても、絶対に収まりません。

したがってデプロイは **段階的に**です。各コントラクトを個別のトランザクションでデプロイし、トークンは `Rome は1回のトランザクションあたりのアカウント数を制限するため、トークンは段階的にデプロイされ、単発の` ではなく `の代わりに`を通じてファクトリー相当の状態を得ます。これが作成ウィザードであり、発行者コンソールが手順通りに行う内容です：

![作成ウィザード](https://3990464069-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F671PKS8ZQSNqtoC8PGvz%2Fuploads%2Fgit-blob-94683a3a49ac2418aed8cc66dbf47fad940814f7%2Fcreate-wizard.png?alt=media)

参照実装は `arc/plan/create.ts` (`createSequence()`）で、アプリはそれを6枚のカード（`bloom/lib/createCards.ts`）として、 **8つのトランザクション**にわたって表示します。順序はコントラクトが強制する通りです：

| # | トランザクション        | コントラクト呼び出し                                                                    |
| - | --------------- | ----------------------------------------------------------------------------- |
| 1 | 資産をデプロイ         | `new ArcTokenProxy(impl, initData)` ここで `initData = ArcToken.initialize(...)` |
| 2 | 許可リストモジュールをデプロイ | `new WhitelistRestrictions()`                                                 |
| 3 | あなたを管理者に設定      | `whitelist.initialize(issuer)`                                                |
| 4 | 送金時に適用          | `token.setRestrictionModule(TRANSFER, whitelist)`                             |
| 5 | 利回りモジュールをデプロイ   | `new YieldBlacklistRestrictions()`                                            |
| 6 | あなたを管理者に設定      | `yieldBlacklist.initialize(issuer)`                                           |
| 7 | 利回りに適用          | `token.setRestrictionModule(YIELD, yieldBlacklist)`                           |
| 8 | ファクトリに登録        | `factoryV2.registerToken(token, impl)`                                        |

スクリプト形式では1コマンドです：

```bash
# チェーンごとに1回（ops用。トークンごとではない）— 4つの共有コントラクトをデプロイ
npx tsx deploy-infra.ts

# トークンごと — ウィザードをスクリプトとして再実行
NAME="Mineral Vault I" SYMBOL=ARCV SUPPLY=1000000 DECIMALS=6 npx tsx create-token.ts
```

発行者（`PRIVATE_KEY`）がすべてのトランザクションに署名し、最終的にトークンのロール（`ArcToken.initialize` を付与 `msg.sender` — 手順7を参照）を保持します。登録（tx 8）は、販売窓口の `enableToken` と、ファクトリー経由のアップグレードを解放するものです。

> **これを再びモノリスへ「最適化」しないでください。** ラウンドトリップを節約するためにこれらの呼び出しを1つのトランザクションにまとめると、段階的な経路が避けようとしている62ロック超過をまさに再導入してしまいます。同じ上限が、利回り分配が1保有者ずつトランザクションを進める理由でもあります（手順8）。呼び出しが `Too many accounts: N > 62`で失敗したら、1つのトランザクションが多すぎるアカウントに触れています。調整ではなく分割してください。

***

## 手順3 — 来歴ロックされたアーティファクトからのみプロキシをデプロイする

`registerToken` は、段階的デプロイを安全にするためのセキュリティ境界です。トークンの **実行時コードハッシュが、ファクトリーにデプロイ時に焼き込まれた標準の `ArcTokenProxy`** と一致する場合にのみ受け入れます：

```solidity
// rome-contracts/src/ArcTokenFactoryV2.sol
bytes32 private immutable CANONICAL_PROXY_CODEHASH =
    keccak256(type(ArcTokenProxy).runtimeCode);

function registerToken(address token, address implementation) external {
    if (token.codehash != CANONICAL_PROXY_CODEHASH) revert ProxyCodehashUnknown();
    // …implementation はファクトリーのホワイトリストに入っている必要があり、msg.sender は
    //    token の ADMIN_ROLE を保持していなければならず、トークンはまだ登録済みであってはならない。
}
```

そのコードハッシュは **immutable**なので、他のアーティファクトから作られたプロキシは `ProxyCodehashUnknown`で失敗して閉じます。ここから2つのルールが導かれ、どちらもCIと `CLAUDE.md`:

1. **Tokenプロキシは必ず `rome-contracts/out`** からデプロイしなければなりません。 `contracts/out`— 決して別のビルドからではありません。これが、ファクトリーの標準コードハッシュが固定するコンパイル結果です。
2. **`bytecode_hash = "none"` および `cbor_metadata = false`** で `rome-contracts/foundry.toml` は重要です。メタデータを有効にすると、ファクトリーに埋め込まれる `ArcTokenProxy` 実行時 *の* 埋め込み`(type().runtimeCode`) は、ウィザードがデプロイするスタンドアロンの `out/` アーティファクトとは異なるCBOR/IPFS末尾を持つため、オンチェーンでは `registerToken` reverts while **すべてのソース内Foundryテストは成功します** （Foundryは両方のコピーを埋め込むので、不一致を検出できません）。 `rome-contracts/test/ArtifactProvenance.t.sol` がこの性質を固定しており、資金を使った実行で最初にそれが見つかりました。

> **ソース内のテストスイートがすべて緑でも、来歴を証明したことにはなりません。** イメージを出荷する前に、またコンパイラ設定を変更した後は必ず、 *デプロイ済みの* ファクトリー（読み取り専用、鍵不要）に対してこの性質を証明してください：
>
> ```bash
> cd scripts && CHAIN_ID=<id> npx tsx verify-artifact-provenance.ts
> ```
>
> これはローカルの `ArcTokenProxy` アーティファクトをハッシュ化し、そのハッシュがデプロイ済みファクトリーのバイトコード内にそのまま現れることを確認します。

コンパイラ設定を変える必要があるなら、 **ファクトリーをローテーションし** — 新しいコードハッシュをimmutableで固定した新しいものをデプロイし、配線し直してください。チェックを手作業で修正してはいけません：

```bash
cd scripts && CHAIN_ID=<id> npx tsx rotate-factory.ts
```

ローテーションでは古いアドレスは領収書に残ります。古いファクトリーで登録されたトークンは、新しいファクトリーに再登録しなければなりません。

***

## 手順4 — 共有販売窓口を通じて販売を開始する

販売は作成とは別の行為であり、1つの共有 `ArcTokenPurchase` 販売窓口上で動作します。販売を開始する前に3つの条件が満たされていなければならず、 `ArcTokenPurchase.enableToken` は各条件をオンチェーンで確認します：

```solidity
// contracts/src/ArcTokenPurchase.sol
function enableToken(address _tokenContract, uint256 _numberOfTokens, uint256 _tokenPrice)
    external onlyTokenAdmin(_tokenContract)                       // (a) あなたがトークンのADMIN_ROLEを保持している
{
    // (b) トークンはファクトリーに知られていなければならない — これが registerToken（手順2）で得たものです：
    if (ArcTokenFactory(ps.tokenFactory).getTokenImplementation(_tokenContract) == address(0))
        revert TokenNotCreatedByFactory();
    // (c) 販売窓口はすでに販売在庫を保持していなければならない：
    if (ArcToken(_tokenContract).balanceOf(address(this)) < _numberOfTokens)
        revert ContractMissingRequiredTokens();
    // …価格と数量は正でなければならない。
}
```

ゲートされた資産には4つ目の暗黙の要件があります。なぜなら **すべての送金が許可リストのゲートを通る**ので、在庫を販売窓口へ移すには、その販売窓口アドレス自体があなたのトークンでホワイトリストに入っている必要があります。したがって、販売開始の完全な手順は次の通りです：

1. **販売窓口をホワイトリストに追加** をトークンのホワイトリストモジュールで行う（`batchAddToWhitelist([storefront])` — 手順5を参照）。これがないと、(2) の在庫移転は `TransferRestricted()` 一度ゲートされると
2. **在庫を移す** 販売窓口アドレスへ（インフラ領収書の `arcTokenPurchase`).
3. **`enableToken(token, amount, price)`** — 購入者は今やwUSDCを支払い、RWAを受け取ります。

引き出しの分担は、 *共有* 販売窓口では知っておく価値があります。しかも対称ではありません（`arc/roles.ts` が権限です）：

| 操作                                                         | 権限                                    | 保持者          |
| ---------------------------------------------------------- | ------------------------------------- | ------------ |
| `enableToken` / `disableToken` / `withdrawUnsoldArcTokens` | トークンの `ADMIN_ROLE` (`onlyTokenAdmin`) | あなた、発行者      |
| `withdrawPurchaseTokens` （wUSDC収益）                         | 販売窓口の `DEFAULT_ADMIN_ROLE`            | 販売窓口をデプロイした人 |

自前運用チェーンでは、あなたがその両方です。共有販売窓口では収益プールはすべての発行者の売上であるため、その引き出しはプラットフォームレベルです。単独で制御できると想定せず、それを前提に精算を設計してください。

***

## 手順5 — 独自のKYC / コンプライアンスを組み込む

これは顧客が了承する前に読む部分なので、文章ではなくコードです。 **コンプライアンスはトークンコントラクト自体によって強制されます** — オフチェーンの審査、シーケンサーのフィルタ、取引所ポリシーではありません。ルールは両方のウォレットレーンにおいて、あらゆる実行経路へトークンとともに運ばれます。チェーンが記録するのは、あなたのKYC判定の **結果** を1つのブール値として

```solidity
// contracts/src/restrictions/WhitelistRestrictions.sol — オンチェーン状態の全体
struct WhitelistStorage {
    mapping(address => bool) isWhitelisted;   // ← アドレスごとに1つのブール値。PIIは一切なし。
    bool transfersAllowed;                    // false = gated; ホワイトリスト済みアドレスのみ送金可
    EnumerableSet.AddressSet whitelistedAddresses;
}
```

### あなたのプロセスが書き込むインターフェース

KYCベンダーの承認／却下の判定は、次の呼び出しのいずれか1つになります：

```solidity
interface IWhitelistRestrictions {
    function addToWhitelist(address account) external;                  // onlyRole(MANAGER_ROLE)
    function batchAddToWhitelist(address[] calldata accounts) external; // onlyRole(MANAGER_ROLE)
    function removeFromWhitelist(address account) external;             // onlyRole(MANAGER_ROLE)
    function isWhitelisted(address account) external view returns (bool);
    function setTransfersAllowed(bool allowed) external;                // onlyRole(ADMIN_ROLE)
}
```

### 誰が書き込めるか

> **承認に必要な権限は `MANAGER_ROLE` を&#x20;*****モジュール*****&#x20;であって `ADMIN_ROLE` トークン上ではありません。** `addToWhitelist` / `batchAddToWhitelist` / `removeFromWhitelist` はすべて `onlyRole(MANAGER_ROLE)`であり、そのロールは `WhitelistRestrictions` インスタンス上にあります。トークンで認証してからモジュールに書き込むのは、このコードベースがすでに対価を払ってきた実在の不具合です。拒否に対する修正として `ADMIN_ROLE` を提示しないでください。これはinitialize時に付与され、スイート全体で `WHITELIST_ADMIN_ROLE` どこでもチェックされず **（そのロールは何もゲートしないと断言しています）。** (`arc/roles.ts` WhitelistRestrictions.initialize(issuer)

`ウィザードのtx 3）により、発行者に` がそのモジュール上で付与されます。つまり発行者は箱から出してすぐ `DEFAULT_ADMIN_ROLE`, `ADMIN_ROLE`, `MANAGER_ROLE`, `WHITELIST_ADMIN_ROLE`、そして `UPGRADER_ROLE` を保持しており、それを委任することもできます。なぜなら発行者が `MANAGER_ROLE` を保持しているので、 `DEFAULT_ADMIN_ROLE`、 `grantRole(MANAGER_ROLE, serviceAccount)` で書き込み権限をバックエンド署名者に渡せます。

### 判定がオンチェーン書き込みになる

ベンダーのwebhookを `MANAGER_ROLE`を保持する署名者に向け、承認時にはアドレスをバッチ処理してください：

```typescript
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

// `signer` はこのトークンのホワイトリストモジュール上で MANAGER_ROLE を保持している必要があります。
const wallet = createWalletClient({ account, transport: http(chain.rpcUrl) });

// ベンダーがこれらのアドレスについて「承認済み」と通知：
await wallet.writeContract({
  address: whitelistModule,                 // getRestrictionModule でトークンから解決
  abi: whitelistAbi,
  functionName: 'batchAddToWhitelist',
  args: [approvedAddresses],
});
```

解決 `whitelistModule` トークン自体から（`ArcToken.getRestrictionModule(TRANSFER_RESTRICTION_TYPE)`）、キャッシュされたプロジェクションからではない。――書き込み先を間違えたモジュールに向けると、かつて「任意の資産で approve」があるトークンの許可リストに書き込んでしまったようなことになる。

### ウォレット署名版（セルフサービスのデモ）

訪問者が自分で参加する、権限不要のテストデプロイでは、 `MANAGER_ROLE` を `TestApprover` (`rome-contracts/src/TestApprover.sol`に付与し、その `モジュール` は **immutable**のあとで `approve(address)` は誰でも、自分のウォレットから、自分のガス代を払って呼び出せます。これは `addToWhitelist` を、アドレスがまだ登録されていない場合にのみ呼び出します：

```solidity
function approve(address account) external {
    IWhitelistRestrictions target = IWhitelistRestrictions(module);   // 不変 — モジュールは1つ、永遠に
    if (!target.isWhitelisted(account)) target.addToWhitelist(account);
    emit Approved(account, msg.sender);
}
```

これは「サインインのゲート」です。承認がオンチェーンの許可リスト項目になり、その後トークンがそれを強制する、実際の仕組みを訪問者に示しますが、いかなる資産の保管も行いません。 **を決して付与しないでください `MANAGER_ROLE` を `TestApprover` 本番トークンに** ――そのトークンの許可リストを権限不要にしてしまいます。

### ゲートが有効になったとき、そしてプロバイダを切り替えるとき

ゲートは、資産が **ゲート済み** (`setTransfersAllowed(false)`, `ADMIN_ROLE` になるまで制限しません（モジュール上で）。ゲートがない間は送金は自由です。先に許可リストを作成し、そのあとでゲートしてください。いったんゲートされると、どちらか一方でもリスト外の送金は、型付きエラー `TransferRestricted()` (`0xe827105e`).

> **後でKYCプロバイダを切り替えても、オンチェーンの変更は不要です。** 許可リストモジュール、そのインターフェース、そして保存されるブール値は、プロバイダ非依存です。ベンダーを切り替えるには、同じ `MANAGER_ROLE` 署名者に別の判断ソースを向けるだけです。再デプロイも、移行も、状態変更もありません。チェーンはどのベンダーが呼び出しを行ったかを知りませんでした。結果だけを知っているのです。

***

## ステップ 6 — 同じ資産を Solana ウォレットに開放する

同じ3つのコントラクトは、Solanaウォレットから到達できます **ブリッジも第2トークンもなしで**。Solanaネイティブのユーザーは、どこにもEVMキーを持ちません。代わりに、そのEVMアイデンティティは *synthetic*であり、トランザクションの実際のSolana署名者からオンチェーンで導出されます：

```
synthetic EVM address = keccak256(solana_pubkey)[12..32]
```

Rome EVMプログラムはこれを署名者自体から導出します（`do_tx_unsigned::derive_sender`）、そのため偽装できず、対応するsecp256k1秘密鍵も存在しません―― **このアドレスを動かせるのは、Solana署名だけです**。参照実装の導出は `arc/materialise/solana/identity.ts` (`syntheticAddress`）であり、オンチェーンのルールとバイト互換です：

```typescript
export function syntheticAddress(pubkey: SolanaPubkeyInput): `0x${string}` {
  return `0x${keccak256(pubkeyBytes(pubkey)).slice(-40)}`;
}
```

**そのレーンを支えるためにビルダーが行うこと** — なお *そのどれもコントラクト変更ではありません*:

1. **Solanaユーザーを、そのsyntheticアドレスで許可リストに入れます。** コンプライアンス層にとってsyntheticアドレスは普通のアドレスです。ユーザーのSolana公開鍵を貼り付け、syntheticを導出し、 `batchAddToWhitelist([synthetic])`を実行します。1つの許可リストが、両方のウォレット世界をまたぎます。
2. **最初の価値移動呼び出しの前に、署名用PDAを1回だけ準備します。** syntheticに署名する外部権限PDAは先に存在していなければならず、その作成は1回の `create_pda` 呼び出しです。どちら側からでも送信できます。ユーザー自身のSolanaウォレットからレーン経由で送るか（SDKの `buildCreatePdaCall`、 `isSyntheticProvisioned` が false を返した後）、または発行者がEVMキーから送るかです。Bloomの発行者ツールは、現在は保有者のオンボーディングの一環としてこれを送信します。アプリはまだユーザーの最初のアクションでそれを行わないため、保有者の最初の購入の前に準備してください。SDK呼び出しでどう行うかは [Dual-lane app](/ja/gaido/dual-lane-app.md#one-time-activate-provision-the-synthetic).
3. **手作業ではなく、レーンライブラリ経由で送信してください。** `submitDoTxUnsigned` (`arc/materialise/solana/submit.ts`）はチェーンのプロキシにトランザクションの組み立てを依頼し、手組みのものが間違える点を処理します。レーン上のすべてのコントラクト呼び出しは、 **プロキシによって組み立てられたSolana v1トランザクション** (`rome_composeUnsignedAtomicV1`であり、アドレス参照テーブルはありません。レーンライブラリは古い形式へフォールバックせず、失敗時は閉じます。ステップ5のキャッシュイン操作は、アプリ自身が組み立てる別個のSolanaトランザクションであり、組み立て済みのコントラクト呼び出しではありません。
   * **アカウントの発見と計算予算は、プロキシの仕事です。** プロキシは呼び出しをエミュレートし、各アカウントの書き込み可否を決め（呼び出しの `value` はエミュレーション対象の一部なので、渡してください）、返すトランザクションに対して計算量とヒープの予算を設定します。コード側では計算予算命令を組み立てたり、手数料プールアカウントを追加したりしません。
   * **1回の呼び出しにつき2つの署名、どちらもSolanaキーから。** まずウォレットが読みやすい認可メッセージ（*Rome authorization* と、未署名のEIP-1559ペイロードのハッシュを合わせたもの）に署名します。Wallet Standardの `solana:signMessage`です。プロキシは、呼び出しを組み立てる前にこれを検証します。次に、プロキシが返すv1トランザクションに署名します（`solana:signTransaction` を生のワイヤ上で）。レーンライブラリはこれらを `signAuthHash` および `signV1Wire`として扱い、1つにまとめた方法は使いません。これを基に構築する前に、ウォレットがv1トランザクションに署名できることを確認してください。Solflareは現在devnetで可能で、Phantomはまだです。
   * **アプリは署名済みトランザクションを自分でSolana RPCに送信します。** プロキシは組み立てます。原子的な呼び出しをブロードキャストするわけではありません。
   * **署名を求める前に検証してください。** レーンライブラリは組み立て済みメッセージを解析し、トランザクションのプロンプトの前に不正なものを拒否します。
4. **最初のwUSDC支出の前に、1回限りのラッパー権限付与を入れてください。** wUSDCはユーザーのトークンをそのユーザーのSPLデリゲートとして移動するため、これまで権限付与されていないsyntheticアドレスは、最初のwUSDC送金でSPLエラー `0x4` を受け取ります。ラッパー上の `isEnabled(user)` を読み、false のときは `approve_spl(wrapper, 2^64-1, mint)` をHelperプリコンパイル上で最初の実行ステップとして前置してください。これはどちらのレーンでも普通の呼び出しで、ウォレットごとに1回です。参照は [ウォレットごとの一回限りの許可](/ja/ji-ben-gai-nian/token-interop.md#one-time-grant-per-wallet).
5. **それを使う呼び出しの前に、キャッシュを先に入れてください。** syntheticは静止時には何も持ちません。保持しているwUSDCが呼び出しの支出額に足りないときは、まず不足分をウォレット自身のUSDCトークンアカウントから移してください（syntheticのトークンアカウントを冪等に作成し、その後Romeプログラムのactivate-token-account命令で、EVMを実行せずにトークンを移動します）。これは1回のプロンプトで署名される1つのSolanaトランザクションであり、syntheticがすでに十分保持している場合はスキップされます。
6. **アトミックコンポーザが拒否するものだけをリレーします。** 1つのSolanaトランザクションに収まらない呼び出し（およそ140万計算単位、64アカウント、4 KB）は、コンポーザにより *外部のアトミック計画には反復的な組み立てが必要です*と拒否されます。その拒否が起きたときだけ、そしてそれ以前ではなく、同じ認可署名で `rome_relayUnsignedV1` を呼び出してください。プロキシが組み立てて送信し、Solana手数料を支払い、EVMトランザクションハッシュ（Solana署名ではない）を返します。syntheticはチェーンのネイティブガスをweiで支払うため、その残高が `gasLimit x maxFeePerGas`に足りない場合は、先に `rome_composeExternalDepositV1` で補充してください（ウォレット署名のv1トランザクション。金額は10^12 weiの倍数でなければなりません）。実務上はウォレットごとに1回です。revert、ネットワーク障害、拒否された署名など、その他のエラーは再送せず、そのまま再送出されます。詳細は [リレイされた呼び出しと長時間呼び出し](/ja/gaido/call-evm-from-solana.md#relayed-calls-and-long-calls).

ユーザーは **fee payer** であり、直接呼び出しのためにSolanaウォレットからlamports（SOL）を支払います。制御するsyntheticは資産を保持し、SOLは保持しません。各レーン送信の後は、次の送信の前にEVM nonceをポーリングしてください。インデクサはSolanaの確定よりわずかに遅れるため、そうしないと次の呼び出しが、チェーンですでに使い終えたnonce上で組み立てられてしまいます。

> **最初にアトミック、拒否時にリレー。** すべての呼び出しのデフォルトは、ユーザーのウォレットで署名され、1つのSolanaトランザクション内で実行される1つのEVMトランザクションです。リレーは、収まらない呼び出しに対する例外であり、好みで選ぶ第2の経路ではありません。直接呼び出しは2回のプロンプト、リレー呼び出しは1回、そしてリレー呼び出しはlamportsではなくRome gasで支払われます。複数ステップの行程（approveしてからbuyする）は、複数の完全な呼び出しであり、それぞれが独自のトランザクションです。1つの実行をトランザクション間で分割するのではありません。

**RWAが周辺から漏れ出さない理由。** 資産はEVMステート上に存在します。残高はRome EVMプログラムのアカウント内のストレージです。 **SPLミントはありません** し、Solanaネイティブな移動対象もありません。レーンの資産移動プリミティブはSPLトークンアカウントに対して動作しますが、EVMストレージの残高にはそれがありません。Solana署名で表現できる移動はすべてトークンコントラクトへの呼び出しであり、そこで許可リストゲートが実行されます。提供済みのスイートでは、非ホワイトリストアドレスへの送金は、EVMキーで署名してもSolanaキーで署名しても同一に revert することをアサートします。現金側（wUSDC）は意図的な例外で、SPLに裏付けられており *は* ユーザー自身のトークンアカウントへスイープされます。

***

## ステップ 7 — デプロイ後に誰が権限を持つかを把握する

`ArcToken.initialize` はプロキシコンストラクタ（tx 1）から実行され、ロールを `msg.sender` に付与します。――ワザードの実行主体、つまり発行者です。明示的に **5つ** のロールを付与します：

```solidity
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
_grantRole(MANAGER_ROLE, msg.sender);
_grantRole(YIELD_MANAGER_ROLE, msg.sender);
_grantRole(YIELD_DISTRIBUTOR_ROLE, msg.sender);
```

ビルダーが見落としてはならない2つの結果：

* **`MINTER_ROLE`, `BURNER_ROLE`、そして `UPGRADER_ROLE` は initialize では付与されません。** 初期供給は `initialize` 内でミントされます（内部の `_mint`で、ロールは不要です）が、 *後の* `トークンのラッパーを見つけるには、その` または `burn` には、まず発行者が自分自身にそのロールを付与する必要があります。彼らは `DEFAULT_ADMIN_ROLE` を持っているためそれができます（各ロールの管理者であり、どこからも `_setRoleAdmin`は呼ばれないので、 `DEFAULT_ADMIN_ROLE` がそれらすべてを管理しています）。ただし、これは意図的な追加手順であり、常時の権限ではありません。
* **`UPGRADER_ROLE` はfactoryに付与されることはありません。** factoryを介したアップグレード（`ArcTokenFactoryV2.upgradeToken`）には、 **factory自身** が `UPGRADER_ROLE` をトークン上で保持している必要があります。自分自身に付与しても何も起こりません。その付与はfactoryが取り消せない一方通行なので、オプトインです。factory介在のアップグレードを使いたいなら、 `grantRole(UPGRADER_ROLE, factory)` を明示的に行ってください。そうでなければ、発行者がトークンを直接アップグレードします。

完全な21権限モデル（6つのコントラクトにまたがる11のロール名――同じ名前でもコントラクトごとに別の権限です）は `arc/roles.ts`に文書化されています。 `rolesRequiredFor(contract, functionName)` を、ロール自身のコントラクトを読む代わりに使ってください。4つのストアフロント関数と2つのfactory関数は、 *異なる* コントラクト上にある権限によってゲートされているためです。

***

## ステップ 8 — 利回りを分配する

トークンはwUSDCで利回りを支払います（ `initialize`で固定、 `setYieldToken` 未満 `YIELD_MANAGER_ROLE`で変更可能）。分配は保有者セットを順に走査します：

```solidity
function distributeYieldWithLimit(uint256 totalAmount, uint256 startIndex, uint256 maxHolders)
    external onlyRole(YIELD_DISTRIBUTOR_ROLE) nonReentrant
    returns (uint256 nextIndex, uint256 totalHolders, uint256 amountDistributed);
```

正しく駆動するために重要な仕組み：

* **全額 `totalAmount` は、呼び出し元からは `startIndex == 0` のときだけ** (`safeTransferFrom(msg.sender, this, totalAmount)`として引き出されます。最初のウィンドウの前に、その総額をトークンに承認してください。後続のウィンドウは、すでに保持している残高から支払われます。
* **`nextIndex` は `0` でラップします。** ループで `distributeYieldWithLimit(total, nextIndex, …)` を `nextIndex` が0に戻るまで `0`.
* **利回り制限のある保有者の持分はトークンコントラクトに残ります** ――再分配されず、スキップされます。

> **Romeでは実用上の上限は `maxHolders = 1` トランザクションごとに** ――ステップ2と同じ62アカウント上限――です。そのため、利回りは1トランザクションごとに1保有者ずつ走査されます（保有者1人あたり約318Kガスを計測）。ホワイトリスト済みの全保有者は、EVMでもSolanaネイティブでも、按分で受け取ります。参照スクリプトは `measure-yield-batch.ts` で、funded smokeの利回りステップがエンドツーエンドでそれを検証します。

```bash
# USAGE.md 参照形式: nextIndex が 0 に戻るまで、1 tx につき1保有者を処理
distributeYieldWithLimit(total, offset, 1)
```

***

## ステップ 9 — デプロイしたものを検証する

Romeではソースが自動でどこにも送信されないため、検証は自分で実行するステップです。インフラでも新しいトークンでも、何かをデプロイした後は、読み取り専用・鍵不要・冪等の検証ツール（すでに検証済みのコントラクトはスキップされるので、トークン発行後に再実行すると新しいものだけが検証されます）を実行してください：

```bash
cd scripts && CHAIN_ID=<id> npx tsx verify-deployments.ts
```

これが期待するのは **3つの** トークンごとのコントラクト（proxy + 両方のモジュール）に加えてインフラであり、デプロイを失敗させるのではなく、検証できなかったものを報告します。これを手作業で1回やったとき、7つのコントラクトを見落としました。

Romeレジストリに資産を登録すると、検証は **ゲート**として扱われ、装飾ではありません（`register-asset.ts`）：トークンが `apps/arc/<chain>.json` レコードを得るには、 `standard: arc-permissioned-erc20` と `transferRestriction` ――その許可リストゲートの名前――を載せたうえで、3つのコントラクトすべてがSourcifyで検証されている必要があります。そのレコードは事実をチェーンから読み取ります（wizardでデプロイされたトークンにはコミット済みのレシートがありません）。ローカルレジストリのチェックアウトにのみ書き込みます。PRを通すのは人の仕事です。

```bash
CHAIN_ID=200010 npx tsx register-asset.ts <tokenAddress>        # 検証 + レジストリレコードの準備
CHAIN_ID=200010 DRY=1 npx tsx register-asset.ts <tokenAddress>  # 読み取り専用プレビュー
```

全ループのエンドツーエンド証明――作成、ホワイトリスト化、ゲート、両レーンでの売却、利回り分配――のために、任意のチェーンで funded smoke を実行してください：

```bash
PRIVATE_KEY=… CHAIN_ID=… SOLANA_KEYPAIR=… npx tsx smoke.ts
```

***

## ステップ 10 — オフチェーンに残るものを把握する

信頼モデルは意図的に正直であり、ビルダーはそれを自分のユーザーにも同じように示すべきです：

* **許可リストフラグは、あなたのオフチェーン判断です。** KYC/AML はあなたのプロセス、あなたのベンダー、あなたのルールで行われます。チェーンは *結果*, `isWhitelisted(addr)`.
* **オンチェーン状態は、アドレスごとに1つのブール値だけです。** PIIも、書類も、アイデンティティの主張も、オンチェーン証明も、そのアドレスが誰のものかを示す暗号学的証明もありません。これはPlume上で使われるArcのモデルであり、オンチェーン・アイデンティティ標準（例: ERC-3643）の重い移転を、より軽い仕組みと発行者保有の信頼に置き換えます。
* **復旧は発行者の権限であり、逃げ道ではありません。** 失われたウォレット――EVMでもSolanaでも――は、代替アドレスをホワイトリストに入れ、必要であれば自分の法的手続きの下でmint/burn/upgradeを使って復旧します（Arcには強制送金の組み込みプリミティブはありません）。したがって、発行者の鍵管理はコンプライアンス体制の一部です。
* **Bloom独自のリレイヤーはなく、保管面もありません。** ユーザーは両レーンで自分のガスを自分で持ちます。アプリがfee-payerサービスを運営するということは、鍵を保持することを意味し、RWA製品が追加すべきではない攻撃面です。Solanaレーンでの1つのリレーは、1つのトランザクションに収まらない呼び出しのためのチェーンプロキシのものであり、ユーザーはそれでも自分の署名で承認し、自分のRome gasで支払います。

***

## 参照: ツール

すべてのスクリプトはツールの `scripts/` ディレクトリから実行され、 `PRIVATE_KEY`, `CHAIN_ID`、そして `REGISTRY_ROOT` がエクスポートされています。

| スクリプト                           | 何をするか                                             |
| ------------------------------- | ------------------------------------------------- |
| `deploy-infra.ts`               | チェーンごとに1回: wUSDC 用に配線された4つの共有コントラクト。              |
| `create-token.ts`               | 部分的な wizard ― 8トランザクション、1トークン。                    |
| `rotate-factory.ts`             | コンパイラ/成果物の変更後に新しいfactoryをデプロイし、配線し直します。           |
| `verify-artifact-provenance.ts` | デプロイ済みfactoryがあなたのproxy成果物を受け入れることを証明します（読み取り専用）。 |
| `verify-deployments.ts`         | トークンごとの3コントラクト + インフラをSourcifyで検証します（読み取り専用）。     |
| `register-asset.ts`             | ゲートを検証し、その後レジストリのコンプライアンス記録を準備します。                |
| `measure-yield-batch.ts`        | チェーン上で1トランザクションあたりの利回り上限を再計測します。                  |
| `smoke.ts`                      | 両レーンをまたぐ、資金投入済みのエンドツーエンドスイート。                     |
| `sweep-synthetic-native.ts`     | syntheticアカウントからネイティブガスを回収します（テストの衛生管理）。          |

より深い背景は、このガイドの横にあります： `docs/ARCHITECTURE.md` （レイヤー化されたアプリモデル）、 `docs/LANES.md` （2つのレーンと保管）、 `docs/COMPLIANCE.md` （信頼モデル）、そして `docs/USAGE.md` （運用手順書）。


---

# 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/romenoapuri/bloom/building.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.
