Commit d146ef52 authored by James Kim's avatar James Kim

add deployment script for OptimistInviter

parent e30fd3f7
...@@ -6,6 +6,8 @@ const config: DeployConfig = { ...@@ -6,6 +6,8 @@ const config: DeployConfig = {
optimistName: '', optimistName: '',
optimistSymbol: '', optimistSymbol: '',
attestorAddress: '', attestorAddress: '',
optimistInviterName: '',
optimistInviterInviteGranter: '',
} }
export default config export default config
...@@ -6,6 +6,8 @@ const config: DeployConfig = { ...@@ -6,6 +6,8 @@ const config: DeployConfig = {
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterName: 'OptimistInviter',
} }
export default config export default config
...@@ -6,6 +6,8 @@ const config: DeployConfig = { ...@@ -6,6 +6,8 @@ const config: DeployConfig = {
optimistName: 'OP Citizenship', optimistName: 'OP Citizenship',
optimistSymbol: 'OPNFT', optimistSymbol: 'OPNFT',
attestorAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', attestorAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
optimistInviterInviteGranter: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
optimistInviterName: 'OptimistInviter',
} }
export default config export default config
...@@ -6,6 +6,8 @@ const config: DeployConfig = { ...@@ -6,6 +6,8 @@ const config: DeployConfig = {
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterName: 'OptimistInviter',
} }
export default config export default config
...@@ -6,6 +6,8 @@ const config: DeployConfig = { ...@@ -6,6 +6,8 @@ const config: DeployConfig = {
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819', attestorAddress: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistInviterInviteGranter: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistInviterName: 'OptimistInviter',
} }
export default config export default config
...@@ -6,6 +6,8 @@ const config: DeployConfig = { ...@@ -6,6 +6,8 @@ const config: DeployConfig = {
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterName: 'OptimistInviter',
} }
export default config export default config
...@@ -61,9 +61,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -61,9 +61,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
addr addr
) )
const implementation = await Proxy.callStatic.implementation({ const implementation = await Proxy.connect(
from: ethers.constants.AddressZero, ethers.constants.AddressZero
}) ).callStatic.implementation()
console.log(`implementation is set to ${implementation}`) console.log(`implementation is set to ${implementation}`)
if ( if (
getAddress(implementation) !== getAddress(implementation) !==
...@@ -82,9 +82,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -82,9 +82,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
} }
const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress
const admin = await Proxy.callStatic.admin({ const admin = await Proxy.connect(
from: ethers.constants.AddressZero, ethers.constants.AddressZero
}) ).callStatic.admin()
console.log(`admin is set to ${admin}`) console.log(`admin is set to ${admin}`)
if (getAddress(admin) !== getAddress(l2ProxyOwnerAddress)) { if (getAddress(admin) !== getAddress(l2ProxyOwnerAddress)) {
console.log('admin not set correctly') console.log('admin not set correctly')
...@@ -99,7 +99,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -99,7 +99,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
console.log('Contract deployment complete') console.log('Contract deployment complete')
await assertContractVariable(Proxy, 'admin', l2ProxyOwnerAddress) await assertContractVariable(Proxy, 'admin', l2ProxyOwnerAddress)
await assertContractVariable(AttestationStation, 'version', '1.0.0') await assertContractVariable(AttestationStation, 'version', '1.1.0')
} }
deployFn.tags = ['AttestationStationProxy', 'OptimistEnvironment'] deployFn.tags = ['AttestationStationProxy', 'OptimistEnvironment']
......
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@nomiclabs/hardhat-ethers'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import type { DeployConfig } from '../../src'
const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const deployConfig = hre.deployConfig as DeployConfig
const { deployer } = await hre.getNamedAccounts()
console.log(`Deploying OptimistInviter implementation with ${deployer}`)
const Deployment__AttestationStation = await hre.deployments.get(
'AttestationStationProxy'
)
const attestationStationAddress = Deployment__AttestationStation.address
console.log(`Using ${attestationStationAddress} as the ATTESTATION_STATION`)
console.log(
`Using ${deployConfig.optimistInviterInviteGranter} as INVITE_GRANTER`
)
const { deploy } = await hre.deployments.deterministic('OptimistInviter', {
salt: hre.ethers.utils.solidityKeccak256(['string'], ['OptimistInviter']),
from: deployer,
args: [
deployConfig.optimistInviterInviteGranter,
attestationStationAddress,
],
log: true,
})
await deploy()
}
deployFn.tags = ['OptimistInviter', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy']
export default deployFn
/* Imports: External */
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { assertContractVariable } from '@eth-optimism/contracts-bedrock/src/deploy-utils'
import { ethers, utils } from 'ethers'
import type { DeployConfig } from '../../src'
const { getAddress } = utils
// Required conditions before deploying - Specified in `deployFn.dependencies`
// - AttestationStationProxy is deployed and points to the correct implementation
// - OptimistInviterImpl is deployed
//
// Steps
// 1. Deploy OptimistInviterProxy
// 2. Point the newly deployed proxy to the implementation, if it hasn't been done already
// 3. Update the admin of the proxy to the l2ProxyOwnerAddress, if it hasn't been done already
// 4. Basic sanity checks for contract variables
const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const deployConfig = hre.deployConfig as DeployConfig
// Deployer should be set in hardhat.config.ts
const { deployer } = await hre.getNamedAccounts()
// We want the ability to deploy to a deterministic address, so we need the init bytecode to be
// consistent across deployments. The ddd will quickly transfer the ownership of the Proxy to a
// multisig after deployment.
//
// We need a consistent ddd, since the Proxy takes a `_admin` constructor argument, which
// affects the init bytecode and hence deployed address.
const ddd = deployConfig.ddd
if (getAddress(deployer) !== getAddress(ddd)) {
// Not a hard requirement. We can deploy with any account and just set the `_admin` to the
// ddd, but requiring that the deployer is the same as the ddd minimizes number of hot wallets
// we need to keep track of during deployment.
throw new Error('Must deploy with the ddd')
}
// Get the up to date deployment of the OptimistInviter contract
const Deployment__OptimistInviterImpl = await hre.deployments.get(
'OptimistInviter'
)
console.log(`Deploying OptimistInviterProxy with ${deployer}`)
// Deploys the Proxy.sol contract with the `_admin` constructor param set to the ddd (=== deployer).
const { deploy } = await hre.deployments.deterministic(
'OptimistInviterProxy',
{
salt: hre.ethers.utils.solidityKeccak256(
['string'],
['OptimistInviterProxy']
),
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
}
)
// Deploy the Proxy contract
await deploy()
const Deployment__OptimistInviterProxy = await hre.deployments.get(
'OptimistInviterProxy'
)
console.log(
`OptimistProxy deployed to ${Deployment__OptimistInviterProxy.address}`
)
// Deployed Proxy.sol contract
const Proxy = await hre.ethers.getContractAt(
'Proxy',
Deployment__OptimistInviterProxy.address
)
// Deployed Proxy.sol contract with the OptimistInviter interface
const OptimistInviter = await hre.ethers.getContractAt(
'OptimistInviter',
Deployment__OptimistInviterProxy.address
)
// Gets the current implementation address the proxy is pointing to.
// callStatic is used since the `Proxy.implementation()` is not a view function and ethers will
// try to make a transaction if we don't use callStatic. Using the zero address as `from` lets us
// call functions on the proxy and not trigger the delegatecall. See Proxy.sol proxyCallIfNotAdmin
// modifier for more details.
const implementation = await Proxy.connect(
ethers.constants.AddressZero
).callStatic.implementation()
console.log(`implementation set to ${implementation}`)
if (
getAddress(implementation) !==
getAddress(Deployment__OptimistInviterImpl.address)
) {
// If the proxy isn't pointing to the correct implementation, we need to set it to the correct
// one, then call initialize() in the proxy's context.
console.log(
'implementation not set to OptimistInviter implementation contract'
)
console.log(
`Setting implementation to ${Deployment__OptimistInviterImpl.address}`
)
const name = deployConfig.optimistInviterName
// Create the calldata for the call to `initialize()`
const calldata = OptimistInviter.interface.encodeFunctionData(
'initialize',
[name]
)
// ethers.Signer for the ddd
const dddSigner = await hre.ethers.provider.getSigner(deployer)
// Point the proxy to the deployed OptimistInviter implementation contract,
// and call `initialize()` in the proxy's context
const tx = await Proxy.connect(dddSigner).upgradeToAndCall(
Deployment__OptimistInviterImpl.address,
calldata
)
const receipt = await tx.wait()
console.log(`implementation set in ${receipt.transactionHash}`)
} else {
console.log(
'implementation already set to OptimistInviter implementation contract'
)
}
const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress
// Get the current proxy admin address
const admin = await Proxy.connect(
ethers.constants.AddressZero
).callStatic.admin()
console.log(`admin currently set to ${admin}`)
if (getAddress(admin) !== getAddress(l2ProxyOwnerAddress)) {
// If the proxy admin isn't the l2ProxyOwnerAddress, we need to update it
// We're assuming that the proxy admin is the ddd right now.
console.log('admin is not set to the l2ProxyOwnerAddress')
console.log(`Setting admin to ${l2ProxyOwnerAddress}`)
// ethers.Signer for the ddd
const dddSigner = await hre.ethers.provider.getSigner(deployer)
// change admin to the l2ProxyOwnerAddress
const tx = await Proxy.connect(dddSigner).changeAdmin(l2ProxyOwnerAddress)
const receipt = await tx.wait()
console.log(`admin set in ${receipt.transactionHash}`)
} else {
console.log('admin already set to proxy owner address')
}
const Deployment__AttestationStation = await hre.deployments.get(
'AttestationStationProxy'
)
await assert(
getAddress(
await Proxy.connect(ethers.constants.AddressZero).callStatic.admin()
) === getAddress(l2ProxyOwnerAddress)
)
await assertContractVariable(OptimistInviter, 'version', '1.0.0')
await assertContractVariable(
OptimistInviter,
'INVITE_GRANTER',
deployConfig.optimistInviterInviteGranter
)
await assertContractVariable(
OptimistInviter,
'ATTESTATION_STATION',
Deployment__AttestationStation.address
)
await assertContractVariable(OptimistInviter, 'EIP712_VERSION', '1.0.0')
}
deployFn.tags = ['OptimistInviterProxy', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy', 'OptimistInviter']
export default deployFn
...@@ -48,9 +48,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -48,9 +48,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
Deployment__OptimistProxy.address Deployment__OptimistProxy.address
) )
const implementation = await Proxy.callStatic.implementation({ const implementation = await Proxy.connect(
from: ethers.constants.AddressZero, ethers.constants.AddressZero
}) ).callStatic.implementation()
console.log(`implementation set to ${implementation}`) console.log(`implementation set to ${implementation}`)
if (getAddress(implementation) !== getAddress(Deployment__Optimist.address)) { if (getAddress(implementation) !== getAddress(Deployment__Optimist.address)) {
console.log('implementation not set to Optimist contract') console.log('implementation not set to Optimist contract')
...@@ -75,9 +75,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -75,9 +75,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
} }
const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress
const admin = await Proxy.callStatic.admin({ const admin = await Proxy.connect(
from: ethers.constants.AddressZero, ethers.constants.AddressZero
}) ).callStatic.admin()
console.log(`admin set to ${admin}`) console.log(`admin set to ${admin}`)
if (getAddress(admin) !== getAddress(l2ProxyOwnerAddress)) { if (getAddress(admin) !== getAddress(l2ProxyOwnerAddress)) {
console.log('detected admin is not set') console.log('detected admin is not set')
...@@ -96,7 +96,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -96,7 +96,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
await assertContractVariable(Proxy, 'admin', l2ProxyOwnerAddress) await assertContractVariable(Proxy, 'admin', l2ProxyOwnerAddress)
await assertContractVariable(Optimist, 'name', deployConfig.optimistName) await assertContractVariable(Optimist, 'name', deployConfig.optimistName)
await assertContractVariable(Optimist, 'verson', '1.0.0') await assertContractVariable(Optimist, 'version', '1.0.0')
await assertContractVariable(Optimist, 'symbol', deployConfig.optimistSymbol) await assertContractVariable(Optimist, 'symbol', deployConfig.optimistSymbol)
await assertContractVariable( await assertContractVariable(
Optimist, Optimist,
......
...@@ -29,10 +29,20 @@ export interface DeployConfig { ...@@ -29,10 +29,20 @@ export interface DeployConfig {
optimistSymbol: string optimistSymbol: string
/** /**
* Address of the priviledged attestor for the Optimist contract. * Address of the privileged attestor for the Optimist contract.
*/ */
attestorAddress: string attestorAddress: string
/**
* Address of the privileged account for the OptimistInviter contract that can grant invites.
*/
optimistInviterInviteGranter: string
/**
* Name of OptimistInviter contract, used for the EIP712 domain separator.
*/
optimistInviterName: string
/** /**
* Address of the owner of the proxies on L2. There will be a ProxyAdmin deployed as a predeploy * Address of the owner of the proxies on L2. There will be a ProxyAdmin deployed as a predeploy
* after bedrock, so the owner of proxies should be updated to that after the upgrade. * after bedrock, so the owner of proxies should be updated to that after the upgrade.
...@@ -63,6 +73,12 @@ export const configSpec: DeployConfigSpec<DeployConfig> = { ...@@ -63,6 +73,12 @@ export const configSpec: DeployConfigSpec<DeployConfig> = {
attestorAddress: { attestorAddress: {
type: 'address', type: 'address',
}, },
optimistInviterInviteGranter: {
type: 'address',
},
optimistInviterName: {
type: 'string',
},
l2ProxyOwnerAddress: { l2ProxyOwnerAddress: {
type: 'address', type: 'address',
}, },
......
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