はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

今回は、ERC7579のモジュールシステムを拡張し、署名検証とポリシー判定を分離して柔軟に組み合わせられるようにする仕組みを提案しているERC7780についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

概要

ERC7780は、ERC7579のモジュールシステム上に6つの新しいモジュールタイプを導入する提案です。 これらのモジュールタイプは「Policy(ポリシー)」「Signer(署名者)」「StatelessValidator(ステートレスバリデータ)」などであり、アカウント実装において任意で利用できます。

ERC7579は、スマートアカウントのモジュラーアーキテクチャを定義した規格です。 ERC7780はこの規格を拡張し、署名検証とパーミッション制御をより細かく分離・再利用できるようにします。

主要な特徴は以下の通りです。

  • 6つの新しいモジュールタイプ(Policy、Signer、StatelessValidator等)を追加
  • 署名検証ロジックとポリシー判定ロジックを分離可能
  • 1つのモジュールが複数のタイプを実装可能
  • ガスコストの削減が期待できる

ERC7579とは、Account Abstraction(アカウント抽象化)におけるスマートアカウントのモジュラー設計を標準化した規格です。 バリデータ、エグゼキュータ、フォールバックハンドラなどのモジュールタイプを定義しています。

動機

ERC7780が提案された背景には、署名検証とパーミッション制御をより柔軟に構成したいという課題がありました。

構成性の向上

従来のERC7579では、バリデータモジュールが署名検証とポリシー判定の両方を担当していました。 ERC7780では、これらを別々のモジュールに分離することで、各コンポーネントを独立して開発・再利用できるようになります。

例えば、署名検証ロジック(ECDSA、WebAuthn、マルチシグなど)と、実行ポリシー(支出制限、時間制限、ホワイトリストなど)を別々のモジュールとして実装し、自由に組み合わせることが可能になります。

ガス効率の改善

ステートレスなバリデータを導入することで、ストレージアクセスを削減し、ガスコストを低減できます。 StatelessValidatorは状態を持たないため、署名検証時にストレージを読み書きする必要がありません。

仕様

モジュールタイプ一覧

ERC7780では、以下の6つのモジュールタイプを新たに定義しています。

タイプID 名称 説明
5 Policy UserOperationの実行可否を判定する
6 Signer 署名の検証を行う
7 StatelessValidator 状態を持たない署名検証を行う
8 PreValidationHookERC1271 ERC1271署名検証前の前処理を行う
9 PreValidationHookERC4337 ERC4337検証前の前処理を行う
10 StatelessValidatorWithSender 送信者情報を含むステートレス検証を行う

IPolicy

Policyモジュールは、UserOperationや署名がアカウントのポリシーに適合しているかを検証します。 例えば、1日の支出上限チェックや、特定のコントラクトへのアクセス制限などを実装できます。

インターフェース定義を以下に示します。

javascript
interface IPolicy is IModule {
    function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp)
        external payable returns (uint256);

    function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external view returns (uint256);
}
関数
checkUserOpPolicy
javascript
function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp)
    external payable returns (uint256);

UserOperationがポリシーに適合しているかを検証する関数です。

この関数は、指定されたUserOperationがアカウントのポリシールールに従っているかどうかを判定します。 ポリシーに違反している場合は、検証失敗を示す値を返します。 payable修飾子が付いているのは、検証時にガス代の前払いが必要な場合があるためです。

引数

  • id
    • ポリシーを識別するための32バイトの識別子。
  • userOp
    • 検証対象のPackedUserOperation構造体。

戻り値

  • uint256
    • 検証結果を示す値。0は成功、それ以外は失敗を示す。
checkSignaturePolicy
javascript
function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
    external view returns (uint256);

署名がポリシーに適合しているかを検証する関数です。

この関数は、ERC1271形式の署名検証においてポリシーチェックを行います。 署名自体の暗号学的な検証ではなく、署名が許可されたコンテキストで行われているかを判定します。 view関数であるため、状態を変更しません。

引数

  • id
    • ポリシーを識別するための32バイトの識別子。
  • sender
    • 署名検証を要求したアドレス。
  • hash
    • 署名対象のメッセージハッシュ。
  • sig
    • 検証対象の署名データ。

戻り値

  • uint256
    • 検証結果を示す値。0は成功、それ以外は失敗を示す。

ISigner

Signerモジュールは、署名の暗号学的な検証を担当します。 ECDSA、WebAuthn、BLS署名など、様々な署名スキームを実装できます。

インターフェース定義を以下に示します。

javascript
interface ISigner is IModule {
    function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
        external payable returns (uint256);

    function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external view returns (bytes4);
}
関数
checkUserOpSignature
javascript
function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
    external payable returns (uint256);

UserOperationの署名を検証する関数です。

この関数は、UserOperationに含まれる署名が正当な署名者によるものかを検証します。 ERC4337のバリデーションフローで使用され、署名が有効であれば成功を示す値を返します。

