diff --git a/.github/actions/setup-sui/action.yaml b/.github/actions/setup-sui/action.yaml index 030604cf..1c544e57 100644 --- a/.github/actions/setup-sui/action.yaml +++ b/.github/actions/setup-sui/action.yaml @@ -37,7 +37,6 @@ runs: mkdir -p sui-binaries mv ./sui ./sui-binaries/ mv ./sui-debug ./sui-binaries/ - mv ./sui-test-validator ./sui-binaries/ rm -rf sui-${{ inputs.SUI_VERSION }}-ubuntu-x86_64.tgz - name: Save Sui binaries @@ -53,7 +52,6 @@ runs: run: | sudo cp ./sui-cli/sui-binaries/sui /usr/local/bin/sui sudo cp ./sui-cli/sui-binaries/sui-debug /usr/local/bin/sui-debug - sudo cp ./sui-cli/sui-binaries/sui-test-validator /usr/local/bin/sui-test-validator - name: Setup Sui Wallet shell: bash diff --git a/.github/workflows/test-sui.yaml b/.github/workflows/test-sui.yaml index 47d14a32..155ebdb9 100644 --- a/.github/workflows/test-sui.yaml +++ b/.github/workflows/test-sui.yaml @@ -1,22 +1,41 @@ name: Test Sui -on: - pull_request: - paths: - - sui/** - - common/** - - .github/actions/setup-sui/** - - .github/workflows/test-sui.yaml +on: pull_request jobs: + check-relevant-changes: + name: Check for Relevant Changes + runs-on: blacksmith-2vcpu-ubuntu-2204 + outputs: + run_tests: ${{ steps.filter.outputs.sui == 'true' || steps.filter.outputs.common == 'true' || steps.filter.outputs.github == 'true' }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + sui: + - 'sui/**' + common: + - 'common/**' + github: + - '.github/actions/setup-sui/**' + - '.github/workflows/test-sui.yaml' + - name: Summarize Changes + run: | + echo "Changes in sui: ${{ steps.filter.outputs.sui }}" + echo "Changes in common: ${{ steps.filter.outputs.common }}" + echo "Changes in github: ${{ steps.filter.outputs.github }}" test-sui: name: Test Sui + needs: check-relevant-changes + if: ${{ needs.check-relevant-changes.outputs.run_tests == 'true' }} runs-on: blacksmith-2vcpu-ubuntu-2204 steps: - name: Checkout code uses: actions/checkout@v4 - - name: Install Node.js + - name: Setup Node.js uses: useblacksmith/setup-node@v5 with: node-version: 18.x @@ -37,7 +56,9 @@ jobs: SUI_VERSION: ${{ env.SUI_VERSION }} - name: Spin up Sui Network - run: nohup sh -c "sui-test-validator" > nohup.out 2> nohup.err < /dev/null & + # sui-test-validator will be deprecated in the future. + # this command follows the guide in https://docs.sui.io/guides/developer/getting-started/local-network + run: nohup sh -c "RUST_LOG="off,sui_node=info" sui start --with-faucet --force-regenesis" > nohup.out 2> nohup.err < /dev/null & - name: Wait for Sui network uses: nev7n/wait_for_response@v1 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, diff --git a/package-lock.json b/package-lock.json index 4e5b0c8d..45db62c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@axelar-network/axelar-cgp-solidity": "6.3.1", - "@axelar-network/axelar-cgp-sui": "^0.4.1", + "@axelar-network/axelar-cgp-sui": "^0.5.0", "@axelar-network/axelar-gmp-sdk-solidity": "5.10.0", "@axelar-network/interchain-token-service": "1.2.4", "@cosmjs/cosmwasm-stargate": "^0.32.1", @@ -42,38 +42,6 @@ "node": ">=18" } }, - "../axelar-cgp-sui": { - "version": "0.3.0", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@cosmjs/cosmwasm-stargate": "^0.32.2", - "@mysten/sui": "^1.3.0", - "ethers": "^5.0.0", - "secp256k1": "^5.0.0", - "tmp": "^0.2.1", - "typescript": "^5.3.3" - }, - "devDependencies": { - "@changesets/cli": "^2.27.6", - "@ianvs/prettier-plugin-sort-imports": "^4.2.1", - "@types/node": "^20.14.11", - "@types/tmp": "^0.2.6", - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", - "chai": "^4.3.7", - "dotenv": "^16.3.1", - "eslint": "^8.57.0", - "eslint-config-richardpringle": "^2.0.0", - "mocha": "^10.4.0", - "prettier": "^2.8.7", - "prettier-plugin-sort-imports": "^1.8.5", - "typescript": "^5.5.3" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@0no-co/graphql.web": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.7.tgz", @@ -176,9 +144,10 @@ } }, "node_modules/@axelar-network/axelar-cgp-sui": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-sui/-/axelar-cgp-sui-0.4.1.tgz", - "integrity": "sha512-7ZPotOheP0ZxSm++SHsjpvTTsTuyJx747U3S0Ud+3RVXNBMnMCuC0jZsoyqD0sJMJdQLmT4pz/fFOIPCWAhjhw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-sui/-/axelar-cgp-sui-0.5.0.tgz", + "integrity": "sha512-DgsClAAZ211nDNM/cjxDdrO5OGVZeMAKbaRPxnStU/Gsj66r0iw3xQf8xJudEAOWcNKRXgjVAGrP87MHOB34sg==", + "license": "MIT", "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.32.2", "@mysten/sui": "^1.3.0", diff --git a/package.json b/package.json index 12fc79e1..bdba82d5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "homepage": "https://github.com/axelarnetwork/axelar-contract-deployments#readme", "dependencies": { "@axelar-network/axelar-cgp-solidity": "6.3.1", - "@axelar-network/axelar-cgp-sui": "^0.4.1", + "@axelar-network/axelar-cgp-sui": "^0.5.0", "@axelar-network/axelar-gmp-sdk-solidity": "5.10.0", "@axelar-network/interchain-token-service": "1.2.4", "@cosmjs/cosmwasm-stargate": "^0.32.1", diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 9aa99b58..4e2bd0f8 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -1,5 +1,5 @@ const { Command, Option } = require('commander'); -const { updateMoveToml, TxBuilder } = require('@axelar-network/axelar-cgp-sui'); +const { updateMoveToml, TxBuilder, bcsStructs } = require('@axelar-network/axelar-cgp-sui'); const { ethers } = require('hardhat'); const { toB64 } = require('@mysten/sui/utils'); const { bcs } = require('@mysten/sui/bcs'); @@ -14,8 +14,6 @@ const { getWallet, printWalletInfo, broadcast, - bytes32Struct, - signersStruct, upgradePackage, UPGRADE_POLICIES, getSigners, @@ -153,12 +151,10 @@ async function postDeployAxelarGateway(published, keypair, client, config, chain `${suiPackageAddress}::package::UpgradeCap`, ]); - const encodedSigners = signersStruct - .serialize({ - ...signers, - nonce: bytes32Struct.serialize(signers.nonce).toBytes(), - }) - .toBytes(); + const encodedSigners = bcsStructs.gateway.WeightedSigners.serialize({ + ...signers, + nonce: bcsStructs.common.Bytes32.serialize(signers.nonce).toBytes(), + }).toBytes(); const tx = new Transaction(); diff --git a/sui/gas-service.js b/sui/gas-service.js index eedcf82e..1dc029e2 100644 --- a/sui/gas-service.js +++ b/sui/gas-service.js @@ -2,6 +2,7 @@ const { Command } = require('commander'); const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); const { ethers } = require('hardhat'); +const { bcsStructs } = require('@axelar-network/axelar-cgp-sui'); const { utils: { arrayify }, } = ethers; @@ -10,7 +11,6 @@ const { getWallet, printWalletInfo, broadcast, - gasServiceStruct, getBcsBytesByObjectId, getFormattedAmount, addOptionsToCommands, @@ -91,7 +91,7 @@ async function collectGas(keypair, client, gasServiceConfig, args, options) { const receiver = options.receiver || walletAddress; const bytes = await getBcsBytesByObjectId(client, gasServiceObjectId); - const { balance: gasServiceBalance } = gasServiceStruct.parse(bytes); + const { balance: gasServiceBalance } = bcsStructs.gasService.GasService.parse(bytes); // Check if the gas service balance is sufficient if (gasServiceBalance < unitAmount) { @@ -127,7 +127,7 @@ async function refund(keypair, client, gasServiceConfig, args, options) { const receiver = options.receiver || walletAddress; const bytes = await getBcsBytesByObjectId(client, gasServiceObjectId); - const { balance: gasServiceBalance } = gasServiceStruct.parse(bytes); + const { balance: gasServiceBalance } = bcsStructs.gasService.GasService.parse(bytes); // Check if the gas service balance is sufficient if (gasServiceBalance < unitAmount) { diff --git a/sui/gateway.js b/sui/gateway.js index dd1d6a07..4cfcd84d 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -2,6 +2,7 @@ const { Command, Option } = require('commander'); const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); const { ethers } = require('hardhat'); +const { bcsStructs } = require('@axelar-network/axelar-cgp-sui'); const { utils: { arrayify, keccak256, toUtf8Bytes }, constants: { HashZero }, @@ -9,12 +10,6 @@ const { const { saveConfig, printInfo, loadConfig, getMultisigProof } = require('../common/utils'); const { - bytes32Struct, - signersStruct, - messageToSignStruct, - messageStruct, - executeDataStruct, - proofStruct, addBaseOptions, addOptionsToCommands, getSigners, @@ -88,22 +83,18 @@ function getProof(keypair, commandType, data, contractConfig, options) { const dataHash = arrayify(hashMessage(commandType, data)); - const message = messageToSignStruct - .serialize({ - domain_separator: contractConfig.domainSeparator, - signers_hash: keccak256(signersStruct.serialize(signers).toBytes()), - data_hash: dataHash, - }) - .toBytes(); + const message = bcsStructs.gateway.MessageToSign.serialize({ + domain_separator: contractConfig.domainSeparator, + signers_hash: keccak256(bcsStructs.gateway.WeightedSigners.serialize(signers).toBytes()), + data_hash: dataHash, + }).toBytes(); const signatures = getSignatures(keypair, message, options); - const encodedProof = proofStruct - .serialize({ - signers, - signatures, - }) - .toBytes(); + const encodedProof = bcsStructs.gateway.Proof.serialize({ + signers, + signatures, + }).toBytes(); return encodedProof; } @@ -160,7 +151,7 @@ async function approveMessages(keypair, client, config, chain, contractConfig, a throw new Error('Multisig session not completed'); } - const executeData = executeDataStruct.parse(arrayify('0x' + status.completed.execute_data)); + const executeData = bcsStructs.gateway.ExecuteData.parse(arrayify('0x' + status.completed.execute_data)); const tx = new Transaction(); @@ -183,14 +174,14 @@ async function approve(keypair, client, config, chain, contractConfig, args, opt const [sourceChain, messageId, sourceAddress, destinationId, payloadHash] = args; const encodedMessages = bcs - .vector(messageStruct) + .vector(bcsStructs.gateway.Message) .serialize([ { source_chain: sourceChain, message_id: messageId, source_address: sourceAddress, destination_id: destinationId, - payload_hash: bytes32Struct.serialize(arrayify(payloadHash)).toBytes(), + payload_hash: payloadHash, }, ]) .toBytes(); @@ -222,7 +213,7 @@ async function submitProof(keypair, client, config, chain, contractConfig, args, throw new Error('Multisig session not completed'); } - const executeData = executeDataStruct.parse(arrayify('0x' + status.completed.execute_data)); + const executeData = bcsStructs.gateway.ExecuteData.parse(arrayify('0x' + status.completed.execute_data)); const tx = new Transaction(); @@ -263,12 +254,10 @@ async function rotate(keypair, client, config, chain, contractConfig, args, opti const signers = await getSigners(keypair, config, chain.axelarId, options); const newNonce = options.newNonce ? keccak256(toUtf8Bytes(options.newNonce)) : signers.nonce; - const encodedSigners = signersStruct - .serialize({ - ...signers, - nonce: bytes32Struct.serialize(newNonce).toBytes(), - }) - .toBytes(); + const encodedSigners = bcsStructs.gateway.WeightedSigners.serialize({ + ...signers, + nonce: bcsStructs.common.Bytes32.serialize(newNonce).toBytes(), + }).toBytes(); const encodedProof = getProof(keypair, COMMAND_TYPE_ROTATE_SIGNERS, encodedSigners, contractConfig, options); diff --git a/sui/gmp.js b/sui/gmp.js index 9751b1f5..3efa7ef7 100644 --- a/sui/gmp.js +++ b/sui/gmp.js @@ -1,6 +1,7 @@ const { Command } = require('commander'); const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); +const { bcsStructs } = require('@axelar-network/axelar-cgp-sui'); const { loadConfig, saveConfig, printInfo } = require('../common/utils'); const { getBcsBytesByObjectId, @@ -9,7 +10,6 @@ const { getUnitAmount, getWallet, printWalletInfo, - discoveryStruct, broadcast, } = require('./utils'); const { ethers } = require('hardhat'); @@ -68,7 +68,7 @@ async function execute(keypair, client, contracts, args, options) { // Get Discovery table id from discovery object const tableBcsBytes = await getBcsBytesByObjectId(client, discoveryObjectId); - const { fields } = discoveryStruct.parse(tableBcsBytes); + const { fields } = bcsStructs.common.Discovery.parse(tableBcsBytes); const tableId = fields.id; // Get the transaction list from the discovery table diff --git a/sui/utils/index.js b/sui/utils/index.js index 4c19b535..7bdcf5a4 100644 --- a/sui/utils/index.js +++ b/sui/utils/index.js @@ -1,7 +1,6 @@ module.exports = { ...require('./amount-utils'), ...require('./sign-utils'), - ...require('./types-utils'), ...require('./utils'), ...require('./upgrade-utils'), ...require('./cli-utils'), diff --git a/sui/utils/types-utils.js b/sui/utils/types-utils.js deleted file mode 100644 index 0137a899..00000000 --- a/sui/utils/types-utils.js +++ /dev/null @@ -1,149 +0,0 @@ -'use strict'; - -const { bcs } = require('@mysten/sui/bcs'); -const { fromHEX, toHEX } = require('@mysten/bcs'); -const { ethers } = require('hardhat'); -const { - utils: { arrayify, hexlify }, -} = ethers; - -const addressStruct = bcs.bytes(32).transform({ - input: (val) => arrayify(val), - output: (val) => hexlify(val), -}); - -const signerStruct = bcs.struct('WeightedSigner', { - pub_key: bcs.vector(bcs.u8()), - weight: bcs.u128(), -}); - -const bytes32Struct = bcs.fixedArray(32, bcs.u8()).transform({ - input: (id) => arrayify(id), - output: (id) => hexlify(id), -}); - -const UID = bcs.fixedArray(32, bcs.u8()).transform({ - input: (id) => fromHEX(id), - output: (id) => toHEX(Uint8Array.from(id)), -}); - -const signersStruct = bcs.struct('WeightedSigners', { - signers: bcs.vector(signerStruct), - threshold: bcs.u128(), - nonce: bytes32Struct, -}); - -const messageToSignStruct = bcs.struct('MessageToSign', { - domain_separator: bytes32Struct, - signers_hash: bytes32Struct, - data_hash: bytes32Struct, -}); - -const messageStruct = bcs.struct('Message', { - source_chain: bcs.string(), - message_id: bcs.string(), - source_address: bcs.string(), - destination_id: addressStruct, - payload_hash: bytes32Struct, -}); - -const approvedMessageStruct = bcs.struct('ApprovedMessage', { - source_chain: bcs.string(), - message_id: bcs.string(), - source_address: bcs.string(), - destination_id: addressStruct, - payload: bcs.vector(bcs.u8()), -}); - -const proofStruct = bcs.struct('Proof', { - signers: signersStruct, - signatures: bcs.vector(bcs.vector(bcs.u8())), -}); - -const gasServiceStruct = bcs.struct('GasService', { - id: UID, - balance: bcs.u64(), -}); - -const channelStruct = bcs.struct('Channel', { - id: UID, -}); - -const singletonStruct = bcs.struct('Singleton', { - id: UID, - channel: channelStruct, -}); - -const discoveryTable = bcs.struct('DiscoveryTable', { - id: UID, -}); - -const discoveryStruct = bcs.struct('Discovery', { - id: UID, - fields: discoveryTable, -}); - -const bagStruct = bcs.struct('Bag', { - id: UID, - size: bcs.U64, -}); - -const operatorsStruct = bcs.struct('Operators', { - id: UID, - operators: bcs.vector(addressStruct), - caps: bagStruct, -}); - -const tableStruct = bcs.struct('Table', { - id: UID, - size: bcs.U64, -}); - -const interchainAddressTrackerStruct = bcs.struct('InterchainAddressTracker', { - trusted_addresses: tableStruct, -}); - -const itsStruct = bcs.struct('ITS', { - id: UID, - channel: channelStruct, - address_tracker: interchainAddressTrackerStruct, - unregistered_coin_types: tableStruct, - unregistered_coin_info: bagStruct, - registered_coin_types: tableStruct, - registered_coins: bagStruct, - relayer_discovery_id: UID, -}); - -const coinBagStrcut = bcs.struct('CoinBag', { - bag: bagStruct, -}); - -const squidStruct = bcs.struct('Squid', { - id: UID, - channel: channelStruct, - coin_bag: coinBagStrcut, -}); - -const executeDataStruct = bcs.struct('ExecuteData', { - payload: bcs.vector(bcs.u8()), - proof: bcs.vector(bcs.u8()), -}); - -module.exports = { - addressStruct, - signerStruct, - bytes32Struct, - signersStruct, - operatorsStruct, - messageToSignStruct, - messageStruct, - approvedMessageStruct, - proofStruct, - gasServiceStruct, - channelStruct, - singletonStruct, - discoveryStruct, - itsStruct, - squidStruct, - executeDataStruct, -}; diff --git a/sui/utils/utils.js b/sui/utils/utils.js index 7d083767..da1d84fa 100644 --- a/sui/utils/utils.js +++ b/sui/utils/utils.js @@ -12,8 +12,7 @@ const { const fs = require('fs'); const { fromB64 } = require('@mysten/bcs'); const { CosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); -const { updateMoveToml, copyMovePackage, TxBuilder } = require('@axelar-network/axelar-cgp-sui'); -const { singletonStruct, itsStruct, squidStruct } = require('./types-utils'); +const { updateMoveToml, copyMovePackage, TxBuilder, bcsStructs } = require('@axelar-network/axelar-cgp-sui'); const suiPackageAddress = '0x2'; const suiClockAddress = '0x6'; @@ -131,19 +130,19 @@ const getObjectIdsByObjectTypes = (txn, objectTypes) => // Parse bcs bytes from singleton object which is created when the Test contract is deployed const getSingletonChannelId = async (client, singletonObjectId) => { const bcsBytes = await getBcsBytesByObjectId(client, singletonObjectId); - const data = singletonStruct.parse(bcsBytes); + const data = bcsStructs.gmp.Singleton.parse(bcsBytes); return '0x' + data.channel.id; }; const getItsChannelId = async (client, itsObjectId) => { const bcsBytes = await getBcsBytesByObjectId(client, itsObjectId); - const data = itsStruct.parse(bcsBytes); + const data = bcsStructs.its.ITS.parse(bcsBytes); return '0x' + data.channel.id; }; const getSquidChannelId = async (client, squidObjectId) => { const bcsBytes = await getBcsBytesByObjectId(client, squidObjectId); - const data = squidStruct.parse(bcsBytes); + const data = bcsStructs.squid.Squid.parse(bcsBytes); return '0x' + data.channel.id; }; diff --git a/sui/version.json b/sui/version.json index 32a8c3ef..9ce54d65 100644 --- a/sui/version.json +++ b/sui/version.json @@ -1,3 +1,3 @@ { - "SUI_VERSION": "mainnet-v1.25.3" + "SUI_VERSION": "testnet-v1.32.0" }