0

I'm wondering why it always reverts when I delegatecall a non-payable function from a payable context when the msg.value is not 0.

Simple code below:

contract Trial {
    function magicNumber42() public returns (uint256){
        return 42;
    }
}
function tryDelegate(address number_contract) payable public {
        (bool success, bytes memory data) = number_contract.delegatecall(abi.encodeWithSignature("magicNumber42()"));
        require(success, "failed");
        number = abi.decode(data, (uint256));
}

When I call tryDelegate with a non-zero value it reverts but succeeds when no value attached. Is it by design? In the meantime staticcall doesn't seem to have this problem.

1 Answer 1

0

Yes, the behavior you're seeing is by design. When you use delegatecall to call a function, the msg.value of the original function (in this case, tryDelegate) is passed along to the function you're calling (in this case, magicNumber42). If msg.value is non-zero, delegatecall will revert if the called function is not marked payable.

Whereas, staticcall works fine regardless of whether msg.value is zero or non-zero because it doesn't forward it.

So, delegatecall and staticcall behave differently when it comes to msg.value. Here’s the key difference:

  • delegatecall: This low-level function not only keeps the calling context (storage, msg.sender, msg.value), but it also forwards msg.value to the target function. As a result, if the target function isn’t marked payable, delegatecall will revert when msg.value is non-zero, causing issues with nonpayable functions.

  • staticcall: This is a read-only call intended for functions that don’t modify state, and it does not forward any Ether (msg.value is implicitly set to zero). Thus, staticcall bypasses this issue with nonpayable functions because no Ether is ever sent along, even if the caller is in a payable context.

Since staticcall never forwards any value, it works without reverts in cases like yours, where the function called (i.e., magicNumber42) is non-payable.

2
  • This is a strange behavior, if I want to do some calculation with msg.value by delegatecalling a library function then that function has to be marked payable as well, or directly calling it then.
    – Yu Zhong
    Commented Nov 10 at 23:44
  • Yes. Kindly close the question by marking it as accepted, if it helped you or cleared your doubt. Thanks. Commented Nov 11 at 2:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.