github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/scripts/L2Genesis.s.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.15; 3 4 import { Script } from "forge-std/Script.sol"; 5 import { console2 as console } from "forge-std/console2.sol"; 6 7 import { Artifacts } from "scripts/Artifacts.s.sol"; 8 import { DeployConfig } from "scripts/DeployConfig.s.sol"; 9 import { Predeploys } from "src/libraries/Predeploys.sol"; 10 import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; 11 import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; 12 import { L2StandardBridge } from "src/L2/L2StandardBridge.sol"; 13 import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol"; 14 import { SequencerFeeVault } from "src/L2/SequencerFeeVault.sol"; 15 import { FeeVault } from "src/universal/FeeVault.sol"; 16 import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; 17 import { L1Block } from "src/L2/L1Block.sol"; 18 import { GovernanceToken } from "src/governance/GovernanceToken.sol"; 19 import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; 20 21 interface IInitializable { 22 function initialize(address _addr) external; 23 } 24 25 /// @dev The general flow of adding a predeploy is: 26 /// 1. _setPredeployProxies uses vm.etch to set the Proxy.sol deployed bytecode for proxy address `0x420...000` to 27 /// `0x420...000 + PROXY_COUNT - 1`. 28 /// Additionally, the PROXY_ADMIN_ADDRESS and PROXY_IMPLEMENTATION_ADDRESS storage slots are set for the proxy 29 /// address. 30 /// 2. `vm.etch` sets the deployed bytecode for each predeploy at the implementation address (i.e. `0xc0d3` 31 /// namespace). 32 /// 3. The `initialize` method is called at the implementation address with zero/dummy vaules if the method exists. 33 /// 4. The `initialize` method is called at the proxy address with actual vaules if the method exists. 34 /// 5. A `require` check to verify the expected implementation address is set for the proxy. 35 /// @notice The following safety invariants are used when setting state: 36 /// 1. `vm.getDeployedBytecode` can only be used with `vm.etch` when there are no side 37 /// effects in the constructor and no immutables in the bytecode. 38 /// 2. A contract must be deployed using the `new` syntax if there are immutables in the code. 39 /// Any other side effects from the init code besides setting the immutables must be cleaned up afterwards. 40 /// 3. A contract is deployed using the `new` syntax, however it's not proxied and is still expected to exist at 41 /// a 42 /// specific implementation address (i.e. `0xc0d3` namespace). In this case we deploy an instance of the 43 /// contract 44 /// using `new` syntax, use `contract.code` to retrieve it's deployed bytecode, `vm.etch` the bytecode at the 45 /// expected implementation address, and `vm.store` to set any storage slots that are 46 /// expected to be set after a new deployment. Lastly, we reset the account code and storage slots the contract 47 /// was initially deployed to so it's not included in the `vm.dumpState`. 48 contract L2Genesis is Script, Artifacts { 49 uint256 constant PROXY_COUNT = 2048; 50 uint256 constant PRECOMPILE_COUNT = 256; 51 DeployConfig public constant cfg = 52 DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); 53 54 /// @notice The storage slot that holds the address of a proxy implementation. 55 /// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)` 56 bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS = 57 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 58 59 /// @notice The storage slot that holds the address of the owner. 60 /// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)` 61 bytes32 internal constant PROXY_ADMIN_ADDRESS = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; 62 uint80 internal constant DEV_ACCOUNT_FUND_AMT = 10_000 ether; 63 /// @notice Default Anvil dev accounts. Only funded if `cfg.fundDevAccounts == true`. 64 address[10] internal devAccounts = [ 65 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 66 0x70997970C51812dc3A010C7d01b50e0d17dc79C8, 67 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC, 68 0x90F79bf6EB2c4f870365E785982E1f101E93b906, 69 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65, 70 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc, 71 0x976EA74026E726554dB657fA54763abd0C3a0aa9, 72 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955, 73 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f, 74 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 75 ]; 76 77 string internal outfile; 78 79 /// @dev Reads the deploy config, sets `outfile` which is where the `vm.dumpState` will be saved to, and 80 /// loads in the addresses for the L1 contract deployments. 81 function setUp() public override { 82 Artifacts.setUp(); 83 84 string memory path = string.concat(vm.projectRoot(), "/deploy-config/", deploymentContext, ".json"); 85 vm.etch(address(cfg), vm.getDeployedCode("DeployConfig.s.sol:DeployConfig")); 86 vm.label(address(cfg), "DeployConfig"); 87 vm.allowCheatcodes(address(cfg)); 88 cfg.read(path); 89 90 outfile = string.concat(vm.projectRoot(), "/deployments/", deploymentContext, "/genesis-l2.json"); 91 92 _loadAddresses(string.concat(vm.projectRoot(), "/deployments/", deploymentContext, "/.deploy")); 93 } 94 95 /// @dev Sets the precompiles, proxies, and the implementation accounts to be `vm.dumpState` 96 /// to generate a L2 genesis alloc. 97 /// @notice The alloc object is sorted numerically by address. 98 function run() public { 99 _dealEthToPrecompiles(); 100 _setPredeployProxies(); 101 _setPredeployImplementations(); 102 103 if (cfg.fundDevAccounts()) { 104 _fundDevAccounts(); 105 } 106 107 /// Reset so its not included state dump 108 vm.etch(address(cfg), ""); 109 110 vm.dumpState(outfile); 111 _sortJsonByKeys(outfile); 112 } 113 114 /// @notice Give all of the precompiles 1 wei so that they are 115 /// not considered empty accounts. 116 function _dealEthToPrecompiles() internal { 117 for (uint256 i; i < PRECOMPILE_COUNT; i++) { 118 vm.deal(address(uint160(i)), 1); 119 } 120 } 121 122 /// @dev Set up the accounts that correspond to the predeploys. 123 /// The Proxy bytecode should be set. All proxied predeploys should have 124 /// the 1967 admin slot set to the ProxyAdmin predeploy. All defined predeploys 125 /// should have their implementations set. 126 function _setPredeployProxies() internal { 127 bytes memory code = vm.getDeployedCode("Proxy.sol:Proxy"); 128 uint160 prefix = uint160(0x420) << 148; 129 130 console.log( 131 "Setting proxy deployed bytecode for addresses in range %s through %s", 132 address(prefix | uint160(0)), 133 address(prefix | uint160(PROXY_COUNT - 1)) 134 ); 135 for (uint256 i = 0; i < PROXY_COUNT; i++) { 136 address addr = address(prefix | uint160(i)); 137 if (_notProxied(addr)) { 138 continue; 139 } 140 141 vm.etch(addr, code); 142 vm.store(addr, PROXY_ADMIN_ADDRESS, bytes32(uint256(uint160(Predeploys.PROXY_ADMIN)))); 143 144 if (_isDefinedPredeploy(addr)) { 145 address implementation = _predeployToCodeNamespace(addr); 146 console.log("Setting proxy %s implementation: %s", addr, implementation); 147 vm.store(addr, PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(implementation)))); 148 } 149 } 150 } 151 152 /// @notice LEGACY_ERC20_ETH is not being predeployed since it's been deprecated. 153 /// @dev Sets all the implementations for the predeploy proxies. For contracts without proxies, 154 /// sets the deployed bytecode at their expected predeploy address. 155 function _setPredeployImplementations() internal { 156 _setLegacyMessagePasser(); 157 _setDeployerWhitelist(); 158 _setWETH9(); 159 _setL2StandardBridge(); 160 _setL2CrossDomainMessenger(); 161 _setSequencerFeeVault(); 162 _setOptimismMintableERC20Factory(); 163 _setL1BlockNumber(); 164 _setGasPriceOracle(); 165 _setGovernanceToken(); 166 _setL1Block(); 167 } 168 169 /// @notice This predeploy is following the saftey invariant #1. 170 function _setLegacyMessagePasser() internal { 171 _setImplementationCode(Predeploys.LEGACY_MESSAGE_PASSER, "LegacyMessagePasser"); 172 } 173 174 /// @notice This predeploy is following the saftey invariant #1. 175 function _setDeployerWhitelist() internal { 176 _setImplementationCode(Predeploys.DEPLOYER_WHITELIST, "DeployerWhitelist"); 177 } 178 179 /// @notice This predeploy is following the saftey invariant #1. 180 /// Contract metadata hash appended to deployed bytecode will differ 181 /// from previous L2 genesis output. 182 /// This contract is NOT proxied. 183 /// @dev We're manually setting storage slots because we need to deployment to be at 184 /// the address `Predeploys.WETH9`, so we can't just deploy a new instance of `WETH9`. 185 function _setWETH9() internal { 186 console.log("Setting %s implementation at: %s", "WETH9", Predeploys.WETH9); 187 vm.etch(Predeploys.WETH9, vm.getDeployedCode("WETH9.sol:WETH9")); 188 189 vm.store( 190 Predeploys.WETH9, 191 /// string public name 192 hex"0000000000000000000000000000000000000000000000000000000000000000", 193 /// "Wrapped Ether" 194 hex"577261707065642045746865720000000000000000000000000000000000001a" 195 ); 196 vm.store( 197 Predeploys.WETH9, 198 /// string public symbol 199 hex"0000000000000000000000000000000000000000000000000000000000000001", 200 /// "WETH" 201 hex"5745544800000000000000000000000000000000000000000000000000000008" 202 ); 203 vm.store( 204 Predeploys.WETH9, 205 // uint8 public decimals 206 hex"0000000000000000000000000000000000000000000000000000000000000002", 207 /// 18 208 hex"0000000000000000000000000000000000000000000000000000000000000012" 209 ); 210 } 211 212 /// @notice This predeploy is following the saftey invariant #1. 213 /// We're initializing the implementation with `address(0)` so 214 /// it's not left uninitialized. After `initialize` is called on the 215 /// proxy to set the storage slot with the expected value. 216 function _setL2StandardBridge() internal { 217 address impl = _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE, "L2StandardBridge"); 218 219 L2StandardBridge(payable(impl)).initialize(L1StandardBridge(payable(address(0)))); 220 221 L2StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).initialize( 222 L1StandardBridge(mustGetAddress("L1StandardBridgeProxy")) 223 ); 224 225 _checkL2StandardBridge(impl); 226 } 227 228 /// @notice This predeploy is following the saftey invariant #1. 229 /// We're initializing the implementation with `address(0)` so 230 /// it's not left uninitialized. After `initialize` is called on the 231 /// proxy to set the storage slot with the expected value. 232 function _setL2CrossDomainMessenger() internal { 233 address impl = _setImplementationCode(Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L2CrossDomainMessenger"); 234 235 L2CrossDomainMessenger(impl).initialize(L1CrossDomainMessenger(address(0))); 236 237 L2CrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).initialize( 238 L1CrossDomainMessenger(mustGetAddress("L1CrossDomainMessengerProxy")) 239 ); 240 241 _checkL2CrossDomainMessenger(impl); 242 } 243 244 /// @notice This predeploy is following the saftey invariant #2, 245 /// because the constructor args are non-static L1 contract 246 /// addresses that are being read from the deploy config 247 /// that are set as immutables. 248 /// @dev Because the constructor args are stored as immutables, 249 /// we don't have to worry about setting storage slots. 250 function _setSequencerFeeVault() internal { 251 SequencerFeeVault vault = new SequencerFeeVault({ 252 _recipient: cfg.sequencerFeeVaultRecipient(), 253 _minWithdrawalAmount: cfg.sequencerFeeVaultMinimumWithdrawalAmount(), 254 _withdrawalNetwork: FeeVault.WithdrawalNetwork(cfg.sequencerFeeVaultWithdrawalNetwork()) 255 }); 256 257 address impl = _predeployToCodeNamespace(Predeploys.SEQUENCER_FEE_WALLET); 258 console.log("Setting %s implementation at: %s", "SequencerFeeVault", impl); 259 vm.etch(impl, address(vault).code); 260 261 /// Reset so its not included state dump 262 vm.etch(address(vault), ""); 263 vm.resetNonce(address(vault)); 264 265 _checkSequencerFeeVault(impl); 266 } 267 268 /// @notice This predeploy is following the saftey invariant #1. 269 /// We're initializing the implementation with `address(0)` so 270 /// it's not left uninitialized. After `initialize` is called on the 271 /// proxy to set the storage slot with the expected value. 272 function _setOptimismMintableERC20Factory() internal { 273 address impl = 274 _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY, "OptimismMintableERC20Factory"); 275 276 OptimismMintableERC20Factory(impl).initialize(address(0)); 277 278 OptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY).initialize( 279 Predeploys.L2_STANDARD_BRIDGE 280 ); 281 282 _checkOptimismMintableERC20Factory(impl); 283 } 284 285 /// @notice This predeploy is following the saftey invariant #1. 286 /// This contract has no initializer. 287 function _setL1BlockNumber() internal { 288 _setImplementationCode(Predeploys.L1_BLOCK_NUMBER, "L1BlockNumber"); 289 } 290 291 /// @notice This predeploy is following the saftey invariant #1. 292 /// This contract has no initializer. 293 function _setGasPriceOracle() internal { 294 _setImplementationCode(Predeploys.GAS_PRICE_ORACLE, "GasPriceOracle"); 295 } 296 297 /// @notice This predeploy is following the saftey invariant #3. 298 function _setGovernanceToken() internal { 299 if (!cfg.enableGovernance()) { 300 console.log("Governance not enabled, skipping setting governanace token"); 301 return; 302 } 303 304 GovernanceToken token = new GovernanceToken(); 305 console.log("Setting %s implementation at: %s", "GovernanceToken", Predeploys.GOVERNANCE_TOKEN); 306 vm.etch(Predeploys.GOVERNANCE_TOKEN, address(token).code); 307 308 bytes32 _nameSlot = hex"0000000000000000000000000000000000000000000000000000000000000003"; 309 bytes32 _symbolSlot = hex"0000000000000000000000000000000000000000000000000000000000000004"; 310 bytes32 _ownerSlot = hex"000000000000000000000000000000000000000000000000000000000000000a"; 311 312 vm.store(Predeploys.GOVERNANCE_TOKEN, _nameSlot, vm.load(address(token), _nameSlot)); 313 vm.store(Predeploys.GOVERNANCE_TOKEN, _symbolSlot, vm.load(address(token), _symbolSlot)); 314 vm.store(Predeploys.GOVERNANCE_TOKEN, _ownerSlot, bytes32(uint256(uint160(cfg.governanceTokenOwner())))); 315 316 /// Reset so its not included state dump 317 vm.etch(address(token), ""); 318 vm.resetNonce(address(token)); 319 } 320 321 /// @notice This predeploy is following the saftey invariant #1. 322 /// This contract has no initializer. 323 /// @dev Previously the initial L1 attributes was set at genesis, to simplify, 324 /// they no longer are so the resulting storage slots are no longer set. 325 function _setL1Block() internal { 326 _setImplementationCode(Predeploys.L1_BLOCK_ATTRIBUTES, "L1Block"); 327 } 328 329 /// @dev Returns true if the address is not proxied. 330 function _notProxied(address _addr) internal pure returns (bool) { 331 return _addr == Predeploys.GOVERNANCE_TOKEN || _addr == Predeploys.WETH9; 332 } 333 334 /// @dev Returns true if the address is a predeploy. 335 function _isDefinedPredeploy(address _addr) internal pure returns (bool) { 336 return _addr == Predeploys.L2_TO_L1_MESSAGE_PASSER || _addr == Predeploys.L2_CROSS_DOMAIN_MESSENGER 337 || _addr == Predeploys.L2_STANDARD_BRIDGE || _addr == Predeploys.L2_ERC721_BRIDGE 338 || _addr == Predeploys.SEQUENCER_FEE_WALLET || _addr == Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY 339 || _addr == Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == Predeploys.L1_BLOCK_ATTRIBUTES 340 || _addr == Predeploys.GAS_PRICE_ORACLE || _addr == Predeploys.DEPLOYER_WHITELIST || _addr == Predeploys.WETH9 341 || _addr == Predeploys.L1_BLOCK_NUMBER || _addr == Predeploys.LEGACY_MESSAGE_PASSER 342 || _addr == Predeploys.PROXY_ADMIN || _addr == Predeploys.BASE_FEE_VAULT || _addr == Predeploys.L1_FEE_VAULT 343 || _addr == Predeploys.GOVERNANCE_TOKEN || _addr == Predeploys.SCHEMA_REGISTRY || _addr == Predeploys.EAS; 344 } 345 346 /// @dev Function to compute the expected address of the predeploy implementation 347 /// in the genesis state. 348 function _predeployToCodeNamespace(address _addr) internal pure returns (address) { 349 return address( 350 uint160(uint256(uint160(_addr)) & 0xffff | uint256(uint160(0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000))) 351 ); 352 } 353 354 function _setImplementationCode(address _addr, string memory _name) internal returns (address) { 355 address impl = _predeployToCodeNamespace(_addr); 356 console.log("Setting %s implementation at: %s", _name, impl); 357 vm.etch(impl, vm.getDeployedCode(string.concat(_name, ".sol:", _name))); 358 359 _verifyProxyImplementationAddress(_addr, impl); 360 361 return impl; 362 } 363 364 /// @dev Function to verify the expected implementation address is set for the respective proxy. 365 function _verifyProxyImplementationAddress(address _proxy, address _impl) internal view { 366 require( 367 EIP1967Helper.getImplementation(_proxy) == _impl, 368 "Expected different address at Proxys PROXY_IMPLEMENTATION_ADDRESS storage slot" 369 ); 370 } 371 372 /// @dev Function to verify that a contract was initialized, and can't be reinitialized. 373 /// @notice There isn't a good way to know if the resulting revering is due to abi mismatch 374 /// or because it's already been initialized 375 function _verifyCantReinitialize(address _contract, address _arg) internal { 376 vm.expectRevert("Initializable: contract is already initialized"); 377 IInitializable(_contract).initialize(_arg); 378 } 379 380 /// @dev Helper function to sort the genesis alloc numerically by address. 381 function _sortJsonByKeys(string memory _path) internal { 382 string[] memory commands = new string[](3); 383 commands[0] = "bash"; 384 commands[1] = "-c"; 385 commands[2] = string.concat("cat <<< $(jq -S '.' ", _path, ") > ", _path); 386 vm.ffi(commands); 387 } 388 389 function _fundDevAccounts() internal { 390 for (uint256 i; i < devAccounts.length; i++) { 391 console.log("Funding dev account %s with %s ETH", devAccounts[i], DEV_ACCOUNT_FUND_AMT / 1e18); 392 vm.deal(devAccounts[i], DEV_ACCOUNT_FUND_AMT); 393 } 394 395 _checkDevAccountsFunded(); 396 } 397 398 ////////////////////////////////////////////////////// 399 /// Post Checks 400 ////////////////////////////////////////////////////// 401 function _checkL2StandardBridge(address _impl) internal { 402 _verifyCantReinitialize(_impl, address(0)); 403 _verifyCantReinitialize(Predeploys.L2_STANDARD_BRIDGE, mustGetAddress("L1StandardBridgeProxy")); 404 } 405 406 function _checkL2CrossDomainMessenger(address _impl) internal { 407 _verifyCantReinitialize(_impl, address(0)); 408 _verifyCantReinitialize(Predeploys.L2_CROSS_DOMAIN_MESSENGER, mustGetAddress("L1CrossDomainMessengerProxy")); 409 } 410 411 function _checkSequencerFeeVault(address _impl) internal view { 412 _verifyProxyImplementationAddress(Predeploys.SEQUENCER_FEE_WALLET, _impl); 413 } 414 415 function _checkOptimismMintableERC20Factory(address _impl) internal { 416 _verifyCantReinitialize(_impl, address(0)); 417 _verifyCantReinitialize(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY, Predeploys.L2_STANDARD_BRIDGE); 418 } 419 420 function _checkDevAccountsFunded() internal view { 421 for (uint256 i; i < devAccounts.length; i++) { 422 if (devAccounts[i].balance != DEV_ACCOUNT_FUND_AMT) { 423 revert( 424 string.concat("Dev account not funded with expected amount of ETH: ", vm.toString(devAccounts[i])) 425 ); 426 } 427 } 428 } 429 }