はじめに

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

今回は、EIP7702をAvalancheに導入し、EOAがコードを持てるようにする一方で、非同期実行(SAE)でも安全性を保つために「予約残高」と「nonce分離」を組み合わせる仕組みを提案しているACP209についてまとめていきます!

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

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

概要

提案の概要

ACP209は、EthereumのEIP7702(SetCodeTransaction)で導入された新しいトランザクション形式を、Avalanche EVMにも導入する提案です。 これにより、EOA(Externally Owned Account)が自身のアカウントにコードを設定でき、スマートコントラクトのように振る舞うEOAを実現します。 その結果、複数操作の一括実行や代理支払い、権限制御など、UXの大幅な向上が期待できます。

しかし、AvalancheはSAE(Streaming Asynchronous Execution:非同期実行)を採用しているため、EIP7702をそのまま導入すると安全性を損ないます。 そこで、ACP209ではEIP7702をAvalanche向けに拡張し、SAEの不変条件を維持した形で導入する仕組みを提案しています。

課題

EIP7702は、Ethereumの同期実行モデルを前提としており、AvalancheのSAEモデルに必要な2つの不変条件(invariants)を破壊します。

  • 残高の不変性が崩れる EIP7702では、EOAがコードを持つことで他アカウントの操作や内部呼び出しによって残高が変動し得ます。  そのため、検証時点では支払い可能と判断されたトランザクションが実行時には手数料や送付額を支払えなくなる可能性があります。
  • nonceの固定性が崩れる  delegated(コード設定済み)EOAは、トランザクション内でcreate操作を行えるため、実行中にnonceが増加します。  これにより、同一アカウントの他のトランザクションが実行順序の影響で無効化され、検証時点での有効性保証が成り立たなくなる可能性があります。

この2点により、SAEが前提とする「検証時点で実行可能性を保証する」静的解析モデルが崩れ、結果としてDoS攻撃のリスク**が生じます。

解決策

ReservedBalance(予約残高)の導入

delegated EOAに対して、支払い能力をあらかじめロックする「予約残高」を設けます。 ReservedBalanceManagerプリコンパイルで管理し、ブロック検証時に「全未実行トランザクションの最大手数料+送付額」をカバーできているかを確認します。 こうして、他のトランザクションやコールによって残高が変動しても、支払い保証が常に成立します。

Nonceの検証と実行を分離

nonce のズレによってトランザクションが無効化されないよう、検証時と実行時で別々の nonce を使用します。

  • 検証時 SettledStateRootのnonce+未実行トランザクション数で確認
  • 実行時 直近使用 nonce+1 を適用

これにより、実行中のcreate操作などでnonceが変化しても、ブロック収録済みトランザクションの有効性が維持されます。

ACP209は、EIP7702で導入された新しい「set code transaction(コード設定トランザクション)」の仕組みを、AvalancheのEVM(Ethereum Virtual Machine)実装にも取り入れる提案です。

EIP7702については以下の記事を参考にしてください。

EIP7702は、2025年5月のPectraアップグレードでEthereumメインネットに導入されました。 これにより、従来のEOA(Externally Owned Account/秘密鍵で直接操作されるアカウント)も、自身のアカウントにコードを設定できるようになりました。 つまり、スマートコントラクトのように振る舞えるEOAを実現しました。

この変更により、以下のようなUX(ユーザー体験)の改善が可能になりました。

改善点 内容
複数操作の一括実行 複数の操作を1つのトランザクションとしてまとめ、アトミックに実行できる
代理支払い 他のアカウントの代わりにトランザクション手数料を支払うことが可能
権限の一時的な低下(Privilege De-escalation) 一時的に権限を制限して安全に操作を行える

Avalancheでは、EIP7702と同様のUXを実現するために、この「set code transaction」を導入する提案を行っています。 ただし、AvalancheにはACP194で提案されたSAE(Streaming Asynchronous Execution/ストリーミング非同期実行)機構が存在するため、その仕組みと安全に共存するように調整が必要です。

ACP194については以下の記事を参考にしてください。

