Pectra後のEIP-7702:Ethereum アプリ開発者のための実践的プレイブック
2025年5月7日、EthereumのPectraアップグレード(Prague + Electra)がメインネットにリリースされました。開発者にとって最も目に見える変更の一つがEIP-7702で、これは外部所有アカウント(EOA)が資金を移行したりアドレスを変更したりすることなく、スマートコントラクトロジックを「マウント」できるようにします。ウォレット、dapp、リレイヤーを構築している場合、これはスマートアカウントUXへのより簡単な道筋を提供します。
以下は実装重視の簡潔なガイドです:実際に出荷されたもの、7702がどのように動作するか、純粋なERC-4337よりもいつ選択するべきか、そして今日適用できるコピー&ペースト可能なスカフォールド。
実際に出荷されたもの
- EIP-7702はPectraの最終範囲に含まれています。 Pectraハードフォークのメタ-EIPは、含まれる変更の中に7702を正式にリストしています。
- アクティベーション詳細: Pectraは2025年5月7日のエポック364032でメインネット上でアクティベートされ、すべての主要テストネットでの成功したアクティベーションに続きました。
- ツールチ ェーン注記: Solidity v0.8.30は、Pectra互換性のためにデフォルトEVMターゲットをpragueに更新しました。コンパイラとCIパイプラインをアップグレードする必要があります、特に特定のバージョンを固定している場合。
EIP-7702—どのように動作するか(詳細)
EIP-7702は新しいトランザクションタイプと、EOAがその実行ロジックをスマートコントラクトに委任するメカニズムを導入します。
- 新しいトランザクションタイプ(0x04): タイプ4トランザクションには
authorization_list
という新しいフィールドが含まれます。このリストには一つ以上の認証タプル—(chain_id, address, nonce, y_parity, r, s)
—が含まれ、それぞれEOAの秘密鍵で署名されています。このトランザクションが処理されると、プロトコルはEOAのコードフィールドに委任インジケーターを書き込みます:0xef0100 || address
。それ以降、EOAへのすべての呼び出しは指定されたaddress
(実装)にプロキシされますが、EOAのストレージとバランスコンテキスト内で実行されます。この委任は明示的に変更されるまで有効です。 - チェーンスコープ: 認証は
chain_id
を提供することでチェーン固有にできるか、chain_id
が0
に設定 されている場合はすべてのチェーンに適用できます。これにより、ユーザーが各チェーンに対して新しい認証に署名することなく、同じ実装コントラクトを複数のネットワークにデプロイできます。 - 取り消し: EOAを元の非プログラマブル動作に戻すには、実装
address
がゼロアドレスに設定された別の7702トランザクションを送信するだけです。これにより委任インジケーターがクリアされます。 - セルフスポンサード vs. リレー: EOA自身がタイプ4トランザクションを送信することも、サードパーティのリレイヤーがEOAの代わりに送信することもできます。後者はガスレスユーザーエクスペリエンスを作成するために一般的です。nonceハンドリングは方法によって若干異なるため、この区別を正しく管理するライブラリを使用することが重要です。
セキュリティモデルの変化: 元のEOA秘密鍵がまだ存在するため、新しい7702トランザクションを送信して委任を変更することで、常にスマートコントラクトルール(ソーシャルリカバリーや支出制限など)を上書きできます。これは根本的な変化です。
tx.origin
を使用してEOAからの呼び出しであることを確認するコントラクトは再監査が必要です、7702がこれらの前提を破る可能性があるためです。フローを適切に監査してください。
7702かERC-4337か?(そして組み合わせるべき時)
EIP-7702とERC-4337の両方がアカウント抽象化を可能にしますが、異なるニーズに対応します。
- EIP-7702を選ぶべき時…
- ユーザーに資金を移行させたりアドレスを変更させることなく、既存のEOAに即座のスマートアカウントUXを提供したい場合。
- 新機能で段階的にアップグレードできるチェーン間での一貫したアドレスが必要な場合。
- アカウント抽象化への移行を段階的に進めたい場合、シンプルな機能から始めて時間をかけて複雑さを追加する。
- 純粋なERC-4337を選ぶべき時…
- 製品が完全なプログラマビリティと複雑なポリシーエンジン(マルチシグ、高度な回復など)を最初から必要とする場合。
- 既存のEOAを持たない新しいユーザー向けに構築している場合、新しいスマートアカウントアドレスと関連する設定が受け入れられる。
- 両方を組み合わせる: 最も強力なパターンは両方を使用することです。EOAは7702トランザクションを使用してERC-4337ウォレット実装をそのロジックとして指定できます。これによりEOAは4337アカウントのように動作し、既存の4337インフラストラクチャによってバンドル、ペイマスターによるスポンサー、処理が可能になります—すべてユーザーが新しいアドレスを必要とすることなく。これはEIPの著者によって明示的に推奨される前方互換パスです。
適用可能な最小限の7702スカフォールド
実装コントラクトとそれを有効化するクライアントサイドコードの実用的な例を以下に示します。
1. 小さく監査可能な実装コントラクト
このコントラクトコードは指定されるとEOAのコンテキスト内で実行されます。小さく監査可能に保ち、アップグレードメカニズムの追加を検討してください。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @notice EIP-7702で指定された時にEOAコンテキストから呼び出しを実行します。
contract DelegatedAccount {
// 他のコントラクトとの衝突を避けるためのユニークなストレージスロット。
bytes32 private constant INIT_SLOT =
0x3fb93b3d3dcd1d1f4b4a1a8db6f4c5d55a1b7f9ac01dfe8e53b1b0f35f0c1a01;
event Initialized(address indexed account);
event Executed(address indexed to, uint256 value, bytes data, bytes result);
modifier onlyEOA() {
// オプション:特定の関数を呼び出せるユーザーを制限するチェックを追加。
_;
}
function initialize() external payable onlyEOA {
// EOAのストレージにシンプルなワンタイム初期化フラグを設定。
bytes32 slot = INIT_SLOT;
assembly {
if iszero(iszero(sload(slot))) { revert(0, 0) } // すでに初期化済みの場合はrevert
sstore(slot, 1)
}
emit Initialized(address(this));
}
function execute(address to, uint256 value, bytes calldata data)
external
payable
onlyEOA
returns (bytes memory result)
{
(bool ok, bytes memory ret) = to.call{value: value}(data);
require(ok, "CALL_FAILED");
emit Executed(to, value, data, ret);
return ret;
}
function executeBatch(address[] calldata to, uint256[] calldata value, bytes[] calldata data)
external
payable
onlyEOA
{
uint256 n = to.length;
require(n == value.length && n == data.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < n; i++) {
(bool ok, ) = to[i].call{value: value[i]}(data[i]);
require(ok, "CALL_FAILED");
}
}
}
2. viemでEOAにコントラクトを指定(タイプ4 tx)
viem
のような現代的なクライアントは、認証に署名してタイプ4トランザクションを送信する組み込みヘルパーを持っています。この例では、relayer
アカウントがeoa
をアップグレードするためのガスを支払います。
import { createWalletClient, http, encodeFunctionData } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { abi, implementationAddress } from "./DelegatedAccountABI";
// 1. リレイヤー(ガススポンサー)とアップグレードされるEOAを定義
const relayer = privateKeyToAccount(process.env.RELAYER_PK as `0x${string}`);
const eoa = privateKeyToAccount(process.env.EOA_PK as `0x${string}`);
const client = createWalletClient({
account: relayer,
chain: sepolia,
transport: http(),
});
// 2. EOAが実装コントラクトを指す認証に署名
const authorization = await client.signAuthorization({
account: eoa,
contractAddress: implementationAddress,
// EOA自身がこれを送信する場合は追加:executor: 'self'
});
// 3. リレイヤーがタイプ4トランザクションを送信してEOAのコードを設定し、initialize()を呼び出す
const hash = await client.sendTransaction({
to: eoa.address, // 宛先はEOA自身
authorizationList: [authorization], // 新しいEIP-7702フィールド
data: encodeFunctionData({ abi, functionName: "initialize" }),
});
// 4. これで、EOAは追加の認証なしに新しいロジックで制御できます
// 例えば、トランザクションを実行するには:
// await client.sendTransaction({
// to: eoa.address,
// data: encodeFunctionData({ abi, functionName: 'execute', args: [...] })
// });
3. 委任を取り消す(プレーンEOAに戻す)
アップグレードを元に戻 すには、EOAにゼロアドレスを実装として指定する認証に署名させ、別のタイプ4トランザクションを送信します。その後、eth_getCode(eoa.address)
への呼び出しは空のバイトを返すはずです。
本番環境で機能する統合パターン
- 既存ユーザーのためのインプレースアップグレード: dappで、ユーザーがPectra互換ネットワークにいるかどうかを検出します。もしそうなら、ワンタイム認証署名をトリガーするオプションの「アカウントアップグレード」ボタンを表示します。古いウォレットを持つユーザーのためのフォールバック経路(クラシックな
approve
+swap
など)を維持します。 - ガスレスオンボーディング: 初期タイプ4トランザクションをスポンサーするためにリレイヤー(バックエンドまたはサービス)を使用します。継続的なガスレストランザクションには、既存のペイマスターと公開メンプールを活用するためにERC-4337バンドラーを通してユーザー操作をルーティングします。
- クロスチェーン展開: すべてのチェーンで同じ実装コントラクトを指定するために
chain_id = 0
認証を使用します。その後、アプリケーションロジック内でチェーンごとに機能を有効または無効にできます。 - 監視可能性: バックエンドはタイプ4トランザクションをインデックス化し、どのEOAがアップグレードされたかを追跡するために
authorization_list
を解析する必要があります。トランザクション後、eth_getCode
を呼び出してEOAのコードが委任インジケーター(0xef0100 || implementationAddress
)と一致することを確認して変更を検証します。
脅威モデルとGotcha(これをスキップしないでください)
- 委任は永続的: EOAの実装コントラクトへの変更を、標準的なスマートコントラクトアップグレードと同じ重要度で扱ってください。これには監査、明確なユーザーコミュニケーション、理想的にはオプトインフローが必要です。新しいロジックをユーザーに静かにプッシュしないでください。
tx.origin
地雷:msg.sender == tx.origin
を使用してEOAから直接呼び出されたことを確認していたロジックは今では潜在的に脆弱です。このパターンはEIP-712署名や明示的許可リストなど、より堅牢なチェックで置き換える必要があります。- Nonce計算: EOAが自身の7702トランザクションをスポンサーする場合(
executor: 'self'
)、認証nonceとトランザクションnonceが特定の方法で相互作用します。リプレイ問題を避けるために、これを正しく処理するライブラリを常に使用してください。 - ウォレットUXの責任: EIP-7702仕様は、dappがユーザーに任意の指定への署名を求めるべきではないと警告しています。提案された実装を検証し、安全であることを確認するのはウォレットの責任です。ウォレット仲介セキュリティのこの原則に従うようにUXを設計してください。
7702が明確な勝利となる場合
- DEXフロー: マルチステップの
approve
とswap
はexecuteBatch
関数を使用してシングルクリックに結合できます。 - ゲームとセッション: ユーザーが新しいウォレットを作成・資金提供することなく、限られた時間またはスコープでセッションキーのような特権を付与します。
- 企業とフィンテック: スポンサー付きトランザクションを有効にし、会計とアイデンティティのために各チェーンで同じ企業アドレスを維持しながらカスタム支出ポリシーを適用します。
- L2ブリッジとインテント: 異なるネットワーク間で一貫したEOAアイデンティティを持つ、よりスムーズなメタトランザクションフローを作成します。
これらのユースケースは、ERC-4337によって約束された同じコアベネフィットを表していますが、今では単一の認証だけですべての既存EOAで利用可能です。
出荷チェックリスト
プロトコル
- ノード、SDK、インフラプロバイダーがタイプ4トランザクションとPectraの「prague」EVMをサポートすることを確認。
- 新しいトランザクションで
authorization_list
フィールドを解析するようにインデクサーと分析ツールを更新。
コントラクト
- 必須機能(バッチング、取り消しなど)を持つ最小限の監査済み実装コントラクトを開発。
- メインネットにデプロイする前にテストネットで取り消しと再指定フローを徹底的にテスト。
クライアント
- クライアントサイドライブラリ(
viem
、ethers
など)をアップグレードし、signAuthorization
とsendTransaction
機能をテスト。 - セルフスポンサーとリレイの両方のトランザクションパスがnonceとリプレイを正しく処理することを確認。
セキュリティ
- コントラクトから
tx.origin
に基づくすべての前提を削除し、より安全な代替手段に置き換える。 - ユーザーアドレスでの予期しないコード変更を検出し、疑わしいアクティビティについてアラートするデプロイ後監視を実装。
結論: EIP-7702は、すでに使用されている数百万のEOAに対するスマートアカウントUXへの低摩擦オンランプを提供します。小さく監査された実装から始め、ガスレスセットアップにリレイパスを使用し、取り消しを明確で簡単にすれば、完全なアカウント抽象化の90%の利益を提供できます—アドレス変更と資産移行の痛みなしに。