Solidity
自拜占庭以來在可升級合約中使用高級代表呼叫
自從拜占庭以來,我們可以通過使用
returndatacopy
和returndatasize
彙編指令更容易地實現可升級的代理合約。這意味著我們不再需要像使用EtherRouter時那樣註冊返回類型和大小。我們知道如何建構代理合約的最可靠方法就像Zeppelin 代理,它
delegatecall
是在組裝中製造的。delegatecall
然而,當代理合約回退函式看起來像這樣的高級 Solidity 呼叫時,它似乎也可以工作:function () public { bool callSuccess = upgradableContractAddress.delegatecall(msg.data); if (callSuccess) { assembly { returndatacopy(0x0, 0x0, returndatasize) return(0x0, returndatasize) } } else { revert(); } }
這種方法(請參閱此處的整個代理)更簡潔一些,並且需要較少的彙編知識即可理解。我對這種方法的最小測試似乎有效。
那麼在什麼情況下這種高級方法不起作用呢?
如果沒有,高級委託呼叫的編譯字節碼在 Solidity 版本之間發生變化的可能性有多大,從而破壞了這些版本的這種方法?
一個問題是您將數據複製到從 address 開始的記憶體中
0
。這將適用於小於 64 字節的返回大小,但此時將開始覆蓋其他記憶體。相反,您應該做更多類似的事情
let m := mload(0x40) returndatacopy(m, 0, returndatasize) return(0, returndatasize)
要緩解@Tjaden Hess 提到的問題,請執行OpenZeppelin所做的操作:
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); } }
如果呼叫沒有失敗,請像在 Solidity 中通常那樣返回數據。否則,通過程序集還原以找出還原原因。
專業提示:請參閱我在PRBProxy中的實現。