ProxyAdmin.sol 9.27 KB
Newer Older
1
// SPDX-License-Identifier: MIT
2
pragma solidity 0.8.15;
3 4

import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol";
5
import { Proxy } from "./Proxy.sol";
6
import { AddressManager } from "../legacy/AddressManager.sol";
7 8
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";

9 10 11 12 13
/**
 * @title IStaticERC1967Proxy
 * @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface.
 */
interface IStaticERC1967Proxy {
14 15 16 17 18
    function implementation() external view returns (address);

    function admin() external view returns (address);
}

19 20 21 22 23
/**
 * @title IStaticL1ChugSplashProxy
 * @notice IStaticL1ChugSplashProxy is a static version of the ChugSplash proxy interface.
 */
interface IStaticL1ChugSplashProxy {
24 25 26 27 28
    function getImplementation() external view returns (address);

    function getOwner() external view returns (address);
}

29 30
/**
 * @title ProxyAdmin
31 32 33
 * @notice This is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy,
 *         based on the OpenZeppelin implementation. It has backwards compatibility logic to work
 *         with the various types of proxies that have been deployed by Optimism in the past.
34 35 36 37 38
 */
contract ProxyAdmin is Owned {
    /**
     * @notice The proxy types that the ProxyAdmin can manage.
     *
39 40 41
     * @custom:value ERC1967    Represents an ERC1967 compliant transparent proxy interface.
     * @custom:value CHUGSPLASH Represents the Chugsplash proxy interface (legacy).
     * @custom:value RESOLVED   Represents the ResolvedDelegate proxy (legacy).
42 43
     */
    enum ProxyType {
44
        ERC1967,
45 46
        CHUGSPLASH,
        RESOLVED
47 48 49 50
    }

    /**
     * @custom:legacy
51
     * @notice A mapping of proxy types, used for backwards compatibility.
52 53 54 55 56 57 58
     */
    mapping(address => ProxyType) public proxyType;

    /**
     * @custom:legacy
     * @notice A reverse mapping of addresses to names held in the AddressManager. This must be
     *         manually kept up to date with changes in the AddressManager for this contract
59
     *         to be able to work as an admin for the ResolvedDelegateProxy type.
60 61 62 63 64 65
     */
    mapping(address => string) public implementationName;

    /**
     * @custom:legacy
     * @notice The address of the address manager, this is required to manage the
66
     *         ResolvedDelegateProxy type.
67
     */
68
    AddressManager public addressManager;
69 70 71 72 73 74 75 76

    /**
     * @custom:legacy
     * @notice A legacy upgrading indicator used by the old Chugsplash Proxy.
     */
    bool internal upgrading = false;

    /**
77
     * @param _owner Address of the initial owner of this contract.
78
     */
79
    constructor(address _owner) Owned(_owner) {}
80 81

    /**
82 83
     * @notice Sets the proxy type for a given address. Only required for non-standard (legacy)
     *         proxy types.
84
     *
85 86
     * @param _address Address of the proxy.
     * @param _type    Type of the proxy.
87 88 89 90 91 92
     */
    function setProxyType(address _address, ProxyType _type) external onlyOwner {
        proxyType[_address] = _type;
    }

    /**
93 94
     * @notice Sets the implementation name for a given address. Only required for
     *         ResolvedDelegateProxy type proxies that have an implementation name.
95
     *
96 97
     * @param _address Address of the ResolvedDelegateProxy.
     * @param _name    Name of the implementation for the proxy.
98 99 100 101 102 103
     */
    function setImplementationName(address _address, string memory _name) external onlyOwner {
        implementationName[_address] = _name;
    }

    /**
104 105
     * @notice Set the address of the AddressManager. This is required to manage legacy
     *         ResolvedDelegateProxy type proxy contracts.
106
     *
107
     * @param _address Address of the AddressManager.
108
     */
109
    function setAddressManager(AddressManager _address) external onlyOwner {
110
        addressManager = _address;
111 112 113 114
    }

    /**
     * @custom:legacy
115 116 117
     * @notice Set an address in the address manager. Since only the owner of the AddressManager
     *         can directly modify addresses and the ProxyAdmin will own the AddressManager, this
     *         gives the owner of the ProxyAdmin the ability to modify addresses directly.
118
     *
119 120
     * @param _name    Name to set within the AddressManager.
     * @param _address Address to attach to the given name.
121 122 123 124 125 126 127
     */
    function setAddress(string memory _name, address _address) external onlyOwner {
        addressManager.setAddress(_name, _address);
    }

    /**
     * @custom:legacy
128
     * @notice Set the upgrading status for the Chugsplash proxy type.
129
     *
130
     * @param _upgrading Whether or not the system is upgrading.
131
     */
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    function setUpgrading(bool _upgrading) external onlyOwner {
        upgrading = _upgrading;
    }

    /**
     * @notice Updates the admin of the given proxy address.
     *
     * @param _proxy    Address of the proxy to update.
     * @param _newAdmin Address of the new proxy admin.
     */
    function changeProxyAdmin(address payable _proxy, address _newAdmin) external onlyOwner {
        ProxyType ptype = proxyType[_proxy];
        if (ptype == ProxyType.ERC1967) {
            Proxy(_proxy).changeAdmin(_newAdmin);
        } else if (ptype == ProxyType.CHUGSPLASH) {
            L1ChugSplashProxy(_proxy).setOwner(_newAdmin);
        } else if (ptype == ProxyType.RESOLVED) {
            addressManager.transferOwnership(_newAdmin);
        } else {
            revert("ProxyAdmin: unknown proxy type");
        }
    }

    /**
     * @notice Changes a proxy's implementation contract and delegatecalls the new implementation
     *         with some given data. Useful for atomic upgrade-and-initialize calls.
     *
     * @param _proxy          Address of the proxy to upgrade.
     * @param _implementation Address of the new implementation address.
     * @param _data           Data to trigger the new implementation with.
     */
    function upgradeAndCall(
        address payable _proxy,
        address _implementation,
        bytes memory _data
    ) external payable onlyOwner {
        ProxyType ptype = proxyType[_proxy];
        if (ptype == ProxyType.ERC1967) {
            Proxy(_proxy).upgradeToAndCall{ value: msg.value }(_implementation, _data);
        } else {
            // reverts if proxy type is unknown
            upgrade(_proxy, _implementation);
            (bool success, ) = _proxy.call{ value: msg.value }(_data);
            require(success, "ProxyAdmin: call to proxy after upgrade failed");
        }
177 178 179 180
    }

    /**
     * @custom:legacy
181
     * @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening.
182
     *
183 184 185
     * @return Whether or not there is an upgrade going on. May not actually tell you whether an
     *         upgrade is going on, since we don't currently plan to use this variable for anything
     *         other than a legacy indicator to fix a UX bug in the ChugSplash proxy.
186
     */
187 188
    function isUpgrading() external view returns (bool) {
        return upgrading;
189 190 191
    }

    /**
192
     * @notice Returns the implementation of the given proxy address.
193
     *
194 195 196
     * @param _proxy Address of the proxy to get the implementation of.
     *
     * @return Address of the implementation of the proxy.
197
     */
198 199 200 201
    function getProxyImplementation(address _proxy) external view returns (address) {
        ProxyType ptype = proxyType[_proxy];
        if (ptype == ProxyType.ERC1967) {
            return IStaticERC1967Proxy(_proxy).implementation();
202
        } else if (ptype == ProxyType.CHUGSPLASH) {
203
            return IStaticL1ChugSplashProxy(_proxy).getImplementation();
204
        } else if (ptype == ProxyType.RESOLVED) {
205
            return addressManager.getAddress(implementationName[_proxy]);
206 207 208 209 210 211
        } else {
            revert("ProxyAdmin: unknown proxy type");
        }
    }

    /**
212 213 214
     * @notice Returns the admin of the given proxy address.
     *
     * @param _proxy Address of the proxy to get the admin of.
215
     *
216
     * @return Address of the admin of the proxy.
217
     */
218 219 220 221
    function getProxyAdmin(address payable _proxy) external view returns (address) {
        ProxyType ptype = proxyType[_proxy];
        if (ptype == ProxyType.ERC1967) {
            return IStaticERC1967Proxy(_proxy).admin();
222
        } else if (ptype == ProxyType.CHUGSPLASH) {
223
            return IStaticL1ChugSplashProxy(_proxy).getOwner();
224
        } else if (ptype == ProxyType.RESOLVED) {
225
            return addressManager.owner();
226 227 228 229 230 231
        } else {
            revert("ProxyAdmin: unknown proxy type");
        }
    }

    /**
232
     * @notice Changes a proxy's implementation contract.
233
     *
234 235
     * @param _proxy          Address of the proxy to upgrade.
     * @param _implementation Address of the new implementation address.
236
     */
237 238 239 240
    function upgrade(address payable _proxy, address _implementation) public onlyOwner {
        ProxyType ptype = proxyType[_proxy];
        if (ptype == ProxyType.ERC1967) {
            Proxy(_proxy).upgradeTo(_implementation);
241
        } else if (ptype == ProxyType.CHUGSPLASH) {
242
            L1ChugSplashProxy(_proxy).setStorage(
243
                0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
244
                bytes32(uint256(uint160(_implementation)))
245
            );
246
        } else if (ptype == ProxyType.RESOLVED) {
247
            string memory name = implementationName[_proxy];
248
            addressManager.setAddress(name, _implementation);
249 250
        } else {
            revert("ProxyAdmin: unknown proxy type");
251 252 253
        }
    }
}