特に、アカウントの nonce(トランザクション順序番号)や残高の扱いを変更し、EIP7702が持つ潜在的な問題を回避する必要があります。 これにより、EIP7702の利便性をAvalancheのEVMでも享受しつつ、SAEの前提条件(invariants)を壊さない形で統合することが目的です。

動機

ACP209の背景には、EIP7702がもたらすUX改善をAvalancheでも実現したいという強い動機があります。 しかし、EIP7702をそのまま採用すると、ACP194で定義されたSAE(ストリーミング非同期実行)に必要な「不変条件(invariants)」が崩れてしまう問題があります。

そのため、ACP209では、EIP7702をAvalanche向けに安全に拡張・修正することを目的としています。

SAEの採用による期待効果

Avalancheコミュニティでは、ACP194で提案されたSAEを支持する声が多く挙がっています。 その理由は以下です。

効果 説明
ガスレートの向上 C-Chainを含むAvalanche EVMチェーンのターゲットガスレートを引き上げられる
フロントラン防止 暗号化メンプール(encrypted mempool)の導入が可能になり、フロントラン(先回り攻撃)を防げる
リアルタイムVRFの利用 トランザクション実行中にリアルタイムなVRF(Verifiable Random Function/検証可能な乱数関数)を利用できる

こうした利点を活かすためには、EIP7702の機能をAvalanche EVMに適用しつつも、SAEに必要な不変条件(invariants)を維持する必要があります。

ACP194で必要とされる不変条件

EIP7702では、Ethereumの仕様上、以下の2つの不変条件が破られています。 これらは、SAEの実現には絶対に守らなければならない条件です。

不変条件 説明
アカウント残高の減少条件 アカウントの残高は、そのアカウント自身から発行されたトランザクションによってのみ減少する
Nonceの増加条件 トランザクションの実行が始まった後にEOAのnonceが増加してはならない

これらの不変条件が必要な理由は、SAEにおける静的解析の信頼性を確保するためです。 静的解析とは、トランザクションを実際に実行せずに事前に安全性を確認するプロセスのことです。

静的解析で確認される主な内容は以下の2点です。

  • トランザクションが適切な nonce を持っていること
  • トランザクションが支払う手数料や送付額を含め、十分な残高を持っていること

SAEでは、ブロック承認時にこの解析を行い、「確実に実行可能なトランザクションだけを含めたブロック」を合意します。 ブロックが合意された後、トランザクションは非同期の実行キューに送られ、順次実行されます。

しかし、もしトランザクションの実行中にEOAの残高や nonce が変化するような仕様が存在すると、解析時点では有効と判断されたトランザクションが、実行時には無効になる可能性があります。 これが発生すると、ブロック承認後に不正なトランザクションが混入し、以下のようなリスクが生じます。

  • 無効なトランザクションが実行キューを占有する(DoS攻撃の温床になる)
  • 手数料が支払われないままブロックに含まれるため、チェーンの安定性が損なわれる

したがって、これら2つの不変条件を守ることが、AvalancheのSAEモデルを安全に維持する鍵となります。

EIP7702の課題とAvalancheでの影響

EIP7702が導入した「他のアカウントのコード設定」や「** nonce 変更**」などの仕組みは、Ethereumのメンプールにおいても静的検証を難しくすることがすでに指摘されています。

EIP7702のセキュリティ考察では、以下のように説明されています。

他のアカウントからのトランザクションを「古くする(stale)」ことが可能であり、これによりノードはトランザクションの静的な有効性を判断できなくなる。

EIP7702では、あるアカウントの状態を別のトランザクションによって動的に変えられるようになります。 例えば、以下のような状況を考えてみます。

  1. あるアカウント(A)がトランザクションTx1を発行しており、現在の状態に基づいてそのトランザクションは有効である。
  2. しかし、別のトランザクションTx2が同じアカウント(A)や関連アカウントの状態(nonceやコード、残高など)を変更できる場合、Tx1の前提が崩れてしまう。
  3. その結果、Tx1はもはや有効ではなくなり、「stale(古い・無効になった)トランザクション」になる。

つまり、他のトランザクションが先に実行されることで、後から送られたトランザクションが実行不可能になる**可能性があるのです。

