Solidity Delegatecall – How to ”.call” a Function of Another Contract Using ”.call”

delegatecallevmhackreentrant-attackssolidity

So, I'm learning advanced smart contract development. Two days ago, I learned about Reentrancy attacks and then I also created two contracts Protocol.sol (vulnerable contract) + Hacker.sol (attacker contract) to put my knowledge to the test. I was able to perform everything smoothly, I was importing the Protocol.sol (ABI + address) contract in my Hacker.sol. Today, I learned that we can call another smart contract function without importing the ABI, just using the contract address via ".call" & delegate call.
So, again to put my knowledge to the test, I used Protocol.sol & Hacker.sol.

Protocol.sol:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

contract Protocol {
    mapping(address => uint256) public balances;

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

    function withdraw() public payable {
        require(balances[msg.sender] > 0, "BRUH");
        (bool success, ) = (msg.sender).call{value: 1 ether}("");
        require(success);
        balances[msg.sender] = 0;
    }

    function getBalance() public view returns(uint256) {
        return address(this).balance;
    }
}

Hacker.sol:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

contract Hacker {

    function protocolDeposit(address protocol) public payable {
        (bool success,) = protocol.call{value: msg.value}(abi.encodeWithSignature("deposit()"));
        require(success, "call failed");
    }

    function attack(address protocol) public payable {
        (bool hacked,) = protocol.call(abi.encodeWithSignature("withdraw()"));
        require(hacked, "attack failed");
    }

    // fallback() external payable {
    //     (bool hacked,) = protocol.call(abi.encodeWithSignature("withdraw()"));
    //     require(hacked, "hack failed");
    // }

    function rektMoney() public view returns(uint256) {
        return address(this).balance;
    }
}

The problem, I am facing right now is calling withdraw() func. I am able to deposit ETH using Hacker.sol into Protocol.sol but I'm unable to call withdraw() using attack

Maybe it is because the withdraw func in the protocol.sol is also using call to transfer ETH.

How to ".call" a function of another contract which is using ".call" as well?

How I can solve this problem? Pls Help, Thanks in Advance

Best Answer

You need to impalement receive or fallback function in order to receive money in your hacker contract.

 // function to receive eth from others                                           
 receive() external payable {
        emit receivedMoney(msg.sender, msg.value); // event to emit when receive eth , this is optional
 }

since you haven't implemented that's why you are facing this issue.

And one more thing you need to make you withdraw() & attack(address protocol) function to be payable, since we are not accepting any ETH from user in that function.

I hope you understand, if not comment for better understanding.

have a great day 😊

Related Topic