forked from ethereum-optimism/optimism
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ProxyAdmin.sol
254 lines (232 loc) · 9.41 KB
/
ProxyAdmin.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { Proxy } from "./Proxy.sol";
import { AddressManager } from "../legacy/AddressManager.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
/**
* @title IStaticERC1967Proxy
* @notice IStaticERC1967Proxy is a static version of the ERC1967 proxy interface.
*/
interface IStaticERC1967Proxy {
function implementation() external view returns (address);
function admin() external view returns (address);
}
/**
* @title IStaticL1ChugSplashProxy
* @notice IStaticL1ChugSplashProxy is a static version of the ChugSplash proxy interface.
*/
interface IStaticL1ChugSplashProxy {
function getImplementation() external view returns (address);
function getOwner() external view returns (address);
}
/**
* @title ProxyAdmin
* @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.
*/
contract ProxyAdmin is Ownable {
/**
* @notice The proxy types that the ProxyAdmin can manage.
*
* @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).
*/
enum ProxyType {
ERC1967,
CHUGSPLASH,
RESOLVED
}
/**
* @notice A mapping of proxy types, used for backwards compatibility.
*/
mapping(address => ProxyType) public proxyType;
/**
* @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
* to be able to work as an admin for the ResolvedDelegateProxy type.
*/
mapping(address => string) public implementationName;
/**
* @notice The address of the address manager, this is required to manage the
* ResolvedDelegateProxy type.
*/
AddressManager public addressManager;
/**
* @notice A legacy upgrading indicator used by the old Chugsplash Proxy.
*/
bool internal upgrading;
/**
* @param _owner Address of the initial owner of this contract.
*/
constructor(address _owner) Ownable() {
_transferOwnership(_owner);
}
/**
* @notice Sets the proxy type for a given address. Only required for non-standard (legacy)
* proxy types.
*
* @param _address Address of the proxy.
* @param _type Type of the proxy.
*/
function setProxyType(address _address, ProxyType _type) external onlyOwner {
proxyType[_address] = _type;
}
/**
* @notice Sets the implementation name for a given address. Only required for
* ResolvedDelegateProxy type proxies that have an implementation name.
*
* @param _address Address of the ResolvedDelegateProxy.
* @param _name Name of the implementation for the proxy.
*/
function setImplementationName(address _address, string memory _name) external onlyOwner {
implementationName[_address] = _name;
}
/**
* @notice Set the address of the AddressManager. This is required to manage legacy
* ResolvedDelegateProxy type proxy contracts.
*
* @param _address Address of the AddressManager.
*/
function setAddressManager(AddressManager _address) external onlyOwner {
addressManager = _address;
}
/**
* @custom:legacy
* @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.
*
* @param _name Name to set within the AddressManager.
* @param _address Address to attach to the given name.
*/
function setAddress(string memory _name, address _address) external onlyOwner {
addressManager.setAddress(_name, _address);
}
/**
* @custom:legacy
* @notice Set the upgrading status for the Chugsplash proxy type.
*
* @param _upgrading Whether or not the system is upgrading.
*/
function setUpgrading(bool _upgrading) external onlyOwner {
upgrading = _upgrading;
}
/**
* @custom:legacy
* @notice Legacy function used to tell ChugSplashProxy contracts if an upgrade is happening.
*
* @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.
*/
function isUpgrading() external view returns (bool) {
return upgrading;
}
/**
* @notice Returns the implementation of the given proxy address.
*
* @param _proxy Address of the proxy to get the implementation of.
*
* @return Address of the implementation of the proxy.
*/
function getProxyImplementation(address _proxy) external view returns (address) {
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
return IStaticERC1967Proxy(_proxy).implementation();
} else if (ptype == ProxyType.CHUGSPLASH) {
return IStaticL1ChugSplashProxy(_proxy).getImplementation();
} else if (ptype == ProxyType.RESOLVED) {
return addressManager.getAddress(implementationName[_proxy]);
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
/**
* @notice Returns the admin of the given proxy address.
*
* @param _proxy Address of the proxy to get the admin of.
*
* @return Address of the admin of the proxy.
*/
function getProxyAdmin(address payable _proxy) external view returns (address) {
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
return IStaticERC1967Proxy(_proxy).admin();
} else if (ptype == ProxyType.CHUGSPLASH) {
return IStaticL1ChugSplashProxy(_proxy).getOwner();
} else if (ptype == ProxyType.RESOLVED) {
return addressManager.owner();
} else {
revert("ProxyAdmin: unknown proxy type");
}
}
/**
* @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.
*
* @param _proxy Address of the proxy to upgrade.
* @param _implementation Address of the new implementation address.
*/
function upgrade(address payable _proxy, address _implementation) public onlyOwner {
ProxyType ptype = proxyType[_proxy];
if (ptype == ProxyType.ERC1967) {
Proxy(_proxy).upgradeTo(_implementation);
} else if (ptype == ProxyType.CHUGSPLASH) {
L1ChugSplashProxy(_proxy).setStorage(
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
bytes32(uint256(uint160(_implementation)))
);
} else if (ptype == ProxyType.RESOLVED) {
string memory name = implementationName[_proxy];
addressManager.setAddress(name, _implementation);
} else {
// It should not be possible to retrieve a ProxyType value which is not matched by
// one of the previous conditions.
assert(false);
}
}
/**
* @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");
}
}
}