Commit 4c486d70 authored by Kelvin Fichter's avatar Kelvin Fichter

feat(ct): overhaul deployment process

Overhauls the contract deployment process to use a simpler typed
deployment method. Removes the need for deployment bash scripts and
makes review of deployment configurations much easier.
parent 3525635a
...@@ -36,30 +36,7 @@ services: ...@@ -36,30 +36,7 @@ services:
# Env vars for the deployment script. # Env vars for the deployment script.
CONTRACTS_RPC_URL: http://l1_chain:8545 CONTRACTS_RPC_URL: http://l1_chain:8545
CONTRACTS_DEPLOYER_KEY: 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' CONTRACTS_DEPLOYER_KEY: 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
CONTRACTS_TARGET_NETWORK: 'custom' CONTRACTS_TARGET_NETWORK: 'local'
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
ports: ports:
# expose the service to the host for getting the contract addrs # expose the service to the host for getting the contract addrs
- ${DEPLOYER_PORT:-8080}:8081 - ${DEPLOYER_PORT:-8080}:8081
......
...@@ -24,39 +24,7 @@ curl \ ...@@ -24,39 +24,7 @@ curl \
echo "Connected to L1." echo "Connected to L1."
echo "Building deployment command." echo "Building deployment command."
DEPLOY_CMD="npx hardhat deploy" DEPLOY_CMD="npx hardhat deploy --network $CONTRACTS_TARGET_NETWORK"
# 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"
echo "Deploying contracts. Deployment command:" echo "Deploying contracts. Deployment command:"
echo "$DEPLOY_CMD" echo "$DEPLOY_CMD"
...@@ -81,20 +49,13 @@ echo "}" >> addresses.json ...@@ -81,20 +49,13 @@ echo "}" >> addresses.json
echo "Built addresses.json. Content:" echo "Built addresses.json. Content:"
jq . addresses.json 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." echo "Building dump file."
yarn run build:dump npx hardhat take-dump --network $CONTRACTS_TARGET_NETWORK
mv addresses.json ./dist/dumps mv addresses.json ./genesis
cp ./genesis/$CONTRACTS_TARGET_NETWORK.json ./genesis/state-dump.latest.json
# service the addresses and dumps # service the addresses and dumps
echo "Starting server." echo "Starting server."
python3 -m http.server \ python3 -m http.server \
--bind "0.0.0.0" 8081 \ --bind "0.0.0.0" 8081 \
--directory ./dist/dumps --directory ./genesis
# 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=
...@@ -32,7 +32,9 @@ import { L1CrossDomainMessenger } from "@eth-optimism/contracts/L1/messaging/L1C ...@@ -32,7 +32,9 @@ import { L1CrossDomainMessenger } from "@eth-optimism/contracts/L1/messaging/L1C
``` ```
## Guide for Developers ## Guide for Developers
### Setup ### Setup
Install the following: Install the following:
- [`Node.js` (14+)](https://nodejs.org/en/) - [`Node.js` (14+)](https://nodejs.org/en/)
- [`npm`](https://www.npmjs.com/get-npm) - [`npm`](https://www.npmjs.com/get-npm)
...@@ -46,87 +48,75 @@ cd contracts ...@@ -46,87 +48,75 @@ cd contracts
``` ```
Install `npm` packages: Install `npm` packages:
```shell ```shell
yarn install yarn install
``` ```
### Running Tests ### Running Tests
Tests are executed via `yarn`: Tests are executed via `yarn`:
```shell ```shell
yarn test yarn test
``` ```
Run specific tests by giving a path to the file you want to run: Run specific tests by giving a path to the file you want to run:
```shell ```shell
yarn test ./test/path/to/my/test.spec.ts yarn test ./test/path/to/my/test.spec.ts
``` ```
### Measuring test coverage: ### Measuring test coverage:
```shell ```shell
yarn test:coverage yarn test:coverage
``` ```
The output is most easily viewable by opening the html file in your browser: The output is most easily viewable by opening the html file in your browser:
```shell ```shell
open ./coverage/index.html open ./coverage/index.html
``` ```
### Compiling and Building ### 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: Compile and build the various required with the `build` command:
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.
You can also build specific components as follows:
```shell ```shell
yarn build:contracts yarn build
``` ```
### Deploying the Contracts ### Deploying the Contracts
#### Required environment variables #### Required environment variables
You must set the following environment variables to execute a deployment: 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`.
```bash Fill out each of the environment variables before continuing.
# 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=...
# RPC URL connected to the L1 chain we're deploying to #### Creating a deployment configuration
export CONTRACTS_RPC_URL=...
# Your Etherscan API key for the L1 network Before you can carry out a deployment, you must create a deployment configuration file inside of the [deploy-config](./deploy-config/) folder.
export ETHERSCAN_API_KEY=... 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.
#### Creating a deployment script
Before you can carry out a deployment, you must create a deployment script. #### Executing a deployment
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.
Most variables within the deploy script are relatively self-explanatory. Once you've created your deploy config, you can execute a deployment with the following command:
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:
``` ```
--tags upgrade npx hardhat deploy --network <my network name>
``` ```
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 <my network name> --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. 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. 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. 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 ...@@ -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. 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). 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/<name>`. After your deployment is complete, your new contracts will be written to an artifacts directory in `./deployments/<my network name>`.
Your contracts will also be automatically verified as part of the deployment script.
#### 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 <my network name>
```
#### Creating a genesis file #### Creating a genesis file
Optimism expects that certain contracts (called "predeploys") be deployed to the L2 network at pre-determined addresses. 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. We guarantee this by creating a genesis file in which certain contracts are already within the L2 state at the genesis block.
You must first create a genesis generation script. To create the genesis file for your network, you must first deploy the L1 contracts using the appropriate commands from above.
Like in the deploy script, we recommend starting from an [existing script](./scripts/deploy-scripts/mainnet-genesis.sh). Once you've deployed your contracts, run the following command:
Modify each of the values within this script to match the values of your own deployment, taking any L1 contract addresses from the `./deployments/<name>` folder that was just generated or modified.
```
npx hardhat take-dump --network <my network name>
```
Execute this script to generate the genesis file. A genesis file will be created for you at `/genesis/<my network name>.json`.
You will find this genesis file at `./dist/dumps/state-dump.latest.json`.
You can then ingest this file via `geth init`. You can then ingest this file via `geth init`.
### Hardhat tasks ### Hardhat tasks
......
/* 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))
})()
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
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
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
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
...@@ -9,10 +9,12 @@ import { ...@@ -9,10 +9,12 @@ import {
sendImpersonatedTx, sendImpersonatedTx,
BIG_BALANCE, BIG_BALANCE,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
if ((hre as any).deployConfig.forked !== 'true') { const deployConfig = getDeployConfig(hre.network.name)
if (!deployConfig.isForkedNetwork) {
return return
} }
......
...@@ -2,18 +2,20 @@ ...@@ -2,18 +2,20 @@
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { names } from '../src/address-names' import { names } from '../src/address-names'
import { getDeployConfig } from '../src/deploy-config'
/* Imports: External */ /* Imports: External */
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const deployConfig = getDeployConfig(hre.network.name)
await deploy(names.unmanaged.Lib_AddressManager, { await deploy(names.unmanaged.Lib_AddressManager, {
from: deployer, from: deployer,
args: [], args: [],
log: true, log: true,
waitConfirmations: (hre as any).deployConfig.numDeployConfirmations, waitConfirmations: deployConfig.numDeployConfirmations,
}) })
} }
......
...@@ -6,9 +6,12 @@ import { ...@@ -6,9 +6,12 @@ import {
deployAndVerifyAndThen, deployAndVerifyAndThen,
getContractFromArtifact, getContractFromArtifact,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const Lib_AddressManager = await getContractFromArtifact( const Lib_AddressManager = await getContractFromArtifact(
hre, hre,
names.unmanaged.Lib_AddressManager names.unmanaged.Lib_AddressManager
...@@ -19,9 +22,9 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -19,9 +22,9 @@ const deployFn: DeployFunction = async (hre) => {
name: names.managed.contracts.CanonicalTransactionChain, name: names.managed.contracts.CanonicalTransactionChain,
args: [ args: [
Lib_AddressManager.address, Lib_AddressManager.address,
(hre as any).deployConfig.ctcMaxTransactionGasLimit, deployConfig.l2BlockGasLimit,
(hre as any).deployConfig.ctcL2GasDiscountDivisor, deployConfig.ctcL2GasDiscountDivisor,
(hre as any).deployConfig.ctcEnqueueGasCost, deployConfig.ctcEnqueueGasCost,
], ],
}) })
} }
......
...@@ -6,9 +6,12 @@ import { ...@@ -6,9 +6,12 @@ import {
deployAndVerifyAndThen, deployAndVerifyAndThen,
getContractFromArtifact, getContractFromArtifact,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const Lib_AddressManager = await getContractFromArtifact( const Lib_AddressManager = await getContractFromArtifact(
hre, hre,
names.unmanaged.Lib_AddressManager names.unmanaged.Lib_AddressManager
...@@ -19,8 +22,8 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -19,8 +22,8 @@ const deployFn: DeployFunction = async (hre) => {
name: names.managed.contracts.StateCommitmentChain, name: names.managed.contracts.StateCommitmentChain,
args: [ args: [
Lib_AddressManager.address, Lib_AddressManager.address,
(hre as any).deployConfig.sccFraudProofWindow, deployConfig.sccFaultProofWindowSeconds,
(hre as any).deployConfig.sccSequencerPublishWindow, deployConfig.sccSequencerPublishWindowSeconds,
], ],
}) })
} }
......
...@@ -7,9 +7,12 @@ import { ...@@ -7,9 +7,12 @@ import {
deployAndVerifyAndThen, deployAndVerifyAndThen,
getContractFromArtifact, getContractFromArtifact,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const Lib_AddressManager = await getContractFromArtifact( const Lib_AddressManager = await getContractFromArtifact(
hre, hre,
names.unmanaged.Lib_AddressManager names.unmanaged.Lib_AddressManager
...@@ -45,7 +48,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -45,7 +48,7 @@ const deployFn: DeployFunction = async (hre) => {
console.log( console.log(
`Transferring ownership of L1CrossDomainMessenger (implementation)...` `Transferring ownership of L1CrossDomainMessenger (implementation)...`
) )
const owner = (hre as any).deployConfig.ovmAddressManagerOwner const owner = deployConfig.ovmAddressManagerOwner
await contract.transferOwnership(owner) await contract.transferOwnership(owner)
console.log(`Checking that contract owner was correctly set...`) console.log(`Checking that contract owner was correctly set...`)
......
...@@ -7,10 +7,13 @@ import { ...@@ -7,10 +7,13 @@ import {
deployAndVerifyAndThen, deployAndVerifyAndThen,
getContractFromArtifact, getContractFromArtifact,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
import { predeploys } from '../src/predeploys' import { predeploys } from '../src/predeploys'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const Lib_AddressManager = await getContractFromArtifact( const Lib_AddressManager = await getContractFromArtifact(
hre, hre,
names.unmanaged.Lib_AddressManager names.unmanaged.Lib_AddressManager
...@@ -42,13 +45,13 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -42,13 +45,13 @@ const deployFn: DeployFunction = async (hre) => {
// CanonicalTransactionChain. // CanonicalTransactionChain.
{ {
name: names.managed.accounts.OVM_Sequencer, 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 // OVM_Proposer is the address allowed to submit state roots (transaction results) to the
// StateCommitmentChain. // StateCommitmentChain.
{ {
name: names.managed.accounts.OVM_Proposer, name: names.managed.accounts.OVM_Proposer,
address: (hre as any).deployConfig.ovmProposerAddress, address: deployConfig.ovmProposerAddress,
}, },
] ]
...@@ -69,7 +72,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -69,7 +72,7 @@ const deployFn: DeployFunction = async (hre) => {
name: names.unmanaged.AddressDictator, name: names.unmanaged.AddressDictator,
args: [ args: [
Lib_AddressManager.address, Lib_AddressManager.address,
(hre as any).deployConfig.ovmAddressManagerOwner, deployConfig.ovmAddressManagerOwner,
namesAndAddresses.map((pair) => { namesAndAddresses.map((pair) => {
return pair.name return pair.name
}), }),
......
...@@ -4,9 +4,11 @@ import { hexStringEquals, awaitCondition } from '@eth-optimism/core-utils' ...@@ -4,9 +4,11 @@ import { hexStringEquals, awaitCondition } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { getContractFromArtifact } from '../src/deploy-utils' import { getContractFromArtifact } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
// There's a risk that we could get front-run during a fresh deployment, which would brick this // 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) => { ...@@ -43,7 +45,7 @@ const deployFn: DeployFunction = async (hre) => {
) )
console.log(`Setting Proxy__OVM_L1CrossDomainMessenger owner...`) console.log(`Setting Proxy__OVM_L1CrossDomainMessenger owner...`)
const owner = (hre as any).deployConfig.ovmAddressManagerOwner const owner = deployConfig.ovmAddressManagerOwner
await Proxy__OVM_L1CrossDomainMessenger.transferOwnership(owner) await Proxy__OVM_L1CrossDomainMessenger.transferOwnership(owner)
console.log(`Checking that the contract owner was correctly set...`) console.log(`Checking that the contract owner was correctly set...`)
......
...@@ -9,9 +9,12 @@ import { ...@@ -9,9 +9,12 @@ import {
getContractFromArtifact, getContractFromArtifact,
deployAndVerifyAndThen, deployAndVerifyAndThen,
} from '../src/deploy-utils' } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names' import { names } from '../src/address-names'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const Proxy__OVM_L1StandardBridge = await getContractFromArtifact( const Proxy__OVM_L1StandardBridge = await getContractFromArtifact(
hre, hre,
'Proxy__OVM_L1StandardBridge' 'Proxy__OVM_L1StandardBridge'
...@@ -31,7 +34,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -31,7 +34,7 @@ const deployFn: DeployFunction = async (hre) => {
name: names.unmanaged.ChugSplashDictator, name: names.unmanaged.ChugSplashDictator,
args: [ args: [
Proxy__OVM_L1StandardBridge.address, Proxy__OVM_L1StandardBridge.address,
(hre as any).deployConfig.ovmAddressManagerOwner, deployConfig.ovmAddressManagerOwner,
ethers.utils.keccak256(bridgeCode), ethers.utils.keccak256(bridgeCode),
ethers.utils.hexZeroPad('0x00', 32), ethers.utils.hexZeroPad('0x00', 32),
ethers.utils.hexZeroPad(Proxy__OVM_L1CrossDomainMessenger.address, 32), ethers.utils.hexZeroPad(Proxy__OVM_L1CrossDomainMessenger.address, 32),
......
...@@ -4,9 +4,12 @@ import { hexStringEquals, awaitCondition } from '@eth-optimism/core-utils' ...@@ -4,9 +4,12 @@ import { hexStringEquals, awaitCondition } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { getContractFromArtifact } from '../src/deploy-utils' import { getContractFromArtifact } from '../src/deploy-utils'
import { getDeployConfig } from '../src/deploy-config'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const deployConfig = getDeployConfig(hre.network.name)
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const Lib_AddressManager = await getContractFromArtifact( const Lib_AddressManager = await getContractFromArtifact(
hre, hre,
'Lib_AddressManager', 'Lib_AddressManager',
...@@ -15,7 +18,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -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() const remoteOwner = await Lib_AddressManager.owner()
if (hexStringEquals(owner, remoteOwner)) { if (hexStringEquals(owner, remoteOwner)) {
console.log( console.log(
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
"build": "yarn build:contracts && yarn autogen:artifacts && yarn build:typescript", "build": "yarn build:contracts && yarn autogen:artifacts && yarn build:typescript",
"build:typescript": "tsc -p ./tsconfig.build.json", "build:typescript": "tsc -p ./tsconfig.build.json",
"build:contracts": "hardhat compile --show-stack-traces", "build:contracts": "hardhat compile --show-stack-traces",
"build:dump": "ts-node bin/take-dump.ts",
"autogen:markdown": "ts-node scripts/generate-markdown.ts", "autogen:markdown": "ts-node scripts/generate-markdown.ts",
"autogen:artifacts": "ts-node scripts/generate-artifacts.ts && ts-node scripts/generate-deployed-artifacts.ts", "autogen:artifacts": "ts-node scripts/generate-artifacts.ts && ts-node scripts/generate-deployed-artifacts.ts",
"test": "yarn test:contracts", "test": "yarn test:contracts",
......
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
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<DeployConfig> => {
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<DeployConfig> => {
// 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<DeployConfig>
}
/* Imports: External */
import { ethers, Contract } from 'ethers' import { ethers, Contract } from 'ethers'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Signer } from '@ethersproject/abstract-signer' import { Signer } from '@ethersproject/abstract-signer'
import { sleep, awaitCondition } from '@eth-optimism/core-utils' import { sleep, awaitCondition } from '@eth-optimism/core-utils'
import { HttpNetworkConfig } from 'hardhat/types' import { HttpNetworkConfig } from 'hardhat/types'
import { getDeployConfig } from './deploy-config'
/** /**
* @param {Any} hre Hardhat runtime environment * @param {Any} hre Hardhat runtime environment
* @param {String} name Contract name from the names object * @param {String} name Contract name from the names object
...@@ -31,13 +32,14 @@ export const deployAndVerifyAndThen = async ({ ...@@ -31,13 +32,14 @@ export const deployAndVerifyAndThen = async ({
}) => { }) => {
const { deploy } = hre.deployments const { deploy } = hre.deployments
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const deployConfig = getDeployConfig(hre.network.name)
const result = await deploy(name, { const result = await deploy(name, {
contract, contract,
from: deployer, from: deployer,
args, args,
log: true, log: true,
waitConfirmations: hre.deployConfig.numDeployConfirmations, waitConfirmations: deployConfig.numDeployConfirmations,
}) })
await hre.ethers.provider.waitForTransaction(result.transactionHash) await hre.ethers.provider.waitForTransaction(result.transactionHash)
...@@ -92,6 +94,8 @@ export const getAdvancedContract = (opts: { ...@@ -92,6 +94,8 @@ export const getAdvancedContract = (opts: {
hre: any hre: any
contract: Contract contract: Contract
}): Contract => { }): Contract => {
const deployConfig = getDeployConfig(opts.hre.network.name)
// Temporarily override Object.defineProperty to bypass ether's object protection. // Temporarily override Object.defineProperty to bypass ether's object protection.
const def = Object.defineProperty const def = Object.defineProperty
Object.defineProperty = (obj, propName, prop) => { Object.defineProperty = (obj, propName, prop) => {
...@@ -115,7 +119,7 @@ export const getAdvancedContract = (opts: { ...@@ -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. // 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 // 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. // 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) { if (contract.interface.getFunction(fnName).constant) {
gasPrice = 0 gasPrice = 0
} }
...@@ -147,7 +151,7 @@ export const getAdvancedContract = (opts: { ...@@ -147,7 +151,7 @@ export const getAdvancedContract = (opts: {
return contract[fnName](...args) return contract[fnName](...args)
} }
} else if ( } else if (
receipt.confirmations >= opts.hre.deployConfig.numDeployConfirmations receipt.confirmations >= deployConfig.numDeployConfirmations
) { ) {
return tx return tx
} }
...@@ -163,7 +167,9 @@ export const fundAccount = async ( ...@@ -163,7 +167,9 @@ export const fundAccount = async (
address: string, address: string,
amount: ethers.BigNumber 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') throw new Error('this method can only be used against a forked network')
} }
...@@ -194,7 +200,9 @@ export const sendImpersonatedTx = async (opts: { ...@@ -194,7 +200,9 @@ export const sendImpersonatedTx = async (opts: {
gas: string gas: string
args: any[] 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') throw new Error('this method can only be used against a forked network')
} }
......
/* 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)
})
export * from './deploy'
export * from './l2-gasprice' export * from './l2-gasprice'
export * from './set-owner' export * from './set-owner'
export * from './take-dump'
export * from './validate-address-dictator' export * from './validate-address-dictator'
export * from './validate-chugsplash-dictator' export * from './validate-chugsplash-dictator'
export * from './whitelist' export * from './whitelist'
......
/* External Imports */ import * as path from 'path'
import * as fs from 'fs'
import { exec } from 'child_process' import { exec } from 'child_process'
import { promisify } from 'util' import { promisify } from 'util'
import * as mkdirp from 'mkdirp'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { import { task } from 'hardhat/config'
computeStorageSlots,
getStorageLayout,
} from '@defi-wonderland/smock/dist/src/utils'
import { remove0x } from '@eth-optimism/core-utils' import { remove0x } from '@eth-optimism/core-utils'
/* Internal Imports */ import { predeploys } from '../src/predeploys'
import { predeploys } from './predeploys' import { getContractFromArtifact } from '../src/deploy-utils'
import { getContractArtifact } from './contract-artifacts' import { getDeployConfig } from '../src/deploy-config'
import { names } from '../src/address-names'
export interface RollupDeployConfig {
// Address that will own the L2 deployer whitelist. task('take-dump').setAction(async (args, hre) => {
whitelistOwner: string /* eslint-disable @typescript-eslint/no-var-requires */
// Address that will own the L2 gas price oracle.
gasPriceOracleOwner: string // Needs to be imported here or hardhat will throw a fit about hardhat being imported from
// Overhead value of the gas price oracle // within the configuration file.
gasPriceOracleOverhead: number const {
// Scalar value of the gas price oracle computeStorageSlots,
gasPriceOracleScalar: number getStorageLayout,
// L1 base fee of the gas price oracle } = require('@defi-wonderland/smock/dist/src/utils')
gasPriceOracleL1BaseFee: number
// L2 gas price of the gas price oracle // Needs to be imported here because the artifacts can only be generated after the contracts have
gasPriceOracleGasPrice: number // been compiled, but compiling the contracts will import the config file which, as a result,
// Number of decimals of the gas price oracle scalar // will import this file.
gasPriceOracleDecimals: number const { getContractArtifact } = require('../src/contract-artifacts')
// Initial value for the L2 block gas limit.
l2BlockGasLimit: number /* eslint-enable @typescript-eslint/no-var-requires */
// Chain ID to give the L2 network.
l2ChainId: number // Make sure we have a deploy config for this network
// Address of the key that will sign blocks. const deployConfig = getDeployConfig(hre.network.name)
blockSignerAddress: string
// Address of the L1StandardBridge contract. // Basic warning so users know that the whitelist will be disabled if the owner is the zero address.
l1StandardBridgeAddress: string if (
// Address of the L1 fee wallet. deployConfig.ovmWhitelistOwner === undefined ||
l1FeeWalletAddress: string deployConfig.ovmWhitelistOwner === ethers.constants.AddressZero
// Address of the L1CrossDomainMessenger contract. ) {
l1CrossDomainMessengerAddress: string console.log(
// Block height to activate berlin hardfork 'WARNING: whitelist owner is undefined or address(0), whitelist will be disabled'
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<any> => {
// 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}`)
}
} }
const variables = { const variables = {
OVM_DeployerWhitelist: { OVM_DeployerWhitelist: {
owner: cfg.whitelistOwner, owner: deployConfig.ovmWhitelistOwner,
}, },
OVM_GasPriceOracle: { OVM_GasPriceOracle: {
_owner: cfg.gasPriceOracleOwner, _owner: deployConfig.ovmGasPriceOracleOwner,
gasPrice: cfg.gasPriceOracleGasPrice, gasPrice: deployConfig.gasPriceOracleL2GasPrice,
l1BaseFee: cfg.gasPriceOracleL1BaseFee, l1BaseFee: deployConfig.gasPriceOracleL1BaseFee,
overhead: cfg.gasPriceOracleOverhead, overhead: deployConfig.gasPriceOracleOverhead,
scalar: cfg.gasPriceOracleScalar, scalar: deployConfig.gasPriceOracleScalar,
decimals: cfg.gasPriceOracleDecimals, decimals: deployConfig.gasPriceOracleDecimals,
}, },
L2StandardBridge: { L2StandardBridge: {
l1TokenBridge: cfg.l1StandardBridgeAddress, l1TokenBridge: (
await getContractFromArtifact(
hre,
names.managed.contracts.Proxy__OVM_L1StandardBridge
)
).address,
messenger: predeploys.L2CrossDomainMessenger, messenger: predeploys.L2CrossDomainMessenger,
}, },
OVM_SequencerFeeVault: { OVM_SequencerFeeVault: {
l1FeeWallet: cfg.l1FeeWalletAddress, l1FeeWallet: deployConfig.ovmFeeWalletAddress,
}, },
OVM_ETH: { OVM_ETH: {
l2Bridge: predeploys.L2StandardBridge, l2Bridge: predeploys.L2StandardBridge,
...@@ -89,7 +77,12 @@ export const makeL2GenesisFile = async ( ...@@ -89,7 +77,12 @@ export const makeL2GenesisFile = async (
// We default the xDomainMsgSender to this value to save gas. // We default the xDomainMsgSender to this value to save gas.
// See usage of this default in the L2CrossDomainMessenger contract. // See usage of this default in the L2CrossDomainMessenger contract.
xDomainMsgSender: '0x000000000000000000000000000000000000dEaD', 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. // Set the messageNonce to a high value to avoid overwriting old sent messages.
messageNonce: 100000, messageNonce: 100000,
}, },
...@@ -139,10 +132,10 @@ export const makeL2GenesisFile = async ( ...@@ -139,10 +132,10 @@ export const makeL2GenesisFile = async (
commit = '0000000000000000000000000000000000000000' commit = '0000000000000000000000000000000000000000'
} }
return { const genesis = {
commit, commit,
config: { config: {
chainId: cfg.l2ChainId, chainId: deployConfig.l2ChainId,
homesteadBlock: 0, homesteadBlock: 0,
eip150Block: 0, eip150Block: 0,
eip155Block: 0, eip155Block: 0,
...@@ -152,19 +145,27 @@ export const makeL2GenesisFile = async ( ...@@ -152,19 +145,27 @@ export const makeL2GenesisFile = async (
petersburgBlock: 0, petersburgBlock: 0,
istanbulBlock: 0, istanbulBlock: 0,
muirGlacierBlock: 0, muirGlacierBlock: 0,
berlinBlock: cfg.berlinBlock, berlinBlock: deployConfig.hfBerlinBlock,
clique: { clique: {
period: 0, period: 0,
epoch: 30000, epoch: 30000,
}, },
}, },
difficulty: '1', difficulty: '1',
gasLimit: cfg.l2BlockGasLimit.toString(10), gasLimit: deployConfig.l2BlockGasLimit.toString(10),
extradata: extradata:
'0x' + '0x' +
'00'.repeat(32) + '00'.repeat(32) +
remove0x(cfg.blockSignerAddress) + remove0x(deployConfig.ovmBlockSignerAddress) +
'00'.repeat(65), '00'.repeat(65),
alloc: dump, 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))
})
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