Commit f23bae0b authored by Maurelian's avatar Maurelian Committed by GitHub

bedrock: Proxy Admin improvements (#2787)

* bedrock: Use startPrank to clean up tests

* bedrock: Simplify static calling code in proxy admin

* bedrock: rename OpenZeppelin proxy to ERC1967
parent ae036d9a
---
'@eth-optimism/contracts-bedrock': patch
---
bedrock: ProxyAdmin rename OpenZeppelin proxy to ERC1967
---
'@eth-optimism/contracts-bedrock': patch
---
bedrock: Simplify ProxyAdmin static calls
...@@ -126,28 +126,28 @@ Proxy_Test:test_payableUpgradeToAndCall() (gas: 53887) ...@@ -126,28 +126,28 @@ Proxy_Test:test_payableUpgradeToAndCall() (gas: 53887)
Proxy_Test:test_revertUpgradeToAndCall() (gas: 104501) Proxy_Test:test_revertUpgradeToAndCall() (gas: 104501)
Proxy_Test:test_upgradeToAndCall() (gas: 125238) Proxy_Test:test_upgradeToAndCall() (gas: 125238)
Proxy_Test:test_zeroAddressCaller() (gas: 14758) Proxy_Test:test_zeroAddressCaller() (gas: 14758)
ProxyAdmin_Test:test_chugsplashChangeProxyAdmin() (gas: 35994) ProxyAdmin_Test:test_chugsplashChangeProxyAdmin() (gas: 35647)
ProxyAdmin_Test:test_chugsplashGetProxyAdmin() (gas: 16107) ProxyAdmin_Test:test_chugsplashGetProxyAdmin() (gas: 15711)
ProxyAdmin_Test:test_chugsplashGetProxyImplementation() (gas: 51947) ProxyAdmin_Test:test_chugsplashGetProxyImplementation() (gas: 51122)
ProxyAdmin_Test:test_chugsplashUpgrade() (gas: 49443) ProxyAdmin_Test:test_chugsplashUpgrade() (gas: 48994)
ProxyAdmin_Test:test_chugsplashUpgradeAndCall() (gas: 82729) ProxyAdmin_Test:test_chugsplashUpgradeAndCall() (gas: 82311)
ProxyAdmin_Test:test_delegateResolvedChangeProxyAdmin() (gas: 33846) ProxyAdmin_Test:test_delegateResolvedChangeProxyAdmin() (gas: 33976)
ProxyAdmin_Test:test_delegateResolvedGetProxyAdmin() (gas: 18102) ProxyAdmin_Test:test_delegateResolvedGetProxyAdmin() (gas: 17685)
ProxyAdmin_Test:test_delegateResolvedGetProxyImplementation() (gas: 63364) ProxyAdmin_Test:test_delegateResolvedGetProxyImplementation() (gas: 62016)
ProxyAdmin_Test:test_delegateResolvedUpgrade() (gas: 59156) ProxyAdmin_Test:test_delegateResolvedUpgrade() (gas: 58422)
ProxyAdmin_Test:test_delegateResolvedUpgradeAndCall() (gas: 98627) ProxyAdmin_Test:test_delegateResolvedUpgradeAndCall() (gas: 97948)
ProxyAdmin_Test:test_isUpgrading() (gas: 19531) ProxyAdmin_Test:test_erc1967ChangeProxyAdmin() (gas: 33862)
ProxyAdmin_Test:test_onlyOwner() (gas: 22672) ProxyAdmin_Test:test_erc1967GetProxyAdmin() (gas: 15672)
ProxyAdmin_Test:test_erc1967GetProxyImplementation() (gas: 52124)
ProxyAdmin_Test:test_erc1967Upgrade() (gas: 50036)
ProxyAdmin_Test:test_erc1967UpgradeAndCall() (gas: 79019)
ProxyAdmin_Test:test_isUpgrading() (gas: 19530)
ProxyAdmin_Test:test_onlyOwner() (gas: 22715)
ProxyAdmin_Test:test_onlyOwnerSetAddressManager() (gas: 10622) ProxyAdmin_Test:test_onlyOwnerSetAddressManager() (gas: 10622)
ProxyAdmin_Test:test_onlyOwnerSetImplementationName() (gas: 11113) ProxyAdmin_Test:test_onlyOwnerSetImplementationName() (gas: 11135)
ProxyAdmin_Test:test_onlyOwnerSetProxyType() (gas: 10774) ProxyAdmin_Test:test_onlyOwnerSetProxyType() (gas: 10751)
ProxyAdmin_Test:test_openZeppelinChangeProxyAdmin() (gas: 34248) ProxyAdmin_Test:test_owner() (gas: 9776)
ProxyAdmin_Test:test_openZeppelinGetProxyAdmin() (gas: 16090) ProxyAdmin_Test:test_proxyType() (gas: 20622)
ProxyAdmin_Test:test_openZeppelinGetProxyImplementation() (gas: 52950)
ProxyAdmin_Test:test_openZeppelinUpgrade() (gas: 50507)
ProxyAdmin_Test:test_openZeppelinUpgradeAndCall() (gas: 79435)
ProxyAdmin_Test:test_owner() (gas: 9796)
ProxyAdmin_Test:test_proxyType() (gas: 20644)
ProxyAdmin_Test:test_setImplementationName() (gas: 38957) ProxyAdmin_Test:test_setImplementationName() (gas: 38957)
ResourceMetering_Test:test_initialResourceParams() (gas: 8986) ResourceMetering_Test:test_initialResourceParams() (gas: 8986)
ResourceMetering_Test:test_updateNoGasDelta() (gas: 2008269) ResourceMetering_Test:test_updateNoGasDelta() (gas: 2008269)
......
...@@ -40,21 +40,21 @@ contract ProxyAdmin_Test is Test { ...@@ -40,21 +40,21 @@ contract ProxyAdmin_Test is Test {
// that is used for the implementation. // that is used for the implementation.
resolved = new Lib_ResolvedDelegateProxy(address(addressManager), "a"); resolved = new Lib_ResolvedDelegateProxy(address(addressManager), "a");
// Impersonate alice for setting up the admin.
vm.startPrank(alice);
// Set the address of the address manager in the admin so that it // Set the address of the address manager in the admin so that it
// can resolve the implementation address of legacy // can resolve the implementation address of legacy
// Lib_ResolvedDelegateProxy based proxies. // Lib_ResolvedDelegateProxy based proxies.
vm.prank(alice); admin.setAddressManager(addressManager);
admin.setAddressManager(address(addressManager));
// Set the reverse lookup of the Lib_ResolvedDelegateProxy // Set the reverse lookup of the Lib_ResolvedDelegateProxy
// proxy // proxy
vm.prank(alice);
admin.setImplementationName(address(resolved), "a"); admin.setImplementationName(address(resolved), "a");
// Set the proxy types // Set the proxy types
vm.prank(alice); admin.setProxyType(address(proxy), ProxyAdmin.ProxyType.ERC1967);
admin.setProxyType(address(chugsplash), ProxyAdmin.ProxyType.Chugsplash); admin.setProxyType(address(chugsplash), ProxyAdmin.ProxyType.Chugsplash);
vm.prank(alice);
admin.setProxyType(address(resolved), ProxyAdmin.ProxyType.ResolvedDelegate); admin.setProxyType(address(resolved), ProxyAdmin.ProxyType.ResolvedDelegate);
vm.stopPrank();
implementation = new SimpleStorage(); implementation = new SimpleStorage();
} }
...@@ -70,7 +70,7 @@ contract ProxyAdmin_Test is Test { ...@@ -70,7 +70,7 @@ contract ProxyAdmin_Test is Test {
function test_onlyOwnerSetAddressManager() external { function test_onlyOwnerSetAddressManager() external {
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
admin.setAddressManager(address(0)); admin.setAddressManager(Lib_AddressManager((address(0))));
} }
function test_onlyOwnerSetImplementationName() external { function test_onlyOwnerSetImplementationName() external {
...@@ -90,7 +90,7 @@ contract ProxyAdmin_Test is Test { ...@@ -90,7 +90,7 @@ contract ProxyAdmin_Test is Test {
function test_proxyType() external { function test_proxyType() external {
assertEq( assertEq(
uint256(admin.proxyType(address(proxy))), uint256(admin.proxyType(address(proxy))),
uint256(ProxyAdmin.ProxyType.OpenZeppelin) uint256(ProxyAdmin.ProxyType.ERC1967)
); );
assertEq( assertEq(
uint256(admin.proxyType(address(chugsplash))), uint256(admin.proxyType(address(chugsplash))),
...@@ -102,7 +102,7 @@ contract ProxyAdmin_Test is Test { ...@@ -102,7 +102,7 @@ contract ProxyAdmin_Test is Test {
); );
} }
function test_openZeppelinGetProxyImplementation() external { function test_erc1967GetProxyImplementation() external {
getProxyImplementation(proxy); getProxyImplementation(proxy);
} }
...@@ -129,7 +129,7 @@ contract ProxyAdmin_Test is Test { ...@@ -129,7 +129,7 @@ contract ProxyAdmin_Test is Test {
} }
} }
function test_openZeppelinGetProxyAdmin() external { function test_erc1967GetProxyAdmin() external {
getProxyAdmin(proxy); getProxyAdmin(proxy);
} }
...@@ -146,7 +146,7 @@ contract ProxyAdmin_Test is Test { ...@@ -146,7 +146,7 @@ contract ProxyAdmin_Test is Test {
assertEq(owner, address(admin)); assertEq(owner, address(admin));
} }
function test_openZeppelinChangeProxyAdmin() external { function test_erc1967ChangeProxyAdmin() external {
changeProxyAdmin(proxy); changeProxyAdmin(proxy);
} }
...@@ -164,19 +164,26 @@ contract ProxyAdmin_Test is Test { ...@@ -164,19 +164,26 @@ contract ProxyAdmin_Test is Test {
vm.prank(alice); vm.prank(alice);
admin.changeProxyAdmin(_proxy, address(128)); admin.changeProxyAdmin(_proxy, address(128));
// The proxy is not longer the admin and can // The proxy is no longer the admin and can
// no longer call the proxy interface except for // no longer call the proxy interface except for
// the ResolvedDelegate type which anybody can call // the ResolvedDelegate type on which anybody can
// the admin interface // call the admin interface.
if (proxyType != ProxyAdmin.ProxyType.ResolvedDelegate) { if (proxyType == ProxyAdmin.ProxyType.ERC1967) {
vm.expectRevert(); vm.expectRevert("Proxy: implementation not initialized");
admin.getProxyAdmin(_proxy); admin.getProxyAdmin(_proxy);
} else if (proxyType == ProxyAdmin.ProxyType.Chugsplash) {
vm.expectRevert("L1ChugSplashProxy: implementation is not set yet");
admin.getProxyAdmin(_proxy);
} else if (proxyType == ProxyAdmin.ProxyType.ResolvedDelegate) {
// Just an empty block to show that all cases are covered
} else {
vm.expectRevert("ProxyAdmin: unknown proxy type");
} }
// Call the proxy contract directly to get the admin. // Call the proxy contract directly to get the admin.
// Different proxy types have different interfaces. // Different proxy types have different interfaces.
vm.prank(address(128)); vm.prank(address(128));
if (proxyType == ProxyAdmin.ProxyType.OpenZeppelin) { if (proxyType == ProxyAdmin.ProxyType.ERC1967) {
assertEq(_proxy.admin(), address(128)); assertEq(_proxy.admin(), address(128));
} else if (proxyType == ProxyAdmin.ProxyType.Chugsplash) { } else if (proxyType == ProxyAdmin.ProxyType.Chugsplash) {
assertEq( assertEq(
...@@ -193,7 +200,7 @@ contract ProxyAdmin_Test is Test { ...@@ -193,7 +200,7 @@ contract ProxyAdmin_Test is Test {
} }
} }
function test_openZeppelinUpgrade() external { function test_erc1967Upgrade() external {
upgrade(proxy); upgrade(proxy);
} }
...@@ -213,7 +220,7 @@ contract ProxyAdmin_Test is Test { ...@@ -213,7 +220,7 @@ contract ProxyAdmin_Test is Test {
assertEq(impl, address(implementation)); assertEq(impl, address(implementation));
} }
function test_openZeppelinUpgradeAndCall() external { function test_erc1967UpgradeAndCall() external {
upgradeAndCall(proxy); upgradeAndCall(proxy);
} }
......
...@@ -6,25 +6,39 @@ import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol"; ...@@ -6,25 +6,39 @@ import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol";
import { Lib_AddressManager } from "../legacy/Lib_AddressManager.sol"; import { Lib_AddressManager } from "../legacy/Lib_AddressManager.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol"; import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
// Define static interfaces of these proxies so that we can easily
// use staticcall on the getters we need.
interface IStatic_ERC1967Proxy {
function implementation() external view returns (address);
function admin() external view returns (address);
}
interface IStatic_L1ChugSplashProxy {
function getImplementation() external view returns (address);
function getOwner() external view returns (address);
}
/** /**
* @title ProxyAdmin * @title ProxyAdmin
* @dev This is an auxiliary contract meant to be assigned as the admin of a Proxy, based on * @dev This is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy,
* the OpenZeppelin implementation. It has backwards compatibility logic to work with the * based on the OpenZeppelin implementation. It has backwards compatibility logic to work with
* various types of proxies that have been deployed by Optimism. * the various types of proxies that have been deployed by Optimism.
*/ */
contract ProxyAdmin is Owned { contract ProxyAdmin is Owned {
/** /**
* @notice The proxy types that the ProxyAdmin can manage. * @notice The proxy types that the ProxyAdmin can manage.
* *
* @custom:value OpenZeppelin Represents the OpenZeppelin style transparent proxy * @custom:value ERC1967 Represents an ERC1967 compliant transparent proxy
* interface, this is the standard. * interface, this is the default.
* @custom:value Chugsplash Represents the Chugsplash proxy interface, * @custom:value Chugsplash Represents the Chugsplash proxy interface,
* this is legacy. * this is legacy.
* @custom:value ResolvedDelegate Represents the ResolvedDelegate proxy * @custom:value ResolvedDelegate Represents the ResolvedDelegate proxy
* interface, this is legacy. * interface, this is legacy.
*/ */
enum ProxyType { enum ProxyType {
OpenZeppelin, ERC1967,
Chugsplash, Chugsplash,
ResolvedDelegate ResolvedDelegate
} }
...@@ -88,8 +102,8 @@ contract ProxyAdmin is Owned { ...@@ -88,8 +102,8 @@ contract ProxyAdmin is Owned {
* *
* @param _address The address of the address manager. * @param _address The address of the address manager.
*/ */
function setAddressManager(address _address) external onlyOwner { function setAddressManager(Lib_AddressManager _address) external onlyOwner {
addressManager = Lib_AddressManager(_address); addressManager = _address;
} }
/** /**
...@@ -135,28 +149,15 @@ contract ProxyAdmin is Owned { ...@@ -135,28 +149,15 @@ contract ProxyAdmin is Owned {
function getProxyImplementation(Proxy proxy) external view returns (address) { function getProxyImplementation(Proxy proxy) external view returns (address) {
ProxyType proxyType = proxyType[address(proxy)]; ProxyType proxyType = proxyType[address(proxy)];
// We need to manually run the static call since the getter cannot be flagged as view if (proxyType == ProxyType.ERC1967) {
address target; return IStatic_ERC1967Proxy(address(proxy)).implementation();
bytes memory data;
if (proxyType == ProxyType.OpenZeppelin) {
target = address(proxy);
data = abi.encodeWithSelector(Proxy.implementation.selector);
} else if (proxyType == ProxyType.Chugsplash) { } else if (proxyType == ProxyType.Chugsplash) {
target = address(proxy); return IStatic_L1ChugSplashProxy(address(proxy)).getImplementation();
data = abi.encodeWithSelector(L1ChugSplashProxy.getImplementation.selector);
} else if (proxyType == ProxyType.ResolvedDelegate) { } else if (proxyType == ProxyType.ResolvedDelegate) {
target = address(addressManager); return addressManager.getAddress(implementationName[address(proxy)]);
data = abi.encodeWithSelector(
Lib_AddressManager.getAddress.selector,
implementationName[address(proxy)]
);
} else { } else {
revert("ProxyAdmin: unknown proxy type"); revert("ProxyAdmin: unknown proxy type");
} }
(bool success, bytes memory returndata) = target.staticcall(data);
require(success);
return abi.decode(returndata, (address));
} }
/** /**
...@@ -169,25 +170,15 @@ contract ProxyAdmin is Owned { ...@@ -169,25 +170,15 @@ contract ProxyAdmin is Owned {
function getProxyAdmin(Proxy proxy) external view returns (address) { function getProxyAdmin(Proxy proxy) external view returns (address) {
ProxyType proxyType = proxyType[address(proxy)]; ProxyType proxyType = proxyType[address(proxy)];
// We need to manually run the static call since the getter cannot be flagged as view if (proxyType == ProxyType.ERC1967) {
address target; return IStatic_ERC1967Proxy(address(proxy)).admin();
bytes memory data;
if (proxyType == ProxyType.OpenZeppelin) {
target = address(proxy);
data = abi.encodeWithSelector(Proxy.admin.selector);
} else if (proxyType == ProxyType.Chugsplash) { } else if (proxyType == ProxyType.Chugsplash) {
target = address(proxy); return IStatic_L1ChugSplashProxy(address(proxy)).getOwner();
data = abi.encodeWithSelector(L1ChugSplashProxy.getOwner.selector);
} else if (proxyType == ProxyType.ResolvedDelegate) { } else if (proxyType == ProxyType.ResolvedDelegate) {
target = address(addressManager); return addressManager.owner();
data = abi.encodeWithSignature("owner()");
} else { } else {
revert("ProxyAdmin: unknown proxy type"); revert("ProxyAdmin: unknown proxy type");
} }
(bool success, bytes memory returndata) = target.staticcall(data);
require(success);
return abi.decode(returndata, (address));
} }
/** /**
...@@ -200,12 +191,14 @@ contract ProxyAdmin is Owned { ...@@ -200,12 +191,14 @@ contract ProxyAdmin is Owned {
function changeProxyAdmin(Proxy proxy, address newAdmin) external onlyOwner { function changeProxyAdmin(Proxy proxy, address newAdmin) external onlyOwner {
ProxyType proxyType = proxyType[address(proxy)]; ProxyType proxyType = proxyType[address(proxy)];
if (proxyType == ProxyType.OpenZeppelin) { if (proxyType == ProxyType.ERC1967) {
proxy.changeAdmin(newAdmin); proxy.changeAdmin(newAdmin);
} else if (proxyType == ProxyType.Chugsplash) { } else if (proxyType == ProxyType.Chugsplash) {
L1ChugSplashProxy(payable(proxy)).setOwner(newAdmin); L1ChugSplashProxy(payable(proxy)).setOwner(newAdmin);
} else if (proxyType == ProxyType.ResolvedDelegate) { } else if (proxyType == ProxyType.ResolvedDelegate) {
Lib_AddressManager(addressManager).transferOwnership(newAdmin); addressManager.transferOwnership(newAdmin);
} else {
revert("ProxyAdmin: unknown proxy type");
} }
} }
...@@ -218,7 +211,7 @@ contract ProxyAdmin is Owned { ...@@ -218,7 +211,7 @@ contract ProxyAdmin is Owned {
function upgrade(Proxy proxy, address implementation) public onlyOwner { function upgrade(Proxy proxy, address implementation) public onlyOwner {
ProxyType proxyType = proxyType[address(proxy)]; ProxyType proxyType = proxyType[address(proxy)];
if (proxyType == ProxyType.OpenZeppelin) { if (proxyType == ProxyType.ERC1967) {
proxy.upgradeTo(implementation); proxy.upgradeTo(implementation);
} else if (proxyType == ProxyType.Chugsplash) { } else if (proxyType == ProxyType.Chugsplash) {
L1ChugSplashProxy(payable(proxy)).setStorage( L1ChugSplashProxy(payable(proxy)).setStorage(
...@@ -227,7 +220,9 @@ contract ProxyAdmin is Owned { ...@@ -227,7 +220,9 @@ contract ProxyAdmin is Owned {
); );
} else if (proxyType == ProxyType.ResolvedDelegate) { } else if (proxyType == ProxyType.ResolvedDelegate) {
string memory name = implementationName[address(proxy)]; string memory name = implementationName[address(proxy)];
Lib_AddressManager(addressManager).setAddress(name, implementation); addressManager.setAddress(name, implementation);
} else {
revert("ProxyAdmin: unknown proxy type");
} }
} }
...@@ -246,9 +241,10 @@ contract ProxyAdmin is Owned { ...@@ -246,9 +241,10 @@ contract ProxyAdmin is Owned {
) external payable onlyOwner { ) external payable onlyOwner {
ProxyType proxyType = proxyType[address(proxy)]; ProxyType proxyType = proxyType[address(proxy)];
if (proxyType == ProxyType.OpenZeppelin) { if (proxyType == ProxyType.ERC1967) {
proxy.upgradeToAndCall{ value: msg.value }(implementation, data); proxy.upgradeToAndCall{ value: msg.value }(implementation, data);
} else { } else {
// reverts if proxy type is unknown
upgrade(proxy, implementation); upgrade(proxy, implementation);
(bool success, ) = address(proxy).call{ value: msg.value }(data); (bool success, ) = address(proxy).call{ value: msg.value }(data);
require(success); require(success);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment