Skip to content

EIP-8246 (remove SELFDESTRUCT burn) - #2512

Open
brett-monad wants to merge 1 commit into
bruce/eip-7997from
eip-8246-remove-selfdestruct-burn
Open

brett-monad wants to merge 1 commit into
bruce/eip-7997from
eip-8246-remove-selfdestruct-burn

Conversation

@brett-monad

@brett-monad brett-monad commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Removes the two paths by which SELFDESTRUCT still destroys ETH. This is a prerequisite of EIP-7708 rather than an adjacent EIP — 7708's current text lists requires: 8246 and no longer specifies a burn — which is what lets the 7708 implementation land with no Burn log at all. That is a sequencing constraint rather than a code one: 8246 must be live before 7708 is, or 7708 would claim complete native-value logging while two burn paths went unlogged.

Rule 1: a same-transaction account destructing to itself keeps its balance. State::selfdestruct moves the balance whenever the beneficiary differs; for the destruct-to-self case one predicate, selfdestruct_to_self_burns, holds the revision rule: always before Cancun, only for a same-transaction account under EIP-6780, never under EIP-8246.

Rule 2: at finalization a destructed account holding a balance is preserved — nonce reset, code and storage cleared, balance untouched — rather than deleted. Nonce and code are cleared first and EIP-161 is applied to the result, so an empty account is still deleted. Rule 2 also covers the EIP's second burn, value sent to an account already marked for selfdestruction, because it keys on the balance rather than on who the beneficiary was.

Gated on a new eip_8246_active(), which is >= MONAD_ETH_AMSTERDAM in both trait families; pre-Amsterdam revisions must keep burning.

Reserve balance is deliberately unchanged

A preserved account is a funded, nonce-0, codeless address, and no predicate distinguishes it from an ordinary pre-funded address — three Account fields match by construction, and the fourth, incarnation, is drawn from the same source for both and is absent from the merkle account. So it takes the existing MIP-4 floor exactly as a pre-funded address does today. Exempting it would mean making the storage-generation key carry account provenance, and that equivalence is the reason not to.

Spec tests

No change to the Amsterdam exclusion list. The Amsterdam suite is disabled at
this commit and opens once every Amsterdam EIP is in the tree. Nothing needs
excluding: the pinned bundle is generated with 8246 in effect, and its nine
selfdestruct fixtures all pass against this tree. The unit tests are the gate
on this PR.

Testing

Full MONAD_COMPILER_TESTING=ON build clean. ctest clean apart from the known BacktraceTest.works. All spec-test jobs pass. clang-format and license.sh clean.

Known limitation: the exec-event schema cannot express a code-cleared surviving account.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T2YcdwU1mj3Lqunp9Y7pqa

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the EIP-8246 implementation covering State::selfdestruct, State::destruct_suicides, the trait flag, the state-diff tracer comment, and the coinbase-award comment in execute_transaction.cpp.

The selfdestruct reshuffle is correct: the new eip_8246_active arm collapses to a no-op when address == beneficiary (no burn) and to a transfer otherwise, and the pre-Cancun and Cancun-only arms are unchanged. The destruct_suicides refactor preserves prior semantics for < Cancun (unconditional reset), for post-Cancun with mismatched incarnation (skip), and for post-Cancun with matching incarnation under !eip_8246_active (reset). The new preservation path only fires when the account was created this tx, has non-zero balance, and mutates in place so that the incarnation drives the reincarnation flag through commit — I verified Account::operator== includes incarnation and that create_contract stamps it. The storage-zeroing iterates the original map, which is the correct superset given set_storage/get_storage both populate the original map before or in place of current. MONAD_ASSERT(orig != original_.end()) holds because the only path that inserts into current_ is current_account_state, which first goes through original_account_state. The destruct_touched_dead interaction is sound: a preserved account has balance > 0, so is_dead is false and the untemplated pass spares it. can_merge/relaxed-merge ordering (before execute_final) leaves the preservation unaffected. The tests are thorough — the preserved-storage, credit-after-destruct, touched-dead, merge-through-trie, state-root-parity, and same-block-recreation cases each pin a distinct invariant.

Verdict: CORRECT

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 24, 2026 15:35
@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch from 4b05bfa to f927f4e Compare August 24, 2026 15:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements EIP-8246, removing SELFDESTRUCT balance burns and preserving funded accounts from Amsterdam onward.

Changes:

  • Adds Amsterdam-gated EIP-8246 support.
  • Updates transfer and finalization behavior.
  • Expands state, storage, merge, and tracing tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Summary
