Commit cfec0345 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

ctp: fixup deploy scripts for the nft bridge (#3570)

* ctp: fixup deploy scripts for the nft bridge

The most important one is the deploy script for the
`Proxy` that will end up at the predeploy on L2. There
are specific checks for the correct deployer account
being used. Will likely require some changes to the
hardhat config when doing the actual deployment.

* ctp: update deploy scripts

* ctp: update deploy scripts

* ctp: move away from deterministic deployments

* op: deployments

* fixes

* deploy-script: refactor

* deploy: comments
parent 3b98cc61
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'hardhat' import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import fetch from 'node-fetch'
import {
isTargetL1Network,
predeploy,
getProxyAdmin,
validateERC721Bridge,
} from '../../src/nft-bridge-deploy-helpers'
// Handle the `ops` deployment
const getL1CrossDomainMessengerProxyDeployment = async (
hre: HardhatRuntimeEnvironment
) => {
const network = hre.network.name
if (network === 'ops-l1') {
const res = await fetch(
'http://localhost:8080/deployments/local/Proxy__OVM_L1CrossDomainMessenger.json'
)
return res.json()
} else {
return hre.deployments.get('Proxy__OVM_L1CrossDomainMessenger')
}
}
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deploy } = hre.deployments
const { getAddress } = hre.ethers.utils
if (!isTargetL1Network(hre.network.name)) {
console.log(`Deploying to unsupported network ${hre.network.name}`)
return
}
console.log(`Deploying L1ERC721Bridge to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
const Deployment__L1ERC721BridgeProxy = await hre.deployments.get(
'L1ERC721BridgeProxy'
)
const L1ERC721BridgeProxy = await hre.ethers.getContractAt(
'Proxy',
Deployment__L1ERC721BridgeProxy.address
)
const admin = await L1ERC721BridgeProxy.callStatic.admin()
if (getAddress(admin) !== getAddress(deployer)) {
throw new Error('deployer is not proxy admin')
}
// Get the address of the currently deployed L1CrossDomainMessenger.
// This should be the address of the proxy
const Deployment__L1CrossDomainMessengerProxy =
await getL1CrossDomainMessengerProxyDeployment(hre)
await hre.deployments.deploy('L1ERC721Bridge', { const L1CrossDomainMessengerProxyAddress =
Deployment__L1CrossDomainMessengerProxy.address
// Deploy the L1ERC721Bridge. The arguments are
// - messenger
// - otherBridge
// Since this is the L1ERC721Bridge, the otherBridge is the
// predeploy address
await deploy('L1ERC721Bridge', {
from: deployer, from: deployer,
args: [ethers.constants.AddressZero, ethers.constants.AddressZero], args: [L1CrossDomainMessengerProxyAddress, predeploy],
log: true, log: true,
waitConfirmations: 1,
})
const Deployment__L1ERC721Bridge = await hre.deployments.get('L1ERC721Bridge')
console.log(
`L1ERC721Bridge deployed to ${Deployment__L1ERC721Bridge.address}`
)
await validateERC721Bridge(hre, Deployment__L1ERC721Bridge.address, {
messenger: L1CrossDomainMessengerProxyAddress,
otherBridge: predeploy,
})
{
// Upgrade the Proxy to the newly deployed implementation
const tx = await L1ERC721BridgeProxy.upgradeTo(
Deployment__L1ERC721Bridge.address
)
const receipt = await tx.wait()
console.log(`L1ERC721BridgeProxy upgraded: ${receipt.transactionHash}`)
}
{
// Set the admin correctly
const newAdmin = getProxyAdmin(hre.network.name)
const tx = await L1ERC721BridgeProxy.changeAdmin(newAdmin)
const receipt = await tx.wait()
console.log(`L1ERC721BridgeProxy admin updated: ${receipt.transactionHash}`)
}
await validateERC721Bridge(hre, L1ERC721BridgeProxy.address, {
messenger: L1CrossDomainMessengerProxyAddress,
otherBridge: predeploy,
}) })
} }
......
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { isTargetL1Network } from '../../src/nft-bridge-deploy-helpers'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deploy } = hre.deployments
const { deploy } = await hre.deployments.deterministic( if (!isTargetL1Network(hre.network.name)) {
'L1ERC721BridgeProxy', console.log(`Deploying to unsupported network ${hre.network.name}`)
{ return
contract: 'Proxy', }
salt: hre.ethers.utils.solidityKeccak256(
['string'], console.log(`Deploying L1ERC721BridgeProxy to ${hre.network.name}`)
['L1ERC721BridgeProxy'] console.log(`Using deployer ${deployer}`)
),
from: deployer,
args: [hre.deployConfig.ddd],
log: true,
}
)
await deploy() await deploy('L1ERC721BridgeProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: 1,
})
const Deployment__L1ERC721BridgeProxy = await hre.deployments.get(
'L1ERC721BridgeProxy'
)
console.log(
`L1ERC721BridgeProxy deployed to ${Deployment__L1ERC721BridgeProxy.address}`
)
} }
deployFn.tags = ['L1ERC721BridgeProxy'] deployFn.tags = ['L1ERC721BridgeProxy']
......
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'hardhat' import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { predeploys } from '@eth-optimism/contracts'
import {
isTargetL2Network,
predeploy,
validateERC721Bridge,
getProxyAdmin,
} from '../../src/nft-bridge-deploy-helpers'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { getAddress } = hre.ethers.utils
if (!isTargetL2Network(hre.network.name)) {
console.log(`Deploying to unsupported network ${hre.network.name}`)
return
}
console.log(`Deploying L2ERC721Bridge to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
const L2ERC721BridgeProxy = await hre.ethers.getContractAt('Proxy', predeploy)
// Check to make sure that the admin of the proxy is the deployer.
// The deployer of the L2ERC721Bridge should be the same as the
// admin of the L2ERC721BridgeProxy so that it is easy to upgrade
// the implementation. The admin is then changed depending on the
// network after the L2ERC721BridgeProxy is upgraded to the implementation
const admin = await L2ERC721BridgeProxy.callStatic.admin()
if (getAddress(admin) !== getAddress(deployer)) {
throw new Error(`Unexpected admin ${admin}`)
}
const Deployment__L1ERC721Bridge = await hre.deployments.get(
'L1ERC721BridgeProxy'
)
const L1ERC721BridgeAddress = Deployment__L1ERC721Bridge.address
// Deploy the L2ERC721Bridge implementation
await hre.deployments.deploy('L2ERC721Bridge', { await hre.deployments.deploy('L2ERC721Bridge', {
from: deployer, from: deployer,
args: [ethers.constants.AddressZero, ethers.constants.AddressZero], args: [predeploys.L2CrossDomainMessenger, L1ERC721BridgeAddress],
log: true, log: true,
waitConfirmations: 1,
}) })
const Deployment__L2ERC721Bridge = await hre.deployments.get('L2ERC721Bridge')
console.log(
`L2ERC721Bridge deployed to ${Deployment__L2ERC721Bridge.address}`
)
await validateERC721Bridge(hre, Deployment__L2ERC721Bridge.address, {
messenger: predeploys.L2CrossDomainMessenger,
otherBridge: L1ERC721BridgeAddress,
})
{
// Upgrade the implementation of the proxy to the newly deployed
// L2ERC721Bridge
const tx = await L2ERC721BridgeProxy.upgradeTo(
Deployment__L2ERC721Bridge.address
)
const receipt = await tx.wait()
console.log(
`Upgraded the implementation of the L2ERC721BridgeProxy: ${receipt.transactionhash}`
)
}
await validateERC721Bridge(hre, L2ERC721BridgeProxy.address, {
messenger: predeploys.L2CrossDomainMessenger,
otherBridge: L1ERC721BridgeAddress,
})
{
const newAdmin = getProxyAdmin(hre.network.name)
console.log(`Changing admin to ${newAdmin}`)
const tx = await L2ERC721BridgeProxy.changeAdmin(newAdmin)
const receipt = await tx.wait()
console.log(
`Changed admin of the L2ERC721BridgeProxy: ${receipt.transactionHash}`
)
}
} }
deployFn.tags = ['L2ERC721BridgeImplementation'] deployFn.tags = ['L2ERC721BridgeImplementation']
deployFn.dependencies = ['L2ERC721BridgeProxy'] deployFn.dependencies = ['L2ERC721BridgeProxy', 'L1ERC721BridgeProxy']
export default deployFn export default deployFn
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
const predeploy = '0x4200000000000000000000000000000000000014'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { getAddress } = hre.ethers.utils
const { deploy } = await hre.deployments.deterministic( console.log(`Deploying L2ERC721BridgeProxy to ${hre.network.name}`)
'L2ERC721BridgeProxy', console.log(`Using deployer ${deployer}`)
{
contract: 'Proxy', // Check to make sure that the Proxy has not been deployed yet
salt: hre.ethers.utils.solidityKeccak256( const pre = await hre.ethers.provider.getCode(predeploy, 'latest')
['string'], if (pre !== '0x') {
['L2ERC721BridgeProxy'] console.log(`Code already deployed to ${predeploy}`)
), return
from: deployer, }
args: [hre.deployConfig.ddd],
log: true, // A special deployer account must be used
} const mainnetDeployer = getAddress(
'0x53A6eecC2dD4795Fcc68940ddc6B4d53Bd88Bd9E'
)
const goerliDeployer = getAddress(
'0x5c679a57e018f5f146838138d3e032ef4913d551'
) )
const localDeployer = getAddress('0xdfc82d475833a50de90c642770f34a9db7deb725')
// Deploy the L2ERC721BridgeProxy as a predeploy address
if (hre.network.name === 'optimism') {
if (getAddress(deployer) !== mainnetDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else if (hre.network.name === 'optimism-goerli') {
if (getAddress(deployer) !== goerliDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else if (hre.network.name === 'ops-l2') {
if (getAddress(deployer) !== localDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else {
throw new Error(`Unknown network: ${hre.network.name}`)
}
// Set the deployer as the admin of the Proxy. This is
// temporary, the admin will be updated when deploying
// the implementation
await hre.deployments.deploy('L2ERC721BridgeProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: 1,
})
await deploy() // Check that the Proxy was deployed to the correct address
const code = await hre.ethers.provider.getCode(predeploy)
if (code === '0x') {
throw new Error('Code is not set at expected predeploy address')
}
console.log(`L2ERC721BridgeProxy deployed to ${predeploy}`)
} }
deployFn.tags = ['L2ERC721BridgeProxy'] deployFn.tags = ['L2ERC721BridgeProxy']
......
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { ethers } from 'hardhat' import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { getProxyAdmin, predeploy } from '../../src/nft-bridge-deploy-helpers'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { getAddress } = hre.ethers.utils
console.log(`Deploying OptimismMintableERC721Factory to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
const Deployment__OptimismMintableERC721FactoryProxy =
await hre.deployments.get('OptimismMintableERC721FactoryProxy')
const OptimismMintableERC721FactoryProxy = await hre.ethers.getContractAt(
'Proxy',
Deployment__OptimismMintableERC721FactoryProxy.address
)
// Check that the admin of the OptimismMintableERC721FactoryProxy is the
// deployer. This makes it easy to upgrade the implementation of the proxy
// and then transfer the admin privilege after deploying the implementation
const admin = await OptimismMintableERC721FactoryProxy.callStatic.admin()
if (getAddress(admin) !== getAddress(deployer)) {
throw new Error('deployer is not proxy admin')
}
let remoteChainId: number
if (hre.network.name === 'optimism') {
remoteChainId = 1
} else if (hre.network.name === 'optimism-goerli') {
remoteChainId = 5
} else if (hre.network.name === 'ops-l2') {
remoteChainId = 31337
} else {
remoteChainId = hre.deployConfig.remoteChainId
}
if (typeof remoteChainId !== 'number') {
throw new Error('remoteChainId not defined')
}
await hre.deployments.deploy('OptimismMintableERC721Factory', { await hre.deployments.deploy('OptimismMintableERC721Factory', {
from: deployer, from: deployer,
args: [ethers.constants.AddressZero, 0], args: [predeploy, remoteChainId],
log: true, log: true,
waitConfirmations: 1,
}) })
const Deployment__OptimismMintableERC721Factory = await hre.deployments.get(
'OptimismMintableERC721Factory'
)
console.log(
`OptimismMintableERC721Factory deployed to ${Deployment__OptimismMintableERC721Factory.address}`
)
{
// Upgrade the Proxy to the newly deployed implementation
const tx = await OptimismMintableERC721FactoryProxy.upgradeTo(
Deployment__OptimismMintableERC721Factory.address
)
const receipt = await tx.wait()
console.log(
`OptimismMintableERC721FactoryProxy upgraded: ${receipt.transactionHash}`
)
}
{
const newAdmin = getProxyAdmin(hre.network.name)
const tx = await OptimismMintableERC721FactoryProxy.changeAdmin(newAdmin)
const receipt = await tx.wait()
console.log(
`OptimismMintableERC721FactoryProxy admin updated: ${receipt.transactionHash}`
)
}
} }
deployFn.tags = ['OptimismMintableERC721FactoryImplementation'] deployFn.tags = ['OptimismMintableERC721FactoryImplementation']
......
/* Imports: External */ /* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deploy } = hre.deployments
const { deploy } = await hre.deployments.deterministic( console.log(
'OptimismMintableERC721FactoryProxy', `Deploying OptimismMintableERC721FactoryProxy to ${hre.network.name}`
{
contract: 'Proxy',
salt: hre.ethers.utils.solidityKeccak256(
['string'],
['OptimismMintableERC721FactoryProxy']
),
from: deployer,
args: [hre.deployConfig.ddd],
log: true,
}
) )
console.log(`Using deployer ${deployer}`)
await deploy() // Deploy the OptimismMintableERC721FactoryProxy with
// the deployer as the admin. The admin and implementation
// will be updated with the deployment of the implementation
await deploy('OptimismMintableERC721FactoryProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: 1,
})
const Deployment__OptimismMintableERC721FactoryProxy =
await hre.deployments.get('OptimismMintableERC721FactoryProxy')
console.log(
`OptimismMintableERC721FactoryProxy deployed to ${Deployment__OptimismMintableERC721FactoryProxy.address}`
)
} }
deployFn.tags = ['OptimismMintableERC721FactoryProxy'] deployFn.tags = ['OptimismMintableERC721FactoryProxy']
......
...@@ -112,6 +112,21 @@ const config: HardhatUserConfig = { ...@@ -112,6 +112,21 @@ const config: HardhatUserConfig = {
}, },
}, },
}, },
'ops-l2': {
chainId: 17,
accounts: [
'0x3b8d2345102cce2443acb240db6e87c8edd4bb3f821b17fab8ea2c9da08ea132',
'0xa6aecc98b63bafb0de3b29ae9964b14acb4086057808be29f90150214ebd4a0f',
],
url: 'http://127.0.0.1:8545',
},
'ops-l1': {
chainId: 31337,
accounts: [
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
],
url: 'http://127.0.0.1:9545',
},
}, },
paths: { paths: {
deployConfig: './config/deploy', deployConfig: './config/deploy',
......
#!/bin/bash
set -e
L1_NETWORK=ops-l1
L2_NETWORK=ops-l2
# Step 1: deploy the Proxy to the predeploy address on L2
npx hardhat deploy --tags L2ERC721BridgeProxy --network $L2_NETWORK
# Step 2: deploy the Proxy for the L1ERC721Bridge to L1
npx hardhat deploy --tags L1ERC721BridgeProxy --network $L1_NETWORK
# Step 3: deploy the L2ERC721Bridge implementation
npx hardhat deploy --tags L2ERC721BridgeImplementation --network $L2_NETWORK
# Step 4: deploy the L1ERC721Bridge implementation to L1
npx hardhat deploy --tags L1ERC721BridgeImplementation --network $L1_NETWORK
# Step 5: deploy the Proxy for the OptimismMintableERC721Factory to L2
npx hardhat deploy --tags OptimismMintableERC721FactoryProxy --network $L2_NETWORK
# Step 5: deploy the OptimismMintableERC721Factory to L2
npx hardhat deploy --tags OptimismMintableERC721FactoryImplementation --network $L2_NETWORK
import { utils } from 'ethers'
// https://optimistic.etherscan.io/address/0x2501c477d0a35545a387aa4a3eee4292a9a8b3f0
export const l2MainnetMultisig = '0x2501c477D0A35545a387Aa4A3EEe4292A9a8B3F0'
// https://etherscan.io/address/0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A
export const l1MainnetMultisig = '0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A'
// https://goerli.etherscan.io/address/0xf80267194936da1E98dB10bcE06F3147D580a62e
export const goerliAdmin = '0xf80267194936da1E98dB10bcE06F3147D580a62e'
export const predeploy = '0x4200000000000000000000000000000000000014'
export const predeployDeployer = '0xdfc82d475833a50de90c642770f34a9db7deb725'
export const isTargetL2Network = (network: string): boolean => {
switch (network) {
case 'optimism':
case 'optimism-goerli':
case 'ops-l2':
return true
default:
return false
}
}
export const isTargetL1Network = (network: string): boolean => {
switch (network) {
case 'mainnet':
case 'goerli':
case 'ops-l1':
return true
default:
return false
}
}
export const getProxyAdmin = (network: string): string => {
switch (network) {
case 'optimism':
return l2MainnetMultisig
case 'mainnet':
return l1MainnetMultisig
case 'goerli':
case 'optimism-goerli':
return goerliAdmin
case 'ops-l1':
case 'ops-l2':
return predeployDeployer
default:
throw new Error(`unknown network ${network}`)
}
}
export const validateERC721Bridge = async (hre, address: string, expected) => {
const L1ERC721Bridge = await hre.ethers.getContractAt('ERC721Bridge', address)
const messenger = await L1ERC721Bridge.messenger()
const otherBridge = await L1ERC721Bridge.otherBridge()
if (utils.getAddress(messenger) !== utils.getAddress(expected.messenger)) {
throw new Error(`messenger mismatch`)
}
if (
utils.getAddress(otherBridge) !== utils.getAddress(expected.otherBridge)
) {
throw new Error(`otherBridge mismatch`)
}
}
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