Commit 2bea306a authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: delete hh deploy scripts

Deletes the hardhat deploy scripts from `contracts-bedrock`
now that the foundry scripts have been merged. The `op-stack`
docs are also updated with a new workflow for using the forge
scripts.

The altered instructions in the docs have been tested on goerli
and can confirm that they work.
parent 28038018
......@@ -33,6 +33,8 @@ You’ll need the following software installed to follow this tutorial:
- [Yarn](https://classic.yarnpkg.com/lang/en/docs/install/)
- [Foundry](https://github.com/foundry-rs/foundry#installation)
- [Make](https://linux.die.net/man/1/make)
- [jq](https://github.com/jqlang/jq)
- [direnv](https://direnv.net)
This tutorial was checked on:
......@@ -126,28 +128,38 @@ You can generate all of these keys with the `rekey` tool in the `contracts-bedro
cd packages/contracts-bedrock
```
1. Run the `rekey` command:
1. Use `cast wallet` to generate new accounts
```bash
npx hardhat rekey
echo "Admin:"
cast wallet new
echo "Proposer:"
cast wallet new
echo "Batcher:"
cast wallet new
echo "Sequencer:"
cast wallet new
```
You should get an output like the following:
```
Mnemonic: barely tongue excite actor edge huge lion employ gauge despair this learn
Admin: 0x301c314ca0eedf88a5f7a44680d9dccceb8fcbea
Private Key: ef06ba0291b6e2fa336fd9c06de9c2f18f72ed17cd4fcbda7b376f10592b43d8
Proposer: 0x54355b7d195fcdea96696a522c444c185afaf1a8
Private Key: 8bf67a8cd20087472db00fd869a0ffd7574a4481fb2a07a5f5c6bfb46dcb09ca
Batcher: 0x9a686086e3c74ddd5b59b710b26a73407d9c7e97
Private Key: 1533b607f668cce9553cafbfdfe9529eb31d67f1958d4b16fbdf857a8c50dd56
Sequencer: 0x0324a4c8c1955cb8364e8f07558238b3d2aa5f55
Private Key: fba31658f320bb8ce1ce39fab3c7c2acea6b4dd69cc8483fd85388a461d8426b
Admin:
Successfully created new keypair.
Address: 0x9f92bdF0db69264462FC305913960Edfcc7a7c7F
Private key: 0x30e66956e1a12b81f0f2cfb982286b2f566eb73649833831d9f80b12f8fa183c
Proposer:
Successfully created new keypair.
Address: 0x31dE9B6473fc47af36ec23878bA34824B9F4AB30
Private key: 0x8bd1c8dfffef880f8f9ab8162f97ccd119c1aac28fe00dacf919459f88e0f37d
Batcher:
Successfully created new keypair.
Address: 0x6A3DC843843139f17Fcf04C057bb536A421DC9c6
Private key: 0x3ce44144b7fde797a28f4e47b210a4d42c3a3b642e538b54458cba2740db5ac2
Sequencer:
Successfully created new keypair.
Address: 0x98C6cadB1fe77aBB7bD968fC3E9b206111e72848
Private key: 0x3f4241229bb6f155140d98e0f5dd2aad7ae983f5af5d61555d05eb8e5d9514db
```
Save these accounts and their respective private keys somewhere, you’ll need them later. Fund the `Admin` address with a small amount of Goerli ETH as we’ll use that account to deploy our smart contracts. You’ll also need to fund the `Proposer` and `Batcher` address — note that the `Batcher` burns through the most ETH because it publishes transaction data to L1.
......@@ -160,7 +172,7 @@ Recommended funding amounts are as follows:
::: danger Not for production deployments
The `rekey` tool is *not* designed for production deployments. If you are deploying an OP Stack based chain into production, you should likely be using a combination of hardware security modules and hardware wallets.
The `cast wallet` tool is *not* designed for production deployments. If you are deploying an OP Stack based chain into production, you should likely be using a combination of hardware security modules and hardware wallets.
:::
......@@ -180,10 +192,28 @@ Once you’ve built both repositories, you’ll need head back to the Optimism M
cd packages/contracts-bedrock
```
1. Inside of `contracts-bedrock`, copy `.env.example` to `.env`.
```sh
cp .envrc.example .envrc
```
1. Fill out the environment variables inside of that file:
- `ETH_RPC_URL` — URL for your L1 node.
- `PRIVATE_KEY` — Private key of the `Admin` account.
- `DEPLOYMENT_CONTEXT` - Name of the network, should be "getting-started"
1. Pull the environment variables into context using `direnv`
```bash
direnv allow .
```
1. Before we can create our configuration file, we’ll need to pick an L1 block to serve as the starting point for our Rollup. It’s best to use a finalized L1 block as our starting block. You can use the `cast` command provided by Foundry to grab all of the necessary information (replace `<RPC>` with the URL for your L1 Goerli node):
```bash
cast block finalized --rpc-url <RPC> | grep -E "(timestamp|hash|number)"
cast block finalized --rpc-url $ETH_RPC_URL | grep -E "(timestamp|hash|number)"
```
You’ll get back something that looks like the following:
......@@ -207,21 +237,11 @@ Once you’ve built both repositories, you’ll need head back to the Optimism M
Once you’ve configured your network, it’s time to deploy the L1 smart contracts necessary for the functionality of the chain.
1. Inside of `contracts-bedrock`, copy `.env.example` to `.env`.
```sh
cp .env.example .env
```
1. Fill out the two environment variables inside of that file:
- `L1_RPC` — URL for your L1 node.
- `PRIVATE_KEY_DEPLOYER` — Private key of the `Admin` account.
1. Once you’re ready, deploy the L1 smart contracts:
```bash
npx hardhat deploy --network getting-started --tags l1
forge script scripts/Deploy.s.sol:Deploy --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
forge script scripts/Deploy.s.sol:Deploy --sig 'sync()' --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
```
Contract deployment can take up to 15 minutes. Please wait for all smart contracts to be fully deployed before continuing to the next step.
......
# RPC for the L1 network to deploy to
L1_RPC=
# Private key for the deployer account
PRIVATE_KEY_DEPLOYER=
# Optional Tenderly details for a simulation link during deployment
TENDERLY_PROJECT=
TENDERLY_USERNAME=
# The following settings are useful for manually testing the migration scripts.
# Define if cast commands should be printed.
CAST_COMMANDS=1
# Saves deployment artifacts when using the 'live' network
SAVE_DEPLOYMENTS=1
# Disable the live deployer so that transactions must be submitted manually
DISABLE_LIVE_DEPLOYER=true
# Sets the deployer's key to match the first default hardhat account
PRIVATE_KEY_DEPLOYER=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# RPC for the network to deploy to
export ETH_RPC_URL=
# Sets the deployer's key to match the first default hardhat account
export PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# Name of the deployed network
export DEPLOYMENT_CONTEXT=getting-started
# Optional Tenderly details for a simulation link during deployment
export TENDERLY_PROJECT=
export TENDERLY_USERNAME=
......@@ -16,3 +16,6 @@ scripts/differential-testing/differential-testing
deployments/900
deployments/901
deployments/hardhat
.envrc
!.envrc.example
deployments/getting-started
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
await deploy({
hre,
name: 'Lib_AddressManager',
contract: 'AddressManager',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'owner', deployer)
},
})
}
deployFn.tags = ['AddressManager', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
getContractsFromArtifacts,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
const proxyAdmin = await deploy({
hre,
name: 'ProxyAdmin',
args: [deployer],
postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer.
await assertContractVariable(contract, 'owner', deployer)
},
})
let addressManagerOnProxy = await proxyAdmin.callStatic.addressManager()
if (addressManagerOnProxy !== addressManager.address) {
// Set the address manager on the proxy admin
console.log(
`ProxyAdmin(${proxyAdmin.address}).setAddressManager(${addressManager.address})`
)
const tx = await proxyAdmin.setAddressManager(addressManager.address)
await tx.wait()
}
// Validate the address manager was set correctly.
addressManagerOnProxy = await proxyAdmin.callStatic.addressManager()
assert(
addressManagerOnProxy === addressManager.address,
'AddressManager not set on ProxyAdmin'
)
}
deployFn.tags = ['ProxyAdmin', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
getDeploymentAddress,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({
hre,
name: 'Proxy__OVM_L1StandardBridge',
contract: 'L1ChugSplashProxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'getOwner', proxyAdmin)
},
})
}
deployFn.tags = ['L1StandardBridgeProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({
hre,
name: 'L2OutputOracleProxy',
contract: 'Proxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
},
})
}
deployFn.tags = ['L2OutputOracleProxy', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy, getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
// The name in the address manager for this contract
const name = 'OVM_L1CrossDomainMessenger'
console.log(
`Setting up ResolvedDelegateProxy with AddressManager(${addressManager.address})`
)
const l1CrossDomainMessengerProxy = await deploy({
hre,
name: 'Proxy__OVM_L1CrossDomainMessenger',
contract: 'ResolvedDelegateProxy',
args: [addressManager.address, name],
})
let addr = await addressManager.getAddress(name)
if (addr !== l1CrossDomainMessengerProxy.address) {
console.log(
`AddressManager(${addressManager.address}).setAddress(${name}, ${l1CrossDomainMessengerProxy.address})`
)
const tx = await addressManager.setAddress(
name,
l1CrossDomainMessengerProxy.address
)
await tx.wait()
}
addr = await addressManager.getAddress(name)
assert(
addr === l1CrossDomainMessengerProxy.address,
`${name} not set correctly`
)
}
deployFn.tags = ['L1CrossDomainMessengerProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({
hre,
name: 'OptimismPortalProxy',
contract: 'Proxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
},
})
}
deployFn.tags = ['OptimismPortalProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({
hre,
name: 'OptimismMintableERC20FactoryProxy',
contract: 'Proxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
},
})
}
deployFn.tags = ['OptimismMintableERC20FactoryProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
getDeploymentAddress,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({
hre,
name: 'L1ERC721BridgeProxy',
contract: 'Proxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
},
})
}
deployFn.tags = ['L1ERC721BridgeProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({
hre,
name: 'SystemConfigProxy',
contract: 'Proxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
},
})
}
deployFn.tags = ['SystemConfigProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy, getDeploymentAddress } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
// We only want to deploy the dgf on devnet for now
const network = await hre.ethers.provider.getNetwork()
const chainId = network.chainId
if (chainId === 900) {
console.log('Devnet detected, deploying DisputeGameFactoryProxy')
const disputeGameFactoryProxy = await deploy({
hre,
name: 'DisputeGameFactoryProxy',
contract: 'Proxy',
args: [proxyAdmin],
})
console.log(
`DisputeGameFactoryProxy deployed at ${disputeGameFactoryProxy.address}`
)
} else {
console.log('Skipping deployment of DisputeGameFactoryProxy')
}
}
deployFn.tags = ['DisputeGameFactoryProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { predeploys } from '../src'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
)
await deploy({
hre,
name: 'L1StandardBridge',
args: [L1CrossDomainMessengerProxy.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'MESSENGER',
L1CrossDomainMessengerProxy.address
)
await assertContractVariable(
contract,
'OTHER_BRIDGE',
predeploys.L2StandardBridge
)
},
})
}
deployFn.tags = ['L1StandardBridgeImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const OptimismPortalProxy = await getContractFromArtifact(
hre,
'OptimismPortalProxy'
)
await deploy({
hre,
name: 'L1CrossDomainMessenger',
args: [OptimismPortalProxy.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'PORTAL',
OptimismPortalProxy.address
)
},
})
}
deployFn.tags = ['L1CrossDomainMessengerImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const isLiveDeployer =
deployer.toLowerCase() === hre.deployConfig.controller.toLowerCase()
const L2OutputOracleProxy = await getContractFromArtifact(
hre,
'L2OutputOracleProxy'
)
const Artifact__SystemConfigProxy = await hre.deployments.get(
'SystemConfigProxy'
)
const portalGuardian = hre.deployConfig.portalGuardian
const portalGuardianCode = await hre.ethers.provider.getCode(portalGuardian)
if (portalGuardianCode === '0x') {
console.log(
`WARNING: setting OptimismPortal.GUARDIAN to ${portalGuardian} and it has no code`
)
if (!isLiveDeployer) {
throw new Error(
'Do not deploy to production networks without the GUARDIAN being a contract'
)
}
}
// Deploy the OptimismPortal implementation as paused to
// ensure that users do not interact with it and instead
// interact with the proxied contract.
// The `portalGuardian` is set at the GUARDIAN.
await deploy({
hre,
name: 'OptimismPortal',
args: [
L2OutputOracleProxy.address,
portalGuardian,
true, // paused
Artifact__SystemConfigProxy.address,
],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'L2_ORACLE',
L2OutputOracleProxy.address
)
await assertContractVariable(
contract,
'GUARDIAN',
hre.deployConfig.portalGuardian
)
await assertContractVariable(
contract,
'SYSTEM_CONFIG',
Artifact__SystemConfigProxy.address
)
},
})
}
deployFn.tags = ['OptimismPortalImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
if (hre.deployConfig.l2BlockTime === 0) {
throw new Error(
'L2OutputOracle deployment: l2BlockTime must be greater than 0'
)
}
if (hre.deployConfig.l2OutputOracleSubmissionInterval === 0) {
throw new Error('L2OutputOracle deployment: submissionInterval cannot be 0')
}
await deploy({
hre,
name: 'L2OutputOracle',
args: [
hre.deployConfig.l2OutputOracleSubmissionInterval,
hre.deployConfig.l2BlockTime,
0,
0,
hre.deployConfig.l2OutputOracleProposer,
hre.deployConfig.l2OutputOracleChallenger,
hre.deployConfig.finalizationPeriodSeconds,
],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'SUBMISSION_INTERVAL',
hre.deployConfig.l2OutputOracleSubmissionInterval
)
await assertContractVariable(
contract,
'L2_BLOCK_TIME',
hre.deployConfig.l2BlockTime
)
await assertContractVariable(
contract,
'PROPOSER',
hre.deployConfig.l2OutputOracleProposer
)
await assertContractVariable(
contract,
'CHALLENGER',
hre.deployConfig.l2OutputOracleChallenger
)
await assertContractVariable(
contract,
'FINALIZATION_PERIOD_SECONDS',
hre.deployConfig.finalizationPeriodSeconds
)
},
})
}
deployFn.tags = ['L2OutputOracleImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { predeploys } from '../src'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
)
await deploy({
hre,
name: 'L1ERC721Bridge',
args: [L1CrossDomainMessengerProxy.address, predeploys.L2ERC721Bridge],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'MESSENGER',
L1CrossDomainMessengerProxy.address
)
},
})
}
deployFn.tags = ['L1ERC721BridgeImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const L1StandardBridgeProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1StandardBridge'
)
await deploy({
hre,
name: 'OptimismMintableERC20Factory',
args: [L1StandardBridgeProxy.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'BRIDGE',
L1StandardBridgeProxy.address
)
},
})
}
deployFn.tags = ['OptimismMintableERC20FactoryImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
// We only want to deploy the dgf on devnet for now
const network = await hre.ethers.provider.getNetwork()
const chainId = network.chainId
if (chainId === 900) {
const disputeGameFactory = await deploy({
hre,
name: 'DisputeGameFactory',
args: [],
})
console.log(`DisputeGameFactory deployed at ${disputeGameFactory.address}`)
} else {
console.log('Skipping deployment of DisputeGameFactory implementation')
}
}
deployFn.tags = ['DisputeGameFactoryImpl', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import { BigNumber } from 'ethers'
import { defaultResourceConfig } from '../src/constants'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const batcherHash = hre.ethers.utils
.hexZeroPad(hre.deployConfig.batchSenderAddress, 32)
.toLowerCase()
const l2GenesisBlockGasLimit = BigNumber.from(
hre.deployConfig.l2GenesisBlockGasLimit
)
const l2GasLimitLowerBound = BigNumber.from(
defaultResourceConfig.systemTxMaxGas +
defaultResourceConfig.maxResourceLimit
)
if (l2GenesisBlockGasLimit.lt(l2GasLimitLowerBound)) {
throw new Error(
`L2 genesis block gas limit must be at least ${l2GasLimitLowerBound}`
)
}
await deploy({
hre,
name: 'SystemConfig',
args: [
hre.deployConfig.finalSystemOwner,
hre.deployConfig.gasPriceOracleOverhead,
hre.deployConfig.gasPriceOracleScalar,
batcherHash,
l2GenesisBlockGasLimit,
hre.deployConfig.p2pSequencerAddress,
defaultResourceConfig,
],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'owner',
hre.deployConfig.finalSystemOwner
)
await assertContractVariable(
contract,
'overhead',
hre.deployConfig.gasPriceOracleOverhead
)
await assertContractVariable(
contract,
'scalar',
hre.deployConfig.gasPriceOracleScalar
)
await assertContractVariable(contract, 'batcherHash', batcherHash)
await assertContractVariable(
contract,
'unsafeBlockSigner',
hre.deployConfig.p2pSequencerAddress
)
const config = await contract.resourceConfig()
assert(config.maxResourceLimit === defaultResourceConfig.maxResourceLimit)
assert(
config.elasticityMultiplier ===
defaultResourceConfig.elasticityMultiplier
)
assert(
config.baseFeeMaxChangeDenominator ===
defaultResourceConfig.baseFeeMaxChangeDenominator
)
assert(
BigNumber.from(config.systemTxMaxGas).eq(
defaultResourceConfig.systemTxMaxGas
)
)
assert(
BigNumber.from(config.minimumBaseFee).eq(
defaultResourceConfig.minimumBaseFee
)
)
assert(
BigNumber.from(config.maximumBaseFee).eq(
defaultResourceConfig.maximumBaseFee
)
)
},
})
}
deployFn.tags = ['SystemConfigImpl', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
let addressManagerOwner = await addressManager.callStatic.owner()
if (addressManagerOwner !== proxyAdmin.address) {
console.log(
`AddressManager(${addressManager.address}).transferOwnership(${proxyAdmin.address})`
)
const tx = await addressManager.transferOwnership(proxyAdmin.address)
await tx.wait()
}
addressManagerOwner = await addressManager.callStatic.owner()
assert(
addressManagerOwner === proxyAdmin.address,
'AddressManager owner not set correctly'
)
}
deployFn.tags = ['AddressManager', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
// We only want to deploy the dgf on devnet for now
const network = await hre.ethers.provider.getNetwork()
const chainId = network.chainId
// The DisputeGameFactory is only deployed on devnet
if (chainId === 900) {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, disputeGameFactoryProxy, disputeGameFactoryImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'DisputeGameFactoryProxy',
iface: 'DisputeGameFactory',
signerOrProvider: deployer,
},
{
name: 'DisputeGameFactory',
},
])
const finalOwner = hre.deployConfig.finalSystemOwner
try {
const tx = await proxyAdmin.upgradeAndCall(
disputeGameFactoryProxy.address,
disputeGameFactoryImpl.address,
disputeGameFactoryProxy.interface.encodeFunctionData('initialize', [
finalOwner,
])
)
await tx.wait()
} catch (e) {
console.log('DisputeGameFactory already initialized')
}
const fetchedOwner = await disputeGameFactoryProxy.callStatic.owner()
assert(fetchedOwner === finalOwner)
console.log('Updgraded and initialized DisputeGameFactory')
} else {
console.log('Skipping initialization of DisputeGameFactory')
}
}
deployFn.tags = ['DisputeGameFactoryInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { BigNumber } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { defaultResourceConfig } from '../src/constants'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, systemConfigProxy, systemConfigImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
{
name: 'SystemConfig',
},
])
const batcherHash = hre.ethers.utils
.hexZeroPad(hre.deployConfig.batchSenderAddress, 32)
.toLowerCase()
const l2GenesisBlockGasLimit = BigNumber.from(
hre.deployConfig.l2GenesisBlockGasLimit
)
const l2GasLimitLowerBound = BigNumber.from(
defaultResourceConfig.systemTxMaxGas +
defaultResourceConfig.maxResourceLimit
)
if (l2GenesisBlockGasLimit.lt(l2GasLimitLowerBound)) {
throw new Error(
`L2 genesis block gas limit must be at least ${l2GasLimitLowerBound}`
)
}
try {
const tx = await proxyAdmin.upgradeAndCall(
systemConfigProxy.address,
systemConfigImpl.address,
systemConfigImpl.interface.encodeFunctionData('initialize', [
hre.deployConfig.finalSystemOwner,
hre.deployConfig.gasPriceOracleOverhead,
hre.deployConfig.gasPriceOracleScalar,
batcherHash,
l2GenesisBlockGasLimit,
hre.deployConfig.p2pSequencerAddress,
defaultResourceConfig,
])
)
await tx.wait()
} catch (e) {
console.log('SystemConfig already initialized')
console.log(e)
}
const version = await systemConfigProxy.callStatic.version()
console.log(`SystemConfig version: ${version}`)
console.log('Upgraded SystemConfig')
}
deployFn.tags = ['SystemConfigInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1StandardBridgeProxy, l1StandardBridgeImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
iface: 'L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L1StandardBridge',
},
])
const proxyType = await proxyAdmin.callStatic.proxyType(
l1StandardBridgeProxy.address
)
if (proxyType !== 1) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setProxyType(${l1StandardBridgeProxy.address}, 1)`
)
// Set the L1StandardBridge to the UPGRADEABLE proxy type.
const tx = await proxyAdmin.setProxyType(l1StandardBridgeProxy.address, 1)
await tx.wait()
}
try {
const tx = await proxyAdmin.upgrade(
l1StandardBridgeProxy.address,
l1StandardBridgeImpl.address
)
await tx.wait()
} catch (e) {
console.log('L1StandardBridge already initialized')
}
const version = await l1StandardBridgeProxy.callStatic.version()
console.log(`L1StandardBridge version: ${version}`)
console.log('Upgraded L1StandardBridge')
}
deployFn.tags = ['L1StandardBridgeInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1ERC721BridgeProxy, l1ERC721BridgeImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
iface: 'L1ERC721Bridge',
signerOrProvider: deployer,
},
{
name: 'L1ERC721Bridge',
},
])
try {
const tx = await proxyAdmin.upgrade(
l1ERC721BridgeProxy.address,
l1ERC721BridgeImpl.address
)
await tx.wait()
} catch (e) {
console.log('L1ERC721Bridge already initialized')
}
const version = await l1ERC721BridgeProxy.callStatic.version()
console.log(`L1ERC721Bridge version: ${version}`)
console.log('Upgraded L1ERC721Bridge')
}
deployFn.tags = ['L1ERC721BridgeInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, mintableERC20FactoryProxy, mintableERC20FactoryImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20FactoryProxy',
iface: 'OptimismMintableERC20Factory',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20Factory',
},
])
try {
const tx = await proxyAdmin.upgrade(
mintableERC20FactoryProxy.address,
mintableERC20FactoryImpl.address
)
await tx.wait()
} catch (e) {
console.log('OptimismMintableERC20Factory already initialized')
}
const version = await mintableERC20FactoryProxy.callStatic.version()
console.log(`OptimismMintableERC20Factory version: ${version}`)
console.log('Upgraded OptimismMintableERC20Factory')
}
deployFn.tags = ['OptimismMintableERC20FactoryInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1CrossDomainMessengerProxy, l1CrossDomainMessengerImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1CrossDomainMessenger',
iface: 'L1CrossDomainMessenger',
signerOrProvider: deployer,
},
{
name: 'L1CrossDomainMessenger',
},
])
const proxyType = await proxyAdmin.callStatic.proxyType(
l1CrossDomainMessengerProxy.address
)
if (proxyType !== 2) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setProxyType(${l1CrossDomainMessengerProxy.address}, 2)`
)
// Set the L1CrossDomainMessenger to the RESOLVED proxy type.
const tx = await proxyAdmin.setProxyType(
l1CrossDomainMessengerProxy.address,
2
)
await tx.wait()
}
const name = 'OVM_L1CrossDomainMessenger'
const implementationName = proxyAdmin.implementationName(
l1CrossDomainMessengerImpl.address
)
if (implementationName !== name) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setImplementationName(${l1CrossDomainMessengerImpl.address}, 'OVM_L1CrossDomainMessenger')`
)
const tx = await proxyAdmin.setImplementationName(
l1CrossDomainMessengerProxy.address,
name
)
await tx.wait()
}
try {
const tx = await proxyAdmin.upgradeAndCall(
l1CrossDomainMessengerProxy.address,
l1CrossDomainMessengerImpl.address,
l1CrossDomainMessengerImpl.interface.encodeFunctionData('initialize')
)
await tx.wait()
} catch (e) {
console.log('L1CrossDomainMessenger already initialized')
}
const version = await l1CrossDomainMessengerProxy.callStatic.version()
console.log(`L1CrossDomainMessenger version: ${version}`)
console.log('Upgraded L1CrossDomainMessenger')
}
deployFn.tags = ['L1CrossDomainMessengerInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l2OutputOracleProxy, l2OutputOracleImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracleProxy',
iface: 'L2OutputOracle',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracle',
},
])
const startingBlockNumber = hre.deployConfig.l2OutputOracleStartingBlockNumber
let startingTimestamp = hre.deployConfig.l2OutputOracleStartingTimestamp
if (startingTimestamp < 0) {
const l1StartingBlock = await hre.ethers.provider.getBlock(
hre.deployConfig.l1StartingBlockTag
)
if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${hre.deployConfig.l1StartingBlockTag}`
)
}
startingTimestamp = l1StartingBlock.timestamp
}
try {
const tx = await proxyAdmin.upgradeAndCall(
l2OutputOracleProxy.address,
l2OutputOracleImpl.address,
l2OutputOracleProxy.interface.encodeFunctionData('initialize', [
startingBlockNumber,
startingTimestamp,
])
)
await tx.wait()
} catch (e) {
console.log('L2OutputOracle already initialized')
}
const fetchedStartingBlockNumber =
await l2OutputOracleProxy.callStatic.startingBlockNumber()
const fetchedStartingTimestamp =
await l2OutputOracleProxy.callStatic.startingTimestamp()
assert(fetchedStartingBlockNumber.toNumber() === startingBlockNumber)
assert(fetchedStartingTimestamp.toNumber() === startingTimestamp)
console.log('Updgraded and initialized L2OutputOracle')
const version = await l2OutputOracleProxy.callStatic.version()
console.log(`L2OutputOracle version: ${version}`)
}
deployFn.tags = ['L2OutputOracleInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, optimismPortalProxy, optimismPortalImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'OptimismPortalProxy',
iface: 'OptimismPortal',
signerOrProvider: deployer,
},
{
name: 'OptimismPortal',
},
])
// Initialize the portal, setting paused to false
try {
const tx = await proxyAdmin.upgradeAndCall(
optimismPortalProxy.address,
optimismPortalImpl.address,
optimismPortalProxy.interface.encodeFunctionData('initialize', [false])
)
await tx.wait()
} catch (e) {
console.log('OptimismPortal already initialized')
}
const isPaused = await optimismPortalProxy.callStatic.paused()
assert(isPaused === false)
console.log('Upgraded and initialized OptimismPortal')
const version = await optimismPortalProxy.callStatic.version()
console.log(`OptimismPortal version: ${version}`)
}
deployFn.tags = ['OptimismPortalInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin] = await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
])
const finalOwner = hre.deployConfig.finalSystemOwner
const proxyAdminOwner = await proxyAdmin.callStatic.owner()
if (proxyAdminOwner !== finalOwner) {
const tx = await proxyAdmin.transferOwnership(finalOwner)
await tx.wait()
await awaitCondition(
async () => {
return (await proxyAdmin.callStatic.owner()) === finalOwner
},
30000,
1000
)
}
}
deployFn.tags = ['ProxyAdmin', 'transferOwnership', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'L1Block',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'DEPOSITOR_ACCOUNT',
ethers.utils.getAddress('0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001')
)
},
})
}
deployFn.tags = ['L1BlockImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const Artifact__L1CrossDomainMessenger = await hre.companionNetworks[
'l1'
].deployments.get('L1CrossDomainMessengerProxy')
await deploy({
hre,
name: 'L2CrossDomainMessenger',
args: [Artifact__L1CrossDomainMessenger.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'OTHER_MESSENGER',
ethers.utils.getAddress(Artifact__L1CrossDomainMessenger.address)
)
},
})
}
deployFn.tags = ['L2CrossDomainMessengerImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const Artifact__L1StandardBridge = await hre.companionNetworks[
'l1'
].deployments.get('L1StandardBridgeProxy')
await deploy({
hre,
name: 'L2StandardBridge',
args: [Artifact__L1StandardBridge.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'OTHER_BRIDGE',
ethers.utils.getAddress(Artifact__L1StandardBridge.address)
)
},
})
}
deployFn.tags = ['L2StandardBridgeImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'L2ToL1MessagePasser',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'MESSAGE_VERSION', 1)
},
})
}
deployFn.tags = ['L2ToL1MessagePasserImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { predeploys } from '../src/constants'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const Artifact__L1ERC721Bridge = await hre.companionNetworks[
'l1'
].deployments.get('L1ERC721BridgeProxy')
await deploy({
hre,
name: 'L2ERC721Bridge',
args: [predeploys.L2CrossDomainMessenger, Artifact__L1ERC721Bridge.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'MESSENGER',
ethers.utils.getAddress(predeploys.L2CrossDomainMessenger)
)
await assertContractVariable(
contract,
'OTHER_BRIDGE',
ethers.utils.getAddress(Artifact__L1ERC721Bridge.address)
)
},
})
}
deployFn.tags = ['L2ERC721BridgeImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'GasPriceOracle',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'DECIMALS', 6)
},
})
}
deployFn.tags = ['GasPriceOracle', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const l1 = hre.network.companionNetworks['l1']
const deployConfig = hre.getDeployConfig(l1)
const sequencerFeeVaultRecipient = deployConfig.sequencerFeeVaultRecipient
if (sequencerFeeVaultRecipient === ethers.constants.AddressZero) {
throw new Error(`SequencerFeeVault RECIPIENT undefined`)
}
const sequencerFeeVaultMinimumWithdrawalAmount =
deployConfig.sequencerFeeVaultMinimumWithdrawalAmount
const sequencerFeeVaultWithdrawalNetwork =
deployConfig.sequencerFeeVaultWithdrawalNetwork
if (sequencerFeeVaultWithdrawalNetwork >= 2) {
throw new Error('SequencerFeeVault WITHDRAWAL_NETWORK must be 0 or 1')
}
await deploy({
hre,
name: 'SequencerFeeVault',
args: [
sequencerFeeVaultRecipient,
sequencerFeeVaultMinimumWithdrawalAmount,
sequencerFeeVaultWithdrawalNetwork,
],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'RECIPIENT',
ethers.utils.getAddress(sequencerFeeVaultRecipient)
)
await assertContractVariable(
contract,
'MIN_WITHDRAWAL_AMOUNT',
sequencerFeeVaultMinimumWithdrawalAmount
)
await assertContractVariable(
contract,
'WITHDRAWAL_NETWORK',
sequencerFeeVaultWithdrawalNetwork
)
},
})
}
deployFn.tags = ['SequencerFeeVaultImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const l1 = hre.network.companionNetworks['l1']
const deployConfig = hre.getDeployConfig(l1)
const baseFeeVaultRecipient = deployConfig.baseFeeVaultRecipient
if (baseFeeVaultRecipient === ethers.constants.AddressZero) {
throw new Error('BaseFeeVault RECIPIENT undefined')
}
const baseFeeVaultMinimumWithdrawalAmount =
deployConfig.baseFeeVaultMinimumWithdrawalAmount
const baseFeeVaultWithdrawalNetwork =
deployConfig.baseFeeVaultWithdrawalNetwork
if (baseFeeVaultWithdrawalNetwork >= 2) {
throw new Error('BaseFeeVault WITHDRAWAL_NETWORK must be 0 or 1')
}
await deploy({
hre,
name: 'BaseFeeVault',
args: [
baseFeeVaultRecipient,
baseFeeVaultMinimumWithdrawalAmount,
baseFeeVaultWithdrawalNetwork,
],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'RECIPIENT',
ethers.utils.getAddress(baseFeeVaultRecipient)
)
await assertContractVariable(
contract,
'MIN_WITHDRAWAL_AMOUNT',
baseFeeVaultMinimumWithdrawalAmount
)
await assertContractVariable(
contract,
'WITHDRAWAL_NETWORK',
baseFeeVaultWithdrawalNetwork
)
},
})
}
deployFn.tags = ['BaseFeeVaultImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { ethers } from 'ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const l1 = hre.network.companionNetworks['l1']
const deployConfig = hre.getDeployConfig(l1)
const l1FeeVaultRecipient = deployConfig.l1FeeVaultRecipient
if (l1FeeVaultRecipient === ethers.constants.AddressZero) {
throw new Error('L1FeeVault RECIPIENT undefined')
}
const l1FeeVaultMinimumWithdrawalAmount =
deployConfig.l1FeeVaultMinimumWithdrawalAmount
const l1FeeVaultWithdrawalNetwork = deployConfig.l1FeeVaultWithdrawalNetwork
if (l1FeeVaultWithdrawalNetwork >= 2) {
throw new Error('L1FeeVault WITHDRAWAL_NETWORK must be 0 or 1')
}
await deploy({
hre,
name: 'L1FeeVault',
args: [
l1FeeVaultRecipient,
l1FeeVaultMinimumWithdrawalAmount,
l1FeeVaultWithdrawalNetwork,
],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'RECIPIENT',
ethers.utils.getAddress(l1FeeVaultRecipient)
)
await assertContractVariable(
contract,
'MIN_WITHDRAWAL_AMOUNT',
l1FeeVaultMinimumWithdrawalAmount
)
await assertContractVariable(
contract,
'WITHDRAWAL_NETWORK',
l1FeeVaultWithdrawalNetwork
)
},
})
}
deployFn.tags = ['L1FeeVaultImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
import { predeploys } from '../src/constants'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'OptimismMintableERC20Factory',
args: [predeploys.L2StandardBridge],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'BRIDGE',
ethers.utils.getAddress(predeploys.L2StandardBridge)
)
},
})
}
deployFn.tags = ['OptimismMintableERC20FactoryImpl', 'l2']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import { assertContractVariable, deploy } from '../src/deploy-utils'
import { predeploys } from '../src/constants'
const deployFn: DeployFunction = async (hre) => {
const OptimismMintableERC721Factory = await hre.ethers.getContractAt(
'OptimismMintableERC721Factory',
predeploys.OptimismMintableERC721Factory
)
const remoteChainId = await OptimismMintableERC721Factory.REMOTE_CHAIN_ID()
await deploy({
hre,
name: 'OptimismMintableERC721Factory',
args: [predeploys.L2StandardBridge, remoteChainId],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'BRIDGE',
ethers.utils.getAddress(predeploys.L2StandardBridge)
)
await assertContractVariable(contract, 'REMOTE_CHAIN_ID', remoteChainId)
},
})
}
deployFn.tags = ['OptimismMintableERC721FactoryImpl', 'l2']
export default deployFn
import assert from 'assert'
import { URLSearchParams } from 'url'
import { ethers, Contract } from 'ethers'
import { Provider } from '@ethersproject/abstract-provider'
import { Signer } from '@ethersproject/abstract-signer'
import { sleep } from '@eth-optimism/core-utils'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { Deployment, DeployResult } from 'hardhat-deploy/dist/types'
import 'hardhat-deploy'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
/**
* Wrapper around hardhat-deploy with some extra features.
*
* @param opts Options for the deployment.
* @param opts.hre HardhatRuntimeEnvironment.
* @param opts.contract Name of the contract to deploy.
* @param opts.name Name to use for the deployment file.
* @param opts.iface Interface to use for the returned contract.
* @param opts.args Arguments to pass to the contract constructor.
* @param opts.postDeployAction Action to perform after the contract is deployed.
* @returns Deployed contract object.
*/
export const deploy = async ({
hre,
name,
iface,
args,
contract,
postDeployAction,
}: {
hre: HardhatRuntimeEnvironment
name: string
args: any[]
contract?: string
iface?: string
postDeployAction?: (contract: Contract) => Promise<void>
}): Promise<Contract> => {
const { deployer } = await hre.getNamedAccounts()
// Hardhat deploy will usually do this check for us, but currently doesn't also consider
// external deployments when doing this check. By doing the check ourselves, we also get to
// consider external deployments. If we already have the deployment, return early.
let result: Deployment | DeployResult = await hre.deployments.getOrNull(name)
// Wrap in a try/catch in case there is not a deployConfig for the current network.
let numDeployConfirmations: number
try {
numDeployConfirmations = hre.deployConfig.numDeployConfirmations
} catch (e) {
numDeployConfirmations = 1
}
if (result) {
console.log(`skipping ${name}, using existing at ${result.address}`)
} else {
result = await hre.deployments.deploy(name, {
contract,
from: deployer,
args,
log: true,
waitConfirmations: numDeployConfirmations,
})
console.log(`Deployed ${name} at ${result.address}`)
// Only wait for the transaction if it was recently deployed in case the
// result was deployed a long time ago and was pruned from the backend.
await hre.ethers.provider.waitForTransaction(result.transactionHash)
}
// Check to make sure there is code
const code = await hre.ethers.provider.getCode(result.address)
if (code === '0x') {
throw new Error(`no code for ${result.address}`)
}
// Create the contract object to return.
const created = asAdvancedContract({
confirmations: numDeployConfirmations,
contract: new Contract(
result.address,
iface !== undefined
? (await hre.ethers.getContractFactory(iface)).interface
: result.abi,
hre.ethers.provider.getSigner(deployer)
),
})
// Run post-deploy actions if necessary.
if ((result as DeployResult).newlyDeployed) {
if (postDeployAction) {
await postDeployAction(created)
}
}
return created
}
/**
* Returns a version of the contract object which modifies all of the input contract's methods to
* automatically await transaction receipts and confirmations. Will also throw if we timeout while
* waiting for a transaction to be included in a block.
*
* @param opts Options for the contract.
* @param opts.hre HardhatRuntimeEnvironment.
* @param opts.contract Contract to wrap.
* @returns Wrapped contract object.
*/
export const asAdvancedContract = (opts: {
contract: Contract
confirmations?: number
gasPrice?: number
}): Contract => {
// Temporarily override Object.defineProperty to bypass ether's object protection.
const def = Object.defineProperty
Object.defineProperty = (obj, propName, prop) => {
prop.writable = true
return def(obj, propName, prop)
}
const contract = new Contract(
opts.contract.address,
opts.contract.interface,
opts.contract.signer || opts.contract.provider
)
// Now reset Object.defineProperty
Object.defineProperty = def
for (const fnName of Object.keys(contract.functions)) {
const fn = contract[fnName].bind(contract)
;(contract as any)[fnName] = async (...args: any) => {
// We want to use the configured gas price but we need to set the gas price to zero if we're
// triggering a static function.
let gasPrice = opts.gasPrice
if (contract.interface.getFunction(fnName).constant) {
gasPrice = 0
}
// Now actually trigger the transaction (or call).
const tx = await fn(...args, {
gasPrice,
})
// Meant for static calls, we don't need to wait for anything, we get the result right away.
if (typeof tx !== 'object' || typeof tx.wait !== 'function') {
return tx
}
// Wait for the transaction to be included in a block and wait for the specified number of
// deployment confirmations.
const maxTimeout = 120
let timeout = 0
while (true) {
await sleep(1000)
const receipt = await contract.provider.getTransactionReceipt(tx.hash)
if (receipt === null) {
timeout++
if (timeout > maxTimeout) {
throw new Error('timeout exceeded waiting for txn to be mined')
}
} else if (receipt.confirmations >= (opts.confirmations || 0)) {
return tx
}
}
}
}
return contract
}
/**
* Creates a contract object from a deployed artifact.
*
* @param hre HardhatRuntimeEnvironment.
* @param name Name of the deployed contract to get an object for.
* @param opts Options for the contract.
* @param opts.iface Optional interface to use for the contract object.
* @param opts.signerOrProvider Optional signer or provider to use for the contract object.
* @returns Contract object.
*/
export const getContractFromArtifact = async (
hre: HardhatRuntimeEnvironment,
name: string,
opts: {
iface?: string
signerOrProvider?: Signer | Provider | string
} = {}
): Promise<ethers.Contract> => {
const artifact = await hre.deployments.get(name)
// Get the deployed contract's interface.
let iface = new hre.ethers.utils.Interface(artifact.abi)
// Override with optional iface name if requested.
if (opts.iface) {
const factory = await hre.ethers.getContractFactory(opts.iface)
iface = factory.interface
}
let signerOrProvider: Signer | Provider = hre.ethers.provider
if (opts.signerOrProvider) {
if (typeof opts.signerOrProvider === 'string') {
signerOrProvider = hre.ethers.provider.getSigner(opts.signerOrProvider)
} else {
signerOrProvider = opts.signerOrProvider
}
}
let numDeployConfirmations: number
try {
numDeployConfirmations = hre.deployConfig.numDeployConfirmations
} catch (e) {
numDeployConfirmations = 1
}
return asAdvancedContract({
confirmations: numDeployConfirmations,
contract: new hre.ethers.Contract(
artifact.address,
iface,
signerOrProvider
),
})
}
/**
* Gets multiple contract objects from their respective deployed artifacts.
*
* @param hre HardhatRuntimeEnvironment.
* @param configs Array of contract names and options.
* @returns Array of contract objects.
*/
export const getContractsFromArtifacts = async (
hre: HardhatRuntimeEnvironment,
configs: Array<{
name: string
iface?: string
signerOrProvider?: Signer | Provider | string
}>
): Promise<ethers.Contract[]> => {
const contracts = []
for (const config of configs) {
contracts.push(await getContractFromArtifact(hre, config.name, config))
}
return contracts
}
/**
* Helper function for asserting that a contract variable is set to the expected value.
*
* @param contract Contract object to query.
* @param variable Name of the variable to query.
* @param expected Expected value of the variable.
*/
export const assertContractVariable = async (
contract: ethers.Contract,
variable: string,
expected: any
) => {
// Need to make a copy that doesn't have a signer or we get the error that contracts with
// signers cannot override the from address.
const temp = new ethers.Contract(
contract.address,
contract.interface,
contract.provider
)
const actual = await temp.callStatic[variable]({
from: ethers.constants.AddressZero,
})
if (ethers.utils.isAddress(expected)) {
assert(
actual.toLowerCase() === expected.toLowerCase(),
`[FATAL] ${variable} is ${actual} but should be ${expected}`
)
return
}
assert(
actual === expected || (actual.eq && actual.eq(expected)),
`[FATAL] ${variable} is ${actual} but should be ${expected}`
)
}
/**
* Returns the address for a given deployed contract by name.
*
* @param hre HardhatRuntimeEnvironment.
* @param name Name of the deployed contract.
* @returns Address of the deployed contract.
*/
export const getDeploymentAddress = async (
hre: HardhatRuntimeEnvironment,
name: string
): Promise<string> => {
const deployment = await hre.deployments.get(name)
return deployment.address
}
/**
* JSON-ifies an ethers transaction object.
*
* @param tx Ethers transaction object.
* @returns JSON-ified transaction object.
*/
export const printJsonTransaction = (tx: ethers.PopulatedTransaction): void => {
console.log(
'JSON transaction parameters:\n' +
JSON.stringify(
{
from: tx.from,
to: tx.to,
data: tx.data,
value: tx.value,
chainId: tx.chainId,
},
null,
2
)
)
}
/**
* Helper for transferring a Proxy to a target contract.
*
* @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live.
* @param opts.proxy proxy contract.
* @param opts.target target contract.
*/
export const doOwnershipTransfer = async (opts: {
isLiveDeployer?: boolean
proxy: ethers.Contract
name: string
transferFunc: string
target: ethers.Contract
}): Promise<void> => {
if (opts.isLiveDeployer) {
console.log(`Setting ${opts.name} owner to target ${opts.target.address}`)
await opts.proxy[opts.transferFunc](opts.target.address)
} else {
const tx = await opts.proxy.populateTransaction[opts.transferFunc](
opts.target.address
)
console.log(`
Please transfer ${opts.name} (proxy) owner to MSD
- ${opts.name} address: ${opts.proxy.address}
- target address: ${opts.target.address}
`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(opts.target.provider, tx)
}
}
/**
* Check if the script should submit the transaction or wait for the deployer to do it manually.
*
* @param hre HardhatRuntimeEnvironment.
* @param ovveride Allow manually disabling live transaction submission. Useful for testing.
* @returns True if the current step is the target step.
*/
export const liveDeployer = async (opts: {
hre: HardhatRuntimeEnvironment
disabled: string | undefined
}): Promise<boolean> => {
if (!!opts.disabled) {
console.log('Live deployer manually disabled')
return false
}
const { deployer } = await opts.hre.getNamedAccounts()
const ret =
deployer.toLowerCase() === opts.hre.deployConfig.controller.toLowerCase()
console.log('Setting live deployer to', ret)
return ret
}
/**
* Prints a direct link to a Tenderly simulation.
*
* @param provider Ethers Provider.
* @param tx Ethers transaction object.
*/
export const printTenderlySimulationLink = async (
provider: ethers.providers.Provider,
tx: ethers.PopulatedTransaction
): Promise<void> => {
if (process.env.TENDERLY_PROJECT && process.env.TENDERLY_USERNAME) {
console.log(
`https://dashboard.tenderly.co/${process.env.TENDERLY_PROJECT}/${
process.env.TENDERLY_USERNAME
}/simulator/new?${new URLSearchParams({
network: (await provider.getNetwork()).chainId.toString(),
contractAddress: tx.to,
rawFunctionInput: tx.data,
from: tx.from,
}).toString()}`
)
}
}
/**
* Prints a cast commmand for submitting a given transaction.
*
* @param tx Ethers transaction object.
*/
export const printCastCommand = (tx: ethers.PopulatedTransaction): void => {
if (process.env.CAST_COMMANDS) {
if (!!tx.value && tx.value.gt(0)) {
console.log(
`cast send ${tx.to} ${tx.data} --from ${tx.from} --value ${tx.value}`
)
} else {
console.log(`cast send ${tx.to} ${tx.data} --from ${tx.from} `)
}
}
}
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