EIP-4758: Deactivate selfdestruct


Complete verkle trie construction for motivation: HackMD - Collaborative Markdown Knowledge Base
Analysis on applications that would be affected by this change: Impact Analysis of Neutering SELFDESTRUCT - Dev Update #2 - HackMD


Abstract says:

The new functionality will be only to send all Ether in the account to the caller.

I believe you intended to say it sends to a beneficiary (instruction input address) and not the caller.

1 Like

Also I’ll paste @chfast’s comment from draft PR here:

You forgot to mention that SENDALL still terminates execution.

Should it be repriced, as it’s now only updating balance?

The SELFDESTRUCT has one additional quirk not handled here: when the beneficiary is the selfdestructing address itself the balance is burned instead of being transferred.

Agreed that it should be mentioned explicitly in the EIP: EIP-4758: Deactivate selfdestruct by dankrad · Pull Request #4758 · ethereum/EIPs · GitHub.

But the new behavior can also be inferred from the EIP: the account removal doesn’t happen so a SENDALL with self as beneficiary is basically a no-op that just terminates execution of the current frame.

GitHub - jwasinger/eth-selfdestruct-analysis . This is an updated impact analysis which looks at usage of SELFDESTRUCT after the London hard fork and identifies contracts with large holdings that could be affected by the changes in EIP-4758

As mentioned by @chfast SELFDESTRUCT can be used to burn ETH by ‘selfdestructing’ to the current address. There is another quirk however which is changed by this EIP:

SELFDESTRUCT does not immediately destroy code and send the balance. This is done after all call frames are done and thus every opcode of the transaction has been executed. Only then are the following steps executed:

1. Send all ETH of the contract to the beneficiary
2. Destroy the contract (code = empty, balance = empty, nonce = 0, balance = 0)

Due to this order it is thus possible to destroy ETH. Also, notice that it is possible to do “multiple” selfdestructs by calling the selfdestruct from another contract multiple times. It is possible to first SELFDESTRUCT to address A and then SELFDESTRUCT to address B. In this case, the beneficiary is B, not A. If this changes, it should be specified on the EIP. It now seems that SENDALL sends all ETH of the current contract to the beneficiary and immediately exits the current call frame - which I guess is fine, but the EIP also states that it “renames” SELFDESTRUCT to SENDALL which is due to these quirks not really the case.

I don’t believe this is correct. The ETH is sent immediately. @gumb0 has double-checked this.

Oops, you are right, have semi-deleted my post.

I am a consumer of the reincarnation upgrade pattern. I built an NFT contract ownership system (0x000000000000c57CF0A1f923d44527e703F1ad70 on every chain) to facilitate this pattern. It helps us upgrade our contract without needing to re-approve and migrate every token for every protocol. Our storage footprint on the protocol would be much larger without SELFDESTRUCT. The SLOAD+DELEGATECALL pattern, especially after Berlin, costs way too much for practical use in gas-denominated auctions, so we cannot use it.

I am willing to pay millions in gas to facilitate code changes. I do not see why code must be immutable but not storage. Perhaps there is a fair way to price a code change, and we could introduce a replacement opcode like SETCODE.

The main drawback of SELFDESTRUCT right now from an engineering perspective is that it doesn’t take place until the end of the transaction, so I need two separate transactions to do the upgrade. This has prevented wider adoption, as you could not safely upgrade a token, for example, without risking being sandwiched.


Our production system heavily utilizes the CREATE2 and SELFDESTRUCT loop. We have a few million dollars of TVL, with more expected to arrive in the near future. The system wasn’t designed to be modified once deployed and this EIP would fully brick our value-storage system, rendering funds inaccessible for users.

As a result, I’m very opposed to this EIP. It breaks a system that allows us to deploy and undeploy smart contract proxies within the same block, which is good not only for gas costs and allowing lower storage utilization, but also for security. By never having code deployed on those contracts outside of very predetermined periods, we reduce our attack surface area significantly. It is frustrating to be punished for being at the forefront of security in smart contract development, and we strongly request that this EIP be reconsidered or modified in some way.

Perhaps a change to CREATE2 could be considered in concert with this, such that it checks for existing byte-code and quietly fails rather than reverting. That would allow our system to continue to function unimpeded by the implementation of this proposal.


Is there any progress on this EIP-4758??

What do you think on repricing CREATE and CREATE2 when constructor returns zero size? Nothing will be deployed, only constructor code will be executed from new address. Now this costs 32k

I have no strong opinions on this EIP and whether or not it is the right path forward.

Did want to flag for visibility though as part of this conversation, that this has impact on the BytecodeStorage.sol library that we introduced in our ERC721-conforming “CoreContract” at Art Blocks here: Contract bytecode for script storage by ryley-o · Pull Request #299 · ArtBlocks/artblocks-contracts · GitHub.

This would not be a breaking change perse, so if this EIP were approved+implemented it wouldn’t be a dramatic concern point for our team by any means.

That said, a meaningful part of our rationale for adding this functionality in the first place was the intention to be mindful custodians of our impact on state bloat (see Allow for cleanup of unused contract bytecode for script storage by jakerockland · Pull Request #304 · ArtBlocks/artblocks-contracts · GitHub for context).

It sounds like this rationale of ours may not hold water vs. the concerns around the state management complexity that comes with SELFDESTRUCT which is totally valid and again not a matter that we have a strong opinion on.

tl;dr, we’re making use of SELFDESTRUCT at Art Blocks, but it is in a way that is not dramatically impacted by this EIP in a way that we have strong concern about–I am sharing all of this for additional context/visibility and not because we have a strong opinion on the matter.

1 Like

Not sure if this actually fully solves the problem that this EIP intends to capture, and again I definitely do not have a strong horse in this race, but has the approach of functionality limiting the amount of state change caused by a given usage of SELFDESTRUCT, by way of changing the op-code pricing for the opcode in order to more directly bound state change to the current gas limit, an approach that has been considered?

Could definitely understand that this type of approach would be impractical with regards to the client complexity it would add to calculate gas costs in a way that fits the state-change-bounding constraints that are driving this PR, but figured I would ask! :smile::purple_heart:

Related discussion regarding Shanghai inclusion: Shanghai Core EIP Consideration - #35 by wjmelements

That is an interesting idea. You could probably set it up so that CREATE2 will allow you to deploy the exact same bytecode to the same address, but fail for all other code.

I’d be concerned about the case where someone expects SELFDESTRUCT followed by a CREATE2 to clear storage, which wouldn’t happen.

Could you elaborate?