著者: コング

編集者: シェリー

序文

Ethereum は Pectra アップグレードを導入しようとしていますが、これは間違いなく重要なアップデートです。この機会に、多くの重要な Ethereum 改善提案が導入される予定です。その中で、EIP-7702 は Ethereum 外部アカウント (EOA) に革命的な変更を加えました。この提案により、EOA と契約アカウント CA の境界が曖昧になります。これは、EIP-4337 後のネイティブ アカウント抽象化に向けた重要なステップであり、Ethereum エコシステムに新しいインタラクション モードをもたらします。

現在、Pectra はテスト ネットワークに導入されており、まもなくメイン ネットワークでも導入される予定です。この記事では、EIP-7702 の実装メカニズムを深く分析し、それがもたらす可能性のある機会と課題を探り、さまざまな参加者に実用的な運用ガイドラインを提供します。

プロトコル分析

概要

EIP-7702 では、EOA がスマート コントラクト アドレスを指定してコードを設定できる新しいトランザクション タイプが導入されています。これにより、EOA はトランザクションを開始する機能を維持しながら、スマート コントラクトのようにコードを実行できるようになります。この機能により、EOA にプログラマビリティと構成可能性が提供され、ユーザーは EOA でソーシャルリカバリ、権限制御、マルチ署名管理、zk 検証、サブスクリプション支払い、トランザクションスポンサーシップ、トランザクションバッチ処理などの機能を実装できるようになります。 EIP-7702 は、EIP-4337 によって実装されたスマート コントラクト ウォレットと完全に互換性があることは注目に値します。両者のシームレスな統合により、新しい機能の開発および適用プロセスが大幅に簡素化されます。

EIP-7702 の具体的な実装では、SET_CODE_TX_TYPE (0x04) のトランザクション タイプを導入します。そのデータ構造は次のように定義されます。

rlp([chain_id、nonce、max_priority_fee_per_gas、max_fee_per_gas、gas_limit、destination、value、data、access_list、authorization_list、signature_y_parity、signature_r、signature_s])

authorization_list フィールドは次のように定義されます。

authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]

新しいトランザクション構造では、authorization_list フィールドを除き、残りは EIP-4844 と同じセマンティクスに従います。このフィールドはリスト型です。リストには複数の承認エントリを含めることができます。各承認エントリで:

  • chain_id フィールドは、この承認委任が有効になるチェーンを示します。
  • アドレス フィールドは委任先のターゲット アドレスを示します。
  • nonceフィールドは、現在の承認済みアカウントのnonceと一致している必要があります。
  • y_parity、r、sフィールドは、承認に署名する承認済みアカウントの署名データです。

トランザクションの authorization_list フィールドには、複数の異なる承認済みアカウント (EOA) によって署名された承認エントリを含めることができます。つまり、トランザクションの開始者は承認者と異なる可能性があり、承認者の承認済み操作に対するガス支払いを実現します。

成し遂げる

認証データに署名する場合、認証者はまず chain_id、address、nonce に対して RLP エンコーディングを実行する必要があります。エンコードされたデータは、Keccak256を使用してMAGIC番号とともにハッシュされ、署名されるデータが得られます[1]。最後に、ハッシュ化されたデータは、許可された人の秘密鍵を使用して署名され、y_parity、r、s データが得られます。このうち、MAGIC (0x05) は、異なるタイプの署名の結果が競合しないようにするためのフィールド区切り文字として使用されます。

// Go-ethereum/core/types/tx_setcode.go#L109-L113func (a *SetCodeAuthorization) sigHash() common.Hash { return prefixedRlpHash(0x05, []any{ a.ChainID, a.Address, a.Nonce, })}

認可者によって認可されたchain_idが0の場合、認可者はEIP-7702をサポートするすべてのEVM互換チェーンで認可を再生することを許可する[2]ことを意味することに注意してください(ノンスも一致する場合)。

// Go-ethereum/core/state_transition.go#L562if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 { return authorization, ErrAuthorizationWrongChainID}

