# [EIP8246] SELFDESTRUCTでETHが消えないようにする仕組みを理解しよう！

> EIP8246は、SELFDESTRUCT によってETHが総供給から消える処理を取り除く提案です。SELFDESTRUCT の対象としてマークされたアカウントは、トランザクション終了時にコード・ストレージ・nonce を消されます。

- URL: https://cardene.dev/articles/63aabc5cfbd7a6aaa5bf
- 公開日: 2026-06-03
- 更新日: 2026-06-03
- トピック: Blockchain
- タグ: eip, smartcontract, evm, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください！

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://twitter.com/cardene777

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

今回は「**SELFDESTRUCTでETHが消えないようにし、残高だけを保持するEIP8246**」についてまとめていきます。

**EIP8246**は、`SELFDESTRUCT` に残っているETHの `burn` 処理をなくす提案です。
ここでの `burn` は、ETHが総供給から消える処理を指します。
同じトランザクション内で作られたコントラクトが `SELFDESTRUCT` した時でも、残高は消さず、コード・ストレージ・`nonce` だけを終了処理で消します。

:::message
以下にまとめられている提案を解説しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-8246

## 概要

**EIP8246**は、`SELFDESTRUCT` によってETHが総供給から消える処理を取り除く提案です。
`SELFDESTRUCT` の対象としてマークされたアカウントは、トランザクション終了時にコード・ストレージ・`nonce` を消されます。
一方で、残高は保持されます。

この変更は、`SELFDESTRUCT` を完全に無効化する提案ではありません。
同じトランザクション内で作られたコントラクトに対して、終了処理でコードやストレージを消す効果は残します。
変わるのは、残高を消してETHを `burn` する処理です。

全体像は以下の図です。

![EIP8246の全体像](/images/qiita-a63d7488-8038-4304-9e95-efec3d954e68.webp)

左側は、`SELFDESTRUCT` の残り処理によってETHが消えるパターンです。
右側は、**EIP8246** 後の扱いです。
残高は保持され、実行できるコードと永続ストレージは残りません。

**EIP6780** によって、`SELFDESTRUCT` の多くの削除効果はすでに制限されています。
ただし、同じトランザクション内で作られたコントラクトが `SELFDESTRUCT` を実行した時だけ、ETHが `burn` される余地が残っていました。
**EIP8246** は、この残り処理を消します。

## 動機

`SELFDESTRUCT` の残り `burn` は、実際の利用がほとんどありません。
それでも、EVM実装、仕様、テストでは特殊な処理として扱う必要があります。
使用頻度が低い機能のために、クライアント実装やテストケースへ複雑さが残ることが課題です。

**EIP6780** の後でも、同じトランザクション内で作られたコントラクトではETHを `burn` できます。
具体的には、作成されたコントラクトが自分自身を受取先にして `SELFDESTRUCT` する時や、`SELFDESTRUCT` 後に `CALL` や別の `SELFDESTRUCT` で追加のETHを受け取る時です。

提案では、Ethereumメインネットのジェネシスからおよそ2500万ブロックまでを再実行した結果が示されています。
Cancun後にこの処理で `burn` された事例は2件で、トランザクション終了処理中に残高が `burn` された事例は0件でした。
一方、Cancun前の履歴では、自己 `burn` は合計54件でした。

この数字から、残っている `burn` 処理は、**EIP6780** で取り除かれた挙動よりさらに利用頻度が低いと分かります。
そのため、この処理を消しても影響範囲はかなり限定されます。

もう1つの理由は、ETHが総供給から消えるEVM上の最後の仕組みをなくすことです。
`SELFDESTRUCT` の残り `burn` を消すと、EVM命令だけでETHが総供給から離れる処理がなくなります。

## 仕様

**EIP8246** が変更するのは、`SELFDESTRUCT` のうち、同じトランザクション内で作られたコントラクトに関する残高処理と終了処理です。
その他の `SELFDESTRUCT` の挙動は変わりません。

仕様の要点は以下です。

| 対象 | 変更後の扱い |
|------|--------------|
| 同じトランザクション内で作られたコントラクトが、自分自身を受取先にして `SELFDESTRUCT` する時 | そのアカウントの残高は変わりません。 |
| トランザクション終了時に `SELFDESTRUCT` 対象としてマークされたアカウント | 削除ではなく、`nonce` を0へ戻し、残高を保持し、コードとストレージを消します。 |
| 上記以外の `SELFDESTRUCT` | **EIP6780** 後の挙動から変わりません。 |
| 終了処理後の残高が0のアカウント | **EIP161** によってステートから削除されます。 |

#### 実行時の残高

同じトランザクション内で作られたコントラクトが `SELFDESTRUCT` を実行し、受取先がそのコントラクト自身だった時、残高は変わりません。
これにより、`SELFDESTRUCT(address(this))` のような処理でETHが消えなくなります。

