From e80a522cb78ea058e3a0279e74b2e8f321f2cff8 Mon Sep 17 00:00:00 2001 From: eguajardo Date: Fri, 30 Aug 2024 11:10:13 -0600 Subject: [PATCH] refactor(amplifier): update to async style (#351) --- cosmwasm/deploy-contract.js | 88 +++++++++---------- cosmwasm/submit-proposal.js | 165 ++++++++++++++++++------------------ cosmwasm/utils.js | 143 +++++++++++++++---------------- 3 files changed, 187 insertions(+), 209 deletions(-) diff --git a/cosmwasm/deploy-contract.js b/cosmwasm/deploy-contract.js index c1fe1646..9731b366 100644 --- a/cosmwasm/deploy-contract.js +++ b/cosmwasm/deploy-contract.js @@ -3,11 +3,16 @@ require('dotenv').config(); const { isNil } = require('lodash'); +const { instantiate2Address } = require('@cosmjs/cosmwasm-stargate'); + const { isNumber, printInfo, loadConfig, saveConfig, prompt, getChainConfig } = require('../common'); const { prepareWallet, prepareClient, + fromHex, + getSalt, getChains, + updateContractConfig, fetchCodeIdFromCodeHash, uploadContract, instantiateContract, @@ -17,8 +22,8 @@ const { const { Command, Option } = require('commander'); const { addAmplifierOptions } = require('./cli-utils'); -const upload = (client, wallet, chainName, config, options) => { - const { reuseCodeId, contractName, fetchCodeId } = options; +const upload = async (client, wallet, chainName, config, options) => { + const { reuseCodeId, contractName, fetchCodeId, instantiate2, salt, chainNames } = options; const { axelar: { contracts: { [contractName]: contractConfig }, @@ -29,31 +34,22 @@ const upload = (client, wallet, chainName, config, options) => { if (!fetchCodeId && (!reuseCodeId || isNil(contractConfig.codeId))) { printInfo('Uploading contract binary'); - return uploadContract(client, wallet, config, options) - .then(({ address, codeId }) => { - printInfo('Uploaded contract binary'); - contractConfig.codeId = codeId; - - if (!address) { - return; - } - - if (chainConfig) { - contractConfig[chainConfig.axelarId] = { - ...contractConfig[chainConfig.axelarId], - address, - }; - } else { - contractConfig.address = address; - } - - printInfo('Expected contract address', address); - }) - .then(() => ({ wallet, client })); - } + const { checksum, codeId } = await uploadContract(client, wallet, config, options); + + printInfo('Uploaded contract binary'); + contractConfig.codeId = codeId; + + if (instantiate2) { + const [account] = await wallet.getAccounts(); + const address = instantiate2Address(fromHex(checksum), account.address, getSalt(salt, contractName, chainNames), 'axelar'); - printInfo('Skipping upload. Reusing previously uploaded bytecode'); - return Promise.resolve({ wallet, client }); + updateContractConfig(contractConfig, chainConfig, 'address', address); + + printInfo('Expected contract address', address); + } + } else { + printInfo('Skipping upload. Reusing previously uploaded bytecode'); + } }; const instantiate = async (client, wallet, chainName, config, options) => { @@ -72,18 +68,11 @@ const instantiate = async (client, wallet, chainName, config, options) => { } const initMsg = makeInstantiateMsg(contractName, chainName, config); - return instantiateContract(client, wallet, initMsg, config, options).then((contractAddress) => { - if (chainConfig) { - contractConfig[chainConfig.axelarId] = { - ...contractConfig[chainConfig.axelarId], - address: contractAddress, - }; - } else { - contractConfig.address = contractAddress; - } + const contractAddress = await instantiateContract(client, wallet, initMsg, config, options); - printInfo(`Instantiated ${chainName === 'none' ? '' : chainName.concat(' ')}${contractName}. Address`, contractAddress); - }); + updateContractConfig(contractConfig, chainConfig, 'address', contractAddress); + + printInfo(`Instantiated ${chainName === 'none' ? '' : chainName.concat(' ')}${contractName}. Address`, contractAddress); }; const main = async (options) => { @@ -92,19 +81,18 @@ const main = async (options) => { const chains = getChains(config, options); - await prepareWallet(options) - .then((wallet) => prepareClient(config, wallet)) - .then(({ wallet, client }) => upload(client, wallet, chains[0], config, options)) - .then(({ wallet, client }) => { - if (uploadOnly || prompt(`Proceed with deployment on axelar?`, yes)) { - return; - } - - return chains.reduce((promise, chain) => { - return promise.then(() => instantiate(client, wallet, chain.toLowerCase(), config, options)); - }, Promise.resolve()); - }) - .then(() => saveConfig(config, env)); + const wallet = await prepareWallet(options); + const client = await prepareClient(config, wallet); + + await upload(client, wallet, chains[0], config, options); + + if (!(uploadOnly || prompt(`Proceed with deployment on axelar?`, yes))) { + for (const chain of chains) { + await instantiate(client, wallet, chain.toLowerCase(), config, options); + } + } + + saveConfig(config, env); }; const programHandler = () => { diff --git a/cosmwasm/submit-proposal.js b/cosmwasm/submit-proposal.js index 366e5bed..8833d6ea 100644 --- a/cosmwasm/submit-proposal.js +++ b/cosmwasm/submit-proposal.js @@ -4,11 +4,16 @@ require('dotenv').config(); const { createHash } = require('crypto'); +const { instantiate2Address } = require('@cosmjs/cosmwasm-stargate'); + const { prepareWallet, prepareClient, + fromHex, + getSalt, readWasmFile, getChains, + updateContractConfig, fetchCodeIdFromCodeHash, decodeProposalAttributes, encodeStoreCodeProposal, @@ -18,7 +23,6 @@ const { encodeExecuteContractProposal, submitProposal, makeInstantiateMsg, - instantiate2AddressForProposal, governanceAddress, } = require('./utils'); const { isNumber, saveConfig, loadConfig, printInfo, prompt } = require('../common'); @@ -33,23 +37,15 @@ const { const { Command, Option } = require('commander'); const { addAmplifierOptions } = require('./cli-utils'); -const updateContractConfig = (contractConfig, chainConfig, key, value) => { - if (chainConfig) { - contractConfig[chainConfig.axelarId] = { - ...contractConfig[chainConfig.axelarId], - [key]: value, - }; - } else { - contractConfig[key] = value; - } -}; +const predictAndUpdateAddress = async (client, contractConfig, chainConfig, options) => { + const { contractName, salt, chainNames, runAs } = options; -const predictAndUpdateAddress = (client, contractConfig, chainConfig, options, contractName, chainName) => { - return instantiate2AddressForProposal(client, contractConfig, options).then((contractAddress) => { - updateContractConfig(contractConfig, chainConfig, 'address', contractAddress); + const { checksum } = await client.getCodeDetails(contractConfig.codeId); + const contractAddress = instantiate2Address(fromHex(checksum), runAs, getSalt(salt, contractName, chainNames), 'axelar'); - return contractAddress; - }); + updateContractConfig(contractConfig, chainConfig, 'address', contractAddress); + + return contractAddress; }; const printProposal = (proposal, proposalType) => { @@ -59,7 +55,7 @@ const printProposal = (proposal, proposalType) => { ); }; -const storeCode = (client, wallet, config, options) => { +const storeCode = async (client, wallet, config, options) => { const { contractName } = options; const { axelar: { @@ -72,18 +68,17 @@ const storeCode = (client, wallet, config, options) => { printProposal(proposal, StoreCodeProposal); if (prompt(`Proceed with proposal submission?`, options.yes)) { - return Promise.resolve(); + return; } - return submitProposal(client, wallet, config, options, proposal).then((proposalId) => { - printInfo('Proposal submitted', proposalId); + const proposalId = await submitProposal(client, wallet, config, options, proposal); + printInfo('Proposal submitted', proposalId); - contractConfig.storeCodeProposalId = proposalId; - contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex'); - }); + contractConfig.storeCodeProposalId = proposalId; + contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex'); }; -const storeInstantiate = (client, wallet, config, options, chainName) => { +const storeInstantiate = async (client, wallet, config, options, chainName) => { const { contractName, instantiate2 } = options; const { axelar: { @@ -102,15 +97,14 @@ const storeInstantiate = (client, wallet, config, options, chainName) => { printProposal(proposal, StoreAndInstantiateContractProposal); if (prompt(`Proceed with proposal submission?`, options.yes)) { - return Promise.resolve(); + return; } - return submitProposal(client, wallet, config, options, proposal).then((proposalId) => { - printInfo('Proposal submitted', proposalId); + const proposalId = await submitProposal(client, wallet, config, options, proposal); + printInfo('Proposal submitted', proposalId); - updateContractConfig(contractConfig, chainConfig, 'storeInstantiateProposalId', proposalId); - contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex'); - }); + updateContractConfig(contractConfig, chainConfig, 'storeInstantiateProposalId', proposalId); + contractConfig.storeCodeProposalCodeHash = createHash('sha256').update(readWasmFile(options)).digest().toString('hex'); }; const instantiate = async (client, wallet, config, options, chainName) => { @@ -129,7 +123,7 @@ const instantiate = async (client, wallet, config, options, chainName) => { } if (predictOnly) { - return predictAndUpdateAddress(client, contractConfig, chainConfig, options, contractName, chainName); + return predictAndUpdateAddress(client, contractConfig, chainConfig, options); } const initMsg = makeInstantiateMsg(contractName, chainName, config); @@ -145,21 +139,20 @@ const instantiate = async (client, wallet, config, options, chainName) => { } if (prompt(`Proceed with proposal submission?`, options.yes)) { - return Promise.resolve(); + return; } - return submitProposal(client, wallet, config, options, proposal).then((proposalId) => { - printInfo('Proposal submitted', proposalId); + const proposalId = await submitProposal(client, wallet, config, options, proposal); + printInfo('Proposal submitted', proposalId); - updateContractConfig(contractConfig, chainConfig, 'instantiateProposalId', proposalId); + updateContractConfig(contractConfig, chainConfig, 'instantiateProposalId', proposalId); - if (instantiate2) { - return predictAndUpdateAddress(client, contractConfig, chainConfig, options, contractName, chainName); - } - }); + if (instantiate2) { + return predictAndUpdateAddress(client, contractConfig, chainConfig, options); + } }; -const execute = (client, wallet, config, options, chainName) => { +const execute = async (client, wallet, config, options, chainName) => { const { contractName } = options; const { axelar: { @@ -173,14 +166,13 @@ const execute = (client, wallet, config, options, chainName) => { printProposal(proposal, ExecuteContractProposal); if (prompt(`Proceed with proposal submission?`, options.yes)) { - return Promise.resolve(); + return; } - return submitProposal(client, wallet, config, options, proposal).then((proposalId) => { - printInfo('Proposal submitted', proposalId); + const proposalId = await submitProposal(client, wallet, config, options, proposal); + printInfo('Proposal submitted', proposalId); - updateContractConfig(contractConfig, chainConfig, 'executeProposalId', proposalId); - }); + updateContractConfig(contractConfig, chainConfig, 'executeProposalId', proposalId); }; const main = async (options) => { @@ -195,53 +187,58 @@ const main = async (options) => { config.axelar.contracts[contractName] = {}; } - await prepareWallet(options) - .then((wallet) => prepareClient(config, wallet)) - .then(({ wallet, client }) => { - switch (proposalType) { - case 'store': - return storeCode(client, wallet, config, options); + const wallet = await prepareWallet(options); + const client = await prepareClient(config, wallet); - case 'storeInstantiate': { - const chains = getChains(config, options); + switch (proposalType) { + case 'store': + await storeCode(client, wallet, config, options); + break; - return chains.reduce((promise, chain) => { - return promise.then(() => storeInstantiate(client, wallet, config, options, chain.toLowerCase())); - }, Promise.resolve()); - } + case 'storeInstantiate': { + const chains = getChains(config, options); - case 'instantiate': { - const chains = getChains(config, options); - - return chains.reduce((promise, chain) => { - return promise.then(() => - instantiate(client, wallet, config, options, chain.toLowerCase()).then((contractAddress) => { - if (contractAddress) { - printInfo( - `Predicted address for ${ - chain.toLowerCase() === 'none' ? '' : chain.toLowerCase().concat(' ') - }${contractName}. Address`, - contractAddress, - ); - } - }), - ); - }, Promise.resolve()); - } + for (const chain of chains) { + await storeInstantiate(client, wallet, config, options, chain.toLowerCase()); + } + + break; + } - case 'execute': { - const chains = getChains(config, options); + case 'instantiate': { + const chains = getChains(config, options); - return chains.reduce((promise, chain) => { - return promise.then(() => execute(client, wallet, config, options, chain.toLowerCase())); - }, Promise.resolve()); + for (const chain of chains) { + const contractAddress = await instantiate(client, wallet, config, options, chain.toLowerCase()); + + if (contractAddress) { + printInfo( + `Predicted address for ${ + chain.toLowerCase() === 'none' ? '' : chain.toLowerCase().concat(' ') + }${contractName}. Address`, + contractAddress, + ); } + } + + break; + } + + case 'execute': { + const chains = getChains(config, options); - default: - throw new Error('Invalid proposal type'); + for (const chain of chains) { + await execute(client, wallet, config, options, chain.toLowerCase()); } - }) - .then(() => saveConfig(config, env)); + + break; + } + + default: + throw new Error('Invalid proposal type'); + } + + saveConfig(config, env); }; const programHandler = () => { diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js index f036a714..ccdf2968 100644 --- a/cosmwasm/utils.js +++ b/cosmwasm/utils.js @@ -5,7 +5,7 @@ const { createHash } = require('crypto'); const { readFileSync } = require('fs'); const { calculateFee, GasPrice } = require('@cosmjs/stargate'); -const { instantiate2Address, SigningCosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); +const { SigningCosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); const { DirectSecp256k1HdWallet } = require('@cosmjs/proto-signing'); const { MsgSubmitProposal } = require('cosmjs-types/cosmos/gov/v1beta1/tx'); const { @@ -31,12 +31,10 @@ const { normalizeBech32 } = require('@cosmjs/encoding'); const governanceAddress = 'axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj'; -const prepareWallet = ({ mnemonic }) => DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'axelar' }); +const prepareWallet = async ({ mnemonic }) => await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: 'axelar' }); -const prepareClient = ({ axelar: { rpc, gasPrice } }, wallet) => - SigningCosmWasmClient.connectWithSigner(rpc, wallet, { gasPrice }).then((client) => { - return { wallet, client }; - }); +const prepareClient = async ({ axelar: { rpc, gasPrice } }, wallet) => + await SigningCosmWasmClient.connectWithSigner(rpc, wallet, { gasPrice }); const pascalToSnake = (str) => str.replace(/([A-Z])/g, (group) => `_${group.toLowerCase()}`).replace(/^_/, ''); @@ -74,57 +72,59 @@ const getChains = (config, { chainNames, instantiate2 }) => { return chains; }; +const updateContractConfig = (contractConfig, chainConfig, key, value) => { + if (chainConfig) { + contractConfig[chainConfig.axelarId] = { + ...contractConfig[chainConfig.axelarId], + [key]: value, + }; + } else { + contractConfig[key] = value; + } +}; + const uploadContract = async (client, wallet, config, options) => { - const { contractName, instantiate2, salt, chainNames } = options; - return wallet - .getAccounts() - .then(([account]) => { - const wasm = readWasmFile(options); - const { - axelar: { gasPrice, gasLimit }, - } = config; - const uploadFee = gasLimit === 'auto' ? 'auto' : calculateFee(gasLimit, GasPrice.fromString(gasPrice)); - return client.upload(account.address, wasm, uploadFee).then(({ checksum, codeId }) => ({ checksum, codeId, account })); - }) - .then(({ account, checksum, codeId }) => { - const address = instantiate2 - ? instantiate2Address(fromHex(checksum), account.address, getSalt(salt, contractName, chainNames), 'axelar') - : null; - - return { codeId, address }; - }); -}; - -const instantiateContract = (client, wallet, initMsg, config, options) => { + const { + axelar: { gasPrice, gasLimit }, + } = config; + + const [account] = await wallet.getAccounts(); + const wasm = readWasmFile(options); + + const uploadFee = gasLimit === 'auto' ? 'auto' : calculateFee(gasLimit, GasPrice.fromString(gasPrice)); + + return await client.upload(account.address, wasm, uploadFee); +}; + +const instantiateContract = async (client, wallet, initMsg, config, options) => { const { contractName, salt, instantiate2, chainNames, admin } = options; - return wallet - .getAccounts() - .then(([account]) => { - const contractConfig = config.axelar.contracts[contractName]; - - const { - axelar: { gasPrice, gasLimit }, - } = config; - const initFee = gasLimit === 'auto' ? 'auto' : calculateFee(gasLimit, GasPrice.fromString(gasPrice)); - - const contractLabel = getLabel(options); - - return instantiate2 - ? client.instantiate2( - account.address, - contractConfig.codeId, - getSalt(salt, contractName, chainNames), - initMsg, - contractLabel, - initFee, - { admin }, - ) - : client.instantiate(account.address, contractConfig.codeId, initMsg, contractLabel, initFee, { - admin, - }); - }) - .then(({ contractAddress }) => contractAddress); + const [account] = await wallet.getAccounts(); + + const contractConfig = config.axelar.contracts[contractName]; + + const { + axelar: { gasPrice, gasLimit }, + } = config; + const initFee = gasLimit === 'auto' ? 'auto' : calculateFee(gasLimit, GasPrice.fromString(gasPrice)); + + const contractLabel = getLabel(options); + + const { contractAddress } = instantiate2 + ? await client.instantiate2( + account.address, + contractConfig.codeId, + getSalt(salt, contractName, chainNames), + initMsg, + contractLabel, + initFee, + { admin }, + ) + : await client.instantiate(account.address, contractConfig.codeId, initMsg, contractLabel, initFee, { + admin, + }); + + return contractAddress; }; const validateAddress = (address) => { @@ -566,12 +566,6 @@ const fetchCodeIdFromCodeHash = async (client, contractConfig) => { return codeId; }; -const instantiate2AddressForProposal = (client, contractConfig, { contractName, salt, chainNames, runAs }) => { - return client - .getCodeDetails(contractConfig.codeId) - .then(({ checksum }) => instantiate2Address(fromHex(checksum), runAs, getSalt(salt, contractName, chainNames), 'axelar')); -}; - const getInstantiatePermission = (accessType, addresses) => { return { permission: accessType, @@ -734,36 +728,35 @@ const encodeSubmitProposal = (content, config, options, proposer) => { }; }; -const submitProposal = (client, wallet, config, options, content) => { - return wallet - .getAccounts() - .then(([account]) => { - const { - axelar: { gasPrice, gasLimit }, - } = config; +const submitProposal = async (client, wallet, config, options, content) => { + const [account] = await wallet.getAccounts(); + + const { + axelar: { gasPrice, gasLimit }, + } = config; + + const submitProposalMsg = encodeSubmitProposal(content, config, options, account.address); - const submitProposalMsg = encodeSubmitProposal(content, config, options, account.address); + const fee = gasLimit === 'auto' ? 'auto' : calculateFee(gasLimit, GasPrice.fromString(gasPrice)); + const { events } = await client.signAndBroadcast(account.address, [submitProposalMsg], fee, ''); - const fee = gasLimit === 'auto' ? 'auto' : calculateFee(gasLimit, GasPrice.fromString(gasPrice)); - return client.signAndBroadcast(account.address, [submitProposalMsg], fee, ''); - }) - .then( - ({ events }) => events.find(({ type }) => type === 'submit_proposal').attributes.find(({ key }) => key === 'proposal_id').value, - ); + return events.find(({ type }) => type === 'submit_proposal').attributes.find(({ key }) => key === 'proposal_id').value; }; module.exports = { governanceAddress, prepareWallet, prepareClient, + fromHex, + getSalt, calculateDomainSeparator, readWasmFile, getChains, + updateContractConfig, uploadContract, instantiateContract, makeInstantiateMsg, fetchCodeIdFromCodeHash, - instantiate2AddressForProposal, decodeProposalAttributes, encodeStoreCodeProposal, encodeStoreInstantiateProposal,