diff --git a/README.md b/README.md index 7a4b8178..17ba338b 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,9 @@ Install dependencies via `npm ci` -[EVM deployment instructions](./evm/README.md) +## Deployment Instructions + +- [EVM](./evm/README.md) +- [Cosmwasm](./cosmwasm/README.md) +- [Sui](./sui/README.md) +- [Stellar](./stellar/README.md) diff --git a/common/utils.js b/common/utils.js index 71ce7284..3ffc004c 100644 --- a/common/utils.js +++ b/common/utils.js @@ -10,7 +10,7 @@ const readlineSync = require('readline-sync'); const { CosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); const { ethers } = require('hardhat'); const { - utils: { keccak256, hexlify }, + utils: { keccak256, hexlify, defaultAbiCoder }, } = ethers; const { normalizeBech32 } = require('@cosmjs/encoding'); @@ -347,6 +347,17 @@ const isValidCosmosAddress = (str) => { } }; +const getSaltFromKey = (key) => { + return keccak256(defaultAbiCoder.encode(['string'], [key.toString()])); +}; + +const getContractConfig = async (config, chain) => { + const key = Buffer.from('config'); + const client = await CosmWasmClient.connect(config.axelar.rpc); + const value = await client.queryContractRaw(config.axelar.contracts.MultisigProver[chain].address, key); + return JSON.parse(Buffer.from(value).toString('ascii')); +}; + async function getDomainSeparator(config, chain, options) { // Allow any domain separator for local deployments or `0x` if not provided if (options.env === 'local') { @@ -377,22 +388,38 @@ async function getDomainSeparator(config, chain, options) { throw new Error(`missing or invalid chain ID`); } + const expectedDomainSeparator = calculateDomainSeparator(chain.axelarId, routerAddress, chainId); + + if (options.domainSeparator === 'offline') { + printInfo('Computed domain separator offline'); + return expectedDomainSeparator; + } + printInfo(`Retrieving domain separator for ${chain.name} from Axelar network`); const domainSeparator = hexlify((await getContractConfig(config, chain.axelarId)).domain_separator); - const expectedDomainSeparator = calculateDomainSeparator(chain.axelarId, routerAddress, chainId); if (domainSeparator !== expectedDomainSeparator) { throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); } - return domainSeparator; + return expectedDomainSeparator; } -const getContractConfig = async (config, chain) => { - const key = Buffer.from('config'); +const getChainConfig = (config, chainName) => { + const chainConfig = config.chains[chainName] || config[chainName]; + + if (!chainConfig) { + throw new Error(`Chain ${chainName} not found in config`); + } + + return chainConfig; +}; + +const getMultisigProof = async (config, chain, multisigSessionId) => { + const query = { proof: { multisig_session_id: `${multisigSessionId}` } }; const client = await CosmWasmClient.connect(config.axelar.rpc); - const value = await client.queryContractRaw(config.axelar.contracts.MultisigProver[chain].address, key); - return JSON.parse(Buffer.from(value).toString('ascii')); + const value = await client.queryContractSmart(config.axelar.contracts.MultisigProver[chain].address, query); + return value; }; const calculateDomainSeparator = (chain, router, network) => keccak256(Buffer.from(`${chain}${router}${network}`)); @@ -429,4 +456,9 @@ module.exports = { timeout, validateParameters, getDomainSeparator, + getChainConfig, + getMultisigProof, + getContractConfig, + getSaltFromKey, + calculateDomainSeparator, }; diff --git a/cosmwasm/deploy-contract.js b/cosmwasm/deploy-contract.js index 86432027..57d418bc 100644 --- a/cosmwasm/deploy-contract.js +++ b/cosmwasm/deploy-contract.js @@ -4,7 +4,7 @@ require('dotenv').config(); const { isNil } = require('lodash'); const { CHAIN_ENVIRONMENTS } = require('../common'); -const { isNumber, printInfo, loadConfig, saveConfig, prompt } = require('../evm/utils'); +const { isNumber, printInfo, loadConfig, saveConfig, prompt, getChainConfig } = require('../evm/utils'); const { prepareWallet, prepareClient, @@ -24,8 +24,8 @@ const upload = (client, wallet, chainName, config, options) => { axelar: { contracts: { [contractName]: contractConfig }, }, - chains: { [chainName]: chainConfig }, } = config; + const chainConfig = chainName === 'none' ? undefined : getChainConfig(config, chainName); if (!fetchCodeId && (!reuseCodeId || isNil(contractConfig.codeId))) { printInfo('Uploading contract binary'); @@ -53,7 +53,7 @@ const upload = (client, wallet, chainName, config, options) => { .then(() => ({ wallet, client })); } - printInfo('Skipping upload. Reusing previously uploaded binary'); + printInfo('Skipping upload. Reusing previously uploaded bytecode'); return Promise.resolve({ wallet, client }); }; diff --git a/cosmwasm/submit-proposal.js b/cosmwasm/submit-proposal.js index d638b470..940aaeae 100644 --- a/cosmwasm/submit-proposal.js +++ b/cosmwasm/submit-proposal.js @@ -258,7 +258,7 @@ const programHandler = () => { .env('ENV'), ); program.addOption(new Option('-m, --mnemonic ', 'mnemonic').makeOptionMandatory(true).env('MNEMONIC')); - program.addOption(new Option('-a, --artifactPath ', 'artifact path').makeOptionMandatory(true).env('ARTIFACT_PATH')); + program.addOption(new Option('-a, --artifactPath ', 'artifact path').env('ARTIFACT_PATH')); program.addOption(new Option('-c, --contractName ', 'contract name').makeOptionMandatory(true)); program.addOption(new Option('-n, --chainNames ', 'chain names').default('none')); diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js index 7a897da6..33c5239d 100644 --- a/cosmwasm/utils.js +++ b/cosmwasm/utils.js @@ -1,10 +1,6 @@ 'use strict'; const zlib = require('zlib'); -const { ethers } = require('hardhat'); -const { - utils: { keccak256 }, -} = ethers; const { createHash } = require('crypto'); const { readFileSync } = require('fs'); @@ -20,7 +16,16 @@ const { ExecuteContractProposal, } = 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 { + isString, + isStringArray, + isKeccak256Hash, + isNumber, + toBigNumberString, + getChainConfig, + getSaltFromKey, + calculateDomainSeparator, +} = require('../common'); const { normalizeBech32 } = require('@cosmjs/encoding'); const governanceAddress = 'axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj'; @@ -46,8 +51,6 @@ const isValidCosmosAddress = (str) => { const fromHex = (str) => new Uint8Array(Buffer.from(str.replace('0x', ''), 'hex')); -const calculateDomainSeparator = (chain, router, network) => keccak256(Buffer.from(`${chain}${router}${network}`)); - const getSalt = (salt, contractName, chainNames) => fromHex(getSaltFromKey(salt || contractName.concat(chainNames))); const getLabel = ({ contractName, label }) => label || contractName; @@ -66,11 +69,7 @@ const getChains = (config, { chainNames, instantiate2 }) => { throw new Error('Cannot pass --instantiate2 with more than one chain'); } - const undefinedChain = chains.find((chain) => !config.chains[chain.toLowerCase()] && chain !== 'none'); - - if (undefinedChain) { - throw new Error(`Chain ${undefinedChain} is not defined in the info file`); - } + chains.every((chain) => chain === 'none' || getChainConfig(config, chain)); return chains; }; @@ -216,7 +215,7 @@ const makeNexusGatewayInstantiateMsg = ({ nexus }, { Router: { address: router } const makeVotingVerifierInstantiateMsg = ( contractConfig, { ServiceRegistry: { address: serviceRegistryAddress }, Rewards: { address: rewardsAddress } }, - { id: chainId }, + { axelarId: chainId }, ) => { const { [chainId]: { @@ -286,7 +285,7 @@ const makeVotingVerifierInstantiateMsg = ( }; }; -const makeGatewayInstantiateMsg = ({ Router: { address: routerAddress }, VotingVerifier }, { id: chainId }) => { +const makeGatewayInstantiateMsg = ({ Router: { address: routerAddress }, VotingVerifier }, { axelarId: chainId }) => { const { [chainId]: { address: verifierAddress }, } = VotingVerifier; @@ -305,8 +304,8 @@ const makeGatewayInstantiateMsg = ({ Router: { address: routerAddress }, VotingV const makeMultisigProverInstantiateMsg = (config, chainName) => { const { axelar: { contracts, chainId: axelarChainId }, - chains: { [chainName]: chainConfig }, } = config; + const chainConfig = getChainConfig(config, chainName); const { axelarId: chainId } = chainConfig; @@ -424,8 +423,8 @@ const makeMultisigProverInstantiateMsg = (config, chainName) => { const makeInstantiateMsg = (contractName, chainName, config) => { const { axelar: { contracts }, - chains: { [chainName]: chainConfig }, } = config; + const chainConfig = getChainConfig(config, chainName); const { [contractName]: contractConfig } = contracts; @@ -618,12 +617,12 @@ const getExecuteContractParams = (config, options, chainName) => { axelar: { contracts: { [contractName]: contractConfig }, }, - chains: { [chainName]: chainConfig }, } = config; + const chainConfig = getChainConfig(config, chainName); return { ...getSubmitProposalParams(options), - contract: chainConfig ? contractConfig[chainConfig.axelarId].address : contractConfig.address, + contract: contractConfig[chainConfig.axelarId]?.address || contractConfig.address, msg: Buffer.from(msg), }; }; diff --git a/evm/utils.js b/evm/utils.js index 612e4c1d..2cf54448 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -34,6 +34,7 @@ const { sleep, findProjectRoot, timeout, + getSaltFromKey, } = require('../common'); const { create3DeployContract, @@ -48,10 +49,6 @@ const IDeployer = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/ID const { exec } = require('child_process'); const { verifyContract } = require(`${__dirname}/../axelar-chains-config`); -const getSaltFromKey = (key) => { - return keccak256(defaultAbiCoder.encode(['string'], [key.toString()])); -}; - const deployCreate = async (wallet, contractJson, args = [], options = {}, verifyOptions = null, chain = {}) => { const factory = new ContractFactory(contractJson.abi, contractJson.bytecode, wallet); @@ -493,13 +490,6 @@ const getEVMAddresses = async (config, chain, options = {}) => { return { addresses, weights, threshold, keyID: evmAddresses.key_id }; }; -const getContractConfig = async (config, chain) => { - const key = Buffer.from('config'); - const client = await CosmWasmClient.connect(config.axelar.rpc); - const value = await client.queryContractRaw(config.axelar.contracts.MultisigProver[chain].address, key); - return JSON.parse(Buffer.from(value).toString('ascii')); -}; - const getAmplifierKeyAddresses = async (config, chain) => { const client = await CosmWasmClient.connect(config.axelar.rpc); const { id: verifierSetId, verifier_set: verifierSet } = await client.queryContractSmart( @@ -1062,7 +1052,6 @@ module.exports = { getDeployOptions, isValidChain, getAmplifierKeyAddresses, - getContractConfig, relayTransaction, getDeploymentTx, getWeightedSigners, diff --git a/package-lock.json b/package-lock.json index a9f2d8e4..1c96f88d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3116,9 +3116,9 @@ } }, "node_modules/axios": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", - "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -4373,9 +4373,9 @@ } }, "node_modules/elliptic": { - "version": "6.5.6", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.6.tgz", - "integrity": "sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==", + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -7605,9 +7605,9 @@ "peer": true }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "peer": true, "dependencies": { diff --git a/sui/README.md b/sui/README.md index 373fed84..636858d4 100644 --- a/sui/README.md +++ b/sui/README.md @@ -189,6 +189,11 @@ node sui/gateway.js rotate --signers wallet --proof wallet --currentNonce test - Use the same nonce for `--currentNonce` as the `--nonce` when deploying the gateway. +To submit a proof constructed on Amplifier, run the following with the multisig session id, +```bash +node sui/gateway.js submitProof [multisig session id] +``` + ### Multisig To create a Multisig, follow the documentation [here](https://docs.sui.io/guides/developer/cryptography/multisig). diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 6a2da820..fd8b454e 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -195,6 +195,7 @@ async function postDeployAxelarGateway(published, keypair, client, config, chain // Update chain configuration chain.contracts.AxelarGateway = { + address: packageId, objects: { Gateway: gateway, RelayerDiscovery: relayerDiscovery, @@ -248,7 +249,8 @@ async function deploy(keypair, client, supportedContract, config, chain, options // Deploy package const published = await deployPackage(packageDir, client, keypair, options); - printInfo(`Deployed ${packageName}`, published.publishTxn.digest); + printInfo(`Deployed ${packageName} Package`, published.packageId); + printInfo(`Deployed ${packageName} Tx`, published.publishTxn.digest); // Update chain configuration with deployed contract address chain.contracts[packageName] = { @@ -325,7 +327,10 @@ const GATEWAY_CMD_OPTIONS = [ new Option('--minimumRotationDelay ', 'minium delay for signer rotations (in second)') .argParser((val) => parseInt(val) * 1000) .default(24 * 60 * 60), - new Option('--domainSeparator ', 'domain separator'), + new Option( + '--domainSeparator ', + 'domain separator (pass in the keccak256 hash value OR "offline" meaning that its computed locally)', + ), new Option('--nonce ', 'nonce for the signer (defaults to HashZero)'), new Option('--previousSigners ', 'number of previous signers to retain').default('15'), ]; diff --git a/sui/gateway.js b/sui/gateway.js index f83a25fb..7425b786 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -7,19 +7,22 @@ const { constants: { HashZero }, } = ethers; -const { saveConfig, printInfo, loadConfig } = require('../common/utils'); +const { saveConfig, printInfo, loadConfig, getMultisigProof } = require('../common/utils'); const { bytes32Struct, signersStruct, messageToSignStruct, messageStruct, + executeDataStruct, proofStruct, addBaseOptions, + addOptionsToCommands, getSigners, getWallet, printWalletInfo, getRawPrivateKey, broadcast, + suiClockAddress, } = require('./utils'); const secp256k1 = require('secp256k1'); @@ -105,12 +108,7 @@ function getProof(keypair, commandType, data, contractConfig, options) { return encodedProof; } -async function callContract(keypair, client, config, chain, args, options) { - if (!chain.contracts.axelar_gateway) { - throw new Error('Axelar Gateway package not found.'); - } - - const contractConfig = chain.contracts.axelar_gateway; +async function callContract(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const [destinationChain, destinationAddress, payload] = args; @@ -149,12 +147,38 @@ async function callContract(keypair, client, config, chain, args, options) { printInfo('Contract called'); } -async function approveMessages(keypair, client, config, chain, args, options) { - if (!chain.contracts.axelar_gateway) { - throw new Error('Axelar Gateway package not found.'); +async function approveMessages(keypair, client, config, chain, contractConfig, args, options) { + const packageId = contractConfig.address; + const [multisigSessionId] = args; + const { payload, status } = await getMultisigProof(config, chain.axelarId, multisigSessionId); + + if (!payload.messages) { + throw new Error('No messages to approve'); } - const contractConfig = chain.contracts.axelar_gateway; + if (!status.completed) { + throw new Error('Multisig session not completed'); + } + + const executeData = executeDataStruct.parse(arrayify('0x' + status.completed.execute_data)); + + const tx = new Transaction(); + + tx.moveCall({ + target: `${packageId}::gateway::approve_messages`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.payload)).toBytes()), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.proof)).toBytes()), + ], + }); + + const receipt = await broadcast(client, keypair, tx); + + printInfo('Approved messages', receipt.digest); +} + +async function approve(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const [sourceChain, messageId, sourceAddress, destinationId, payloadHash] = args; @@ -178,23 +202,67 @@ async function approveMessages(keypair, client, config, chain, args, options) { tx.moveCall({ target: `${packageId}::gateway::approve_messages`, arguments: [ - tx.object(contractConfig.objects.gateway), + tx.object(contractConfig.objects.Gateway), tx.pure(bcs.vector(bcs.u8()).serialize(encodedMessages).toBytes()), tx.pure(bcs.vector(bcs.u8()).serialize(encodedProof).toBytes()), ], }); - await broadcast(client, keypair, tx); + const receipt = await broadcast(client, keypair, tx); - printInfo('Approved messages'); + printInfo('Approved messages', receipt.digest); } -async function rotateSigners(keypair, client, config, chain, args, options) { - if (!chain.contracts.axelar_gateway) { - throw new Error('Axelar Gateway package not found.'); +async function submitProof(keypair, client, config, chain, contractConfig, args, options) { + const packageId = contractConfig.address; + const [multisigSessionId] = args; + const { payload, status } = await getMultisigProof(config, chain.axelarId, multisigSessionId); + + if (!status.completed) { + throw new Error('Multisig session not completed'); } - const contractConfig = chain.contracts.axelar_gateway; + const executeData = executeDataStruct.parse(arrayify('0x' + status.completed.execute_data)); + + if (!payload.verifier_set) { + throw new Error('No signers to rotate'); + } + + const tx = new Transaction(); + + if (payload.verifier_set) { + printInfo('Submitting rotate_signers'); + + tx.moveCall({ + target: `${packageId}::gateway::rotate_signers`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object(suiClockAddress), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.payload)).toBytes()), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.proof)).toBytes()), + ], + }); + } else if (payload.messages) { + printInfo('Submitting approve_messages'); + + tx.moveCall({ + target: `${packageId}::gateway::approve_messages`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.payload)).toBytes()), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.proof)).toBytes()), + ], + }); + } else { + throw new Error(`Unknown payload type: ${payload}`); + } + + const receipt = await broadcast(client, keypair, tx); + + printInfo('Signers rotated', receipt.digest); +} + +async function rotate(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const signers = await getSigners(keypair, config, chain.axelarId, options); @@ -213,16 +281,16 @@ async function rotateSigners(keypair, client, config, chain, args, options) { tx.moveCall({ target: `${packageId}::gateway::rotate_signers`, arguments: [ - tx.object(contractConfig.objects.gateway), - tx.object('0x6'), + tx.object(contractConfig.objects.Gateway), + tx.object(suiClockAddress), tx.pure(bcs.vector(bcs.u8()).serialize(encodedSigners).toBytes()), tx.pure(bcs.vector(bcs.u8()).serialize(encodedProof).toBytes()), ], }); - await broadcast(client, keypair, tx); + const receipt = await broadcast(client, keypair, tx); - printInfo('Signers rotated succesfully'); + printInfo('Signers rotated', receipt.digest); } async function mainProcessor(processor, args, options) { @@ -231,7 +299,11 @@ async function mainProcessor(processor, args, options) { const [keypair, client] = getWallet(config.sui, options); await printWalletInfo(keypair, client, config.sui, options); - await processor(keypair, client, config, config.sui, args, options); + if (!config.sui.contracts?.AxelarGateway) { + throw new Error('Axelar Gateway package not found.'); + } + + await processor(keypair, client, config, config.sui, config.sui.contracts.AxelarGateway, args, options); saveConfig(config, options.env); } @@ -241,7 +313,7 @@ if (require.main === module) { program.name('gateway').description('Gateway contract operations.'); - const rotateSignersCmd = program + program .command('rotate') .description('Rotate signers of the gateway contract') .addOption(new Option('--signers ', 'JSON with the initial signer set')) @@ -249,19 +321,33 @@ if (require.main === module) { .addOption(new Option('--currentNonce ', 'nonce of the existing signers')) .addOption(new Option('--newNonce ', 'nonce of the new signers (useful for test rotations)')) .action((options) => { - mainProcessor(rotateSigners, [], options); + mainProcessor(rotate, [], options); }); - const approveMessagesCmd = program + program .command('approve ') .description('Approve messages at the gateway contract') .addOption(new Option('--proof ', 'JSON of the proof')) .addOption(new Option('--currentNonce ', 'nonce of the existing signers')) .action((sourceChain, messageId, sourceAddress, destinationId, payloadHash, options) => { - mainProcessor(approveMessages, [sourceChain, messageId, sourceAddress, destinationId, payloadHash], options); + mainProcessor(approve, [sourceChain, messageId, sourceAddress, destinationId, payloadHash], options); + }); + + program + .command('approveMessages ') + .description('Approve messages at the gateway contract from amplifier proof') + .action((multisigSessionId, options) => { + mainProcessor(approveMessages, [multisigSessionId], options); + }); + + program + .command('submitProof ') + .description('Submit proof for the provided amplifier multisig session id') + .action((multisigSessionId, options) => { + mainProcessor(submitProof, [multisigSessionId], options); }); - const callContractCmd = program + program .command('call-contract ') .description('Initiate sending a cross-chain message via the gateway') .addOption(new Option('--channel ', 'Existing channel ID to initiate a cross-chain message over')) @@ -269,10 +355,7 @@ if (require.main === module) { mainProcessor(callContract, [destinationChain, destinationAddress, payload], options); }); - addBaseOptions(program); - addBaseOptions(rotateSignersCmd); - addBaseOptions(callContractCmd); - addBaseOptions(approveMessagesCmd); + addOptionsToCommands(program, addBaseOptions); program.parse(); } diff --git a/sui/utils/types-utils.js b/sui/utils/types-utils.js index afa87595..0137a899 100644 --- a/sui/utils/types-utils.js +++ b/sui/utils/types-utils.js @@ -124,6 +124,11 @@ const squidStruct = bcs.struct('Squid', { coin_bag: coinBagStrcut, }); +const executeDataStruct = bcs.struct('ExecuteData', { + payload: bcs.vector(bcs.u8()), + proof: bcs.vector(bcs.u8()), +}); + module.exports = { addressStruct, signerStruct, @@ -140,4 +145,5 @@ module.exports = { discoveryStruct, itsStruct, squidStruct, + executeDataStruct, };