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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity ^0.8.0;
     3  
     4  import { Vm } from "forge-std/Vm.sol";
     5  import { Executables } from "scripts/Executables.sol";
     6  import { stdJson } from "forge-std/StdJson.sol";
     7  
     8  /// @notice Contains information about a storage slot. Mirrors the layout of the storage
     9  ///         slot object in Forge artifacts so that we can deserialize JSON into this struct.
    10  struct StorageSlot {
    11      uint256 astId;
    12      string _contract;
    13      string label;
    14      uint256 offset;
    15      string slot;
    16      string _type;
    17  }
    18  
    19  /// @title ForgeArtifacts
    20  /// @notice Library for interacting with the forge artifacts.
    21  library ForgeArtifacts {
    22      /// @notice Foundry cheatcode VM.
    23      Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
    24  
    25      /// @notice Removes the semantic versioning from a contract name. The semver will exist if the contract is compiled
    26      /// more than once with different versions of the compiler.
    27      function _stripSemver(string memory _name) internal returns (string memory out_) {
    28          string[] memory cmd = new string[](3);
    29          cmd[0] = Executables.bash;
    30          cmd[1] = "-c";
    31          cmd[2] = string.concat(
    32              Executables.echo, " ", _name, " | ", Executables.sed, " -E 's/[.][0-9]+\\.[0-9]+\\.[0-9]+//g'"
    33          );
    34          bytes memory res = vm.ffi(cmd);
    35          out_ = string(res);
    36      }
    37  
    38      /// @notice Builds the fully qualified name of a contract. Assumes that the
    39      ///         file name is the same as the contract name but strips semver for the file name.
    40      function _getFullyQualifiedName(string memory _name) internal returns (string memory out_) {
    41          string memory sanitized = _stripSemver(_name);
    42          out_ = string.concat(sanitized, ".sol:", _name);
    43      }
    44  
    45      /// @notice Returns the storage layout for a deployed contract.
    46      function getStorageLayout(string memory _name) public returns (string memory layout_) {
    47          string[] memory cmd = new string[](3);
    48          cmd[0] = Executables.bash;
    49          cmd[1] = "-c";
    50          cmd[2] = string.concat(Executables.jq, " -r '.storageLayout' < ", _getForgeArtifactPath(_name));
    51          bytes memory res = vm.ffi(cmd);
    52          layout_ = string(res);
    53      }
    54  
    55      /// @notice Returns the abi from a the forge artifact
    56      function getAbi(string memory _name) public returns (string memory abi_) {
    57          string[] memory cmd = new string[](3);
    58          cmd[0] = Executables.bash;
    59          cmd[1] = "-c";
    60          cmd[2] = string.concat(Executables.jq, " -r '.abi' < ", _getForgeArtifactPath(_name));
    61          bytes memory res = vm.ffi(cmd);
    62          abi_ = string(res);
    63      }
    64  
    65      /// @notice Returns the methodIdentifiers from the forge artifact
    66      function getMethodIdentifiers(string memory _name) internal returns (string[] memory ids_) {
    67          string[] memory cmd = new string[](3);
    68          cmd[0] = Executables.bash;
    69          cmd[1] = "-c";
    70          cmd[2] = string.concat(Executables.jq, " '.methodIdentifiers | keys' < ", _getForgeArtifactPath(_name));
    71          bytes memory res = vm.ffi(cmd);
    72          ids_ = stdJson.readStringArray(string(res), "");
    73      }
    74  
    75      function _getForgeArtifactDirectory(string memory _name) internal returns (string memory dir_) {
    76          string[] memory cmd = new string[](3);
    77          cmd[0] = Executables.bash;
    78          cmd[1] = "-c";
    79          cmd[2] = string.concat(Executables.forge, " config --json | ", Executables.jq, " -r .out");
    80          bytes memory res = vm.ffi(cmd);
    81          string memory contractName = _stripSemver(_name);
    82          dir_ = string.concat(vm.projectRoot(), "/", string(res), "/", contractName, ".sol");
    83      }
    84  
    85      /// @notice Returns the filesystem path to the artifact path. If the contract was compiled
    86      ///         with multiple solidity versions then return the first one based on the result of `ls`.
    87      function _getForgeArtifactPath(string memory _name) internal returns (string memory out_) {
    88          string memory directory = _getForgeArtifactDirectory(_name);
    89          string memory path = string.concat(directory, "/", _name, ".json");
    90          if (vm.exists(path)) {
    91              return path;
    92          }
    93  
    94          string[] memory cmd = new string[](3);
    95          cmd[0] = Executables.bash;
    96          cmd[1] = "-c";
    97          cmd[2] = string.concat(
    98              Executables.ls,
    99              " -1 --color=never ",
   100              directory,
   101              " | ",
   102              Executables.jq,
   103              " -R -s -c 'split(\"\n\") | map(select(length > 0))'"
   104          );
   105          bytes memory res = vm.ffi(cmd);
   106          string[] memory files = stdJson.readStringArray(string(res), "");
   107          out_ = string.concat(directory, "/", files[0]);
   108      }
   109  
   110      /// @notice Returns the forge artifact given a contract name.
   111      function _getForgeArtifact(string memory _name) internal returns (string memory out_) {
   112          string memory forgeArtifactPath = _getForgeArtifactPath(_name);
   113          out_ = vm.readFile(forgeArtifactPath);
   114      }
   115  
   116      /// @notice Pulls the `_initialized` storage slot information from the Forge artifacts for a given contract.
   117      function getInitializedSlot(string memory _contractName) internal returns (StorageSlot memory slot_) {
   118          string memory storageLayout = getStorageLayout(_contractName);
   119  
   120          string[] memory command = new string[](3);
   121          command[0] = Executables.bash;
   122          command[1] = "-c";
   123          command[2] = string.concat(
   124              Executables.echo,
   125              " '",
   126              storageLayout,
   127              "'",
   128              " | ",
   129              Executables.jq,
   130              " '.storage[] | select(.label == \"_initialized\" and .type == \"t_uint8\")'"
   131          );
   132          bytes memory rawSlot = vm.parseJson(string(vm.ffi(command)));
   133          slot_ = abi.decode(rawSlot, (StorageSlot));
   134      }
   135  
   136      /// @notice Accepts a filepath and then ensures that the directory
   137      ///         exists for the file to live in.
   138      function ensurePath(string memory _path) internal {
   139          (, bytes memory returndata) =
   140              address(vm).call(abi.encodeWithSignature("split(string,string)", _path, string("/")));
   141          string[] memory outputs = abi.decode(returndata, (string[]));
   142  
   143          string memory path = "";
   144          for (uint256 i = 0; i < outputs.length - 1; i++) {
   145              path = string.concat(path, outputs[i], "/");
   146          }
   147          vm.createDir(path, true);
   148      }
   149  }