-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(op-node): add DeployUpgrades.s.sol
- Loading branch information
Showing
3 changed files
with
375 additions
and
0 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
363 changes: 363 additions & 0 deletions
363
packages/contracts-bedrock/scripts/DeployUpgrades.s.sol
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,363 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { VmSafe } from "forge-std/Vm.sol"; | ||
import { Script } from "forge-std/Script.sol"; | ||
|
||
import { console2 as console } from "forge-std/console2.sol"; | ||
import { stdJson } from "forge-std/StdJson.sol"; | ||
|
||
import { Deployer } from "scripts/Deployer.sol"; | ||
|
||
import { Proxy } from "src/universal/Proxy.sol"; | ||
import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; | ||
import { StandardBridge } from "src/universal/StandardBridge.sol"; | ||
import { OptimismPortal } from "src/L1/OptimismPortal.sol"; | ||
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; | ||
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; | ||
import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; | ||
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; | ||
import { SystemConfig } from "src/L1/SystemConfig.sol"; | ||
import { ResourceMetering } from "src/L1/ResourceMetering.sol"; | ||
import { Constants } from "src/libraries/Constants.sol"; | ||
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; | ||
import { ProtocolVersions, ProtocolVersion } from "src/L1/ProtocolVersions.sol"; | ||
import { StorageSetter } from "src/universal/StorageSetter.sol"; | ||
import { Predeploys } from "src/libraries/Predeploys.sol"; | ||
import { Chains } from "scripts/Chains.sol"; | ||
import { Config } from "scripts/Config.sol"; | ||
|
||
import { ChainAssertions } from "scripts/ChainAssertions.sol"; | ||
import { Types } from "scripts/Types.sol"; | ||
import { LibStateDiff } from "scripts/libraries/LibStateDiff.sol"; | ||
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; | ||
import { ForgeArtifacts } from "scripts/ForgeArtifacts.sol"; | ||
import { Process } from "scripts/libraries/Process.sol"; | ||
|
||
/// @title Deploy | ||
/// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function. | ||
/// To add a new contract to the system, add a public function that deploys that individual contract. | ||
/// Then add a call to that function inside of `run`. Be sure to call the `save` function after each | ||
/// deployment so that hardhat-deploy style artifacts can be generated using a call to `sync()`. | ||
/// The `CONTRACT_ADDRESSES_PATH` environment variable can be set to a path that contains a JSON file full of | ||
/// contract name to address pairs. That enables this script to be much more flexible in the way it is used. | ||
/// This contract must not have constructor logic because it is set into state using `etch`. | ||
contract Deploy is Deployer { | ||
using stdJson for string; | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// Modifiers // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Modifier that wraps a function in broadcasting. | ||
modifier broadcast() { | ||
vm.startBroadcast(msg.sender); | ||
_; | ||
vm.stopBroadcast(); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// Accessors // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice The create2 salt used for deployment of the contract implementations. | ||
/// Using this helps to reduce config across networks as the implementation | ||
/// addresses will be the same across networks when deployed with create2. | ||
function _implSalt() internal view returns (bytes32) { | ||
return keccak256(bytes(Config.implSalt())); | ||
} | ||
|
||
/// @notice Returns the proxy addresses, not reverting if any are unset. | ||
function _proxiesUnstrict() internal view returns (Types.ContractSet memory proxies_) { | ||
proxies_ = Types.ContractSet({ | ||
L1CrossDomainMessenger: getAddress("L1CrossDomainMessengerProxy"), | ||
L1StandardBridge: getAddress("L1StandardBridgeProxy"), | ||
L2OutputOracle: getAddress("L2OutputOracleProxy"), | ||
DisputeGameFactory: getAddress("DisputeGameFactoryProxy"), | ||
DelayedWETH: getAddress("DelayedWETHProxy"), | ||
AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"), | ||
OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"), | ||
OptimismPortal: getAddress("OptimismPortalProxy"), | ||
OptimismPortal2: getAddress("OptimismPortalProxy"), | ||
SystemConfig: getAddress("SystemConfigProxy"), | ||
L1ERC721Bridge: getAddress("L1ERC721BridgeProxy"), | ||
ProtocolVersions: getAddress("ProtocolVersionsProxy"), | ||
SuperchainConfig: getAddress("SuperchainConfigProxy") | ||
}); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// SetUp and Run // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Deploy all of the L1 contracts necessary for a full Superchain with a single Op Chain. | ||
function run() public { | ||
console.log("Deploying a fresh OP Stack including SuperchainConfig"); | ||
_run(); | ||
} | ||
|
||
/// @notice Internal function containing the deploy logic. | ||
function _run() internal virtual { | ||
console.log("start of L1 Deploy!"); | ||
setupSuperchain(); | ||
console.log("set up superchain!"); | ||
setupOpChain(); | ||
console.log("set up op chain!"); | ||
deployStorageSetter(); | ||
console.log("set up StorageSetter!"); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// High Level Deployment Functions // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Deploy a full system with a new SuperchainConfig | ||
/// The Superchain system has 2 singleton contracts which lie outside of an OP Chain: | ||
/// 1. The SuperchainConfig contract | ||
/// 2. The ProtocolVersions contract | ||
function setupSuperchain() public { | ||
console.log("Setting up Superchain"); | ||
deployERC1967Proxy("SuperchainConfigProxy"); | ||
deploySuperchainConfig(); | ||
} | ||
|
||
/// @notice Deploy a new OP Chain, with an existing SuperchainConfig provided | ||
function setupOpChain() public { | ||
console.log("Deploying OP Chain"); | ||
|
||
// Ensure that the requisite contracts are deployed | ||
mustGetAddress("SuperchainConfigProxy"); | ||
deployImplementations(); | ||
} | ||
|
||
/// @notice Deploy all of the implementations | ||
function deployImplementations() public { | ||
console.log("Deploying implementations"); | ||
deployL1CrossDomainMessenger(); | ||
deployOptimismMintableERC20Factory(); | ||
deploySystemConfig(); | ||
deployL1StandardBridge(); | ||
deployL1ERC721Bridge(); | ||
deployOptimismPortal(); | ||
deployL2OutputOracle(); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// Non-Proxied Deployment Functions // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
|
||
/// @notice Deploy the StorageSetter contract, used for upgrades. | ||
function deployStorageSetter() public broadcast returns (address addr_) { | ||
console.log("Deploying StorageSetter"); | ||
StorageSetter setter = new StorageSetter{ salt: _implSalt() }(); | ||
console.log("StorageSetter deployed at: %s", address(setter)); | ||
string memory version = setter.version(); | ||
console.log("StorageSetter version: %s", version); | ||
save("StorageSetter", address(setter)); | ||
addr_ = address(setter); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// Proxy Deployment Functions // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Deploys an ERC1967Proxy contract with the ProxyAdmin as the owner. | ||
/// @param _name The name of the proxy contract to be deployed. | ||
/// @return addr_ The address of the deployed proxy contract. | ||
function deployERC1967Proxy(string memory _name) public returns (address addr_) { | ||
uint256 chainid = block.chainid; | ||
address proxyAdmin; | ||
if (chainid == Chains.BscTestnet) { | ||
proxyAdmin = Constants.BSCTESTNET_PROXY_ADMIN; | ||
} else if (chainid == Chains.BscMainnet) { | ||
proxyAdmin = Constants.BSCMAINNET_PROXY_ADMIN; | ||
} else if (chainid == Chains.BscQAnet) { | ||
proxyAdmin = Constants.BSCQANET_PROXY_ADMIN; | ||
} | ||
console.log("proxyAdmin is %s", proxyAdmin); | ||
addr_ = deployERC1967ProxyWithOwner(_name, proxyAdmin); | ||
} | ||
|
||
/// @notice Deploys an ERC1967Proxy contract with a specified owner. | ||
/// @param _name The name of the proxy contract to be deployed. | ||
/// @param _proxyOwner The address of the owner of the proxy contract. | ||
/// @return addr_ The address of the deployed proxy contract. | ||
function deployERC1967ProxyWithOwner( | ||
string memory _name, | ||
address _proxyOwner | ||
) | ||
public | ||
broadcast | ||
returns (address addr_) | ||
{ | ||
console.log(string.concat("Deploying ERC1967 proxy for ", _name)); | ||
Proxy proxy = new Proxy({ _admin: _proxyOwner }); | ||
|
||
require(EIP1967Helper.getAdmin(address(proxy)) == _proxyOwner); | ||
|
||
save(_name, address(proxy)); | ||
console.log(" at %s", address(proxy)); | ||
addr_ = address(proxy); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////// | ||
// Implementation Deployment Functions // | ||
//////////////////////////////////////////////////////////////// | ||
|
||
/// @notice Deploy the SuperchainConfig contract | ||
function deploySuperchainConfig() public broadcast { | ||
SuperchainConfig superchainConfig = new SuperchainConfig{ salt: _implSalt() }(); | ||
|
||
require(superchainConfig.guardian() == address(0)); | ||
bytes32 initialized = vm.load(address(superchainConfig), bytes32(0)); | ||
require(initialized != 0); | ||
|
||
save("SuperchainConfig", address(superchainConfig)); | ||
console.log("SuperchainConfig deployed at %s", address(superchainConfig)); | ||
} | ||
|
||
/// @notice Deploy the L1CrossDomainMessenger | ||
function deployL1CrossDomainMessenger() public broadcast returns (address addr_) { | ||
console.log("Deploying L1CrossDomainMessenger implementation"); | ||
L1CrossDomainMessenger messenger = new L1CrossDomainMessenger{ salt: _implSalt() }(); | ||
|
||
save("L1CrossDomainMessenger", address(messenger)); | ||
console.log("L1CrossDomainMessenger deployed at %s", address(messenger)); | ||
|
||
// Override the `L1CrossDomainMessenger` contract to the deployed implementation. This is necessary | ||
// to check the `L1CrossDomainMessenger` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.L1CrossDomainMessenger = address(messenger); | ||
ChainAssertions.checkL1CrossDomainMessenger({ _contracts: contracts, _vm: vm, _isProxy: false }); | ||
|
||
addr_ = address(messenger); | ||
} | ||
|
||
/// @notice Deploy the OptimismPortal | ||
function deployOptimismPortal() public broadcast returns (address addr_) { | ||
console.log("Deploying OptimismPortal implementation"); | ||
addr_ = address(new OptimismPortal{ salt: _implSalt() }()); | ||
save("OptimismPortal", addr_); | ||
console.log("OptimismPortal deployed at %s", addr_); | ||
|
||
// Override the `OptimismPortal` contract to the deployed implementation. This is necessary | ||
// to check the `OptimismPortal` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.OptimismPortal = addr_; | ||
ChainAssertions.checkOptimismPortal({ _contracts: contracts, _cfg: cfg, _isProxy: false }); | ||
} | ||
|
||
/// @notice Deploy the L2OutputOracle | ||
function deployL2OutputOracle() public broadcast returns (address addr_) { | ||
console.log("Deploying L2OutputOracle implementation"); | ||
L2OutputOracle oracle = new L2OutputOracle{ salt: _implSalt() }(); | ||
|
||
save("L2OutputOracle", address(oracle)); | ||
console.log("L2OutputOracle deployed at %s", address(oracle)); | ||
|
||
// Override the `L2OutputOracle` contract to the deployed implementation. This is necessary | ||
// to check the `L2OutputOracle` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.L2OutputOracle = address(oracle); | ||
ChainAssertions.checkL2OutputOracle({ | ||
_contracts: contracts, | ||
_cfg: cfg, | ||
_l2OutputOracleStartingTimestamp: 0, | ||
_isProxy: false | ||
}); | ||
|
||
addr_ = address(oracle); | ||
} | ||
|
||
/// @notice Deploy the OptimismMintableERC20Factory | ||
function deployOptimismMintableERC20Factory() public broadcast returns (address addr_) { | ||
console.log("Deploying OptimismMintableERC20Factory implementation"); | ||
OptimismMintableERC20Factory factory = new OptimismMintableERC20Factory{ salt: _implSalt() }(); | ||
|
||
save("OptimismMintableERC20Factory", address(factory)); | ||
console.log("OptimismMintableERC20Factory deployed at %s", address(factory)); | ||
|
||
// Override the `OptimismMintableERC20Factory` contract to the deployed implementation. This is necessary | ||
// to check the `OptimismMintableERC20Factory` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.OptimismMintableERC20Factory = address(factory); | ||
ChainAssertions.checkOptimismMintableERC20Factory({ _contracts: contracts, _isProxy: false }); | ||
|
||
addr_ = address(factory); | ||
} | ||
|
||
/// @notice Deploy the ProtocolVersions | ||
function deployProtocolVersions() public broadcast returns (address addr_) { | ||
console.log("Deploying ProtocolVersions implementation"); | ||
ProtocolVersions versions = new ProtocolVersions{ salt: _implSalt() }(); | ||
save("ProtocolVersions", address(versions)); | ||
console.log("ProtocolVersions deployed at %s", address(versions)); | ||
|
||
// Override the `ProtocolVersions` contract to the deployed implementation. This is necessary | ||
// to check the `ProtocolVersions` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.ProtocolVersions = address(versions); | ||
ChainAssertions.checkProtocolVersions({ _contracts: contracts, _cfg: cfg, _isProxy: false }); | ||
|
||
addr_ = address(versions); | ||
} | ||
|
||
/// @notice Deploy the SystemConfig | ||
function deploySystemConfig() public broadcast returns (address addr_) { | ||
console.log("Deploying SystemConfig implementation"); | ||
addr_ = address(new SystemConfig{ salt: _implSalt() }()); | ||
save("SystemConfig", addr_); | ||
console.log("SystemConfig deployed at %s", addr_); | ||
|
||
// Override the `SystemConfig` contract to the deployed implementation. This is necessary | ||
// to check the `SystemConfig` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.SystemConfig = addr_; | ||
ChainAssertions.checkSystemConfig({ _contracts: contracts, _cfg: cfg, _isProxy: false }); | ||
} | ||
|
||
/// @notice Deploy the L1StandardBridge | ||
function deployL1StandardBridge() public broadcast returns (address addr_) { | ||
console.log("Deploying L1StandardBridge implementation"); | ||
|
||
L1StandardBridge bridge = new L1StandardBridge{ salt: _implSalt() }(); | ||
|
||
save("L1StandardBridge", address(bridge)); | ||
console.log("L1StandardBridge deployed at %s", address(bridge)); | ||
|
||
// Override the `L1StandardBridge` contract to the deployed implementation. This is necessary | ||
// to check the `L1StandardBridge` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.L1StandardBridge = address(bridge); | ||
ChainAssertions.checkL1StandardBridge({ _contracts: contracts, _isProxy: false }); | ||
|
||
addr_ = address(bridge); | ||
} | ||
|
||
/// @notice Deploy the L1ERC721Bridge | ||
function deployL1ERC721Bridge() public broadcast returns (address addr_) { | ||
console.log("Deploying L1ERC721Bridge implementation"); | ||
L1ERC721Bridge bridge = new L1ERC721Bridge{ salt: _implSalt() }(); | ||
|
||
save("L1ERC721Bridge", address(bridge)); | ||
console.log("L1ERC721Bridge deployed at %s", address(bridge)); | ||
|
||
// Override the `L1ERC721Bridge` contract to the deployed implementation. This is necessary | ||
// to check the `L1ERC721Bridge` implementation alongside dependent contracts, which | ||
// are always proxies. | ||
Types.ContractSet memory contracts = _proxiesUnstrict(); | ||
contracts.L1ERC721Bridge = address(bridge); | ||
|
||
ChainAssertions.checkL1ERC721Bridge({ _contracts: contracts, _isProxy: false }); | ||
|
||
addr_ = address(bridge); | ||
} | ||
} |
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