From 936ca5ff29b20be4df26e14008d0a3817dd81852 Mon Sep 17 00:00:00 2001 From: Dmytro Stebaiev Date: Thu, 14 Sep 2023 17:12:49 +0300 Subject: [PATCH] Remove long functions --- .eslintrc.cjs | 1 - src/deploy.ts | 56 ++++---- src/gnosis-safe.ts | 96 +++++++------ src/multiSend.ts | 39 ++--- src/submitters/auto-submitter.ts | 4 + src/types/ContractToUpgrade.ts | 5 + src/upgrader.ts | 240 +++++++++++++++++++------------ 7 files changed, 262 insertions(+), 179 deletions(-) create mode 100644 src/types/ContractToUpgrade.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 34da299..9e53ffc 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -27,7 +27,6 @@ module.exports = { "never" ], - "max-lines-per-function": "warn", "max-params": "warn", "max-statements": "warn", "multiline-comment-style": "warn", diff --git a/src/deploy.ts b/src/deploy.ts index a127333..da3d25a 100644 --- a/src/deploy.ts +++ b/src/deploy.ts @@ -4,6 +4,10 @@ import {promises as fs} from "fs"; import {SkaleManifestData} from "./types/SkaleManifestData"; import {Artifact} from "hardhat/types"; +interface LibraryArtifacts { + [key: string]: unknown +} + const _deployLibrary = async (libraryName: string) => { const Library = await ethers.getContractFactory(libraryName); @@ -69,6 +73,32 @@ export const getManifestFile = async function getManifestFile () { return (await Manifest.forNetwork(ethers.provider)).file; }; +const updateManifest = async (libraryArtifacts: LibraryArtifacts) => { + const manifest = JSON.parse(await fs.readFile( + await getManifestFile(), + "utf-8" + )) as SkaleManifestData; + if (manifest.libraries === undefined) { + Object.assign( + manifest, + {"libraries": libraryArtifacts} + ); + } else { + Object.assign( + libraryArtifacts, + manifest.libraries + ); + } + await fs.writeFile( + await getManifestFile(), + JSON.stringify( + manifest, + null, + 4 + ) + ); +}; + export const getContractFactory = async (contract: string) => { const {linkReferences} = await artifacts.readArtifact(contract); if (!Object.keys(linkReferences).length) { @@ -82,7 +112,7 @@ export const getContractFactory = async (contract: string) => { } const libraries = await deployLibraries(libraryNames); - const libraryArtifacts: { [key: string]: unknown } = {}; + const libraryArtifacts: LibraryArtifacts = {}; for (const [ libraryName, libraryAddress @@ -94,29 +124,7 @@ export const getContractFactory = async (contract: string) => { }; } - const manifest = JSON.parse(await fs.readFile( - await getManifestFile(), - "utf-8" - )) as SkaleManifestData; - if (manifest.libraries === undefined) { - Object.assign( - manifest, - {"libraries": libraryArtifacts} - ); - } else { - Object.assign( - libraryArtifacts, - manifest.libraries - ); - } - await fs.writeFile( - await getManifestFile(), - JSON.stringify( - manifest, - null, - 4 - ) - ); + await updateManifest(libraryArtifacts); return await getLinkedContractFactory( contract, diff --git a/src/gnosis-safe.ts b/src/gnosis-safe.ts index 553f508..49febf7 100644 --- a/src/gnosis-safe.ts +++ b/src/gnosis-safe.ts @@ -26,30 +26,35 @@ const URLS = { } }; +const defaultOptions = { + // Max gas to use in the transaction + "safeTxGas": "0", + + // Gas costs not related to the transaction execution + // (signature check, refund payment...) + "baseGas": "0", + + // Gas price used for the refund calculation + "gasPrice": "0", + + /* Token address (hold by the Safe) + * to be used as a refund to the sender, + * if `null` is Ether + */ + "gasToken": ethers.constants.AddressZero, + + // Address of receiver of gas payment (or `null` if tx.origin) + "refundReceiver": ethers.constants.AddressZero +}; + // Public functions export const createMultiSendTransaction = async ( safeAddress: string, transactions: UnsignedTransaction[] ) => { - const safeTransactionData: MetaTransactionData[] = []; - for (const transaction of transactions) { - safeTransactionData.push({ - "to": transaction.to - ? transaction.to - : ethers.constants.AddressZero, - "data": transaction.data - ? transaction.data.toString() - : "0x", - "value": transaction.value - ? transaction.value.toString() - : "0", - "operation": 0 - }); - } - - const - safeService = await getSafeService(); + const safeTransactionData = getSafeTransactionData(transactions); + const safeService = await getSafeService(); const nonce = await safeService.getNextNonce(safeAddress); console.log( "Will send tx to Gnosis with nonce", @@ -57,33 +62,19 @@ export const createMultiSendTransaction = async ( ); const options = { - // Max gas to use in the transaction - "safeTxGas": "0", - - // Gas costs not related to the transaction execution - // (signature check, refund payment...) - "baseGas": "0", - - // Gas price used for the refund calculation - "gasPrice": "0", - - /* Token address (hold by the Safe) - * to be used as a refund to the sender, - * if `null` is Ether - */ - "gasToken": ethers.constants.AddressZero, - - // Address of receiver of gas payment (or `null` if tx.origin) - "refundReceiver": ethers.constants.AddressZero, - - // Nonce of the Safe, - // Transaction cannot be executed until - // Safe's nonce is not equal to this nonce - nonce + ...defaultOptions, + ...{ + // Nonce of the Safe, + // Transaction cannot be executed until + // Safe's nonce is not equal to this nonce + nonce + } }; const ethAdapter = await getEthAdapter(); - const safeSdk = await Safe.create({ethAdapter, - safeAddress}); + const safeSdk = await Safe.create({ + ethAdapter, + safeAddress + }); const safeTransaction = await safeSdk.createTransaction({ safeTransactionData, options @@ -102,6 +93,25 @@ export const createMultiSendTransaction = async ( // Private functions +const getSafeTransactionData = (transactions: UnsignedTransaction[]) => { + const safeTransactionData: MetaTransactionData[] = []; + for (const transaction of transactions) { + safeTransactionData.push({ + "to": transaction.to + ? transaction.to + : ethers.constants.AddressZero, + "data": transaction.data + ? transaction.data.toString() + : "0x", + "value": transaction.value + ? transaction.value.toString() + : "0", + "operation": 0 + }); + } + return safeTransactionData; +}; + const estimateSafeTransaction = async ( safeAddress: string, safeTransactionData: SafeTransactionDataPartial | MetaTransactionData[] diff --git a/src/multiSend.ts b/src/multiSend.ts index 3ee9546..b150297 100644 --- a/src/multiSend.ts +++ b/src/multiSend.ts @@ -5,6 +5,27 @@ const padWithZeros = ( targetLength: number ) => ("0".repeat(targetLength) + value).slice(-targetLength); +const getOperationBytes = (operation: 0 | 1) => { + if (operation === 0) { + return "00"; + } else if (operation === 1) { + return "01"; + } + throw Error("Operation has an incorrect value"); +}; + +const getToBytes = (to: string) => { + let _to = to; + if (to.startsWith("0x")) { + _to = _to.slice(2); + } + _to = padWithZeros( + _to, + 20 * 2 + ); + return _to; +}; + export const encodeTransaction = ( /* Operation as a uint8 with 0 for a call * or 1 for a delegatecall (=> 1 byte) @@ -20,23 +41,9 @@ export const encodeTransaction = ( // Data as bytes. data: string ) => { - let _operation = ""; - if (operation === 0) { - _operation = "00"; - } else if (operation === 1) { - _operation = "01"; - } else { - throw Error("Operation has an incorrect value"); - } + const _operation = getOperationBytes(operation); - let _to = to; - if (to.startsWith("0x")) { - _to = _to.slice(2); - } - _to = padWithZeros( - _to, - 20 * 2 - ); + const _to = getToBytes(to); const _value = padWithZeros( BigNumber.from(value).toHexString(). diff --git a/src/submitters/auto-submitter.ts b/src/submitters/auto-submitter.ts index ee162ff..7cb333b 100644 --- a/src/submitters/auto-submitter.ts +++ b/src/submitters/auto-submitter.ts @@ -33,6 +33,10 @@ export class AutoSubmitter extends Submitter { } console.log("Owner is a contract"); + return AutoSubmitter.getSubmitterForContractOwner(owner); + } + + private static async getSubmitterForContractOwner (owner: string) { if (ethers.utils.getAddress(owner) === ethers.utils.getAddress(MARIONETTE_ADDRESS)) { console.log("Marionette owner is detected"); diff --git a/src/types/ContractToUpgrade.ts b/src/types/ContractToUpgrade.ts new file mode 100644 index 0000000..a2c054a --- /dev/null +++ b/src/types/ContractToUpgrade.ts @@ -0,0 +1,5 @@ +export interface ContractToUpgrade { + proxyAddress: string, + implementationAddress: string, + name: string +} diff --git a/src/upgrader.ts b/src/upgrader.ts index 82e5783..4c2b581 100644 --- a/src/upgrader.ts +++ b/src/upgrader.ts @@ -20,6 +20,9 @@ import {Submitter} from "./submitters/submitter"; import {SkaleManifestData} from "./types/SkaleManifestData"; import {AutoSubmitter} from "./submitters/auto-submitter"; import {Instance} from "@skalenetwork/skale-contracts-ethers-v5"; +import {LinkReferences} from "hardhat/types"; +import {ContractToUpgrade} from "./types/ContractToUpgrade"; + export abstract class Upgrader { instance: Instance; @@ -69,24 +72,8 @@ export abstract class Upgrader { async upgrade () { const proxyAdmin = await getManifestAdmin(hre) as unknown as ProxyAdmin; - let deployedVersion = await this.getDeployedVersion(); const version = await getVersion(); - if (deployedVersion) { - if (!deployedVersion.includes("-")) { - deployedVersion += "-stable.0"; - } - if (deployedVersion !== this.targetVersion) { - const cannotUpgradeMessage = - `This script can't upgrade version ${deployedVersion}` + - ` to ${version}`; - console.log(chalk.red(cannotUpgradeMessage)); - process.exit(1); - } - } else { - const cannotCheckMessage = - `Can't check currently deployed version of ${this.projectName}`; - console.log(chalk.yellow(cannotCheckMessage)); - } + await this.checkVersion(version); console.log(`Will mark updated version as ${version}`); if (this.deployNewContracts !== undefined) { @@ -94,12 +81,78 @@ export abstract class Upgrader { await this.deployNewContracts(); } - // Deploy new implementations - const contractsToUpgrade: { - proxyAddress: string, - implementationAddress: string, - name: string - }[] = []; + const contractsToUpgrade = await this.deployNewImplementations(); + + this.switchToNewImplementations( + contractsToUpgrade, + proxyAdmin + ); + + if (this.initialize !== undefined) { + await this.initialize(); + } + + // Write version + await this.setVersion(version); + + await fs.writeFile( + `data/transactions-${version}-${network.name}.json`, + JSON.stringify( + this.transactions, + null, + 4 + ) + ); + + await this.submitter.submit(this.transactions); + + await Upgrader.verify(contractsToUpgrade); + + console.log("Done"); + } + + // Private + + private static async verify (contractsToUpgrade: ContractToUpgrade[]) { + if (process.env.NO_VERIFY) { + console.log("Skip verification"); + } else { + console.log("Start verification"); + for (const contract of contractsToUpgrade) { + await verify( + contract.name, + contract.implementationAddress, + [] + ); + } + } + } + + private switchToNewImplementations ( + contractsToUpgrade: ContractToUpgrade[], + proxyAdmin: ProxyAdmin + ) { + for (const contract of contractsToUpgrade) { + const infoMessage = + `Prepare transaction to upgrade ${contract.name}` + + ` at ${contract.proxyAddress}` + + ` to ${contract.implementationAddress}`; + console.log(chalk.yellowBright(infoMessage)); + this.transactions.push({ + "to": proxyAdmin.address, + "data": proxyAdmin.interface.encodeFunctionData( + "upgrade", + [ + contract.proxyAddress, + contract.implementationAddress + ] + ) + }); + } + } + + private async deployNewImplementations () { + const contractsToUpgrade: ContractToUpgrade[] = []; for (const contract of this.contractNamesToUpgrade) { const contractFactory = await Upgrader.getContractFactoryAndUpdateManifest(contract); @@ -130,62 +183,37 @@ export abstract class Upgrader { console.log(chalk.gray(`Contract ${contract} is up to date`)); } } + return contractsToUpgrade; + } - // Switch proxies to new implementations - for (const contract of contractsToUpgrade) { - const infoMessage = - `Prepare transaction to upgrade ${contract.name}` + - ` at ${contract.proxyAddress}` + - ` to ${contract.implementationAddress}`; - console.log(chalk.yellowBright(infoMessage)); - this.transactions.push({ - "to": proxyAdmin.address, - "data": proxyAdmin.interface.encodeFunctionData( - "upgrade", - [ - contract.proxyAddress, - contract.implementationAddress - ] - ) - }); - } - - if (this.initialize !== undefined) { - await this.initialize(); + private async getNormalizedDeployedVersion () { + const deployedVersion = await this.getDeployedVersion(); + if (deployedVersion) { + if (!deployedVersion.includes("-")) { + return `${deployedVersion}-stable.0`; + } + return deployedVersion; } + return deployedVersion; + } - // Write version - await this.setVersion(version); - - await fs.writeFile( - `data/transactions-${version}-${network.name}.json`, - JSON.stringify( - this.transactions, - null, - 4 - ) - ); - - await this.submitter.submit(this.transactions); - - if (process.env.NO_VERIFY) { - console.log("Skip verification"); - } else { - console.log("Start verification"); - for (const contract of contractsToUpgrade) { - await verify( - contract.name, - contract.implementationAddress, - [] - ); + private async checkVersion (version: string) { + const deployedVersion = await this.getNormalizedDeployedVersion(); + if (deployedVersion) { + if (deployedVersion !== this.targetVersion) { + const cannotUpgradeMessage = + `This script can't upgrade version ${deployedVersion}` + + ` to ${version}`; + console.log(chalk.red(cannotUpgradeMessage)); + process.exit(1); } + } else { + const cannotCheckMessage = + `Can't check currently deployed version of ${this.projectName}`; + console.log(chalk.yellow(cannotCheckMessage)); } - - console.log("Done"); } - // Private - private static async getContractFactoryAndUpdateManifest (contract: string) { const {linkReferences} = await artifacts.readArtifact(contract); @@ -193,40 +221,31 @@ export abstract class Upgrader { await getManifestFile(), "utf-8" )) as SkaleManifestData; + if (manifest.libraries === undefined) { + manifest.libraries = {}; + } if (!Object.keys(linkReferences).length) { return await ethers.getContractFactory(contract); } - const librariesToUpgrade = []; - const oldLibraries: {[k: string]: string} = {}; - if (manifest.libraries === undefined) { - manifest.libraries = {}; - } - for (const key of Object.keys(linkReferences)) { - const libraryName = Object.keys(linkReferences[key])[0]; - const {bytecode} = await artifacts.readArtifact(libraryName); - if (manifest.libraries[libraryName] === undefined) { - librariesToUpgrade.push(libraryName); - continue; - } - const libraryBytecodeHash = - manifest.libraries[libraryName].bytecodeHash; - if (hashBytecode(bytecode) !== libraryBytecodeHash) { - librariesToUpgrade.push(libraryName); - } else { - oldLibraries[libraryName] = - manifest.libraries[libraryName].address; - } - } + const { + librariesToUpgrade, + oldLibraries + } = await Upgrader.getLibrariesToUpgrade( + manifest, + linkReferences + ); const libraries = await deployLibraries(librariesToUpgrade); for (const [ libraryName, libraryAddress ] of libraries.entries()) { const {bytecode} = await artifacts.readArtifact(libraryName); - manifest.libraries[libraryName] = {"address": libraryAddress, - "bytecodeHash": hashBytecode(bytecode)}; + manifest.libraries[libraryName] = { + "address": libraryAddress, + "bytecodeHash": hashBytecode(bytecode) + }; } Object.assign( libraries, @@ -245,4 +264,35 @@ export abstract class Upgrader { libraries ); } + + private static async getLibrariesToUpgrade ( + manifest: SkaleManifestData, + linkReferences: LinkReferences + ) { + const librariesToUpgrade = []; + const oldLibraries: {[k: string]: string} = {}; + if (manifest.libraries === undefined) { + manifest.libraries = {}; + } + for (const key of Object.keys(linkReferences)) { + const libraryName = Object.keys(linkReferences[key])[0]; + const {bytecode} = await artifacts.readArtifact(libraryName); + if (manifest.libraries[libraryName] === undefined) { + librariesToUpgrade.push(libraryName); + continue; + } + const libraryBytecodeHash = + manifest.libraries[libraryName].bytecodeHash; + if (hashBytecode(bytecode) !== libraryBytecodeHash) { + librariesToUpgrade.push(libraryName); + } else { + oldLibraries[libraryName] = + manifest.libraries[libraryName].address; + } + } + return { + librariesToUpgrade, + oldLibraries + }; + } }