引数

  • id
    • 署名者を識別するための32バイトの識別子。
  • userOp
    • 検証対象のPackedUserOperation構造体。
  • userOpHash
    • UserOperationのハッシュ値。

戻り値

  • uint256
    • 検証結果を示す値。0は成功、1はタイムスタンプによる遅延検証、それ以外は失敗。
checkSignature
javascript
function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
    external view returns (bytes4);

ERC1271形式の署名を検証する関数です。

この関数は、任意のメッセージに対する署名を検証します。 ERC1271のisValidSignature関数から呼び出されることを想定しており、成功時は0x1626ba7e(ERC1271のマジックバリュー)を返します。

引数

  • id
    • 署名者を識別するための32バイトの識別子。
  • sender
    • 署名検証を要求したアドレス。
  • hash
    • 署名対象のメッセージハッシュ。
  • sig
    • 検証対象の署名データ。

戻り値

  • bytes4
    • ERC1271のマジックバリュー(成功時は0x1626ba7e)。

IStatelessValidator

StatelessValidatorは、内部状態を持たずに署名検証を行うモジュールです。 検証に必要なデータはすべて引数として渡されるため、ストレージアクセスが不要でガス効率が良くなります。

インターフェース定義を以下に示します。

javascript
interface IStatelessValidator {
    function validateSignatureWithData(bytes32 hash, bytes calldata signature, bytes calldata data)
        external view returns (bool);

    function isModuleType(uint256 moduleTypeId)
        external view returns (bool);
}
関数
validateSignatureWithData
javascript
function validateSignatureWithData(bytes32 hash, bytes calldata signature, bytes calldata data)
    external view returns (bool);

提供されたデータを使用して署名を検証する関数です。

この関数は、署名検証に必要なすべての情報を引数として受け取り、ステートレスに検証を行います。 例えば、公開鍵やその他の検証パラメータをdata引数に含めることで、ストレージを参照せずに検証できます。 ERC7780では、すべてのバリデータがこのインターフェースを実装することを推奨しています。

引数

  • hash
    • 署名対象のメッセージハッシュ。
  • signature
    • 検証対象の署名データ。
  • data
    • 検証に必要な追加データ(公開鍵など)。

戻り値

  • bool
    • 署名が有効な場合はtrue、無効な場合はfalse。
isModuleType
javascript
function isModuleType(uint256 moduleTypeId)
    external view returns (bool);

モジュールが特定のタイプをサポートしているかを確認する関数です。

この関数は、モジュールが指定されたタイプID(例えば7はStatelessValidator)をサポートしているかどうかを返します。 1つのモジュールが複数のタイプを実装している場合、該当するすべてのタイプIDに対してtrueを返します。

引数

  • moduleTypeId
    • 確認するモジュールタイプのID。

戻り値

  • bool
    • 指定されたタイプをサポートしている場合はtrue。

IPreValidationHookERC1271

PreValidationHookERC1271は、ERC1271の署名検証前に前処理を行うモジュールです。 署名データの変換や、追加の検証ロジックを実行できます。

インターフェース定義を以下に示します。

javascript
interface IPreValidationHookERC1271 {
    function preValidationHookERC1271(address sender, bytes32 hash, bytes calldata data)
        external view returns (bytes32 hookHash, bytes memory hookSignature);
}
関数
preValidationHookERC1271
javascript
function preValidationHookERC1271(address sender, bytes32 hash, bytes calldata data)
    external view returns (bytes32 hookHash, bytes memory hookSignature);

ERC1271署名検証前の前処理を行う関数です。

この関数は、署名検証の前に実行され、ハッシュや署名データを変換することができます。 例えば、ドメインセパレータを追加したり、署名フォーマットを正規化したりする用途に使用できます。 戻り値として変換後のハッシュと署名を返します。

引数

  • sender
    • 署名検証を要求したアドレス。
  • hash
    • 元のメッセージハッシュ。
  • data
    • フックに渡される追加データ。

戻り値

  • hookHash
    • 変換後のハッシュ値。
  • hookSignature
    • 変換後の署名データ。

IPreValidationHookERC4337

PreValidationHookERC4337は、ERC4337のUserOperation検証前に前処理を行うモジュールです。

インターフェース定義を以下に示します。

javascript
interface IPreValidationHookERC4337 {
    function preValidationHookERC4337(
        PackedUserOperation calldata userOp,
        uint256 missingAccountFunds,
        bytes32 userOpHash
    ) external returns (bytes32 hookHash, bytes memory hookSignature);
}
関数
preValidationHookERC4337
javascript
function preValidationHookERC4337(
    PackedUserOperation calldata userOp,
    uint256 missingAccountFunds,
    bytes32 userOpHash
) external returns (bytes32 hookHash, bytes memory hookSignature);