承認者が承認データに署名すると、トランザクション開始者は署名のために authorization_list フィールドにそのデータを集め、RPC 経由でトランザクションをブロードキャストします。トランザクションが実行のためにブロックに含まれる前に、提案者はまずトランザクションの事前チェックを実行します[3]。この事前チェックでは、このトランザクションが契約作成トランザクションではないことを確認するために、宛先アドレスに対して必須のチェックが実行されます。つまり、EIP-7702タイプのトランザクションを送信する場合、トランザクションの宛先アドレスは空にできません[4]。

// Go-ethereum/core/state_transition.go#L388-L390if msg.To == nil { return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From)}

同時に、このようなトランザクションでは、トランザクション内の authorization_list フィールドに少なくとも 1 つの承認エントリが含まれるように強制されます。複数の承認エントリが同じ承認者によって署名されている場合、最後の承認エントリのみが有効になります。

// Go-ethereum/core/state_transition.go#L391-L393if len(msg.SetCodeAuthorizations) == 0 { return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From)}

その後、トランザクションの実行中に、ノードはまずトランザクション イニシエーターの nonce 値を増やし、次に authorization_list 内の各承認エントリに対して ApplyAuthorization を適用します。 applyAuthorization 操作では、ノードは最初に承認者の nonce をチェックし、次に承認者の nonce を増分します。つまり、トランザクションの開始者と承認者が同じユーザー (EOA) である場合、承認トランザクションに署名するときに nonce の値を 1 増やす必要があります。

// Go-ethereum/core/state_transition.go#L489-L497func (st *stateTransition) execute() (*ExecutionResult, error) { ... st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)

// EIP-7702 認証を適用します。 if msg.SetCodeAuthorizations != nil { for _, auth := range msg.SetCodeAuthorizations { // 注意: エラーは無視され、ここでは無効な認証は単にスキップされます。 st.applyAuthorization(&auth) } } ...}// Go-ethereum/core/state_transition.go#L604func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) error { authorization, err := st.validateAuthorization(auth) ... st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization) ...}// Go-ethereum/core/state_transition.go#L566func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { ... if auth.Nonce+1 < auth.Nonce { return authorization, ErrAuthorizationNonceOverflow } ...}

ノードが承認エントリを適用するときにエラーが発生した場合、この承認エントリはスキップされ、トランザクションは失敗しません。その他の承認エントリは引き続き適用されるため、バッチ承認シナリオでは DoS リスクが発生しないことが保証されます。

// Go-ethereum/core/state_transition.go#L494for _, auth := range msg.SetCodeAuthorizations { // 注意: エラーは無視されます。ここでは無効な認証を単にスキップします。 st.applyAuthorization(&auth)}

承認申請が完了すると、承認者アドレスのコード フィールドは 0xef0100 に設定されます。 address、ここで 0xef0100 は固定識別子であり、address は委任のターゲット アドレスです。 EIP-3541 の制限により、ユーザーは通常の方法で 0xef バイトで始まるコントラクト コードを展開することはできません。そのため、このような識別子は SET_CODE_TX_TYPE (0x04) タイプのトランザクションによってのみ展開できます。

// Go-ethereum/core/state_transition.go#L612st.state.SetCode(authority, types.AddressToDelegation(auth.Address))

// Go-ethereum/core/types/tx_setcode.go#L45var DelegationPrefix = []byte{0xef, 0x01, 0x00}

func AddressToDelegation(addr common.Address) []byte { return append(DelegationPrefix, addr.Bytes()...)}

承認が完了した後、承認者が承認を削除したい場合は、委任のターゲット アドレスを 0 アドレスに設定するだけです。

EIP-7702 で導入された新しいトランザクション タイプにより、承認者 (EOA) はトランザクションを開始する機能を保持しながら、スマート コントラクトのようにコードを実行できるようになります。これにより、EIP-4337 と比較すると、ネイティブ アカウント抽象化 (ネイティブ AA) に近いエクスペリエンスがユーザーに提供され、ユーザーの使用しきい値が大幅に削減されます。

ベストプラクティス

EIP-7702 は Ethereum エコシステムに新たな活力を注入しましたが、新しいアプリケーション シナリオは新たなリスクももたらします。エコシステムの参加者が実践中に注意する必要がある側面は次のとおりです。

秘密鍵の保管

