Proxy.sol fallback

In the contract Proxy.sol what is the reason of having both a receive function and a payable fallback?

2 Likes

The fallback function is exactly how the whole proxy/logic mechanism works.

It allows calling a function which is implemented in the logic contract, despite the fact that it is not implemented in the proxy contract.

When you call that non-implemented function on the proxy contract, the execution defaults (falls-back, if you will) to the fallback function, and then "jumps" to a function of the same prototype (same name and same input argument types) on the logic contract.

What I am refering to is that in the Proxy.sol, starting at line 63 the code is:

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback() external payable virtual {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive() external payable virtual {
        _fallback();
    }

And my question is if there is a reason to implement a receive() function if the fallback is already payable

Well, your question is generally agnostic to the fact that these functions are implemented in the Proxy contract, but in any case:

  • The receive function catches every call to a non-existing function with eth passed as part of the transaction (i.e., msg.value > 0)
  • The fallback function catches every call to a non-existing function without eth passed as part of the transaction (i.e., msg.value == 0)
1 Like

I could have explained the question better indeed

My doubt is: wouldn't the payable fallback suffice? Does it need to have a receive function? What does it add to the implementation?

1 Like

Not sure how I can explain it better than I already have, so I'll just repeat myself:

  • The receive function catches calls with msg.value > 0
  • The fallback function catches calls with msg.value == 0

If you omit the receive function, then you will not be able to call any payable function on your logic contract (more precisely, you will be able to do so, but only when you pass zero eth to such function).

Hello @RenanSouza2

The fallback alone would indeed be enough. Unlike what @barakman says, faillback is not limited to msg.value == 0 (if its marked payable). Both function can support value.

The difference between receive and fallback is in the msg.data. If the calldata is empty and if there is a receive function, it fill be used. Otherwize, fallback is used. This means that regardless of the value, fallback will be called if there is some data. fallback is also the one that is called if there is no data, but receive is not defined.

So why do we have a receive function that is not really needed? To silent solidity warnings that sometimes happen when you have a fallback function but no receive function.

Note that I'm not able to reproduce this warning in remix, so we may change that in the code.

2 Likes

Thanks @Amxx,

In the hardhat I also removed the receive function, no warnings and all tests passed

Yes

I create a PR: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/4434

1 Like

These are the examples that reproduce the warning:

1 Like

I saw that the PR is already included but just to leave here the conclusions on the warning,

It only happens with all of this conditions:

  • payable fallback
  • no receive function
  • another external/public function
2 Likes