From c96991b127e92a912117adb3011fbc1711da4f21 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Wed, 1 Nov 2023 05:23:14 -0400 Subject: [PATCH 1/7] fix: use chain id for unique chain naming (#100) * fix: use chain id for unique chain naming * lint fix --- evm/deploy-its.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/deploy-its.js b/evm/deploy-its.js index 9b647c439..9490cfc9f 100644 --- a/evm/deploy-its.js +++ b/evm/deploy-its.js @@ -207,7 +207,7 @@ async function deployImplementation(wallet, chain, deploymentKey, skipExisting = contracts.AxelarGasService.address, contractConfig.remoteAddressValidator, Object.values(contractConfig.tokenManagerImplementations), - chain.name, + chain.id, // use the unique Axelar registered chain id ], deploymentKey, gasOptions, From b4954b77ac6c5399d50848681ed2ef85585e623f Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 2 Nov 2023 02:24:52 -0400 Subject: [PATCH 2/7] chore: cleanup README (#103) * chore: cleanup README * lint * misc --- .example.env | 4 +- axelar-chains-config/info/stagenet.json | 20 ---------- axelar-chains-config/info/testnet.json | 52 +++++++------------------ evm/README.md | 45 +++++++-------------- evm/deploy-gateway-v6.2.x.js | 44 +++++++++++++++------ package.json | 2 +- 6 files changed, 62 insertions(+), 105 deletions(-) diff --git a/.example.env b/.example.env index 034cb64c4..006d33c72 100644 --- a/.example.env +++ b/.example.env @@ -1,4 +1,2 @@ PRIVATE_KEY = '0x...' -ENV = 'local' -SALT = 'ITS v0.1.0' -SKIP_EXISTING = true \ No newline at end of file +ENV = 'testnet' diff --git a/axelar-chains-config/info/stagenet.json b/axelar-chains-config/info/stagenet.json index 1fc7e384e..f48114a0e 100644 --- a/axelar-chains-config/info/stagenet.json +++ b/axelar-chains-config/info/stagenet.json @@ -14,8 +14,6 @@ "implementationCodehash": "0x5703cdd5d9808d811ab9e516c6298a18ff926b2dc03abeba391e662f737df203", "authModule": "0xa30225bE0D505c67028B49c7175a37ff4883980f", "tokenDeployer": "0xD65b4CEde4733F08939719e228F76d13BC99cBBa", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -97,8 +95,6 @@ "implementationCodehash": "0xc680513d76d4bb9ddebb68f3137e92cf5ee91ffd83f8153de0339b7c3f72c156", "authModule": "0x56FdBc2a6b63D768BE38e0585175dC7956d741F3", "tokenDeployer": "0x4e5Fd0f7046aBE327D637D5D4AB756769cB60A97", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -180,8 +176,6 @@ "implementationCodehash": "0xa9ca57c139ac5ae7a0c65d6307c2b26b3a8624a7ab0a3e450be82d86482e2024", "authModule": "0x68F66b5Ea49B43E4d29BDED1051B90a28EADC67d", "tokenDeployer": "0x34bF216E0ae4aFCFf9283ED6D23A89cF1B0CCED1", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -263,8 +257,6 @@ "implementationCodehash": "0x5703cdd5d9808d811ab9e516c6298a18ff926b2dc03abeba391e662f737df203", "authModule": "0xa30225bE0D505c67028B49c7175a37ff4883980f", "tokenDeployer": "0xD65b4CEde4733F08939719e228F76d13BC99cBBa", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -346,8 +338,6 @@ "implementationCodehash": "0xa9ca57c139ac5ae7a0c65d6307c2b26b3a8624a7ab0a3e450be82d86482e2024", "authModule": "0x68F66b5Ea49B43E4d29BDED1051B90a28EADC67d", "tokenDeployer": "0x34bF216E0ae4aFCFf9283ED6D23A89cF1B0CCED1", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -429,8 +419,6 @@ "implementationCodehash": "0x575efa6334e10e63128cb88913044c7c687f47d267640e41faac93b054ae6140", "authModule": "0x34bF216E0ae4aFCFf9283ED6D23A89cF1B0CCED1", "tokenDeployer": "0xdc9fA655094bBaD6840cd4b05c5658eE2042e122", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -512,8 +500,6 @@ "implementationCodehash": "0x5703cdd5d9808d811ab9e516c6298a18ff926b2dc03abeba391e662f737df203", "authModule": "0xa30225bE0D505c67028B49c7175a37ff4883980f", "tokenDeployer": "0xD65b4CEde4733F08939719e228F76d13BC99cBBa", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -598,8 +584,6 @@ "implementationCodehash": "0xf1c37572ed919c97c02f87c70f41e57dd513f335c53237e1af73330b00f04de0", "authModule": "0xB0288E332552f87Ef51EEE5F4060D1A98e99548B", "tokenDeployer": "0xef180cdD04aCec05E085963aCc61BaeD17b50894", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -680,8 +664,6 @@ "implementationCodehash": "0x3eb48e5f48808ef13805b38cffaa69b898238f02ad768e5d1ee5bc73be6bb579", "authModule": "0xD65b4CEde4733F08939719e228F76d13BC99cBBa", "tokenDeployer": "0x68F66b5Ea49B43E4d29BDED1051B90a28EADC67d", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, @@ -766,8 +748,6 @@ "implementationCodehash": "0x7fb0f83cf34549753919f6f16ed3e895fbe26eda1bfded31247048255b5d4331", "authModule": "0x2F4d0bEecEE4559026A8093EDD0e16377202DBe8", "tokenDeployer": "0xB0288E332552f87Ef51EEE5F4060D1A98e99548B", - "governance": "0xeeC07176Ed1d49D9cb6C52f86E278b82Dd006C3e", - "mintLimiter": "0xdae074A742cC3628aF70bCa2B410654dF0168a5A", "deploymentMethod": "create3", "salt": "AxelarGateway v6.2" }, diff --git a/axelar-chains-config/info/testnet.json b/axelar-chains-config/info/testnet.json index 4ec0e1b83..e93a4f460 100644 --- a/axelar-chains-config/info/testnet.json +++ b/axelar-chains-config/info/testnet.json @@ -15,9 +15,7 @@ "authModule": "0xcbf7900138EBc9CdB3E933B9E6F7071587BD6cb8", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -125,9 +123,7 @@ "authModule": "0xE39d7d526DFd7B6D10972708C77ED19c3f2f5625", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -238,9 +234,7 @@ "authModule": "0xE39d7d526DFd7B6D10972708C77ED19c3f2f5625", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -354,9 +348,7 @@ "authModule": "0xE39d7d526DFd7B6D10972708C77ED19c3f2f5625", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -580,9 +572,7 @@ "authModule": "0xE39d7d526DFd7B6D10972708C77ED19c3f2f5625", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -690,9 +680,7 @@ "authModule": "0x6e0e885F0957086e173D95aAA08191a6c1Ac860d", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -800,9 +788,7 @@ "authModule": "0xD9Af006C3b33a87eE8168B9e25721DFb00FC3d2d", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -910,9 +896,7 @@ "authModule": "0x7A599c9bB4D88F648701B653739688edEfd830E8", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -1019,9 +1003,7 @@ "authModule": "0x6e0e885F0957086e173D95aAA08191a6c1Ac860d", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -1133,9 +1115,7 @@ "authModule": "0x6e0e885F0957086e173D95aAA08191a6c1Ac860d", "tokenDeployer": "0xb28478319B64f8D47e19A120209A211D902F8b8f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -1249,9 +1229,7 @@ "deployer": "0x6f24A47Fc8AE5441Eb47EFfC3665e70e69Ac3F05", "implementationCodehash": "0xc3516b0df24004564d42ebd7c4e4d3814655ea19ec2535627ebcdced0970cccd", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "collector": "0x7F83F5cA2AE4206AbFf8a3C3668e88ce5F11C0B5", @@ -1356,9 +1334,7 @@ "deployer": "0x6f24A47Fc8AE5441Eb47EFfC3665e70e69Ac3F05", "implementationCodehash": "0x327eb5816918332c7b6787ef777587c8f9b39af4ec7b8710cdd037d976b65d5f", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "salt": "AxelarGasService", @@ -1466,9 +1442,7 @@ "deployer": "0x6f24A47Fc8AE5441Eb47EFfC3665e70e69Ac3F05", "implementationCodehash": "0xf9c0cf2181e91d9a39b38b9b1b32a5f170ddd5b6361b9ae5489224cf65d214be", "deploymentMethod": "create3", - "salt": "AxelarGateway v6.2", - "governance": "0x4F0f42bF41ba60895134EC99dE79A041E5269003", - "mintLimiter": "0xF0E17583C906f3e672e591791a88c1116F53081c" + "salt": "AxelarGateway v6.2" }, "AxelarGasService": { "collector": "0x7F83F5cA2AE4206AbFf8a3C3668e88ce5F11C0B5", diff --git a/evm/README.md b/evm/README.md index 8752d1ce5..07c00106a 100644 --- a/evm/README.md +++ b/evm/README.md @@ -1,62 +1,45 @@ # EVM deployment scripts -This folder contains deployment scripts for the following contracts. +This folder contains deployment and operational scripts for various contracts. +By default the version of contracts specified in `package.json` will be used for deployment. ## Setup `npm ci` -For contract verification to work, copy over the appropriate build `artifacts` and `contracts` folder from the source repo into this repo. And update the hardhat config to use the same compiler version and optimizer runs setting. - -You also need to create a `keys.json` file. See `evm/.example.keys.json` for an example. +Add the deployer private key in `.env` folder (see `.example.env` for reference). ## AxelarGateway -1. Compile the contracts from the source repo -2. Copy the `artifacts` folder at the root level of this repo -3. Add the deployer private key in `.env` folder (see `.example.env` for reference) -4. Add additional params in `.env` such as admin addresses, governance etc. -5. If you'd like to auto-verify the contract on the explorer, then add the explorer API key under `keys.json` (see `.example.keys.json`), and add `--verify` flag -6. Run the following depending on the service, - `node evm/deploy-gateway-v5.0.x.js --env testnet -n fantom` +Deploy the gateway contract. + +`node evm/deploy-gateway-v6.2.x.js -e testnet -n ethereum` ## Gateway Upgrade 1. When upgrading the gateway, the proxy contract will be reused. 2. Depending on the upgrade process, Axelar auth and token deployer helper contracts might be reused as well. -3. `node evm/deploy-gateway-v5.0.x.js -e testnet -n fantom --reuseProxy` OR -4. `node evm/deploy-gateway-v5.0.x.js -e testnet -n fantom --reuseProxy --reuseHelpers` +3. `node evm/deploy-gateway-v6.2.x.js -e testnet -n ethereum --reuseProxy` OR +4. `node evm/deploy-gateway-v6.2.x.js -e testnet -n ethereum --reuseProxy --reuseHelpers` 5. This sets the new `implementation` in the chain config. 6. Upgrade to the new implementation contract - `node evm/deploy-gateway-v5.0.x.js -e testnet -n fantom --upgrade` + `node evm/deploy-gateway-v6.2.x.js -e testnet -n ethereum --upgrade` ## AxelarGasService and AxelarDepositService -1. Compile the contracts from the source repo -2. Copy the `artifacts` folder at the root level of this repo -3. Add the deployer private key in `.env` folder (see `.example.env` for reference) -4. If you'd like to auto-verify the contract on the explorer, then add the explorer API key under `keys.json` (see `.example.keys.json`), and add `--verify` flag -5. Use the `--upgrade` flag to upgrade the contract instead -6. Run the following depending on the service, - `node evm/deploy-upgradable.js deploy --env testnet -n fantom -c AxelarGasService -a ../artifacts/contracts/gas-service/` +1. Run the following depending on the service, +`node evm/deploy-upgradable.js -e testnet -n ethereum -c AxelarGasService` +2. Use the `--upgrade` flag to upgrade the contract instead ## InterchainTokenService -Install and copy default setting with - -```bash -npm ci && cp example.env .env -``` - To test the Interchain Token Service deployment ```bash -node evm/deploy-its -n ${chain-name} -s [salt] +node evm/deploy-its -e testnet -n ethereum -s [salt] ``` -Run again with `-v only` to verify deployed contracts. - -You can change `.env` to run the above script to testnet instead of local. Change the `SALT` to get a new address. +Change the `-s SALT` to derive a new address. ## Contract Verification diff --git a/evm/deploy-gateway-v6.2.x.js b/evm/deploy-gateway-v6.2.x.js index bcf5145a4..f4cb96e59 100644 --- a/evm/deploy-gateway-v6.2.x.js +++ b/evm/deploy-gateway-v6.2.x.js @@ -49,7 +49,7 @@ async function checkKeyRotation(config, chain) { } async function getAuthParams(config, chain, options) { - printInfo('Retrieving auth key'); + printInfo(`Retrieving validator addresses for ${chain} from Axelar network`); if (!options.amplifier) { await checkKeyRotation(config, chain); @@ -97,8 +97,8 @@ async function deploy(config, chain, options) { } const contractConfig = chain.contracts[contractName]; - const governance = options.governance || contractConfig.governance || chain.contracts.InterchainGovernance?.address; - const mintLimiter = options.mintLimiter || contractConfig.mintLimiter || chain.contracts.Multisig?.address; + const governance = options.governance || chain.contracts.InterchainGovernance?.address || wallet.address; + const mintLimiter = options.mintLimiter || chain.contracts.Multisig?.address || wallet.address; if (!reuseProxy) { if (governance === undefined) { @@ -110,11 +110,11 @@ async function deploy(config, chain, options) { } if (!(await isContract(governance, provider))) { - printWarn('governance address is not a contract'); + printWarn('Governance address is not a contract. This is optional for test deployments'); } if (!(await isContract(mintLimiter, provider))) { - printWarn('mintLimiter address is not a contract'); + printWarn('MintLimiter address is not a contract. This is optional for test deployments'); } } @@ -236,6 +236,8 @@ async function deploy(config, chain, options) { const params = getProxyParams(governance, mintLimiter); printInfo('Deploying gateway proxy contract'); + printInfo('Governance address', governance); + printInfo('MintLimiter address', mintLimiter); printInfo('Proxy deployment args', `${implementation.address},${params}`); const gatewayProxy = await gatewayProxyFactory.deploy(implementation.address, params, gasOptions); @@ -335,8 +337,6 @@ async function deploy(config, chain, options) { contractConfig.implementationCodehash = implementationCodehash; contractConfig.authModule = auth.address; contractConfig.tokenDeployer = tokenDeployer.address; - contractConfig.governance = governance; - contractConfig.mintLimiter = mintLimiter; contractConfig.deployer = wallet.address; contractConfig.deploymentMethod = options.deployMethod; @@ -344,6 +344,18 @@ async function deploy(config, chain, options) { contractConfig.salt = salt; } + if (!chain.contracts.InterchainGovernance) { + chain.contracts.InterchainGovernance = {}; + } + + chain.contracts.InterchainGovernance.address = governance; + + if (!chain.contracts.Multisig) { + chain.contracts.Multisig = {}; + } + + chain.contracts.Multisig.address = mintLimiter; + printInfo('Deployment status', 'SUCCESS'); saveConfig(config, options.env); @@ -374,11 +386,21 @@ async function upgrade(_, chain, options) { const gateway = new Contract(contractConfig.address, AxelarGateway.abi, wallet); let implementationCodehash = contractConfig.implementationCodehash; - let governance = options.governance || contractConfig.governance || chain.contracts.InterchainGovernance?.address; - let mintLimiter = options.mintLimiter || contractConfig.mintLimiter || chain.contracts.Multisig?.address; + let governance = options.governance || chain.contracts.InterchainGovernance?.address; + let mintLimiter = options.mintLimiter || chain.contracts.Multisig?.address; let setupParams = '0x'; - contractConfig.governance = governance; - contractConfig.mintLimiter = mintLimiter; + + if (!chain.contracts.InterchainGovernance) { + chain.contracts.InterchainGovernance = {}; + } + + chain.contracts.InterchainGovernance.address = governance; + + if (!chain.contracts.Multisig) { + chain.contracts.Multisig = {}; + } + + chain.contracts.Multisig.address = mintLimiter; if (!offline) { if (governance && !(await isContract(governance, provider))) { diff --git a/package.json b/package.json index 8aecd090e..a1e9b6aba 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Axelar contract deployment scripts", "main": "index.js", "scripts": { - "lint": "eslint '**/*.js'", + "lint": "eslint --fix '**/*.js'", "prettier": "prettier --write '**/*.js' 'axelar-chains-config/info/*.json' 'package.json' 'evm/**/*.json' '.github/**/*.yaml'" }, "repository": { From 863b8ff276f9101613b321cb70ebb3ada14ab559 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 2 Nov 2023 18:45:05 -0400 Subject: [PATCH 3/7] feat: create proposal deposit param change script (#104) * feat: create proposal deposit param change script * update readme * lint --- evm/README.md | 5 +++ evm/min-deposit-proposal.js | 86 +++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 evm/min-deposit-proposal.js diff --git a/evm/README.md b/evm/README.md index 07c00106a..789a0e891 100644 --- a/evm/README.md +++ b/evm/README.md @@ -41,6 +41,11 @@ node evm/deploy-its -e testnet -n ethereum -s [salt] Change the `-s SALT` to derive a new address. +## InterchainGovernance + +To update the min deposit on Axelar with a param change proposal, you can generate the proposal via +`node evm/min-deposit-proposal.js -e mainnet -n all --deposit 1000000` + ## Contract Verification ### Prerequisites diff --git a/evm/min-deposit-proposal.js b/evm/min-deposit-proposal.js new file mode 100644 index 000000000..09d49e5e6 --- /dev/null +++ b/evm/min-deposit-proposal.js @@ -0,0 +1,86 @@ +'use strict'; + +require('dotenv').config(); + +const { Command, Option } = require('commander'); +const { mainProcessor, printInfo, isValidNumber, isValidAddress } = require('./utils'); + +const values = []; + +async function processCommand(_, chain, options) { + const { address, deposit } = options; + + printInfo('Chain', chain.name); + + const contracts = chain.contracts; + const contractConfig = contracts.InterchainGovernance; + + let governanceAddress; + + if (isValidAddress(address)) { + governanceAddress = address; + } else { + if (!contractConfig?.address) { + throw new Error(`Governance contract not deployed on ${chain.name}`); + } + + governanceAddress = contractConfig.address; + } + + if (!isValidNumber(deposit)) { + throw new Error('Invalid deposit amount'); + } + + values.push({ + chain: chain.id.toLowerCase(), + contract_address: governanceAddress, + min_deposits: [ + { + denom: 'uaxl', + amount: `${parseInt(deposit) * 1e6}`, + }, + ], + }); +} + +async function main(options) { + await mainProcessor(options, processCommand); + + const paramChange = { + title: 'Update min deposit for governance proposals', + description: `This proposal sets a minimum deposit of ${options.deposit} AXL for any governance proposals for the Axelar gateway contracts.`, + deposit: '2000000000uaxl', + changes: [ + { + subspace: 'axelarnet', + key: 'callContractsProposalMinDeposits', + value: values, + }, + ], + }; + + printInfo('Proposal', JSON.stringify(paramChange, null, 2)); +} + +if (require.main === module) { + const program = new Command(); + + 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')); + program.addOption(new Option('--deposit ', 'min deposit for governance proposals, in terms of AXL')); + + program.action((options) => { + main(options); + }); + + program.parse(); +} From 05756e3a1118a5ce1c0afd0da23236328bbe84b9 Mon Sep 17 00:00:00 2001 From: Stelios Daveas <48155711+sdaveas@users.noreply.github.com> Date: Fri, 3 Nov 2023 17:14:46 +0200 Subject: [PATCH 4/7] add gas options to gateway deployment (#105) --- evm/deploy-gateway-v4.3.x.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/deploy-gateway-v4.3.x.js b/evm/deploy-gateway-v4.3.x.js index fed49ee82..5866bb22d 100644 --- a/evm/deploy-gateway-v4.3.x.js +++ b/evm/deploy-gateway-v4.3.x.js @@ -136,7 +136,7 @@ async function deploy(config, options) { if (skipExisting && contractConfig.implementation) { implementation = gatewayFactory.attach(contractConfig.implementation); } else { - implementation = await gatewayFactory.deploy(auth.address, tokenDeployer.address); + implementation = await gatewayFactory.deploy(auth.address, tokenDeployer.address, gasOptions); await implementation.deployTransaction.wait(chain.confirmations); } From bc7d6c0002484d1d9eba6edb5005a212d574d48e Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 7 Nov 2023 01:26:42 -0500 Subject: [PATCH 5/7] 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 676f7896d..a4751b6f9 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 c02b0ce90..3f125bbba 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 c0368563a..6b3cbeadc 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 8c0553a3b..011c5b258 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 000000000..6ce17f321 --- /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 442e7d579..9218d237d 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 a444ccbf4..9d7afd8bd 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 88c37abda..8f570bf94 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 5866bb22d..866188a47 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 f4cb96e59..75ec39f2c 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 9490cfc9f..7739870ee 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 ba55fe30c..5b82ebc1b 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 3daddeb48..e8189096d 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 ec4a68f82..660de9499 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 83953b06a..6d01f3e7e 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 09d49e5e6..86ffab77c 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 6bf7a851d..d6118800e 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 b7acad61b..205a91b0a 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 f7447d3c4..3cf811d11 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 6b1f4a5ef..8f3bb676d 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 cf0548f08..89f035e05 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 6c740ea62..ff315df11 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 b9f9577b1..12542c670 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 97b0ad170..7703d592d 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 ec58fe045..77eb14b20 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')); From b0890a823f278de823037d261bf17b81a062ad40 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Wed, 8 Nov 2023 18:41:08 -0500 Subject: [PATCH 6/7] feat: add immutable devnet (#107) * feat: add immutable devnet * lint --- axelar-chains-config/info/stagenet.json | 91 ++++++++++++++++ evm/deploy-contract.js | 13 ++- evm/deploy-gateway-v6.2.x.js | 18 +++- evm/deploy-upgradable.js | 2 +- evm/legacy/ConstAddressDeployer.json | 133 ++++++++++++++++++++++++ evm/ownership.js | 14 +-- evm/send-tokens.js | 5 +- evm/upgradable.js | 2 +- evm/utils.js | 6 +- 9 files changed, 266 insertions(+), 18 deletions(-) create mode 100644 evm/legacy/ConstAddressDeployer.json diff --git a/axelar-chains-config/info/stagenet.json b/axelar-chains-config/info/stagenet.json index f48114a0e..b28ea40e5 100644 --- a/axelar-chains-config/info/stagenet.json +++ b/axelar-chains-config/info/stagenet.json @@ -816,6 +816,97 @@ "gasOptions": { "gasLimit": 5000000 } + }, + "immutable-devnet": { + "name": "immutable-devnet", + "id": "immutable-devnet", + "chainId": 15003, + "rpc": "", + "tokenSymbol": "IMX", + "gasOptions": { + "gasLimit": 8000000, + "maxPriorityFeePerGas": 150000000000, + "maxFeePerGas": 160000000000 + }, + "contracts": { + "AxelarGateway": { + "startingKeyIDs": [ + "evm-immutable-devnet-genesis" + ], + "authModule": "0xe0Ad88660B5B09fE9FF8bA6f1dba1A826b1C7583", + "tokenDeployer": "0x42dBF20686794C1a82A95A629373ed2E8CFb5fEb", + "deployer": "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd", + "address": "0x6aF9C075d8C11b9A2CD66bbA801481b3c7A96488", + "implementation": "0x21BB37D5e02Ef479E34f6806C36a506c63741cc1", + "implementationCodehash": "0x25cc2857feace37fdf26f3021ac4af473923d6741611bacf7430f7e72ef99c65", + "deploymentMethod": "create3", + "salt": "AxelarGateway v6.2" + }, + "AxelarGasService": { + "collector": "0xB77A61a241a756E4817c845BEDE7e213a319b1DD", + "salt": "AxelarGasService", + "address": "0x47E840B35e0eca3E7C0577835DD78d95574873e7", + "implementation": "0x102A4324EA951BDd0105798C1fe31f8382EE1309", + "deployer": "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd" + }, + "AxelarDepositService": { + "salt": "AxelarDepositService", + "address": "", + "implementation": "", + "deployer": "", + "wrappedSymbol": "", + "refundIssuer": "0x2517bA7a3E2cef54c1CD8618e7B0B661A7623817" + }, + "ConstAddressDeployer": { + "address": "0x98B2920D53612483F91F12Ed7754E51b4A77919e", + "deployer": "0xE86375704CDb8491a5Ed82D90DceCE02Ee0ac25F", + "deploymentMethod": "create", + "codehash": "0x8fda47a596dfba923270da84e0c32a2d0312f1c03389f83e16f2b5a35ed37fbe", + "predeployCodehash": "0x8fda47a596dfba923270da84e0c32a2d0312f1c03389f83e16f2b5a35ed37fbe" + }, + "Create3Deployer": { + "address": "0x6513Aedb4D1593BA12e50644401D976aebDc90d8", + "deployer": "0x6f24A47Fc8AE5441Eb47EFfC3665e70e69Ac3F05", + "deploymentMethod": "create2", + "codehash": "0xf0ad66defbe082df243d4d274e626f557f97579c5c9e19f33d8093d6160808b7", + "predeployCodehash": "0x73fc31262c4bad113c79439fd231281201c7c7d45b50328bd86bccf37684bf92", + "salt": "Create3Deployer" + }, + "Operators": { + "owner": "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd", + "address": "0xB77A61a241a756E4817c845BEDE7e213a319b1DD", + "deployer": "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd", + "deploymentMethod": "create2", + "codehash": "0xc561dc32ef670c929db9d7fbf6b5f6c074a62a30602481ba3b88912ca6d79feb", + "predeployCodehash": "0xc561dc32ef670c929db9d7fbf6b5f6c074a62a30602481ba3b88912ca6d79feb", + "salt": "Operators" + }, + "Multisig": { + "threshold": 2, + "signers": [ + "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd", + "0xA24156B88D56696a7beE61A38deB5D8aaD8Ceb55", + "0x2517bA7a3E2cef54c1CD8618e7B0B661A7623817" + ], + "address": "0x418FCD3890Ca73f5852eA727505f56396A644873", + "deployer": "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd", + "deploymentMethod": "create3", + "codehash": "0x912095d5076ee40a9dd49c0f9d61d61334c47a78c7512852791652baef26c296", + "predeployCodehash": "0x912095d5076ee40a9dd49c0f9d61d61334c47a78c7512852791652baef26c296", + "salt": "Multisig v5.5" + }, + "InterchainGovernance": { + "minimumTimeDelay": 300, + "governanceChain": "Axelarnet", + "governanceAddress": "axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj", + "address": "0xf6c4679c0FdA9b4e49fCcfedCEE03a730d35E64D", + "deployer": "0x9D97cf3AC20b73c81d8A5233d9FBe09618d4F8bd", + "deploymentMethod": "create3", + "codehash": "0xa8c5d3eab2aefc62e03bcf3c5e1db27cbce0e24d3f87d8dfbbb54676bfbf937c", + "predeployCodehash": "0xe2de43b29f2387b6f3575a1b50d566908fc00e03a8d88ad6be74b674a70874d2", + "salt": "InterchainGovernance v5.5" + } + } } }, "axelar": { diff --git a/evm/deploy-contract.js b/evm/deploy-contract.js index 9d7afd8bd..ab5d1f634 100644 --- a/evm/deploy-contract.js +++ b/evm/deploy-contract.js @@ -221,6 +221,10 @@ async function processCommand(config, chain, options) { const { env, artifactPath, contractName, deployMethod, privateKey, verify, yes } = options; const verifyOptions = verify ? { env, chain: chain.name, only: verify === 'only' } : null; + if (!chain.contracts) { + chain.contracts = {}; + } + const contracts = chain.contracts; if (!contracts[contractName]) { @@ -288,7 +292,14 @@ async function processCommand(config, chain, options) { printInfo('Deployer contract', deployerContract); printInfo(`${contractName} will be deployed to`, predictedAddress); - if (prompt(`Does derived address match existing deployments? Proceed with deployment on ${chain.name}?`, yes)) { + const existingAddress = config.chains.ethereum?.contracts?.[contractName]?.address; + + if (existingAddress !== undefined && predictedAddress !== existingAddress) { + printWarn(`Predicted address ${predictedAddress} does not match existing deployment ${existingAddress} on chain ${chain.name}.`); + printWarn('For official deployment, recheck the deployer, salt, args, or contract bytecode.'); + } + + if (prompt(`Proceed with deployment on ${chain.name}?`, yes)) { return; } diff --git a/evm/deploy-gateway-v6.2.x.js b/evm/deploy-gateway-v6.2.x.js index 75ec39f2c..5365c44c3 100644 --- a/evm/deploy-gateway-v6.2.x.js +++ b/evm/deploy-gateway-v6.2.x.js @@ -93,6 +93,10 @@ async function deploy(config, chain, options) { const wallet = new Wallet(privateKey).connect(provider); await printWalletInfo(wallet); + if (chain.contracts === undefined) { + chain.contracts = {}; + } + if (chain.contracts[contractName] === undefined) { chain.contracts[contractName] = {}; } @@ -117,6 +121,9 @@ async function deploy(config, chain, options) { if (!(await isContract(mintLimiter, provider))) { printWarn('MintLimiter address is not a contract. This is optional for test deployments'); } + + printInfo('Governance address', governance); + printInfo('MintLimiter address', mintLimiter); } const gatewayFactory = new ContractFactory(AxelarGateway.abi, AxelarGateway.bytecode, wallet); @@ -161,7 +168,9 @@ async function deploy(config, chain, options) { contractConfig.deployer = wallet.address; - if (reuseProxy && reuseHelpers) { + if (options.skipExisting && contractConfig.authModule) { + auth = authFactory.attach(contractConfig.authModule); + } else if (reuseProxy && reuseHelpers) { auth = authFactory.attach(await gateway.authModule()); } else { printInfo(`Deploying auth contract`); @@ -180,7 +189,9 @@ async function deploy(config, chain, options) { }); } - if (reuseProxy && reuseHelpers) { + if (options.skipExisting && contractConfig.tokenDeployer) { + tokenDeployer = tokenDeployerFactory.attach(contractConfig.tokenDeployer); + } else if (reuseProxy && reuseHelpers) { tokenDeployer = tokenDeployerFactory.attach(await gateway.tokenDeployer()); } else { printInfo(`Deploying token deployer contract`); @@ -237,8 +248,6 @@ async function deploy(config, chain, options) { const params = getProxyParams(governance, mintLimiter); printInfo('Deploying gateway proxy contract'); - printInfo('Governance address', governance); - printInfo('MintLimiter address', mintLimiter); printInfo('Proxy deployment args', `${implementation.address},${params}`); const gatewayProxy = await gatewayProxyFactory.deploy(implementation.address, params, gasOptions); @@ -521,6 +530,7 @@ async function programHandler() { program.addOption(new Option('--prevKeyIDs ', 'previous key IDs to be used for auth contract')); 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-upgradable.js b/evm/deploy-upgradable.js index 5b82ebc1b..7179e2683 100644 --- a/evm/deploy-upgradable.js +++ b/evm/deploy-upgradable.js @@ -131,7 +131,7 @@ async function deploy(options, chain) { const implArgs = await getImplementationArgs(contractName, contracts, options); const gasOptions = contractConfig.gasOptions || chain.gasOptions || {}; printInfo(`Implementation args for chain ${chain.name}`, implArgs); - console.log(`Gas override for chain ${chain.name}: ${JSON.stringify(gasOptions)}`); + console.log(`Gas override for chain ${chain.name}: ${JSON.stringify(gasOptions, null, 2)}`); const salt = options.salt || contractName; let deployerContract = deployMethod === 'create3' ? contracts.Create3Deployer?.address : contracts.ConstAddressDeployer?.address; diff --git a/evm/legacy/ConstAddressDeployer.json b/evm/legacy/ConstAddressDeployer.json new file mode 100644 index 000000000..94463ed77 --- /dev/null +++ b/evm/legacy/ConstAddressDeployer.json @@ -0,0 +1,133 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ConstAddressDeployer", + "sourceName": "contracts/ConstAddressDeployer.sol", + "abi": [ + { + "inputs": [], + "name": "EmptyBytecode", + "type": "error" + }, + { + "inputs": [], + "name": "FailedDeploy", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInit", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "bytecodeHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "deployedAddress", + "type": "address" + } + ], + "name": "Deployed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "bytecode", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "address", + "name": "deployedAddress_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "bytecode", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "init", + "type": "bytes" + } + ], + "name": "deployAndInit", + "outputs": [ + { + "internalType": "address", + "name": "deployedAddress_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "bytecode", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "deployedAddress", + "outputs": [ + { + "internalType": "address", + "name": "deployedAddress_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506105a4806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80634af63f0214610046578063c2b1041c14610075578063cf4d643214610088575b600080fd5b6100596100543660046103f7565b61009b565b6040516001600160a01b03909116815260200160405180910390f35b610059610083366004610485565b6100da565b6100596100963660046104ee565b6101a9565b604080513360208201529081018290526000906100d39084906060015b6040516020818303038152906040528051906020012061026e565b9392505050565b604080516001600160a01b038416602082015290810182905260009081906060016040516020818303038152906040528051906020012090503081878760405161012592919061055e565b6040519081900381206101879392916020017fff00000000000000000000000000000000000000000000000000000000000000815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b604080513360208201529081018490526000906101ca9086906060016100b8565b90506000816001600160a01b031684846040516101e892919061055e565b6000604051808303816000865af19150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b5050905080610265576040517f4f77232300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60008251600014156102ac576040517f21744a5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818351602085016000f590506001600160a01b0381166102f8576040517f4102e83a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401206040516001600160a01b0383169184917f27b8e3132afa95254770e1c1d214eafde52bc47d1b6e1f5dfcbb380c3ca3f53290600090a492915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261037b57600080fd5b813567ffffffffffffffff808211156103965761039661033b565b604051601f8301601f19908116603f011681019082821181831017156103be576103be61033b565b816040528381528660208588010111156103d757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561040a57600080fd5b823567ffffffffffffffff81111561042157600080fd5b61042d8582860161036a565b95602094909401359450505050565b60008083601f84011261044e57600080fd5b50813567ffffffffffffffff81111561046657600080fd5b60208301915083602082850101111561047e57600080fd5b9250929050565b6000806000806060858703121561049b57600080fd5b843567ffffffffffffffff8111156104b257600080fd5b6104be8782880161043c565b90955093505060208501356001600160a01b03811681146104de57600080fd5b9396929550929360400135925050565b6000806000806060858703121561050457600080fd5b843567ffffffffffffffff8082111561051c57600080fd5b6105288883890161036a565b955060208701359450604087013591508082111561054557600080fd5b506105528782880161043c565b95989497509550505050565b818382376000910190815291905056fea26469706673582212206a8d0537e27fe239a39d5ad8e5334db0447d0a1d7d49a7e82479baaa1319b39664736f6c63430008090033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c80634af63f0214610046578063c2b1041c14610075578063cf4d643214610088575b600080fd5b6100596100543660046103f7565b61009b565b6040516001600160a01b03909116815260200160405180910390f35b610059610083366004610485565b6100da565b6100596100963660046104ee565b6101a9565b604080513360208201529081018290526000906100d39084906060015b6040516020818303038152906040528051906020012061026e565b9392505050565b604080516001600160a01b038416602082015290810182905260009081906060016040516020818303038152906040528051906020012090503081878760405161012592919061055e565b6040519081900381206101879392916020017fff00000000000000000000000000000000000000000000000000000000000000815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b604080513360208201529081018490526000906101ca9086906060016100b8565b90506000816001600160a01b031684846040516101e892919061055e565b6000604051808303816000865af19150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b5050905080610265576040517f4f77232300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60008251600014156102ac576040517f21744a5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818351602085016000f590506001600160a01b0381166102f8576040517f4102e83a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401206040516001600160a01b0383169184917f27b8e3132afa95254770e1c1d214eafde52bc47d1b6e1f5dfcbb380c3ca3f53290600090a492915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261037b57600080fd5b813567ffffffffffffffff808211156103965761039661033b565b604051601f8301601f19908116603f011681019082821181831017156103be576103be61033b565b816040528381528660208588010111156103d757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806040838503121561040a57600080fd5b823567ffffffffffffffff81111561042157600080fd5b61042d8582860161036a565b95602094909401359450505050565b60008083601f84011261044e57600080fd5b50813567ffffffffffffffff81111561046657600080fd5b60208301915083602082850101111561047e57600080fd5b9250929050565b6000806000806060858703121561049b57600080fd5b843567ffffffffffffffff8111156104b257600080fd5b6104be8782880161043c565b90955093505060208501356001600160a01b03811681146104de57600080fd5b9396929550929360400135925050565b6000806000806060858703121561050457600080fd5b843567ffffffffffffffff8082111561051c57600080fd5b6105288883890161036a565b955060208701359450604087013591508082111561054557600080fd5b506105528782880161043c565b95989497509550505050565b818382376000910190815291905056fea26469706673582212206a8d0537e27fe239a39d5ad8e5334db0447d0a1d7d49a7e82479baaa1319b39664736f6c63430008090033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/evm/ownership.js b/evm/ownership.js index 3cf811d11..517573f02 100644 --- a/evm/ownership.js +++ b/evm/ownership.js @@ -20,7 +20,7 @@ async function processCommand(options, chain) { const { contractName, address, action, privateKey, newOwner, yes } = options; const contracts = chain.contracts; - const contractConfig = contracts[contractName]; + const contractConfig = contracts[contractName] || {}; let ownershipAddress; @@ -46,7 +46,7 @@ async function processCommand(options, chain) { const ownershipContract = new Contract(ownershipAddress, IOwnable.abi, wallet); const gasOptions = contractConfig.gasOptions || chain.gasOptions || {}; - printInfo(`Gas override for ${chain.name}`, JSON.stringify(gasOptions)); + printInfo(`Gas override for ${chain.name}`, JSON.stringify(gasOptions, null, 2)); printInfo('Ownership Action', action); @@ -78,7 +78,7 @@ async function processCommand(options, chain) { let owner = await ownershipContract.owner(); if (owner.toLowerCase() !== wallet.address.toLowerCase()) { - throw new Error(`Caller ${wallet.address} is not the contract owner.`); + throw new Error(`Caller ${wallet.address} is not the contract owner but ${owner} is.`); } if (!isAddress(newOwner) || newOwner === AddressZero) { @@ -86,7 +86,7 @@ async function processCommand(options, chain) { } try { - await ownershipContract.transferOwnership(newOwner).then((tx) => tx.wait()); + await ownershipContract.transferOwnership(newOwner, gasOptions).then((tx) => tx.wait()); } catch (error) { throw new Error(error); } @@ -116,7 +116,7 @@ async function processCommand(options, chain) { } try { - await ownershipContract.proposeOwnership(newOwner).then((tx) => tx.wait()); + await ownershipContract.proposeOwnership(newOwner, gasOptions).then((tx) => tx.wait()); } catch (error) { throw new Error(error); } @@ -144,7 +144,7 @@ async function processCommand(options, chain) { } try { - await ownershipContract.acceptOwnership().then((tx) => tx.wait()); + await ownershipContract.acceptOwnership(gasOptions).then((tx) => tx.wait()); } catch (error) { throw new Error(error); } @@ -196,7 +196,7 @@ if (require.main === module) { addBaseOptions(program, { address: true }); - program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); + program.addOption(new Option('-c, --contractName ', 'contract name')); program.addOption( new Option('--action ', 'ownership action').choices([ 'owner', diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 89f035e05..324450310 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -9,7 +9,7 @@ const { getDefaultProvider, utils: { parseEther, parseUnits }, } = ethers; -const { printInfo, printError, printWalletInfo, isAddressArray, mainProcessor, isValidDecimal, prompt } = require('./utils'); +const { printInfo, printError, printWalletInfo, isAddressArray, mainProcessor, isValidDecimal, prompt, copyObject } = require('./utils'); const { addBaseOptions } = require('./cli-utils'); const { storeSignedTx, getWallet, signTransaction } = require('./sign-utils.js'); @@ -51,6 +51,8 @@ async function processCommand(_, chain, options) { printInfo('Chain', chain.name); + const gasOptions = copyObject(chain.gasOptions || {}); + if ( prompt( `Proceed with the transfer of ${chalk.green(amountStr)} ${chalk.green(chain.tokenSymbol)} to ${recipients} on ${chain.name}?`, @@ -66,6 +68,7 @@ async function processCommand(_, chain, options) { const tx = { to: recipient, value: amount, + ...gasOptions, }; if (!offline && chain.name.toLowerCase() === 'binance') { diff --git a/evm/upgradable.js b/evm/upgradable.js index 649df6897..6c673245f 100644 --- a/evm/upgradable.js +++ b/evm/upgradable.js @@ -48,7 +48,7 @@ async function deployCreate2Upgradable( txOptions = null, verifyOptions, ) { - const implementation = await deployCreate(wallet, implementationJson, implementationConstructorArgs, {}, verifyOptions); + const implementation = await deployCreate(wallet, implementationJson, implementationConstructorArgs, txOptions, verifyOptions); const proxy = await deployAndInitContractConstant( constAddressDeployerAddress, diff --git a/evm/utils.js b/evm/utils.js index 7703d592d..7770d04c2 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -34,7 +34,7 @@ const getSaltFromKey = (key) => { const deployCreate = async (wallet, contractJson, args = [], options = {}, verifyOptions = null, chain = {}) => { const factory = new ContractFactory(contractJson.abi, contractJson.bytecode, wallet); - const contract = await factory.deploy(...args, { ...options }); + const contract = await factory.deploy(...args, options); await contract.deployTransaction.wait(chain.confirmations); if (verifyOptions?.env) { @@ -839,7 +839,7 @@ function getContractJSON(contractName, artifactPath) { let contractPath; if (artifactPath) { - contractPath = artifactPath.charAt(0) === '@' ? artifactPath : artifactPath + contractName + '.sol/' + contractName + '.json'; + contractPath = artifactPath.endsWith('.json') ? artifactPath : artifactPath + contractName + '.sol/' + contractName + '.json'; } else { contractPath = getContractPath(contractName); } @@ -850,7 +850,7 @@ function getContractJSON(contractName, artifactPath) { const contractJson = require(contractPath); return contractJson; } catch (err) { - throw new Error(`Failed to load contract JSON for ${contractName} at path ${contractPath}`); + throw new Error(`Failed to load contract JSON for ${contractName} at path ${contractPath} with error: ${err}`); } } From d0c520b777d0e79d8b004c2e19d8fdea3fcb2782 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 9 Nov 2023 13:47:09 -0500 Subject: [PATCH 7/7] fix: improve gateway script to transfer permissions (#111) --- evm/deploy-contract.js | 3 ++- evm/deploy-gateway-v6.2.x.js | 3 ++- evm/gateway.js | 50 +++++++++++++++++++++++++++++++++--- evm/utils.js | 8 +++--- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/evm/deploy-contract.js b/evm/deploy-contract.js index ab5d1f634..1655c8a47 100644 --- a/evm/deploy-contract.js +++ b/evm/deploy-contract.js @@ -2,6 +2,7 @@ require('dotenv').config(); +const chalk = require('chalk'); const { ethers } = require('hardhat'); const { Wallet, @@ -290,7 +291,7 @@ async function processCommand(config, chain, options) { printInfo('Deployment method', deployMethod); printInfo('Deployer contract', deployerContract); - printInfo(`${contractName} will be deployed to`, predictedAddress); + printInfo(`${contractName} will be deployed to`, predictedAddress, chalk.cyan); const existingAddress = config.chains.ethereum?.contracts?.[contractName]?.address; diff --git a/evm/deploy-gateway-v6.2.x.js b/evm/deploy-gateway-v6.2.x.js index 5365c44c3..7c0839d23 100644 --- a/evm/deploy-gateway-v6.2.x.js +++ b/evm/deploy-gateway-v6.2.x.js @@ -3,6 +3,7 @@ require('dotenv').config(); const { Command, Option } = require('commander'); +const chalk = require('chalk'); const { ethers } = require('hardhat'); const { ContractFactory, @@ -149,7 +150,7 @@ async function deploy(config, chain, options) { from: wallet.address, nonce: transactionCount + 3, }); - printInfo('Predicted proxy address', proxyAddress); + printInfo('Predicted proxy address', proxyAddress, chalk.cyan); } const gasOptions = JSON.parse(JSON.stringify(contractConfig.gasOptions || chain.gasOptions || {})); diff --git a/evm/gateway.js b/evm/gateway.js index 660de9499..820d7e130 100644 --- a/evm/gateway.js +++ b/evm/gateway.js @@ -2,6 +2,7 @@ require('dotenv').config(); +const chalk = require('chalk'); const { ethers } = require('hardhat'); const { getDefaultProvider, @@ -264,7 +265,7 @@ async function processCommand(config, chain, options) { } case 'transferGovernance': { - const newGovernance = options.destination; + const newGovernance = options.destination || chain.contracts.InterchainGovernance?.address; if (!isValidAddress(newGovernance)) { throw new Error('Invalid new governor address'); @@ -277,7 +278,7 @@ async function processCommand(config, chain, options) { throw new Error('Wallet address is not the governor'); } - if (prompt(`Proceed with governance transfer to ${newGovernance}`, yes)) { + if (prompt(`Proceed with governance transfer to ${chalk.cyan(newGovernance)}`, yes)) { return; } @@ -292,7 +293,11 @@ async function processCommand(config, chain, options) { throw new Error('Event not emitted in receipt.'); } - contracts.AxelarGateway.governance = newGovernance; + if (!chain.contracts.InterchainGovernance) { + chain.contracts.InterchainGovernance = {}; + } + + chain.contracts.InterchainGovernance.address = newGovernance; break; } @@ -307,6 +312,44 @@ async function processCommand(config, chain, options) { break; } + case 'transferMintLimiter': { + const newMintLimiter = options.destination || chain.contracts.Multisig?.address; + + if (!isValidAddress(newMintLimiter)) { + throw new Error('Invalid address'); + } + + const currMintLimiter = await gateway.mintLimiter(); + printInfo('Current governance', currMintLimiter); + + if (!(currMintLimiter === walletAddress)) { + throw new Error('Wallet address is not the mint limiter'); + } + + if (prompt(`Proceed with mint limiter transfer to ${chalk.cyan(newMintLimiter)}`, yes)) { + return; + } + + const tx = await gateway.transferMintLimiter(newMintLimiter, gasOptions); + printInfo('Transfer mint limiter tx', tx.hash); + + const receipt = await tx.wait(chain.confirmations); + + const eventEmitted = wasEventEmitted(receipt, gateway, 'MintLimiterTransferred'); + + if (!eventEmitted) { + throw new Error('Event not emitted in receipt.'); + } + + if (!chain.contracts.Multisig) { + chain.contracts.Multisig = {}; + } + + chain.contracts.Multisig.address = newMintLimiter; + + break; + } + case 'mintLimit': { if (!options.symbol) { throw new Error('Missing symbol'); @@ -348,6 +391,7 @@ if (require.main === module) { 'transferGovernance', 'governance', 'mintLimiter', + 'transferMintLimiter', 'mintLimit', 'params', ]) diff --git a/evm/utils.js b/evm/utils.js index 7770d04c2..6d4489896 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -120,9 +120,9 @@ const deployCreate3 = async ( return contract; }; -const printInfo = (msg, info = '') => { +const printInfo = (msg, info = '', colour = chalk.green) => { if (info) { - console.log(`${msg}: ${chalk.green(info)}\n`); + console.log(`${msg}: ${colour(info)}\n`); } else { console.log(`${msg}\n`); } @@ -133,7 +133,7 @@ const printWarn = (msg, info = '') => { msg = `${msg}: ${info}`; } - console.log(`${chalk.yellow(msg)}\n`); + console.log(`${chalk.italic.yellow(msg)}\n`); }; const printError = (msg, info = '') => { @@ -141,7 +141,7 @@ const printError = (msg, info = '') => { msg = `${msg}: ${info}`; } - console.log(`${chalk.red(msg)}\n`); + console.log(`${chalk.bold.red(msg)}\n`); }; function printLog(log) {