Skip to content

Commit

Permalink
feat(amplifier): add proposal to change parameters (#366)
Browse files Browse the repository at this point in the history
  • Loading branch information
eguajardo authored Sep 6, 2024
1 parent 8a496e7 commit 95b345f
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 37 deletions.
51 changes: 37 additions & 14 deletions cosmwasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ This folder contains deployment scripts for cosmwasm contracts needed for amplif

`npm ci`


1. Compile the contracts in the amplifier [repo](https://github.com/axelarnetwork/axelar-amplifier) using the [rust optimizer](https://github.com/CosmWasm/rust-optimizer) for cosmwasm.

2. Add a `contracts` object to the `axelar` section of your config. Change any values as necessary. For chain specific contracts (`VotingVerifier`,`Gateway`,`MultisigProver`), there should be one object per chain, where the key is the chain id.

```
"axelar": {
"contracts": {
Expand Down Expand Up @@ -109,23 +109,24 @@ This folder contains deployment scripts for cosmwasm contracts needed for amplif
```

### Deploy the contracts

Deploy each contract. Chain name should match the key of an object in the `chains` section of the config. Chain name should be omitted for contracts that are not chain specific.

`node deploy-contract.js -m [mnemonic] -a [path to contract artifacts] -c [contract name] -e [environment] -n <chain name>`
`node deploy-contract.js -m [mnemonic] -a [path to contract artifacts] -c [contract name] -e [environment] -n <chain name>`

Some of the contracts depend on each other and need to be deployed in a specific order. Note the connection router and nexus gateway each need to know the other's address, so you need to pass `--instantiate2`, and upload each contract before instatiating (by passing `-u`).
1. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Router" --instantiate2 -e devnet -u`
2. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "NexusGateway" --instantiate2 -e devnet -u`
3. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "NexusGateway" --instantiate2 -e devnet -r`
4. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Router" --instantiate2 -e devnet -r`
5. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "ServiceRegistry" -e devnet`
6. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Rewards" -e devnet`
7. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Coordinator" -e devnet`
8. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Multisig" -e devnet`
9. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "VotingVerifier" -e devnet -n "ethereum,avalanche"`
10. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Gateway" -e devnet -n "ethereum,avalanche"`
11. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "MultisigProver" -e devnet -n "ethereum,avalanche"`

1. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Router" --instantiate2 -e devnet -u`
2. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "NexusGateway" --instantiate2 -e devnet -u`
3. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "NexusGateway" --instantiate2 -e devnet -r`
4. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Router" --instantiate2 -e devnet -r`
5. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "ServiceRegistry" -e devnet`
6. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Rewards" -e devnet`
7. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Coordinator" -e devnet`
8. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Multisig" -e devnet`
9. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "VotingVerifier" -e devnet -n "ethereum,avalanche"`
10. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "Gateway" -e devnet -n "ethereum,avalanche"`
11. `node deploy-contract.js -m [mnemonic] -a [path to artifacts] -c "MultisigProver" -e devnet -n "ethereum,avalanche"`

### Constant Address Deployment

Expand Down Expand Up @@ -176,6 +177,7 @@ Use the option `--fetchCodeId` to retrieve and update the code id from the netwo
Note: The rules for chain name specification and the use of `--instantiate2` as described in the "Deploy the contracts" and "Constant Address Deployment" sections above also apply when instantiating through governance. Refer to those sections for details on omitting chain names for certain contracts and using `--instantiate2` for address prediction.

Order of execution to satisfy dependencies:

1. `node cosmwasm/submit-proposal.js --proposalType instantiate -c Router -t "Router roposal title" -d "Router proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000 --instantiate2 --predictOnly`
2. `node cosmwasm/submit-proposal.js --proposalType instantiate -c NexusGateway -t "NexusGateway roposal title" -d "NexusGateway proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000 --instantiate2 --predictOnly`
3. `node cosmwasm/submit-proposal.js --proposalType instantiate -c NexusGateway -t "NexusGateway roposal title" -d "NexusGateway proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000 --instantiate2 --fetchCodeId -y`
Expand Down Expand Up @@ -210,4 +212,25 @@ Example usage:

```
node cosmwasm/submit-proposal.js --proposalType 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"}}'
```
```

### Submit a proposal to change a parameter

To submit a governance proposal to change a parameter, use the `submit-proposal` script with the `--proposalType paramChange` option. The `--changes` option should be used to pass a JSON string representing an array of parameter changes.

Example usage:

```
node cosmwasm/submit-proposal.js \
--proposalType paramChange \
-t "Set Gateway at Nexus Module" \
-d "Proposal to update nexus param gateway address." \
--deposit 100000000 \
--changes '[
{
"subspace": "nexus",
"key": "gateway",
"value": "'$GATEWAY_ADDRESS'"
}
]'
```
2 changes: 1 addition & 1 deletion cosmwasm/cli-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const addAmplifierOptions = (program) => {

program.addOption(new Option('-m, --mnemonic <mnemonic>', 'mnemonic').makeOptionMandatory(true).env('MNEMONIC'));
program.addOption(new Option('-a, --artifactPath <artifactPath>', 'artifact path').env('ARTIFACT_PATH'));
program.addOption(new Option('-c, --contractName <contractName>', 'contract name').makeOptionMandatory(true));
program.addOption(new Option('-c, --contractName <contractName>', 'contract name'));
program.addOption(new Option('-n, --chainNames <chainNames>', 'chain names').default('none').env('CHAINS'));

program.addOption(new Option('-s, --salt <salt>', 'salt for instantiate2. defaults to contract name').env('SALT'));
Expand Down
69 changes: 48 additions & 21 deletions cosmwasm/submit-proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const {
encodeInstantiateProposal,
encodeInstantiate2Proposal,
encodeExecuteContractProposal,
encodeParameterChangeProposal,
submitProposal,
makeInstantiateMsg,
governanceAddress,
Expand All @@ -33,6 +34,7 @@ const {
InstantiateContract2Proposal,
ExecuteContractProposal,
} = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const { ParameterChangeProposal } = require('cosmjs-types/cosmos/params/v1beta1/params');

const { Command, Option } = require('commander');
const { addAmplifierOptions } = require('./cli-utils');
Expand All @@ -55,6 +57,23 @@ const printProposal = (proposal, proposalType) => {
);
};

const confirmProposalSubmission = (options, proposal, proposalType) => {
printProposal(proposal, proposalType);

if (prompt(`Proceed with proposal submission?`, options.yes)) {
return false;
}

return true;
};

const callSubmitProposal = async (client, wallet, config, options, proposal) => {
const proposalId = await submitProposal(client, wallet, config, options, proposal);
printInfo('Proposal submitted', proposalId);

return proposalId;
};

const storeCode = async (client, wallet, config, options) => {
const { contractName } = options;
const {
Expand All @@ -65,14 +84,11 @@ const storeCode = async (client, wallet, config, options) => {

const proposal = encodeStoreCodeProposal(options);

printProposal(proposal, StoreCodeProposal);

if (prompt(`Proceed with proposal submission?`, options.yes)) {
if (!confirmProposalSubmission(options, proposal, StoreCodeProposal)) {
return;
}

const proposalId = await submitProposal(client, wallet, config, options, proposal);
printInfo('Proposal submitted', proposalId);
const proposalId = await callSubmitProposal(client, wallet, config, options, proposal);

contractConfig.storeCodeProposalId = proposalId;
contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex');
Expand All @@ -92,16 +108,13 @@ const storeInstantiate = async (client, wallet, config, options, chainName) => {
}

const initMsg = makeInstantiateMsg(contractName, chainName, config);

const proposal = encodeStoreInstantiateProposal(config, options, initMsg);
printProposal(proposal, StoreAndInstantiateContractProposal);

if (prompt(`Proceed with proposal submission?`, options.yes)) {
if (!confirmProposalSubmission(options, proposal, StoreAndInstantiateContractProposal)) {
return;
}

const proposalId = await submitProposal(client, wallet, config, options, proposal);
printInfo('Proposal submitted', proposalId);
const proposalId = await callSubmitProposal(client, wallet, config, options, proposal);

updateContractConfig(contractConfig, chainConfig, 'storeInstantiateProposalId', proposalId);
contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex');
Expand Down Expand Up @@ -129,21 +142,21 @@ const instantiate = async (client, wallet, config, options, chainName) => {
const initMsg = makeInstantiateMsg(contractName, chainName, config);

let proposal;
let proposalType;

if (instantiate2) {
proposal = encodeInstantiate2Proposal(config, options, initMsg);
printProposal(proposal, InstantiateContract2Proposal);
proposalType = InstantiateContract2Proposal;
} else {
proposal = encodeInstantiateProposal(config, options, initMsg);
printProposal(proposal, InstantiateContractProposal);
proposalType = InstantiateContractProposal;
}

if (prompt(`Proceed with proposal submission?`, options.yes)) {
if (!confirmProposalSubmission(options, proposal, proposalType)) {
return;
}

const proposalId = await submitProposal(client, wallet, config, options, proposal);
printInfo('Proposal submitted', proposalId);
const proposalId = await callSubmitProposal(client, wallet, config, options, proposal);

updateContractConfig(contractConfig, chainConfig, 'instantiateProposalId', proposalId);

Expand All @@ -163,18 +176,25 @@ const execute = async (client, wallet, config, options, chainName) => {

const proposal = encodeExecuteContractProposal(config, options, chainName);

printProposal(proposal, ExecuteContractProposal);

if (prompt(`Proceed with proposal submission?`, options.yes)) {
if (!confirmProposalSubmission(options, proposal, ExecuteContractProposal)) {
return;
}

const proposalId = await submitProposal(client, wallet, config, options, proposal);
printInfo('Proposal submitted', proposalId);
const proposalId = await callSubmitProposal(client, wallet, config, options, proposal);

updateContractConfig(contractConfig, chainConfig, 'executeProposalId', proposalId);
};

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

if (!confirmProposalSubmission(options, proposal, ParameterChangeProposal)) {
return;
}

await callSubmitProposal(client, wallet, config, options, proposal);
};

const main = async (options) => {
const { env, proposalType, contractName } = options;
const config = loadConfig(env);
Expand Down Expand Up @@ -234,6 +254,11 @@ const main = async (options) => {
break;
}

case 'paramChange': {
await paramChange(client, wallet, config, options);
break;
}

default:
throw new Error('Invalid proposal type');
}
Expand All @@ -258,7 +283,7 @@ const programHandler = () => {
);
program.addOption(
new Option('--proposalType <proposalType>', 'proposal type')
.choices(['store', 'storeInstantiate', 'instantiate', 'execute'])
.choices(['store', 'storeInstantiate', 'instantiate', 'execute', 'paramChange'])
.makeOptionMandatory(true),
);

Expand All @@ -272,6 +297,8 @@ const programHandler = () => {

program.addOption(new Option('--msg <msg>', 'json encoded message to submit with an execute contract proposal'));

program.addOption(new Option('--changes <changes>', 'parameter changes'));

program.addOption(new Option('--predictOnly', 'output the predicted changes only').env('PREDICT_ONLY'));

program.action((options) => {
Expand Down
22 changes: 21 additions & 1 deletion cosmwasm/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
InstantiateContract2Proposal,
ExecuteContractProposal,
} = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const { ParameterChangeProposal } = require('cosmjs-types/cosmos/params/v1beta1/params');
const { AccessType } = require('cosmjs-types/cosmwasm/wasm/v1/types');
const {
isString,
Expand Down Expand Up @@ -658,11 +659,20 @@ const getExecuteContractParams = (config, options, chainName) => {

return {
...getSubmitProposalParams(options),
contract: contractConfig[chainConfig.axelarId]?.address || contractConfig.address,
contract: contractConfig[chainConfig?.axelarId]?.address || contractConfig.address,
msg: Buffer.from(msg),
};
};

const getParameterChangeParams = ({ title, description, changes }) => ({
title,
description,
changes: JSON.parse(changes).map(({ value, ...rest }) => ({
...rest,
value: JSON.stringify(value), // `value` must be JSON encoded: https://github.com/cosmos/cosmos-sdk/blob/9abd946ba0cdc6d0e708bf862b2ca202b13f2d7b/x/params/client/utils/utils.go#L23
})),
});

const encodeStoreCodeProposal = (options) => {
const proposal = StoreCodeProposal.fromPartial(getStoreCodeParams(options));

Expand Down Expand Up @@ -716,6 +726,15 @@ const encodeExecuteContractProposal = (config, options, chainName) => {
};
};

const encodeParameterChangeProposal = (options) => {
const proposal = ParameterChangeProposal.fromPartial(getParameterChangeParams(options));

return {
typeUrl: '/cosmos.params.v1beta1.ParameterChangeProposal',
value: Uint8Array.from(ParameterChangeProposal.encode(proposal).finish()),
};
};

const encodeSubmitProposal = (content, config, options, proposer) => {
const {
axelar: { tokenSymbol },
Expand Down Expand Up @@ -767,6 +786,7 @@ module.exports = {
encodeInstantiateProposal,
encodeInstantiate2Proposal,
encodeExecuteContractProposal,
encodeParameterChangeProposal,
submitProposal,
isValidCosmosAddress,
};

0 comments on commit 95b345f

Please sign in to comment.