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  }