Commit 9367c429 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

contracts-bedrock: modularize deployer script (#9061)

* contracts-bedrock: modularize deployer script

Move logic for interacting with artifacts outside
of the deployer contract into its own contract.

This allows for other contracts to easily inherit the
contract so that any contract can read in contract
addresses.

* ctb: handle devnet

* contracts-bedrock: fixup

* lint: fix
parent 35a5aad6
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { console2 as console } from "forge-std/console2.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { Vm } from "forge-std/Vm.sol";
import { Executables } from "scripts/Executables.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Chains } from "scripts/Chains.sol";
/// @notice Represents a deployment. Is serialized to JSON as a key/value
/// pair. Can be accessed from within scripts.
struct Deployment {
string name;
address payable addr;
}
/// @title Artifacts
/// @notice Useful for accessing deployment artifacts from within scripts.
abstract contract Artifacts {
/// @notice Foundry cheatcode VM.
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
/// @notice Error for when attempting to fetch a deployment and it does not exist
error DeploymentDoesNotExist(string);
/// @notice Error for when trying to save an invalid deployment
error InvalidDeployment(string);
/// @notice The set of deployments that have been done during execution.
mapping(string => Deployment) internal _namedDeployments;
/// @notice The same as `_namedDeployments` but as an array.
Deployment[] internal _newDeployments;
/// @notice Path to the directory containing the hh deploy style artifacts
string internal deploymentsDir;
/// @notice The path to the temp deployments file
string internal tempDeploymentsPath;
/// @notice The namespace for the deployment. Can be set with the env var DEPLOYMENT_CONTEXT.
string internal deploymentContext;
/// @notice Setup function. The arguments here
function setUp() public virtual {
string memory root = vm.projectRoot();
// The `deploymentContext` should match the name of the deploy-config file.
deploymentContext = _getDeploymentContext();
deploymentsDir = string.concat(root, "/deployments/", deploymentContext);
if (!vm.isDir(deploymentsDir)) {
vm.createDir(deploymentsDir, true);
}
tempDeploymentsPath = vm.envOr("DEPLOYMENT_OUTFILE", string.concat(deploymentsDir, "/.deploy"));
try vm.readFile(tempDeploymentsPath) returns (string memory) { }
catch {
vm.writeJson("{}", tempDeploymentsPath);
}
console.log("Storing temp deployment data in %s", tempDeploymentsPath);
try vm.createDir(deploymentsDir, true) { } catch (bytes memory) { }
uint256 chainId = vm.envOr("CHAIN_ID", block.chainid);
string memory chainIdPath = string.concat(deploymentsDir, string("/.chainId"));
try vm.readFile(chainIdPath) returns (string memory localChainId) {
if (vm.envOr("STRICT_DEPLOYMENT", true)) {
require(
vm.parseUint(localChainId) == chainId,
string.concat("Misconfigured networks: ", localChainId, " != ", vm.toString(chainId))
);
}
} catch {
vm.writeFile(chainIdPath, vm.toString(chainId));
}
console.log("Connected to network with chainid %s", chainId);
// Load addresses from a JSON file if the CONTRACT_ADDRESSES_PATH environment variable
// is set. Great for loading addresses from `superchain-registry`.
string memory addresses = vm.envOr("CONTRACT_ADDRESSES_PATH", string(""));
if (bytes(addresses).length > 0) {
console.log("Loading addresses from %s", addresses);
_loadAddresses(addresses);
}
}
/// @notice Populates the addresses to be used in a script based on a JSON file.
/// The format of the JSON file is the same that it output by this script
/// as well as the JSON files that contain addresses in the `superchain-ops`
/// repo. The JSON key is the name of the contract and the value is an address.
function _loadAddresses(string memory _path) internal {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq -cr < ", _path);
string memory json = string(vm.ffi(commands));
string[] memory keys = vm.parseJsonKeys(json, "");
for (uint256 i; i < keys.length; i++) {
string memory key = keys[i];
address addr = stdJson.readAddress(json, string.concat("$.", key));
save(key, addr);
}
}
/// @notice Returns all of the deployments done in the current context.
function newDeployments() external view returns (Deployment[] memory) {
return _newDeployments;
}
/// @notice Returns whether or not a particular deployment exists.
/// @param _name The name of the deployment.
/// @return Whether the deployment exists or not.
function has(string memory _name) public view returns (bool) {
Deployment memory existing = _namedDeployments[_name];
if (existing.addr != address(0)) {
return bytes(existing.name).length > 0;
}
return _getExistingDeploymentAddress(_name) != address(0);
}
/// @notice Returns the address of a deployment. Also handles the predeploys.
/// @param _name The name of the deployment.
/// @return The address of the deployment. May be `address(0)` if the deployment does not
/// exist.
function getAddress(string memory _name) public view returns (address payable) {
Deployment memory existing = _namedDeployments[_name];
if (existing.addr != address(0)) {
if (bytes(existing.name).length == 0) {
return payable(address(0));
}
return existing.addr;
}
address addr = _getExistingDeploymentAddress(_name);
if (addr != address(0)) return payable(addr);
bytes32 digest = keccak256(bytes(_name));
if (digest == keccak256(bytes("L2CrossDomainMessenger"))) {
return payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER);
} else if (digest == keccak256(bytes("L2ToL1MessagePasser"))) {
return payable(Predeploys.L2_TO_L1_MESSAGE_PASSER);
} else if (digest == keccak256(bytes("L2StandardBridge"))) {
return payable(Predeploys.L2_STANDARD_BRIDGE);
} else if (digest == keccak256(bytes("L2ERC721Bridge"))) {
return payable(Predeploys.L2_ERC721_BRIDGE);
} else if (digest == keccak256(bytes("SequencerFeeWallet"))) {
return payable(Predeploys.SEQUENCER_FEE_WALLET);
} else if (digest == keccak256(bytes("OptimismMintableERC20Factory"))) {
return payable(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY);
} else if (digest == keccak256(bytes("OptimismMintableERC721Factory"))) {
return payable(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);
} else if (digest == keccak256(bytes("L1Block"))) {
return payable(Predeploys.L1_BLOCK_ATTRIBUTES);
} else if (digest == keccak256(bytes("GasPriceOracle"))) {
return payable(Predeploys.GAS_PRICE_ORACLE);
} else if (digest == keccak256(bytes("L1MessageSender"))) {
return payable(Predeploys.L1_MESSAGE_SENDER);
} else if (digest == keccak256(bytes("DeployerWhitelist"))) {
return payable(Predeploys.DEPLOYER_WHITELIST);
} else if (digest == keccak256(bytes("WETH9"))) {
return payable(Predeploys.WETH9);
} else if (digest == keccak256(bytes("LegacyERC20ETH"))) {
return payable(Predeploys.LEGACY_ERC20_ETH);
} else if (digest == keccak256(bytes("L1BlockNumber"))) {
return payable(Predeploys.L1_BLOCK_NUMBER);
} else if (digest == keccak256(bytes("LegacyMessagePasser"))) {
return payable(Predeploys.LEGACY_MESSAGE_PASSER);
} else if (digest == keccak256(bytes("ProxyAdmin"))) {
return payable(Predeploys.PROXY_ADMIN);
} else if (digest == keccak256(bytes("BaseFeeVault"))) {
return payable(Predeploys.BASE_FEE_VAULT);
} else if (digest == keccak256(bytes("L1FeeVault"))) {
return payable(Predeploys.L1_FEE_VAULT);
} else if (digest == keccak256(bytes("GovernanceToken"))) {
return payable(Predeploys.GOVERNANCE_TOKEN);
} else if (digest == keccak256(bytes("SchemaRegistry"))) {
return payable(Predeploys.SCHEMA_REGISTRY);
} else if (digest == keccak256(bytes("EAS"))) {
return payable(Predeploys.EAS);
}
return payable(address(0));
}
/// @notice Returns the address of a deployment and reverts if the deployment
/// does not exist.
/// @return The address of the deployment.
function mustGetAddress(string memory _name) public view returns (address payable) {
address addr = getAddress(_name);
if (addr == address(0)) {
revert DeploymentDoesNotExist(_name);
}
return payable(addr);
}
/// @notice Returns a deployment that is suitable to be used to interact with contracts.
/// @param _name The name of the deployment.
/// @return The deployment.
function get(string memory _name) public view returns (Deployment memory) {
Deployment memory deployment = _namedDeployments[_name];
if (deployment.addr != address(0)) {
return deployment;
} else {
return _getExistingDeployment(_name);
}
}
/// @notice Writes a deployment to disk as a temp deployment so that the
/// hardhat deploy artifact can be generated afterwards.
/// @param _name The name of the deployment.
/// @param _deployed The address of the deployment.
function save(string memory _name, address _deployed) public {
if (bytes(_name).length == 0) {
revert InvalidDeployment("EmptyName");
}
if (bytes(_namedDeployments[_name].name).length > 0) {
revert InvalidDeployment("AlreadyExists");
}
Deployment memory deployment = Deployment({ name: _name, addr: payable(_deployed) });
_namedDeployments[_name] = deployment;
_newDeployments.push(deployment);
_writeTemp(_name, _deployed);
}
/// @notice Reads the temp deployments from disk that were generated
/// by the deploy script.
/// @return An array of deployments.
function _getTempDeployments() internal returns (Deployment[] memory) {
string memory json = vm.readFile(tempDeploymentsPath);
string[] memory cmd = new string[](3);
cmd[0] = Executables.bash;
cmd[1] = "-c";
cmd[2] = string.concat(Executables.jq, " 'keys' <<< '", json, "'");
bytes memory res = vm.ffi(cmd);
string[] memory names = stdJson.readStringArray(string(res), "");
Deployment[] memory deployments = new Deployment[](names.length);
for (uint256 i; i < names.length; i++) {
string memory contractName = names[i];
address addr = stdJson.readAddress(json, string.concat("$.", contractName));
deployments[i] = Deployment({ name: contractName, addr: payable(addr) });
}
return deployments;
}
/// @notice Adds a deployment to the temp deployments file
function _writeTemp(string memory _name, address _deployed) internal {
vm.writeJson({ json: stdJson.serialize("", _name, _deployed), path: tempDeploymentsPath });
}
/// @notice Reads the artifact from the filesystem by name and returns the address.
/// @param _name The name of the artifact to read.
/// @return The address of the artifact.
function _getExistingDeploymentAddress(string memory _name) internal view returns (address payable) {
return _getExistingDeployment(_name).addr;
}
/// @notice Reads the artifact from the filesystem by name and returns the Deployment.
/// @param _name The name of the artifact to read.
/// @return The deployment corresponding to the name.
function _getExistingDeployment(string memory _name) internal view returns (Deployment memory) {
string memory path = string.concat(deploymentsDir, "/", _name, ".json");
try vm.readFile(path) returns (string memory json) {
bytes memory addr = stdJson.parseRaw(json, "$.address");
return Deployment({ addr: abi.decode(addr, (address)), name: _name });
} catch {
return Deployment({ addr: payable(address(0)), name: "" });
}
}
/// @notice Stubs a deployment retrieved through `get`.
/// @param _name The name of the deployment.
/// @param _addr The mock address of the deployment.
function prankDeployment(string memory _name, address _addr) public {
if (bytes(_name).length == 0) {
revert InvalidDeployment("EmptyName");
}
Deployment memory deployment = Deployment({ name: _name, addr: payable(_addr) });
_namedDeployments[_name] = deployment;
}
/// @notice The context of the deployment is used to namespace the artifacts.
/// An unknown context will use the chainid as the context name.
/// This is legacy code and should be removed in the future.
function _getDeploymentContext() private view returns (string memory) {
string memory context = vm.envOr("DEPLOYMENT_CONTEXT", string(""));
if (bytes(context).length > 0) {
return context;
}
uint256 chainid = vm.envOr("CHAIN_ID", block.chainid);
if (chainid == Chains.Mainnet) {
return "mainnet";
} else if (chainid == Chains.Goerli) {
return "goerli";
} else if (chainid == Chains.OPGoerli) {
return "optimism-goerli";
} else if (chainid == Chains.OPMainnet) {
return "optimism-mainnet";
} else if (chainid == Chains.LocalDevnet || chainid == Chains.GethDevnet) {
return "devnetL1";
} else if (chainid == Chains.Hardhat) {
return "hardhat";
} else if (chainid == Chains.Sepolia) {
return "sepolia";
} else if (chainid == Chains.OPSepolia) {
return "optimism-sepolia";
} else {
return vm.toString(chainid);
}
}
}
...@@ -45,6 +45,7 @@ import "src/libraries/DisputeTypes.sol"; ...@@ -45,6 +45,7 @@ import "src/libraries/DisputeTypes.sol";
import { ChainAssertions } from "scripts/ChainAssertions.sol"; import { ChainAssertions } from "scripts/ChainAssertions.sol";
import { Types } from "scripts/Types.sol"; import { Types } from "scripts/Types.sol";
import { LibStateDiff } from "scripts/libraries/LibStateDiff.sol"; import { LibStateDiff } from "scripts/libraries/LibStateDiff.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
/// @title Deploy /// @title Deploy
/// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function. /// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function.
...@@ -408,8 +409,7 @@ contract Deploy is Deployer { ...@@ -408,8 +409,7 @@ contract Deploy is Deployer {
address proxyAdmin = mustGetAddress("ProxyAdmin"); address proxyAdmin = mustGetAddress("ProxyAdmin");
L1ChugSplashProxy proxy = new L1ChugSplashProxy(proxyAdmin); L1ChugSplashProxy proxy = new L1ChugSplashProxy(proxyAdmin);
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY)))); require(EIP1967Helper.getAdmin(address(proxy)) == proxyAdmin);
require(admin == proxyAdmin);
save("L1StandardBridgeProxy", address(proxy)); save("L1StandardBridgeProxy", address(proxy));
console.log("L1StandardBridgeProxy deployed at %s", address(proxy)); console.log("L1StandardBridgeProxy deployed at %s", address(proxy));
...@@ -433,8 +433,7 @@ contract Deploy is Deployer { ...@@ -433,8 +433,7 @@ contract Deploy is Deployer {
address proxyAdmin = mustGetAddress("ProxyAdmin"); address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({ _admin: proxyAdmin }); Proxy proxy = new Proxy({ _admin: proxyAdmin });
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY)))); require(EIP1967Helper.getAdmin(address(proxy)) == proxyAdmin);
require(admin == proxyAdmin);
save(_name, address(proxy)); save(_name, address(proxy));
console.log(" at %s", address(proxy)); console.log(" at %s", address(proxy));
......
...@@ -15,6 +15,7 @@ import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow. ...@@ -15,6 +15,7 @@ import { CheckGelatoLow } from "src/periphery/drippie/dripchecks/CheckGelatoLow.
import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol"; import { CheckBalanceLow } from "src/periphery/drippie/dripchecks/CheckBalanceLow.sol";
import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
/// @title DeployPeriphery /// @title DeployPeriphery
/// @notice Script used to deploy periphery contracts. /// @notice Script used to deploy periphery contracts.
...@@ -113,8 +114,7 @@ contract DeployPeriphery is Deployer { ...@@ -113,8 +114,7 @@ contract DeployPeriphery is Deployer {
addr_ = preComputedAddress; addr_ = preComputedAddress;
} else { } else {
Proxy proxy = new Proxy{ salt: salt }({ _admin: proxyAdmin }); Proxy proxy = new Proxy{ salt: salt }({ _admin: proxyAdmin });
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY)))); require(EIP1967Helper.getAdmin(address(proxy)) == proxyAdmin);
require(admin == proxyAdmin);
save("FaucetProxy", address(proxy)); save("FaucetProxy", address(proxy));
console.log("FaucetProxy deployed at %s", address(proxy)); console.log("FaucetProxy deployed at %s", address(proxy));
......
...@@ -5,17 +5,10 @@ import { Script } from "forge-std/Script.sol"; ...@@ -5,17 +5,10 @@ import { Script } from "forge-std/Script.sol";
import { stdJson } from "forge-std/StdJson.sol"; import { stdJson } from "forge-std/StdJson.sol";
import { console2 as console } from "forge-std/console2.sol"; import { console2 as console } from "forge-std/console2.sol";
import { Executables } from "scripts/Executables.sol"; import { Executables } from "scripts/Executables.sol";
import { Chains } from "scripts/Chains.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
import { IAddressManager } from "scripts/interfaces/IAddressManager.sol"; import { IAddressManager } from "scripts/interfaces/IAddressManager.sol";
import { LibString } from "solady/utils/LibString.sol"; import { LibString } from "solady/utils/LibString.sol";
import { Artifacts, Deployment } from "scripts/Artifacts.s.sol";
/// @notice store the new deployment to be saved
struct Deployment {
string name;
address payable addr;
}
/// @notice A `hardhat-deploy` style artifact /// @notice A `hardhat-deploy` style artifact
struct Artifact { struct Artifact {
...@@ -51,34 +44,13 @@ struct StorageSlot { ...@@ -51,34 +44,13 @@ struct StorageSlot {
/// When a contract is deployed, call the `save` function to write its name and /// When a contract is deployed, call the `save` function to write its name and
/// contract address to disk. Then the `sync` function can be called to generate /// contract address to disk. Then the `sync` function can be called to generate
/// hardhat deploy style artifacts. Forked from `forge-deploy`. /// hardhat deploy style artifacts. Forked from `forge-deploy`.
abstract contract Deployer is Script { abstract contract Deployer is Script, Artifacts {
/// @notice The set of deployments that have been done during execution.
mapping(string => Deployment) internal _namedDeployments;
/// @notice The same as `_namedDeployments` but as an array.
Deployment[] internal _newDeployments;
/// @notice The namespace for the deployment. Can be set with the env var DEPLOYMENT_CONTEXT.
string internal deploymentContext;
/// @notice Path to the deploy artifact generated by foundry /// @notice Path to the deploy artifact generated by foundry
string internal deployPath; string internal deployPath;
/// @notice Path to the directory containing the hh deploy style artifacts
string internal deploymentsDir;
/// @notice The name of the deploy script that sends the transactions. /// @notice The name of the deploy script that sends the transactions.
/// Can be modified with the env var DEPLOY_SCRIPT /// Can be modified with the env var DEPLOY_SCRIPT
string internal deployScript; string internal deployScript;
/// @notice The path to the temp deployments file
string internal tempDeploymentsPath;
/// @notice Error for when attempting to fetch a deployment and it does not exist
error DeploymentDoesNotExist(string);
/// @notice Error for when trying to save an invalid deployment
error InvalidDeployment(string);
/// @notice The storage slot that holds the address of the implementation.
/// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant IMPLEMENTATION_KEY = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice The storage slot that holds the address of the owner.
/// bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)
bytes32 internal constant OWNER_KEY = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/// @notice Create the global variables and set up the filesystem. /// @notice Create the global variables and set up the filesystem.
/// Forge script will create a file where the prefix is the /// Forge script will create a file where the prefix is the
...@@ -87,64 +59,17 @@ abstract contract Deployer is Script { ...@@ -87,64 +59,17 @@ abstract contract Deployer is Script {
/// env var to specify what function signature was called so that /// env var to specify what function signature was called so that
/// the `sync()` method can be used to create hardhat deploy style /// the `sync()` method can be used to create hardhat deploy style
/// artifacts. /// artifacts.
function setUp() public virtual { function setUp() public virtual override {
string memory root = vm.projectRoot(); Artifacts.setUp();
deployScript = vm.envOr("DEPLOY_SCRIPT", name()); deployScript = vm.envOr("DEPLOY_SCRIPT", name());
deploymentContext = _getDeploymentContext();
string memory sig = vm.envOr("SIG", string("run")); string memory sig = vm.envOr("SIG", string("run"));
string memory deployFile = vm.envOr("DEPLOY_FILE", string.concat(sig, "-latest.json")); string memory deployFile = vm.envOr("DEPLOY_FILE", string.concat(sig, "-latest.json"));
uint256 chainId = vm.envOr("CHAIN_ID", block.chainid); uint256 chainId = vm.envOr("CHAIN_ID", block.chainid);
deployPath = string.concat(root, "/broadcast/", deployScript, ".s.sol/", vm.toString(chainId), "/", deployFile); deployPath = string.concat(
vm.projectRoot(), "/broadcast/", deployScript, ".s.sol/", vm.toString(chainId), "/", deployFile
deploymentsDir = string.concat(root, "/deployments/", deploymentContext); );
try vm.createDir(deploymentsDir, true) { } catch (bytes memory) { }
string memory chainIdPath = string.concat(deploymentsDir, "/.chainId");
try vm.readFile(chainIdPath) returns (string memory localChainId) {
if (vm.envOr("STRICT_DEPLOYMENT", true)) {
require(
vm.parseUint(localChainId) == chainId,
string.concat("Misconfigured networks: ", localChainId, " != ", vm.toString(chainId))
);
}
} catch {
vm.writeFile(chainIdPath, vm.toString(chainId));
}
console.log("Connected to network with chainid %s", chainId);
tempDeploymentsPath = string.concat(deploymentsDir, "/.deploy");
try vm.readFile(tempDeploymentsPath) returns (string memory) { }
catch {
vm.writeJson("{}", tempDeploymentsPath);
}
console.log("Storing temp deployment data in %s", tempDeploymentsPath);
// Load addresses from a JSON file if the CONTRACT_ADDRESSES_PATH environment variable
// is set. Great for loading addresses from `superchain-registry`.
string memory addresses = vm.envOr("CONTRACT_ADDRESSES_PATH", string(""));
if (bytes(addresses).length > 0) {
console.log("Loading addresses from %s", addresses);
_loadAddresses(addresses);
}
}
/// @notice Populates the addresses to be used in a script based on a JSON file.
/// The format of the JSON file is the same that it output by this script
/// as well as the JSON files that contain addresses in the `superchain-ops`
/// repo. The JSON key is the name of the contract and the value is an address.
function _loadAddresses(string memory _path) internal {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq -cr < ", _path);
string memory json = string(vm.ffi(commands));
string[] memory keys = vm.parseJsonKeys(json, "");
for (uint256 i; i < keys.length; i++) {
string memory key = keys[i];
address addr = stdJson.readAddress(json, string.concat("$.", key));
save(key, addr);
}
} }
/// @notice Call this function to sync the deployment artifacts such that /// @notice Call this function to sync the deployment artifacts such that
...@@ -211,158 +136,6 @@ abstract contract Deployer is Script { ...@@ -211,158 +136,6 @@ abstract contract Deployer is Script {
/// name inside of the `broadcast` directory when looking up deployment artifacts. /// name inside of the `broadcast` directory when looking up deployment artifacts.
function name() public pure virtual returns (string memory); function name() public pure virtual returns (string memory);
/// @notice Returns all of the deployments done in the current context.
function newDeployments() external view returns (Deployment[] memory) {
return _newDeployments;
}
/// @notice Returns whether or not a particular deployment exists.
/// @param _name The name of the deployment.
/// @return Whether the deployment exists or not.
function has(string memory _name) public view returns (bool) {
Deployment memory existing = _namedDeployments[_name];
if (existing.addr != address(0)) {
return bytes(existing.name).length > 0;
}
return _getExistingDeploymentAddress(_name) != address(0);
}
/// @notice Returns the address of a deployment. Also handles the predeploys.
/// @param _name The name of the deployment.
/// @return The address of the deployment. May be `address(0)` if the deployment does not
/// exist.
function getAddress(string memory _name) public view returns (address payable) {
Deployment memory existing = _namedDeployments[_name];
if (existing.addr != address(0)) {
if (bytes(existing.name).length == 0) {
return payable(address(0));
}
return existing.addr;
}
address addr = _getExistingDeploymentAddress(_name);
if (addr != address(0)) return payable(addr);
bytes32 digest = keccak256(bytes(_name));
if (digest == keccak256(bytes("L2CrossDomainMessenger"))) {
return payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER);
} else if (digest == keccak256(bytes("L2ToL1MessagePasser"))) {
return payable(Predeploys.L2_TO_L1_MESSAGE_PASSER);
} else if (digest == keccak256(bytes("L2StandardBridge"))) {
return payable(Predeploys.L2_STANDARD_BRIDGE);
} else if (digest == keccak256(bytes("L2ERC721Bridge"))) {
return payable(Predeploys.L2_ERC721_BRIDGE);
} else if (digest == keccak256(bytes("SequencerFeeWallet"))) {
return payable(Predeploys.SEQUENCER_FEE_WALLET);
} else if (digest == keccak256(bytes("OptimismMintableERC20Factory"))) {
return payable(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY);
} else if (digest == keccak256(bytes("OptimismMintableERC721Factory"))) {
return payable(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);
} else if (digest == keccak256(bytes("L1Block"))) {
return payable(Predeploys.L1_BLOCK_ATTRIBUTES);
} else if (digest == keccak256(bytes("GasPriceOracle"))) {
return payable(Predeploys.GAS_PRICE_ORACLE);
} else if (digest == keccak256(bytes("L1MessageSender"))) {
return payable(Predeploys.L1_MESSAGE_SENDER);
} else if (digest == keccak256(bytes("DeployerWhitelist"))) {
return payable(Predeploys.DEPLOYER_WHITELIST);
} else if (digest == keccak256(bytes("WETH9"))) {
return payable(Predeploys.WETH9);
} else if (digest == keccak256(bytes("LegacyERC20ETH"))) {
return payable(Predeploys.LEGACY_ERC20_ETH);
} else if (digest == keccak256(bytes("L1BlockNumber"))) {
return payable(Predeploys.L1_BLOCK_NUMBER);
} else if (digest == keccak256(bytes("LegacyMessagePasser"))) {
return payable(Predeploys.LEGACY_MESSAGE_PASSER);
} else if (digest == keccak256(bytes("ProxyAdmin"))) {
return payable(Predeploys.PROXY_ADMIN);
} else if (digest == keccak256(bytes("BaseFeeVault"))) {
return payable(Predeploys.BASE_FEE_VAULT);
} else if (digest == keccak256(bytes("L1FeeVault"))) {
return payable(Predeploys.L1_FEE_VAULT);
} else if (digest == keccak256(bytes("GovernanceToken"))) {
return payable(Predeploys.GOVERNANCE_TOKEN);
} else if (digest == keccak256(bytes("SchemaRegistry"))) {
return payable(Predeploys.SCHEMA_REGISTRY);
} else if (digest == keccak256(bytes("EAS"))) {
return payable(Predeploys.EAS);
}
return payable(address(0));
}
/// @notice Returns the address of a deployment and reverts if the deployment
/// does not exist.
/// @return The address of the deployment.
function mustGetAddress(string memory _name) public view returns (address payable) {
address addr = getAddress(_name);
if (addr == address(0)) {
revert DeploymentDoesNotExist(_name);
}
return payable(addr);
}
/// @notice Returns a deployment that is suitable to be used to interact with contracts.
/// @param _name The name of the deployment.
/// @return The deployment.
function get(string memory _name) public view returns (Deployment memory) {
Deployment memory deployment = _namedDeployments[_name];
if (deployment.addr != address(0)) {
return deployment;
} else {
return _getExistingDeployment(_name);
}
}
/// @notice Writes a deployment to disk as a temp deployment so that the
/// hardhat deploy artifact can be generated afterwards.
/// @param _name The name of the deployment.
/// @param _deployed The address of the deployment.
function save(string memory _name, address _deployed) public {
if (bytes(_name).length == 0) {
revert InvalidDeployment("EmptyName");
}
if (bytes(_namedDeployments[_name].name).length > 0) {
revert InvalidDeployment("AlreadyExists");
}
Deployment memory deployment = Deployment({ name: _name, addr: payable(_deployed) });
_namedDeployments[_name] = deployment;
_newDeployments.push(deployment);
_writeTemp(_name, _deployed);
}
/// @notice Stubs a deployment retrieved through `get`.
/// @param _name The name of the deployment.
/// @param _addr The mock address of the deployment.
function prankDeployment(string memory _name, address _addr) public {
if (bytes(_name).length == 0) {
revert InvalidDeployment("EmptyName");
}
Deployment memory deployment = Deployment({ name: _name, addr: payable(_addr) });
_namedDeployments[_name] = deployment;
}
/// @notice Reads the temp deployments from disk that were generated
/// by the deploy script.
/// @return An array of deployments.
function _getTempDeployments() internal returns (Deployment[] memory) {
string memory json = vm.readFile(tempDeploymentsPath);
string[] memory cmd = new string[](3);
cmd[0] = Executables.bash;
cmd[1] = "-c";
cmd[2] = string.concat(Executables.jq, " 'keys' <<< '", json, "'");
bytes memory res = vm.ffi(cmd);
string[] memory names = stdJson.readStringArray(string(res), "");
Deployment[] memory deployments = new Deployment[](names.length);
for (uint256 i; i < names.length; i++) {
string memory contractName = names[i];
address addr = stdJson.readAddress(json, string.concat("$.", contractName));
deployments[i] = Deployment({ name: contractName, addr: payable(addr) });
}
return deployments;
}
/// @notice Returns the json of the deployment transaction given a contract address. /// @notice Returns the json of the deployment transaction given a contract address.
function _getDeployTransactionByContractAddress(address _addr) internal returns (string memory) { function _getDeployTransactionByContractAddress(address _addr) internal returns (string memory) {
string[] memory cmd = new string[](3); string[] memory cmd = new string[](3);
...@@ -596,11 +369,6 @@ abstract contract Deployer is Script { ...@@ -596,11 +369,6 @@ abstract contract Deployer is Script {
initialized_ = uint8((uint256(slotVal) >> (slot.offset * 8)) & 0xFF); initialized_ = uint8((uint256(slotVal) >> (slot.offset * 8)) & 0xFF);
} }
/// @notice Adds a deployment to the temp deployments file
function _writeTemp(string memory _name, address _deployed) internal {
vm.writeJson({ json: stdJson.serialize("", _name, _deployed), path: tempDeploymentsPath });
}
/// @notice Turns an Artifact into a json serialized string /// @notice Turns an Artifact into a json serialized string
/// @param _artifact The artifact to serialize /// @param _artifact The artifact to serialize
/// @return The json serialized string /// @return The json serialized string
...@@ -621,54 +389,4 @@ abstract contract Deployer is Script { ...@@ -621,54 +389,4 @@ abstract contract Deployer is Script {
json = stdJson.serialize("", "userdoc", _artifact.userdoc); json = stdJson.serialize("", "userdoc", _artifact.userdoc);
return json; return json;
} }
/// @notice The context of the deployment is used to namespace the artifacts.
/// An unknown context will use the chainid as the context name.
function _getDeploymentContext() private view returns (string memory) {
string memory context = vm.envOr("DEPLOYMENT_CONTEXT", string(""));
if (bytes(context).length > 0) {
return context;
}
uint256 chainid = vm.envOr("CHAIN_ID", block.chainid);
if (chainid == Chains.Mainnet) {
return "mainnet";
} else if (chainid == Chains.Goerli) {
return "goerli";
} else if (chainid == Chains.OPGoerli) {
return "optimism-goerli";
} else if (chainid == Chains.OPMainnet) {
return "optimism-mainnet";
} else if (chainid == Chains.LocalDevnet || chainid == Chains.GethDevnet) {
return "devnetL1";
} else if (chainid == Chains.Hardhat) {
return "hardhat";
} else if (chainid == Chains.Sepolia) {
return "sepolia";
} else if (chainid == Chains.OPSepolia) {
return "optimism-sepolia";
} else {
return vm.toString(chainid);
}
}
/// @notice Reads the artifact from the filesystem by name and returns the address.
/// @param _name The name of the artifact to read.
/// @return The address of the artifact.
function _getExistingDeploymentAddress(string memory _name) internal view returns (address payable) {
return _getExistingDeployment(_name).addr;
}
/// @notice Reads the artifact from the filesystem by name and returns the Deployment.
/// @param _name The name of the artifact to read.
/// @return The deployment corresponding to the name.
function _getExistingDeployment(string memory _name) internal view returns (Deployment memory) {
string memory path = string.concat(deploymentsDir, "/", _name, ".json");
try vm.readFile(path) returns (string memory json) {
bytes memory addr = stdJson.parseRaw(json, "$.address");
return Deployment({ addr: abi.decode(addr, (address)), name: _name });
} catch {
return Deployment({ addr: payable(address(0)), name: "" });
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment