Since Byzantium we can implement upgradable proxy contracts much easier with the use of returndatacopy
and returndatasize
assembly instructions. This means we no longer have to register return types and sizes like when using the EtherRouter.
The most reliable way we know how to structure a proxy contract is like the Zeppelin Proxy where the delegatecall
is made in assembly. However, it also seems to work when doing the delegatecall
as a high level Solidity call where the proxy contract fallback function looks like this instead:
function () public {
bool callSuccess = upgradableContractAddress.delegatecall(msg.data);
if (callSuccess) {
assembly {
returndatacopy(0x0, 0x0, returndatasize)
return(0x0, returndatasize)
}
} else {
revert();
}
}
This approach (see the whole proxy here) is a bit more succinct and requires less knowledge of assembly to understand. My minimal tests for this approach seem to work.
So in what situations will this high level approach not work?
And if there aren't any, how likely is it that the compiled bytecode for the high-level delegatecall will change between versions of Solidity, breaking this approach for those versions?
Best Answer
One issue is that you copy your data into memory starting at address
0
. This will work for return sizes less than 64 bytes, but will start overwriting other memory at that point.Instead you should do something more like