通常のEthereumのような「同期実行」モデルでは、これはMempool(未実行トランザクションの待機場所)レベルでの問題にとどまります。 ノードは「どのトランザクションが先に実行されるか」を見て、有効な順序に並び替えることができます。

しかしAvalancheのような「非同期実行(SAE)」モデルでは、ブロック生成時点ではトランザクションを静的に検証して承認するため、こうした「あとから無効になる可能性のあるトランザクション」が含まれると、実行時点で失敗(invalid)になるリスクが生じます。 これが「ノードはトランザクションの静的な有効性を判断できなくなる」という意味です。

Ethereumのような同期実行モデルでは、これはMempoolレベルでの問題に留まります。 つまり、トランザクションの伝播や順序に悪影響を与える程度です。

しかし、Avalancheのように非同期実行モデル(SAE)を採用している場合、 この問題はさらに深刻化します。 なぜなら、ブロック承認時点で静的に安全と判定されたトランザクションが、実際の実行時に無効化される可能性があるためです。

このようなトランザクションは、ブロックには含まれても実行時にエラーとなり、 DoS(サービス妨害)攻撃となる可能性があります。

そのため、AvalancheではEIP7702をそのまま導入するのではなく、 SAEの不変条件を維持できるように修正を加える必要があります。

仕様

ACP209では、EIP7702で定義された「set code transaction(コード設定トランザクション)」を、AvalancheのEVM実装にも導入します。 基本的な動作はEIP7702と同一ですが、AvalancheではACP194(Streaming Asynchronous Execution: SAE)との整合性を保つ必要があるため、トランザクションの検証ルールと実行ルールに2つの修正が加えられます。

これにより、ブロックに含まれる時点でトランザクションの有効性が保証されるようになります。

主な修正点

修正項目 概要
ReservedBalance(予約残高)の導入 DelegatedAccount(コードを設定したEOA)に対し、トランザクション手数料や送付額を常に支払えるよう「予約残高」を維持させる。ReservedBalanceManagerという新しいプリコンパイルで管理。
Nonceの検証と実行の分離 ブロック検証時の nonce チェックと、実行時の nonce 更新を分離。ブロックに含めた後に実行順序によって nonce がズレても、トランザクションが無効化されないようにする。

予約残高

AvalancheではSAEを採用しているため、ブロック承認時点で「このトランザクションは確実に実行できる」と静的に判断できる必要があります。 そのため、トランザクションの最大手数料と送付額を必ず支払えることを保証する仕組みとして、「ReservedBalance(予約残高)」が導入されます。

この仕組みは、特にEIP7702でコードを設定したEOA(DelegatedAccount)に対して適用されます。 DelegatedAccountは、自身のアカウントにコードを設定することで、スマートコントラクトのように振る舞えるようになります。 しかし、これにより他のアカウントからの操作で残高が変動し、後続のトランザクションが支払い不能になるリスクが生じます。 これを防ぐために、あらかじめ「予約残高」を確保しておき、どんな順序で実行されても支払いが保証されるようにします。

ReservedBalanceManagerプリコンパイル

予約残高を管理するために、以下の新しいステートフルなプリコンパイルコントラクトが追加されます。

項目 値
コントラクト名 ReservedBalanceManager
アドレス 0x0200000000000000000000000000000000000006
管理対象 各アカウントの予約残高(ReservedBalance)

以下のようなインターフェースが定義されます。

solidity
interface IReservedBalanceManager {
    /// @dev アカウントの予約残高が変更されたときに発行されるイベント
    event ReservedBalanceUpdated(address indexed account, uint256 newBalance);

    /// @dev 指定したアカウントに対して、送信されたネイティブトークンを予約残高として預け入れる
    function depositReservedBalance(address account) external payable;

    /// @dev 指定したアカウントの現在の予約残高を取得
    function getReservedBalance(address account) external view returns (uint256 balance);
}

ReservedBalanceManagerは、内部でアカウントごとの予約残高マッピングを保持します。 このプリコンパイルは残高の増加のみを許可し、減少は一切行いません。 減少はEVMによってトランザクション実行時にのみ行われます。 つまり、予約残高は「手動で減らせない安全なロック領域」として機能します。

トランザクション検証時のルール

ブロック検証時(つまりトランザクションをブロックに含める前)には、以下のルールで残高の有効性が確認されます。