EOA はスマートコントラクトに組み込まれたソーシャルリカバリーを使用して、委託後の秘密鍵の紛失による金銭的損失の問題を解決できますが、EOA 秘密鍵が漏洩するリスクを回避することはできません。委任の実行後も、EOA 秘密鍵がアカウントに対する最高レベルの制御権を持ち、秘密鍵を保持する人物がアカウント内の資産を自由に処分できることを明確にする必要があります。ユーザーまたはウォレットサービスプロバイダーが EOA の委託を完了した後、ローカルに保存されている秘密鍵が完全に削除されたとしても、特にサプライチェーン攻撃のリスクがあるシナリオでは、秘密鍵漏洩のリスクを完全に排除することはできません。

ユーザーは、委任されたアカウントを使用する場合でも、秘密鍵の保護を最優先し、常に「自分の鍵がなければ、自分のコインもない」ということを覚えておく必要があります。

マルチチェーンリプレイ

委任承認に署名する際、ユーザーは chainId を通じて委任が有効になるチェーンを選択できます。もちろん、ユーザーは委任に chainId 0 を使用することもできます。これにより、委任を再生して複数のチェーンに反映させることができるため、ユーザーは 1 つの署名で複数のチェーンに委任するのが便利になります。ただし、複数のチェーンに委任された同じコントラクト アドレスに異なる実装コードが存在する可能性があることに注意してください。

ウォレット サービス プロバイダーは、ユーザーが委任を行う際に、委任の有効なチェーンが現在接続されているネットワークと一致しているかどうかを確認し、chainId 0 で委任に署名することで発生する可能性のあるリスクをユーザーに通知する必要があります。

また、ユーザーは、異なるチェーン上の同じコントラクト アドレスのコントラクト コードが必ずしも同じではないことに注意し、まず委任の対象を理解する必要があります。

初期化できません

現在主流のスマート コントラクト ウォレットのほとんどはプロキシ モデルを使用しています。ウォレット プロキシがデプロイされると、DELEGATECALL を介してコントラクト初期化関数が呼び出され、ウォレットの初期化とプロキシ ウォレットのデプロイのアトミック操作が実現され、プリエンプトされる問題を回避します。ただし、ユーザーが委任に EIP-7702 を使用する場合、アドレスのコード フィールドのみが更新され、委任アドレスを呼び出して初期化することはできません。これにより、EIP-7702 は、一般的な ERC-1967 プロキシ コントラクトのように、コントラクト展開トランザクションで初期化関数を呼び出してウォレットを初期化できなくなります。

開発者は、EIP-7702 を既存の EIP-4337 ウォレットと組み合わせる場合、ウォレット初期化操作中に権限チェックを実行するように注意する必要があります (ecrecover を介して署名アドレスを復元することによって権限チェックを実行するなど)。これにより、ウォレット初期化操作がプリエンプトされるリスクを回避できます。

ストレージ管理

ユーザーが EIP-7702 委任機能を使用する場合、機能要件の変更、ウォレットのアップグレードなどにより、別のコントラクト アドレスに再委任する必要がある場合があります。ただし、異なるコントラクトのストレージ構造は異なる場合があります (たとえば、異なるコントラクトの slot0 は異なるタイプのデータを表す場合があります)。再委任の場合、新しい契約が誤って古い契約のデータを再利用する可能性があり、アカウントのロック、資金の損失などの悪影響につながる可能性があります。

ユーザーの場合、再委任は慎重に処理する必要があります。

開発者は、開発プロセス中に ERC-7201 で提案された名前空間の公式に従って、指定された独立したストレージの場所に変数を割り当て、ストレージの競合のリスクを軽減する必要があります。さらに、ERC-7779 (ドラフト) は、EIP-7702 の標準的な再委任プロセスも提供します。これには、ERC-7201 を使用してストレージの競合を防ぐこと、再委任の前にストレージの互換性を確認すること、古い委任インターフェイスを呼び出して古い保存データをクリーンアップすることが含まれます。

偽のトップアップ

ユーザーが手数料を支払うと、EOA もスマート コントラクトとして使用できるようになるため、中央集権型取引所 (CEX) はユニバーサル スマート コントラクトの再チャージの状況に直面する可能性があります。

