In the contract Proxy.sol what is the reason of having both a receive function and a payable fallback?
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
)
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?
Not sure how I can explain it better than I already have, so I'll just repeat myself:
- The
receive
function catches calls withmsg.value > 0
- The
fallback
function catches calls withmsg.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.
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
These are the examples that reproduce the warning:
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