本稿は仕組みの解説であり、事象の報道ではない。仕様の根拠は確定版の EIP-7702。
TL;DR
7702 の署名は、誰でも提出できるコード制御の認可である。トランザクションではない。
- 署名者が責任を負うのは 3 フィールドだけ:
chain_id、委任先address、アカウントnonce。署名対象はkeccak256(0x05 ‖ rlp([chain_id, address, nonce]))。 - EIP-712 ではない。
domainSeparatorもprimaryTypeもフィールド名もない —— ファームウェアが0x05の解析を専用に実装していなければ、利用者が見るのは 32 バイトのハッシュである。 - 有効期限がない。 署名済みの認可を失効させられるのは
nonceだけである。 - だから nonce スプレーの真の目的は命中率の向上ではなく、「取り消し」を無意味にすることである —— nonce を 1 つ焼いても無効化できるのは 1 件だけで、残りは有効なままである。
- 外側のトランザクションが失敗しても委任はロールバックしない。 「失敗」に見える Type-4 トランザクションでも、コードはすでに書き込まれている可能性がある。
- ETH を持つアカウントなら誰でも署名者の代わりに提出し、gas を払える。署名と取り込みは別の 2 つの事柄である。
一、署名対象:3 フィールドと 1 つのマジックバイト
認可タプルは 6 つの値から成るが、実際に署名されるのは最初の 3 つだけである:
| フィールド | 長さ | 意味 |
|---|---|---|
chain_id |
32 バイト | 認可が有効なチェーン;0 は全チェーン共通 |
address |
20 バイト | 委任先コントラクト;0x0 は委任の消去 |
nonce |
8 バイト | 署名時点のアカウント nonce と一致しなければならない |
y_parity |
1 バイト | secp256k1 の復元識別子(0/1) |
r |
32 バイト | ECDSA 署名 |
s |
32 バイト | ECDSA 署名。secp256k1n/2 以下でなければならない(EIP-2 の low-s 要件) |
署名ハッシュの定義は極めて直接的である:
msg = keccak256(0x05 ‖ rlp([chain_id, address, nonce]))0x05 は 7702 専用のマジックバイトで、通常のトランザクション署名(0x00/0x01/0x02/0x03)と区別するためのものである。
これは EIP-712 の domainSeparator 構造ではなく、primaryType もフィールド名もウォレットが描画できる意味論も持たない。 結果として、ハードウェアウォレットと署名インターフェースが受け取るのは 32 バイトのハッシュであり、ファームウェアが 0x05 の解析を専用に実装していない限り、利用者は自分が読めない認可をブラインド署名している。
これが従来のトランザクション署名との最も本質的な違いである。 通常のトランザクション署名は少なくとも to、value、data、gas を束縛する。7702 の認可が束縛するのは「アカウントのコードを誰に向けるか」だけである。
委任先が何をするかは約束しないし、コードが後で変わらないことも約束しない —— 委任先がアップグレード可能なプロキシなら、署名時点では良性の実装でも、アップグレード後に sweep コントラクトになりうる。
覚えておくべき境界が 2 つある。authority は ecrecover で復元できなければならないので、コントラクトアカウントは認可者になれない。そして認可署名そのものはブロードキャストできず、Type-4 トランザクションに載せる必要があるが、載せる側は誰でもよい。
二、オンチェーン処理:2 層の署名と 9 つのステップ
1 本の Type-4 トランザクションには、実は2 層の署名が含まれている:
- 送信者の署名 ——
authorization_listを含むトランザクション全体の RLP ペイロードに署名し、誰が gas を払い、誰の nonce が消費されるかを決める; - 認可者の署名 —— 各タプルに個別に署名し、どのアカウントのコードが書き換えられるかを決める。
この 2 つは完全に無関係でありうる。 これこそスポンサーシップの設計意図である:dApp が利用者の gas を払い、利用者は認可に署名するだけでよい。Binance の事例で operator が Binance 14 の代わりに認可を提出できたのも、同じ理由である。
処理順序は、まず送信者の nonce をインクリメントし、次に認可リストを処理し、最後に calldata を実行する。各タプルについて 9 ステップが順に走る:
1. chain_id == 0 または == 現在のチェーン ID
2. nonce < 2^64 - 1
3. authority = ecrecover(msg, y_parity, r, s)、かつ s <= secp256k1n/2
4. authority を accessed_addresses に追加(ウォーム化)
5. authority のコードが空、またはすでに 0xef0100 で始まる委任である
6. authority の現在の nonce == タプルの nonce
7. authority が空でなければ、グローバル返金カウンタに 12,500 gas を加算
8. code = 0xef0100 ‖ address を書き込む;address が 0 ならコードを消去
9. authority.nonce += 1どのステップが失敗しても、そのタプルをスキップするだけでトランザクション全体はリバートせず、次のタプルの処理を続ける。同じ authority に有効なタプルが複数ある場合は、最後の有効なものが採用される。
gas 構造があの 1,258,562 を説明する
仕様は 2 つの定数を定義している:PER_EMPTY_ACCOUNT_COST = 25,000、PER_AUTH_BASE_COST = 12,500。
件数で前払いし、有効性で返金する:内在コストに 25,000 × 認可リストの長さが含まれ、authority が空でないタプルに対してのみグローバル返金カウンタに 12,500 が加算される。
したがって Binance の認可トランザクションの請求額は正確に復元できる:
50 × 25,000 = 1,250,000 50 件のタプル分を前払い
− 12,500 唯一の有効タプルが既存アカウントに命中
+ 21,000 トランザクションの内在 gas
+ 62 calldata など
= 1,258,5621 単位も違わない。 この計算はもう一つのことも明らかにする:49 件のおとりは無料ではなかった。1 件ごとにきっちり 25,000 gas を燃やし、送信者は本物も偽物も区別なく支払った。
三、nonce:唯一の「有効期間」
7702 に有効期限はない。署名済みの認可を失効させられるのは nonce だけである。 ここを掴まないと、攻防の構図そのものが読めない。
第一に、認可の nonce と通常のトランザクションの nonce は同一のカウンタである。 認可に署名してオンチェーンに載せるとアカウントの nonce が 1 上がり、同じ nonce の保留中の通常トランザクションは恒久的に無効になる。逆に、アカウントが先に通常トランザクションを送れば、以前署名した同じ nonce の認可も無効になる。
第二に、複数のタプルは順に検証され、nonce は処理しながら上がっていく。 Binance の事例のウィンドウ(18,914,775–18,914,824、実際の nonce は 809)を例にとると:
| 並び順 | 結果 | nonce の動き |
|---|---|---|
| 昇順 775→824 | 775–808 は低すぎてスキップ、809 が命中し、その後 810…824 が順次有効化 | 16 回連続で進む(命中分 + その後の 15 件) |
| 降順 824→775 | 824–810 は高すぎてスキップ、809 が命中、808–775 は低すぎてスキップ | 1 回だけ進む |
攻撃者が降順を選んだのは、まさに 1 件だけを有効にするためである —— ホットウォレット自身のトランザクションキューを乱さないために。
第三に、nonce は防御手段でもあるが、「スプレー」で突破される。 悪性の認可が mempool にあると疑うなら、アカウントは直ちにゼロ値の自己送金を出して現在の nonce を消費し、その認可を無効化できる。
だが攻撃者が異なる 50 個の nonce で 50 件の認可を事前署名していれば、nonce を 1 つ焼いて無効化できるのは 1 件だけで、残り 49 件は有効なままである。
これが nonce スプレーの真の目的である:命中率の改善ではなく、「取り消し」を無意味にすることだ。
第四に、chain_id = 0 はクロスチェーン・リプレイのスイッチである。 0 に設定すると、各チェーンで nonce がたまたま一致する限り、同じ署名がすべての EVM チェーンで有効になる。Binance の事例の 50 件はすべて chain_id = 1 だった。したがってあの認可は BNB Chain や Polygon などでリプレイできず、多チェーンの流出には別の説明が必要である。
四、実行コンテキスト:署名の後、アカウントは何になるのか
委任が有効になると、アカウントの code フィールドは 23 バイトの 0xef0100 ‖ address になる。それ以降:
- そのアカウントへの
CALL、DELEGATECALL、STATICCALLはいずれも委任先のコードをロードするが、EOA 自身のコンテキストで実行される:address(this)は EOA、msg.senderは呼び出し元、ストレージの読み書きは EOA のストレージに落ちる; - 下流のトークンコントラクトからすれば
msg.senderはそのウォレット自身なので、ERC-20 の approve は一切不要である; - コード読み取りが非対称になる:
EXTCODESIZEは 23(委任インジケータの長さ)を返し、委任実行中のCODESIZEは委任先のコード長を返す。仕様はEXTCODEHASHのような外部からの取得操作が委任を自動的に追わず、インジケータ自体に作用すると述べている —— つまり従来の「EOA 判定」(EXTCODESIZE == 0)は壊れるが、委任先アドレスはチェーンから直接読み出せる; - 委任チェーンは最初の 1 ホップしか追わない:委任先それ自体が委任アカウントでも、クライアントは再帰しない;
- 外側のトランザクションがリバートしても委任はロールバックしない。 仕様の文言は、処理済みの委任インジケータはロールバックされないである。「失敗」に見える Type-4 トランザクションでも、コードはすでに書き込まれている可能性がある —— シミュレーションと監視の両方にとっての罠である。
委任を消す道は 1 つだけである:現在の nonce で address = 0x0 の認可をもう 1 本署名し、コードを空に戻す。管理者もタイムロックもオンチェーンのスイッチもない。
消すまで、新たに入ってくる資産はすべて自動的に委任先のロジックに晒される。
五、Binance の事例に当てはめる:署名フローはどこで失敗したか
上記の仕組みを 10/29 の事象に当てはめると、流れは段階的に復元できる:
- 攻撃者が 50 件のタプルを構成。
chain_id = 1、address = 0x63e842…、nonce は 18,914,824 から 18,914,775 へ降順; - 署名側がこの 50 個のハッシュに 1 つずつ署名した —— ECDSA 50 回であり、通常の手作業ではほぼ不可能で、自動署名サービスかバッチ事前署名を意味するのが普通である;
- operator が 50 件の署名を 1 本の Type-4 トランザクションに詰め、自ら送信者として 0.006545 ETH(約 14.5 ドル)を支払う;
- 取り込み時のアカウント nonce は 18,914,809 で、命中したのはこの 1 件だけ。コードが書き込まれ、nonce は 18,914,810 になる;
- 3 分 24 秒後、operator が通常のトランザクションで
sweep(address,address[])を呼び出し、コードがホットウォレットのコンテキストで実行され、167,464 ETH、16.15 億ドルの USDT、64 種のトークンが運び出される; - その後 3 時間半のあいだにさらに 12 回呼び出され、うち複数は 23,100 gas の空ポーリングだった。
失敗箇所はオンチェーンではなく、ステップ 2 にある:署名側が「アカウントのコードを書き換える」という要求を、通常のメッセージ署名として処理した。
署名ポリシーが 0x05 ペイロードを解析できていれば、3 つのことが見えたはずである —— 委任先が未検証のコントラクトであること、nonce のウィンドウが 50 値に及ぶこと、chain_id がメインネットであること。どれか 1 つでも遮断の根拠になる。だが実際に見えていたのは、おそらく 16 進数のハッシュだけだった。
六、結論
EIP-7702 の署名フローは 1 文に圧縮できる:秘密鍵で「チェーン ID + 委任先アドレス + nonce」のハッシュに署名し、誰かがそれを Type-4 トランザクションに包めば、アカウントのコードは恒久的に書き換わる。
有効期限もなく、取り消しボタンもなく、実行のロールバックもない。唯一の失効機構は nonce の消費であり —— そしてそれは nonce スプレーによって狙い撃ちで回避されている。
したがって安全な署名フローは、オンチェーン監視ではなく署名の前に重心を置かなければならない:
0x05ペイロードを解析し、委任先アドレスとchain_idを表示する- 委任先をコードハッシュで許可リスト化し、未検証またはアップグレード可能なコントラクトを拒否する
- 「コードの委任」と「資産の移転」を 2 組の鍵と 2 系統の承認に分離する
- ホットウォレットでは Type-4 を直接禁止する
- 署名後、mempool で誰かが代わりに提出していないかを監視する
結論。 7702 は「署名」を、個別の操作への認可から「アカウント制御権の委託」へと格上げした。
ウォレットや取引所がそれを本当に理解しているかは、1 点だけで判断できる:その署名インターフェースは、利用者が確認を押す前に 0xef0100‖address の中のアドレスを人間の言葉で表示できるか。できないなら、どれだけ多重のプロセスを重ねてもブラインド署名にすぎない。
参考資料
- EIP-7702 仕様
- EIP-2:low-s 要件
- 認可トランザクション(Type-4)
- 資産を空にしたトランザクション
- Zealynx:EIP-7702 ウォレットセキュリティの技術的分解
- Halborn:EIP-7702 のセキュリティ考慮事項
あわせて読む:EIP-7702 攻撃チェーンの解剖:18 時間の準備、1 本の署名、1 体のボット、Binance ホットウォレット陥落。本稿は仕組みの解説で、仕様の根拠は確定版の EIP-7702。投資助言ではない。DYOR。