DEV Community

Cover image for Reentrancy Attacks: What They Are and How to Defend Against Them
Diego Cândido
Diego Cândido

Posted on

Reentrancy Attacks: What They Are and How to Defend Against Them

Imagine the following situation: your smart contract is responsible to receive money from some addresses (freezing it) and, after some time, it should refund each address with the fozen amount plus some rewards for the freezing process.

As we know, smart contracts are not reactive; they are passive, which means that they only execute some function when someone calls it. Therefore, the address that wants to withdraw its amount has to call a function named, for example, withdraw().

I imagine this function as follows:

function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount > 0, "No balance");

    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");

    balances[msg.sender] = 0;
}
Enter fullscreen mode Exit fullscreen mode

Everything looks right. But there's a problem: transfers are not synchronous functions, which means they take time to execute. If an address calls withdraw, we send him money. If he call again immediately, we send him more money, because the balance update occours only after the first transaction is completed.

This is what we call a reentrancy attack: an attack where a user calls a withdraw/send function multiple times, trying to steal money from the contract.

🛡️ Preventing the attack

The best way to prevent a smart contract from reentrancy attacks is by updating the contract’s state **before **the transaction occurs. In the previous example, the solution would look like this:

function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount > 0, "No balance");

    balances[msg.sender] = 0;

    (bool success, ) = msg.sender.call{value: amount}("");
    if (!success) {
        balances[msg.sender] = amount;
        revert("Transfer failed");
    }
}
Enter fullscreen mode Exit fullscreen mode

Simple as that. If an address tries to call withdraw again right after the first call, it won’t be possible.

Reentrancy attacks have been a headache for blockchain developers, but most of the vulnerabilities in the Ethereum ecosystem can be solved with simple steps. Happy studying!

Top comments (0)