ERC-3643でセキュリティトークンを作る前に|KYC失効と強制移転の落とし穴

コラム

/約18分で読めます

コラム

/約18分

ERC-3643でセキュリティトークンを作る前に|KYC失効と強制移転の落とし穴
目次(タップで折りたたみ)

    ある証券会社の開発チームが、ST(セキュリティトークン)をERC-3643で発行することになりました。ERC-3643は、受取人の資格や取引条件に応じて移転を制限するトークン規格です。設計の打ち合わせでは、こんな前提が当然のように語られます。

    • 「投資家のKYCを失効させれば、その人の送信も止まる」
    • 「取引ルール(Compliance)を通すようにしておけば、管理者の強制移転もそのルールに縛られる」

    ところが公式実装v4.1.3のコードを読むと、通常の移転で身元を確かめているのは受取人だけです。管理者の強制移転(forcedTransfer)も、Complianceの判定を呼んでいません。つまり、どちらの前提も成り立ちません。

    管理者の操作がルールの外を通れる以上、通常の移転の判定だけを設計しても足りません。凍結、強制移転、復旧、発行・消却、実装の変更を行える管理権限まで含めて設計します。以下では、この開発チームのアーキテクトとスマートコントラクト開発者が進める順に、コントラクト同士の関係、実装の順番、管理権限の統制、本番前のテストをコードとあわせて見ていきます。

    この記事でわかること

    • 通常の移転で確かめられることと、6つのコントラクトの役割
    • 実装の6段階と、Compliance Moduleの作り方
    • 強制移転などの管理権限とアップグレードの統制、本番前に試すテスト

    この記事で使う言葉

    • T-REX:ERC-3643の別名。公式実装の名前にも使われる
    • ONCHAINID:ウォレットとは別に、投資家の身元を表すコントラクト
    • Claim/Claim Topic:身元についての証明(例:KYC済み)と、その種類
    • Trusted Issuer:そのClaimを出してよいと信頼された発行者
    • Compliance Module:保有上限やロックアップなど、商品ごとの取引ルールを実装する部品

    規格全体の使い分けはEIP/ERCユースケース別早見表、RWAの資産・発行・流通の概観はRWAトークン化の入門記事で扱っています。

    通常の移転では、何がどの順で確かめられるか

    公式実装の通常の移転は、ERC-20互換の残高更新より前に、いくつもの確認をします。一時停止、送る側・受け取る側のウォレットの凍結、送れる残高、受取人の身元(Identity)、取引ルールです。すべてを満たしたら残高を移します。最後にCompliance.transferredを呼び、保有者数や期間内の移転量など、モジュール側の状態を更新します。

    // 公式Token.sol v4.1.3の処理順を簡略化
    require(!paused);
    require(!frozen[from] && !frozen[to]);
    require(amount <= balanceOf(from) - frozenTokens[from]);
    require(identityRegistry.isVerified(to));
    require(compliance.canTransfer(from, to, amount));
    
    _transfer(from, to, amount);
    compliance.transferred(from, to, amount);

    大事なのは、isVerifiedとcanTransferが別の問いに答えていることです。

    • Identity Registry(isVerified):「受取人は、この資産を持てる主体か」。ウォレットとONCHAINID、必要なClaim Topic、Trusted Issuerを突き合わせる
    • Compliance(canTransfer):「この送る人・受け取る人・数量の組み合わせを、今実行できるか」。紐付けたすべてのモジュールにmoduleCheckをかける

    該当するコードはToken.solとIdentityRegistry.solで確かめられます。

    冒頭の1つめの誤解はここから来ます。公式v4.1.3の通常の移転で、身元の検証の対象になるのは受取人です。送る側の資格が失効したときの扱いは、次の手段を組み合わせて、はっきり決めておきます。

    • ウォレットの凍結、Identity Registryからの削除
    • Complianceのルール
    • 償還・強制移転の運用

    「KYCのClaimを失効させれば、すべての操作が自動で止まる」と考えてはいけません。

    6つのコントラクトは、どう役割を分けているか

    ERC-3643は、1つのTokenコントラクトではありません。責任の違うコントラクトの集まりで、大きく3つの層に分かれます。

    資産の層

    • Token:残高、凍結、一時停止、Identity Registry・Complianceへの参照を持つ。すべての判定の入口で、残高を移す

    本人確認の層

    • IdentityRegistry:下の3つのレジストリを参照し、isVerified(to)の答えをまとめる
    • IdentityRegistryStorage:ウォレットからONCHAINIDへの対応と国コードを持ち、受取人に紐づく身元を返す
    • ClaimTopicsRegistry:必要なClaim Topicを持つ。必須のトピックがすべてそろうかの基準
    • TrustedIssuersRegistry:トピックごとに信頼するClaim Issuerを持つ。署名・失効を確かめる発行者の候補

    取引ルールの層

    • ModularCompliance+モジュール:移転の上限、期間、保有の上限など、商品ごとのルールを持つ。すべてのモジュールのmoduleCheckを満たすかを評価し、移転の後に状態を更新する

    それぞれを変更できる権限は、後半の一覧にまとめています。

    Identity Registry Storageは、複数の資産で共有する構成もとれます。ただし、必要なClaimやTrusted Issuerが違う資産では、Identity Registryを分けます。公式Docsも、同じ本人確認のルールを共有する資産だけが、同じレジストリを共用できると説明しています。

    Identityコントラクトが持つのは、Claim Topic、発行者、署名、データ等です。そのため、ONCHAINIDのClaimに本人確認書類そのものを置く必要はありません。個人情報の保存先、データの意味、失効、再審査の期限は、別に設計します。

    Claim Topicを「KYC済み」の1種類にまとめてしまうと、法域、投資家の区分、商品ごとの適格性、期限の変更を安全に扱えません。一方で、個人情報や審査資料を公開のチェーンに書き込む設計も避けます。ONCHAINIDが実装する鍵とClaimの構造は、公式ONCHAINIDコントラクトのリポジトリで確かめられます。

    また、Compliance Moduleは「法令を自動で解釈する部品」ではありません。弁護士・コンプライアンス担当者等が決めた販売や移転の条件を、仕様にして実装する場所です。ERC-3643を採用しただけで、個別の資産や業務の法令適合性が決まるわけではありません。

    実装は、ルール表と権限表を先に固めてから配置する

    コントラクトを先にデプロイして後からルールを埋めると、Claim Topic、モジュールの状態、発行済みの残高の整合が崩れやすくなります。実装は次の6段階で進めます。

    1. 資産と主体を決める:トークンが誰のどの権利を表すか。ウォレット上の残高と、法的な権利者の記録をどう対応させるか。
    2. 保有資格のルールを表にする:必要なClaim Topic、Claimを出せる発行者、期限・失効、国コード、再審査、ウォレットを変えたときの扱い。文章とテストケースの両方で書く。
    3. 取引ルールを切り出す:保有上限、販売国、ロックアップ、期間内の移転量、個別承認等を、Compliance Moduleごとに分ける。発行・消却・強制移転に当てはめるかの違いも決める。
    4. コントラクト群をデプロイ・初期化する:公式TREXFactory等を使う場合も、実装、ファクトリー、ONCHAINID、モジュールのcommit・package・コンパイラ・設定値を、構成目録(マニフェスト)に固定する。
    5. モジュールを紐付けてから発行する:残高や時間を内部の状態に持つモジュールは、後から付けると過去の発行・移転を見られない。公式Docsも、状態を持つモジュールを発行後に紐付けないよう注意している。
    6. 権限を実際に運用する主体へ移す:デプロイに使ったEOA(人が秘密鍵で直接操作するアカウント)を残さない。Owner、Token Agent、Identity Registry Agent、Claim Issuer、Complianceの運用者、アップグレード権限者を、職務ごとのマルチシグ・承認の流れへ移し、移したことをテストする。

    compliance.canTransferが答えるのは、モジュールのルールだけです。実際に移転が成功する条件には、一時停止、凍結、自由に動かせる残高、allowance(送金許可額)、受取人のisVerifiedも入ります。そのため、フロントエンドの「送付可能」表示で、この関数だけを呼ばないでください。

    読み取った時点と、トランザクションが実行される時点のあいだに、Claimやルールが変わることもあります。事前のチェックは使い勝手を良くするためのもので、成功を保証するものではありません。

    Compliance Moduleは、判定と状態の更新を対で作る

    ModularCompliance.canTransferは、紐付けたモジュールのmoduleCheckを順に呼び、1つでもfalseなら移転を拒否します。成功した後は、Tokenだけがtransferredを呼び、各モジュールのmoduleTransferActionへ状態の更新を知らせます。発行と消却には、別のcreated/destroyedの流れがあります(ModularCompliance.sol)。

    interface IModule {
        function moduleCheck(
            address from,
            address to,
            uint256 value,
            address compliance
        ) external view returns (bool);
    
        function moduleTransferAction(address from, address to, uint256 value) external;
        function moduleMintAction(address to, uint256 value) external;
        function moduleBurnAction(address from, uint256 value) external;
    }

    この2段の作りでは、判定と更新に同じ定義を使うことが大事です。たとえば「保有者数の上限」で、次のような食い違いがあると、ルールは少しずつ壊れます。

    • すでに持っている人への追加の移転を、新しい保有者として数える
    • 全部を移した後も、送った人を保有者として残す
    • 発行・消却でカウンターを更新しない

    モジュール単体のテストだけでは足りません。Tokenを通した移転・発行・消却・強制移転で、状態を確かめます。

    公式リポジトリのmainを見た範囲では、contracts/compliance/modular/modulesにあるのは、抽象コントラクト、インターフェース、プロキシ、テスト用のモジュールです。CHANGELOGには追加モジュールの履歴がありますが、商用ライセンスにした旨の記載もあります。記事や古いサンプルのモジュール名が、そのままimportできるとは考えないでください。使うpackage・ライセンス・ソース・監査の対象のcommitを、デプロイの構成目録で固定します。

    強制移転・凍結・復旧には、通常の移転より強い権限がある

    ERC-3643が実務向けといわれる理由の一つは、管理のための操作を標準のインターフェースに含んでいることです。鍵の紛失、命令への対応、資格の喪失、誤った処理等に対応できます。同時に、これらはいちばん大きな権限のリスクでもあります。

    公式v4.1.3の実装では、次の操作はどれもToken Agentが呼び出せます。通常の移転との違いは次のとおりです。

    操作通常の移転との主な違い最低限残す記録
    setAddressFrozenウォレット全体の通常の移転を止める対象、根拠になる処理ID、承認者、開始・解除の時刻
    freezePartialTokens残高の一部だけを送れなくする数量、単位、残高のスナップショット、解除の条件
    forcedTransfer受取人の身元は確かめるが、canTransferを呼ばない。部分凍結を必要な分だけ解いて移す送信元・宛先・数量、法的・業務上の根拠、二者承認、トランザクションハッシュ
    recoveryAddress新しいウォレットが同じONCHAINIDの管理鍵を持つことを確かめ、全残高・凍結の状態・レジストリの対応を移す本人確認、新旧のウォレット、ONCHAINID、回復の手続きの記録
    pause / unpause通常の移転を全体で止める。v4.1.3のforcedTransfer自体にはwhenNotPausedがない障害ID、判断した人、影響、再開の条件
    mint / burn供給量とモジュールの状態を変える。v4.1.3のmintは身元とcanTransfer(0,to,amount)を確かめる原簿・入出金との照合、数量、承認、トランザクションハッシュ

    冒頭の2つめの誤解はここです。forcedTransferは、公式v4.1.3でComplianceの事前の判定を通りません。緊急時に要る機能でも、「Agentなら自由に使える」構成は避けます。コントラクトの外のシステムで、次のように補います。

    • 実行の依頼に、処理IDと証拠のハッシュを結びつける
    • 申請者・承認者・署名者を別の人にする
    • マルチシグのしきい値、宛先の許可リスト、1日の上限、監視のアラート、事後の照合をかける

    コントラクトが理由の文字列を求めないなら、業務システムの側で、理由のないトランザクションを署名の対象にしない作りにします。

    アップグレードは、コントラクト群全体の変更経路で見る

    公式T-REX実装は、Token、Identity Registry、レジストリ群、Modular Complianceをプロキシとしてデプロイします。各実装の場所は、Implementation Authorityが返す構成です。アップグレードで不具合の修正を配れる一方、判定・残高・権限の意味も同時に変わりえます。

    実装の参照先を切り替える関数は、6つのコントラクトすべてのOwnerが呼び出し元と一致することを条件にしています。

    具体的には、公式TREXImplementationAuthority.changeImplementationAuthorityが、呼び出し元と各コントラクトのOwnerの一致を確かめます。対象はToken、Identity Registry、Compliance、Identity Registry Storage、Claim Topics Registry、Trusted Issuers Registryです。一致したら、コントラクト群の参照先を変えます(実装)。これは安全のための条件であると同時に、Ownerの設計とアップグレードの影響が、コントラクト群の全体に及ぶことを示しています。

    • OwnerとAgentを分ける:日々の凍結・発行の担当に、実装を変える権限を与えない。
    • 変更を遅らせる:通常のアップグレードはマルチシグとタイムロックを通す。実装アドレス、コードのハッシュ、ストレージ配置(変数がどこに保存されるかの並び)、移行の手順を、公開・監視できる形にする。緊急の経路を設けるなら、対象の操作と有効な時間を絞る。
    • Claim Issuerの鍵を別の系統にする:Token AgentがClaimも出せると、架空の身元を登録して発行・強制移転するまでの道のりが短くなる。
    • 署名の前に、読み解いた中身を突き合わせる:マルチシグの署名画面で、コントラクト、関数セレクタ(どの関数を呼ぶかを表す識別子)、引数、チェーンID、nonce、期待するイベントを確かめる。中身を見ないまま署名しない。
    • 変更のイベントを見張る:Owner・Agent・Trusted Issuer・Claim Topic・モジュール・Compliance・Identity Registry・Implementation Authorityの変更を、アラートの対象にする。

    マルチシグ自体のモジュールやガードも、権限を足す部分です。法人・機関向けの署名の仕組みを比べるなら、Safeのモジュラー構成と採用判断も参考にしてください。

    本番前のテストでは、拒否されるべき操作と状態の整合を試す

    移転が成功する正常系だけでは、許可型トークンの受入試験になりません。少なくとも次のテストを、単体、結合、フォーク環境か検証環境、運用の机上演習に分けて行います。

    領域拒否・境目の想定確かめる不変条件
    本人確認登録されていないウォレット、必須トピックの不足、信頼していない発行者、失効したClaim、発行者の呼び出しのrevertfalseやrevertのとき、残高・モジュールの状態が変わらない
    移転一時停止、送受信の凍結、部分凍結の境目、allowanceの不足、Complianceがfalse自由に動かせる残高を超えず、失敗したトランザクションは丸ごと巻き戻る
    モジュールの状態新しい保有者、全量での退出、発行、消却、強制移転、モジュールの追加・削除カウンター、上限、期間ごとの集計が実際の残高と一致する
    特権操作Agent以外、単独の署名、誤ったチェーン、一時停止中の強制移転、凍結した残高の強制移転承認のルールとコントラクトの実際の動きの差が、監視と手順書に反映されている
    復旧新しいウォレットが管理鍵でない、残高0、登録済み、部分凍結と全体の凍結が重なる全残高、凍結、国コード、身元の対応がそろって移る
    アップグレード権限の不足、版の食い違い、ストレージ配置の差、アップグレード途中の失敗残高・Owner・Agent・レジストリ・モジュールの状態が保たれ、切り戻しの手順がある
    チェーンの外Claim発行のタイムアウト、インデクサーの遅れ、RPCの食い違い、イベントの取り直し、チェーンの再編トランザクションハッシュ・ブロック・ルールの版・処理IDを照合し直せる

    公式リポジトリには、Tokenの移転、Identity Registry、復旧、Compliance、Implementation Authority等のテストがあります。使う版のテストをそのまま流すだけでは足りません。自社で足したモジュールと運用の権限の差分に対して、性質テスト(property test:どんな入力でも成り立つべき性質を確かめるテスト)やファズテスト(ランダムな入力を大量に与えるテスト)を作ります。

    たとえば、次のことを不変条件にします。

    • 通常の移転が成功したら、すべてのモジュールの状態が同じトランザクションで更新される
    • 失敗したら、残高とモジュールの状態がどちらも変わらない
    • Agent以外は、供給・凍結・復旧を変えられない

    日本のSTでは、トークンの残高と権利の移転・業務の記録の関係をはっきりさせる

    ERC-3643は技術の標準です。トークンの残高だけで、次のことが完結するとは限りません。権利の帰属、権利の移転の合意、決済、対抗要件(第三者に権利を主張するための要件)、顧客の資産の分別管理です。

    金融庁の金融商品取引業者等向けの総合的な監督指針 IV-3-7も、電子記録移転有価証券表示権利等について、検証の対象を挙げています。保有・移転・決済の方法、通常の有価証券と違うリスク、秘密鍵等の流出リスク、分別管理です。

    実装では、少なくとも次のものを互いに参照できるようにします。

    • business_transfer_id、トークンのトランザクション
    • 法律・契約で決まった原簿、入出金・DvP、承認の記録
    • 使った保有資格・取引ルールの版

    チェーン上のトランザクションだけが成功して業務の台帳が失敗した場合や、その逆は、例外の状態として扱います。自動のやり直しで、二重に移転しないようにします。

    採用するかは、管理権限を運用できるかで決める

    ERC-3643が向いているのは、次のことをまとめて標準の形でそろえたい場合です。

    • ウォレットと身元を分け、必要なClaimとTrusted Issuerを資産ごとに決める
    • 取引ルールをモジュールにする
    • 凍結・復旧・強制移転まで標準化する

    一方、単純な許可リストで足りる資産もあります。管理する主体を置けないプロトコルや、アップグレードやClaim Issuerを続けて運用できないチームもあります。そうした場合は、コントラクト群の複雑さが利点を上回るかもしれません。

    なお、ネットワークの参加者を限る許可と、パブリックネットワーク上で資産の保有者を限るERC-3643は、別の設計の層です。前者の例は独自の権限モデルを持つコンソーシアムチェーンの事例を参考にしてください。基盤そのものの選び方はRWAのブロックチェーン選定、同じトークンを複数のチェーンへ広げる場合はトークン化資産のマルチチェーン設計で扱っています。

    詳細:コントラクトの関係と変更権限の一覧

    層コントラクト保持するもの移転時の役割主な変更権限
    資産Token残高、凍結、一時停止、Identity Registry・Complianceの参照全判定の入口と残高移転Owner / Agent
    本人確認IdentityRegistry3つのレジストリの参照isVerified(to)を集約Owner / Agent
    本人確認IdentityRegistryStorageウォレット → ONCHAINID、国コード受取人に紐づく身元を返す紐付け済みのIdentity Registry
    本人確認ClaimTopicsRegistry必要なClaim Topic必須のトピックがすべて揃うかの基準Owner
    本人確認TrustedIssuersRegistryトピックごとに信頼するClaim Issuer署名・失効を検証する発行者の候補Owner
    取引ルールModularCompliance + モジュール移転上限、期間、保有上限等の商品固有のルール全モジュールのmoduleCheckをすべて満たすか評価し、移転後の状態を更新Compliance Owner

    XTELAができること

    私たちは、ERC-3643を使うトークンについて、資産と身元のデータモデル、ClaimとComplianceのルール、コントラクト群の構成、権限の分離、鍵管理、監視、照合、復旧・障害時の手順を設計し、開発します。上のテスト表をもとにしたPoCで、強制移転や一時停止の実際の挙動と承認ポリシーの差を確かめてから本番へ進めます。法的な判断が必要な論点は弁護士と連携して進めます。設計の相談はお問い合わせからどうぞ。

    主要参考資料

    確認した版と注意

    ここでの実装の説明は、公式実装v4系を使う場合のものです。確かめたのは、ERC-3643 Final仕様と、公式実装mainのcommit dab1660f(package version 4.1.3)です。技術情報は2026年8月12日時点のものです。2026年9月24日にmainを確かめ直した時点でも、package versionは4.1.3でした。その後のcommitはTREXGatewayの変更だけで、ここで扱ったToken.sol、Identity Registry、Compliance、Implementation Authorityの該当箇所は変わっていません。モジュールのディレクトリの中身も、両日に確かめています。

    実装・Compliance Module・ONCHAINID・法令は更新されえます。個別の資産の法的な分類や、権利の移転の判断は、最新の制度にもとづいて弁護士等の専門家に確かめてください。

    お問い合わせ

    どんなフェーズからでも、お持ちのアイデアや企画をもとにご提案可能です。
    まずはお気軽にご相談下さい!