ERC4337のUserOperation検証前の前処理を行う関数です。

この関数は、UserOperationの検証前に実行されます。 アカウントに不足しているガス代(missingAccountFunds)も引数として渡されるため、ガス代の前払い処理なども実装できます。 viewではなく状態変更が可能なため、必要に応じてストレージの更新も行えます。

引数

  • userOp
    • 検証対象のPackedUserOperation構造体。
  • missingAccountFunds
    • アカウントに不足しているガス代。
  • userOpHash
    • UserOperationのハッシュ値。

戻り値

  • hookHash
    • 変換後のハッシュ値。
  • hookSignature
    • 変換後の署名データ。

IStatelessValidatorWithSender

StatelessValidatorWithSenderは、送信者情報を含めたステートレス検証を行うモジュールです。 基本的なStatelessValidatorに送信者のコンテキストを追加したものです。

インターフェース定義を以下に示します。

javascript
interface IStatelessValidatorWithSender {
    function validateSignatureWithDataWithSender(
        address sender,
        bytes32 hash,
        bytes calldata signature,
        bytes calldata data
    ) external view returns (bool);
}
関数
validateSignatureWithDataWithSender
javascript
function validateSignatureWithDataWithSender(
    address sender,
    bytes32 hash,
    bytes calldata signature,
    bytes calldata data
) external view returns (bool);

送信者情報を含めてステートレスに署名を検証する関数です。

この関数は、validateSignatureWithDataに送信者アドレスを追加したバージョンです。 署名検証において送信者のコンテキストが必要な場合(例えば、送信者ごとに異なる検証ルールを適用する場合)に使用します。

引数

  • sender
    • 署名検証を要求したアドレス。
  • hash
    • 署名対象のメッセージハッシュ。
  • signature
    • 検証対象の署名データ。
  • data
    • 検証に必要な追加データ。

戻り値

  • bool
    • 署名が有効な場合はtrue、無効な場合はfalse。

補足

推奨実装パターン

ERC7780では、すべてのバリデータモジュールがStatelessValidatorインターフェースを実装することを推奨しています。 これにより、以下のメリットが得られます。

  • ガス効率の向上(ストレージアクセスの削減)
  • テストの容易さ(状態に依存しないため)
  • 再利用性の向上

単一モジュールの複数タイプ対応

1つのモジュールが複数のインターフェースを実装することが可能です。 例えば、以下のような組み合わせが考えられます。

組み合わせ例 説明
Signer + StatelessValidator 署名検証機能をステートレスにも提供
Policy + PreValidationHook ポリシーチェックと前処理を統合
Signer + Policy 署名検証とポリシー判定を1つのモジュールで提供

isModuleType関数を使用して、モジュールがサポートするタイプを確認できます。

モジュール間の連携フロー

以下の図は、UserOperation検証時のモジュール間の連携を示しています。

sequenceDiagram
    participant EntryPoint
    participant Account
    participant PreHook as PreValidationHook
    participant Signer
    participant Policy

    EntryPoint->>Account: validateUserOp
    Account->>PreHook: preValidationHookERC4337
    PreHook-->>Account: (hookHash, hookSignature)
    Account->>Signer: checkUserOpSignature
    Signer-->>Account: validation result
    Account->>Policy: checkUserOpPolicy
    Policy-->>Account: policy result
    Account-->>EntryPoint: final result

この図は、UserOperationがEntryPointからAccountに渡され、検証が完了するまでの流れを示しています。

  1. EntryPointがAccountのvalidateUserOpを呼び出します。
  2. AccountはまずPreValidationHookを実行し、必要に応じてハッシュや署名データを変換します。
  3. 次にSignerモジュールで署名の暗号学的な検証を行います。
  4. 署名が有効であれば、Policyモジュールでポリシーチェック(支出制限、アクセス制御など)を実行します。
  5. すべての検証が成功した場合のみ、最終結果としてEntryPointに成功を返します。

この分離アーキテクチャにより、署名検証ロジック(Signer)とビジネスロジック(Policy)を独立して開発・テスト・再利用できます。

互換性

ERC7579との関係

ERC7780はERC7579の拡張として設計されており、後方互換性を維持しています。

項目 互換性 備考
ERC7579 ✅ 既存のモジュールタイプ(1-4)はそのまま使用可能
ERC4337 ✅ UserOperation構造体をそのまま使用
ERC1271 ✅ 署名検証の標準インターフェースに対応

既存のERC7579準拠アカウントは、新しいモジュールタイプをオプションで追加できます。 新しいモジュールタイプを使用しない場合でも、既存の機能には影響ありません。

セキュリティ

セキュリティに関する詳細な考慮事項は、執筆時点ではTBD(未定)となっています。 実装時には最新の仕様を確認してください。

引用

最後に

今回は「ERC7780によるERC7579検証モジュールの拡張」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!