diff --git a/.eslintrc.cjs b/.eslintrc.cjs index adf690e..6a4a664 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -27,7 +27,6 @@ module.exports = { "never" ], - "no-await-in-loop": "warn", "no-console": "warn", "no-continue": "warn", "no-duplicate-imports": "warn", diff --git a/src/contractFactory.ts b/src/contractFactory.ts index 1ac3030..f7b27d0 100644 --- a/src/contractFactory.ts +++ b/src/contractFactory.ts @@ -21,19 +21,41 @@ const getSkaleManifest = async () => { return manifest as SkaleManifestData; }; +const loadBytesCodes = async (libraryNames: string[]) => { + const byteCodes = new Map(); + + (await Promise. + all(libraryNames.map((libraryName) => (async () => { + const {bytecode} = await artifacts.readArtifact(libraryName); + return [ + libraryName, + bytecode + ]; + })()))).forEach(([ + libraryName, + bytecode + ]) => { + byteCodes.set( + libraryName, + bytecode + ); + }); + return byteCodes; +}; + const updateManifest = async ( manifest: SkaleManifestData, libraries: Map, oldLibraries: {[k: string]: string} ) => { + const byteCodes = await loadBytesCodes(Array.from(libraries.keys())); for (const [ libraryName, libraryAddress ] of libraries.entries()) { - const {bytecode} = await artifacts.readArtifact(libraryName); manifest.libraries[libraryName] = { "address": libraryAddress, - "bytecodeHash": hashBytecode(bytecode) + "bytecodeHash": hashBytecode(byteCodes.get(libraryName) as string) }; } Object.assign( @@ -88,13 +110,14 @@ const getLibrariesToUpgrade = async ( ) => { const librariesToUpgrade = []; const oldLibraries: {[k: string]: string} = {}; - for (const libraryName of getLibrariesNames(linkReferences)) { - const {bytecode} = await artifacts.readArtifact(libraryName); + const librariesNames = getLibrariesNames(linkReferences); + const byteCodes = await loadBytesCodes(librariesNames); + for (const libraryName of librariesNames) { if (manifest.libraries[libraryName] === undefined) { librariesToUpgrade.push(libraryName); } else if ( - hashBytecode(bytecode) !== manifest.libraries[libraryName]. - bytecodeHash + hashBytecode(byteCodes.get(libraryName) as string) !== + manifest.libraries[libraryName].bytecodeHash ) { librariesToUpgrade.push(libraryName); } else { diff --git a/src/deploy.ts b/src/deploy.ts index f4df96d..72eccd1 100644 --- a/src/deploy.ts +++ b/src/deploy.ts @@ -8,22 +8,34 @@ interface LibraryArtifacts { [key: string]: unknown } -const _deployLibrary = async (libraryName: string) => { - const - Library = await ethers.getContractFactory(libraryName); - const library = await Library.deploy(); +const deployLibrary = async (libraryName: string, nonce: number) => { + const Library = await ethers.getContractFactory(libraryName); + const library = await Library.deploy({nonce}); await library.deployed(); return library.address; }; export const deployLibraries = async (libraryNames: string[]) => { + const [deployer] = await ethers.getSigners(); + const nonce = await deployer.getTransactionCount(); const libraries = new Map(); - for (const libraryName of libraryNames) { + + (await Promise.all(libraryNames.map((libraryName, index) => (async () => [ + libraryName, + await deployLibrary( + libraryName, + nonce + index + ) + ])()))).forEach(([ + libraryName, + libraryAddress + ]) => { libraries.set( libraryName, - await _deployLibrary(libraryName) + libraryAddress ); - } + }); + return libraries; }; @@ -101,16 +113,33 @@ const updateManifest = async (libraryArtifacts: LibraryArtifacts) => { const getLibraryArtifacts = async (libraries: Map) => { const libraryArtifacts: LibraryArtifacts = {}; - for (const [ - libraryName, - libraryAddress - ] of libraries.entries()) { + + const getLibraryArtifact = async ( + libraryName: string, + libraryAddress: string + ) => { const {bytecode} = await artifacts.readArtifact(libraryName); - libraryArtifacts[libraryName] = { + return { "address": libraryAddress, - "bytecodeHash": hashBytecode(bytecode) + "bytecodeHash": hashBytecode(bytecode), + libraryName + }; + }; + + for (const libraryArtifact of await Promise. + all(Array.from(libraries.entries()).map(([ + libraryName, + libraryAddress + ]) => getLibraryArtifact( + libraryName, + libraryAddress + )))) { + libraryArtifacts[libraryArtifact.libraryName] = { + "address": libraryArtifact.address, + "bytecodeHash": libraryArtifact.bytecodeHash }; } + return libraryArtifacts; }; diff --git a/src/gnosis-safe.ts b/src/gnosis-safe.ts index 574eb12..c19cba6 100644 --- a/src/gnosis-safe.ts +++ b/src/gnosis-safe.ts @@ -124,20 +124,20 @@ const estimateSafeTransaction = async ( ) => { console.log("Estimate gas"); const safeService = await getSafeService(); - for (const transaction of safeTransactionData as MetaTransactionData[]) { - const estimateResponse = await safeService.estimateSafeTransaction( - safeAddress, - { - "to": transaction.to, - "value": transaction.value, - "data": transaction.data, - "operation": transaction.operation || 0 - } - ); - console.log(chalk.cyan(`Recommend to set gas limit to ${parseInt( - estimateResponse.safeTxGas, - 10 - )}`)); + const gasEstimations = await Promise. + all((safeTransactionData as MetaTransactionData[]). + map((transaction) => safeService.estimateSafeTransaction( + safeAddress, + { + "to": transaction.to, + "value": transaction.value, + "data": transaction.data, + "operation": transaction.operation || 0 + } + ))); + for (const estimateResponse of gasEstimations) { + console.log(chalk.cyan("Recommend to set gas limit" + + ` to ${estimateResponse.safeTxGas}`)); } console.log(chalk.green("Send transaction to gnosis safe")); }; diff --git a/src/submitters/eoa-submitter.ts b/src/submitters/eoa-submitter.ts index 592eeaa..64ce210 100644 --- a/src/submitters/eoa-submitter.ts +++ b/src/submitters/eoa-submitter.ts @@ -8,17 +8,19 @@ export class EoaSubmitter extends Submitter { async submit (transactions: UnsignedTransaction[]) { EoaSubmitter._atomicityWarning(); const [deployer] = await ethers.getSigners(); - for (const transaction of transactions) { - console.log(`Send transaction via ${this.name}`); - const response = await deployer.sendTransaction({ - "to": transaction.to, - "value": transaction.value, - "data": transaction.data - }); - console.log("Waiting for a transaction" + - ` with nonce ${response.nonce}`); - await response.wait(); - console.log("The transaction was sent"); - } + const nonce = await deployer.getTransactionCount(); + console.log(`Send transaction via ${this.name}`); + const responses = + await Promise.all(transactions. + map((transaction, index) => deployer.sendTransaction({ + "to": transaction.to, + "value": transaction.value, + "data": transaction.data, + "nonce": nonce + index + }))); + + console.log("Waiting for transactions"); + await Promise.all(responses.map((response) => response.wait())); + console.log("The transactions were sent"); } } diff --git a/src/submitters/safe-ima-legacy-marionette-submitter.ts b/src/submitters/safe-ima-legacy-marionette-submitter.ts index 6aaf5e4..7b8fa42 100644 --- a/src/submitters/safe-ima-legacy-marionette-submitter.ts +++ b/src/submitters/safe-ima-legacy-marionette-submitter.ts @@ -44,12 +44,9 @@ export class SafeImaLegacyMarionetteSubmitter extends SafeToImaSubmitter { if (transactions.length > 1) { SafeImaLegacyMarionetteSubmitter._atomicityWarning(); } - const transactionsToMarionette = []; - for (const transaction of transactions) { - transactionsToMarionette.push({ - "to": this.marionette.address, - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - "data": await this.marionette.encodeFunctionCall( + const transactionsToMarionette = + (await Promise.all(transactions. + map((transaction) => this.marionette.encodeFunctionCall( transaction.to ? transaction.to : ethers.constants.AddressZero, @@ -59,9 +56,12 @@ export class SafeImaLegacyMarionetteSubmitter extends SafeToImaSubmitter { transaction.data ? transaction.data : "0x" - ) as BytesLike - }); - } + ) as Promise)) + ).map((data) => ({ + data, + "to": this.marionette.address + })); + await super.submit(transactionsToMarionette); } } diff --git a/src/upgrader.ts b/src/upgrader.ts index 09f8609..873c4a8 100644 --- a/src/upgrader.ts +++ b/src/upgrader.ts @@ -30,6 +30,10 @@ interface Target { contractNamesToUpgrade: string[] } +const withoutUndefined = (array: Array) => array. + filter((element) => element !== undefined) as Array; + + export abstract class Upgrader { instance: Instance; @@ -137,13 +141,11 @@ export abstract class Upgrader { console.log("Skip verification"); } else { console.log("Start verification"); - for (const contract of contractsToUpgrade) { - await verify( - contract.name, - contract.implementationAddress, - [] - ); - } + await Promise.all(contractsToUpgrade.map((contract) => verify( + contract.name, + contract.implementationAddress, + [] + ))); } } @@ -171,15 +173,9 @@ export abstract class Upgrader { } private async deployNewImplementations () { - const contractsToUpgrade: ContractToUpgrade[] = []; - for (const contract of this.contractNamesToUpgrade) { - const updatedContract = - await this.deployNewImplementation(contract); - if (updatedContract !== undefined) { - contractsToUpgrade.push(updatedContract); - } - } - return contractsToUpgrade; + const contracts = await Promise.all(this.contractNamesToUpgrade. + map(this.deployNewImplementation)); + return withoutUndefined(contracts); } private async deployNewImplementation (contract: string) { diff --git a/src/verification.ts b/src/verification.ts index d036d55..5a79f03 100644 --- a/src/verification.ts +++ b/src/verification.ts @@ -49,6 +49,30 @@ const verificationAttempt = async ( return false; }; +interface VerificationTarget { + contractName: string; + contractAddress: string; + constructorArguments: object; +} + +const verifyWithRetry = async ( + verificationTarget: VerificationTarget, + attempts: number +) => { + if (attempts > 0) { + if (!await verificationAttempt( + verificationTarget.contractName, + verificationTarget.contractAddress, + verificationTarget.constructorArguments + )) { + await verifyWithRetry( + verificationTarget, + attempts - 1 + ); + } + } +}; + export const verify = async ( contractName: string, contractAddress: string, @@ -60,15 +84,14 @@ export const verify = async ( console.log(chalk.grey(errorMessage)); return; } - for (let retry = 0; retry <= 5; retry += 1) { - if (await verificationAttempt( + await verifyWithRetry( + { contractName, contractAddress, constructorArguments - )) { - break; - } - } + }, + 5 + ); }; export const verifyProxy = async (