Commit b1e9c0e2 authored by James Kim's avatar James Kim

update Optimist and OptimistAllowlist deploy scripts

parent 18747f11
...@@ -5,9 +5,11 @@ const config: DeployConfig = { ...@@ -5,9 +5,11 @@ const config: DeployConfig = {
l2ProxyOwnerAddress: '', l2ProxyOwnerAddress: '',
optimistName: '', optimistName: '',
optimistSymbol: '', optimistSymbol: '',
attestorAddress: '', optimistBaseUriAttestorAddress: '',
optimistInviterName: '',
optimistInviterInviteGranter: '', optimistInviterInviteGranter: '',
optimistInviterName: '',
optimistAllowlistAllowlistAttestor: '',
optimistAllowlistCoinbaseQuestAttestor: '',
} }
export default config export default config
...@@ -5,9 +5,13 @@ const config: DeployConfig = { ...@@ -5,9 +5,13 @@ const config: DeployConfig = {
l2ProxyOwnerAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', l2ProxyOwnerAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', optimistBaseUriAttestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterName: 'OptimistInviter', optimistInviterName: 'OptimistInviter',
optimistAllowlistAllowlistAttestor:
'0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistAllowlistCoinbaseQuestAttestor:
'0x661B7Acca8ebd93AFd349a088e9a9A00053DB1BF',
} }
export default config export default config
...@@ -5,9 +5,13 @@ const config: DeployConfig = { ...@@ -5,9 +5,13 @@ const config: DeployConfig = {
l2ProxyOwnerAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', l2ProxyOwnerAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
optimistName: 'OP Citizenship', optimistName: 'OP Citizenship',
optimistSymbol: 'OPNFT', optimistSymbol: 'OPNFT',
attestorAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', optimistBaseUriAttestorAddress: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
optimistInviterInviteGranter: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', optimistInviterInviteGranter: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
optimistInviterName: 'OptimistInviter', optimistInviterName: 'OptimistInviter',
optimistAllowlistAllowlistAttestor:
'0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
optimistAllowlistCoinbaseQuestAttestor:
'0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
} }
export default config export default config
...@@ -5,9 +5,13 @@ const config: DeployConfig = { ...@@ -5,9 +5,13 @@ const config: DeployConfig = {
l2ProxyOwnerAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', l2ProxyOwnerAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', optimistBaseUriAttestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterName: 'OptimistInviter', optimistInviterName: 'OptimistInviter',
optimistAllowlistAllowlistAttestor:
'0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistAllowlistCoinbaseQuestAttestor:
'0x661B7Acca8ebd93AFd349a088e9a9A00053DB1BF',
} }
export default config export default config
...@@ -2,12 +2,17 @@ import { DeployConfig } from '../../src' ...@@ -2,12 +2,17 @@ import { DeployConfig } from '../../src'
const config: DeployConfig = { const config: DeployConfig = {
ddd: '0x9C6373dE60c2D3297b18A8f964618ac46E011B58', ddd: '0x9C6373dE60c2D3297b18A8f964618ac46E011B58',
// EcoPod test account
l2ProxyOwnerAddress: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819', l2ProxyOwnerAddress: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819', optimistBaseUriAttestorAddress: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistInviterInviteGranter: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819', optimistInviterInviteGranter: '0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistInviterName: 'OptimistInviter', optimistInviterName: 'OptimistInviter',
optimistAllowlistAllowlistAttestor:
'0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
optimistAllowlistCoinbaseQuestAttestor:
'0x8F0EBDaA1cF7106bE861753B0f9F5c0250fE0819',
} }
export default config export default config
...@@ -5,9 +5,13 @@ const config: DeployConfig = { ...@@ -5,9 +5,13 @@ const config: DeployConfig = {
l2ProxyOwnerAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', l2ProxyOwnerAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistName: 'Optimist', optimistName: 'Optimist',
optimistSymbol: 'OPTIMIST', optimistSymbol: 'OPTIMIST',
attestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', optimistBaseUriAttestorAddress: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3', optimistInviterInviteGranter: '0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistInviterName: 'OptimistInviter', optimistInviterName: 'OptimistInviter',
optimistAllowlistAllowlistAttestor:
'0x60c5C9c98bcBd0b0F2fD89B24c16e533BaA8CdA3',
optimistAllowlistCoinbaseQuestAttestor:
'0x661B7Acca8ebd93AFd349a088e9a9A00053DB1BF',
} }
export default config export default config
/* 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 OptimistAllowlist implementation with ${deployer}`)
const Deployment__AttestationStation = await hre.deployments.get(
'AttestationStationProxy'
)
const Deployment__OptimistInviter = await hre.deployments.get(
'OptimistInviterProxy'
)
const attestationStationAddress = Deployment__AttestationStation.address
const optimistInviterAddress = Deployment__OptimistInviter.address
console.log(`Using ${attestationStationAddress} as the ATTESTATION_STATION`)
console.log(
`Using ${deployConfig.optimistAllowlistAllowlistAttestor} as ALLOWLIST_ATTESTOR`
)
console.log(
`Using ${deployConfig.optimistAllowlistCoinbaseQuestAttestor} as COINBASE_QUEST_ATTESTOR`
)
console.log(`Using ${optimistInviterAddress} as OPTIMIST_INVITER`)
const { deploy } = await hre.deployments.deterministic('OptimistAllowlist', {
salt: hre.ethers.utils.solidityKeccak256(['string'], ['OptimistAllowlist']),
from: deployer,
args: [
attestationStationAddress,
deployConfig.optimistAllowlistAllowlistAttestor,
deployConfig.optimistAllowlistCoinbaseQuestAttestor,
optimistInviterAddress,
],
log: true,
})
await deploy()
}
deployFn.tags = ['OptimistAllowlistImpl', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy', 'OptimistInviterProxy']
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'
import { setupProxyContract } from '../../src/helpers/setupProxyContract'
const { getAddress } = utils
// Required conditions before deploying - Specified in `deployFn.dependencies`
// - AttestationStationProxy is deployed and points to the correct implementation
// - OptimistInviterProxy is deployed and points to the correct implementation
// - OptimistAllowlistImpl is deployed
//
// Steps
// 1. Deploy OptimistAllowlistProxy
// 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 OptimistAllowlist contract
const Deployment__OptimistAllowlistImpl = await hre.deployments.get(
'OptimistAllowlist'
)
console.log(`Deploying OptimistAllowlistProxy with ${deployer}`)
// Deploys the Proxy.sol contract with the `_admin` constructor param set to the ddd (=== deployer).
const { deploy } = await hre.deployments.deterministic(
'OptimistAllowlistProxy',
{
salt: hre.ethers.utils.solidityKeccak256(
['string'],
['OptimistAllowlistProxy']
),
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
}
)
// Deploy the Proxy contract
await deploy()
const Deployment__OptimistAllowlistProxy = await hre.deployments.get(
'OptimistAllowlistProxy'
)
console.log(
`OptimistAllowlistProxy deployed to ${Deployment__OptimistAllowlistProxy.address}`
)
// Deployed Proxy.sol contract
const Proxy = await hre.ethers.getContractAt(
'Proxy',
Deployment__OptimistAllowlistProxy.address
)
// Deployed Proxy.sol contract with the OptimistAllowlist interface
const OptimistAllowlist = await hre.ethers.getContractAt(
'OptimistAllowlist',
Deployment__OptimistAllowlistProxy.address
)
// ethers.Signer for the ddd. Should be the current owner of the Proxy.
const dddSigner = await hre.ethers.provider.getSigner(deployer)
// intended admin of the Proxy
const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress
// setup the Proxy contract with correct implementation and admin
await setupProxyContract(Proxy, dddSigner, {
targetImplAddress: Deployment__OptimistAllowlistImpl.address,
targetProxyOwnerAddress: l2ProxyOwnerAddress,
})
const Deployment__AttestationStationProxy = await hre.deployments.get(
'AttestationStationProxy'
)
const Deployment__OptimistInviter = await hre.deployments.get(
'OptimistInviterProxy'
)
await assert(
getAddress(
await Proxy.connect(ethers.constants.AddressZero).callStatic.admin()
) === getAddress(l2ProxyOwnerAddress)
)
await assertContractVariable(OptimistAllowlist, 'version', '1.0.0')
await assertContractVariable(
OptimistAllowlist,
'ATTESTATION_STATION',
Deployment__AttestationStationProxy.address
)
await assertContractVariable(
OptimistAllowlist,
'ALLOWLIST_ATTESTOR',
deployConfig.optimistAllowlistAllowlistAttestor
)
await assertContractVariable(
OptimistAllowlist,
'COINBASE_QUEST_ATTESTOR',
deployConfig.optimistAllowlistCoinbaseQuestAttestor
)
await assertContractVariable(
OptimistAllowlist,
'OPTIMIST_INVITER',
Deployment__OptimistInviter.address
)
}
deployFn.tags = ['OptimistAllowlistProxy', 'OptimistEnvironment']
deployFn.dependencies = [
'AttestationStationProxy',
'OptimistInviterProxy',
'OptimistAllowlistImpl',
]
export default deployFn
...@@ -14,13 +14,19 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -14,13 +14,19 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
console.log(`Deploying Optimist implementation with ${deployer}`) console.log(`Deploying Optimist implementation with ${deployer}`)
const Deployment__AttestationStation = await hre.deployments.get( const Deployment__AttestationStationProxy = await hre.deployments.get(
'AttestationStationProxy' 'AttestationStationProxy'
) )
const attestationStationAddress = Deployment__AttestationStation.address const attestationStationAddress = Deployment__AttestationStationProxy.address
console.log(`Using ${attestationStationAddress} as the ATTESTATION_STATION`)
console.log(
`Using ${deployConfig.optimistBaseUriAttestorAddress} as BASE_URI_ATTESTOR`
)
console.log(`Using ${attestationStationAddress} as the AttestationStation`) const Deployment__OptimistAllowlistProxy = await hre.deployments.get(
console.log(`Using ${deployConfig.attestorAddress} as ATTESTOR`) 'OptimistAllowlistProxy'
)
const optimistAllowlistAddress = Deployment__OptimistAllowlistProxy.address
const { deploy } = await hre.deployments.deterministic('Optimist', { const { deploy } = await hre.deployments.deterministic('Optimist', {
salt: hre.ethers.utils.solidityKeccak256(['string'], ['Optimist']), salt: hre.ethers.utils.solidityKeccak256(['string'], ['Optimist']),
...@@ -28,8 +34,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -28,8 +34,9 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
args: [ args: [
deployConfig.optimistName, deployConfig.optimistName,
deployConfig.optimistSymbol, deployConfig.optimistSymbol,
deployConfig.attestorAddress, deployConfig.optimistBaseUriAttestorAddress,
attestationStationAddress, attestationStationAddress,
optimistAllowlistAddress,
], ],
log: true, log: true,
}) })
...@@ -37,6 +44,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -37,6 +44,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
await deploy() await deploy()
} }
deployFn.tags = ['Optimist', 'OptimistEnvironment'] deployFn.tags = ['OptimistImpl', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy', 'OptimistAllowlistProxy']
export default deployFn export default deployFn
...@@ -37,7 +37,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -37,7 +37,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
await deploy() await deploy()
} }
deployFn.tags = ['OptimistInviter', 'OptimistEnvironment'] deployFn.tags = ['OptimistInviterImpl', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy'] deployFn.dependencies = ['AttestationStationProxy']
export default deployFn export default deployFn
...@@ -10,6 +10,7 @@ import { assertContractVariable } from '@eth-optimism/contracts-bedrock/src/depl ...@@ -10,6 +10,7 @@ import { assertContractVariable } from '@eth-optimism/contracts-bedrock/src/depl
import { ethers, utils } from 'ethers' import { ethers, utils } from 'ethers'
import type { DeployConfig } from '../../src' import type { DeployConfig } from '../../src'
import { setupProxyContract } from '../../src/helpers/setupProxyContract'
const { getAddress } = utils const { getAddress } = utils
...@@ -73,7 +74,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -73,7 +74,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
'OptimistInviterProxy' 'OptimistInviterProxy'
) )
console.log( console.log(
`OptimistProxy deployed to ${Deployment__OptimistInviterProxy.address}` `OptimistInviterProxy deployed to ${Deployment__OptimistInviterProxy.address}`
) )
// Deployed Proxy.sol contract // Deployed Proxy.sol contract
...@@ -88,79 +89,25 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -88,79 +89,25 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
Deployment__OptimistInviterProxy.address 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 const name = deployConfig.optimistInviterName
// Create the calldata for the call to `initialize()` // Create the calldata for the call to `initialize()`
const calldata = OptimistInviter.interface.encodeFunctionData( const initializeCalldata = OptimistInviter.interface.encodeFunctionData(
'initialize', 'initialize',
[name] [name]
) )
// ethers.Signer for the ddd // ethers.Signer for the ddd. Should be the current owner of the Proxy.
const dddSigner = await hre.ethers.provider.getSigner(deployer) const dddSigner = await hre.ethers.provider.getSigner(deployer)
// Point the proxy to the deployed OptimistInviter implementation contract, // intended admin of the Proxy
// 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 const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress
// Get the current proxy admin address // setup the Proxy contract with correct implementation and admin
const admin = await Proxy.connect( await setupProxyContract(Proxy, dddSigner, {
ethers.constants.AddressZero targetImplAddress: Deployment__OptimistInviterImpl.address,
).callStatic.admin() targetProxyOwnerAddress: l2ProxyOwnerAddress,
postUpgradeCallCalldata: initializeCalldata,
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( const Deployment__AttestationStation = await hre.deployments.get(
'AttestationStationProxy' 'AttestationStationProxy'
...@@ -190,6 +137,6 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -190,6 +137,6 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
} }
deployFn.tags = ['OptimistInviterProxy', 'OptimistEnvironment'] deployFn.tags = ['OptimistInviterProxy', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy', 'OptimistInviter'] deployFn.dependencies = ['AttestationStationProxy', 'OptimistInviterImpl']
export default deployFn export default deployFn
...@@ -5,8 +5,9 @@ import '@eth-optimism/hardhat-deploy-config' ...@@ -5,8 +5,9 @@ import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import { assertContractVariable } from '@eth-optimism/contracts-bedrock/src/deploy-utils' import { assertContractVariable } from '@eth-optimism/contracts-bedrock/src/deploy-utils'
import { ethers, utils } from 'ethers' import { utils } from 'ethers'
import { setupProxyContract } from '../../src/helpers/setupProxyContract'
import type { DeployConfig } from '../../src' import type { DeployConfig } from '../../src'
const { getAddress } = utils const { getAddress } = utils
...@@ -21,7 +22,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -21,7 +22,7 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
throw new Error('Must deploy with the ddd') throw new Error('Must deploy with the ddd')
} }
const Deployment__Optimist = await hre.deployments.get('Optimist') const Deployment__OptimistImpl = await hre.deployments.get('Optimist')
console.log(`Deploying OptimistProxy with ${deployer}`) console.log(`Deploying OptimistProxy with ${deployer}`)
...@@ -48,69 +49,58 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { ...@@ -48,69 +49,58 @@ const deployFn: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
Deployment__OptimistProxy.address Deployment__OptimistProxy.address
) )
const implementation = await Proxy.connect( // ethers.Signer for the ddd. Should be the current owner of the Proxy.
ethers.constants.AddressZero const dddSigner = await hre.ethers.provider.getSigner(deployer)
).callStatic.implementation()
console.log(`implementation set to ${implementation}`) // intended admin of the Proxy
if (getAddress(implementation) !== getAddress(Deployment__Optimist.address)) { const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress
console.log('implementation not set to Optimist contract')
console.log(`Setting implementation to ${Deployment__Optimist.address}`)
// Create the calldata for the call to `initialize()` // Create the calldata for the call to `initialize()`
const name = deployConfig.optimistName const name = deployConfig.optimistName
const symbol = deployConfig.optimistSymbol const symbol = deployConfig.optimistSymbol
const calldata = Optimist.interface.encodeFunctionData('initialize', [ const initializeCalldata = Optimist.interface.encodeFunctionData(
name, 'initialize',
symbol, [name, symbol]
])
const tx = await Proxy.upgradeToAndCall(
Deployment__Optimist.address,
calldata
) )
const receipt = await tx.wait()
console.log(`implementation set in ${receipt.transactionHash}`)
} else {
console.log('implementation already set to Optimist contract')
}
const l2ProxyOwnerAddress = deployConfig.l2ProxyOwnerAddress // setup the Proxy contract with correct implementation and admin, and initialize atomically
const admin = await Proxy.connect( await setupProxyContract(Proxy, dddSigner, {
ethers.constants.AddressZero targetImplAddress: Deployment__OptimistImpl.address,
).callStatic.admin() targetProxyOwnerAddress: l2ProxyOwnerAddress,
console.log(`admin set to ${admin}`) postUpgradeCallCalldata: initializeCalldata,
if (getAddress(admin) !== getAddress(l2ProxyOwnerAddress)) { })
console.log('detected admin is not set')
console.log(`Setting admin to ${l2ProxyOwnerAddress}`)
const tx = await Proxy.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( const Deployment__AttestationStationProxy = await hre.deployments.get(
'AttestationStationProxy' 'AttestationStationProxy'
) )
const Deployment__OptimistAllowlistProxy = await hre.deployments.get(
'OptimistAllowlistProxy'
)
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, 'version', '1.0.0') await assertContractVariable(Optimist, 'version', '2.0.0')
await assertContractVariable(Optimist, 'symbol', deployConfig.optimistSymbol) await assertContractVariable(Optimist, 'symbol', deployConfig.optimistSymbol)
await assertContractVariable( await assertContractVariable(
Optimist, Optimist,
'ATTESTOR', 'BASE_URI_ATTESTOR',
deployConfig.attestorAddress deployConfig.optimistBaseUriAttestorAddress
)
await assertContractVariable(
Optimist,
'OPTIMIST_ALLOWLIST',
Deployment__OptimistAllowlistProxy.address
) )
await assertContractVariable( await assertContractVariable(
Optimist, Optimist,
'ATTESTATION_STATION', 'ATTESTATION_STATION',
Deployment__AttestationStation.address Deployment__AttestationStationProxy.address
) )
} }
deployFn.tags = ['OptimistProxy', 'OptimistEnvironment'] deployFn.tags = ['OptimistProxy', 'OptimistEnvironment']
deployFn.dependencies = ['AttestationStationProxy', 'Optimist'] deployFn.dependencies = ['AttestationStationProxy', 'OptimistImpl']
export default deployFn export default deployFn
...@@ -31,7 +31,7 @@ export interface DeployConfig { ...@@ -31,7 +31,7 @@ export interface DeployConfig {
/** /**
* Address of the privileged attestor for the Optimist contract. * Address of the privileged attestor for the Optimist contract.
*/ */
attestorAddress: string optimistBaseUriAttestorAddress: string
/** /**
* Address of the privileged account for the OptimistInviter contract that can grant invites. * Address of the privileged account for the OptimistInviter contract that can grant invites.
...@@ -43,6 +43,17 @@ export interface DeployConfig { ...@@ -43,6 +43,17 @@ export interface DeployConfig {
*/ */
optimistInviterName: string optimistInviterName: string
/**
* Address of previleged account for the OptimistAllowlist contract that can add/remove people
* from allowlist.
*/
optimistAllowlistAllowlistAttestor: string
/**
* Address of Coinbase attestor that attests to whether someone has completed Quest.
*/
optimistAllowlistCoinbaseQuestAttestor: 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.
...@@ -70,7 +81,7 @@ export const configSpec: DeployConfigSpec<DeployConfig> = { ...@@ -70,7 +81,7 @@ export const configSpec: DeployConfigSpec<DeployConfig> = {
type: 'string', type: 'string',
default: 'OPTIMIST', default: 'OPTIMIST',
}, },
attestorAddress: { optimistBaseUriAttestorAddress: {
type: 'address', type: 'address',
}, },
optimistInviterInviteGranter: { optimistInviterInviteGranter: {
...@@ -79,6 +90,15 @@ export const configSpec: DeployConfigSpec<DeployConfig> = { ...@@ -79,6 +90,15 @@ export const configSpec: DeployConfigSpec<DeployConfig> = {
optimistInviterName: { optimistInviterName: {
type: 'string', type: 'string',
}, },
optimistAllowlistAllowlistAttestor: {
type: 'address',
},
optimistAllowlistCoinbaseQuestAttestor: {
type: 'address',
},
l2ProxyOwnerAddress: { l2ProxyOwnerAddress: {
type: 'address', type: 'address',
}, },
......
import assert from 'assert'
import { ethers, utils } from 'ethers'
const { getAddress } = utils
type ProxyConfig = {
targetImplAddress: string
targetProxyOwnerAddress: string
postUpgradeCallCalldata?: string
}
// Sets up the newly deployed proxy contract such that:
// 1. The proxy's implementation is set to the target implementation
// 2. The proxy's admin is set to the target proxy owner
//
// If the values are set correctly already, it makes no transactions.
const setupProxyContract = async (
proxyContract: ethers.Contract,
proxyOwnerSigner: ethers.Signer,
{
targetImplAddress,
targetProxyOwnerAddress,
postUpgradeCallCalldata,
}: ProxyConfig
) => {
const currentAdmin = await proxyContract
.connect(ethers.constants.AddressZero)
.callStatic.admin()
// The proxy owner signer needs to be the current admin, otherwise we don't have permission
// to update the implmentation or admin
const proxyOwnerSignerAddress = await proxyOwnerSigner.getAddress()
assert(
proxyOwnerSignerAddress === currentAdmin,
'proxyOwnerSigner is not the admin'
)
// 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 currentImplementation = await proxyContract
.connect(ethers.constants.AddressZero)
.callStatic.implementation()
console.log(`implementation currently set to ${currentImplementation}`)
if (getAddress(currentImplementation) !== getAddress(targetImplAddress)) {
// 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 correct contract')
console.log(`Setting implementation to ${targetImplAddress}`)
let tx: ethers.providers.TransactionResponse
if (!postUpgradeCallCalldata) {
console.log(
'postUpgradeCallCalldata is not provided. Using Proxy.upgrade()'
)
// Point the proxy to the target implementation
tx = await proxyContract
.connect(proxyOwnerSigner)
.upgradeTo(targetImplAddress)
} else {
console.log(
'postUpgradeCallCalldata is provided. Using Proxy.upgradeAndCall()'
)
// Point the proxy to the target implementation,
// and call function in the proxy's context
tx = await proxyContract
.connect(proxyOwnerSigner)
.upgradeToAndCall(targetImplAddress, postUpgradeCallCalldata)
}
const receipt = await tx.wait()
console.log(`implementation set in ${receipt.transactionHash}`)
} else {
console.log(`implementation already set correctly to ${targetImplAddress}`)
}
console.log(`admin set to ${currentAdmin}`)
if (getAddress(currentAdmin) !== getAddress(targetProxyOwnerAddress)) {
// 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('detected admin is not set correctly')
console.log(`Setting admin to ${targetProxyOwnerAddress}`)
// change admin to the l2ProxyOwnerAddress
const tx = await proxyContract
.connect(proxyOwnerSigner)
.changeAdmin(targetProxyOwnerAddress)
const receipt = await tx.wait()
console.log(`admin set in ${receipt.transactionHash}`)
} else {
console.log(`admin already set correctly to ${targetProxyOwnerAddress}`)
}
const updatedImplementation = await proxyContract
.connect(ethers.constants.AddressZero)
.callStatic.implementation()
const updatedAdmin = await proxyContract
.connect(ethers.constants.AddressZero)
.callStatic.admin()
assert(
getAddress(updatedAdmin) === getAddress(targetProxyOwnerAddress),
'Something went wrong - admin not set correctly after transaction'
)
assert(
getAddress(updatedImplementation) === getAddress(targetImplAddress),
'Something went wrong - implementation not set correctly after transaction'
)
console.log(
`Proxy at ${proxyContract.address} is set up with implementation: ${updatedImplementation} and admin: ${updatedAdmin}`
)
}
export { setupProxyContract }
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