Seit Byzanz können wir aktualisierbare Proxy-Verträge viel einfacher mit der Verwendungs- returndatacopy
und returndatasize
Bauanleitung implementieren. Das bedeutet, dass wir Rückgabetypen und -größen nicht mehr registrieren müssen, wie bei der Verwendung des EtherRouter .
Der zuverlässigste Weg, den wir kennen, um einen Proxy-Vertrag zu strukturieren, ist wie der Zeppelin-Proxy , bei dem delegatecall
der in Assembly hergestellt wird. Es scheint jedoch auch zu funktionieren, wenn Sie den delegatecall
Solidity-Aufruf auf hoher Ebene ausführen, bei dem die Fallback-Funktion des Proxy-Vertrags stattdessen so aussieht:
function () public {
bool callSuccess = upgradableContractAddress.delegatecall(msg.data);
if (callSuccess) {
assembly {
returndatacopy(0x0, 0x0, returndatasize)
return(0x0, returndatasize)
}
} else {
revert();
}
}
Dieser Ansatz (siehe den gesamten Proxy hier ) ist etwas prägnanter und erfordert weniger Kenntnisse der Assemblierung, um ihn zu verstehen. Meine minimalen Tests für diesen Ansatz scheinen zu funktionieren.
In welchen Situationen funktioniert dieser Ansatz auf hoher Ebene nicht?
Und wenn es keine gibt, wie wahrscheinlich ist es, dass sich der kompilierte Bytecode für den Delegiertenaufruf auf hoher Ebene zwischen den Versionen von Solidity ändert und dieser Ansatz für diese Versionen bricht?
Ein Problem besteht darin, dass Sie Ihre Daten ab Adresse in den Speicher kopieren 0
. Dies funktioniert für Rückgabegrößen von weniger als 64 Byte, beginnt jedoch an diesem Punkt, anderen Speicher zu überschreiben.
Stattdessen sollten Sie eher etwas tun
let m := mload(0x40)
returndatacopy(m, 0, returndatasize)
return(0, returndatasize)
Um das von @Tjaden Hess erwähnte Problem zu beheben, tun Sie, was OpenZeppelin tut:
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
Wenn der Aufruf nicht fehlschlägt, geben Sie die Daten zurück, wie Sie es normalerweise in Solidity tun würden. Andernfalls kehren Sie über die Baugruppe zurück, um den Rückgängiggrund aufzublasen.
Pro-Tipp: Siehe meine Implementierung davon in PRBProxy .
skang404
okme
Paul Razvan Berg