diff --git a/ops/docker-compose.yml b/ops/docker-compose.yml index 99e4c2c19f327..9b8e5b23fae36 100644 --- a/ops/docker-compose.yml +++ b/ops/docker-compose.yml @@ -36,30 +36,7 @@ services: # Env vars for the deployment script. CONTRACTS_RPC_URL: http://l1_chain:8545 CONTRACTS_DEPLOYER_KEY: 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' - CONTRACTS_TARGET_NETWORK: 'custom' - OVM_ADDRESS_MANAGER_OWNER: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' - OVM_PROPOSER_ADDRESS: '0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc' - OVM_SEQUENCER_ADDRESS: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8' - SCC_FRAUD_PROOF_WINDOW: 0 - NUM_DEPLOY_CONFIRMATIONS: 0 - # skip compilation when run in docker-compose, since the contracts - # were already compiled in the builder step - NO_COMPILE: 1 - - # Env vars for the dump script. - # Default hardhat account 5 - GAS_PRICE_ORACLE_OWNER: '0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc' - # setting the whitelist owner to address(0) disables the whitelist - WHITELIST_OWNER: '0x0000000000000000000000000000000000000000' - L1_FEE_WALLET_ADDRESS: '0x391716d440c151c42cdf1c95c1d83a5427bca52c' - L2_CHAIN_ID: 987 - L2_BLOCK_GAS_LIMIT: 15000000 - BLOCK_SIGNER_ADDRESS: '0x00000398232E2064F896018496b4b44b3D62751F' - GAS_PRICE_ORACLE_OVERHEAD: 2750 - GAS_PRICE_ORACLE_SCALAR: 1500000 - GAS_PRICE_ORACLE_L1_BASE_FEE: 1 - GAS_PRICE_ORACLE_GAS_PRICE: 1 - GAS_PRICE_ORACLE_DECIMALS: 6 + CONTRACTS_TARGET_NETWORK: 'local' ports: # expose the service to the host for getting the contract addrs - ${DEPLOYER_PORT:-8080}:8081 diff --git a/ops/scripts/deployer.sh b/ops/scripts/deployer.sh index 9bb8f1c87806a..331789d65fca4 100755 --- a/ops/scripts/deployer.sh +++ b/ops/scripts/deployer.sh @@ -24,39 +24,7 @@ curl \ echo "Connected to L1." echo "Building deployment command." -DEPLOY_CMD="npx hardhat deploy" - -# Helper method to concatenate things onto $DEPLOY_CMD. -# Usage: concat_cmd "str-to-concat" -function concat_cmd() { - DEPLOY_CMD="$DEPLOY_CMD $1" -} - -# Helper method to conditionally concatenate CLI arguments -# when a given env var is defined. -# Usage: concat_arg "--arg-name" "env-var-name" -function concat_arg() { - ARG=$1 - ENV_VAR=$2 - if [ -n "${!ENV_VAR+x}" ]; then - echo "$ENV_VAR set to ${!ENV_VAR}, applying $ARG argument." - concat_cmd "$ARG \"${!ENV_VAR}\"" - else - echo "$ENV_VAR is not not set, skipping $ARG argument." - fi -} - -concat_arg "--network" "CONTRACTS_TARGET_NETWORK" -concat_arg "--ovm-address-manager-owner" "OVM_ADDRESS_MANAGER_OWNER" -concat_arg "--ovm-proposer-address" "OVM_PROPOSER_ADDRESS" -concat_arg "--ovm-sequencer-address" "OVM_SEQUENCER_ADDRESS" -concat_arg "--l1-block-time-seconds" "L1_BLOCK_TIME_SECONDS" -concat_arg "--ctc-max-transaction-gas-limit" "CTC_MAX_TRANSACTION_GAS_LIMIT" -concat_arg "--ctc-l2-gas-discount-divisor" "CTC_L2_GAS_DISCOUNT_DIVISOR" -concat_arg "--ctc-enqueue-gas-cost" "CTC_ENQUEUE_GAS_COST" -concat_arg "--scc-fraud-proof-window" "SCC_FRAUD_PROOF_WINDOW" -concat_arg "--num-deploy-confirmations" "NUM_DEPLOY_CONFIRMATIONS" -concat_arg "--forked" "FORKED" +DEPLOY_CMD="npx hardhat deploy --network $CONTRACTS_TARGET_NETWORK" echo "Deploying contracts. Deployment command:" echo "$DEPLOY_CMD" @@ -81,20 +49,13 @@ echo "}" >> addresses.json echo "Built addresses.json. Content:" jq . addresses.json -echo "Env vars for the dump script:" -export L1_STANDARD_BRIDGE_ADDRESS=$(cat "./addresses.json" | jq -r .Proxy__OVM_L1StandardBridge) -export L1_CROSS_DOMAIN_MESSENGER_ADDRESS=$(cat "./addresses.json" | jq -r .Proxy__OVM_L1CrossDomainMessenger) -echo "ADDRESS_MANAGER_ADDRESS=$ADDRESS_MANAGER_ADDRESS" -echo "L1_STANDARD_BRIDGE_ADDRESS=$L1_STANDARD_BRIDGE_ADDRESS" -echo "L1_CROSS_DOMAIN_MESSENGER_ADDRESS=$L1_CROSS_DOMAIN_MESSENGER_ADDRESS" - - echo "Building dump file." -yarn run build:dump -mv addresses.json ./dist/dumps +npx hardhat take-dump --network $CONTRACTS_TARGET_NETWORK +mv addresses.json ./genesis +cp ./genesis/$CONTRACTS_TARGET_NETWORK.json ./genesis/state-dump.latest.json # service the addresses and dumps echo "Starting server." python3 -m http.server \ --bind "0.0.0.0" 8081 \ - --directory ./dist/dumps + --directory ./genesis diff --git a/packages/contracts/.env.example b/packages/contracts/.env.example new file mode 100644 index 0000000000000..1a6f7bbacefcc --- /dev/null +++ b/packages/contracts/.env.example @@ -0,0 +1,11 @@ +# Name for the network to deploy to ("mainnet", "kovan", etc.) +CONTRACTS_TARGET_NETWORK= + +# Private key that will send deployment transactions +CONTRACTS_DEPLOYER_KEY= + +# RPC URL connected to the L1 chain we're deploying to +CONTRACTS_RPC_URL= + +# Your Etherscan API key for the L1 network +ETHERSCAN_API_KEY= diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 30b617315fb38..cca0308e11e65 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -32,7 +32,9 @@ import { L1CrossDomainMessenger } from "@eth-optimism/contracts/L1/messaging/L1C ``` ## Guide for Developers + ### Setup + Install the following: - [`Node.js` (14+)](https://nodejs.org/en/) - [`npm`](https://www.npmjs.com/get-npm) @@ -46,87 +48,75 @@ cd contracts ``` Install `npm` packages: + ```shell yarn install ``` ### Running Tests + Tests are executed via `yarn`: + ```shell yarn test ``` Run specific tests by giving a path to the file you want to run: + ```shell yarn test ./test/path/to/my/test.spec.ts ``` ### Measuring test coverage: + ```shell yarn test:coverage ``` The output is most easily viewable by opening the html file in your browser: + ```shell open ./coverage/index.html ``` ### Compiling and Building -Easiest way is to run the primary build script: -```shell -yarn build -``` -Running the full build command will perform the following actions: -1. `build:contracts` - Compile all Solidity contracts with both the EVM and OVM compilers. -2. `build:typescript` - Builds the typescript files that are used to export utilities into js. -3. `build:copy` - Copies various other files into the dist folder. -4. `build:dump` - Generates a genesis state from the contracts that L2 geth will use. -5. `build:typechain` - Generates [TypeChain](https://github.com/ethereum-ts/TypeChain) artifacts. +Compile and build the various required with the `build` command: -You can also build specific components as follows: ```shell -yarn build:contracts +yarn build ``` ### Deploying the Contracts #### Required environment variables -You must set the following environment variables to execute a deployment: - -```bash -# Name for the network to deploy to ("mainnet", "kovan", etc.) -export CONTRACTS_TARGET_NETWORK=... - -# Private key that will send deployment transactions -export CONTRACTS_DEPLOYER_KEY=... +You must set several required environment variables before you can execute a deployment. +Duplicate the file [`.env.example`](./.env.example) and rename your duplicate to `.env`. +Fill out each of the environment variables before continuing. -# RPC URL connected to the L1 chain we're deploying to -export CONTRACTS_RPC_URL=... +#### Creating a deployment configuration -# Your Etherscan API key for the L1 network -export ETHERSCAN_API_KEY=... -``` - -#### Creating a deployment script +Before you can carry out a deployment, you must create a deployment configuration file inside of the [deploy-config](./deploy-config/) folder. +Deployment configuration files are TypeScript files that export an object that conforms to the `DeployConfig` type. +See [mainnet.ts](./deploy-config/mainnet.ts) for an example deployment configuration. +We recommend duplicating an existing deployment config and modifying it to satisfy your requirements. -Before you can carry out a deployment, you must create a deployment script. -See [mainnet.sh](./scripts/deploy-scripts/mainnet.sh) for an example deployment script. -We recommend duplicating an existing deployment script and modifying it to satisfy your requirements. +#### Executing a deployment -Most variables within the deploy script are relatively self-explanatory. -If you intend to upgrade an existing system you **MUST** [include the following argument](https://github.com/ethereum-optimism/optimism/blob/6f633f915b34a46ac14430724bed9722af8bd05e/packages/contracts/scripts/deploy-scripts/mainnet.sh#L33) in the deploy script: +Once you've created your deploy config, you can execute a deployment with the following command: ``` ---tags upgrade +npx hardhat deploy --network ``` -If you are deploying a system from scratch, you should **NOT** include `--tags upgrade` or you will fail to deploy several contracts. +Note that this only applies to fresh deployments. +If you want to upgrade an existing system (instead of deploying a new system from scratch), you must use the following command instead: -#### Executing a deployment +``` +npx hardhat deploy --network --tags upgrade +``` -Once you've created your deploy script, simply run the script to trigger a deployment. During the deployment process, you will be asked to transfer ownership of several contracts to a special contract address. You will also be asked to verify various configuration values. This is a safety mechanism to make sure that actions within an upgrade are performed atomically. @@ -134,19 +124,29 @@ Ownership of these addresses will be automatically returned to the original owne The original owner can always recover ownership from the upgrade contract in an emergency. Please read these instructions carefully, verify each of the presented configuration values, and carefully confirm that the contract you are giving ownership to has not been compromised (e.g., check the code on Etherscan). -After your deployment is complete, your new contracts will be written to an artifacts directory in `./deployments/`. -Your contracts will also be automatically verified as part of the deployment script. +After your deployment is complete, your new contracts will be written to an artifacts directory in `./deployments/`. + +#### Verifying contract source code + +Contracts will be automatically verified via both [Etherscan](https://etherscan.io) and [Sourcify](https://sourcify.dev/) during the deployment process. +If there was an issue with verification during the deployment, you can manually verify your contracts with the command: + +``` +npx hardhat etherscan-verify --network +``` #### Creating a genesis file Optimism expects that certain contracts (called "predeploys") be deployed to the L2 network at pre-determined addresses. -Doing this requires that you generate a special genesis file to be used by your corresponding L2Geth nodes. -You must first create a genesis generation script. -Like in the deploy script, we recommend starting from an [existing script](./scripts/deploy-scripts/mainnet-genesis.sh). -Modify each of the values within this script to match the values of your own deployment, taking any L1 contract addresses from the `./deployments/` folder that was just generated or modified. +We guarantee this by creating a genesis file in which certain contracts are already within the L2 state at the genesis block. +To create the genesis file for your network, you must first deploy the L1 contracts using the appropriate commands from above. +Once you've deployed your contracts, run the following command: + +``` +npx hardhat take-dump --network +``` -Execute this script to generate the genesis file. -You will find this genesis file at `./dist/dumps/state-dump.latest.json`. +A genesis file will be created for you at `/genesis/.json`. You can then ingest this file via `geth init`. ### Hardhat tasks diff --git a/packages/contracts/bin/take-dump.ts b/packages/contracts/bin/take-dump.ts deleted file mode 100644 index 9dc3bbe60741d..0000000000000 --- a/packages/contracts/bin/take-dump.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* External Imports */ - -import * as fs from 'fs' -import * as path from 'path' - -import * as mkdirp from 'mkdirp' - -const ensure = (value, key) => { - if (typeof value === 'undefined' || value === null || Number.isNaN(value)) { - throw new Error(`${key} is undefined, null or NaN`) - } -} - -/* Internal Imports */ -import { makeL2GenesisFile } from '../src/make-genesis' -;(async () => { - const outdir = path.resolve(__dirname, '../dist/dumps') - const outfile = path.join(outdir, 'state-dump.latest.json') - mkdirp.sync(outdir) - - const env = process.env - - // An account that represents the owner of the whitelist - const whitelistOwner = env.WHITELIST_OWNER - // The gas price oracle owner, can update values is GasPriceOracle L2 predeploy - const gasPriceOracleOwner = env.GAS_PRICE_ORACLE_OWNER - // The initial overhead value for the GasPriceOracle - const gasPriceOracleOverhead = parseInt( - env.GAS_PRICE_ORACLE_OVERHEAD || '2750', - 10 - ) - // The initial scalar value for the GasPriceOracle. The actual - // scalar is scaled downwards by the number of decimals - const gasPriceOracleScalar = parseInt( - env.GAS_PRICE_ORACLE_SCALAR || '1500000', - 10 - ) - // The initial decimals that scale down the scalar in the GasPriceOracle - const gasPriceOracleDecimals = parseInt( - env.GAS_PRICE_ORACLE_DECIMALS || '6', - 10 - ) - // The initial L1 base fee in the GasPriceOracle. This determines how - // expensive the L1 portion of the transaction fee is. - const gasPriceOracleL1BaseFee = parseInt( - env.GAS_PRICE_ORACLE_L1_BASE_FEE || '1', - 10 - ) - // The initial L2 gas price set in the GasPriceOracle - const gasPriceOracleGasPrice = parseInt( - env.GAS_PRICE_ORACLE_GAS_PRICE || '1', - 10 - ) - // The L2 block gas limit, used in the L2 block headers as well to limit - // the amount of execution for a single block. - const l2BlockGasLimit = parseInt(env.L2_BLOCK_GAS_LIMIT, 10) - // The L2 chain id, added to the chain config - const l2ChainId = parseInt(env.L2_CHAIN_ID, 10) - // The block signer address, added to the block extradata for clique consensus - const blockSignerAddress = env.BLOCK_SIGNER_ADDRESS - // The L1 standard bridge address for cross domain messaging - const l1StandardBridgeAddress = env.L1_STANDARD_BRIDGE_ADDRESS - // The L1 fee wallet address, used to restrict the account that fees on L2 can - // be withdrawn to on L1 - const l1FeeWalletAddress = env.L1_FEE_WALLET_ADDRESS - // The L1 cross domain messenger address, used for cross domain messaging - const l1CrossDomainMessengerAddress = env.L1_CROSS_DOMAIN_MESSENGER_ADDRESS - // The block height at which the berlin hardfork activates - const berlinBlock = parseInt(env.BERLIN_BLOCK, 10) || 0 - - ensure(whitelistOwner, 'WHITELIST_OWNER') - ensure(gasPriceOracleOwner, 'GAS_PRICE_ORACLE_OWNER') - ensure(l2BlockGasLimit, 'L2_BLOCK_GAS_LIMIT') - ensure(l2ChainId, 'L2_CHAIN_ID') - ensure(blockSignerAddress, 'BLOCK_SIGNER_ADDRESS') - ensure(l1StandardBridgeAddress, 'L1_STANDARD_BRIDGE_ADDRESS') - ensure(l1FeeWalletAddress, 'L1_FEE_WALLET_ADDRESS') - ensure(l1CrossDomainMessengerAddress, 'L1_CROSS_DOMAIN_MESSENGER_ADDRESS') - ensure(berlinBlock, 'BERLIN_BLOCK') - - // Basic warning so users know that the whitelist will be disabled if the owner is the zero address. - if (env.WHITELIST_OWNER === '0x' + '00'.repeat(20)) { - console.log( - 'WARNING: whitelist owner is address(0), whitelist will be disabled' - ) - } - - const genesis = await makeL2GenesisFile({ - whitelistOwner, - gasPriceOracleOwner, - gasPriceOracleOverhead, - gasPriceOracleScalar, - gasPriceOracleL1BaseFee, - gasPriceOracleGasPrice, - gasPriceOracleDecimals, - l2BlockGasLimit, - l2ChainId, - blockSignerAddress, - l1StandardBridgeAddress, - l1FeeWalletAddress, - l1CrossDomainMessengerAddress, - berlinBlock, - }) - - fs.writeFileSync(outfile, JSON.stringify(genesis, null, 4)) -})() diff --git a/packages/contracts/deploy-config/goerli.ts b/packages/contracts/deploy-config/goerli.ts new file mode 100644 index 0000000000000..57d4c9e331395 --- /dev/null +++ b/packages/contracts/deploy-config/goerli.ts @@ -0,0 +1,20 @@ +import { DeployConfig } from '../src/deploy-config' + +const config: DeployConfig = { + network: 'goerli', + l1BlockTimeSeconds: 15, + l2BlockGasLimit: 15_000_000, + l2ChainId: 420, + ctcL2GasDiscountDivisor: 32, + ctcEnqueueGasCost: 60_000, + sccFaultProofWindowSeconds: 604800, + sccSequencerPublishWindowSeconds: 12592000, + ovmSequencerAddress: '0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244', + ovmProposerAddress: '0x9A2F243c605e6908D96b18e21Fb82Bf288B19EF3', + ovmBlockSignerAddress: '0x27770a9694e4B4b1E130Ab91Bc327C36855f612E', + ovmFeeWalletAddress: '0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244', + ovmAddressManagerOwner: '0x32b70c156302d28A9119445d2bbb9ab1cBD01671', + ovmGasPriceOracleOwner: '0x84f70449f90300997840eCb0918873745Ede7aE6', +} + +export default config diff --git a/packages/contracts/deploy-config/kovan.ts b/packages/contracts/deploy-config/kovan.ts new file mode 100644 index 0000000000000..6c51a7d938992 --- /dev/null +++ b/packages/contracts/deploy-config/kovan.ts @@ -0,0 +1,22 @@ +import { DeployConfig } from '../src/deploy-config' + +const config: DeployConfig = { + network: 'kovan', + numDeployConfirmations: 1, + gasPrice: 5_000_000_000, + l1BlockTimeSeconds: 15, + l2BlockGasLimit: 15_000_000, + l2ChainId: 69, + ctcL2GasDiscountDivisor: 32, + ctcEnqueueGasCost: 60_000, + sccFaultProofWindowSeconds: 10, + sccSequencerPublishWindowSeconds: 12592000, + ovmSequencerAddress: '0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244', + ovmProposerAddress: '0x9A2F243c605e6908D96b18e21Fb82Bf288B19EF3', + ovmBlockSignerAddress: '0x00000398232E2064F896018496b4b44b3D62751F', + ovmFeeWalletAddress: '0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244', + ovmAddressManagerOwner: '0x18394B52d3Cb931dfA76F63251919D051953413d', + ovmGasPriceOracleOwner: '0x84f70449f90300997840eCb0918873745Ede7aE6', +} + +export default config diff --git a/packages/contracts/deploy-config/local.ts b/packages/contracts/deploy-config/local.ts new file mode 100644 index 0000000000000..0e8207d4981ec --- /dev/null +++ b/packages/contracts/deploy-config/local.ts @@ -0,0 +1,20 @@ +import { DeployConfig } from '../src/deploy-config' + +const config: DeployConfig = { + network: 'local', + l1BlockTimeSeconds: 15, + l2BlockGasLimit: 15_000_000, + l2ChainId: 987, + ctcL2GasDiscountDivisor: 32, + ctcEnqueueGasCost: 60_000, + sccFaultProofWindowSeconds: 0, + sccSequencerPublishWindowSeconds: 12592000, + ovmSequencerAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', + ovmProposerAddress: '0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc', + ovmBlockSignerAddress: '0x00000398232E2064F896018496b4b44b3D62751F', + ovmFeeWalletAddress: '0x391716d440c151c42cdf1c95c1d83a5427bca52c', + ovmAddressManagerOwner: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', + ovmGasPriceOracleOwner: '0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc', +} + +export default config diff --git a/packages/contracts/deploy-config/mainnet.ts b/packages/contracts/deploy-config/mainnet.ts new file mode 100644 index 0000000000000..a850b6e2ac74d --- /dev/null +++ b/packages/contracts/deploy-config/mainnet.ts @@ -0,0 +1,23 @@ +import { DeployConfig } from '../src/deploy-config' + +const config: DeployConfig = { + network: 'mainnet', + numDeployConfirmations: 4, + gasPrice: 150_000_000_000, + l1BlockTimeSeconds: 15, + l2BlockGasLimit: 15_000_000, + l2ChainId: 10, + ctcL2GasDiscountDivisor: 32, + ctcEnqueueGasCost: 60_000, + sccFaultProofWindowSeconds: 604800, + sccSequencerPublishWindowSeconds: 12592000, + ovmSequencerAddress: '0x6887246668a3b87F54DeB3b94Ba47a6f63F32985', + ovmProposerAddress: '0x473300df21D047806A082244b417f96b32f13A33', + ovmBlockSignerAddress: '0x00000398232E2064F896018496b4b44b3D62751F', + ovmFeeWalletAddress: '0x391716d440c151c42cdf1c95c1d83a5427bca52c', + ovmAddressManagerOwner: '0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A', + ovmGasPriceOracleOwner: '0x7107142636C85c549690b1Aca12Bdb8052d26Ae6', + ovmWhitelistOwner: '0x648E3e8101BFaB7bf5997Bd007Fb473786019159', +} + +export default config diff --git a/packages/contracts/deploy/000-hardhat-setup.ts b/packages/contracts/deploy/000-hardhat-setup.ts index 3f86fb7aa18ad..f92e54b2224cc 100644 --- a/packages/contracts/deploy/000-hardhat-setup.ts +++ b/packages/contracts/deploy/000-hardhat-setup.ts @@ -9,10 +9,12 @@ import { sendImpersonatedTx, BIG_BALANCE, } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' const deployFn: DeployFunction = async (hre) => { - if ((hre as any).deployConfig.forked !== 'true') { + const deployConfig = getDeployConfig(hre.network.name) + if (!deployConfig.isForkedNetwork) { return } diff --git a/packages/contracts/deploy/001-Lib_AddressManager.deploy.ts b/packages/contracts/deploy/001-Lib_AddressManager.deploy.ts index 41140cd7d539b..29bb06aeb5b92 100644 --- a/packages/contracts/deploy/001-Lib_AddressManager.deploy.ts +++ b/packages/contracts/deploy/001-Lib_AddressManager.deploy.ts @@ -2,18 +2,20 @@ import { DeployFunction } from 'hardhat-deploy/dist/types' import { names } from '../src/address-names' +import { getDeployConfig } from '../src/deploy-config' /* Imports: External */ const deployFn: DeployFunction = async (hre) => { const { deploy } = hre.deployments const { deployer } = await hre.getNamedAccounts() + const deployConfig = getDeployConfig(hre.network.name) await deploy(names.unmanaged.Lib_AddressManager, { from: deployer, args: [], log: true, - waitConfirmations: (hre as any).deployConfig.numDeployConfirmations, + waitConfirmations: deployConfig.numDeployConfirmations, }) } diff --git a/packages/contracts/deploy/004-OVM_CanonicalTransactionChain.deploy.ts b/packages/contracts/deploy/004-OVM_CanonicalTransactionChain.deploy.ts index 8ab5e7585b3fb..99d3e99269bba 100644 --- a/packages/contracts/deploy/004-OVM_CanonicalTransactionChain.deploy.ts +++ b/packages/contracts/deploy/004-OVM_CanonicalTransactionChain.deploy.ts @@ -6,9 +6,12 @@ import { deployAndVerifyAndThen, getContractFromArtifact, } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) + const Lib_AddressManager = await getContractFromArtifact( hre, names.unmanaged.Lib_AddressManager @@ -19,9 +22,9 @@ const deployFn: DeployFunction = async (hre) => { name: names.managed.contracts.CanonicalTransactionChain, args: [ Lib_AddressManager.address, - (hre as any).deployConfig.ctcMaxTransactionGasLimit, - (hre as any).deployConfig.ctcL2GasDiscountDivisor, - (hre as any).deployConfig.ctcEnqueueGasCost, + deployConfig.l2BlockGasLimit, + deployConfig.ctcL2GasDiscountDivisor, + deployConfig.ctcEnqueueGasCost, ], }) } diff --git a/packages/contracts/deploy/005-OVM_StateCommitmentChain.deploy.ts b/packages/contracts/deploy/005-OVM_StateCommitmentChain.deploy.ts index 7a4f280e1da31..97c9d0ecf447d 100644 --- a/packages/contracts/deploy/005-OVM_StateCommitmentChain.deploy.ts +++ b/packages/contracts/deploy/005-OVM_StateCommitmentChain.deploy.ts @@ -6,9 +6,12 @@ import { deployAndVerifyAndThen, getContractFromArtifact, } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) + const Lib_AddressManager = await getContractFromArtifact( hre, names.unmanaged.Lib_AddressManager @@ -19,8 +22,8 @@ const deployFn: DeployFunction = async (hre) => { name: names.managed.contracts.StateCommitmentChain, args: [ Lib_AddressManager.address, - (hre as any).deployConfig.sccFraudProofWindow, - (hre as any).deployConfig.sccSequencerPublishWindow, + deployConfig.sccFaultProofWindowSeconds, + deployConfig.sccSequencerPublishWindowSeconds, ], }) } diff --git a/packages/contracts/deploy/007-OVM_L1CrossDomainMessenger.deploy.ts b/packages/contracts/deploy/007-OVM_L1CrossDomainMessenger.deploy.ts index c79df6417ef96..fa7a7b1434ba6 100644 --- a/packages/contracts/deploy/007-OVM_L1CrossDomainMessenger.deploy.ts +++ b/packages/contracts/deploy/007-OVM_L1CrossDomainMessenger.deploy.ts @@ -7,9 +7,12 @@ import { deployAndVerifyAndThen, getContractFromArtifact, } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) + const Lib_AddressManager = await getContractFromArtifact( hre, names.unmanaged.Lib_AddressManager @@ -45,7 +48,7 @@ const deployFn: DeployFunction = async (hre) => { console.log( `Transferring ownership of L1CrossDomainMessenger (implementation)...` ) - const owner = (hre as any).deployConfig.ovmAddressManagerOwner + const owner = deployConfig.ovmAddressManagerOwner await contract.transferOwnership(owner) console.log(`Checking that contract owner was correctly set...`) diff --git a/packages/contracts/deploy/010-AddressDictator.deploy.ts b/packages/contracts/deploy/010-AddressDictator.deploy.ts index c0f963ab62647..ab661d7a8b4ba 100644 --- a/packages/contracts/deploy/010-AddressDictator.deploy.ts +++ b/packages/contracts/deploy/010-AddressDictator.deploy.ts @@ -7,10 +7,13 @@ import { deployAndVerifyAndThen, getContractFromArtifact, } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' import { predeploys } from '../src/predeploys' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) + const Lib_AddressManager = await getContractFromArtifact( hre, names.unmanaged.Lib_AddressManager @@ -42,13 +45,13 @@ const deployFn: DeployFunction = async (hre) => { // CanonicalTransactionChain. { name: names.managed.accounts.OVM_Sequencer, - address: (hre as any).deployConfig.ovmSequencerAddress, + address: deployConfig.ovmSequencerAddress, }, // OVM_Proposer is the address allowed to submit state roots (transaction results) to the // StateCommitmentChain. { name: names.managed.accounts.OVM_Proposer, - address: (hre as any).deployConfig.ovmProposerAddress, + address: deployConfig.ovmProposerAddress, }, ] @@ -69,7 +72,7 @@ const deployFn: DeployFunction = async (hre) => { name: names.unmanaged.AddressDictator, args: [ Lib_AddressManager.address, - (hre as any).deployConfig.ovmAddressManagerOwner, + deployConfig.ovmAddressManagerOwner, namesAndAddresses.map((pair) => { return pair.name }), diff --git a/packages/contracts/deploy/012-initialize-Proxy__L1CrossDomainMessenger.ts b/packages/contracts/deploy/012-initialize-Proxy__L1CrossDomainMessenger.ts index 0e918bc4010ca..a25730e86ff07 100644 --- a/packages/contracts/deploy/012-initialize-Proxy__L1CrossDomainMessenger.ts +++ b/packages/contracts/deploy/012-initialize-Proxy__L1CrossDomainMessenger.ts @@ -4,9 +4,11 @@ import { hexStringEquals, awaitCondition } from '@eth-optimism/core-utils' /* Imports: Internal */ import { getContractFromArtifact } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) const { deployer } = await hre.getNamedAccounts() // There's a risk that we could get front-run during a fresh deployment, which would brick this @@ -43,7 +45,7 @@ const deployFn: DeployFunction = async (hre) => { ) console.log(`Setting Proxy__OVM_L1CrossDomainMessenger owner...`) - const owner = (hre as any).deployConfig.ovmAddressManagerOwner + const owner = deployConfig.ovmAddressManagerOwner await Proxy__OVM_L1CrossDomainMessenger.transferOwnership(owner) console.log(`Checking that the contract owner was correctly set...`) diff --git a/packages/contracts/deploy/013-ChugSplashDictator.deploy.ts b/packages/contracts/deploy/013-ChugSplashDictator.deploy.ts index 86cefd71877d9..60a2c20039543 100644 --- a/packages/contracts/deploy/013-ChugSplashDictator.deploy.ts +++ b/packages/contracts/deploy/013-ChugSplashDictator.deploy.ts @@ -9,9 +9,12 @@ import { getContractFromArtifact, deployAndVerifyAndThen, } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' import { names } from '../src/address-names' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) + const Proxy__OVM_L1StandardBridge = await getContractFromArtifact( hre, 'Proxy__OVM_L1StandardBridge' @@ -31,7 +34,7 @@ const deployFn: DeployFunction = async (hre) => { name: names.unmanaged.ChugSplashDictator, args: [ Proxy__OVM_L1StandardBridge.address, - (hre as any).deployConfig.ovmAddressManagerOwner, + deployConfig.ovmAddressManagerOwner, ethers.utils.keccak256(bridgeCode), ethers.utils.hexZeroPad('0x00', 32), ethers.utils.hexZeroPad(Proxy__OVM_L1CrossDomainMessenger.address, 32), diff --git a/packages/contracts/deploy/015-finalize.ts b/packages/contracts/deploy/015-finalize.ts index 9e0d574fff4f4..08a518db24ce7 100644 --- a/packages/contracts/deploy/015-finalize.ts +++ b/packages/contracts/deploy/015-finalize.ts @@ -4,9 +4,12 @@ import { hexStringEquals, awaitCondition } from '@eth-optimism/core-utils' /* Imports: Internal */ import { getContractFromArtifact } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' const deployFn: DeployFunction = async (hre) => { + const deployConfig = getDeployConfig(hre.network.name) const { deployer } = await hre.getNamedAccounts() + const Lib_AddressManager = await getContractFromArtifact( hre, 'Lib_AddressManager', @@ -15,7 +18,7 @@ const deployFn: DeployFunction = async (hre) => { } ) - const owner = (hre as any).deployConfig.ovmAddressManagerOwner + const owner = deployConfig.ovmAddressManagerOwner const remoteOwner = await Lib_AddressManager.owner() if (hexStringEquals(owner, remoteOwner)) { console.log( diff --git a/packages/contracts/package.json b/packages/contracts/package.json index ecf8c0cfe5447..9b10c25c92e0d 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -20,7 +20,6 @@ "build": "yarn build:contracts && yarn autogen:artifacts && yarn build:typescript", "build:typescript": "tsc -p ./tsconfig.build.json", "build:contracts": "hardhat compile --show-stack-traces", - "build:dump": "ts-node bin/take-dump.ts", "autogen:markdown": "ts-node scripts/generate-markdown.ts", "autogen:artifacts": "ts-node scripts/generate-artifacts.ts && ts-node scripts/generate-deployed-artifacts.ts", "test": "yarn test:contracts", diff --git a/packages/contracts/scripts/deploy-scripts/goerli-genesis.sh b/packages/contracts/scripts/deploy-scripts/goerli-genesis.sh deleted file mode 100755 index 02cc3a1f4e871..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/goerli-genesis.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -export L2_BLOCK_GAS_LIMIT=15000000 -export L2_CHAIN_ID=420 -export BLOCK_SIGNER_ADDRESS=0x27770a9694e4B4b1E130Ab91Bc327C36855f612E -export L1_STANDARD_BRIDGE_ADDRESS=0x73298186A143a54c20ae98EEE5a025bD5979De02 -export L1_FEE_WALLET_ADDRESS=0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 -export L1_CROSS_DOMAIN_MESSENGER_ADDRESS=0xEcC89b9EDD804850C4F343A278Be902be11AaF42 -export WHITELIST_OWNER=0x0000000000000000000000000000000000000000 -export GAS_PRICE_ORACLE_OWNER=0x84f70449f90300997840eCb0918873745Ede7aE6 -yarn build:dump diff --git a/packages/contracts/scripts/deploy-scripts/goerli.sh b/packages/contracts/scripts/deploy-scripts/goerli.sh deleted file mode 100755 index 7425daae12e49..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/goerli.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -### DEPLOYMENT SCRIPT ### -# To be called from root of contracts dir # - -# Required env vars -if [[ -z "$CONTRACTS_DEPLOYER_KEY" ]]; then - echo "Must pass CONTRACTS_DEPLOYER_KEY" - exit 1 -fi -if [[ -z "$CONTRACTS_RPC_URL" ]]; then - echo "Must pass CONTRACTS_RPC_URL" - exit 1 -fi -if [[ -z "$ETHERSCAN_API_KEY" ]]; then - echo "Must pass ETHERSCAN_API_KEY" - exit 1 -fi - -CONTRACTS_TARGET_NETWORK=goerli \ -npx hardhat deploy \ - --l1-block-time-seconds 15 \ - --ctc-max-transaction-gas-limit 15000000 \ - --ctc-l2-gas-discount-divisor 32 \ - --ctc-enqueue-gas-cost 60000 \ - --scc-fraud-proof-window 604800 \ - --scc-sequencer-publish-window 12592000 \ - --ovm-sequencer-address 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 \ - --ovm-proposer-address 0x9A2F243c605e6908D96b18e21Fb82Bf288B19EF3 \ - --ovm-address-manager-owner 0x32b70c156302d28A9119445d2bbb9ab1cBD01671 \ - --network goerli - -CONTRACTS_TARGET_NETWORK=goerli \ -npx hardhat etherscan-verify --network goerli diff --git a/packages/contracts/scripts/deploy-scripts/kovan-genesis.sh b/packages/contracts/scripts/deploy-scripts/kovan-genesis.sh deleted file mode 100755 index f87d6b31f0f14..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/kovan-genesis.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -export L2_BLOCK_GAS_LIMIT=15000000 -export L2_CHAIN_ID=69 -export BLOCK_SIGNER_ADDRESS=0x00000398232E2064F896018496b4b44b3D62751F -export L1_STANDARD_BRIDGE_ADDRESS=0x22F24361D548e5FaAfb36d1437839f080363982B -export L1_FEE_WALLET_ADDRESS=0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 -export L1_CROSS_DOMAIN_MESSENGER_ADDRESS=0x4361d0F75A0186C05f971c566dC6bEa5957483fD -export WHITELIST_OWNER=0x0000000000000000000000000000000000000000 -export GAS_PRICE_ORACLE_OWNER=0x84f70449f90300997840eCb0918873745Ede7aE6 -yarn build:dump diff --git a/packages/contracts/scripts/deploy-scripts/kovan.sh b/packages/contracts/scripts/deploy-scripts/kovan.sh deleted file mode 100755 index 559ae1f7845e3..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/kovan.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -### DEPLOYMENT SCRIPT ### -# To be called from root of contracts dir # - -# Required env vars -if [[ -z "$CONTRACTS_DEPLOYER_KEY" ]]; then - echo "Must pass CONTRACTS_DEPLOYER_KEY" - exit 1 -fi -if [[ -z "$CONTRACTS_RPC_URL" ]]; then - echo "Must pass CONTRACTS_RPC_URL" - exit 1 -fi -if [[ -z "$ETHERSCAN_API_KEY" ]]; then - echo "Must pass ETHERSCAN_API_KEY" - exit 1 -fi - -CONTRACTS_TARGET_NETWORK=kovan \ -npx hardhat deploy \ - --l1-block-time-seconds 15 \ - --ctc-max-transaction-gas-limit 15000000 \ - --ctc-l2-gas-discount-divisor 32 \ - --ctc-enqueue-gas-cost 60000 \ - --scc-fraud-proof-window 10 \ - --scc-sequencer-publish-window 12592000 \ - --ovm-sequencer-address 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 \ - --ovm-proposer-address 0x9A2F243c605e6908D96b18e21Fb82Bf288B19EF3 \ - --ovm-address-manager-owner 0x18394B52d3Cb931dfA76F63251919D051953413d \ - --gasprice 1000000000 \ - --num-deploy-confirmations 1 \ - --tags upgrade \ - --network kovan - -CONTRACTS_TARGET_NETWORK=kovan \ -npx hardhat etherscan-verify \ - --network kovan \ - --sleep diff --git a/packages/contracts/scripts/deploy-scripts/mainnet-genesis.sh b/packages/contracts/scripts/deploy-scripts/mainnet-genesis.sh deleted file mode 100644 index 962cd59103a62..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/mainnet-genesis.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -export L2_BLOCK_GAS_LIMIT=15000000 -export L2_CHAIN_ID=10 -export BLOCK_SIGNER_ADDRESS=0x00000398232E2064F896018496b4b44b3D62751F -export L1_STANDARD_BRIDGE_ADDRESS=0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1 -export L1_FEE_WALLET_ADDRESS=0x391716d440c151c42cdf1c95c1d83a5427bca52c -export L1_CROSS_DOMAIN_MESSENGER_ADDRESS=0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 -export WHITELIST_OWNER=0x648E3e8101BFaB7bf5997Bd007Fb473786019159 -export GAS_PRICE_ORACLE_OWNER=0x7107142636C85c549690b1Aca12Bdb8052d26Ae6 -yarn build:dump diff --git a/packages/contracts/scripts/deploy-scripts/mainnet-trial-genesis.sh b/packages/contracts/scripts/deploy-scripts/mainnet-trial-genesis.sh deleted file mode 100755 index 0349b100f3907..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/mainnet-trial-genesis.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -export L2_BLOCK_GAS_LIMIT=15000000 -export L2_CHAIN_ID=66666 -export BLOCK_SIGNER_ADDRESS=0x27770a9694e4B4b1E130Ab91Bc327C36855f612E -export L1_STANDARD_BRIDGE_ADDRESS=0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1 -export L1_FEE_WALLET_ADDRESS=0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 -export L1_CROSS_DOMAIN_MESSENGER_ADDRESS=0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 -export WHITELIST_OWNER=0x0000000000000000000000000000000000000000 -export GAS_PRICE_ORACLE_OWNER=0x84f70449f90300997840eCb0918873745Ede7aE6 -yarn build:dump diff --git a/packages/contracts/scripts/deploy-scripts/mainnet-trial.sh b/packages/contracts/scripts/deploy-scripts/mainnet-trial.sh deleted file mode 100755 index f1a23ac0cc4fb..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/mainnet-trial.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -### DEPLOYMENT SCRIPT ### -# To be called from root of contracts dir # - -# Required env vars -if [[ -z "$CONTRACTS_DEPLOYER_KEY" ]]; then - echo "Must pass CONTRACTS_DEPLOYER_KEY" - exit 1 -fi -if [[ -z "$CONTRACTS_RPC_URL" ]]; then - echo "Must pass CONTRACTS_RPC_URL" - exit 1 -fi -if [[ -z "$ETHERSCAN_API_KEY" ]]; then - echo "Must pass ETHERSCAN_API_KEY" - exit 1 -fi - -CONTRACTS_TARGET_NETWORK=mainnet-trial \ -npx hardhat deploy \ - --ctc-max-transaction-gas-limit 15000000 \ - --ctc-enqueue-gas-cost 60000 \ - --ctc-l2-gas-discount-divisor 32 \ - --l1-block-time-seconds 15 \ - --ovm-address-manager-owner 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A \ - --ovm-sequencer-address 0xB79f76EF2c5F0286176833E7B2eEe103b1CC3244 \ - --ovm-proposer-address 0x9A2F243c605e6908D96b18e21Fb82Bf288B19EF3 \ - --scc-fraud-proof-window 10 \ - --scc-sequencer-publish-window 12592000 \ - --network mainnet-trial \ - --gasprice 2000000000000 \ - --forked true \ - --num-deploy-confirmations 0 \ - --tags upgrade diff --git a/packages/contracts/scripts/deploy-scripts/mainnet.sh b/packages/contracts/scripts/deploy-scripts/mainnet.sh deleted file mode 100755 index 8982767a5ad29..0000000000000 --- a/packages/contracts/scripts/deploy-scripts/mainnet.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -### DEPLOYMENT SCRIPT ### -# To be called from root of contracts dir # - -# Required env vars -if [[ -z "$CONTRACTS_DEPLOYER_KEY" ]]; then - echo "Must pass CONTRACTS_DEPLOYER_KEY" - exit 1 -fi -if [[ -z "$CONTRACTS_RPC_URL" ]]; then - echo "Must pass CONTRACTS_RPC_URL" - exit 1 -fi -if [[ -z "$ETHERSCAN_API_KEY" ]]; then - echo "Must pass ETHERSCAN_API_KEY" - exit 1 -fi - -CONTRACTS_TARGET_NETWORK=mainnet \ -npx hardhat deploy \ - --l1-block-time-seconds 15 \ - --ctc-max-transaction-gas-limit 15000000 \ - --ctc-l2-gas-discount-divisor 32 \ - --ctc-enqueue-gas-cost 60000 \ - --scc-fraud-proof-window 604800 \ - --scc-sequencer-publish-window 12592000 \ - --ovm-sequencer-address 0x6887246668a3b87F54DeB3b94Ba47a6f63F32985 \ - --ovm-proposer-address 0x473300df21D047806A082244b417f96b32f13A33 \ - --ovm-address-manager-owner 0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A \ - --gasprice 150000000000 \ - --num-deploy-confirmations 4 \ - --tags upgrade \ - --network mainnet - -CONTRACTS_TARGET_NETWORK=mainnet \ -npx hardhat etherscan-verify \ - --network mainnet \ - --sleep diff --git a/packages/contracts/src/deploy-config.ts b/packages/contracts/src/deploy-config.ts new file mode 100644 index 0000000000000..c708ba3a1f521 --- /dev/null +++ b/packages/contracts/src/deploy-config.ts @@ -0,0 +1,282 @@ +import { ethers } from 'ethers' + +/** + * Defines the configuration for a deployment. + */ +export interface DeployConfig { + /** + * Name of the network to deploy to. Must be the name of one of the networks listed in + * hardhat.config.ts. + */ + network: string + + /** + * Whether or not this network is a forked network. + */ + isForkedNetwork?: boolean + + /** + * Optional number of confs to wait during deployment. + */ + numDeployConfirmations?: number + + /** + * Optional gas price to use for deployment transactions. + */ + gasPrice?: number + + /** + * Estimated average L1 block time in seconds. + */ + l1BlockTimeSeconds: number + + /** + * Gas limit for blocks on L2. + */ + l2BlockGasLimit: number + + /** + * Chain ID for the L2 network. + */ + l2ChainId: number + + /** + * Discount divisor used to calculate gas burn for L1 to L2 transactions. + */ + ctcL2GasDiscountDivisor: number + + /** + * Cost of the "enqueue" function in the CTC. + */ + ctcEnqueueGasCost: number + + /** + * Fault proof window in seconds. + */ + sccFaultProofWindowSeconds: number + + /** + * Sequencer publish window in seconds. + */ + sccSequencerPublishWindowSeconds: number + + /** + * Address of the Sequencer (publishes to CTC). + */ + ovmSequencerAddress: string + + /** + * Address of the Proposer (publishes to SCC). + */ + ovmProposerAddress: string + + /** + * Address of the account that will sign blocks. + */ + ovmBlockSignerAddress: string + + /** + * Address that will receive fees on L1. + */ + ovmFeeWalletAddress: string + + /** + * Address of the owner of the AddressManager contract on L1. + */ + ovmAddressManagerOwner: string + + /** + * Address of the owner of the GasPriceOracle contract on L2. + */ + ovmGasPriceOracleOwner: string + + /** + * Optional whitelist owner address. + */ + ovmWhitelistOwner?: string + + /** + * Optional initial overhead value for GPO (default: 2750). + */ + gasPriceOracleOverhead?: number + + /** + * Optional initial scalar value for GPO (default: 1500000). + */ + gasPriceOracleScalar?: number + + /** + * Optional initial decimals for GPO (default: 6). + */ + gasPriceOracleDecimals?: number + + /** + * Optional initial L1 base fee for GPO (default: 1). + */ + gasPriceOracleL1BaseFee?: number + + /** + * Optional initial L2 gas price for GPO (default: 1). + */ + gasPriceOracleL2GasPrice?: number + + /** + * Optional block number to enable the Berlin hardfork (default: 0). + */ + hfBerlinBlock?: number +} + +/** + * Specification for each of the configuration options. + */ +const configSpec: { + [K in keyof DeployConfig]: { + type: string + default?: any + } +} = { + network: { + type: 'string', + }, + isForkedNetwork: { + type: 'boolean', + default: false, + }, + numDeployConfirmations: { + type: 'number', + default: 0, + }, + gasPrice: { + type: 'number', + default: undefined, + }, + l1BlockTimeSeconds: { + type: 'number', + }, + l2BlockGasLimit: { + type: 'number', + }, + l2ChainId: { + type: 'number', + }, + ctcL2GasDiscountDivisor: { + type: 'number', + }, + ctcEnqueueGasCost: { + type: 'number', + }, + sccFaultProofWindowSeconds: { + type: 'number', + }, + sccSequencerPublishWindowSeconds: { + type: 'number', + }, + ovmSequencerAddress: { + type: 'address', + }, + ovmProposerAddress: { + type: 'address', + }, + ovmBlockSignerAddress: { + type: 'address', + }, + ovmFeeWalletAddress: { + type: 'address', + }, + ovmAddressManagerOwner: { + type: 'address', + }, + ovmGasPriceOracleOwner: { + type: 'address', + }, + ovmWhitelistOwner: { + type: 'address', + default: ethers.constants.AddressZero, + }, + gasPriceOracleOverhead: { + type: 'number', + default: 2750, + }, + gasPriceOracleScalar: { + type: 'number', + default: 1_500_000, + }, + gasPriceOracleDecimals: { + type: 'number', + default: 6, + }, + gasPriceOracleL1BaseFee: { + type: 'number', + default: 1, + }, + gasPriceOracleL2GasPrice: { + type: 'number', + default: 1, + }, + hfBerlinBlock: { + type: 'number', + default: 0, + }, +} + +/** + * Gets the deploy config for the given network. + * + * @param network Network name. + * @returns Deploy config for the given network. + */ +export const getDeployConfig = (network: string): Required => { + let config: DeployConfig + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + config = require(`../deploy-config/${network}.ts`).default + } catch (err) { + throw new Error( + `error while loading deploy config for network: ${network}, ${err}` + ) + } + + return parseDeployConfig(config) +} + +/** + * Parses and validates the given deploy config, replacing any missing values with defaults. + * + * @param config Deploy config to parse. + * @returns Parsed deploy config. + */ +export const parseDeployConfig = ( + config: DeployConfig +): Required => { + // Create a clone of the config object. Shallow clone is fine because none of the input options + // are expected to be objects or functions etc. + const parsed = { ...config } + + for (const [key, spec] of Object.entries(configSpec)) { + // Make sure the value is defined, or use a default. + if (parsed[key] === undefined) { + if ('default' in spec) { + parsed[key] = spec.default + } else { + throw new Error( + `deploy config is missing required field: ${key} (${spec.type})` + ) + } + } else { + // Make sure the default has the correct type. + if (spec.type === 'address') { + if (!ethers.utils.isAddress(parsed[key])) { + throw new Error( + `deploy config field: ${key} is not of type ${spec.type}: ${parsed[key]}` + ) + } + } else if (typeof parsed[key] !== spec.type) { + throw new Error( + `deploy config field: ${key} is not of type ${spec.type}: ${parsed[key]}` + ) + } + } + } + + return parsed as Required +} diff --git a/packages/contracts/src/deploy-utils.ts b/packages/contracts/src/deploy-utils.ts index 6e9416ad3e103..eda8526851173 100644 --- a/packages/contracts/src/deploy-utils.ts +++ b/packages/contracts/src/deploy-utils.ts @@ -1,10 +1,11 @@ -/* Imports: External */ import { ethers, Contract } from 'ethers' import { Provider } from '@ethersproject/abstract-provider' import { Signer } from '@ethersproject/abstract-signer' import { sleep, awaitCondition } from '@eth-optimism/core-utils' import { HttpNetworkConfig } from 'hardhat/types' +import { getDeployConfig } from './deploy-config' + /** * @param {Any} hre Hardhat runtime environment * @param {String} name Contract name from the names object @@ -31,13 +32,14 @@ export const deployAndVerifyAndThen = async ({ }) => { const { deploy } = hre.deployments const { deployer } = await hre.getNamedAccounts() + const deployConfig = getDeployConfig(hre.network.name) const result = await deploy(name, { contract, from: deployer, args, log: true, - waitConfirmations: hre.deployConfig.numDeployConfirmations, + waitConfirmations: deployConfig.numDeployConfirmations, }) await hre.ethers.provider.waitForTransaction(result.transactionHash) @@ -92,6 +94,8 @@ export const getAdvancedContract = (opts: { hre: any contract: Contract }): Contract => { + const deployConfig = getDeployConfig(opts.hre.network.name) + // Temporarily override Object.defineProperty to bypass ether's object protection. const def = Object.defineProperty Object.defineProperty = (obj, propName, prop) => { @@ -115,7 +119,7 @@ export const getAdvancedContract = (opts: { // We want to use the gas price that has been configured at the beginning of the deployment. // However, if the function being triggered is a "constant" (static) function, then we don't // want to provide a gas price because we're prone to getting insufficient balance errors. - let gasPrice = opts.hre.deployConfig.gasPrice || undefined + let gasPrice = deployConfig.gasPrice || undefined if (contract.interface.getFunction(fnName).constant) { gasPrice = 0 } @@ -147,7 +151,7 @@ export const getAdvancedContract = (opts: { return contract[fnName](...args) } } else if ( - receipt.confirmations >= opts.hre.deployConfig.numDeployConfirmations + receipt.confirmations >= deployConfig.numDeployConfirmations ) { return tx } @@ -163,7 +167,9 @@ export const fundAccount = async ( address: string, amount: ethers.BigNumber ) => { - if ((hre as any).deployConfig.forked !== 'true') { + const deployConfig = getDeployConfig(hre.network.name) + + if (!deployConfig.isForkedNetwork) { throw new Error('this method can only be used against a forked network') } @@ -194,7 +200,9 @@ export const sendImpersonatedTx = async (opts: { gas: string args: any[] }) => { - if ((opts.hre as any).deployConfig.forked !== 'true') { + const deployConfig = getDeployConfig(opts.hre.network.name) + + if (!deployConfig.isForkedNetwork) { throw new Error('this method can only be used against a forked network') } diff --git a/packages/contracts/tasks/deploy.ts b/packages/contracts/tasks/deploy.ts deleted file mode 100644 index 72a2fa8bade8a..0000000000000 --- a/packages/contracts/tasks/deploy.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* Imports: External */ -import { ethers } from 'ethers' -import { task } from 'hardhat/config' -import * as types from 'hardhat/internal/core/params/argumentTypes' - -const DEFAULT_L1_BLOCK_TIME_SECONDS = 15 -const DEFAULT_CTC_MAX_TRANSACTION_GAS_LIMIT = 11_000_000 -const DEFAULT_CTC_L2_GAS_DISCOUNT_DIVISOR = 32 -const DEFAULT_CTC_ENQUEUE_GAS_COST = 60_000 -const DEFAULT_SCC_FRAUD_PROOF_WINDOW = 60 * 60 * 24 * 7 // 7 days -const DEFAULT_SCC_SEQUENCER_PUBLISH_WINDOW = 60 * 30 // 30 minutes -const DEFAULT_DEPLOY_CONFIRMATIONS = 12 - -task('deploy') - // Rollup config options - .addOptionalParam( - 'l1BlockTimeSeconds', - 'Number of seconds on average between every L1 block.', - DEFAULT_L1_BLOCK_TIME_SECONDS, - types.int - ) - .addOptionalParam( - 'ctcMaxTransactionGasLimit', - 'Max gas limit for L1 queue transactions.', - DEFAULT_CTC_MAX_TRANSACTION_GAS_LIMIT, - types.int - ) - .addOptionalParam( - 'ctcL2GasDiscountDivisor', - 'Max gas limit for L1 queue transactions.', - DEFAULT_CTC_L2_GAS_DISCOUNT_DIVISOR, - types.int - ) - .addOptionalParam( - 'ctcEnqueueGasCost', - 'Max gas limit for L1 queue transactions.', - DEFAULT_CTC_ENQUEUE_GAS_COST, - types.int - ) - .addOptionalParam( - 'sccFraudProofWindow', - 'Number of seconds until a transaction is considered finalized.', - DEFAULT_SCC_FRAUD_PROOF_WINDOW, - types.int - ) - .addOptionalParam( - 'sccSequencerPublishWindow', - 'Number of seconds that the sequencer is exclusively allowed to post state roots.', - DEFAULT_SCC_SEQUENCER_PUBLISH_WINDOW, - types.int - ) - // Permissioned address options - .addOptionalParam( - 'ovmSequencerAddress', - 'Address of the sequencer. Must be provided or this deployment will fail.', - undefined, - types.string - ) - .addOptionalParam( - 'ovmProposerAddress', - 'Address of the account that will propose state roots. Must be provided or this deployment will fail.', - undefined, - types.string - ) - .addOptionalParam( - 'ovmAddressManagerOwner', - 'Address that will own the Lib_AddressManager. Must be provided or this deployment will fail.', - undefined, - types.string - ) - .addOptionalParam( - 'numDeployConfirmations', - 'Number of confirmations to wait for each transaction in the deployment. More is safer.', - DEFAULT_DEPLOY_CONFIRMATIONS, - types.int - ) - .addOptionalParam( - 'forked', - 'Enable this when using a forked network (use "true")', - undefined, - types.string - ) - .setAction(async (args, hre: any, runSuper) => { - // Necessary because hardhat doesn't let us attach non-optional parameters to existing tasks. - const validateAddressArg = (argName: string) => { - if (args[argName] === undefined) { - throw new Error( - `argument for ${argName} is required but was not provided` - ) - } - if (!ethers.utils.isAddress(args[argName])) { - throw new Error( - `argument for ${argName} is not a valid address: ${args[argName]}` - ) - } - } - - validateAddressArg('ovmSequencerAddress') - validateAddressArg('ovmProposerAddress') - validateAddressArg('ovmAddressManagerOwner') - - hre.deployConfig = args - return runSuper(args) - }) diff --git a/packages/contracts/tasks/index.ts b/packages/contracts/tasks/index.ts index 1f0e0f750470e..2debb046b156b 100644 --- a/packages/contracts/tasks/index.ts +++ b/packages/contracts/tasks/index.ts @@ -1,6 +1,6 @@ -export * from './deploy' export * from './l2-gasprice' export * from './set-owner' +export * from './take-dump' export * from './validate-address-dictator' export * from './validate-chugsplash-dictator' export * from './whitelist' diff --git a/packages/contracts/src/make-genesis.ts b/packages/contracts/tasks/take-dump.ts similarity index 51% rename from packages/contracts/src/make-genesis.ts rename to packages/contracts/tasks/take-dump.ts index 7b3419849c3a1..0d5232eed4b6c 100644 --- a/packages/contracts/src/make-genesis.ts +++ b/packages/contracts/tasks/take-dump.ts @@ -1,83 +1,71 @@ -/* External Imports */ +import * as path from 'path' +import * as fs from 'fs' import { exec } from 'child_process' import { promisify } from 'util' +import * as mkdirp from 'mkdirp' import { ethers } from 'ethers' -import { - computeStorageSlots, - getStorageLayout, -} from '@defi-wonderland/smock/dist/src/utils' +import { task } from 'hardhat/config' import { remove0x } from '@eth-optimism/core-utils' -/* Internal Imports */ -import { predeploys } from './predeploys' -import { getContractArtifact } from './contract-artifacts' - -export interface RollupDeployConfig { - // Address that will own the L2 deployer whitelist. - whitelistOwner: string - // Address that will own the L2 gas price oracle. - gasPriceOracleOwner: string - // Overhead value of the gas price oracle - gasPriceOracleOverhead: number - // Scalar value of the gas price oracle - gasPriceOracleScalar: number - // L1 base fee of the gas price oracle - gasPriceOracleL1BaseFee: number - // L2 gas price of the gas price oracle - gasPriceOracleGasPrice: number - // Number of decimals of the gas price oracle scalar - gasPriceOracleDecimals: number - // Initial value for the L2 block gas limit. - l2BlockGasLimit: number - // Chain ID to give the L2 network. - l2ChainId: number - // Address of the key that will sign blocks. - blockSignerAddress: string - // Address of the L1StandardBridge contract. - l1StandardBridgeAddress: string - // Address of the L1 fee wallet. - l1FeeWalletAddress: string - // Address of the L1CrossDomainMessenger contract. - l1CrossDomainMessengerAddress: string - // Block height to activate berlin hardfork - berlinBlock: number -} - -/** - * Generates the initial state for the L2 system by injecting the relevant L2 system contracts. - * - * @param cfg Configuration for the L2 system. - * @returns Generated L2 genesis state. - */ -export const makeL2GenesisFile = async ( - cfg: RollupDeployConfig -): Promise => { - // Very basic validation. - for (const [key, val] of Object.entries(cfg)) { - if (val === undefined) { - throw new Error(`must provide an input for config value: ${key}`) - } +import { predeploys } from '../src/predeploys' +import { getContractFromArtifact } from '../src/deploy-utils' +import { getDeployConfig } from '../src/deploy-config' +import { names } from '../src/address-names' + +task('take-dump').setAction(async (args, hre) => { + /* eslint-disable @typescript-eslint/no-var-requires */ + + // Needs to be imported here or hardhat will throw a fit about hardhat being imported from + // within the configuration file. + const { + computeStorageSlots, + getStorageLayout, + } = require('@defi-wonderland/smock/dist/src/utils') + + // Needs to be imported here because the artifacts can only be generated after the contracts have + // been compiled, but compiling the contracts will import the config file which, as a result, + // will import this file. + const { getContractArtifact } = require('../src/contract-artifacts') + + /* eslint-enable @typescript-eslint/no-var-requires */ + + // Make sure we have a deploy config for this network + const deployConfig = getDeployConfig(hre.network.name) + + // Basic warning so users know that the whitelist will be disabled if the owner is the zero address. + if ( + deployConfig.ovmWhitelistOwner === undefined || + deployConfig.ovmWhitelistOwner === ethers.constants.AddressZero + ) { + console.log( + 'WARNING: whitelist owner is undefined or address(0), whitelist will be disabled' + ) } const variables = { OVM_DeployerWhitelist: { - owner: cfg.whitelistOwner, + owner: deployConfig.ovmWhitelistOwner, }, OVM_GasPriceOracle: { - _owner: cfg.gasPriceOracleOwner, - gasPrice: cfg.gasPriceOracleGasPrice, - l1BaseFee: cfg.gasPriceOracleL1BaseFee, - overhead: cfg.gasPriceOracleOverhead, - scalar: cfg.gasPriceOracleScalar, - decimals: cfg.gasPriceOracleDecimals, + _owner: deployConfig.ovmGasPriceOracleOwner, + gasPrice: deployConfig.gasPriceOracleL2GasPrice, + l1BaseFee: deployConfig.gasPriceOracleL1BaseFee, + overhead: deployConfig.gasPriceOracleOverhead, + scalar: deployConfig.gasPriceOracleScalar, + decimals: deployConfig.gasPriceOracleDecimals, }, L2StandardBridge: { - l1TokenBridge: cfg.l1StandardBridgeAddress, + l1TokenBridge: ( + await getContractFromArtifact( + hre, + names.managed.contracts.Proxy__OVM_L1StandardBridge + ) + ).address, messenger: predeploys.L2CrossDomainMessenger, }, OVM_SequencerFeeVault: { - l1FeeWallet: cfg.l1FeeWalletAddress, + l1FeeWallet: deployConfig.ovmFeeWalletAddress, }, OVM_ETH: { l2Bridge: predeploys.L2StandardBridge, @@ -89,7 +77,12 @@ export const makeL2GenesisFile = async ( // We default the xDomainMsgSender to this value to save gas. // See usage of this default in the L2CrossDomainMessenger contract. xDomainMsgSender: '0x000000000000000000000000000000000000dEaD', - l1CrossDomainMessenger: cfg.l1CrossDomainMessengerAddress, + l1CrossDomainMessenger: ( + await getContractFromArtifact( + hre, + names.managed.contracts.Proxy__OVM_L1CrossDomainMessenger + ) + ).address, // Set the messageNonce to a high value to avoid overwriting old sent messages. messageNonce: 100000, }, @@ -139,10 +132,10 @@ export const makeL2GenesisFile = async ( commit = '0000000000000000000000000000000000000000' } - return { + const genesis = { commit, config: { - chainId: cfg.l2ChainId, + chainId: deployConfig.l2ChainId, homesteadBlock: 0, eip150Block: 0, eip155Block: 0, @@ -152,19 +145,27 @@ export const makeL2GenesisFile = async ( petersburgBlock: 0, istanbulBlock: 0, muirGlacierBlock: 0, - berlinBlock: cfg.berlinBlock, + berlinBlock: deployConfig.hfBerlinBlock, clique: { period: 0, epoch: 30000, }, }, difficulty: '1', - gasLimit: cfg.l2BlockGasLimit.toString(10), + gasLimit: deployConfig.l2BlockGasLimit.toString(10), extradata: '0x' + '00'.repeat(32) + - remove0x(cfg.blockSignerAddress) + + remove0x(deployConfig.ovmBlockSignerAddress) + '00'.repeat(65), alloc: dump, } -} + + // Make sure the output location exists + const outdir = path.resolve(__dirname, '../genesis') + const outfile = path.join(outdir, `${hre.network.name}.json`) + mkdirp.sync(outdir) + + // Write the genesis file + fs.writeFileSync(outfile, JSON.stringify(genesis, null, 4)) +})