github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/scripts/Artifacts.s.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity ^0.8.0;
     3  
     4  import { console2 as console } from "forge-std/console2.sol";
     5  import { stdJson } from "forge-std/StdJson.sol";
     6  import { Vm } from "forge-std/Vm.sol";
     7  import { Executables } from "scripts/Executables.sol";
     8  import { Predeploys } from "src/libraries/Predeploys.sol";
     9  import { Config } from "scripts/Config.sol";
    10  import { StorageSlot } from "scripts/ForgeArtifacts.sol";
    11  import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
    12  import { LibString } from "solady/utils/LibString.sol";
    13  import { ForgeArtifacts } from "scripts/ForgeArtifacts.sol";
    14  import { IAddressManager } from "scripts/interfaces/IAddressManager.sol";
    15  
    16  /// @notice Represents a deployment. Is serialized to JSON as a key/value
    17  ///         pair. Can be accessed from within scripts.
    18  struct Deployment {
    19      string name;
    20      address payable addr;
    21  }
    22  
    23  /// @title Artifacts
    24  /// @notice Useful for accessing deployment artifacts from within scripts.
    25  ///         When a contract is deployed, call the `save` function to write its name and
    26  ///         contract address to disk. Inspired by `forge-deploy`.
    27  abstract contract Artifacts {
    28      /// @notice Foundry cheatcode VM.
    29      Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
    30      /// @notice Error for when attempting to fetch a deployment and it does not exist
    31  
    32      error DeploymentDoesNotExist(string);
    33      /// @notice Error for when trying to save an invalid deployment
    34      error InvalidDeployment(string);
    35      /// @notice The set of deployments that have been done during execution.
    36  
    37      mapping(string => Deployment) internal _namedDeployments;
    38      /// @notice The same as `_namedDeployments` but as an array.
    39      Deployment[] internal _newDeployments;
    40      /// @notice Path to the directory containing the hh deploy style artifacts
    41      string internal deploymentsDir;
    42      /// @notice The path to the deployment artifact that is being written to.
    43      string internal deploymentOutfile;
    44      /// @notice The namespace for the deployment. Can be set with the env var DEPLOYMENT_CONTEXT.
    45      string internal deploymentContext;
    46  
    47      /// @notice Setup function. The arguments here
    48      function setUp() public virtual {
    49          deploymentOutfile = Config.deploymentOutfile();
    50          console.log("Writing artifact to %s", deploymentOutfile);
    51          ForgeArtifacts.ensurePath(deploymentOutfile);
    52  
    53          uint256 chainId = Config.chainID();
    54          console.log("Connected to network with chainid %s", chainId);
    55  
    56          // Load addresses from a JSON file if the CONTRACT_ADDRESSES_PATH environment variable
    57          // is set. Great for loading addresses from `superchain-registry`.
    58          string memory addresses = Config.contractAddressesPath();
    59          if (bytes(addresses).length > 0) {
    60              console.log("Loading addresses from %s", addresses);
    61              _loadAddresses(addresses);
    62          }
    63      }
    64  
    65      /// @notice Populates the addresses to be used in a script based on a JSON file.
    66      ///         The format of the JSON file is the same that it output by this script
    67      ///         as well as the JSON files that contain addresses in the `superchain-registry`
    68      ///         repo. The JSON key is the name of the contract and the value is an address.
    69      function _loadAddresses(string memory _path) internal {
    70          string[] memory commands = new string[](3);
    71          commands[0] = "bash";
    72          commands[1] = "-c";
    73          commands[2] = string.concat("jq -cr < ", _path);
    74          string memory json = string(vm.ffi(commands));
    75          string[] memory keys = vm.parseJsonKeys(json, "");
    76          for (uint256 i; i < keys.length; i++) {
    77              string memory key = keys[i];
    78              address addr = stdJson.readAddress(json, string.concat("$.", key));
    79              save(key, addr);
    80          }
    81      }
    82  
    83      /// @notice Returns all of the deployments done in the current context.
    84      function newDeployments() external view returns (Deployment[] memory) {
    85          return _newDeployments;
    86      }
    87  
    88      /// @notice Returns whether or not a particular deployment exists.
    89      /// @param _name The name of the deployment.
    90      /// @return Whether the deployment exists or not.
    91      function has(string memory _name) public view returns (bool) {
    92          Deployment memory existing = _namedDeployments[_name];
    93          return bytes(existing.name).length > 0;
    94      }
    95  
    96      /// @notice Returns the address of a deployment. Also handles the predeploys.
    97      /// @param _name The name of the deployment.
    98      /// @return The address of the deployment. May be `address(0)` if the deployment does not
    99      ///         exist.
   100      function getAddress(string memory _name) public view returns (address payable) {
   101          Deployment memory existing = _namedDeployments[_name];
   102          if (existing.addr != address(0)) {
   103              if (bytes(existing.name).length == 0) {
   104                  return payable(address(0));
   105              }
   106              return existing.addr;
   107          }
   108  
   109          bytes32 digest = keccak256(bytes(_name));
   110          if (digest == keccak256(bytes("L2CrossDomainMessenger"))) {
   111              return payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER);
   112          } else if (digest == keccak256(bytes("L2ToL1MessagePasser"))) {
   113              return payable(Predeploys.L2_TO_L1_MESSAGE_PASSER);
   114          } else if (digest == keccak256(bytes("L2StandardBridge"))) {
   115              return payable(Predeploys.L2_STANDARD_BRIDGE);
   116          } else if (digest == keccak256(bytes("L2ERC721Bridge"))) {
   117              return payable(Predeploys.L2_ERC721_BRIDGE);
   118          } else if (digest == keccak256(bytes("SequencerFeeWallet"))) {
   119              return payable(Predeploys.SEQUENCER_FEE_WALLET);
   120          } else if (digest == keccak256(bytes("OptimismMintableERC20Factory"))) {
   121              return payable(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY);
   122          } else if (digest == keccak256(bytes("OptimismMintableERC721Factory"))) {
   123              return payable(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);
   124          } else if (digest == keccak256(bytes("L1Block"))) {
   125              return payable(Predeploys.L1_BLOCK_ATTRIBUTES);
   126          } else if (digest == keccak256(bytes("GasPriceOracle"))) {
   127              return payable(Predeploys.GAS_PRICE_ORACLE);
   128          } else if (digest == keccak256(bytes("L1MessageSender"))) {
   129              return payable(Predeploys.L1_MESSAGE_SENDER);
   130          } else if (digest == keccak256(bytes("DeployerWhitelist"))) {
   131              return payable(Predeploys.DEPLOYER_WHITELIST);
   132          } else if (digest == keccak256(bytes("WETH9"))) {
   133              return payable(Predeploys.WETH9);
   134          } else if (digest == keccak256(bytes("LegacyERC20ETH"))) {
   135              return payable(Predeploys.LEGACY_ERC20_ETH);
   136          } else if (digest == keccak256(bytes("L1BlockNumber"))) {
   137              return payable(Predeploys.L1_BLOCK_NUMBER);
   138          } else if (digest == keccak256(bytes("LegacyMessagePasser"))) {
   139              return payable(Predeploys.LEGACY_MESSAGE_PASSER);
   140          } else if (digest == keccak256(bytes("ProxyAdmin"))) {
   141              return payable(Predeploys.PROXY_ADMIN);
   142          } else if (digest == keccak256(bytes("BaseFeeVault"))) {
   143              return payable(Predeploys.BASE_FEE_VAULT);
   144          } else if (digest == keccak256(bytes("L1FeeVault"))) {
   145              return payable(Predeploys.L1_FEE_VAULT);
   146          } else if (digest == keccak256(bytes("GovernanceToken"))) {
   147              return payable(Predeploys.GOVERNANCE_TOKEN);
   148          } else if (digest == keccak256(bytes("SchemaRegistry"))) {
   149              return payable(Predeploys.SCHEMA_REGISTRY);
   150          } else if (digest == keccak256(bytes("EAS"))) {
   151              return payable(Predeploys.EAS);
   152          }
   153          return payable(address(0));
   154      }
   155  
   156      /// @notice Returns the address of a deployment and reverts if the deployment
   157      ///         does not exist.
   158      /// @return The address of the deployment.
   159      function mustGetAddress(string memory _name) public view returns (address payable) {
   160          address addr = getAddress(_name);
   161          if (addr == address(0)) {
   162              revert DeploymentDoesNotExist(_name);
   163          }
   164          return payable(addr);
   165      }
   166  
   167      /// @notice Returns a deployment that is suitable to be used to interact with contracts.
   168      /// @param _name The name of the deployment.
   169      /// @return The deployment.
   170      function get(string memory _name) public view returns (Deployment memory) {
   171          return _namedDeployments[_name];
   172      }
   173  
   174      /// @notice Appends a deployment to disk as a JSON deploy artifact.
   175      /// @param _name The name of the deployment.
   176      /// @param _deployed The address of the deployment.
   177      function save(string memory _name, address _deployed) public {
   178          if (bytes(_name).length == 0) {
   179              revert InvalidDeployment("EmptyName");
   180          }
   181          if (bytes(_namedDeployments[_name].name).length > 0) {
   182              revert InvalidDeployment("AlreadyExists");
   183          }
   184  
   185          console.log("Saving %s: %s", _name, _deployed);
   186          Deployment memory deployment = Deployment({ name: _name, addr: payable(_deployed) });
   187          _namedDeployments[_name] = deployment;
   188          _newDeployments.push(deployment);
   189          _appendDeployment(_name, _deployed);
   190      }
   191  
   192      /// @notice Reads the deployment artifact from disk that were generated
   193      ///         by the deploy script.
   194      /// @return An array of deployments.
   195      function _getDeployments() internal returns (Deployment[] memory) {
   196          string memory json = vm.readFile(deploymentOutfile);
   197          string[] memory cmd = new string[](3);
   198          cmd[0] = Executables.bash;
   199          cmd[1] = "-c";
   200          cmd[2] = string.concat(Executables.jq, " 'keys' <<< '", json, "'");
   201          bytes memory res = vm.ffi(cmd);
   202          string[] memory names = stdJson.readStringArray(string(res), "");
   203  
   204          Deployment[] memory deployments = new Deployment[](names.length);
   205          for (uint256 i; i < names.length; i++) {
   206              string memory contractName = names[i];
   207              address addr = stdJson.readAddress(json, string.concat("$.", contractName));
   208              deployments[i] = Deployment({ name: contractName, addr: payable(addr) });
   209          }
   210          return deployments;
   211      }
   212  
   213      /// @notice Adds a deployment to the temp deployments file
   214      function _appendDeployment(string memory _name, address _deployed) internal {
   215          vm.writeJson({ json: stdJson.serialize("", _name, _deployed), path: deploymentOutfile });
   216      }
   217  
   218      /// @notice Stubs a deployment retrieved through `get`.
   219      /// @param _name The name of the deployment.
   220      /// @param _addr The mock address of the deployment.
   221      function prankDeployment(string memory _name, address _addr) public {
   222          if (bytes(_name).length == 0) {
   223              revert InvalidDeployment("EmptyName");
   224          }
   225  
   226          Deployment memory deployment = Deployment({ name: _name, addr: payable(_addr) });
   227          _namedDeployments[_name] = deployment;
   228      }
   229  
   230      /// @notice Returns the value of the internal `_initialized` storage slot for a given contract.
   231      function loadInitializedSlot(string memory _contractName) public returns (uint8 initialized_) {
   232          address contractAddress;
   233          // Check if the contract name ends with `Proxy` and, if so, get the implementation address
   234          if (LibString.endsWith(_contractName, "Proxy")) {
   235              contractAddress = EIP1967Helper.getImplementation(getAddress(_contractName));
   236              _contractName = LibString.slice(_contractName, 0, bytes(_contractName).length - 5);
   237              // If the EIP1967 implementation address is 0, we try to get the implementation address from legacy
   238              // AddressManager, which would work if the proxy is ResolvedDelegateProxy like L1CrossDomainMessengerProxy.
   239              if (contractAddress == address(0)) {
   240                  contractAddress =
   241                      IAddressManager(mustGetAddress("AddressManager")).getAddress(string.concat("OVM_", _contractName));
   242              }
   243          } else {
   244              contractAddress = mustGetAddress(_contractName);
   245          }
   246          StorageSlot memory slot = ForgeArtifacts.getInitializedSlot(_contractName);
   247          bytes32 slotVal = vm.load(contractAddress, bytes32(vm.parseUint(slot.slot)));
   248          initialized_ = uint8((uint256(slotVal) >> (slot.offset * 8)) & 0xFF);
   249      }
   250  }