この変更は、受取先への送金処理を新しく追加するものではありません。
自分自身を受取先にした時に、残高が消える特殊処理をやめるだけです。

#### トランザクション終了時の扱い

トランザクション終了時に、`SELFDESTRUCT` 対象としてマークされたアカウントは以下の状態へ変更されます。

| 要素 | 終了処理後 |
|------|------------|
| `nonce` | `0` に戻ります。 |
| `balance` | 変わりません。 |
| `code` | 消えます。 |
| `storage` | すべて消えます。 |

同じトランザクション内で作られて `SELFDESTRUCT` したコントラクトの終了処理は以下です。

![EIP8246後のトランザクション終了処理](/images/qiita-7e6067d1-b9e2-4200-a5b4-ab262fa910ca.webp)

図の中心は、終了処理で「残高」と「実行可能な状態」を分ける点です。
残高は保持されますが、コードとストレージは消され、`nonce` は0に戻ります。
残高が0なら、**EIP161** のルールでステートから削除されます。

#### 残高のみのアカウント

終了処理後に残高が残ると、そのアドレスは残高のみを持つアカウントとしてステートに残ります。
コードはないため、そのアドレスのコントラクト処理は実行できません。
ストレージも消えているため、以前の状態を読み出すこともできません。

`nonce` を0へ戻す点も重要です。
残高だけが残っても、将来の別トランザクションで `CREATE2` による同一アドレスへの再作成を妨げないためです。

## 設計根拠

**EIP8246** は、`burn` を別の場所で補正するのではなく、`burn` が起きる元の処理をなくします。
これにより、EVM実装や仕様で「このパターンだけETHを消す」という特殊処理を持ち続ける必要がなくなります。

一方で、`SELFDESTRUCT` の終了処理をすべて消すわけではありません。
同じトランザクション内で作られたコントラクトについては、コード・ストレージ・`nonce` を消す効果を残します。
そのため、`SELFDESTRUCT` 後のアドレスは実行できるコントラクトとして残りません。

残高だけを残す設計は、変更範囲を最小限にするためのものです。
代替案として、`nonce`、コード、ストレージ、残高をすべて保持する設計もあります。
しかし、それは `SELFDESTRUCT` をより広く無効化する変更になり、今回の目的である `burn` 削除より大きな意味変更になります。

`nonce` を0へ戻す理由は、将来の `CREATE2` を妨げないためです。
残高のみのアカウントが残っても、コードがなく、`nonce` が0であれば、同じアドレスへ後続トランザクションでコントラクトを作り直せます。

## 互換性

**EIP8246** はコンセンサスルールを変えるため、ハードフォークが必要です。
同じトランザクション内で作られたコントラクトが `SELFDESTRUCT` を実行する時の最終ステートが変わります。

影響範囲は以下の図です。

![EIP8246の互換性と影響範囲](/images/qiita-1d6e9111-dcd3-46f1-9380-527eba902895.webp)

変更対象は、同じトランザクション内で作られたコントラクトの残り `burn` 処理です。
同じトランザクション内で作られていないコントラクトの `SELFDESTRUCT` は、**EIP6780** 後の扱いから変わりません。
また、残高0のアカウントが **EIP161** によって削除される点も残ります。

以前は、同じトランザクション内で作られたコントラクトが自分自身を受取先にして `SELFDESTRUCT` すると、ETHを `burn` できました。
また、`SELFDESTRUCT` 後にそのコントラクトへETHが送られ、トランザクション終了時に残高が消えるパターンもありました。
**EIP8246** 後は、どちらのパターンでもETHは `burn` されません。

以前は、この種のコントラクトはトランザクション終了時に削除されました。
変更後は、最終残高が0のコントラクトは削除され、最終残高が0ではないコントラクトは残高のみのアカウントとして残ります。
コード、ストレージ、`nonce` は残りません。

`CREATE2` で同じアドレスへ後続トランザクションから作り直す使い方は動作し続けます。
`nonce` が0に戻り、コードも消えるため、残高だけが残ったアカウントでも再作成を妨げません。

この提案は、`SELFDESTRUCT` 全体をさらに広く変更するのではなく、残っていた `burn` 処理だけを対象にします。

注意点として、Optimism系のL2では `SELFDESTRUCT` の `burn` 機能が通常運用で使われています。
メインネットと同じ変更を入れるには、別の移行計画が必要です。

## テストケース

提案では、`SELFDESTRUCT` の組み合わせが多いため、既存の `SELFDESTRUCT` テストを更新しつつ、変更対象のテストを追加する必要があるとしています。
特に、以下の軸を組み合わせて確認します。