CEX は、スマート コントラクトでの誤った再チャージのリスクを防ぐために、トレースを介して各再チャージ トランザクションのステータスを確認する必要があります。

アカウント変換

EIP-7702 委任の実装後、ユーザーのアカウント タイプを EOA と SC 間で自由に変換できるようになり、アカウントでトランザクションを開始したり、呼び出したりできるようになります。つまり、アカウントが自分自身を呼び出して外部呼び出しを行うと、その msg.sender も tx.origin になり、EOA のみのプロジェクトのセキュリティ上の前提が一部破られます。

契約開発者が tx.origin が常に EOA であると想定することはもはや現実的ではありません。同様に、msg.sender == tx.origin をチェックすることによる再入攻撃に対する保護も失敗します。

開発者は、開発プロセス中に将来の参加者がすべてスマート コントラクトになる可能性があることを想定する必要があります。

契約の互換性

既存の ERC-721 および ERC-777 トークンには、コントラクトに転送するときにフック関数があるため、受信者はトークンを正常に受信するために、対応するコールバック関数を実装する必要があります。

開発者の場合、ユーザーによって委任されたターゲット コントラクトは、主流のトークンとの互換性を確保するために、対応するコールバック関数を実装する必要があります。

釣りチェック

EIP-7702 委任を実装すると、ユーザーのアカウント内の資産はスマート コントラクトによって制御される可能性があります。ユーザーがアカウントを悪意のある契約に委任すると、攻撃者が資金を盗むことが容易になります。

特にウォレット サービス プロバイダーは、できるだけ早く EIP-7702 タイプのトランザクションをサポートすることが重要であり、ユーザーが委任契約に署名するときには、委任の対象契約をユーザーに強調表示して、フィッシング攻撃のリスクを軽減する必要があります。

さらに、アカウント委任の対象契約のより詳細な自動分析(オープンソースチェック、権限チェックなど)により、ユーザーはそのようなリスクをより適切に回避できるようになります。

要約する

この記事では、Ethereum の今後の Pectra アップグレードにおける EIP-7702 提案について説明します。 EIP-7702 は、新しいトランザクション タイプを導入することで EOA をプログラム可能かつ構成可能にし、EOA と契約アカウントの境界を曖昧にします。現在、EIP-7702 と互換性のある実証済みのスマート コントラクト標準がないため、実際のアプリケーションでは、ユーザー、ウォレット サービス プロバイダー、開発者、CEX などのさまざまなエコシステム参加者が多くの課題と機会に直面しています。この記事で説明するベスト プラクティスは、すべての潜在的なリスクを網羅できるわけではありませんが、実際の運用においてはすべての関係者が参照し、適用する価値があります。

[EOAアカウントコードの設定]

https://holesky.etherscan.io/tx/0x29252bf527155a29fc0df3a2eb7f5259564f5ee7a15792ba4e2ca59318080182

[EOA アカウントコードの設定解除]

https://holesky.etherscan.io/tx/0xd410d2d2a2ad19dc82a19435faa9c19279fa5b96985988daad5d40d1a8ee2269

関連リンク

[1] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/types/tx_setcode.go#L109-L113

[2] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/state_transition.go#L562

[3] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/state_transition.go#L304

[4] https://github.com/ethereum/go-ethereum/blob/7fed9584b5426be5db6d7b0198acdec6515d9c81/core/state_transition.go#L388-L390

参考文献

[EIP-7702] https://eips.ethereum.org/EIPS/eip-7702

[EIP-4844]https://eips.ethereum.org/EIPS/eip-4844

[イーサリアムGo]https://github.com/ethereum/go-ethereum/tree/7fed9584b5426be5db6d7b0198acdec6515d9c81

[EIP-3541]https://eips.ethereum.org/EIPS/eip-3541#後方互換性

[Cobo: EIP-7702 実践ガイド]

https://mp.weixin.qq.com/s/ojh9uLw-sJNArQe-U73lHQ

[Viem]https://viem.sh/experimental/eip7702/signAuthorization

[ERC-7210]https://eips.ethereum.org/EIPS/eip-7201

[ERC-7779]https://eips.ethereum.org/EIPS/eip-7779