From c83524168f553830b90789718b6f86173ea04460 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 24 Oct 2023 17:08:08 -0400 Subject: [PATCH] feat: identify chain from signed tx during broadcast (#92) * feat: identify chain from signed tx during broadcast * lint --- evm/broadcast-transactions.js | 48 ++++++++++++++++++++++------------- evm/deploy-contract.js | 3 ++- evm/governance.js | 11 ++++++-- evm/utils.js | 16 ++++++++++++ 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index b72502ee..c0368563 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -7,32 +7,38 @@ const { providers: { getDefaultProvider }, } = ethers; -const { printError, printInfo, printWarn, prompt, mainProcessor } = require('./utils'); +const { printError, printInfo, printWarn, getConfigByChainId, prompt, loadConfig } = require('./utils'); const { sendTransaction, getSignedTx, storeSignedTx } = require('./sign-utils'); -async function processCommand(_, chain, options) { - const { filePath, rpc } = options; +async function processCommand(config, _, options, file) { + const { rpc } = options; + + const transaction = await getSignedTx(file); + const parsedTx = ethers.utils.parseTransaction(transaction.signedTx); + + const chain = getConfigByChainId(parsedTx.chainId, config); const provider = getDefaultProvider(rpc || chain.rpc); + if (parsedTx.chainId !== transaction.unsignedTx.chainId) { + printError( + `ChainId mismatch: signed tx chain id ${parsedTx.chainId} doesn't match unsigned tx chain id ${transaction.unsignedTx.chainId}`, + ); + return; + } + if ( prompt( - `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green( - options.filePath, - )} on chain ${chalk.green(chain.name)}`, + `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green(file)} on chain ${chalk.green( + chain.name, + )}`, options.yes, ) ) { return; } - if (!filePath) { - throw new Error('FilePath is not provided in user info'); - } - - const transaction = await getSignedTx(filePath); - - if (transaction.status === 'PENDING') { + if (transaction.status !== 'SUCCESS') { printInfo('Broadcasting transaction', JSON.stringify(transaction.unsignedTx, null, 2)); // Send the signed transaction @@ -46,21 +52,30 @@ async function processCommand(_, chain, options) { printError('Error broadcasting tx', error); } - storeSignedTx(filePath, transaction); + storeSignedTx(file, transaction); } else { printWarn('Skipping broadcast, transaction status is', transaction.status); } } async function main(options) { - await mainProcessor(options, processCommand); + const config = loadConfig(options.env); + const { files } = options; + + if (!files || files.length === 0) { + throw new Error('FilePath is not provided in user info'); + } + + for (const file of files) { + await processCommand(config, null, options, file); + } } const program = new Command(); program.name('broadcast-transactions').description('Broadcast all the pending signed transactions of the signer'); -program.addOption(new Option('--filePath ', 'The file where the signed tx are stored').makeOptionMandatory(true)); +program.addOption(new Option('--files [files...]', 'The file where the signed tx are stored').makeOptionMandatory(true)); program.addOption( new Option('-e, --env ', 'environment') .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) @@ -68,7 +83,6 @@ program.addOption( .makeOptionMandatory(true) .env('ENV'), ); -program.addOption(new Option('-n, --chainName ', 'chain names').makeOptionMandatory(true)); program.addOption(new Option('-r, --rpc ', 'The chain rpc')); program.addOption(new Option('-y, --yes', 'skip prompts')); diff --git a/evm/deploy-contract.js b/evm/deploy-contract.js index 1d51f59a..993974b5 100644 --- a/evm/deploy-contract.js +++ b/evm/deploy-contract.js @@ -14,6 +14,7 @@ const { printInfo, printWarn, printError, + copyObject, isString, isNumber, isAddressArray, @@ -258,7 +259,7 @@ async function processCommand(config, chain, options) { printInfo('Pre-deploy Contract bytecode hash', predeployCodehash); const constructorArgs = await getConstructorArgs(contractName, chain, wallet, options); - const gasOptions = JSON.parse(JSON.stringify(contractConfig.gasOptions || chain.gasOptions || {})); + const gasOptions = copyObject(contractConfig.gasOptions || chain.gasOptions || {}); // Some chains require a gas adjustment if (env === 'mainnet' && !gasOptions.gasPrice && (chain.name === 'Fantom' || chain.name === 'Binance' || chain.name === 'Polygon')) { diff --git a/evm/governance.js b/evm/governance.js index 034c4bbd..99e96b72 100644 --- a/evm/governance.js +++ b/evm/governance.js @@ -12,6 +12,7 @@ const { const { Command, Option } = require('commander'); const { printInfo, + copyObject, printWalletInfo, isValidTimeFormat, dateToEta, @@ -62,7 +63,7 @@ async function getGatewaySetupParams(governance, gateway, contracts, options) { } async function processCommand(_, chain, options) { - const { contractName, address, action, date, privateKey, yes } = options; + const { env, contractName, address, action, date, privateKey, yes } = options; const contracts = chain.contracts; const contractConfig = contracts[contractName]; @@ -103,7 +104,13 @@ async function processCommand(_, chain, options) { const governance = new Contract(governanceAddress, IGovernance.abi, wallet); - const gasOptions = contractConfig?.gasOptions || chain?.gasOptions || { gasLimit: 5e6 }; + const gasOptions = copyObject(contractConfig?.gasOptions || chain?.gasOptions || { gasLimit: 5e6 }); + + // Some chains require a gas adjustment + if (env === 'mainnet' && !gasOptions.gasPrice && (chain.name === 'Fantom' || chain.name === 'Binance' || chain.name === 'Polygon')) { + gasOptions.gasPrice = Math.floor((await provider.getGasPrice()) * 1.4); + } + printInfo('Gas options', JSON.stringify(gasOptions, null, 2)); printInfo('Proposal Action', action); diff --git a/evm/utils.js b/evm/utils.js index 61a06323..97b0ad17 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -698,6 +698,10 @@ function isValidAddress(address, allowZeroAddress) { return isAddress(address); } +function copyObject(obj) { + return JSON.parse(JSON.stringify(obj)); +} + const mainProcessor = async (options, processCommand, save = true, catchErr = false) => { if (!options.env) { throw new Error('Environment was not provided'); @@ -763,12 +767,23 @@ const prompt = (question, yes = false) => { return answer !== 'y'; }; +function getConfigByChainId(chainId, config) { + for (const chain of Object.values(config.chains)) { + if (chain.chainId === chainId) { + return chain; + } + } + + throw new Error(`Chain with chainId ${chainId} not found in the config`); +} + module.exports = { deployCreate, deployCreate2, deployCreate3, deployContract, writeJSON, + copyObject, httpGet, printObj, printLog, @@ -791,6 +806,7 @@ module.exports = { getProxy, getEVMBatch, getEVMAddresses, + getConfigByChainId, sleep, loadConfig, saveConfig,