条件 検証ルール
アカウントがEIP7702を使っていない場合 通常のEOA。予約残高は不要。アカウントの通常残高から、ACP194で定義されたpending execution queue(未実行トランザクションキュー)を考慮して、手数料・送付額を支払えることを確認。
アカウントがEIP7702を使っている場合(コードを設定済みまたは設定中) DelegatedAccountとして扱う。現在のSettledStateRootにおける予約残高が、未実行トランザクション全体の最大手数料+送付額の合計を十分にカバーしている必要がある。

ここで「SettledStateRoot」とは、ACP194で定義される確定済みステートのことです。 これは、ブロックチェーンが合意した「静的解析可能な最新状態」を意味します。

トランザクション実行時のルール

トランザクション実行中は、以下のように残高が扱われます。

処理内容 ルール
手数料・送付額の差し引き まず通常残高(regular balance)から差し引き、足りない場合のみ予約残高(reserved balance)から補う。
コード実行中の残高参照 コード実行中は通常残高のみ利用可能。予約残高は直接参照・操作できず、唯一の操作は depositReservedBalance 関数による増加のみ。
ガスリファンド時 実行後にガスが返還される場合、まず予約残高に返還。上限は実行前の予約残高。余った分がある場合は通常残高に加算。

Nonceの扱い

EIP7702の導入後、EOAがコードを持てるようになるとトランザクション実行中に nonce が変化するケースが発生します。 これにより、すでにブロックに含まれた他のトランザクションの nonce がずれてしまい、実行時に無効化されるリスクが生じます。

この問題を解決するため、Avalancheではブロック検証時の nonce チェックと実行時のnonce更新を分離します。

ブロック検証時のルール

ブロックにトランザクションを含める時、nonce は以下のように検証されます。

  • nonce は SettledStateRoot(ACP194で定義)を基準に判定されます。
  • さらに、同じアカウントからすでに PendingExecutionQueue(未実行トランザクションキュー)に入っているトランザクション数を加味します。

つまり、検証時に求められる nonce は以下です。

settled state rootのnonce + 既にキューに入っているトランザクション数

この方法により、「まだ実行されていないがブロックに含まれている」トランザクションを正しく数え上げることができます。

実行時のルール

実際の実行時には、以下のルールで nonce が処理されます。

  • 実行時に使われる nonce は、そのアカウントが最後に使用した nonce + 1 です。
  • これは、アカウントが発行した全トランザクションや、作成した全コントラクトも含めて一貫性を持たせるためです。

したがって、トランザクションに署名時に指定した nonce と、実際に実行で使用される nonce が異なることがあります。

この設計の効果と影響

この「検証と実行の分離」により、実行順序によってトランザクションが無効化されることを防止できます。 つまり、一度ブロックに含めたトランザクションは、他のトランザクションの実行順によって取り消されることがありません。

また、nonce の一意性は保持されているため、リプレイ攻撃防止(同じ nonce の再利用防止)も従来通り機能します。

ただし、1つ注意点があります。 従来、コントラクト作成トランザクションの結果アドレスは「送信者アドレスnonce」から決定できましたが、実行時の nonce がトランザクションの nonce と異なる可能性があるため、コントラクトアドレスが事前に確定できなくなる場合があります。

互換性

破壊される不変条件

EIP7702では、従来のEVMが前提としていたいくつかの不変条件が崩れます。 これらはAvalancheでも同様に影響を及ぼします。

破壊される項目 説明
tx.origin == msg.sender の条件 これまでは、トランザクションの最上位フレーム(直接実行)でのみ、tx.originとmsg.senderが同一でした。EIP7702によりアカウントがdelegated(コードを設定済み)になると、1つのトランザクション内で複数のコールを発行できるため、この関係が常に成り立たなくなります。
EOAのNonce増加ルール 本来、EOAの nonce(トランザクションカウンタ)は実行開始後に増加しないことが前提でした。しかし、delegatedアカウントが create 操作(コントラクト作成)を行うと、実行中に nonce が増加する可能性があります。
コントラクトアドレスの決定方法 これまでは、EOAによるコントラクト作成トランザクション(toが空のもの)の結果アドレスは、送信者アドレス+トランザクション nonce から算出されていました。しかし、実行前に nonce が増加する可能性があるため、実際に使用される nonce が署名時と異なる場合、結果アドレスが変化します。この影響は、delegatedアカウントかつコード内でcreateを行うケースに限られます。

