Self-Destruct Abuse

The selfdestruct(address) opcode destroys a contract and sends all of its ETH to the specified address. This Ether arrives without triggering a receive() or fallback() function — so contracts that track their ETH balance via address(this).balance can have their accounting broken by a forced ETH injection.

How it works

Attackers deploy a contract with some ETH, then self-destruct it targeting your contract as the recipient. Your contract suddenly holds more ETH than expected — and if your logic assumes address(this).balance reflects only intentional deposits, all invariants break.

Vulnerable pattern

// VULNERABLE — logic based on exact balance
contract Vault {
    uint public expectedBalance;
    
    function deposit() public payable {
        expectedBalance += msg.value;
    }
    
    // This can be broken by a selfdestruct attack
    function isBalanced() public view returns (bool) {
        return address(this).balance == expectedBalance;
    }
    
    function withdraw() public {
        require(isBalanced(), "Balance mismatch");
        // Attack: selfdestruct sends extra ETH, isBalanced() always returns false
        // withdraw() is permanently bricked
    }
}

Safe pattern

// SAFE — track balances internally, don't rely on address(this).balance
mapping(address => uint) internal _deposits;
uint internal _totalDeposits;

function deposit() public payable {
    _deposits[msg.sender] += msg.value;
    _totalDeposits += msg.value;
}

// Use _totalDeposits for logic, not address(this).balance

Other self-destruct risks

  • Unprotected selfdestruct: A contract with a public or unguarded selfdestruct call can be destroyed by any caller.
  • Library destruction: Parity's wallet library was destroyed via an unprotected selfdestruct, freezing $280 million.

How to prevent it

  • Never use address(this).balance in contract logic — track deposits and withdrawals in internal state variables.
  • Protect or eliminate selfdestruct calls — consider removing them entirely (they're being deprecated in EIP-6049).
  • If you must use selfdestruct, guard it behind onlyOwner with multi-sig or timelocks.
← Back to Glossary