Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

feat: add ToposCore upgrade script #123

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions scripts/upgrade-topos-core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import {
AbstractProvider,
BytesLike,
ContractTransactionResponse,
Interface,
isHexString,
JsonRpcProvider,
Wallet,
} from 'ethers'

import { ToposCore__factory } from '../typechain-types/factories/contracts/topos-core/ToposCore__factory'
import { CodeHash__factory } from '../typechain-types/factories/contracts/topos-core/CodeHash__factory'

import toposCoreJSON from '../artifacts/contracts/topos-core/ToposCore.sol/ToposCore.json'

import {
Arg,
ContractOutputJSON,
deployContractConstant,
predictContractConstant,
} from './const-addr-deployer'

/**
*
* This script performs the following tasks:
* 1. Deploys the `ToposCore` contract (using the `ConstAddressDeployer`)
* & upgrades it to the latest version
JDawg287 marked this conversation as resolved.
Show resolved Hide resolved
* 2. Does not modify the `ToposCoreProxy` contract
* 3. Does not deploy the `TokenDeployer` or `ERC20Messaging` contract
*/
const main = async function (...args: string[]) {
const [
_providerEndpoint,
_sequencerPrivateKey,
_toposCoreProxyAddress,
_gasLimit,
] = args
const provider = new JsonRpcProvider(_providerEndpoint)

// this is used to deploy a new contract
const toposDeployerPrivateKey = sanitizeHexString(
process.env.PRIVATE_KEY || ''
)
if (!isHexString(toposDeployerPrivateKey, 32)) {
console.error(
'ERROR: Please provide a valid toposDeployer private key! (PRIVATE_KEY)'
)
process.exit(1)
}

// sequencerPrivateKey belongs to the owner of the ToposCore contract
const sequencerPrivateKey = sanitizeHexString(_sequencerPrivateKey || '')
if (!isHexString(sequencerPrivateKey, 32)) {
console.error('ERROR: Please provide a valid sequencer private key!')
process.exit(1)
}

const toposCoreSalt = process.env.TOPOS_CORE_SALT
verifySalt('ToposCore', 'TOPOS_CORE_SALT', toposCoreSalt)

const toposDeployerWallet = new Wallet(toposDeployerPrivateKey, provider)
const sequencerWallet = new Wallet(sequencerPrivateKey, provider)

const upgradedToposCoreAddress = await processContract(
toposDeployerWallet,
toposCoreJSON,
toposCoreSalt!,
[],
4_000_000
)

const codeHash = await new CodeHash__factory(toposDeployerWallet).deploy()
await codeHash.waitForDeployment()
const toposCoreCodeHash = await codeHash.getCodeHash(upgradedToposCoreAddress)

const toposCoreConnectedToSequencer = ToposCore__factory.connect(
_toposCoreProxyAddress,
sequencerWallet
)
const currentToposCoreAddress =
await toposCoreConnectedToSequencer.implementation()
console.log(`Current ToposCore address: ${currentToposCoreAddress}`)

const upgradeTx = await toposCoreConnectedToSequencer.upgrade(
upgradedToposCoreAddress,
toposCoreCodeHash,
{
gasLimit: _gasLimit ? BigInt(_gasLimit) : 5_000_000,
}
)
await processTransaction(provider, upgradeTx, [
new Interface(toposCoreJSON.abi),
])
console.log(
`ToposCore successfully upgraded to ${await toposCoreConnectedToSequencer.implementation()}`
)
}

const processContract = async function (
toposDeployerWallet: Wallet,
contractJson: ContractOutputJSON,
salt: string,
args: Arg[] = [],
gasLimit: number | null = null
) {
const predictedContractAddress = await predictContractConstant(
toposDeployerWallet,
contractJson,
salt,
args
).catch((error) => {
console.error(error)
process.exit(1)
})

const codeAtPredictedAddress = await toposDeployerWallet?.provider?.getCode(
predictedContractAddress
)

const thereIsCodeAtAddress = codeAtPredictedAddress !== '0x'

if (thereIsCodeAtAddress) {
return predictedContractAddress
} else {
const newContractAddress = await deployContractConstant(
toposDeployerWallet,
contractJson,
salt,
args,
gasLimit
)
.then(async (contract) => await contract.getAddress())
.catch((error) => {
console.error(error)
process.exit(1)
})

return newContractAddress
}
}

async function processTransaction(
provider: AbstractProvider,
inputTx: ContractTransactionResponse,
interfaceList: Interface[]
) {
try {
const receipt = await inputTx.wait()
if (receipt?.status === 1) {
console.log(`Transaction executed successfully!`)
}
} catch (error) {
let errorMessage
const txResponse = await provider.getTransaction(inputTx.hash)
if (txResponse === null) {
errorMessage = `Transaction with hash ${inputTx.hash} not found.`
} else {
try {
await provider.call(txResponse)
} catch (customError: unknown) {
if (
typeof customError === 'object' &&
customError !== null &&
'data' in customError
) {
for (const iface of interfaceList) {
const data = customError.data as BytesLike
const decodedCustomError = iface.parseError(data)
if (decodedCustomError !== null) {
errorMessage = `Transaction failed with custom error: ${
decodedCustomError!.name
} ${JSON.stringify({ args: decodedCustomError!.args })}`
}
}
} else {
errorMessage = (customError as Error).toString()
}
}
}
console.error(errorMessage)
process.exit(1)
}
}

const sanitizeHexString = function (hexString: string) {
return hexString.startsWith('0x') ? hexString : `0x${hexString}`
}

const verifySalt = function (
contractName: string,
envVarName: string,
localVar: string | undefined
) {
if (!localVar) {
console.error(
`ERROR: Please provide a salt for ${contractName}! (${envVarName})`
)
process.exit(1)
}
}

const args = process.argv.slice(2)
main(...args)
Loading