From edff5352d3343c88d9c19a084d496bce36d9da2a Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Wed, 21 Aug 2024 06:27:15 +0400 Subject: [PATCH 1/9] fix(sui)!: support sui amplifier deployment --- README.md | 7 ++- common/utils.js | 18 ++++-- cosmwasm/deploy-contract.js | 6 +- cosmwasm/submit-proposal.js | 2 +- cosmwasm/utils.js | 20 +++--- sui/deploy-contract.js | 4 +- sui/gateway.js | 117 +++++++++++++++++++++++++++++++----- sui/gmp.js | 26 +++----- sui/types-utils.js | 6 ++ 9 files changed, 149 insertions(+), 57 deletions(-) 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..f21451e8 100644 --- a/common/utils.js +++ b/common/utils.js @@ -383,16 +383,22 @@ async function getDomainSeparator(config, chain, options) { if (domainSeparator !== expectedDomainSeparator) { throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); + +const getChainConfig = (config, chainName) => { + const chainConfig = config.chains[chainName] || config[chainName]; + + if (!chainConfig) { + throw new Error(`Chain ${chainName} not found in config`); } - return domainSeparator; + return chainConfig; } -const getContractConfig = async (config, chain) => { - const key = Buffer.from('config'); +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 +435,6 @@ module.exports = { timeout, validateParameters, getDomainSeparator, + getChainConfig, + getMultisigProof, }; diff --git a/cosmwasm/deploy-contract.js b/cosmwasm/deploy-contract.js index 8e68223b..d06bc807 100644 --- a/cosmwasm/deploy-contract.js +++ b/cosmwasm/deploy-contract.js @@ -3,7 +3,7 @@ require('dotenv').config(); const { isNil } = require('lodash'); -const { isNumber, printInfo, loadConfig, saveConfig, prompt } = require('../evm/utils'); +const { isNumber, printInfo, loadConfig, saveConfig, prompt, getChainConfig } = require('../evm/utils'); const { prepareWallet, prepareClient, @@ -23,8 +23,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'); @@ -52,7 +52,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 ebde299e..da9541a1 100644 --- a/cosmwasm/submit-proposal.js +++ b/cosmwasm/submit-proposal.js @@ -257,7 +257,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 41bfe995..da6e9233 100644 --- a/cosmwasm/utils.js +++ b/cosmwasm/utils.js @@ -20,7 +20,7 @@ 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 { getSaltFromKey, isString, isStringArray, isKeccak256Hash, isNumber, toBigNumberString, getChainConfig } = require('../evm/utils'); const { normalizeBech32 } = require('@cosmjs/encoding'); const governanceAddress = 'axelar10d07y265gmmuvt4z0w9aw880jnsr700j7v9daj'; @@ -66,11 +66,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 +212,7 @@ const makeNexusGatewayInstantiateMsg = ({ nexus }, { Router: { address: router } const makeVotingVerifierInstantiateMsg = ( contractConfig, { ServiceRegistry: { address: serviceRegistryAddress }, Rewards: { address: rewardsAddress } }, - { id: chainId }, + { axelarId: chainId }, ) => { const { [chainId]: { @@ -286,7 +282,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 +301,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 +420,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 +614,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/sui/deploy-contract.js b/sui/deploy-contract.js index af3e6fad..e021a3a5 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -190,6 +190,7 @@ async function postDeployAxelarGateway(published, keypair, client, config, chain // Update chain configuration chain.contracts.AxelarGateway = { + address: packageId, objects: { Gateway: gateway, RelayerDiscovery: relayerDiscovery, @@ -243,7 +244,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] = { diff --git a/sui/gateway.js b/sui/gateway.js index d46721de..798b5faa 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -1,18 +1,18 @@ -const { saveConfig, printInfo } = require('../common/utils'); +const { saveConfig, printInfo, getMultisigProof } = require('../common/utils'); const { Command, Option } = require('commander'); const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); const { ethers } = require('hardhat'); const { - utils: { arrayify, keccak256, toUtf8Bytes }, + utils: { arrayify, keccak256, toUtf8Bytes, hexlify }, constants: { HashZero }, } = ethers; const { loadConfig } = require('../common/utils'); const { addBaseOptions } = require('./cli-utils'); const { getWallet, printWalletInfo, getRawPrivateKey, broadcast } = require('./sign-utils'); -const { bytes32Struct, signersStruct, messageToSignStruct, messageStruct, proofStruct } = require('./types-utils'); -const { getSigners } = require('./deploy-gateway'); +const { bytes32Struct, signersStruct, executeDataStruct, messageToSignStruct, messageStruct, proofStruct } = require('./types-utils'); +const { getSigners } = require('./utils'); const secp256k1 = require('secp256k1'); const COMMAND_TYPE_APPROVE_MESSAGES = 0; @@ -98,11 +98,11 @@ function getProof(keypair, commandType, data, contractConfig, options) { } async function callContract(keypair, client, config, chain, args, options) { - if (!chain.contracts.axelar_gateway) { + if (!chain.contracts.AxelarGateway) { throw new Error('Axelar Gateway package not found.'); } - const contractConfig = chain.contracts.axelar_gateway; + const contractConfig = chain.contracts.AxelarGateway; const packageId = contractConfig.address; const [destinationChain, destinationAddress, payload] = args; @@ -142,11 +142,46 @@ async function callContract(keypair, client, config, chain, args, options) { } async function approveMessages(keypair, client, config, chain, args, options) { - if (!chain.contracts.axelar_gateway) { + if (!chain.contracts.AxelarGateway) { throw new Error('Axelar Gateway package not found.'); } - const contractConfig = chain.contracts.axelar_gateway; + const contractConfig = chain.contracts.AxelarGateway; + 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'); + } + + if (!status.completed) { + throw new Error('Multisig session not completed'); + } + const executeData = executeDataStruct.parse(new Uint8Array(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()), + ], + }); + + await broadcast(client, keypair, tx); + + printInfo('Approved messages'); +} + +async function approve(keypair, client, config, chain, args, options) { + if (!chain.contracts.AxelarGateway) { + throw new Error('Axelar Gateway package not found.'); + } + + const contractConfig = chain.contracts.AxelarGateway; const packageId = contractConfig.address; const [sourceChain, messageId, sourceAddress, destinationId, payloadHash] = args; @@ -182,11 +217,47 @@ async function approveMessages(keypair, client, config, chain, args, options) { } async function rotateSigners(keypair, client, config, chain, args, options) { - if (!chain.contracts.axelar_gateway) { + if (!chain.contracts.AxelarGateway) { + throw new Error('Axelar Gateway package not found.'); + } + + const contractConfig = chain.contracts.AxelarGateway; + const packageId = contractConfig.address; + const [multisigSessionId] = args; + const { payload, status } = await getMultisigProof(config, chain.axelarId, multisigSessionId); + + if (!payload.verifier_set) { + throw new Error('No signers to rotate'); + } + + if (!status.completed) { + throw new Error('Multisig session not completed'); + } + const executeData = executeDataStruct.parse(new Uint8Array(arrayify('0x' + status.completed.execute_data))); + + const tx = new Transaction(); + + tx.moveCall({ + target: `${packageId}::gateway::rotate_signers`, + arguments: [ + tx.object(contractConfig.objects.Gateway), + tx.object('0x6'), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.payload)).toBytes()), + tx.pure(bcs.vector(bcs.u8()).serialize(new Uint8Array(executeData.proof)).toBytes()), + ], + }); + + await broadcast(client, keypair, tx); + + printInfo('Signers rotated succesfully'); +} + +async function rotate(keypair, client, config, chain, args, options) { + if (!chain.contracts.AxelarGateway) { throw new Error('Axelar Gateway package not found.'); } - const contractConfig = chain.contracts.axelar_gateway; + const contractConfig = chain.contracts.AxelarGateway; const packageId = contractConfig.address; const signers = await getSigners(keypair, config, chain.axelarId, options); @@ -205,7 +276,7 @@ async function rotateSigners(keypair, client, config, chain, args, options) { tx.moveCall({ target: `${packageId}::gateway::rotate_signers`, arguments: [ - tx.object(contractConfig.objects.gateway), + tx.object(contractConfig.objects.Gateway), tx.object('0x6'), tx.pure(bcs.vector(bcs.u8()).serialize(encodedSigners).toBytes()), tx.pure(bcs.vector(bcs.u8()).serialize(encodedProof).toBytes()), @@ -233,7 +304,7 @@ if (require.main === module) { program.name('gateway').description('Gateway contract operations.'); - const rotateSignersCmd = program + const rotateCmd = program .command('rotate') .description('Rotate signers of the gateway contract') .addOption(new Option('--signers ', 'JSON with the initial signer set')) @@ -241,16 +312,30 @@ 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 + const approveCmd = 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); + }); + + const approveMessagesCmd = program + .command('approveMessages ') + .description('Approve messages at the gateway contract from amplifier proof') + .action((multisigSessionId, options) => { + mainProcessor(approveMessages, [multisigSessionId], options); + }); + + const rotateSignersCmd = program + .command('rotateSigners ') + .description('Rotate signers at the gateway contract from amplifier proof') + .action((multisigSessionId, options) => { + mainProcessor(rotateSigners, [multisigSessionId], options); }); const callContractCmd = program @@ -263,7 +348,9 @@ if (require.main === module) { addBaseOptions(program); addBaseOptions(rotateSignersCmd); + addBaseOptions(rotateCmd); addBaseOptions(callContractCmd); + addBaseOptions(approveCmd); addBaseOptions(approveMessagesCmd); program.parse(); diff --git a/sui/gmp.js b/sui/gmp.js index 5c3ab2a5..bf3cbd54 100644 --- a/sui/gmp.js +++ b/sui/gmp.js @@ -20,8 +20,8 @@ async function sendCommand(keypair, client, contracts, args, options) { const [testConfig, gasServiceConfig] = contracts; const gasServicePackageId = gasServiceConfig.address; - const singletonObjectId = testConfig.objects.singleton; - const channelId = testConfig.objects.channelId; + const singletonObjectId = testConfig.objects.Singleton; + const channelId = testConfig.objects.ChannelId; const unitAmount = getUnitAmount(feeAmount); const walletAddress = keypair.toSuiAddress(); @@ -34,28 +34,16 @@ async function sendCommand(keypair, client, contracts, args, options) { target: `${testConfig.address}::test::send_call`, arguments: [ tx.object(singletonObjectId), + tx.object(gasServiceConfig.objects.GasService), tx.pure(bcs.string().serialize(destinationChain).toBytes()), tx.pure(bcs.string().serialize(destinationAddress).toBytes()), tx.pure(bcs.vector(bcs.u8()).serialize(arrayify(payload)).toBytes()), + tx.pure.address(refundAddress), // Refund address + coin, // Coin + tx.pure(bcs.vector(bcs.u8()).serialize(arrayify(params)).toBytes()), // Params ], }); - if (gasServiceConfig) { - tx.moveCall({ - target: `${gasServicePackageId}::gas_service::pay_gas`, - arguments: [ - tx.object(gasServiceConfig.objects.GasService), - coin, // Coin - tx.pure.address(channelId), // Channel address - tx.pure(bcs.string().serialize(destinationChain).toBytes()), // Destination chain - tx.pure(bcs.string().serialize(destinationAddress).toBytes()), // Destination address - tx.pure(bcs.vector(bcs.u8()).serialize(arrayify(payload)).toBytes()), // Payload - tx.pure.address(refundAddress), // Refund address - tx.pure(bcs.vector(bcs.u8()).serialize(arrayify(params)).toBytes()), // Params - ], - }); - } - const receipt = await broadcast(client, keypair, tx); printInfo('Call sent', receipt.digest); @@ -160,7 +148,7 @@ async function processCommand(command, chain, args, options) { await printWalletInfo(keypair, client, chain, options); - const contracts = [chain.contracts.test, chain.contracts.GasService, chain.contracts.axelar_gateway]; + const contracts = [chain.contracts.Test, chain.contracts.GasService, chain.contracts.AxelarGateway]; await command(keypair, client, contracts, args, options); } diff --git a/sui/types-utils.js b/sui/types-utils.js index afa87595..0137a899 100644 --- a/sui/types-utils.js +++ b/sui/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, }; From 29d5e3dbabd72c50bbdf9e2599cc1c9438437c72 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Wed, 21 Aug 2024 06:34:11 +0400 Subject: [PATCH 2/9] prettier --- common/utils.js | 16 ++++++++++++++-- cosmwasm/deploy-contract.js | 2 +- cosmwasm/utils.js | 2 +- evm/utils.js | 8 -------- sui/gateway.js | 4 +++- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/common/utils.js b/common/utils.js index f21451e8..2dda35ce 100644 --- a/common/utils.js +++ b/common/utils.js @@ -347,6 +347,13 @@ const isValidCosmosAddress = (str) => { } }; +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') { @@ -383,6 +390,10 @@ async function getDomainSeparator(config, chain, options) { if (domainSeparator !== expectedDomainSeparator) { throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); + } + + return domainSeparator; +} const getChainConfig = (config, chainName) => { const chainConfig = config.chains[chainName] || config[chainName]; @@ -392,10 +403,10 @@ const getChainConfig = (config, chainName) => { } return chainConfig; -} +}; const getMultisigProof = async (config, chain, multisigSessionId) => { - const query = { proof: { multisig_session_id: `${multisigSessionId}` }}; + const query = { proof: { multisig_session_id: `${multisigSessionId}` } }; const client = await CosmWasmClient.connect(config.axelar.rpc); const value = await client.queryContractSmart(config.axelar.contracts.MultisigProver[chain].address, query); return value; @@ -437,4 +448,5 @@ module.exports = { getDomainSeparator, getChainConfig, getMultisigProof, + getContractConfig, }; diff --git a/cosmwasm/deploy-contract.js b/cosmwasm/deploy-contract.js index d06bc807..85574b87 100644 --- a/cosmwasm/deploy-contract.js +++ b/cosmwasm/deploy-contract.js @@ -24,7 +24,7 @@ const upload = (client, wallet, chainName, config, options) => { contracts: { [contractName]: contractConfig }, }, } = config; - const chainConfig = (chainName === 'none') ? undefined : getChainConfig(config, chainName); + const chainConfig = chainName === 'none' ? undefined : getChainConfig(config, chainName); if (!fetchCodeId && (!reuseCodeId || isNil(contractConfig.codeId))) { printInfo('Uploading contract binary'); diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js index 802db512..52417740 100644 --- a/cosmwasm/utils.js +++ b/cosmwasm/utils.js @@ -66,7 +66,7 @@ const getChains = (config, { chainNames, instantiate2 }) => { throw new Error('Cannot pass --instantiate2 with more than one chain'); } - chains.every((chain) => (chain === 'none') || getChainConfig(config, chain)); + chains.every((chain) => chain === 'none' || getChainConfig(config, chain)); return chains; }; diff --git a/evm/utils.js b/evm/utils.js index 612e4c1d..60f08e6a 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -493,13 +493,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 +1055,6 @@ module.exports = { getDeployOptions, isValidChain, getAmplifierKeyAddresses, - getContractConfig, relayTransaction, getDeploymentTx, getWeightedSigners, diff --git a/sui/gateway.js b/sui/gateway.js index ed37a052..3700891f 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -3,7 +3,7 @@ const { Transaction } = require('@mysten/sui/transactions'); const { bcs } = require('@mysten/sui/bcs'); const { ethers } = require('hardhat'); const { - utils: { arrayify, keccak256, toUtf8Bytes, hexlify }, + utils: { arrayify, keccak256, toUtf8Bytes }, constants: { HashZero }, } = ethers; @@ -167,6 +167,7 @@ async function approveMessages(keypair, client, config, chain, args, options) { if (!status.completed) { throw new Error('Multisig session not completed'); } + const executeData = executeDataStruct.parse(new Uint8Array(arrayify('0x' + status.completed.execute_data))); const tx = new Transaction(); @@ -242,6 +243,7 @@ async function rotateSigners(keypair, client, config, chain, args, options) { if (!status.completed) { throw new Error('Multisig session not completed'); } + const executeData = executeDataStruct.parse(new Uint8Array(arrayify('0x' + status.completed.execute_data))); const tx = new Transaction(); From 4e0f27d320c6d0560a87a4f1ab540591c22bf8ae Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Wed, 21 Aug 2024 13:06:31 +0400 Subject: [PATCH 3/9] cli env workaround --- common/cli-utils.js | 2 +- common/utils.js | 10 +++++----- cosmwasm/deploy-contract.js | 2 +- cosmwasm/submit-proposal.js | 2 +- sui/utils/cli-utils.js | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/cli-utils.js b/common/cli-utils.js index 88fbc4a9..26e92d6a 100644 --- a/common/cli-utils.js +++ b/common/cli-utils.js @@ -7,7 +7,7 @@ const { Option } = require('commander'); const addBaseOptions = (program, options = {}) => { program.addOption( new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'devnet-amplifier', 'devnet-verifiers', 'stagenet', 'testnet', 'mainnet']) + .choices(['local', 'devnet', 'devnet-amplifier', 'devnet-sui', 'devnet-verifiers', 'stagenet', 'testnet', 'mainnet']) .default('testnet') .makeOptionMandatory(true) .env('ENV'), diff --git a/common/utils.js b/common/utils.js index 2dda35ce..e517c725 100644 --- a/common/utils.js +++ b/common/utils.js @@ -385,14 +385,14 @@ async function getDomainSeparator(config, chain, options) { } printInfo(`Retrieving domain separator for ${chain.name} from Axelar network`); - const domainSeparator = hexlify((await getContractConfig(config, chain.axelarId)).domain_separator); + // 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})`); - } + // if (domainSeparator !== expectedDomainSeparator) { + // throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); + // } - return domainSeparator; + return expectedDomainSeparator; } const getChainConfig = (config, chainName) => { diff --git a/cosmwasm/deploy-contract.js b/cosmwasm/deploy-contract.js index 85574b87..9d4d97a8 100644 --- a/cosmwasm/deploy-contract.js +++ b/cosmwasm/deploy-contract.js @@ -114,7 +114,7 @@ const programHandler = () => { program.addOption( new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) + .choices(['local', 'devnet', 'stagenet', 'testnet', 'devnet-sui', 'mainnet']) .default('testnet') .makeOptionMandatory(true) .env('ENV'), diff --git a/cosmwasm/submit-proposal.js b/cosmwasm/submit-proposal.js index da9541a1..6da91d4d 100644 --- a/cosmwasm/submit-proposal.js +++ b/cosmwasm/submit-proposal.js @@ -251,7 +251,7 @@ const programHandler = () => { // TODO: combine deploy-contract and submit-proposal options to remove duplicates program.addOption( new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'devnet-amplifier', 'devnet-verifiers', 'stagenet', 'testnet', 'mainnet']) + .choices(['local', 'devnet', 'devnet-amplifier', 'devnet-sui', 'devnet-verifiers', 'stagenet', 'testnet', 'mainnet']) .default('devnet-amplifier') .makeOptionMandatory(true) .env('ENV'), diff --git a/sui/utils/cli-utils.js b/sui/utils/cli-utils.js index 8cf95622..07ff287c 100644 --- a/sui/utils/cli-utils.js +++ b/sui/utils/cli-utils.js @@ -8,7 +8,7 @@ const { getUnitAmount } = require('./amount-utils'); const addBaseOptions = (program, options = {}) => { program.addOption( new Option('-e, --env ', 'environment') - .choices(['local', 'devnet', 'devnet-amplifier', 'devnet-verifiers', 'stagenet', 'testnet', 'mainnet']) + .choices(['local', 'devnet', 'devnet-amplifier', 'devnet-verifiers', 'devnet-sui', 'stagenet', 'testnet', 'mainnet']) .default('testnet') .makeOptionMandatory(true) .env('ENV'), From 094782a5314d0aa9aec12003fe97bb30e4f4b53e Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 22 Aug 2024 04:47:58 +0400 Subject: [PATCH 4/9] comment --- common/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/common/utils.js b/common/utils.js index e517c725..4bae3aa6 100644 --- a/common/utils.js +++ b/common/utils.js @@ -388,6 +388,7 @@ async function getDomainSeparator(config, chain, options) { // const domainSeparator = hexlify((await getContractConfig(config, chain.axelarId)).domain_separator); const expectedDomainSeparator = calculateDomainSeparator(chain.axelarId, routerAddress, chainId); + // TODO: add domain separator prediction support to display before amplifier contracts are ready // if (domainSeparator !== expectedDomainSeparator) { // throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); // } From a32d18495464015621a4a999c86913f06adb8baf Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 22 Aug 2024 04:50:14 +0400 Subject: [PATCH 5/9] remove import --- common/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/utils.js b/common/utils.js index 4bae3aa6..1a135693 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 }, } = ethers; const { normalizeBech32 } = require('@cosmjs/encoding'); From 6e3a086adb6c01ee7dd74d6d707c660147544bb7 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 27 Aug 2024 04:45:17 +0400 Subject: [PATCH 6/9] address comments --- common/utils.js | 8 ++++- cosmwasm/utils.js | 17 +++++----- evm/utils.js | 5 +-- sui/gateway.js | 80 +++++++++++++++++------------------------------ 4 files changed, 46 insertions(+), 64 deletions(-) diff --git a/common/utils.js b/common/utils.js index 1a135693..3aacb270 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 }, + utils: { keccak256, defaultAbiCoder }, } = ethers; const { normalizeBech32 } = require('@cosmjs/encoding'); @@ -347,6 +347,10 @@ 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); @@ -450,4 +454,6 @@ module.exports = { getChainConfig, getMultisigProof, getContractConfig, + getSaltFromKey, + calculateDomainSeparator, }; diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js index 52417740..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, getChainConfig } = 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; diff --git a/evm/utils.js b/evm/utils.js index 60f08e6a..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); diff --git a/sui/gateway.js b/sui/gateway.js index 3700891f..5d20dc95 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -16,11 +16,13 @@ const { executeDataStruct, proofStruct, addBaseOptions, + addOptionsToCommands, getSigners, getWallet, printWalletInfo, getRawPrivateKey, broadcast, + suiClockAddress, } = require('./utils'); const secp256k1 = require('secp256k1'); @@ -106,12 +108,7 @@ function getProof(keypair, commandType, data, contractConfig, options) { return encodedProof; } -async function callContract(keypair, client, config, chain, args, options) { - if (!chain.contracts.AxelarGateway) { - throw new Error('Axelar Gateway package not found.'); - } - - const contractConfig = chain.contracts.AxelarGateway; +async function callContract(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const [destinationChain, destinationAddress, payload] = args; @@ -150,12 +147,7 @@ async function callContract(keypair, client, config, chain, args, options) { printInfo('Contract called'); } -async function approveMessages(keypair, client, config, chain, args, options) { - if (!chain.contracts.AxelarGateway) { - throw new Error('Axelar Gateway package not found.'); - } - - const contractConfig = chain.contracts.AxelarGateway; +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); @@ -168,7 +160,7 @@ async function approveMessages(keypair, client, config, chain, args, options) { throw new Error('Multisig session not completed'); } - const executeData = executeDataStruct.parse(new Uint8Array(arrayify('0x' + status.completed.execute_data))); + const executeData = executeDataStruct.parse(arrayify('0x' + status.completed.execute_data)); const tx = new Transaction(); @@ -181,17 +173,12 @@ async function approveMessages(keypair, client, config, chain, args, options) { ], }); - await broadcast(client, keypair, tx); + const receipt = await broadcast(client, keypair, tx); - printInfo('Approved messages'); + printInfo('Approved messages', receipt.digest); } -async function approve(keypair, client, config, chain, args, options) { - if (!chain.contracts.AxelarGateway) { - throw new Error('Axelar Gateway package not found.'); - } - - const contractConfig = chain.contracts.AxelarGateway; +async function approve(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const [sourceChain, messageId, sourceAddress, destinationId, payloadHash] = args; @@ -221,17 +208,12 @@ async function approve(keypair, client, config, chain, args, options) { ], }); - 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.AxelarGateway) { - throw new Error('Axelar Gateway package not found.'); - } - - const contractConfig = chain.contracts.AxelarGateway; +async function rotateSigners(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const [multisigSessionId] = args; const { payload, status } = await getMultisigProof(config, chain.axelarId, multisigSessionId); @@ -244,7 +226,7 @@ async function rotateSigners(keypair, client, config, chain, args, options) { throw new Error('Multisig session not completed'); } - const executeData = executeDataStruct.parse(new Uint8Array(arrayify('0x' + status.completed.execute_data))); + const executeData = executeDataStruct.parse(arrayify('0x' + status.completed.execute_data)); const tx = new Transaction(); @@ -252,23 +234,18 @@ async function rotateSigners(keypair, client, config, chain, args, options) { target: `${packageId}::gateway::rotate_signers`, arguments: [ tx.object(contractConfig.objects.Gateway), - tx.object('0x6'), + 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()), ], }); - await broadcast(client, keypair, tx); + const receipt = await broadcast(client, keypair, tx); - printInfo('Signers rotated succesfully'); + printInfo('Signers rotated', receipt.digest); } -async function rotate(keypair, client, config, chain, args, options) { - if (!chain.contracts.AxelarGateway) { - throw new Error('Axelar Gateway package not found.'); - } - - const contractConfig = chain.contracts.AxelarGateway; +async function rotate(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; const signers = await getSigners(keypair, config, chain.axelarId, options); @@ -288,7 +265,7 @@ async function rotate(keypair, client, config, chain, args, options) { target: `${packageId}::gateway::rotate_signers`, arguments: [ tx.object(contractConfig.objects.Gateway), - tx.object('0x6'), + tx.object(suiClockAddress), tx.pure(bcs.vector(bcs.u8()).serialize(encodedSigners).toBytes()), tx.pure(bcs.vector(bcs.u8()).serialize(encodedProof).toBytes()), ], @@ -305,7 +282,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); } @@ -315,7 +296,7 @@ if (require.main === module) { program.name('gateway').description('Gateway contract operations.'); - const rotateCmd = program + program .command('rotate') .description('Rotate signers of the gateway contract') .addOption(new Option('--signers ', 'JSON with the initial signer set')) @@ -326,7 +307,7 @@ if (require.main === module) { mainProcessor(rotate, [], options); }); - const approveCmd = program + program .command('approve ') .description('Approve messages at the gateway contract') .addOption(new Option('--proof ', 'JSON of the proof')) @@ -335,21 +316,21 @@ if (require.main === module) { mainProcessor(approve, [sourceChain, messageId, sourceAddress, destinationId, payloadHash], options); }); - const approveMessagesCmd = program + program .command('approveMessages ') .description('Approve messages at the gateway contract from amplifier proof') .action((multisigSessionId, options) => { mainProcessor(approveMessages, [multisigSessionId], options); }); - const rotateSignersCmd = program + program .command('rotateSigners ') .description('Rotate signers at the gateway contract from amplifier proof') .action((multisigSessionId, options) => { mainProcessor(rotateSigners, [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')) @@ -357,12 +338,7 @@ if (require.main === module) { mainProcessor(callContract, [destinationChain, destinationAddress, payload], options); }); - addBaseOptions(program); - addBaseOptions(rotateSignersCmd); - addBaseOptions(rotateCmd); - addBaseOptions(callContractCmd); - addBaseOptions(approveCmd); - addBaseOptions(approveMessagesCmd); + addOptionsToCommands(program, addBaseOptions); program.parse(); } From 6faca9d14857ca97bc1a1b25db1ec7bdcd7c8fd8 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 27 Aug 2024 04:55:25 +0400 Subject: [PATCH 7/9] allow offline computation for domain separator --- common/utils.js | 16 ++++++++++------ sui/deploy-contract.js | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/utils.js b/common/utils.js index 3aacb270..4d51ba8e 100644 --- a/common/utils.js +++ b/common/utils.js @@ -388,14 +388,18 @@ async function getDomainSeparator(config, chain, options) { throw new Error(`missing or invalid chain ID`); } - 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 (options.domainSeparator === 'offline') { + printInfo('Computed domain separator offline') + return expectedDomainSeparator; + } - // TODO: add domain separator prediction support to display before amplifier contracts are ready - // if (domainSeparator !== expectedDomainSeparator) { - // throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); - // } + printInfo(`Retrieving domain separator for ${chain.name} from Axelar network`); + const domainSeparator = hexlify((await getContractConfig(config, chain.axelarId)).domain_separator); + + if (domainSeparator !== expectedDomainSeparator) { + throw new Error(`unexpected domain separator (want ${expectedDomainSeparator}, got ${domainSeparator})`); + } return expectedDomainSeparator; } diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index 09a91fb7..ff9cba48 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -327,7 +327,7 @@ 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'), ]; From 51224cb26f29dd81db85a0b3b5d4c75bbfd72bf4 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 27 Aug 2024 05:13:28 +0400 Subject: [PATCH 8/9] simplify proof submission --- package-lock.json | 18 +++++++-------- sui/README.md | 5 ++++ sui/gateway.js | 59 ++++++++++++++++++++++++++++++----------------- 3 files changed, 52 insertions(+), 30 deletions(-) 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/gateway.js b/sui/gateway.js index 5d20dc95..9646e746 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -202,7 +202,7 @@ async function approve(keypair, client, config, chain, contractConfig, args, opt 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()), ], @@ -213,32 +213,49 @@ async function approve(keypair, client, config, chain, contractConfig, args, opt printInfo('Approved messages', receipt.digest); } -async function rotateSigners(keypair, client, config, chain, contractConfig, args, options) { +async function submitProof(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; - const [multisigSessionId] = args; + const [ multisigSessionId ] = args; const { payload, status } = await getMultisigProof(config, chain.axelarId, multisigSessionId); - if (!payload.verifier_set) { - throw new Error('No signers to rotate'); - } - if (!status.completed) { throw new Error('Multisig session not completed'); } 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(); - 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()), - ], - }); + 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); @@ -271,9 +288,9 @@ async function rotate(keypair, client, config, chain, contractConfig, args, opti ], }); - 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) { @@ -324,10 +341,10 @@ if (require.main === module) { }); program - .command('rotateSigners ') - .description('Rotate signers at the gateway contract from amplifier proof') + .command('submitProof ') + .description('Submit proof for the provided amplifier multisig session id') .action((multisigSessionId, options) => { - mainProcessor(rotateSigners, [multisigSessionId], options); + mainProcessor(submitProof, [multisigSessionId], options); }); program From 0a82199e890690a4be3596054f70d5d71b72f412 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 27 Aug 2024 05:15:31 +0400 Subject: [PATCH 9/9] prettier --- common/utils.js | 5 +++-- sui/deploy-contract.js | 5 ++++- sui/gateway.js | 22 +++++++++++----------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/common/utils.js b/common/utils.js index 4d51ba8e..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, defaultAbiCoder }, + utils: { keccak256, hexlify, defaultAbiCoder }, } = ethers; const { normalizeBech32 } = require('@cosmjs/encoding'); @@ -389,8 +389,9 @@ async function getDomainSeparator(config, chain, options) { } const expectedDomainSeparator = calculateDomainSeparator(chain.axelarId, routerAddress, chainId); + if (options.domainSeparator === 'offline') { - printInfo('Computed domain separator offline') + printInfo('Computed domain separator offline'); return expectedDomainSeparator; } diff --git a/sui/deploy-contract.js b/sui/deploy-contract.js index ff9cba48..fd8b454e 100644 --- a/sui/deploy-contract.js +++ b/sui/deploy-contract.js @@ -327,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 (pass in the keccak256 hash value OR "offline" meaning that its computed locally)'), + 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 9646e746..7425b786 100644 --- a/sui/gateway.js +++ b/sui/gateway.js @@ -215,7 +215,7 @@ async function approve(keypair, client, config, chain, contractConfig, args, opt async function submitProof(keypair, client, config, chain, contractConfig, args, options) { const packageId = contractConfig.address; - const [ multisigSessionId ] = args; + const [multisigSessionId] = args; const { payload, status } = await getMultisigProof(config, chain.axelarId, multisigSessionId); if (!status.completed) { @@ -243,16 +243,16 @@ async function submitProof(keypair, client, config, chain, contractConfig, args, ], }); } 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()), - ], - }); + 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}`); }