Commit 7c495418 authored by Mark Tyneway's avatar Mark Tyneway

wip: foundry deploy

Get the end to end structure of foundry deploy working in CI.
Can deploy the entire system in a single block.
parent ec33fd92
......@@ -8,6 +8,7 @@ import calendar
import datetime
import time
import shutil
import http.client
import devnet.log_setup
from devnet.genesis import GENESIS_TMPL
......@@ -51,6 +52,10 @@ def main():
os.makedirs(devnet_dir, exist_ok=True)
run_command(['docker-compose', 'build', '--progress', 'plain'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir
})
if args.deploy:
log.info('Devnet with upcoming smart contract deployments')
devnet_deploy(paths)
......@@ -114,6 +119,7 @@ def devnet_deploy(paths):
'PWD': paths.ops_bedrock_dir
})
wait_up(8545)
wait_for_rpc_server('127.0.0.1:8545')
log.info('Generating network config.')
devnet_cfg_orig = pjoin(paths.contracts_bedrock_dir, 'deploy-config', 'devnetL1.json')
......@@ -129,11 +135,18 @@ def devnet_deploy(paths):
addresses = read_json(paths.addresses_json_path)
else:
log.info('Deploying contracts.')
run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy', '--tags', 'l1'], env={
'CHAIN_ID': '900',
'L1_RPC': 'http://localhost:8545',
'PRIVATE_KEY_DEPLOYER': 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
}, cwd=paths.contracts_bedrock_dir)
run_command([
'forge', 'script', 'scripts/DeployConfig.s.sol',
'--tc', 'Deploy', '--private-key', '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
'--broadcast', '--rpc-url', 'http://127.0.0.1:8545'
], env={}, cwd=paths.contracts_bedrock_dir)
run_command([
'forge', 'script', 'scripts/DeployConfig.s.sol',
'--tc', 'Deploy', '--private-key', '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
'--sig', 'sync()', '--rpc-url', 'http://127.0.0.1:8545', '--broadcast'
], env={}, cwd=paths.contracts_bedrock_dir)
contracts = os.listdir(paths.deployment_dir)
addresses = {}
for c in contracts:
......@@ -148,12 +161,14 @@ def devnet_deploy(paths):
'CanonicalTransactionChain': '0x0000000000000000000000000000000000000000',
'BondManager': '0x0000000000000000000000000000000000000000',
})
sdk_addresses['L1CrossDomainMessenger'] = addresses['Proxy__OVM_L1CrossDomainMessenger']
sdk_addresses['L1StandardBridge'] = addresses['Proxy__OVM_L1StandardBridge']
sdk_addresses['L1CrossDomainMessenger'] = addresses['L1CrossDomainMessengerProxy']
sdk_addresses['L1StandardBridge'] = addresses['L1StandardBridgeProxy']
sdk_addresses['OptimismPortal'] = addresses['OptimismPortalProxy']
sdk_addresses['L2OutputOracle'] = addresses['L2OutputOracleProxy']
write_json(paths.addresses_json_path, addresses)
write_json(paths.sdk_addresses_json_path, sdk_addresses)
log.info(f'Wrote sdk addresses to {paths.sdk_addresses_json_path}')
if os.path.exists(paths.genesis_l2_path):
log.info('L2 genesis and rollup configs already generated.')
......@@ -178,6 +193,7 @@ def devnet_deploy(paths):
'PWD': paths.ops_bedrock_dir
})
wait_up(9545)
wait_for_rpc_server('127.0.0.1:9545')
log.info('Bringing up everything else.')
run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
......@@ -189,6 +205,26 @@ def devnet_deploy(paths):
log.info('Devnet ready.')
def wait_for_rpc_server(url):
log.info(f'Waiting for RPC server at {url}')
conn = http.client.HTTPConnection(url)
headers = {'Content-type': 'application/json'}
body = '{"id":1, "jsonrpc":"2.0", "method": "eth_chainId", "params":[]}'
while True:
try:
conn.request('POST', '/', body, headers)
response = conn.getresponse()
conn.close()
if response.status < 300:
log.info(f'RPC server at {url} ready')
return
except Exception as e:
log.info(f'Waiting for RPC server at {url}')
time.sleep(1)
def run_command(args, check=True, shell=False, cwd=None, env=None):
env = env if env else {}
return subprocess.run(
......
......@@ -19,7 +19,7 @@ type Deployment struct {
DeployedBytecode hexutil.Bytes `json:"deployedBytecode"`
Devdoc json.RawMessage `json:"devdoc"`
Metadata string `json:"metadata"`
Receipt Receipt `json:"receipt"`
Receipt json.RawMessage `json:"receipt"`
SolcInputHash string `json:"solcInputHash"`
StorageLayout solc.StorageLayout `json:"storageLayout"`
TransactionHash common.Hash `json:"transactionHash"`
......
......@@ -239,16 +239,16 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
}
}
if config.FundDevAccounts {
FundDevAccounts(memDB)
SetPrecompileBalances(memDB)
}
stateDB, err := backend.Blockchain().State()
if err != nil {
return nil, err
}
if config.FundDevAccounts {
FundDevAccounts(stateDB)
SetPrecompileBalances(stateDB)
}
for _, dep := range deployments {
st, err := stateDB.StorageTrie(dep.Address)
if err != nil {
......
......@@ -13,3 +13,5 @@ deploy-config/mainnet-forked.json
test-case-generator/fuzz
.resource-metering.csv
scripts/differential-testing/differential-testing
deployments/900
deployments/901
......@@ -10,6 +10,7 @@
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
"l2GenesisBlockGasLimit": "0x1c9c380",
......@@ -40,7 +41,7 @@
"governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"eip1559Denominator": 8,
"eip1559Elasticity": 2,
"l1GenesisBlockTimestamp": "0x648a0943",
"l1GenesisBlockTimestamp": "0x64935846",
"l1StartingBlockTag": "earliest",
"l2GenesisRegolithTimeOffset": "0x0"
}
\ No newline at end of file
......@@ -16,6 +16,8 @@
"l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": 0,
"l2OutputOracleStartingBlockNumber": 0,
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x6925B8704Ff96DEe942623d6FB5e946EF5884b63",
"l2GenesisBlockBaseFeePerGas": "0x3B9ACA00",
......@@ -34,5 +36,8 @@
"governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"finalizationPeriodSeconds": 2,
"numDeployConfirmations": 1
"numDeployConfirmations": 1,
"eip1559Denominator": 50,
"eip1559Elasticity": 10,
"l2GenesisRegolithTimeOffset": "0x0"
}
\ No newline at end of file
......@@ -22,7 +22,10 @@ no_match_contract = 'EchidnaFuzz'
fs_permissions = [
{ 'access'='read-write', 'path'='./.resource-metering.csv' },
{ 'access'='read', 'path'='./deployments/' },
{ 'access'='read-write', 'path'='./deployments/' },
{ 'access'='read', 'path'='./deploy-config/' },
{ 'access'='read', 'path'='./broadcast/' },
{ access = 'read', path = './forge-artifacts/' },
]
[profile.ci]
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Script } from "forge-std/Script.sol";
import { Test } from "forge-std/Test.sol";
import { console2 as console } from "forge-std/console2.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { Deployer } from "./Deployer.sol";
import { ProxyAdmin } from "../contracts/universal/ProxyAdmin.sol";
import { AddressManager } from "../contracts/legacy/AddressManager.sol";
import { L1ChugSplashProxy } from "../contracts/legacy/L1ChugSplashProxy.sol";
import { Proxy } from "../contracts/universal/Proxy.sol";
import { L1StandardBridge } from "../contracts/L1/L1StandardBridge.sol";
import { OptimismPortal } from "../contracts/L1/OptimismPortal.sol";
import { L1ChugSplashProxy } from "../contracts/legacy/L1ChugSplashProxy.sol";
import { ResolvedDelegateProxy } from "../contracts/legacy/ResolvedDelegateProxy.sol";
import { L1CrossDomainMessenger } from "../contracts/L1/L1CrossDomainMessenger.sol";
import { OptimismPortal } from "../contracts/L1/OptimismPortal.sol";
import { L2OutputOracle } from "../contracts/L1/L2OutputOracle.sol";
import { OptimismMintableERC20Factory } from "../contracts/universal/OptimismMintableERC20Factory.sol";
import { SystemConfig } from "../contracts/L1/SystemConfig.sol";
import { ResourceMetering } from "../contracts/L1/ResourceMetering.sol";
import { Constants } from "../contracts/libraries/Constants.sol";
import { DisputeGameFactory } from "../contracts/dispute/DisputeGameFactory.sol";
import { L1StandardBridge } from "../contracts/L1/L1StandardBridge.sol";
import { L1ERC721Bridge } from "../contracts/L1/L1ERC721Bridge.sol";
import { Predeploys } from "../contracts/libraries/Predeploys.sol";
/// @title DeployConfig
/// @notice It would be a lot more code to have default values if they are not set in the json file.
contract DeployConfig is Script {
string internal _json;
address public finalSystemOwner;
address public controller;
address public portalGuardian;
uint256 public l1ChainID;
uint256 public l2ChainID;
uint256 public l2BlockTime;
uint256 public maxSequencerDrift;
uint256 public sequencerWindowSize;
uint256 public channelTimeout;
address public p2pSequencerAddress;
address public batchInboxAddress;
address public batchSenderAddress;
uint256 public l2OutputOracleSubmissionInterval;
int256 internal _l2OutputOracleStartingTimestamp;
uint256 public l2OutputOracleStartingBlockNumber;
address public l2OutputOracleProposer;
address public l2OutputOracleChallenger;
uint256 public finalizationPeriodSeconds;
address public proxyAdminOwner;
address public baseFeeVaultRecipient;
address public l1FeeVaultRecipient;
address public sequencerFeeVaultRecipient;
string public governanceTokenName;
string public governanceTokenSymbol;
address public governanceTokenOwner;
uint256 public l2GenesisBlockGasLimit;
uint256 public l2GenesisBlockBaseFeePerGas;
uint256 public gasPriceOracleOverhead;
uint256 public gasPriceOracleScalar;
uint256 public eip1559Denominator;
uint256 public eip1559Elasticity;
uint256 public l2GenesisRegolithTimeOffset;
constructor(string memory _path) {
_json = vm.readFile(_path);
finalSystemOwner = stdJson.readAddress(_json, "$.finalSystemOwner");
controller = stdJson.readAddress(_json, "$.controller");
portalGuardian = stdJson.readAddress(_json, "$.portalGuardian");
l1ChainID = stdJson.readUint(_json, "$.l1ChainID");
l2ChainID = stdJson.readUint(_json, "$.l2ChainID");
l2BlockTime = stdJson.readUint(_json, "$.l2BlockTime");
maxSequencerDrift = stdJson.readUint(_json, "$.maxSequencerDrift");
sequencerWindowSize = stdJson.readUint(_json, "$.sequencerWindowSize");
channelTimeout = stdJson.readUint(_json, "$.channelTimeout");
p2pSequencerAddress = stdJson.readAddress(_json, "$.p2pSequencerAddress");
batchInboxAddress = stdJson.readAddress(_json, "$.batchInboxAddress");
batchSenderAddress = stdJson.readAddress(_json, "$.batchSenderAddress");
l2OutputOracleSubmissionInterval = stdJson.readUint(_json, "$.l2OutputOracleSubmissionInterval");
_l2OutputOracleStartingTimestamp = stdJson.readInt(_json, "$.l2OutputOracleStartingTimestamp");
l2OutputOracleStartingBlockNumber = stdJson.readUint(_json, "$.l2OutputOracleStartingBlockNumber");
l2OutputOracleProposer = stdJson.readAddress(_json, "$.l2OutputOracleProposer");
l2OutputOracleChallenger = stdJson.readAddress(_json, "$.l2OutputOracleChallenger");
finalizationPeriodSeconds = stdJson.readUint(_json, "$.finalizationPeriodSeconds");
proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner");
baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient");
l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient");
sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient");
governanceTokenName = stdJson.readString(_json, "$.governanceTokenName");
governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol");
governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner");
l2GenesisBlockGasLimit = stdJson.readUint(_json, "$.l2GenesisBlockGasLimit");
l2GenesisBlockBaseFeePerGas = stdJson.readUint(_json, "$.l2GenesisBlockBaseFeePerGas");
gasPriceOracleOverhead = stdJson.readUint(_json, "$.gasPriceOracleOverhead");
gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar");
eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator");
eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity");
l2GenesisRegolithTimeOffset = stdJson.readUint(_json, "$.l2GenesisRegolithTimeOffset");
}
function l1StartingBlockTag() public returns (bytes32) {
try vm.parseJsonBytes32(_json, "$.l1StartingBlockTag") returns (bytes32 tag) {
return tag;
} catch {
try vm.parseJsonString(_json, "$.l1StartingBlockTag") returns (string memory tag) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("cast block ", tag, " --json | jq -r .hash");
bytes memory res = vm.ffi(cmd);
return abi.decode(res, (bytes32));
} catch {
try vm.parseJsonUint(_json, "$.l1StartingBlockTag") returns (uint256 tag) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("cast block ", vm.toString(tag), " --json | jq -r .hash");
bytes memory res = vm.ffi(cmd);
return abi.decode(res, (bytes32));
} catch {}
}
}
revert("l1StartingBlockTag must be a bytes32, string or uint256");
}
function l2OutputOracleStartingTimestamp() public returns (uint256) {
if (_l2OutputOracleStartingTimestamp < 0) {
bytes32 tag = l1StartingBlockTag();
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("cast block ", vm.toString(tag), " --json | jq .timestamp");
bytes memory res = vm.ffi(cmd);
return stdJson.readUint(string(res), "");
}
return uint256(_l2OutputOracleStartingTimestamp);
}
}
/// @title Deploy
/// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function.
/// To add a new contract to the system, add a public function that deploys that individual contract.
/// Then add a call to that function inside of `run`. Be sure to call the `save` function after each
/// deployment so that hardhat-deploy style artifacts can be generated using a call to `sync()`.
contract Deploy is Deployer {
DeployConfig cfg;
function setUp() public override {
super.setUp();
string memory path = string.concat(vm.projectRoot(), "/deploy-config/", deploymentContext, ".json");
cfg = new DeployConfig(path);
console.log("Deploying from %s", deployScript);
console.log("Deployment context: %s", deploymentContext);
}
function run() public {
deployAddressManager();
deployProxyAdmin();
deployOptimismPortalProxy();
deployL2OutputOracleProxy();
deploySystemConfigProxy();
deployL1StandardBridgeProxy();
deployL1CrossDomainMessengerProxy();
deployOptimismMintableERC20FactoryProxy();
deployL1ERC721BridgeProxy();
deployDisputeGameFactoryProxy();
deployOptimismPortal();
deployL1CrossDomainMessenger();
deployL2OutputOracle();
deployOptimismMintableERC20Factory();
deploySystemConfig();
deployL1StandardBridge();
deployL1ERC721Bridge();
deployDisputeGameFactory();
transferAddressManagerOwnership();
initializeDisputeGameFactory();
initializeSystemConfig();
initializeL1StandardBridge();
initializeL1ERC721Bridge();
initializeOptimismMintableERC20Factory();
initializeL1CrossDomainMessenger();
initializeL2OutputOracle();
initializeOptimismPortal();
transferProxyAdminOwnership();
}
/// @notice Modifier that wraps a function in broadcasting.
modifier broadcast() {
vm.startBroadcast();
_;
vm.stopBroadcast();
}
/// @notice Deploy the AddressManager
function deployAddressManager() broadcast() public returns (address) {
AddressManager manager = new AddressManager();
require(manager.owner() == msg.sender);
save("AddressManager", address(manager));
console.log("AddressManager deployed at %s", address(manager));
return address(manager);
}
/// @notice Deploy the ProxyAdmin
function deployProxyAdmin() broadcast() public returns (address) {
ProxyAdmin admin = new ProxyAdmin({
_owner: msg.sender
});
require(admin.owner() == msg.sender);
AddressManager addressManager = AddressManager(mustGetAddress("AddressManager"));
if (admin.addressManager() != addressManager) {
admin.setAddressManager(addressManager);
}
require(admin.addressManager() == addressManager);
save("ProxyAdmin", address(admin));
console.log("ProxyAdmin deployed at %s", address(admin));
return address(admin);
}
/// @notice Deploy the L1StandardBridgeProxy
function deployL1StandardBridgeProxy() broadcast() public returns (address) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
L1ChugSplashProxy proxy = new L1ChugSplashProxy(proxyAdmin);
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("L1StandardBridgeProxy", address(proxy));
console.log("L1StandardBridgeProxy deployed at %s", address(proxy));
return address(proxy);
}
/// @notice Deploy the L2OutputOracleProxy
function deployL2OutputOracleProxy() broadcast() public returns (address) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({
_admin: proxyAdmin
});
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("L2OutputOracleProxy", address(proxy));
console.log("L2OutputOracleProxy deployed at %s", address(proxy));
return address(proxy);
}
/// @notice Deploy the L1CrossDomainMessengerProxy
function deployL1CrossDomainMessengerProxy() broadcast() public returns (address) {
AddressManager addressManager = AddressManager(mustGetAddress("AddressManager"));
string memory name = "OVM_L1CrossDomainMessenger";
ResolvedDelegateProxy proxy = new ResolvedDelegateProxy(addressManager, name);
save("L1CrossDomainMessengerProxy", address(proxy));
console.log("L1CrossDomainMessengerProxy deployed at %s", address(proxy));
address addr = addressManager.getAddress(name);
if (addr != address(proxy)) {
addressManager.setAddress(name, address(proxy));
}
require(addressManager.getAddress(name) == address(proxy));
return address(proxy);
}
/// @notice Deploy the OptimismPortalProxy
function deployOptimismPortalProxy() broadcast() public returns (address) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({
_admin: proxyAdmin
});
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("OptimismPortalProxy", address(proxy));
console.log("OptimismPortalProxy deployed at %s", address(proxy));
return address(proxy);
}
/// @notice Deploy the OptimismMintableERC20FactoryProxy
function deployOptimismMintableERC20FactoryProxy() broadcast() public returns (address) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy(proxyAdmin);
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("OptimismMintableERC20FactoryProxy", address(proxy));
console.log("OptimismMintableERC20FactoryProxy deployed at %s", address(proxy));
return address(proxy);
}
/// @notice Deploy the L1ERC721BridgeProxy
function deployL1ERC721BridgeProxy() broadcast() public returns (address) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({
_admin: proxyAdmin
});
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("L1ERC721BridgeProxy", address(proxy));
console.log("L1ERC721BridgeProxy deployed at %s", address(proxy));
return address(proxy);
}
/// @notice Deploy the SystemConfigProxy
function deploySystemConfigProxy() broadcast() public returns (address) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({
_admin: proxyAdmin
});
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("SystemConfigProxy", address(proxy));
console.log("SystemConfigProxy deployed at %s", address(proxy));
return address(proxy);
}
/// @notice Deploy the DisputeGameFactoryProxy
function deployDisputeGameFactoryProxy() broadcast() public returns (address) {
if (block.chainid == 900) {
address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({
_admin: proxyAdmin
});
address admin = address(uint160(uint256(vm.load(address(proxy), OWNER_KEY))));
require(admin == proxyAdmin);
save("DisputeGameFactoryProxy", address(proxy));
console.log("DisputeGameFactoryProxy deployed at %s", address(proxy));
return address(proxy);
}
return address(0);
}
/// @notice Deploy the L1CrossDomainMessenger
function deployL1CrossDomainMessenger() broadcast() public returns (address) {
address portal = mustGetAddress("OptimismPortalProxy");
L1CrossDomainMessenger messenger = new L1CrossDomainMessenger({
_portal: OptimismPortal(payable(portal))
});
require(address(messenger.PORTAL()) == portal);
save("L1CrossDomainMessenger", address(messenger));
console.log("L1CrossDomainMessenger deployed at %s", address(messenger));
return address(messenger);
}
/// @notice Deploy the OptimismPortal
function deployOptimismPortal() broadcast() public returns (address) {
address l2OutputOracleProxy = mustGetAddress("L2OutputOracleProxy");
address systemConfigProxy = mustGetAddress("SystemConfigProxy");
address guardian = cfg.portalGuardian();
if (guardian.code.length == 0) {
console.log("Portal guardian has no code: %s", guardian);
}
OptimismPortal portal = new OptimismPortal({
_l2Oracle: L2OutputOracle(l2OutputOracleProxy),
_guardian: guardian,
_paused: true,
_config: SystemConfig(systemConfigProxy)
});
require(address(portal.L2_ORACLE()) == l2OutputOracleProxy);
require(portal.GUARDIAN() == guardian);
require(address(portal.SYSTEM_CONFIG()) == systemConfigProxy);
require(portal.paused() == true);
save("OptimismPortal", address(portal));
console.log("OptimismPortal deployed at %s", address(portal));
return address(portal);
}
/// @notice Deploy the L2OutputOracle
function deployL2OutputOracle() broadcast() public returns (address) {
L2OutputOracle oracle = new L2OutputOracle({
_submissionInterval: cfg.l2OutputOracleSubmissionInterval(),
_l2BlockTime: cfg.l2BlockTime(),
_startingBlockNumber: cfg.l2OutputOracleStartingBlockNumber(),
_startingTimestamp: cfg.l2OutputOracleStartingTimestamp(),
_proposer: cfg.l2OutputOracleProposer(),
_challenger: cfg.l2OutputOracleChallenger(),
_finalizationPeriodSeconds: cfg.finalizationPeriodSeconds()
});
require(oracle.SUBMISSION_INTERVAL() == cfg.l2OutputOracleSubmissionInterval());
require(oracle.L2_BLOCK_TIME() == cfg.l2BlockTime());
require(oracle.PROPOSER() == cfg.l2OutputOracleProposer());
require(oracle.CHALLENGER() == cfg.l2OutputOracleChallenger());
require(oracle.FINALIZATION_PERIOD_SECONDS() == cfg.finalizationPeriodSeconds());
require(oracle.startingBlockNumber() == cfg.l2OutputOracleStartingBlockNumber());
require(oracle.startingTimestamp() == cfg.l2OutputOracleStartingTimestamp());
save("L2OutputOracle", address(oracle));
console.log("L2OutputOracle deployed at %s", address(oracle));
return address(oracle);
}
/// @notice Deploy the OptimismMintableERC20Factory
function deployOptimismMintableERC20Factory() broadcast() public returns (address) {
address l1StandardBridgeProxy = mustGetAddress("L1StandardBridgeProxy");
OptimismMintableERC20Factory factory = new OptimismMintableERC20Factory(l1StandardBridgeProxy);
require(factory.BRIDGE() == l1StandardBridgeProxy);
save("OptimismMintableERC20Factory", address(factory));
console.log("OptimismMintableERC20Factory deployed at %s", address(factory));
return address(factory);
}
/// @notice Deploy the DisputeGameFactory
function deployDisputeGameFactory() broadcast() public returns (address) {
if (block.chainid == 900) {
DisputeGameFactory factory = new DisputeGameFactory();
save("DisputeGameFactory", address(factory));
console.log("DisputeGameFactory deployed at %s", address(factory));
return address(factory);
}
return address(0);
}
/// @notice Deploy the SystemConfig
function deploySystemConfig() broadcast() public returns (address) {
bytes32 batcherHash = bytes32(uint256(uint160(cfg.batchSenderAddress())));
SystemConfig config = new SystemConfig({
_owner: cfg.finalSystemOwner(),
_overhead: cfg.gasPriceOracleOverhead(),
_scalar: cfg.gasPriceOracleScalar(),
_batcherHash: batcherHash,
_gasLimit: uint64(cfg.l2GenesisBlockGasLimit()),
_unsafeBlockSigner: cfg.p2pSequencerAddress(),
_config: Constants.DEFAULT_RESOURCE_CONFIG()
});
require(config.owner() == cfg.finalSystemOwner());
require(config.overhead() == cfg.gasPriceOracleOverhead());
require(config.scalar() == cfg.gasPriceOracleScalar());
require(config.unsafeBlockSigner() == cfg.p2pSequencerAddress());
require(config.batcherHash() == batcherHash);
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit);
require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier);
require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator);
require(resourceConfig.systemTxMaxGas == rconfig.systemTxMaxGas);
require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee);
require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee);
save("SystemConfig", address(config));
console.log("SystemConfig deployed at %s", address(config));
return address(config);
}
/// @notice Deploy the L1StandardBridge
function deployL1StandardBridge() broadcast() public returns (address) {
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
L1StandardBridge bridge = new L1StandardBridge({
_messenger: payable(l1CrossDomainMessengerProxy)
});
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
save("L1StandardBridge", address(bridge));
console.log("L1StandardBridge deployed at %s", address(bridge));
return address(bridge);
}
/// @notice Deploy the L1ERC721Bridge
function deployL1ERC721Bridge() broadcast() public returns (address) {
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
L1ERC721Bridge bridge = new L1ERC721Bridge({
_messenger: l1CrossDomainMessengerProxy,
_otherBridge: Predeploys.L2_ERC721_BRIDGE
});
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(bridge.OTHER_BRIDGE() == Predeploys.L2_ERC721_BRIDGE);
save("L1ERC721Bridge", address(bridge));
console.log("L1ERC721Bridge deployed at %s", address(bridge));
return address(bridge);
}
/// @notice Transfer ownership of the address manager to the ProxyAdmin
function transferAddressManagerOwnership() broadcast() public {
AddressManager addressManager = AddressManager(mustGetAddress("AddressManager"));
address owner = addressManager.owner();
address proxyAdmin = mustGetAddress("ProxyAdmin");
if (owner != proxyAdmin) {
addressManager.transferOwnership(proxyAdmin);
console.log("AddressManager ownership transferred to %s", proxyAdmin);
}
require(addressManager.owner() == proxyAdmin);
}
/// @notice Initialize the DisputeGameFactory
function initializeDisputeGameFactory() broadcast() public {
if (block.chainid == 900) {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address disputeGameFactoryProxy = mustGetAddress("DisputeGameFactoryProxy");
address disputeGameFactory = mustGetAddress("DisputeGameFactory");
proxyAdmin.upgradeAndCall({
_proxy: payable(disputeGameFactoryProxy),
_implementation: disputeGameFactory,
_data: abi.encodeCall(
DisputeGameFactory.initialize,
(cfg.finalSystemOwner())
)
});
string memory version = DisputeGameFactory(disputeGameFactoryProxy).version();
console.log("DisputeGameFactory version: %s", version);
}
}
/// @notice
function initializeSystemConfig() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address systemConfigProxy = mustGetAddress("SystemConfigProxy");
address systemConfig = mustGetAddress("SystemConfig");
bytes32 batcherHash = bytes32(uint256(uint160(cfg.batchSenderAddress())));
proxyAdmin.upgradeAndCall({
_proxy: payable(systemConfigProxy),
_implementation: systemConfig,
_data: abi.encodeCall(
SystemConfig.initialize,
(
cfg.finalSystemOwner(),
cfg.gasPriceOracleOverhead(),
cfg.gasPriceOracleScalar(),
batcherHash,
uint64(cfg.l2GenesisBlockGasLimit()),
cfg.p2pSequencerAddress(),
Constants.DEFAULT_RESOURCE_CONFIG()
)
)
});
SystemConfig config = SystemConfig(systemConfigProxy);
string memory version = config.version();
console.log("SystemConfig version: %s", version);
require(config.owner() == cfg.finalSystemOwner());
require(config.overhead() == cfg.gasPriceOracleOverhead());
require(config.scalar() == cfg.gasPriceOracleScalar());
require(config.unsafeBlockSigner() == cfg.p2pSequencerAddress());
require(config.batcherHash() == batcherHash);
ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG();
ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig();
require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit);
require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier);
require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator);
require(resourceConfig.systemTxMaxGas == rconfig.systemTxMaxGas);
require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee);
require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee);
}
/// @notice
function initializeL1StandardBridge() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address l1StandardBridgeProxy = mustGetAddress("L1StandardBridgeProxy");
address l1StandardBridge = mustGetAddress("L1StandardBridge");
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
uint256 proxyType = uint256(proxyAdmin.proxyType(l1StandardBridgeProxy));
if (proxyType != uint256(ProxyAdmin.ProxyType.CHUGSPLASH)) {
proxyAdmin.setProxyType(l1StandardBridgeProxy, ProxyAdmin.ProxyType.CHUGSPLASH);
}
require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH));
proxyAdmin.upgrade({
_proxy: payable(l1StandardBridgeProxy),
_implementation: l1StandardBridge
});
string memory version = L1StandardBridge(payable(l1StandardBridgeProxy)).version();
console.log("L1StandardBridge version: %s", version);
L1StandardBridge bridge = L1StandardBridge(payable(l1StandardBridgeProxy));
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE);
}
/// @notice
function initializeL1ERC721Bridge() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address l1ERC721BridgeProxy = mustGetAddress("L1ERC721BridgeProxy");
address l1ERC721Bridge = mustGetAddress("L1ERC721Bridge");
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
proxyAdmin.upgrade({
_proxy: payable(l1ERC721BridgeProxy),
_implementation: l1ERC721Bridge
});
L1ERC721Bridge bridge = L1ERC721Bridge(l1ERC721BridgeProxy);
string memory version = bridge.version();
console.log("L1ERC721Bridge version: %s", version);
require(address(bridge.MESSENGER()) == l1CrossDomainMessengerProxy);
require(bridge.OTHER_BRIDGE() == Predeploys.L2_ERC721_BRIDGE);
}
/// @notice
function initializeOptimismMintableERC20Factory() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address optimismMintableERC20FactoryProxy = mustGetAddress("OptimismMintableERC20FactoryProxy");
address optimismMintableERC20Factory = mustGetAddress("OptimismMintableERC20Factory");
address l1StandardBridgeProxy = mustGetAddress("L1StandardBridgeProxy");
proxyAdmin.upgrade({
_proxy: payable(optimismMintableERC20FactoryProxy),
_implementation: optimismMintableERC20Factory
});
OptimismMintableERC20Factory factory = OptimismMintableERC20Factory(optimismMintableERC20FactoryProxy);
string memory version = factory.version();
console.log("OptimismMintableERC20Factory version: %s", version);
require(factory.BRIDGE() == l1StandardBridgeProxy);
}
/// @notice
function initializeL1CrossDomainMessenger() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
address l1CrossDomainMessenger = mustGetAddress("L1CrossDomainMessenger");
address optimismPortalProxy = mustGetAddress("OptimismPortalProxy");
uint256 proxyType = uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy));
if (proxyType != uint256(ProxyAdmin.ProxyType.RESOLVED)) {
proxyAdmin.setProxyType(l1CrossDomainMessengerProxy, ProxyAdmin.ProxyType.RESOLVED);
}
require(uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED));
string memory name = "OVM_L1CrossDomainMessenger";
string memory implName = proxyAdmin.implementationName(l1CrossDomainMessenger);
if (keccak256(bytes(name)) != keccak256(bytes(implName))) {
proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, name);
}
require(
keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) == keccak256(bytes(name))
);
proxyAdmin.upgradeAndCall({
_proxy: payable(l1CrossDomainMessengerProxy),
_implementation: l1CrossDomainMessenger,
_data: abi.encodeCall(L1CrossDomainMessenger.initialize, ())
});
L1CrossDomainMessenger messenger = L1CrossDomainMessenger(l1CrossDomainMessengerProxy);
string memory version = messenger.version();
console.log("L1CrossDomainMessenger version: %s", version);
require(address(messenger.PORTAL()) == optimismPortalProxy);
}
/// @notice
function initializeL2OutputOracle() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address l2OutputOracleProxy = mustGetAddress("L2OutputOracleProxy");
address l2OutputOracle = mustGetAddress("L2OutputOracle");
proxyAdmin.upgradeAndCall({
_proxy: payable(l2OutputOracleProxy),
_implementation: l2OutputOracle,
_data: abi.encodeCall(
L2OutputOracle.initialize,
(
cfg.l2OutputOracleStartingBlockNumber(),
cfg.l2OutputOracleStartingTimestamp()
)
)
});
L2OutputOracle oracle = L2OutputOracle(l2OutputOracleProxy);
string memory version = oracle.version();
console.log("L2OutputOracle version: %s", version);
require(oracle.SUBMISSION_INTERVAL() == cfg.l2OutputOracleSubmissionInterval());
require(oracle.L2_BLOCK_TIME() == cfg.l2BlockTime());
require(oracle.PROPOSER() == cfg.l2OutputOracleProposer());
require(oracle.CHALLENGER() == cfg.l2OutputOracleChallenger());
require(oracle.FINALIZATION_PERIOD_SECONDS() == cfg.finalizationPeriodSeconds());
require(oracle.startingBlockNumber() == cfg.l2OutputOracleStartingBlockNumber());
require(oracle.startingTimestamp() == cfg.l2OutputOracleStartingTimestamp());
}
/// @notice
function initializeOptimismPortal() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address optimismPortalProxy = mustGetAddress("OptimismPortalProxy");
address optimismPortal = mustGetAddress("OptimismPortal");
address l2OutputOracleProxy = mustGetAddress("L2OutputOracleProxy");
address systemConfigProxy = mustGetAddress("SystemConfigProxy");
proxyAdmin.upgradeAndCall({
_proxy: payable(optimismPortalProxy),
_implementation: optimismPortal,
_data: abi.encodeCall(OptimismPortal.initialize, (false))
});
OptimismPortal portal = OptimismPortal(payable(optimismPortalProxy));
string memory version = portal.version();
console.log("OptimismPortal version: %s", version);
require(address(portal.L2_ORACLE()) == l2OutputOracleProxy);
require(portal.GUARDIAN() == cfg.portalGuardian());
require(address(portal.SYSTEM_CONFIG()) == systemConfigProxy);
require(portal.paused() == false);
}
/// @notice
function transferProxyAdminOwnership() broadcast() public {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address owner = proxyAdmin.owner();
address finalSystemOwner = cfg.finalSystemOwner();
if (owner != finalSystemOwner) {
proxyAdmin.transferOwnership(finalSystemOwner);
console.log("ProxyAdmin ownership transferred to: %s", finalSystemOwner);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Script } from "forge-std/Script.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { console2 as console } from "forge-std/console2.sol";
/// @notice store the new deployment to be saved
struct Deployment {
string name;
address payable addr;
}
/// @notice A `hardhat-deploy` style artifact
struct Artifact {
string abi;
address addr;
string[] args;
bytes bytecode;
bytes deployedBytecode;
string devdoc;
string metadata;
uint256 numDeployments;
string receipt;
bytes32 solcInputHash;
string storageLayout;
bytes32 transactionHash;
string userdoc;
}
/// @title Deployer
/// @author tynes
/// @notice A contract that can make deploying and interacting with deployments easy.
/// 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
/// hardhat deploy style artifacts. Forked from `forge-deploy`.
contract Deployer is Script {
/// @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
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.
/// Can be modified with the env var DEPLOY_SCRIPT
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
function setUp() public virtual {
string memory root = vm.projectRoot();
deployScript = vm.envOr("DEPLOY_SCRIPT", string("DeployConfig"));
deploymentContext = _getDeploymentContext();
string memory deployFile = vm.envOr("DEPLOY_FILE", string("run-latest.json"));
deployPath = string.concat(root, "/broadcast/", deployScript, ".s.sol/", vm.toString(block.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 chainid) {
uint256 chainId = vm.parseUint(chainid);
require(chainId == block.chainid, "Misconfigured networks");
} catch {
vm.writeFile(chainIdPath, vm.toString(block.chainid));
}
console.log("Connected to network with chainid %s", block.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);
}
/// @notice Call this function to sync the deployment artifacts such that
/// hardhat deploy style artifacts are created.
function sync() public {
Deployment[] memory deployments = _getTempDeployments();
console.log("Syncing %s deployments", deployments.length);
for (uint256 i; i < deployments.length; i++) {
address addr = deployments[i].addr;
string memory deploymentName = deployments[i].name;
string memory deployTx = _getDeployTransactionByContractAddress(addr);
string memory contractName = stdJson.readString(deployTx, ".contractName");
console.log("Syncing %s", deploymentName);
string memory fqn = getFullyQualifiedName(contractName);
string[] memory args = getDeployTransactionConstructorArguments(deployTx);
bytes memory code = vm.getCode(fqn);
bytes memory deployedCode = vm.getDeployedCode(fqn);
string memory receipt = _getDeployReceiptByContractAddress(addr);
string memory artifactPath = string.concat(deploymentsDir, "/", deploymentName, ".json");
uint256 numDeployments = 0;
try vm.readFile(artifactPath) returns (string memory res) {
numDeployments = stdJson.readUint(string(res), "$.numDeployments");
vm.removeFile(artifactPath);
} catch {}
Artifact memory artifact = Artifact({
abi: getAbi(contractName),
addr: addr,
args: args,
bytecode: code,
deployedBytecode: deployedCode,
devdoc: getDevDoc(contractName),
metadata: getMetadata(contractName),
numDeployments: numDeployments,
receipt: receipt,
solcInputHash: bytes32(0),
storageLayout: getStorageLayout(contractName),
transactionHash: stdJson.readBytes32(deployTx, "$.hash"),
userdoc: getUserDoc(contractName)
});
string memory json = _serializeArtifact(artifact);
vm.writeJson({
json: json,
path: artifactPath
});
}
console.log("Synced temp deploy files, deleting %s", tempDeploymentsPath);
vm.removeFile(tempDeploymentsPath);
}
/// @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 _getExistingDeploymentAdress(_name) != address(0);
}
/// @notice Returns the address of a deployment.
/// @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;
}
return _getExistingDeploymentAdress(_name);
}
/// @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] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("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 name = names[i];
address addr = stdJson.readAddress(json, string.concat("$.", name));
deployments[i] = Deployment({
name: name,
addr: payable(addr)
});
}
return deployments;
}
/// @notice Returns the json of the deployment transaction given a contract address.
function _getDeployTransactionByContractAddress(address _addr) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.transactions[] | select(.contractAddress == ", '"', vm.toString(_addr), '"', ") | select(.transactionType == ", '"CREATE"', ")' < ", deployPath);
bytes memory res = vm.ffi(cmd);
return string(res);
}
/// @notice Returns the constructor arguent of a deployment transaction given a transaction json.
function getDeployTransactionConstructorArguments(string memory _transaction) internal returns (string[] memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.arguments' <<< '", _transaction, "'");
bytes memory res = vm.ffi(cmd);
string[] memory args = new string[](0);
if (keccak256(bytes("null")) != keccak256(res)) {
args = stdJson.readStringArray(string(res), "");
}
return args;
}
/// @notice Builds the fully qualified name of a contract. Assumes that the
/// file name is the same as the contract name.
function getFullyQualifiedName(string memory _name) internal pure returns (string memory) {
return string.concat(_name, ".sol:", _name);
}
/// @notice Returns the filesystem path to the artifact path. Assumes that the name of the
/// file matches the name of the contract.
function _getForgeArtifactPath(string memory _name) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = "forge config --json | jq -r .out";
bytes memory res = vm.ffi(cmd);
string memory forgeArtifactPath = string.concat(vm.projectRoot(), "/", string(res), "/", _name, ".sol/", _name, ".json");
return forgeArtifactPath;
}
/// @notice Returns the forge artifact given a contract name.
function _getForgeArtifact(string memory _name) internal returns (string memory) {
string memory forgeArtifactPath = _getForgeArtifactPath(_name);
string memory forgeArtifact = vm.readFile(forgeArtifactPath);
return forgeArtifact;
}
/// @notice Returns the receipt of a deployment transaction.
function _getDeployReceiptByContractAddress(address addr) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.receipts[] | select(.contractAddress == ", '"', vm.toString(addr), '"', ")' < ", deployPath);
bytes memory res = vm.ffi(cmd);
string memory receipt = string(res);
return receipt;
}
/// @notice Returns the devdoc for a deployed contract.
function getDevDoc(string memory _name) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.devdoc' < ", _getForgeArtifactPath(_name));
bytes memory res = vm.ffi(cmd);
return string(res);
}
/// @notice Returns the storage layout for a deployed contract.
function getStorageLayout(string memory _name) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.storageLayout' < ", _getForgeArtifactPath(_name));
bytes memory res = vm.ffi(cmd);
return string(res);
}
/// @notice Returns the abi for a deployed contract.
function getAbi(string memory _name) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.abi' < ", _getForgeArtifactPath(_name));
bytes memory res = vm.ffi(cmd);
return string(res);
}
/// @notice Returns the userdoc for a deployed contract.
function getUserDoc(string memory _name) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq -r '.userdoc' < ", _getForgeArtifactPath(_name));
bytes memory res = vm.ffi(cmd);
return string(res);
}
/// @notice
function getMetadata(string memory _name) internal returns (string memory) {
string[] memory cmd = new string[](3);
cmd[0] = "/usr/bin/bash";
cmd[1] = "-c";
cmd[2] = string.concat("jq '.metadata | tostring' < ", _getForgeArtifactPath(_name));
bytes memory res = vm.ffi(cmd);
return string(res);
}
/// @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
/// @param _artifact The artifact to serialize
/// @return The json serialized string
function _serializeArtifact(Artifact memory _artifact) internal returns (string memory) {
string memory json = "";
json = stdJson.serialize("", "address", _artifact.addr);
json = stdJson.serialize("", "abi", _artifact.abi);
json = stdJson.serialize("", "args", _artifact.args);
json = stdJson.serialize("", "bytecode", _artifact.bytecode);
json = stdJson.serialize("", "deployedBytecode", _artifact.deployedBytecode);
json = stdJson.serialize("", "devdoc", _artifact.devdoc);
json = stdJson.serialize("", "metadata", _artifact.metadata);
json = stdJson.serialize("", "numDeployments", _artifact.numDeployments);
json = stdJson.serialize("", "receipt", _artifact.receipt);
json = stdJson.serialize("", "solcInputHash", _artifact.solcInputHash);
json = stdJson.serialize("", "storageLayout", _artifact.storageLayout);
json = stdJson.serialize("", "transactionHash", _artifact.transactionHash);
json = stdJson.serialize("", "userdoc", _artifact.userdoc);
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 returns (string memory) {
string memory context = vm.envOr("DEPLOYMENT_CONTEXT", string(""));
if (bytes(context).length > 0) {
return context;
}
uint256 chainid = block.chainid;
if (chainid == 1) {
return "mainnet";
} else if (chainid == 5) {
return "goerli";
} else if (chainid == 420) {
return "optimism-goerli";
} else if (chainid == 10) {
return "optimism-mainnet";
} else if (chainid == 900) {
return "devnetL1";
} else if (chainid == 31337) {
return "hardhat";
} 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 _getExistingDeploymentAdress(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