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(amplifier): add proposal type storeInstantiate #314

Merged
merged 3 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
14 changes: 14 additions & 0 deletions cosmwasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,17 @@ Order of execution to satisfy dependencies:
9. `node cosmwasm/submit-proposal.js --proposalType instantiate -c VotingVerifier -t "VotingVerifier roposal title" -d "VotingVerifier proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000 --instantiate2 --fetchCodeId -y -n "avalanche"`
10. `node cosmwasm/submit-proposal.js --proposalType instantiate -c Gateway -t "Gateway roposal title" -d "Gateway proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000 --instantiate2 --fetchCodeId -y -n "avalanche"`
11. `node cosmwasm/submit-proposal.js --proposalType instantiate -c MultisigProver -t "MultisigProver roposal title" -d "MultisigProver proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000 --instantiate2 --fetchCodeId -y -n "avalanche"`

### Uploading and instantiating in one step

The proposal type `storeInstantiate` allows uploading and instantiating in one step. However, there are a couple of caveats to be aware of:

1. There is no support for `instantiate2` in this proposal type. This means that the contract address will not be known until the proposal is executed.

2. Since governance proposals are executed asynchronously, both the codeId and contract address are not immediately available. Querying the network for the correct values could be tricky if multiple proposals are executed together.

Example usage:

```
node cosmwasm/submit-proposal.js --proposalType storeInstantiate -c ServiceRegistry -t "ServiceRegistry proposal title" -d "ServiceRegistry proposal description" -r $RUN_AS_ACCOUNT --deposit 100000000
```
6 changes: 5 additions & 1 deletion cosmwasm/deploy-contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require('dotenv').config();
const { isNil } = require('lodash');