これらの変更により、既存のスマートコントラクトやウォレットが依存していた挙動が変わる可能性があります。

ReservedBalance(予約残高)の要件

EIP7702をAvalancheに導入するにあたって、delegatedアカウントは常に十分な予約残高を保持している必要があります。 これはSAE(非同期実行)の安全性を担保するためです。

以下のルールが適用されます。

条件 内容
set codeトランザクションが受理された後 EOAは未実行キューに含まれる全トランザクションの「最大手数料+送付額」合計をカバーできる予約残高を持っていなければならない。
予約残高がゼロの場合 delegatedアカウントは新たなトランザクションを送信できない。他アカウントからReservedBalanceManagerプリコンパイルを通じて予約残高を提供してもらう必要がある。
自己資金で初期予約残高を確保する場合 set codeトランザクションを送信する前に、ReservedBalanceManagerのdepositReservedBalance関数を呼び出して、あらかじめ残高を予約する必要がある。
アカウントの全残高(regular+reserved)を移転したい場合 まず通常残高をすべて予約残高に変換してからTransferする必要がある。

この仕組みにより、トランザクションの順序や他アカウントの操作に影響されず、常に手数料と送金を保証できるようになります。

RPCの変更点

ウォレットやクライアントが予約残高を認識できるよう、以下のRPC変更が推奨されます。

RPCメソッド 内容
eth_getBalance 通常残高と予約残高の合計を返すように更新する。これにより、ウォレットUIでの残高表示が直感的になる。
eth_getReservedBalance 新規メソッド。指定アカウントの予約残高を個別に照会できるようにする。

これにより、既存ウォレットが即座に対応できない場合でも、段階的な互換対応が可能になります。

セキュリティ

EIP7702で定義されたセキュリティ上の考慮事項は、Avalanche実装にも原則そのまま適用されます。

ただし、以下の2項目は今回のAvalanche版では適用対象外です。

除外項目 理由
Sponsored Transaction Relayers(代理支払い中継者) Avalancheでは予約残高機構によって手数料支払いが保証されるため、外部の中継者モデルは不要。
Transaction Propagation(トランザクション伝播) Nonceの検証と実行を分離する仕組みにより、EIP7702で指摘されていた「静的検証の難しさ」は解消済み。

追加の考慮点

Avalanche版では、予約残高を利用するため、Transfer時に状態更新コストが増加する可能性があります。 このため、従来の単純送金に必要な 21,000 gas が十分かどうかを再検討する必要があります。

もしガスが不足する場合、送金手数料を引き上げる選択肢もありますが、以下の互換性問題を引き起こすため慎重な設計が求められます。

  • 既存のスマートコントラクトのgas limit固定設計
  • オフチェーンサービス(ウォレット・API)のガス見積りロジック

未解決の課題

質問 概要
ReservedBalanceManagerの導入コストは妥当か? UX改善のための新しいトランザクション型(set code transaction)に対し、ReservedBalanceManagerの設計・実装・UX複雑性が見合うかを検討する必要がある。多くのUX改善は、ERC2771のようなントラクトレイヤーの仕組みでも実現可能だが、全てのコントラクトが対応しているわけではない。
ReservedBalanceManagerの導入は、delegatedコントラクトにネイティブトークン送金権限を与える価値があるか? 代替案として、「delegatedコントラクトはネイティブトークンを消費不可」にする設計もある。その場合、WAVAXなどのラップトークン(ERC20)を使えば同等の処理が可能。ただし、これも実装複雑性やEthereumとの互換性に影響するため慎重な判断が必要。

ERC2771については以下の記事を参考にしてください。

最後に

今回は「EIP7702をAvalancheに導入し、EOAがコードを持てるようにする一方で、非同期実行(SAE)でも安全性を保つために「予約残高」と「nonce分離」を組み合わせる仕組みを提案しているACP209」についてまとめてきました! いかがだったでしょうか?

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

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