From d2b591116e728f78166c214b19b54b7afd13eabe Mon Sep 17 00:00:00 2001 From: Edwin Guajardo Date: Fri, 22 Nov 2024 15:14:26 -0600 Subject: [PATCH 1/3] add subcommand to register a chain to ITS hub --- cosmwasm/README.md | 34 +++++++++++++++++++++++++++ cosmwasm/cli-utils.js | 12 ++++++++++ cosmwasm/submit-proposal.js | 47 +++++++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/cosmwasm/README.md b/cosmwasm/README.md index 7836ca9c..0f758e8a 100644 --- a/cosmwasm/README.md +++ b/cosmwasm/README.md @@ -214,6 +214,40 @@ Example usage: node cosmwasm/submit-proposal.js execute -c Router -t "Proposal title" -d "Proposal description" --deposit 100000000 --msg '{"register_chain":{"chain":"avalanche","gateway_address":"axelar17cnq5hujmkf2lr2c5hatqmhzlvwm365rqc5ugryphxeftavjef9q89zxvp","msg_id_format":"hex_tx_hash_and_event_index"}}' ``` +### Register chain to ITS Hub through governance proposal + +To submit a governance proposal to register an ITS chain, use the `submit-proposal` script with the `register-its-chain` command. The `--chains` option must be used to pass a comma separated list of chains to register to ITS hub. + +**Prerequisites**: Chain configuration in json file must include the following attributes per chain: + +| Attribute | Description | EVM | Sui | +| --------------------------- | ------------------------------------------------------------------------------------------ | --- | --- | +| `maxUintBits` | Number of bits for the chain's maximum uint representation | 256 | 64 | +| `maxDecimalsWhenTruncating` | Maximum decimal places allowed when truncating ITS token amounts transferred to this chain | 255 | 6 | + +Example configuration: + +``` +"chains": { + "some-sui-chain": { + "axelarId": "some-sui-chain", + "maxUintBits": 64, + "maxDecimalsWhenTruncating": 6, + }, + "some-evm-chain": { + "axelarId": "some-evm-chain", + "maxUintBits": 256, + "maxDecimalsWhenTruncating": 255, + } +} +``` + +Example usage: + +``` +node cosmwasm/submit-proposal.js register-its-chain --chains avalanche-fuji,sui-test2 -t "Proposal title" -d "Proposal description" --deposit 100000000 -r $RUN_AS_ACCOUNT +``` + ### Submit a proposal to change a parameter To submit a governance proposal to change a parameter, use the `submit-proposal` script with the `paramChange` command. The `--changes` option should be used to pass a JSON string representing an array of parameter changes. diff --git a/cosmwasm/cli-utils.js b/cosmwasm/cli-utils.js index 75746a07..8f2e4884 100644 --- a/cosmwasm/cli-utils.js +++ b/cosmwasm/cli-utils.js @@ -41,6 +41,10 @@ const addAmplifierOptions = (program, options) => { addExecuteProposalOptions(program); } + if (options.registerItsChainOptions) { + addRegisterItsChainOptions(program); + } + if (options.paramChangeProposalOptions) { addParamChangeProposalOptions(program); } @@ -121,6 +125,14 @@ const addExecuteProposalOptions = (program) => { program.addOption(new Option('--msg ', 'json encoded execute message').makeOptionMandatory(true)); }; +const addRegisterItsChainOptions = (program) => { + program.addOption( + new Option('--chains ', 'comma separated list of chains to register to ITS hub') + .argParser((val) => val.split(',')) + .makeOptionMandatory(true), + ); +}; + const addParamChangeProposalOptions = (program) => { program.addOption(new Option('--changes ', 'parameter changes')); }; diff --git a/cosmwasm/submit-proposal.js b/cosmwasm/submit-proposal.js index f2e3a7a4..1a818dbc 100644 --- a/cosmwasm/submit-proposal.js +++ b/cosmwasm/submit-proposal.js @@ -27,7 +27,7 @@ const { submitProposal, makeInstantiateMsg, } = require('./utils'); -const { saveConfig, loadConfig, printInfo, prompt } = require('../common'); +const { saveConfig, loadConfig, printInfo, prompt, getChainConfig } = require('../common'); const { StoreCodeProposal, StoreAndInstantiateContractProposal, @@ -161,6 +161,41 @@ const execute = async (client, wallet, config, options) => { await callSubmitProposal(client, wallet, config, options, proposal); }; +const registerItsChain = async (client, wallet, config, options) => { + const chains = options.chains.map((chain) => { + const chainConfig = getChainConfig(config, chain); + + if (!chainConfig.maxUintBits) { + throw new Error(`Missing maxUintBits for chain ${chain}`); + } + + if (!chainConfig.maxDecimalsWhenTruncating) { + throw new Error(`Missing maxDecimalsWhenTruncating for chain ${chain}`); + } + + const itsEdgeContract = chainConfig.contracts.InterchainTokenService?.address || chainConfig.contracts.ITS?.objects?.ChannelId; + + if (!itsEdgeContract) { + throw new Error(`Missing ITS edge contract for chain ${chain}`); + } + + return { + chain: chainConfig.axelarId, + its_edge_contract: itsEdgeContract, + truncation: { + max_uint: (2n ** BigInt(chainConfig.maxUintBits) - 1n).toString(), + max_decimals_when_truncating: chainConfig.maxDecimalsWhenTruncating, + }, + }; + }); + + await execute(client, wallet, config, { + ...options, + contractName: 'InterchainTokenService', + msg: `{ "register_chains": { "chains": ${JSON.stringify(chains)} } }`, + }); +}; + const paramChange = async (client, wallet, config, options) => { const proposal = encodeParameterChangeProposal(options); @@ -250,12 +285,20 @@ const programHandler = () => { const executeCmd = program .command('execute') - .description('Submit a execute wasm contract proposal') + .description('Submit an execute wasm contract proposal') .action((options) => { mainProcessor(execute, options); }); addAmplifierOptions(executeCmd, { contractOptions: true, executeProposalOptions: true, proposalOptions: true, runAs: true }); + const registerItsChainCmd = program + .command('register-its-chain') + .description('Submit an execute wasm contract proposal to register an ITS chain') + .action((options) => { + mainProcessor(registerItsChain, options); + }); + addAmplifierOptions(registerItsChainCmd, { registerItsChainOptions: true, proposalOptions: true, runAs: true }); + const paramChangeCmd = program .command('paramChange') .description('Submit a parameter change proposal') From 4d53b666cf43459c894047fd8548dead8f8e1684 Mon Sep 17 00:00:00 2001 From: Edwin Guajardo Date: Tue, 26 Nov 2024 11:58:53 -0600 Subject: [PATCH 2/3] review --- common/utils.js | 11 +++++++++++ cosmwasm/README.md | 31 +++++++++++++++++-------------- cosmwasm/cli-utils.js | 12 ------------ cosmwasm/submit-proposal.js | 28 ++++++++++------------------ cosmwasm/utils.js | 23 +++++++++++++++++++++++ 5 files changed, 61 insertions(+), 44 deletions(-) diff --git a/common/utils.js b/common/utils.js index a63f1eb6..8fa77a69 100644 --- a/common/utils.js +++ b/common/utils.js @@ -440,6 +440,16 @@ const getMultisigProof = async (config, chain, multisigSessionId) => { const calculateDomainSeparator = (chain, router, network) => keccak256(Buffer.from(`${chain}${router}${network}`)); +const getItsEdgeContract = (chainConfig) => { + const itsEdgeContract = chainConfig.contracts.InterchainTokenService?.address || chainConfig.contracts.ITS?.objects?.ChannelId; + + if (!itsEdgeContract) { + throw new Error(`Missing ITS edge contract for chain ${chainConfig.name}`); + } + + return itsEdgeContract; +}; + module.exports = { loadConfig, saveConfig, @@ -478,4 +488,5 @@ module.exports = { getAmplifierContractOnchainConfig, getSaltFromKey, calculateDomainSeparator, + getItsEdgeContract, }; diff --git a/cosmwasm/README.md b/cosmwasm/README.md index 1bf73530..b32e9488 100644 --- a/cosmwasm/README.md +++ b/cosmwasm/README.md @@ -216,30 +216,33 @@ Example usage: node cosmwasm/submit-proposal.js execute -c Router -t "Proposal title" -d "Proposal description" --deposit 100000000 --msg '{"register_chain":{"chain":"avalanche","gateway_address":"axelar17cnq5hujmkf2lr2c5hatqmhzlvwm365rqc5ugryphxeftavjef9q89zxvp","msg_id_format":"hex_tx_hash_and_event_index"}}' ``` -### Register chain to ITS Hub through governance proposal +### Register chain on ITS Hub through governance proposal -To submit a governance proposal to register an ITS chain, use the `submit-proposal` script with the `register-its-chain` command. The `--chains` option must be used to pass a comma separated list of chains to register to ITS hub. +To submit a governance proposal to register an ITS chain, use the `submit-proposal` script with the `its-hub-register-chains ` command. The `chains` argument is used to pass a list of chains to register on ITS hub. -**Prerequisites**: Chain configuration in json file must include the following attributes per chain: +**Prerequisites**: ITS hub contract configuration in json file must include the following attributes per chain: | Attribute | Description | EVM | Sui | | --------------------------- | ------------------------------------------------------------------------------------------ | --- | --- | | `maxUintBits` | Number of bits for the chain's maximum uint representation | 256 | 64 | | `maxDecimalsWhenTruncating` | Maximum decimal places allowed when truncating ITS token amounts transferred to this chain | 255 | 6 | +For EVM chains, the values above are used by default if not specified explicitly. + Example configuration: ``` -"chains": { - "some-sui-chain": { - "axelarId": "some-sui-chain", - "maxUintBits": 64, - "maxDecimalsWhenTruncating": 6, - }, - "some-evm-chain": { - "axelarId": "some-evm-chain", - "maxUintBits": 256, - "maxDecimalsWhenTruncating": 255, +"axelar": { + "contracts": { + ... + "InterchainTokenService": { + ... + "some-sui-chain": { + "maxUintBits": 64, + "maxDecimalsWhenTruncating": 6, + } + } + ... } } ``` @@ -247,7 +250,7 @@ Example configuration: Example usage: ``` -node cosmwasm/submit-proposal.js register-its-chain --chains avalanche-fuji,sui-test2 -t "Proposal title" -d "Proposal description" --deposit 100000000 -r $RUN_AS_ACCOUNT +node cosmwasm/submit-proposal.js its-hub-register-chains avalanche-fuji sui-test2 -t "Proposal title" -d "Proposal description" --deposit 100000000 -r $RUN_AS_ACCOUNT ``` ### Submit a proposal to change a parameter diff --git a/cosmwasm/cli-utils.js b/cosmwasm/cli-utils.js index 8f2e4884..75746a07 100644 --- a/cosmwasm/cli-utils.js +++ b/cosmwasm/cli-utils.js @@ -41,10 +41,6 @@ const addAmplifierOptions = (program, options) => { addExecuteProposalOptions(program); } - if (options.registerItsChainOptions) { - addRegisterItsChainOptions(program); - } - if (options.paramChangeProposalOptions) { addParamChangeProposalOptions(program); } @@ -125,14 +121,6 @@ const addExecuteProposalOptions = (program) => { program.addOption(new Option('--msg ', 'json encoded execute message').makeOptionMandatory(true)); }; -const addRegisterItsChainOptions = (program) => { - program.addOption( - new Option('--chains ', 'comma separated list of chains to register to ITS hub') - .argParser((val) => val.split(',')) - .makeOptionMandatory(true), - ); -}; - const addParamChangeProposalOptions = (program) => { program.addOption(new Option('--changes ', 'parameter changes')); }; diff --git a/cosmwasm/submit-proposal.js b/cosmwasm/submit-proposal.js index 095bbc2a..92c29a15 100644 --- a/cosmwasm/submit-proposal.js +++ b/cosmwasm/submit-proposal.js @@ -16,6 +16,7 @@ const { getAmplifierBaseContractConfig, getAmplifierContractConfig, updateCodeId, + getChainTruncationParams, decodeProposalAttributes, encodeStoreCodeProposal, encodeStoreInstantiateProposal, @@ -27,7 +28,7 @@ const { submitProposal, makeInstantiateMsg, } = require('./utils'); -const { saveConfig, loadConfig, printInfo, prompt, getChainConfig } = require('../common'); +const { saveConfig, loadConfig, printInfo, prompt, getChainConfig, getItsEdgeContract } = require('../common'); const { StoreCodeProposal, StoreAndInstantiateContractProposal, @@ -170,27 +171,16 @@ const execute = async (client, wallet, config, options) => { const registerItsChain = async (client, wallet, config, options) => { const chains = options.chains.map((chain) => { const chainConfig = getChainConfig(config, chain); + const { maxUintBits, maxDecimalsWhenTruncating } = getChainTruncationParams(config, chainConfig); - if (!chainConfig.maxUintBits) { - throw new Error(`Missing maxUintBits for chain ${chain}`); - } - - if (!chainConfig.maxDecimalsWhenTruncating) { - throw new Error(`Missing maxDecimalsWhenTruncating for chain ${chain}`); - } - - const itsEdgeContract = chainConfig.contracts.InterchainTokenService?.address || chainConfig.contracts.ITS?.objects?.ChannelId; - - if (!itsEdgeContract) { - throw new Error(`Missing ITS edge contract for chain ${chain}`); - } + const itsEdgeContract = getItsEdgeContract(chainConfig); return { chain: chainConfig.axelarId, its_edge_contract: itsEdgeContract, truncation: { - max_uint: (2n ** BigInt(chainConfig.maxUintBits) - 1n).toString(), - max_decimals_when_truncating: chainConfig.maxDecimalsWhenTruncating, + max_uint: (2n ** BigInt(maxUintBits) - 1n).toString(), + max_decimals_when_truncating: maxDecimalsWhenTruncating, }, }; }); @@ -298,9 +288,11 @@ const programHandler = () => { addAmplifierOptions(executeCmd, { contractOptions: true, executeProposalOptions: true, proposalOptions: true, runAs: true }); const registerItsChainCmd = program - .command('register-its-chain') + .command('its-hub-register-chains') .description('Submit an execute wasm contract proposal to register an ITS chain') - .action((options) => { + .argument('', 'list of chains to register on ITS hub') + .action((chains, options) => { + options.chains = chains; mainProcessor(registerItsChain, options); }); addAmplifierOptions(registerItsChainCmd, { registerItsChainOptions: true, proposalOptions: true, runAs: true }); diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js index ab902c55..284a2fe9 100644 --- a/cosmwasm/utils.js +++ b/cosmwasm/utils.js @@ -29,9 +29,13 @@ const { getChainConfig, getSaltFromKey, calculateDomainSeparator, + validateParameters, } = require('../common'); const { normalizeBech32 } = require('@cosmjs/encoding'); +const DEFAULT_MAX_UINT_BITS_EVM = 256; +const DEFAULT_MAX_DECIMALS_WHEN_TRUNCATING_EVM = 255; + const governanceAddress = 'axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj'; const prepareWallet = async ({ mnemonic }) => await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'axelar' }); @@ -606,6 +610,24 @@ const fetchCodeIdFromCodeHash = async (client, contractBaseConfig) => { return codeId; }; +const getChainTruncationParams = (config, chainConfig) => { + const key = chainConfig.axelarId.toLowerCase(); + const chainTruncationParams = config.axelar.contracts.InterchainTokenService[key]; + + let maxUintBits = chainTruncationParams?.maxUintBits; + let maxDecimalsWhenTruncating = chainTruncationParams?.maxDecimalsWhenTruncating; + + // set EVM default values + if (chainConfig.chainType == 'evm') { + maxUintBits = maxUintBits || DEFAULT_MAX_UINT_BITS_EVM; + maxDecimalsWhenTruncating = maxDecimalsWhenTruncating || DEFAULT_MAX_DECIMALS_WHEN_TRUNCATING_EVM; + } + + validateParameters({ isValidNumber: { maxUintBits }, isValidNumber: { maxDecimalsWhenTruncating } }); + + return { maxUintBits, maxDecimalsWhenTruncating }; +}; + const getInstantiatePermission = (accessType, addresses) => { return { permission: accessType, @@ -840,6 +862,7 @@ module.exports = { instantiateContract, makeInstantiateMsg, fetchCodeIdFromCodeHash, + getChainTruncationParams, decodeProposalAttributes, encodeStoreCodeProposal, encodeStoreInstantiateProposal, From 1d64a172610225e6748bea96ddffef6a7d7a5e2d Mon Sep 17 00:00:00 2001 From: Edwin Guajardo Date: Tue, 26 Nov 2024 12:04:27 -0600 Subject: [PATCH 3/3] lint --- cosmwasm/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js index 284a2fe9..cbaf8400 100644 --- a/cosmwasm/utils.js +++ b/cosmwasm/utils.js @@ -618,12 +618,12 @@ const getChainTruncationParams = (config, chainConfig) => { let maxDecimalsWhenTruncating = chainTruncationParams?.maxDecimalsWhenTruncating; // set EVM default values - if (chainConfig.chainType == 'evm') { + if (chainConfig.chainType === 'evm') { maxUintBits = maxUintBits || DEFAULT_MAX_UINT_BITS_EVM; maxDecimalsWhenTruncating = maxDecimalsWhenTruncating || DEFAULT_MAX_DECIMALS_WHEN_TRUNCATING_EVM; } - validateParameters({ isValidNumber: { maxUintBits }, isValidNumber: { maxDecimalsWhenTruncating } }); + validateParameters({ isValidNumber: { maxUintBits, maxDecimalsWhenTruncating } }); return { maxUintBits, maxDecimalsWhenTruncating }; };