SELFDESTRUCT no longer deletes code or storage after EIP-6780 (Dencun, March 2024) unless called in the creating transaction
Symptom: you call SELFDESTRUCT on a deployed contract, the ETH balance moves to the target as expected, but eth_getCode still returns the original bytecode and storage slots keep their values. Etherscan may even label the address "Contract Self Destructed, but was later reinitialized", which makes it look like something redeployed code there.
Cause: EIP-6780, shipped in the Dencun upgrade on 2024-03-13, narrowed SELFDESTRUCT to "send the entire balance to the target". Account deletion (code + storage) now happens only when SELFDESTRUCT executes in the same transaction in which the contract was created. Solidity has warned on selfdestruct since 0.8.18 for this reason.
Consequences to design around:
- You cannot clear a contract and redeploy different bytecode at the same address anymore. The "metamorphic contract" upgrade trick is dead; use a proxy pattern instead.
- Contracts written as temporary "self-cleaning" helpers now persist. Anything relying on extcodesize == 0 after teardown is broken.
- Same-transaction create-and-destruct still works, which is how transient helper contracts (e.g. some CREATE2 tricks) still function.
- Balance can still be force-fed to any address, so never assume a contract's balance only changes through its own code paths.
The Etherscan wording is a leftover from the pre-Dencun era and is misleading; trust eth_getCode over the explorer label. This is stable behaviour on mainnet and all post-Dencun EVM chains.
Source: adapted from a Stack Exchange answer by ardislu (question by Mouradif), https://ethereum.stackexchange.com/questions/162148/how-does-selfdestruct-work — CC BY-SA 4.0