category/vm/evm/traits.hpp Adds the EIP-8246 activation predicate.
category/execution/ethereum/trace/state_tracer.cpp Documents preserved-account tracing behavior.
category/execution/ethereum/test/test_call_trace.cpp Verifies SELFDESTRUCT trace values.
category/execution/ethereum/state3/state.cpp Implements non-burning and account-preservation logic.
category/execution/ethereum/state2/test/test_state.cpp Tests preservation, deletion, storage clearing, and commits.
category/execution/ethereum/execute_transaction.cpp Documents finalization ordering.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch from f927f4e to b89479a Compare August 25, 2026 09:19
@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch 2 times, most recently from ff1dfbe to e1b9d1a Compare August 26, 2026 15:35
@brett-monad
brett-monad changed the base branch from eip-8024-dupn-swapn-exchange to main August 26, 2026 15:38
@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch 2 times, most recently from 584bd9e to ca8f8dc Compare August 27, 2026 10:59
@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch 2 times, most recently from d002b44 to e2cf84d Compare August 27, 2026 12:42
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from e2cf84d to 37cb8c0 Compare September 4, 2026 10:41
@Baltoli
Baltoli changed the base branch from main to bruce/eip-7997 September 4, 2026 10:42
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from 37cb8c0 to 2132967 Compare September 4, 2026 12:46
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from 2132967 to c66d816 Compare September 4, 2026 14:29
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from c66d816 to fec7654 Compare September 7, 2026 09:24
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from adefbab to c699e12 Compare September 9, 2026 15:23
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from c699e12 to 3dfc152 Compare September 10, 2026 10:51
Comment thread category/execution/ethereum/state2/test/test_state.cpp Outdated
Comment thread category/execution/ethereum/state3/state.cpp Outdated
Comment thread category/execution/ethereum/execute_transaction.cpp Outdated
Comment thread category/execution/ethereum/state2/test/test_state.cpp Outdated
@ryankeleti
ryankeleti force-pushed the eip-8246-remove-selfdestruct-burn branch from 3dfc152 to 1648fa4 Compare September 10, 2026 18:40
@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch 2 times, most recently from 42ba207 to af1e775 Compare September 11, 2026 18:51
Comment thread category/execution/ethereum/state2/test/test_state.cpp Outdated
Comment thread category/execution/ethereum/state3/state.cpp Outdated
Comment thread category/execution/ethereum/state3/state.cpp Outdated
Comment thread category/execution/ethereum/state3/state.cpp Outdated
Comment thread category/execution/ethereum/state3/state.cpp Outdated
Comment thread category/execution/ethereum/state3/state.cpp Outdated
@Baltoli
Baltoli force-pushed the eip-8246-remove-selfdestruct-burn branch from af1e775 to 33603d6 Compare September 14, 2026 12:24
…n Amsterdam

EIP-8246 removes the two paths by which SELFDESTRUCT still destroys ETH. It is
a prerequisite of EIP-7708, whose current text requires it and no longer
specifies a burn, so 8246 has to be live before 7708 or that EIP would claim
complete native-value logging while two burn paths went unlogged.

Rule 1: a same-transaction account destructing to itself keeps its balance.
State::selfdestruct moves the balance whenever the beneficiary differs; for the
destruct-to-self case one predicate, selfdestruct_to_self_burns, holds the
revision rule: always before Cancun, only for a same-transaction account under
EIP-6780, never under EIP-8246.

Rule 2: at finalization a destructed account is preserved rather than deleted
when it holds a balance -- nonce reset, code and storage cleared, balance
untouched. destruct_suicides clears nonce and code first and then applies
EIP-161 to the result, so an empty account is still deleted. Rule 2 also covers
the EIP's second burn, value sent to an account already marked for
selfdestruction, because it keys on the balance rather than on who the
beneficiary was.

Gated on a new eip_8246_active() predicate: pre-Amsterdam revisions must keep
burning.

Implementation notes.

The preserved account is mutated in place so its incarnation survives: it is
the storage-generation key the commit builders compare against the pre-block
account to decide whether the old storage subtree is rebuilt, and a fresh
Account would default it to a value that is itself a legal incarnation.

zero_storage writes an explicit zero for every key in either storage map rather
than erasing them: a missing key falls back to the original map or the block
delta and could still read a pre-destruct value. The two key sets differ only
on the RPC full-state-override path, where set_to_state_incarnation stamps the
current incarnation onto a pre-existing contract.

A preserved account survives destruct_touched_dead because holding a balance
makes it not empty under EIP-161. BlockState::can_merge runs before
execute_final, which is what stops relaxed merge from zeroing the balance and
committing an empty account. The reserve-balance check runs at depth zero
before finalization, so rule 2 is invisible to it and only rule 1 is, which
removes a debit.

Testing. The Amsterdam spec suite stays switched off here, as on the commits
below it: the fixtures are generated with the whole fork active, so the suite
opens only once every Amsterdam EIP is in the tree. The pinned bundle is
generated with EIP-8246 in effect, so the selfdestruct fixtures outside
amsterdam/ (cancun/eip6780_selfdestruct, tangerine_whistle eip150_selfdestruct,
frontier/create, paris/security, monad_nine/mip4_checkreservebalance) expect
the balance-only account that 8246 preserves, and pass once the suite is
enabled. Until then the unit tests are the gate. Every assertion was checked by
deleting the code it covers and confirming the test fails.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@brett-monad
brett-monad force-pushed the eip-8246-remove-selfdestruct-burn branch from 33603d6 to 8f90b15 Compare September 15, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants