EIP-6780は、SELFDESTRUCTオペコードの挙動を変更するイーサリアムのプロトコルアップグレードだ。中核となる変更は、SELFDESTRUCTがコントラクトの「デプロイと同一のトランザクション内」で呼び出された場合にのみコード、ストレージ、アカウントを完全に削除するようになったことだ。それ以外の場合(実際の使用例の大多数)は、コントラクトの資金を転出させるだけで、コードとストレージデータはすべてチェーン上にそのまま残る。これはSELFDESTRUCTの元々の挙動——実行後にコントラクト全体が消滅する——とは完全に異なる。
この変更が存在するのは、イーサリアムが将来Verkle treeという新しい状態ストレージアーキテクチャを採用する予定であり、その下ではアカウントデータが複数のキーに分割して保存されるため、「1つのアカウントに属するすべてのデータをアトミックに一度に削除する」ことが技術的に非現実的かつ非効率になるからだ。EIP-6780は、古い削除ロジックに合わせるために状態ツリー全体の設計を異常に複雑にするのではなく、SELFDESTRUCTの挙動を新しいアーキテクチャの制約に合わせて調整することを選んだ。これは「古い機能の完全性を犠牲にして将来のスケーラビリティを得る」という典型的なエンジニアリング上のトレードオフだ。
具体的なルールは2つの分岐に分かれる。分岐1:SELFDESTRUCTがコントラクトのデプロイと同一のトランザクション内で呼び出された場合——従来の挙動を維持し、コード、ストレージ、アカウントがすべて削除される。これはファクトリーコントラクトパターンにおける「使い捨て」の補助コントラクトで一般的だ。分岐2:デプロイ完了後、一定時間を経てSELFDESTRUCTが呼び出された場合——資金転送のアクションのみが実行され、コントラクトのコードと過去に書き込まれたストレージデータはすべてそのままチェーン上に残り照会可能であり、アカウント自体も存在し続ける。分析者が「自己破壊した」コントラクトの現在の状態を判断するには、コントラクトの現在の残高がゼロか(資金転出が完了したか)、そしてコントラクトのコードとストレージが依然として照会可能か(大多数のケースで照会可能)を別々に確認する必要がある。
読者にとっての実際的な影響は、オンチェーンのデューデリジェンスを行ったり、あるコントラクトの過去の資金フローや脆弱性記録を追跡したりしていて「このコントラクトはSELFDESTRUCTを実行した」と見た場合、それを「このコントラクトはもう存在せず無視してよい」というシグナルとして直接扱ってはいけないということだ。正しい対応は、さらに確認することだ。コントラクトのコードとストレージデータが依然として照会可能か(通常は可能)。過去の取引履歴とロジックは完全なオンチェーンの証拠として残っており、コントラクトが自己破壊命令を実行したからといって消えたり信頼できなくなったりすることはない。
イーサリアムの初期設計では、SELFDESTRUCTはスマートコントラクトが呼び出せるオペコードで、実行されると残りの資金を指定されたアドレスに転送し、同時にこのコントラクトのコード、ストレージデータ、アカウント自体をチェーンの状態から完全に削除していた。「消滅」という表現がまさに当てはまり、このコントラクトアカウントはその後存在しなくなる。この設計は元々、開発者がコントラクトが不要になった際に状態スペースを解放し、おまけとして一部のガス返金(過去はデータ削除で一部のガスコストが返金された)を得られるようにするためのものだった。
問題はイーサリアムの長期的なスケーラビリティのロードマップにある。イーサリアムは将来、Verkle treeと呼ばれる新しい状態ストレージ構造を採用する予定で、この構造の下ではアカウントデータは複数の異なるキーに分割して保存される。この構造の下では、1つのアカウントに関連するすべてのデータを「アトミックに」一度に削除することは技術的に非現実的になる。不可能というわけではなく、状態ツリーの設計全体が異常に複雑で非効率になってしまうということだ。EIP-6780の解決策は、アーキテクチャ全体を古い削除ロジックに適合させるよう強制することではなく、逆にSELFDESTRUCTというオペコードの挙動を調整し、新しいアーキテクチャの制約に合わせることだった。
調整後のルールは次の通りだ。SELFDESTRUCTがコントラクトの「デプロイと同一のトランザクション」内で呼び出された場合、従来通りの挙動を維持する——コード、ストレージ、アカウントを完全に削除する。しかし、デプロイが完了してから一定時間後に呼び出された場合(実際の使用状況の大多数がこちらに該当する)、現在は資金転送のみを実行し、コントラクトのコードとストレージデータはすべてそのままチェーン上に残る。この設計は、デプロイの瞬間に自己破壊するファクトリーコントラクトパターン(使い捨ての補助コントラクト)によく見られる正当な使用例を維持しつつ、問題を引き起こしていた削除動作を取り除いている。
これは、「SELFDESTRUCTを実行した」コントラクトを見た分析者が、それがチェーンから消滅したと単純に仮定できなくなったことを意味する。より正確な状況は次の通りだ。コントラクトの残高はゼロになり、資金は転出済みだが、そのコードは依然として照会可能であり、過去に書き込まれたストレージデータも依然として読み取れる。ただ、このコントラクトはもう誰にも正常に呼び出されたり操作されたりすることはなく、文字通りの「空の抜け殻」になる。オンチェーンのデューデリジェンスや過去の資金フローの追跡を行う人にとって、この区別は重要だ。「実際に削除された」コントラクトと「残高はゼロだがコードはまだ存在する」コントラクトは、リスク評価の観点では全く異なる意味を持つ。後者の過去のロジック、過去の脆弱性記録、かつての攻撃対象領域は、すべて完全に検証可能であり、コントラクトが「自己破壊したように見える」からといって消えてしまうわけではない。
かつては、コントラクトが「死んでいる」かどうかの判断は、「SELFDESTRUCTが呼び出されたことがあるか」という二元的な問いに単純化できた。EIP-6780以降、この問いは少なくとも2つの層に分割する必要がある。コントラクトに残高があるか(資金が転出されたか)、そしてコントラクトのコードとストレージが依然として照会可能か(大多数のケースでは答えは「はい」)。「SELFDESTRUCTイベントが発動したか」だけを確認してそのコントラクトを「消滅した」とラベル付けし、追跡リストから外す分析プラットフォームは、実際には完全な履歴データをまだ保持し、理論上は何らかの残存権限を通じて操作され得るコントラクトを見落としている可能性がある。