Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cosmwasm): add subcommand to register a chain on ITS hub through governance proposal #449

Merged
merged 6 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -478,4 +488,5 @@ module.exports = {
getAmplifierContractOnchainConfig,
getSaltFromKey,
calculateDomainSeparator,
getItsEdgeContract,
};
37 changes: 37 additions & 0 deletions cosmwasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,43 @@ 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 on ITS Hub through governance proposal

To submit a governance proposal to register an ITS chain, use the `submit-proposal` script with the `its-hub-register-chains <chains...>` command. The `chains` argument is used to pass a list of chains to register on ITS hub.

**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:

```
"axelar": {
"contracts": {
...
"InterchainTokenService": {
...
"some-sui-chain": {
"maxUintBits": 64,
"maxDecimalsWhenTruncating": 6,
}
}
...
}
}
```

Example usage:

```
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

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.
Expand Down
39 changes: 37 additions & 2 deletions cosmwasm/submit-proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const {
getAmplifierBaseContractConfig,
getAmplifierContractConfig,
updateCodeId,
getChainTruncationParams,
decodeProposalAttributes,
encodeStoreCodeProposal,
encodeStoreInstantiateProposal,
Expand All @@ -27,7 +28,7 @@ const {
submitProposal,
makeInstantiateMsg,
} = require('./utils');
const { saveConfig, loadConfig, printInfo, prompt } = require('../common');
const { saveConfig, loadConfig, printInfo, prompt, getChainConfig, getItsEdgeContract } = require('../common');
const {
StoreCodeProposal,
StoreAndInstantiateContractProposal,
Expand Down Expand Up @@ -167,6 +168,30 @@ 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);
const { maxUintBits, maxDecimalsWhenTruncating } = getChainTruncationParams(config, chainConfig);

const itsEdgeContract = getItsEdgeContract(chainConfig);

return {
chain: chainConfig.axelarId,
its_edge_contract: itsEdgeContract,
truncation: {
max_uint: (2n ** BigInt(maxUintBits) - 1n).toString(),
max_decimals_when_truncating: maxDecimalsWhenTruncating,
},
};
});

await execute(client, wallet, config, {
eguajardo marked this conversation as resolved.
Show resolved Hide resolved
...options,
contractName: 'InterchainTokenService',
msg: `{ "register_chains": { "chains": ${JSON.stringify(chains)} } }`,
});
};

const paramChange = async (client, wallet, config, options) => {
const proposal = encodeParameterChangeProposal(options);

Expand Down Expand Up @@ -256,12 +281,22 @@ 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('its-hub-register-chains')
.description('Submit an execute wasm contract proposal to register an ITS chain')
.argument('<chains...>', '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 });

const paramChangeCmd = program
.command('paramChange')
.description('Submit a parameter change proposal')
Expand Down
23 changes: 23 additions & 0 deletions cosmwasm/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
Expand Down Expand Up @@ -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, maxDecimalsWhenTruncating } });

return { maxUintBits, maxDecimalsWhenTruncating };
};

const getInstantiatePermission = (accessType, addresses) => {
return {
permission: accessType,
Expand Down Expand Up @@ -840,6 +862,7 @@ module.exports = {
instantiateContract,
makeInstantiateMsg,
fetchCodeIdFromCodeHash,
getChainTruncationParams,
decodeProposalAttributes,
encodeStoreCodeProposal,
encodeStoreInstantiateProposal,
Expand Down