-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
4 changed files
with
170 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@chugsplash/contracts': minor | ||
--- | ||
|
||
Adds the ProxyAdmin, which owns the proxies for a project. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,169 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.9; | ||
|
||
import { ChugSplashRegistry } from "./ChugSplashRegistry.sol"; | ||
import { Proxy } from "@eth-optimism/contracts-bedrock/contracts/universal/Proxy.sol"; | ||
|
||
/** | ||
* @title ProxyAdmin | ||
* @notice The ProxyAdmin is a contract associated with each ChugSplashManager that owns the various | ||
* proxies for a given project. The ProxyAdmin delegatecalls into various ProxyAdapter | ||
* contracts that correspond to different proxy types. Using this pattern, the ProxyAdmin | ||
* can universally handle all different proxy types as long as the ProxyAdmin is considered | ||
* the owning address of each proxy. | ||
*/ | ||
contract ProxyAdmin { | ||
function getProxyImplementation(address _proxy, bytes32 _proxyType) | ||
public | ||
returns (address implementation) | ||
{} | ||
/** | ||
* @notice The storage slot that holds the address of the implementation contract for default | ||
* proxies. | ||
* bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) | ||
*/ | ||
bytes32 internal constant EIP1967_IMPLEMENTATION_KEY = | ||
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; | ||
|
||
function upgrade( | ||
address _proxy, | ||
bytes32 _proxyType, | ||
address _implementation | ||
) public {} | ||
/** | ||
* @notice Address of the ChugSplashRegistry. | ||
*/ | ||
ChugSplashRegistry public immutable registry; | ||
|
||
/** | ||
* @notice Address of the ProxyUpdater. | ||
*/ | ||
address public immutable proxyUpdater; | ||
|
||
/** | ||
* @param _registry Address of the ChugSplashRegistry. | ||
* @param _proxyUpdater Address of the ProxyUpdater. | ||
*/ | ||
constructor(ChugSplashRegistry _registry, address _proxyUpdater) { | ||
registry = _registry; | ||
proxyUpdater = _proxyUpdater; | ||
} | ||
|
||
/** | ||
* @notice Sets new code for the proxy contract's implementation. | ||
* | ||
* @param _proxy Address of the proxy, which can be a default proxy or a non-standard proxy. | ||
* @param _proxyType The proxy's type. This is the zero-address for default proxies. | ||
* @param _code Creation bytecode to be deployed. | ||
*/ | ||
function setProxyCode( | ||
address _proxy, | ||
address payable _proxy, | ||
bytes32 _proxyType, | ||
bytes memory _data | ||
) public {} | ||
bytes memory _code | ||
) public { | ||
// If no proxy type has been set, use the default proxy. | ||
if (_proxyType == bytes32(0)) { | ||
// Upgrade the proxy's implementation to be the ProxyUpdater, and call `setCode` on the | ||
// proxy. | ||
Proxy(_proxy).upgradeToAndCall( | ||
proxyUpdater, | ||
abi.encodeWithSignature("setCode(bytes32,bytes)", EIP1967_IMPLEMENTATION_KEY, _code) | ||
); | ||
} else { | ||
// If a proxy type has been specified for this action, then the ProxyAdmin delegatecalls | ||
// the corresponding adapter, which contains the logic that will be used to call the | ||
// proxy from the context of this contract. | ||
|
||
// Get the adapter that corresponds to this proxy type. | ||
address adapter = registry.adapters(_proxyType); | ||
require(adapter != address(0), "ProxyAdmin: proxy type has no adapter"); | ||
|
||
// Delegatecall the adapter to upgrade the proxy's implementation to be the | ||
// ProxyUpdater, which has the `setCode` function. | ||
_upgradeWithAdapter(_proxy, adapter, proxyUpdater); | ||
|
||
// Delegatecall the adapter, which in turn will call the proxy to trigger a `setCode` | ||
// action. | ||
(bool success, ) = adapter.delegatecall( | ||
abi.encodeWithSignature("setProxyCode(address,bytes)", _proxy, _code) | ||
); | ||
require(success, "ProxyAdmin: delegatecall to set proxy code failed"); | ||
} | ||
} | ||
|
||
/** | ||
* @notice Modifies a storage slot within the proxy contract. | ||
* | ||
* @param _proxy Address of the proxy, which can be a default proxy or a non-standard proxy. | ||
* @param _proxyType The proxy's type. This is the zero-address for default proxies. | ||
* @param _key Storage key to modify. | ||
* @param _value New value for the storage key. | ||
*/ | ||
function setProxyStorage( | ||
address _proxy, | ||
address payable _proxy, | ||
bytes32 _proxyType, | ||
bytes32 _key, | ||
bytes32 _value | ||
) public {} | ||
) public { | ||
// If no proxy type has been set, use the default proxy. | ||
if (_proxyType == bytes32(0)) { | ||
// Upgrade the proxy's implementation to be the ProxyUpdater, and call `setStorage` on | ||
// the proxy. | ||
Proxy(_proxy).upgradeToAndCall( | ||
proxyUpdater, | ||
abi.encodeWithSignature("setStorage(bytes32,bytes32)", _key, _value) | ||
); | ||
} else { | ||
// If a proxy type has been specified for this action, then the ProxyAdmin delegatecalls | ||
// the corresponding adapter, which contains the logic that will be used to call the | ||
// proxy from the context of this contract. | ||
|
||
// Get the adapter that corresponds to this proxy type. | ||
address adapter = registry.adapters(_proxyType); | ||
require(adapter != address(0), "ProxyAdmin: proxy type has no adapter"); | ||
|
||
// Get the address of the current implementation for the proxy. The ProxyAdmin will set | ||
// the proxy's implementation back to this address after setting it to be the | ||
// ProxyUpdater and calling `setStorage`. | ||
(bool success, bytes memory implementationBytes) = adapter.delegatecall( | ||
abi.encodeWithSignature("getProxyImplementation()") | ||
); | ||
require(success, "ProxyAdmin: delegatecall to get proxy implementation failed"); | ||
|
||
// Delegatecall the adapter to upgrade the proxy's implementation to be the | ||
// ProxyUpdater, which has the `setStorage` function. | ||
_upgradeWithAdapter(_proxy, adapter, proxyUpdater); | ||
|
||
// Delegatecall the adapter, which in turn will call the proxy to trigger a `setStorage` | ||
// action. | ||
(bool setStorageSuccess, ) = adapter.delegatecall( | ||
abi.encodeWithSignature( | ||
"setProxyStorage(address,bytes32,bytes32)", | ||
_proxy, | ||
_key, | ||
_value | ||
) | ||
); | ||
require(setStorageSuccess, "ProxyAdmin: delegatecall to set proxy storage failed"); | ||
|
||
// Convert the implementation's type from bytes to address. | ||
address implementation; | ||
assembly { | ||
implementation := mload(add(implementationBytes, 32)) | ||
} | ||
|
||
// Delegatecall the adapter to set the proxy's implementation back to its original | ||
// address. | ||
_upgradeWithAdapter(_proxy, adapter, implementation); | ||
} | ||
} | ||
|
||
/** | ||
* @notice Delegatecalls an adapter to upgrade a non-standard proxy's implementation contract. | ||
* | ||
* @param _proxy Address of the non-standard proxy. | ||
* @param _adapter Address of the adapter to use for the proxy. | ||
* @param _implementation Address to set as the proxy's new implementation contract. | ||
*/ | ||
function _upgradeWithAdapter( | ||
address _proxy, | ||
address _adapter, | ||
address _implementation | ||
) internal { | ||
(bool success, ) = _adapter.delegatecall( | ||
abi.encodeWithSignature("upgradeProxyTo(address,address)", _proxy, _implementation) | ||
); | ||
require(success, "ProxyAdmin: delegatecall to upgrade proxy failed"); | ||
} | ||
} |