What is selfdestruct in Solidity?
Written by John Favole
Reviewed by Brady Werkheiser
Update: Selfdestruct was deprecated during the Shanghai Ethereum upgrade as called for by EIP-6049. This article is now outdated and only reflects how the feature formerly worked.
Selfdestruct is a keyword in Solidity that is used when developers wanted to terminate a contract. In a 2021 research paper, "Why Do Smart Contracts Self-Destruct?
Investigating the Selfdestruct Function on Ethereum", around 800 contracts have included the selfdestruct keyword, making it an important concept to understand while studying Solidity.
This article defines what selfdestruct was, describe its purpose, and provide an example of implementing selfdestruct in a Solidity smart contract.
What is selfdestruct?
Selfdestruct was a keyword that is used to terminate a contract, remove the bytecode from the Ethereum blockchain, and send any contract funds to a specified address.
Selfdestruct originated in 2016 when the Ethereum blockchain and decentralized organizations (DAOs) were in their formative stages. One of the earliest DAOs lost 3.6 million ETH to a hack. The attack continued for days due to the immutability of Solidity contracts. Since a way to destroy a contract didn’t exist at that time, earl DAO developers forked the entire blockchain to prevent further exploits.
Therefore, selfdestruct was created to serve as an exit door in case of security threats. Although it was first called the “suicide function”, it was renamed to selfdestruct from Solidity v0.5.0 onwards.
Why do developers use the selfdestruct function?
Developers used the selfdestruct function primarily to improve smart contract code security, clean up unused contracts and transfer Ethereum assets quickly.
Selfdestruct was helpful when developers need to upgrade smart contracts. For instance, the ERC-20 framework is the standard implementation for all fungible tokens on Ethereum for the purpose of interoperability. Any token that does not interface with the ERC-20 standard will have difficulty interacting with other contracts.
In such cases, developers will change to a new contract instead of upgrading the current contract. Thus, they could have used the selfdestruct function to extract funds from the current contract and build a new contract with the required functionalities.
Developers also used the selfdestruct function to safeguard against potential security threats. According to the same 2021 selfdestruct smart contract report, when developers find security flaws in their contracts, a selfdestruct function helps them immediately terminate the flawed contract and replace it with a secure contract.
What are the disadvantages of using selfdestruct?
The disadvantages of the selfdestruct function were its inability to recover ERC-20 tokens, and its inability to reroute tokens after self destruction.
Typically, selfdestruct functions are called by developers who fail to communicate about contract termination on time. Even when a team communicates the update, it may not circulate fast enough. As a result, some people might end up sending funds to the destroyed contract thinking it’s still active. In such a case, the funds are lost.
The second disadvantage of using selfdestruct is that it can only transfer Ether (ETH) and not other ERC-20 tokens such as altcoins or NFTs which follow the ERC-721 token standard. Once selfdestruct is called, these assets can never be recovered.
Why did some consider the selfdestruct function to be dangerous?
The selfdestruct function made it easier for malicious developers to execute rug pulls.
The function made it possible for developers to call selfdestruct, and direct the funds to their personal wallets. For this reason, some protocol users and developers have expressed mixed feelings about using the selfdestruct function.
How does selfdestruct work?
Selfdestruct works by erasing the contract bytecode from the chain, sending any liquidity to a specified address, and then, refunding a portion of the gas fees to developers.
A contract consists of two components: state and functions. These components define contract behavior and callable functions. Removing the bytecode means that the contract has no components that define it, rendering the contract uncallable.
The selfdestruct function had a required parameter that indicated where the caller wanted the contracts funds to end up after destruction.
What is negative gas and why is it relevant?
Negative gas refers to a part of the transaction fee that the Ethereum chain refunds whenever the developers use the selfdestruct function.
Ethereum incentivized using the selfdestruct function by rewarding the caller with the gas savings generated by removing the data from the blockchain. When a contract calls the selfdestruct function, half of the total gas used for the transaction was refunded to the caller.
Does selfdestruct remove the contract's history on the Ethereum blockchain?
No, the selfdestruct function does not remove the history of a contract from the Ethereum chain — it only removes the contract's bytecode.
Ethereum is a blockchain, a public ledger, where a network of interconnected nodes always keep a copy of the blockchain’s state. Therefore, all the data and transactions done before calling the selfdestruct function are permanently recorded onchain and cannot be altered.
Are funds permanently lost after calling the selfdestruct function?
No and Yes, existing ETH is sent to another address specified by the function caller, while ERC-20 tokens are lost.
Additionally, if anyone sends funds to a selfdestructed contract (i.e. terminated contract) they will not be rerouted and will be lost.
Selfdestruct Function Example
The goal of this program is to allow minting of NFTs until a certain amount of ETH in the contract is reached. Once reached, the program sends all the Ether to the last minter’s wallet as a bonus. Each person can mint multiple NFTs with 1 Ether but only one at a time. This contract is not meant to be used in production, it is for example purposes only.
// SPDX-License-Identifier : MIT
pragma solidity ^0.8.17;
contract Mint{
address public minter;
uint public target = 30 Ether;
function depositMintingEther() public payable {
require(msg.value == 1 Ether, "You can only mint one NFT at a time");
uint bal = address(this).balance;
require(bal <= target, "We have run out of NFTs");
if(bal == target){
lastMinter = msg.sender;
}
}
function receiveFunds() public {
require(msg.sender == lastMinter);
(bool success, ) = msg.sender.call{value : address(this).balance}("");
require(success, "Cannot send funds");
}
}
contract Attack{
Mint badMinter;
constructor(Mint _badMinter) {
badMinter = Mint(_badMinter);
}
function spoiler () public payable{
address payable mintAddress = payable(address(badMinter))
selfdestruct(mintAddress));
}
}
The main issue with the mint contract is that it uses “this.balance” to verify if the contract has the sufficient funds or not to trigger the end clause. So, the attacker can easily send an amount to push the contract over the specified limit and then use the selfdestruct keyword to redirect the funds to their declared constructor.
Therefore, while using selfdestruct, developers have to be careful about the variables you use to satisfy specific conditions. Avoid using any reference to contract addresses or the funds in the contract, as they can be artificially manipulated.
How to Use the Selfdestruct Function
Initializing a selfdestruct function is simple: you need to use the selfdestruct keyword within a function and specify a payable address that can receive contract funds after the selfdestruct function is called.
Here is an example of a selfdestruct function typically taught in a Solidity course:
function destroy(address apocalypse) public {
selfdestruct(payable(apocalypse));
}
This is what the code is doing:
The name of the function is destroy
The parameter specifies the address as apocalypse
When the destroy function is called, the address is specified via the apocalypse variable
The function visibility is declared public so other contracts can access it.
Then we use the selfdestruct keyword and pass the apocalypse variable after declaring it as payable.
Now, since the function is public, it represents a potential security flaw. To counter this, you can consider adding an onlyOwner modifier or using a require statement to confirm that only the owner can call the destroy function.
function destroy(address apocalypse) public {
require(owner == msg.sender, "only the owner can call this");
selfdestruct(payable(apocalypse));
}
Learn Solidity and the Selfdestruct Function with Alchemy’s Solidity Developer Course
Selfdestruct acts as a failsafe to terminate a contract during security breaches, when developers want to upgrade contracts, or to remove old contracts. While the usage of selfdestruct is still debated in the Solidity developer community, introducing the function has helped protect against many potential hacks.
You can learn more about important functions like selfdestruct and become a better Solidity developer by signing up for Alchemy University's free, 7-week Ethereum Bootcamp. If developers are new to development in general, Alchemy's 3-week JavaScript crash course is a great prerequisite before starting an Ethereum bootcamp.
Related overviews
Explore the Best Free and Paid Courses for Learning Solidity Development
Your Guide to Getting Started With Solidity Arrays—Functions, Declaring, and Troubleshooting
Go from Beginner to Gigabrain Solidity Developer