From bc7d6c0002484d1d9eba6edb5005a212d574d48e Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 7 Nov 2023 01:26:42 -0500 Subject: [PATCH] feat: script optimizations (#99) * feat: script optimizations * feat: address comments * feat: address comments * feat: add to gh action test * feat: test different deployment methods in gh action * feat: address comments --------- Co-authored-by: Dean Amiel --- .github/workflows/test.yaml | 45 +++++++++- evm/balances.js | 13 +-- evm/broadcast-transactions.js | 28 +++---- evm/check-wallet-balance.js | 29 +++---- evm/cli-utils.js | 64 +++++++++++++++ evm/deploy-const-address-deployer.js | 16 +--- evm/deploy-contract.js | 77 +++++++---------- evm/deploy-create3-deployer.js | 18 +--- evm/deploy-gateway-v4.3.x.js | 16 +--- evm/deploy-gateway-v6.2.x.js | 18 +--- evm/deploy-its.js | 15 +--- evm/deploy-upgradable.js | 53 +++++------- evm/execute-contract.js | 118 +++++++++++---------------- evm/gateway.js | 89 ++++++++++---------- evm/governance.js | 99 +++++++++++----------- evm/min-deposit-proposal.js | 12 +-- evm/multisig.js | 87 ++++++++++---------- evm/operators.js | 52 ++++++------ evm/ownership.js | 59 ++++++-------- evm/remove-info.js | 27 +++--- evm/send-tokens.js | 14 +--- evm/update-nonces.js | 36 ++++---- evm/update-static-gas-options.js | 28 +++---- evm/utils.js | 79 ++++++++++++++++++ evm/verify-contract.js | 13 +-- 25 files changed, 557 insertions(+), 548 deletions(-) create mode 100644 evm/cli-utils.js diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 676f7896..a4751b6f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -36,7 +36,28 @@ jobs: "chainId": 31337, "rpc": "http://127.0.0.1:8545", "tokenSymbol": "TEST", - "contracts": {} + "contracts": { + "InterchainGovernance": { + "minimumTimeDelay": 3600 + }, + "AxelarServiceGovernance": { + "minimumTimeDelay": 3600, + "cosigners": [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" + ], + "threshold": 2 + }, + "Multisig": { + "signers": [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" + ], + "threshold": 2 + } + } } } }' > ./axelar-chains-config/info/local.json @@ -52,13 +73,31 @@ jobs: run: cat ./axelar-chains-config/info/local.json - name: Deploy ConstAddressDeployer - run: node evm/deploy-contract.js -a ../node_modules/@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/ -c ConstAddressDeployer -m create -y + run: node evm/deploy-contract.js -c ConstAddressDeployer -m create -y - name: Deploy Create3Deployer - run: node evm/deploy-contract.js -a ../node_modules/@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/ -c Create3Deployer -m create2 -y + run: node evm/deploy-contract.js -c Create3Deployer -m create2 -y - name: Deploy AxelarGateway run: node evm/deploy-gateway-v6.2.x.js -m create3 -s "AxelarGateway v6.2" -g 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -m 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --keyID 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 -y - name: Call Method on Gateway run: node evm/gateway.js --action callContract --destinationChain test2 -y + + - name: Deploy InterchainGovernance using create + run: node evm/deploy-contract.js -c InterchainGovernance -m create -y + + - name: Deploy InterchainGovernance using create2 + run: node evm/deploy-contract.js -c InterchainGovernance -m create2 -y + + - name: Deploy InterchainGovernance using create3 + run: node evm/deploy-contract.js -c InterchainGovernance -m create3 -y + + - name: Deploy AxelarServiceGovernance using create3 + run: node evm/deploy-contract.js -c AxelarServiceGovernance -m create3 -y + + - name: Deploy Multisig using create3 + run: node evm/deploy-contract.js -c Multisig -m create3 -y + + - name: Deploy Operators using create3 + run: node evm/deploy-contract.js -c Operators -m create3 -y diff --git a/evm/balances.js b/evm/balances.js index c02b0ce9..3f125bbb 100644 --- a/evm/balances.js +++ b/evm/balances.js @@ -4,8 +4,9 @@ require('dotenv').config(); const { ethers } = require('hardhat'); const { getDefaultProvider } = ethers; -const { Command, Option } = require('commander'); +const { Command } = require('commander'); const { mainProcessor, printWalletInfo, printInfo } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { getWallet } = require('./sign-utils'); async function processCommand(_, chain, options) { @@ -26,15 +27,7 @@ if (require.main === module) { program.name('balances').description('Display balance of the wallet on specified chains.'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true).env('CHAINS')); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); + addBaseOptions(program); program.action((options) => { main(options); diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index c0368563..6b3cbead 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -8,6 +8,7 @@ const { } = ethers; const { printError, printInfo, printWarn, getConfigByChainId, prompt, loadConfig } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { sendTransaction, getSignedTx, storeSignedTx } = require('./sign-utils'); async function processCommand(config, _, options, file) { @@ -71,23 +72,18 @@ async function main(options) { } } -const program = new Command(); +if (require.main === module) { + const program = new Command(); -program.name('broadcast-transactions').description('Broadcast all the pending signed transactions of the signer'); + program.name('broadcast-transactions').description('Broadcast all the pending signed transactions of the signer'); -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']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-r, --rpc ', 'The chain rpc')); -program.addOption(new Option('-y, --yes', 'skip prompts')); + program.addOption(new Option('--files [files...]', 'The file where the signed tx are stored').makeOptionMandatory(true)); + addBaseOptions(program, { ignoreChainNames: true }); + program.addOption(new Option('-r, --rpc ', 'The chain rpc')); -program.action((options) => { - main(options); -}); + program.action((options) => { + main(options); + }); -program.parse(); + program.parse(); +} diff --git a/evm/check-wallet-balance.js b/evm/check-wallet-balance.js index 8c0553a3..011c5b25 100644 --- a/evm/check-wallet-balance.js +++ b/evm/check-wallet-balance.js @@ -5,6 +5,7 @@ const { ethers } = require('hardhat'); const { getDefaultProvider, BigNumber } = ethers; const { printError, mainProcessor } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { getNonceFileData } = require('./sign-utils'); async function processCommand(_, chain, options) { @@ -47,23 +48,19 @@ async function main(options) { await mainProcessor(options, processCommand); } -const program = new Command(); +if (require.main === module) { + const program = new Command(); -program.name('check-wallet-balance').description('Before offline signing checks if each signer has minimum required wallet balance'); + program.name('check-wallet-balance').description('Before offline signing checks if each signer has minimum required wallet balance'); -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); -program.addOption(new Option('--addresses ', 'The Array of addresses for which the balance to check').env('ADDRESSES')); + addBaseOptions(program, { ignorePrivateKey: true }); -program.action((options) => { - main(options); -}); + program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); + program.addOption(new Option('--addresses ', 'The Array of addresses for which the balance to check').env('ADDRESSES')); -program.parse(); + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/cli-utils.js b/evm/cli-utils.js new file mode 100644 index 00000000..6ce17f32 --- /dev/null +++ b/evm/cli-utils.js @@ -0,0 +1,64 @@ +'use strict'; + +const { Option } = require('commander'); + +const addBaseOptions = (program, options = {}) => { + program.addOption( + new Option('-e, --env ', 'environment') + .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) + .default('testnet') + .makeOptionMandatory(true) + .env('ENV'), + ); + program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); + + if (!options.ignoreChainNames) { + program.addOption( + new Option('-n, --chainNames ', 'chains to run the script over').makeOptionMandatory(true).env('CHAINS'), + ); + program.addOption(new Option('--skipChains ', 'chains to skip over')); + } + + if (!options.ignorePrivateKey) { + program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); + } + + if (options.address) { + program.addOption(new Option('-a, --address
', 'override address')); + } + + return program; +}; + +const addExtendedOptions = (program, options = {}) => { + addBaseOptions(program, options); + + program.addOption(new Option('-v, --verify', 'verify the deployed contract on the explorer').env('VERIFY')); + + if (options.artifactPath) { + program.addOption(new Option('-a, --artifactPath ', 'artifact path')); + } + + if (options.contractName) { + program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); + } + + if (options.salt) { + program.addOption(new Option('-s, --salt ', 'salt to use for create2 deployment').env('SALT')); + } + + if (options.skipExisting) { + program.addOption(new Option('-x, --skipExisting', 'skip existing if contract was already deployed on chain').env('SKIP_EXISTING')); + } + + if (options.upgrade) { + program.addOption(new Option('-u, --upgrade', 'upgrade a deployed contract').env('UPGRADE')); + } + + return program; +}; + +module.exports = { + addBaseOptions, + addExtendedOptions, +}; diff --git a/evm/deploy-const-address-deployer.js b/evm/deploy-const-address-deployer.js index 442e7d57..9218d237 100644 --- a/evm/deploy-const-address-deployer.js +++ b/evm/deploy-const-address-deployer.js @@ -9,10 +9,11 @@ const { Command, Option } = require('commander'); const chalk = require('chalk'); const { printInfo, writeJSON, predictAddressCreate, deployCreate } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); const contractJson = require('@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/ConstAddressDeployer.sol/ConstAddressDeployer.json'); const contractName = 'ConstAddressDeployer'; -async function deployConstAddressDeployer(wallet, chain, options = null, verifyOptions = null) { +async function deployConstAddressDeployer(wallet, chain, options = {}, verifyOptions = null) { printInfo('Deployer address', wallet.address); const contracts = chain.contracts; @@ -125,19 +126,10 @@ if (require.main === module) { program.name('deploy-const-address-deployer').description('Deploy const address deployer'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); + addExtendedOptions(program); + program.addOption(new Option('-i, --ignore', 'ignore the nonce value check')); - program.addOption(new Option('-v, --verify', 'verify the deployed contract on the explorer').env('VERIFY')); program.addOption(new Option('-f, --force', 'proceed with contract deployment even if address already returns a bytecode')); - program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); program.action((options) => { main(options); diff --git a/evm/deploy-contract.js b/evm/deploy-contract.js index a444ccbf..9d7afd8b 100644 --- a/evm/deploy-contract.js +++ b/evm/deploy-contract.js @@ -9,7 +9,6 @@ const { utils: { isAddress, keccak256, toUtf8Bytes }, } = ethers; const { Command, Option } = require('commander'); - const { printInfo, printWarn, @@ -26,9 +25,11 @@ const { prompt, mainProcessor, isContract, + getContractJSON, } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); -async function getConstructorArgs(contractName, chain, wallet, options) { +async function getConstructorArgs(contractName, chain, wallet) { const config = chain.contracts; const contractConfig = config[contractName]; @@ -40,13 +41,15 @@ async function getConstructorArgs(contractName, chain, wallet, options) { throw new Error(`Missing AxelarGateway address in the chain info.`); } - const governanceChain = contractConfig.governanceChain; + const governanceChain = contractConfig.governanceChain || 'Axelarnet'; + contractConfig.governanceChain = governanceChain; if (!isString(governanceChain)) { throw new Error(`Missing AxelarServiceGovernance.governanceChain in the chain info.`); } - const governanceAddress = contractConfig.governanceAddress; + const governanceAddress = contractConfig.governanceAddress || 'axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj'; + contractConfig.governanceAddress = governanceAddress; if (!isString(governanceAddress)) { throw new Error(`Missing AxelarServiceGovernance.governanceAddress in the chain info.`); @@ -120,22 +123,13 @@ async function getConstructorArgs(contractName, chain, wallet, options) { } case 'Multisig': { - const signers = contractConfig.signers || [ - '0x3f5876a2b06E54949aB106651Ab6694d0289b2b4', - '0x9256Fd872118ed3a97754B0fB42c15015d17E0CC', - '0x1486157d505C7F7E546aD00E3E2Eee25BF665C9b', - '0x2eC991B5c0B742AbD9d2ea31fe6c14a85e91C821', - '0xf505462A29E36E26f25Ef0175Ca1eCBa09CC118f', - '0x027c1882B975E2cd771AE068b0389FA38B9dda73', - ]; + const signers = contractConfig.signers; if (!isAddressArray(signers)) { throw new Error(`Missing Multisig.signers in the chain info.`); } - const threshold = contractConfig.threshold || Math.floor((signers.length + 1) / 2); - contractConfig.threshold = threshold; - contractConfig.signers = signers; + const threshold = contractConfig.threshold; if (!isNumber(threshold)) { throw new Error(`Missing Multisig.threshold in the chain info.`); @@ -250,10 +244,7 @@ async function processCommand(config, chain, options) { printInfo('Contract name', contractName); - const contractPath = artifactPath.charAt(0) === '@' ? artifactPath : artifactPath + contractName + '.sol/' + contractName + '.json'; - printInfo('Contract path', contractPath); - - const contractJson = require(contractPath); + const contractJson = getContractJSON(contractName, artifactPath); const predeployCodehash = await getBytecodeHash(contractJson, chain.id); printInfo('Pre-deploy Contract bytecode hash', predeployCodehash); @@ -336,33 +327,21 @@ async function main(options) { await mainProcessor(options, processCommand); } -const program = new Command(); - -program.name('deploy-contract').description('Deploy contracts using create, create2, or create3'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-a, --artifactPath ', 'artifact path').makeOptionMandatory(true)); -program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true).env('CHAINS')); -program.addOption(new Option('--skipChains ', 'chains to skip over')); -program.addOption( - new Option('-m, --deployMethod ', 'deployment method').choices(['create', 'create2', 'create3']).default('create2'), -); -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-s, --salt ', 'salt to use for create2 deployment')); -program.addOption(new Option('-v, --verify ', 'verify the deployed contract on the explorer').env('VERIFY')); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); -program.addOption(new Option('-x, --skipExisting', 'skip existing if contract was already deployed on chain')); -program.addOption(new Option('--ignoreError', 'ignore errors during deployment for a given chain')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('deploy-contract').description('Deploy contracts using create, create2, or create3'); + + addExtendedOptions(program, { artifactPath: true, contractName: true, salt: true, skipChains: true, skipExisting: true }); + + program.addOption( + new Option('-m, --deployMethod ', 'deployment method').choices(['create', 'create2', 'create3']).default('create2'), + ); + program.addOption(new Option('--ignoreError', 'ignore errors during deployment for a given chain')); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/deploy-create3-deployer.js b/evm/deploy-create3-deployer.js index 88c37abd..8f570bf9 100644 --- a/evm/deploy-create3-deployer.js +++ b/evm/deploy-create3-deployer.js @@ -6,15 +6,16 @@ const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider } = ethers; const readlineSync = require('readline-sync'); const { predictContractConstant } = require('@axelar-network/axelar-gmp-sdk-solidity'); -const { Command, Option } = require('commander'); +const { Command } = require('commander'); const chalk = require('chalk'); const { printInfo, writeJSON, deployCreate2 } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); const contractJson = require('@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/Create3Deployer.sol/Create3Deployer.json'); const { deployConstAddressDeployer } = require('./deploy-const-address-deployer'); const contractName = 'Create3Deployer'; -async function deployCreate3Deployer(wallet, chain, options = null, verifyOptions = null) { +async function deployCreate3Deployer(wallet, chain, options = {}, verifyOptions = null) { printInfo('Deployer address', wallet.address); console.log( @@ -94,18 +95,7 @@ if (require.main === module) { program.name('deploy-create3-deployer').description('Deploy create3 deployer'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); - program.addOption(new Option('-s, --salt ', 'salt to use for create2 deployment')); - program.addOption(new Option('-v, --verify ', 'verify the deployed contract on the explorer').env('VERIFY')); - program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); + addExtendedOptions(program, { salt: true }); program.action((options) => { main(options); diff --git a/evm/deploy-gateway-v4.3.x.js b/evm/deploy-gateway-v4.3.x.js index 5866bb22..866188a4 100644 --- a/evm/deploy-gateway-v4.3.x.js +++ b/evm/deploy-gateway-v4.3.x.js @@ -17,6 +17,7 @@ const { isNumber, prompt, } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); const { ethers } = require('hardhat'); const { getContractFactory, @@ -311,22 +312,11 @@ async function programHandler() { program.name('deploy-gateway-v4.3.x').description('Deploy gateway v4.3.x'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainName ', 'chain name').makeOptionMandatory(true).env('CHAIN')); + addExtendedOptions(program, { skipExisting: true, upgrade: true }); + program.addOption(new Option('-r, --rpc ', 'chain rpc url').env('URL')); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); - program.addOption(new Option('-v, --verify', 'verify the deployed contract on the explorer').env('VERIFY')); - program.addOption(new Option('-x, --skipExisting', 'skip deployment for existing contracts in the info files').env('SKIP_EXISTING')); program.addOption(new Option('-a, --adminAddresses ', 'admin addresses').env('ADMIN_ADDRESSES')); program.addOption(new Option('-t, --adminThreshold ', 'admin threshold').env('ADMIN_THRESHOLD')); - program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); - program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); program.action((options) => { main(options); diff --git a/evm/deploy-gateway-v6.2.x.js b/evm/deploy-gateway-v6.2.x.js index f4cb96e5..75ec39f2 100644 --- a/evm/deploy-gateway-v6.2.x.js +++ b/evm/deploy-gateway-v6.2.x.js @@ -28,6 +28,7 @@ const { isContract, deployContract, } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); const { storeSignedTx, signTransaction, getWallet } = require('./sign-utils.js'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); @@ -500,22 +501,12 @@ async function programHandler() { program.name('deploy-gateway-v6.2.x').description('Deploy gateway v6.2.x'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chains to run the script over').makeOptionMandatory(true).env('CHAINS')); - program.addOption(new Option('--skipChains ', 'chains to skip over')); + addExtendedOptions(program, { salt: true, skipChains: true, skipExisting: true, upgrade: true }); + program.addOption(new Option('-r, --rpc ', 'chain rpc url').env('URL')); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); program.addOption( new Option('--deployMethod ', 'deployment method').choices(['create', 'create2', 'create3']).default('create'), ); - program.addOption(new Option('-s, --salt ', 'salt to use for deployment method')); - program.addOption(new Option('-v, --verify', 'verify the deployed contract on the explorer').env('VERIFY')); program.addOption(new Option('-r, --reuseProxy', 'reuse proxy contract modules for new implementation deployment').env('REUSE_PROXY')); program.addOption( new Option('--reuseHelpers', 'reuse helper auth and token deployer contract modules for new implementation deployment').env( @@ -525,14 +516,11 @@ async function programHandler() { program.addOption(new Option('--ignoreError', 'Ignore deployment errors and proceed to next chain')); program.addOption(new Option('-g, --governance ', 'governance address').env('GOVERNANCE')); program.addOption(new Option('-m, --mintLimiter ', 'mint limiter address').env('MINT_LIMITER')); - program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); program.addOption(new Option('-k, --keyID ', 'key ID').env('KEY_ID')); program.addOption(new Option('-a, --amplifier', 'deploy amplifier gateway').env('AMPLIFIER')); program.addOption(new Option('--prevKeyIDs ', 'previous key IDs to be used for auth contract')); - program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); program.addOption(new Option('--offline', 'Run in offline mode')); program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce')); - program.addOption(new Option('-x, --skipExisting', 'skip existing if contract was already deployed on chain')); program.action((options) => { main(options); diff --git a/evm/deploy-its.js b/evm/deploy-its.js index 9490cfc9..7739870e 100644 --- a/evm/deploy-its.js +++ b/evm/deploy-its.js @@ -12,6 +12,7 @@ const { const readlineSync = require('readline-sync'); const chalk = require('chalk'); const { printInfo, loadConfig, saveConfig } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); const TokenManagerDeployer = require('@axelar-network/interchain-token-service/dist/utils/TokenManagerDeployer.sol/TokenManagerDeployer.json'); const StandardizedTokenLockUnlock = require('@axelar-network/interchain-token-service/dist/token-implementations/StandardizedTokenLockUnlock.sol/StandardizedTokenLockUnlock.json'); @@ -362,20 +363,10 @@ if (require.main === module) { program.name('deploy-its').description('Deploy interchain token service'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); + addExtendedOptions(program, { skipExisting: true, upgrade: true }); + program.addOption(new Option('-s, --salt ', 'deployment salt to use for ITS deployment').makeOptionMandatory(true).env('SALT')); - program.addOption(new Option('-v, --verify ', 'verify the deployed contract on the explorer [true|false|only]').env('VERIFY')); - program.addOption(new Option('-x, --skipExisting ', 'skip deploying contracts if they already exist').env('SKIP_EXISTING')); program.addOption(new Option('-o, --operator', 'address of the ITS operator').env('OPERATOR_ADDRESS')); - program.addOption(new Option('-u, --upgrade', 'upgrade ITS').env('UPGRADE')); program.action(async (options) => { options.skipExisting = options.skipExisting === 'true'; diff --git a/evm/deploy-upgradable.js b/evm/deploy-upgradable.js index ba55fe30..5b82ebc1 100644 --- a/evm/deploy-upgradable.js +++ b/evm/deploy-upgradable.js @@ -14,6 +14,7 @@ const { Command, Option } = require('commander'); const { deployUpgradable, deployCreate2Upgradable, deployCreate3Upgradable, upgradeUpgradable } = require('./upgradable'); const { printInfo, printError, saveConfig, loadConfig, printWalletInfo, getDeployedAddress, prompt } = require('./utils'); +const { addExtendedOptions } = require('./cli-utils'); function getProxy(wallet, proxyAddress) { return new Contract(proxyAddress, IUpgradable.abi, wallet); @@ -65,7 +66,7 @@ async function getImplementationArgs(contractName, config, options) { throw new Error(`${contractName} is not supported.`); } -function getInitArgs(contractName, config) { +function getInitArgs(contractName) { switch (contractName) { case 'AxelarGasService': { return '0x'; @@ -79,7 +80,7 @@ function getInitArgs(contractName, config) { throw new Error(`${contractName} is not supported.`); } -function getUpgradeArgs(contractName, config) { +function getUpgradeArgs(contractName) { switch (contractName) { case 'AxelarGasService': { return '0x'; @@ -300,33 +301,21 @@ async function main(options) { } } -const program = new Command(); - -program.name('deploy-upgradable').description('Deploy upgradable contracts'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('--skipChains ', 'chains to skip over')); -program.addOption( - new Option('-m, --deployMethod ', 'deployment method').choices(['create', 'create2', 'create3']).default('create2'), -); -program.addOption(new Option('-a, --artifactPath ', 'artifact path')); -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-s, --salt ', 'salt to use for create2 deployment')); -program.addOption(new Option('-u, --upgrade', 'upgrade a deployed contract')); -program.addOption(new Option('-v, --verify', 'verify the deployed contract on the explorer').env('VERIFY')); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); -program.addOption(new Option('--args ', 'customize deployment args')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('deploy-upgradable').description('Deploy upgradable contracts'); + + addExtendedOptions(program, { artifactPath: true, contractName: true, salt: true, skipChains: true, upgrade: true }); + + program.addOption( + new Option('-m, --deployMethod ', 'deployment method').choices(['create', 'create2', 'create3']).default('create2'), + ); + program.addOption(new Option('--args ', 'customize deployment args')); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/execute-contract.js b/evm/execute-contract.js index 3daddeb4..e8189096 100644 --- a/evm/execute-contract.js +++ b/evm/execute-contract.js @@ -12,12 +12,13 @@ const { const readlineSync = require('readline-sync'); const { Command, Option } = require('commander'); -const { isNumber, isString, loadConfig, saveConfig, printObj, printLog, printError, printInfo } = require('./utils'); +const { isNumber, isString, loadConfig, saveConfig, printObj, printLog, printError, getContractJSON } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); -async function getCallData(methodName, targetContract, inputRecipient, inputAmount) { +async function getCallData(action, targetContract, inputRecipient, inputAmount) { var recipient, amount; - switch (methodName) { + switch (action) { case 'withdraw': { if (inputRecipient) { recipient = inputRecipient; @@ -94,7 +95,7 @@ async function getCallData(methodName, targetContract, inputRecipient, inputAmou } default: { - throw new Error('The method name does not match any of the specified choices'); + throw new Error('The action does not match any of the specified choices'); } } } @@ -108,7 +109,7 @@ async function executeContract(options, chain, wallet) { targetContractAddress, callData, nativeValue, - methodName, + action, recipientAddress, amount, } = options; @@ -128,7 +129,7 @@ async function executeContract(options, chain, wallet) { throw new Error('Missing target address in the address info.'); } - if (!isString(methodName)) { + if (!isString(action)) { throw new Error('Missing method name from the user info.'); } @@ -136,26 +137,17 @@ async function executeContract(options, chain, wallet) { throw new Error('Missing native value from user info'); } - var contractPath = - callContractPath.charAt(0) === '@' ? callContractPath : callContractPath + callContractName + '.sol/' + callContractName + '.json'; - printInfo('Call Contract path', contractPath); - - const IContractExecutor = require(contractPath); + const IContractExecutor = getContractJSON(callContractName, callContractPath); const contract = new Contract(callContractAddress, IContractExecutor.abi, wallet); var finalCallData, finalNativeValue; - if (methodName === 'default') { + if (action === 'default') { finalCallData = callData; finalNativeValue = nativeValue; } else { - contractPath = - targetContractPath.charAt(0) === '@' - ? targetContractPath - : targetContractPath + targetContractName + '.sol/' + targetContractName + '.json'; - printInfo('Target Contract path', contractPath); - const ITargetContract = require(contractPath); + const ITargetContract = getContractJSON(targetContractName, targetContractPath); const targetContract = new Contract(targetContractAddress, ITargetContract.abi, wallet); - finalCallData = await getCallData(methodName, targetContract, recipientAddress, Number(amount)); + finalCallData = await getCallData(action, targetContract, recipientAddress, Number(amount)); finalNativeValue = Number(0); } @@ -199,53 +191,41 @@ async function main(options) { } } -const program = new Command(); - -program.name('execute-contract').description('Executes a call to an external contract'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption( - new Option('-pc, --callContractPath ', 'artifact path for the called contract').makeOptionMandatory(true), -); -program.addOption(new Option('-cn, --callContractName ', 'name of the called contract').makeOptionMandatory(true)); -program.addOption( - new Option('-pt, --targetContractPath ', 'artifact path for the target contract').makeOptionMandatory(true), -); -program.addOption( - new Option( - '-tn, --targetContractName ', - 'name of the target contract that is called through executeContract', - ).makeOptionMandatory(false), -); -program.addOption( - new Option('-ta, --targetContractAddress ', 'The address of the contract to be called') - .makeOptionMandatory(true) - .env('TARGET_ADDR'), -); -program.addOption( - new Option('-v, --nativeValue ', 'The amount of native token (e.g., Ether) to be sent along with the call').default(0), -); -program.addOption( - new Option('-m, --methodName ', 'method name to call in executeContract') - .choices(['withdraw', 'transfer', 'approve', 'default']) - .default('default'), -); -program.addOption( - new Option('-k, --privateKey ', 'The private key of the caller').makeOptionMandatory(true).env('PRIVATE_KEY'), -); -program.addOption(new Option('-c, --callData ', 'The calldata to be sent').env('CALL_DATA').default('0x')); -program.addOption(new Option('-ra, --recipientAddress ', 'The recipient address for the tokens').env('RECIPIENT_ADDR')); -program.addOption(new Option('-am, --amount ', 'The amount of tokens to transfer/withdraw/provide allowance etc.').env('AMOUNT')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('execute-contract').description('Executes a call to an external contract'); + + addBaseOptions(program); + + program.addOption(new Option('-c, --callContractName ', 'name of the called contract').makeOptionMandatory(true)); + program.addOption(new Option('--callContractPath ', 'artifact path for the called contract')); + program.addOption(new Option('--targetContractPath ', 'artifact path for the target contract')); + program.addOption( + new Option('-t, --targetContractName ', 'target contract name called by executeContract').makeOptionMandatory( + true, + ), + ); + program.addOption( + new Option('-a, --targetContractAddress ', 'target contract address') + .makeOptionMandatory(true) + .env('TARGET_ADDR'), + ); + program.addOption( + new Option('-v, --nativeValue ', 'The amount of native token (e.g., Ether) to be sent along with the call').default(0), + ); + program.addOption( + new Option('--action ', 'executeContract action') + .choices(['withdraw', 'transfer', 'approve', 'default']) + .default('default'), + ); + program.addOption(new Option('--callData ', 'The calldata to be sent').env('CALL_DATA').default('0x')); + program.addOption(new Option('--recipientAddress ', 'The recipient address for the tokens').env('RECIPIENT_ADDR')); + program.addOption(new Option('--amount ', 'The amount of tokens to transfer/withdraw/provide allowance etc.').env('AMOUNT')); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/gateway.js b/evm/gateway.js index ec4a68f8..660de949 100644 --- a/evm/gateway.js +++ b/evm/gateway.js @@ -21,6 +21,7 @@ const { mainProcessor, printError, } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { getWallet } = require('./sign-utils'); const IGateway = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarGateway.json'); @@ -326,51 +327,43 @@ async function main(options) { await mainProcessor(options, processCommand); } -const program = new Command(); - -program.name('gateway').description('Script to perform gateway commands'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-c, --contractName ', 'contract name').default('Multisig')); -program.addOption(new Option('-a, --address
', 'override address')); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true).env('CHAINS')); -program.addOption(new Option('--skipChains ', 'chains to skip over')); -program.addOption( - new Option('--action ', 'gateway action') - .choices([ - 'admins', - 'operators', - 'callContract', - 'submitBatch', - 'approve', - 'execute', - 'approveAndExecute', - 'transferGovernance', - 'governance', - 'mintLimiter', - 'mintLimit', - 'params', - ]) - .makeOptionMandatory(true), -); -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); - -program.addOption(new Option('--payload ', 'gmp payload')); -program.addOption(new Option('--commandID ', 'execute command ID')); -program.addOption(new Option('--destination ', 'GMP destination address')); -program.addOption(new Option('--destinationChain ', 'GMP destination chain')); -program.addOption(new Option('--batchID ', 'EVM batch ID').default('')); -program.addOption(new Option('--symbol ', 'EVM token symbol')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('gateway').description('Script to perform gateway commands'); + + addBaseOptions(program, { address: true }); + + program.addOption(new Option('-c, --contractName ', 'contract name').default('Multisig')); + program.addOption( + new Option('--action ', 'gateway action') + .choices([ + 'admins', + 'operators', + 'callContract', + 'submitBatch', + 'approve', + 'execute', + 'approveAndExecute', + 'transferGovernance', + 'governance', + 'mintLimiter', + 'mintLimit', + 'params', + ]) + .makeOptionMandatory(true), + ); + + program.addOption(new Option('--payload ', 'gmp payload')); + program.addOption(new Option('--commandID ', 'execute command ID')); + program.addOption(new Option('--destination ', 'GMP destination address')); + program.addOption(new Option('--destinationChain ', 'GMP destination chain')); + program.addOption(new Option('--batchID ', 'EVM batch ID').default('')); + program.addOption(new Option('--symbol ', 'EVM token symbol')); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/governance.js b/evm/governance.js index 83953b06..6d01f3e7 100644 --- a/evm/governance.js +++ b/evm/governance.js @@ -28,6 +28,7 @@ const { isValidDecimal, prompt, } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { getWallet } = require('./sign-utils.js'); const IGovernance = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarServiceGovernance.json'); const IGateway = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarGateway.json'); @@ -602,56 +603,48 @@ async function main(options) { } } -const program = new Command(); - -program.name('governance').description('Script to manage interchain governance actions'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption( - new Option('-c, --contractName ', 'contract name') - .choices(['InterchainGovernance', 'AxelarServiceGovernance']) - .default('InterchainGovernance'), -); -program.addOption(new Option('-a, --address
', 'override address')); -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('--skipChains ', 'chains to skip over')); -program.addOption( - new Option('--action ', 'governance action').choices([ - 'scheduleTimeLock', - 'cancelTimeLock', - 'approveMultisig', - 'cancelMultisig', - 'executeProposal', - 'executeMultisigProposal', - 'gatewayUpgrade', - 'submitUpgrade', - 'executeUpgrade', - 'cancelUpgrade', - 'withdraw', - 'getProposalEta', - ]), -); -program.addOption(new Option('--newGovernance ', 'governance address').env('GOVERNANCE')); -program.addOption(new Option('--newMintLimiter ', 'mint limiter address').env('MINT_LIMITER')); -program.addOption(new Option('--commandId ', 'command id')); -program.addOption(new Option('--target ', 'governance execution target')); -program.addOption(new Option('--calldata ', 'calldata')); -program.addOption(new Option('--nativeValue ', 'nativeValue').default(0)); -program.addOption(new Option('--proposal ', 'governance proposal payload')); -program.addOption(new Option('--amount ', 'withdraw amount')); -program.addOption(new Option('--date ', 'proposal activation date')); -program.addOption(new Option('--implementation ', 'new gateway implementation')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('governance').description('Script to manage interchain governance actions'); + + addBaseOptions(program, { address: true }); + + program.addOption( + new Option('-c, --contractName ', 'contract name') + .choices(['InterchainGovernance', 'AxelarServiceGovernance']) + .default('InterchainGovernance'), + ); + program.addOption( + new Option('--action ', 'governance action').choices([ + 'scheduleTimeLock', + 'cancelTimeLock', + 'approveMultisig', + 'cancelMultisig', + 'executeProposal', + 'executeMultisigProposal', + 'gatewayUpgrade', + 'submitUpgrade', + 'executeUpgrade', + 'cancelUpgrade', + 'withdraw', + 'getProposalEta', + ]), + ); + program.addOption(new Option('--newGovernance ', 'governance address').env('GOVERNANCE')); + program.addOption(new Option('--newMintLimiter ', 'mint limiter address').env('MINT_LIMITER')); + program.addOption(new Option('--commandId ', 'command id')); + program.addOption(new Option('--target ', 'governance execution target')); + program.addOption(new Option('--calldata ', 'calldata')); + program.addOption(new Option('--nativeValue ', 'nativeValue').default(0)); + program.addOption(new Option('--proposal ', 'governance proposal payload')); + program.addOption(new Option('--amount ', 'withdraw amount')); + program.addOption(new Option('--date ', 'proposal activation date')); + program.addOption(new Option('--implementation ', 'new gateway implementation')); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/min-deposit-proposal.js b/evm/min-deposit-proposal.js index 09d49e5e..86ffab77 100644 --- a/evm/min-deposit-proposal.js +++ b/evm/min-deposit-proposal.js @@ -3,6 +3,7 @@ require('dotenv').config(); const { Command, Option } = require('commander'); +const { addBaseOptions } = require('./cli-utils'); const { mainProcessor, printInfo, isValidNumber, isValidAddress } = require('./utils'); const values = []; @@ -67,15 +68,8 @@ if (require.main === module) { program.name('balances').description('Display balance of the wallet on specified chains.'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true).env('CHAINS')); - program.addOption(new Option('-a, --address
', 'override governance address')); + addBaseOptions(program, { ignorePrivateKey: true, address: true }); + program.addOption(new Option('--deposit ', 'min deposit for governance proposals, in terms of AXL')); program.action((options) => { diff --git a/evm/multisig.js b/evm/multisig.js index 6bf7a851..d6118800 100644 --- a/evm/multisig.js +++ b/evm/multisig.js @@ -23,6 +23,7 @@ const { isValidDecimal, prompt, } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const IMultisig = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IMultisig.json'); const IGateway = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarGateway.json'); const IGovernance = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarServiceGovernance.json'); @@ -313,49 +314,43 @@ async function main(options) { await mainProcessor(options, processCommand, false); } -const program = new Command(); - -program.name('multisig').description('Script to manage multisig actions'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-c, --contractName ', 'contract name').default('Multisig').makeOptionMandatory(false)); -program.addOption(new Option('-a, --address
', 'override address')); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('--skipChains ', 'chains to skip over')); -program.addOption( - new Option('--action ', 'multisig action') - .choices(['signers', 'setTokenMintLimits', 'transferMintLimiter', 'withdraw', 'executeMultisigProposal']) - .makeOptionMandatory(true), -); -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); -program.addOption(new Option('--offline', 'run script in offline mode')); -program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce')); - -// options for setTokenMintLimits -program.addOption(new Option('--symbols ', 'token symbols').makeOptionMandatory(false)); -program.addOption(new Option('--limits ', 'token limits').makeOptionMandatory(false)); - -// option for transferMintLimiter -program.addOption(new Option('--mintLimiter ', 'new mint limiter address').makeOptionMandatory(false)); - -// options for withdraw -program.addOption(new Option('--recipient ', 'withdraw recipient address').makeOptionMandatory(false)); -program.addOption(new Option('--withdrawAmount ', 'withdraw amount').makeOptionMandatory(false)); - -// options for executeMultisigProposal -program.addOption(new Option('--target ', 'execute multisig proposal target').makeOptionMandatory(false)); -program.addOption(new Option('--calldata ', 'execute multisig proposal calldata').makeOptionMandatory(false)); -program.addOption(new Option('--nativeValue ', 'execute multisig proposal nativeValue').makeOptionMandatory(false).default(0)); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('multisig').description('Script to manage multisig actions'); + + addBaseOptions(program, { address: true }); + + program.addOption(new Option('-c, --contractName ', 'contract name').default('Multisig').makeOptionMandatory(false)); + program.addOption( + new Option('--action ', 'multisig action') + .choices(['signers', 'setTokenMintLimits', 'transferMintLimiter', 'withdraw', 'executeMultisigProposal']) + .makeOptionMandatory(true), + ); + program.addOption(new Option('--offline', 'run script in offline mode')); + program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce')); + + // options for setTokenMintLimits + program.addOption(new Option('--symbols ', 'token symbols').makeOptionMandatory(false)); + program.addOption(new Option('--limits ', 'token limits').makeOptionMandatory(false)); + + // option for transferMintLimiter + program.addOption(new Option('--mintLimiter ', 'new mint limiter address').makeOptionMandatory(false)); + + // options for withdraw + program.addOption(new Option('--recipient ', 'withdraw recipient address').makeOptionMandatory(false)); + program.addOption(new Option('--withdrawAmount ', 'withdraw amount').makeOptionMandatory(false)); + + // options for executeMultisigProposal + program.addOption(new Option('--target ', 'execute multisig proposal target').makeOptionMandatory(false)); + program.addOption(new Option('--calldata ', 'execute multisig proposal calldata').makeOptionMandatory(false)); + program.addOption( + new Option('--nativeValue ', 'execute multisig proposal nativeValue').makeOptionMandatory(false).default(0), + ); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/operators.js b/evm/operators.js index b7acad61..205a91b0 100644 --- a/evm/operators.js +++ b/evm/operators.js @@ -22,6 +22,7 @@ const { parseArgs, prompt, } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const IAxelarGasService = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IAxelarGasService.json'); const IOperators = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IOperators.json'); @@ -262,29 +263,28 @@ async function main(options) { } } -const program = new Command(); - -program.name('operators').description('script to manage operators contract'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-c, --contractName ', 'contract name').default('Operators').makeOptionMandatory(false)); -program.addOption(new Option('-n, --chainNames ', 'chains').makeOptionMandatory(true)); -program.addOption(new Option('--address
', 'override address').makeOptionMandatory(false)); -program.addOption( - new Option('--action ', 'operator action').choices(['isOperator', 'addOperator', 'removeOperator', 'collectFees', 'refund']), -); -program.addOption(new Option('--args ', 'operator action arguments').makeOptionMandatory(true)); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('operators').description('script to manage operators contract'); + + addBaseOptions(program, { address: true }); + + program.addOption(new Option('-c, --contractName ', 'contract name').default('Operators').makeOptionMandatory(false)); + program.addOption( + new Option('--action ', 'operator action').choices([ + 'isOperator', + 'addOperator', + 'removeOperator', + 'collectFees', + 'refund', + ]), + ); + program.addOption(new Option('--args ', 'operator action arguments').makeOptionMandatory(true)); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/ownership.js b/evm/ownership.js index f7447d3c..3cf811d1 100644 --- a/evm/ownership.js +++ b/evm/ownership.js @@ -12,6 +12,7 @@ const { } = ethers; const { Command, Option } = require('commander'); const { printInfo, printWalletInfo, loadConfig, saveConfig, prompt } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const IOwnable = require('@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/interfaces/IOwnable.sol/IOwnable.json'); @@ -188,36 +189,28 @@ async function main(options) { } } -const program = new Command(); - -program.name('ownership').description('script to manage contract ownership'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); - -program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); -program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('--address
', 'override address').makeOptionMandatory(false)); -program.addOption( - new Option('--action ', 'ownership action').choices([ - 'owner', - 'pendingOwner', - 'transferOwnership', - 'proposeOwnership', - 'acceptOwnership', - ]), -); -program.addOption(new Option('--newOwner ', 'new owner address').makeOptionMandatory(false)); -program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES')); - -program.action((options) => { - main(options); -}); - -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('ownership').description('script to manage contract ownership'); + + addBaseOptions(program, { address: true }); + + program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); + program.addOption( + new Option('--action ', 'ownership action').choices([ + 'owner', + 'pendingOwner', + 'transferOwnership', + 'proposeOwnership', + 'acceptOwnership', + ]), + ); + program.addOption(new Option('--newOwner ', 'new owner address').makeOptionMandatory(false)); + + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/remove-info.js b/evm/remove-info.js index 6b1f4a5e..8f3bb676 100644 --- a/evm/remove-info.js +++ b/evm/remove-info.js @@ -4,6 +4,7 @@ require('dotenv').config(); const { Command, Option } = require('commander'); const { mainProcessor } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); async function processCommand(options, chain, _) { const { contractName } = options; @@ -19,22 +20,18 @@ async function main(options) { await mainProcessor(options, processCommand, true); } -const program = new Command(); +if (require.main === module) { + const program = new Command(); -program.name('remove-info').description('Remove info about contract from the info file.'); + program.name('remove-info').description('Remove info about contract from the info file.'); -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); + addBaseOptions(program, { ignorePrivateKey: true }); -program.action((options) => { - main(options); -}); + program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); -program.parse(); + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/send-tokens.js b/evm/send-tokens.js index cf0548f0..89f035e0 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -9,8 +9,8 @@ const { getDefaultProvider, utils: { parseEther, parseUnits }, } = ethers; - const { printInfo, printError, printWalletInfo, isAddressArray, mainProcessor, isValidDecimal, prompt } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { storeSignedTx, getWallet, signTransaction } = require('./sign-utils.js'); async function processCommand(_, chain, options) { @@ -102,21 +102,13 @@ if (require.main === module) { program.name('send-tokens').description('Send native tokens to an address.'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); - program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); + addBaseOptions(program); + program.addOption(new Option('-r, --recipients ', 'comma-separated recipients of tokens').makeOptionMandatory(true)); program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)')); program.addOption(new Option('--gasUsage ', 'amount to transfer based on gas usage and gas price').default('5000000')); program.addOption(new Option('--offline', 'Run in offline mode')); program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce')); - program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { main(options); diff --git a/evm/update-nonces.js b/evm/update-nonces.js index 6c740ea6..ff315df1 100644 --- a/evm/update-nonces.js +++ b/evm/update-nonces.js @@ -6,6 +6,7 @@ const { ethers } = require('hardhat'); const { getDefaultProvider } = ethers; const { mainProcessor, printInfo, prompt } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const { getNonceFromProvider, getNonceFileData, updateNonceFileData } = require('./sign-utils'); async function processCommand(_, chain, options) { @@ -49,23 +50,18 @@ async function main(options) { await mainProcessor(options, processCommand); } -const program = new Command(); - -program.name('update-nonces').description('Update nonces for addresses'); - -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); -program.addOption(new Option('--addresses ', 'The Array of addresses for which the nonces to update').env('ADDRESSES')); -program.addOption(new Option('-y, --yes', 'skip prompts')); - -program.action((options) => { - main(options); -}); -program.parse(); +if (require.main === module) { + const program = new Command(); + + program.name('update-nonces').description('Update nonces for addresses'); + + addBaseOptions(program, { ignorePrivateKey: true }); + + program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); + program.addOption(new Option('--addresses ', 'The Array of addresses for which the nonces to update').env('ADDRESSES')); + + program.action((options) => { + main(options); + }); + program.parse(); +} diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index b9f9577b..12542c67 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -10,6 +10,7 @@ const { } = ethers; const { printInfo, mainProcessor, prompt } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); const defaultGasLimit = 3e6; const gasPriceMultiplier = 5; @@ -97,23 +98,18 @@ async function main(options) { await mainProcessor(options, processCommand, true); } -const program = new Command(); +if (require.main === module) { + const program = new Command(); -program.name('update-static-gas-options').description('Update staticGasOptions'); + program.name('update-static-gas-options').description('Update staticGasOptions'); -program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), -); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); -program.addOption(new Option('-y, --yes', 'skip prompts')); + addBaseOptions(program, { ignorePrivateKey: true }); -program.action((options) => { - main(options); -}); + program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); -program.parse(); + program.action((options) => { + main(options); + }); + + program.parse(); +} diff --git a/evm/utils.js b/evm/utils.js index 97b0ad17..7703d592 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -9,6 +9,8 @@ const { } = ethers; const https = require('https'); const http = require('http'); +const fs = require('fs'); +const path = require('path'); const { outputJsonSync } = require('fs-extra'); const zkevm = require('@0xpolygonhermez/zkevm-commonjs'); const readlineSync = require('readline-sync'); @@ -777,6 +779,81 @@ function getConfigByChainId(chainId, config) { throw new Error(`Chain with chainId ${chainId} not found in the config`); } +function findProjectRoot(startDir) { + let currentDir = startDir; + + while (currentDir !== path.parse(currentDir).root) { + const potentialPackageJson = path.join(currentDir, 'package.json'); + + if (fs.existsSync(potentialPackageJson)) { + return currentDir; + } + + currentDir = path.resolve(currentDir, '..'); + } + + throw new Error('Unable to find project root'); +} + +function findContractPath(dir, contractName) { + const files = fs.readdirSync(dir); + + for (const file of files) { + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + + if (stat && stat.isDirectory()) { + const recursivePath = findContractPath(filePath, contractName); + + if (recursivePath) { + return recursivePath; + } + } else if (file === `${contractName}.json`) { + return filePath; + } + } +} + +function getContractPath(contractName) { + const projectRoot = findProjectRoot(__dirname); + + const searchDirs = [ + path.join(projectRoot, 'node_modules', '@axelar-network', 'axelar-gmp-sdk-solidity', 'artifacts', 'contracts'), + path.join(projectRoot, 'node_modules', '@axelar-network', 'axelar-cgp-solidity', 'artifacts', 'contracts'), + ]; + + for (const dir of searchDirs) { + if (fs.existsSync(dir)) { + const contractPath = findContractPath(dir, contractName); + + if (contractPath) { + return contractPath; + } + } + } + + throw new Error(`Contract path for ${contractName} must be entered manually.`); +} + +function getContractJSON(contractName, artifactPath) { + let contractPath; + + if (artifactPath) { + contractPath = artifactPath.charAt(0) === '@' ? artifactPath : artifactPath + contractName + '.sol/' + contractName + '.json'; + } else { + contractPath = getContractPath(contractName); + } + + printInfo('Contract path', contractPath); + + try { + const contractJson = require(contractPath); + return contractJson; + } catch (err) { + throw new Error(`Failed to load contract JSON for ${contractName} at path ${contractPath}`); + } +} + module.exports = { deployCreate, deployCreate2, @@ -822,4 +899,6 @@ module.exports = { verifyContract, prompt, mainProcessor, + getContractPath, + getContractJSON, }; diff --git a/evm/verify-contract.js b/evm/verify-contract.js index ec58fe04..77eb14b2 100644 --- a/evm/verify-contract.js +++ b/evm/verify-contract.js @@ -12,6 +12,7 @@ const { } = ethers; const { Command, Option } = require('commander'); const { verifyContract, getEVMAddresses, printInfo, printError, mainProcessor } = require('./utils'); +const { addBaseOptions } = require('./cli-utils'); async function processCommand(config, chain, options) { const { env, contractName, dir } = options; @@ -217,17 +218,9 @@ if (require.main === module) { program.name('balances').description('Display balance of the wallet on specified chains.'); - program.addOption( - new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) - .default('testnet') - .makeOptionMandatory(true) - .env('ENV'), - ); - program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); - program.addOption(new Option('--skipChains ', 'skip chains')); + addBaseOptions(program, { ignorePrivateKey: true, address: true }); + program.addOption(new Option('-c, --contractName ', 'contract name')); - program.addOption(new Option('-a, --address
', 'contract address')); program.addOption(new Option('-d, --dir ', 'contract artifacts dir')); program.addOption(new Option('--args ', 'contract args')); program.addOption(new Option('--constructorArgs ', 'contract constructor args'));