| 軸 | 確認内容 |
|----|----------|
| 呼び出し階層 | トップレベルのトランザクションか、内部呼び出しか。 |
| 呼び出し種別 | `CALL`、`CREATE`、`CREATE2` のどれか。 |
| コード状態 | 初期化コード内で実行するか、デプロイ済みコードから実行するか。 |
| 実行結果 | `SELFDESTRUCT` が成功するか、ガス不足で失敗するか。 |
| `revert` | `SELFDESTRUCT` の効果が確定するか、内部呼び出しの `revert` で戻るか。 |
| 残高 | `SELFDESTRUCT` 実行時の残高が0か、0ではないか。 |

#### 命令レベル

命令レベルでは、同じトランザクションで作られたコントラクトがどの受取先へ `SELFDESTRUCT` するかを確認します。

| No | ケース |
|----|--------|
| 1 | 同じトランザクションで作られたコントラクトが、自分自身を受取先にして `SELFDESTRUCT` する。 |
| 2 | 自分自身を受取先にした `SELFDESTRUCT` の後に、もう一度自分自身へ `SELFDESTRUCT` する。 |
| 3 | 自分自身を受取先にした `SELFDESTRUCT` の後に、別アドレスへ `SELFDESTRUCT` する。 |
| 4 | 別アドレスを受取先にした `SELFDESTRUCT` の後に、自分自身へ `SELFDESTRUCT` する。 |

#### トランザクション終了処理

終了処理では、`SELFDESTRUCT` 後にETHを受け取るパターンや、コード・ストレージ・`nonce` が消えるかを確認します。

| No | ケース |
|----|--------|
| 5 | 別アドレスへ `SELFDESTRUCT` した後、`CALL` で対象アカウントへETHを送る。 |
| 6 | 別アドレスへ `SELFDESTRUCT` した後、複数回の `CALL` で対象アカウントへETHを送る。 |
| 7 | 別アドレスへ `SELFDESTRUCT` した後、`CALL` でETHを送り、さらに別アドレスへ `SELFDESTRUCT` する。 |
| 8 | 別アドレスへ `SELFDESTRUCT` した後、`CALL` でETHを送り、さらに自分自身へ `SELFDESTRUCT` する。 |
| 9 | 自分自身へ `SELFDESTRUCT` した後、`CALL` で対象アカウントへETHを送る。 |
| 10 | 自分自身へ `SELFDESTRUCT` した後、複数回の `CALL` で対象アカウントへETHを送る。 |
| 11 | 自分自身へ `SELFDESTRUCT` した後、`CALL` でETHを送り、さらに別アドレスへ `SELFDESTRUCT` する。 |
| 12 | 自分自身へ `SELFDESTRUCT` した後、`CALL` でETHを送り、さらに自分自身へ `SELFDESTRUCT` する。 |
| 13 | 新しいアカウントを作り、そのアカウントが別アカウントを作って `nonce` を増やした後、自分自身へ `SELFDESTRUCT` する。 |
| 14 | 新しいアカウントを作り、そのアカウントが別アカウントを作って `nonce` を増やした後、別アドレスへ `SELFDESTRUCT` する。 |
| 15 | 新しいアカウントを作り、そのアカウントがストレージを書き込んだ後、自分自身へ `SELFDESTRUCT` する。 |
| 16 | 新しいアカウントを作り、そのアカウントがストレージを書き込んだ後、別アドレスへ `SELFDESTRUCT` する。 |

#### 複数トランザクション

複数トランザクションでは、`CREATE2` で残高のある新しいアカウントを作り、そのアカウントが自分自身へ `SELFDESTRUCT` した後、同じ処理をもう一度実行します。
ここで確認するのは、残高のみのアカウントが残っても、`nonce` とコードが消えているため、後続トランザクションで再作成できることです。

## セキュリティ

`SELFDESTRUCT` の挙動変更は、複数の呼び出しやコントラクト作成を組み合わせたテストへ広く影響します。
そのため、変更範囲は小さく見えても、テスト量は多くなります。
提案では、**EIP6780** 向けの既存テストを活用して、新しい終了処理へ合わせる方針が示されています。

もう1つの注意点は、ETHの `burn` を前提にした使い方です。
その使い方では、変更後に小額残高だけを持つアカウントが残ります。
ただし、新しいアカウントを作るガスコストはトランザクション送信者が払うため、無料でステートを増やす `DoS` 手段にはなりません。

提案で示されたメインネット再実行では、Cancun後から約2500万ブロックまでに、この処理で記録された事例は2件でした。
そのため、実際の影響はかなり限定的です。

## 最後に

今回は「**SELFDESTRUCTでETHが消えないようにし、残高だけを保持するEIP8246**」についてまとめてきました。

**EIP8246** は、`SELFDESTRUCT` によってETHが消える処理をなくす提案です。
残高は保持しつつ、コード・ストレージ・`nonce` は消すため、実行可能なコントラクトとしては残りません。
利用頻度の低い特殊処理を消し、EVM実装とテストを単純にする変更です。

他でも色々記事を書いているのでぜひよろしければ読んでいってください！

https://amzn.asia/d/gxvJ0Pw

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://twitter.com/cardene777

https://cardene.substack.com/