const { printInfo, loadConfig, saveConfig, prompt } = require('../evm/utils');
const { isNumber, printInfo, loadConfig, saveConfig, prompt } = require('../evm/utils');
const {
prepareWallet,
prepareClient,
Expand Down Expand Up @@ -64,6 +64,10 @@ const instantiate = (client, wallet, chainName, config, options) => {
chains: { [chainName]: chainConfig },
} = config;

if (!isNumber(contractConfig.codeId)) {
throw new Error('Code Id is not defined');
}

const initMsg = makeInstantiateMsg(contractName, chainName, config);
return instantiateContract(client, wallet, initMsg, config, options).then((contractAddress) => {
if (chainConfig) {
Expand Down
54 changes: 51 additions & 3 deletions cosmwasm/submit-proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ const {
getChains,
decodeProposalAttributes,
encodeStoreCodeProposal,
encodeStoreInstantiateProposal,
encodeInstantiateProposal,
encodeInstantiate2Proposal,
submitProposal,
makeInstantiateMsg,
instantiate2AddressForProposal,
governanceAddress,
} = require('./utils');
const { saveConfig, loadConfig, printInfo, prompt } = require('../evm/utils');
const { StoreCodeProposal, InstantiateContractProposal, InstantiateContract2Proposal } = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const { isNumber, saveConfig, loadConfig, printInfo, prompt } = require('../evm/utils');
const {
StoreCodeProposal,
StoreAndInstantiateContractProposal,
InstantiateContractProposal,
InstantiateContract2Proposal,
} = require('cosmjs-types/cosmwasm/wasm/v1/proposal');

const { Command, Option } = require('commander');

Expand Down Expand Up @@ -73,6 +79,36 @@ const storeCode = (client, wallet, config, options) => {
});
};

const storeInstantiate = (client, wallet, config, options, chainName) => {
const { contractName, instantiate2 } = options;
const {
axelar: {
contracts: { [contractName]: contractConfig },
},
chains: { [chainName]: chainConfig },
} = config;

if (instantiate2) {
throw new Error('instantiate2 not supported for storeInstantiate');
}

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

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

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

return submitProposal(client, wallet, config, options, proposal).then((proposalId) => {
printInfo('Proposal submitted', proposalId);

updateContractConfig(contractConfig, chainConfig, 'storeInstantiateProposalId', proposalId);
contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex');
});
};

const fetchAndUpdateCodeId = async (client, contractConfig) => {
const codes = await client.getCodes(); // TODO: create custom function to retrieve codes more efficiently and with pagination
let codeId;
Expand Down Expand Up @@ -107,6 +143,8 @@ const instantiate = async (client, wallet, config, options, chainName) => {

if (fetchCodeId) {
await fetchAndUpdateCodeId(client, contractConfig);
} else if (!isNumber(contractConfig.codeId)) {
throw new Error('Code Id is not defined');
}

const initMsg = makeInstantiateMsg(contractName, chainName, config);
Expand Down Expand Up @@ -147,6 +185,14 @@ const main = async (options) => {
case 'store':
return storeCode(client, wallet, config, options);

case 'storeInstantiate': {
const chains = getChains(config, options);

return chains.reduce((promise, chain) => {
return promise.then(() => storeInstantiate(client, wallet, config, options, chain.toLowerCase()));
}, Promise.resolve());
}

case 'instantiate': {
const chains = getChains(config, options);

Expand Down Expand Up @@ -205,7 +251,9 @@ const programHandler = () => {
program.addOption(new Option('--deposit <deposit>', 'the proposal deposit').makeOptionMandatory(true));
program.addOption(new Option('-r, --runAs <runAs>', 'the address that will execute the message').makeOptionMandatory(true));
program.addOption(
new Option('--proposalType <proposalType>', 'proposal type').choices(['store', 'instantiate']).makeOptionMandatory(true),
new Option('--proposalType <proposalType>', 'proposal type')
.choices(['store', 'storeInstantiate', 'instantiate'])
.makeOptionMandatory(true),
);
program.addOption(new Option('--predictOnly', 'output the predicted changes only').env('PREDICT_ONLY'));

Expand Down
34 changes: 27 additions & 7 deletions cosmwasm/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ const { calculateFee, GasPrice } = require('@cosmjs/stargate');
const { instantiate2Address, SigningCosmWasmClient } = require('@cosmjs/cosmwasm-stargate');
const { DirectSecp256k1HdWallet } = require('@cosmjs/proto-signing');
const { MsgSubmitProposal } = require('cosmjs-types/cosmos/gov/v1beta1/tx');
const { StoreCodeProposal, InstantiateContractProposal, InstantiateContract2Proposal } = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const {
StoreCodeProposal,
StoreAndInstantiateContractProposal,
InstantiateContractProposal,
InstantiateContract2Proposal,
} = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const { AccessType } = require('cosmjs-types/cosmwasm/wasm/v1/types');
const { getSaltFromKey, isString, isStringArray, isKeccak256Hash, isNumber, toBigNumberString } = require('../evm/utils');
const { normalizeBech32 } = require('@cosmjs/encoding');
Expand Down Expand Up @@ -403,12 +408,6 @@ const makeInstantiateMsg = (contractName, chainName, config) => {

const { [contractName]: contractConfig } = contracts;

const { codeId } = contractConfig;

if (!isNumber(codeId)) {
throw new Error('Code Id is not defined');
}

switch (contractName) {
case 'Coordinator': {
if (chainConfig) {
Expand Down Expand Up @@ -535,6 +534,17 @@ const getStoreCodeParams = (options) => {
};
};

const getStoreInstantiateParams = (config, options, msg) => {
const { contractName, admin } = options;

return {
...getStoreCodeParams(options),
admin,
label: contractName,
msg: Buffer.from(JSON.stringify(msg)),
};
};

const getInstantiateContractParams = (config, options, msg) => {
const { contractName, admin } = options;

Expand Down Expand Up @@ -567,6 +577,15 @@ const encodeStoreCodeProposal = (options) => {
};
};

const encodeStoreInstantiateProposal = (config, options, msg) => {
const proposal = StoreAndInstantiateContractProposal.fromPartial(getStoreInstantiateParams(config, options, msg));

return {
typeUrl: '/cosmwasm.wasm.v1.StoreAndInstantiateContractProposal',
value: Uint8Array.from(StoreAndInstantiateContractProposal.encode(proposal).finish()),
};
};

const decodeProposalAttributes = (proposalJson) => {
if (proposalJson.msg) {
proposalJson.msg = JSON.parse(atob(proposalJson.msg));
Expand Down Expand Up @@ -640,6 +659,7 @@ module.exports = {
instantiate2AddressForProposal,
decodeProposalAttributes,
encodeStoreCodeProposal,
encodeStoreInstantiateProposal,
encodeInstantiateProposal,
encodeInstantiate2Proposal,
submitProposal,
Expand Down
Loading