❌Self Destruct
Contracts can be deleted from the blockchain by calling selfdestruct. This function sends all remaining Ether stored in the contract to a designated address. However, this feature can be exploited by malicious contracts to force sending Ether to any contract.
The Vulnerability
Consider a game where the goal is to be the 7th player to deposit 1 Ether. Players can deposit only 1 Ether at a time, and the winner will be able to withdraw all Ether.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract EtherGame {
uint public targetAmount = 7 ether;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: address(this).balance}("");
require(sent, "Failed to send Ether");
}
}A malicious contract can break the game by sending Ether so that the game balance equals or exceeds 7 Ether.
The result? No one can deposit anymore, and the winner cannot be set.
Preventative Techniques
One way to prevent such attacks is to not rely on address(this).balance. Instead, maintain a separate state variable to keep track of the contract’s balance.
In this revised contract, the balance state variable is incremented with each deposit. This way, the contract’s balance is not affected by selfdestruct attacks.
Conclusion
While selfdestruct can be a useful feature in Solidity, it’s crucial to understand its potential vulnerabilities. By not relying on address(this).balance and using a separate state variable, you can prevent malicious contracts from disrupting your contract’s functionality. Always remember, security should be a top priority when developing smart contracts on the blockchain.
Last updated