diff --git a/cosmwasm/migrate/migrate.ts b/cosmwasm/migrate/migrate.ts
index 69f83d38a..83b1c7d7f 100644
--- a/cosmwasm/migrate/migrate.ts
+++ b/cosmwasm/migrate/migrate.ts
@@ -53,7 +53,7 @@ async function checkMigration(
const programHandler = () => {
const program = new Command();
- program.name('migrate').version('1.0.0').description('Automation for migrating Amplifier contracts');
+ program.name('migrate').version('1.1.0').description('Automation for migrating Amplifier contracts');
addAmplifierOptions(
program
@@ -86,7 +86,7 @@ const programHandler = () => {
.addOption(new Option('--address
', 'address of contract to check'))
.addOption(new Option('--coordinator ', 'coordinator address'))
.addOption(new Option('--multisig ', 'multisig address'))
- .description('check migration succeeded')
+ .description('Check migration succeeded')
.action((options: MigrationCheckOptions) => {
mainQueryProcessor(checkMigration, options, []);
}),
diff --git a/cosmwasm/migrate/multisig.ts b/cosmwasm/migrate/multisig.ts
index 0dc541562..704719af4 100644
--- a/cosmwasm/migrate/multisig.ts
+++ b/cosmwasm/migrate/multisig.ts
@@ -95,6 +95,6 @@ export async function migrate(
case '2.1.0':
return multisigToVersion2_3_1(client, options, config, senderAddress, multisigAddress, fee);
default:
- printError(`no migration script found for coordinator ${version}`);
+ printError(`no migration script found for multisig ${version}`);
}
}
diff --git a/cosmwasm/migrate/types.ts b/cosmwasm/migrate/types.ts
index 09921fe80..13d578c10 100644
--- a/cosmwasm/migrate/types.ts
+++ b/cosmwasm/migrate/types.ts
@@ -7,6 +7,8 @@ export interface MigrationOptions extends Options {
dry?: boolean;
direct?: boolean;
ignoreChains?: string;
+ title?: string;
+ description?: string;
codeId?: number;
}
diff --git a/cosmwasm/submit-proposal.js b/cosmwasm/submit-proposal.js
index caf9c7016..cc614e036 100644
--- a/cosmwasm/submit-proposal.js
+++ b/cosmwasm/submit-proposal.js
@@ -3,8 +3,8 @@
require('../common/cli-utils');
const { createHash } = require('crypto');
-
const { instantiate2Address } = require('@cosmjs/cosmwasm-stargate');
+const { AccessType } = require('cosmjs-types/cosmwasm/wasm/v1/types');
const {
CONTRACTS,
@@ -12,6 +12,7 @@ const {
getSalt,
getAmplifierContractConfig,
getCodeId,
+ getCodeDetails,
getChainTruncationParams,
decodeProposalAttributes,
encodeStoreCodeProposal,
@@ -21,6 +22,7 @@ const {
encodeExecuteContractProposal,
encodeParameterChangeProposal,
encodeMigrateContractProposal,
+ encodeUpdateInstantiateConfigProposal,
submitProposal,
validateItsChainChange,
} = require('./utils');
@@ -32,6 +34,7 @@ const {
InstantiateContract2Proposal,
ExecuteContractProposal,
MigrateContractProposal,
+ UpdateInstantiateConfigProposal,
} = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const { ParameterChangeProposal } = require('cosmjs-types/cosmos/params/v1beta1/params');
@@ -337,6 +340,67 @@ const instantiateChainContracts = async (client, config, options, _args, fee) =>
};
};
+async function instantiatePermissions(client, options, config, senderAddress, coordinatorAddress, permittedAddresses, codeId, fee) {
+ const addresses = [...permittedAddresses, coordinatorAddress];
+
+ const updateMsg = JSON.stringify([
+ {
+ codeId: codeId,
+ instantiatePermission: {
+ permission: AccessType.ACCESS_TYPE_ANY_OF_ADDRESSES,
+ addresses: addresses,
+ },
+ },
+ ]);
+
+ const updateOptions = {
+ msg: updateMsg,
+ title: options.title,
+ description: options.description,
+ runAs: senderAddress,
+ deposit: options.deposit,
+ };
+
+ const proposal = encodeUpdateInstantiateConfigProposal(updateOptions);
+
+ if (!confirmProposalSubmission(options, proposal, UpdateInstantiateConfigProposal)) {
+ return;
+ }
+
+ try {
+ await submitProposal(client, config, updateOptions, proposal, fee);
+ printInfo('Instantiate params proposal successfully submitted');
+ } catch (e) {
+ printError(`Error: ${e}`);
+ }
+}
+
+async function coordinatorInstantiatePermissions(client, config, options, _args, fee) {
+ const senderAddress = client.accounts[0].address;
+ const contractAddress = config.axelar.contracts['Coordinator']?.address;
+
+ if (!contractAddress) {
+ throw new Error('cannot find coordinator address in configuration');
+ }
+
+ const codeId = await getCodeId(client, config, { ...options, contractName: options.contractName });
+ const codeDetails = await getCodeDetails(config, codeId);
+ const permissions = codeDetails.instantiatePermission;
+
+ if (
+ permissions?.permission === AccessType.ACCESS_TYPE_EVERYBODY ||
+ (permissions?.address === contractAddress && permissions?.permission === AccessType.ACCESS_TYPE_ONLY_ADDRESS)
+ ) {
+ throw new Error(`coordinator is already allowed to instantiate code id ${codeId}`);
+ }
+
+ const permittedAddresses = permissions.addresses ?? [];
+ if (permittedAddresses.includes(contractAddress) && permissions?.permission === AccessType.ACCESS_TYPE_ANY_OF_ADDRESSES) {
+ throw new Error(`coordinator is already allowed to instantiate code id ${codeId}`);
+ }
+
+ return instantiatePermissions(client, options, config, senderAddress, contractAddress, permittedAddresses, codeId, fee);
+}
const registerDeployment = async (client, config, options, _args, fee) => {
const { chainName } = options;
const coordinator = new CoordinatorManager(config);
@@ -472,6 +536,23 @@ const programHandler = () => {
instantiateOptions: true,
});
+ addAmplifierOptions(
+ program
+ .command('coordinator-instantiate-permissions')
+ .addOption(
+ new Option('--contractName ', 'coordinator will have instantiate permissions for this contract')
+ .makeOptionMandatory(true)
+ .choices(['Gateway', 'VotingVerifier', 'MultisigProver']),
+ )
+ .description('Give coordinator instantiate permissions for the given contract')
+ .action((options) => {
+ mainProcessor(coordinatorInstantiatePermissions, options, []);
+ }),
+ {
+ proposalOptions: true,
+ },
+ );
+
const registerDeploymentCmd = program
.command('register-deployment')
.description('Submit an execute wasm contract proposal to register a deployment')
diff --git a/cosmwasm/utils.js b/cosmwasm/utils.js
index e800c3a98..5aea1647a 100644
--- a/cosmwasm/utils.js
+++ b/cosmwasm/utils.js
@@ -10,9 +10,12 @@ const {
InstantiateContract2Proposal,
ExecuteContractProposal,
MigrateContractProposal,
+ UpdateInstantiateConfigProposal,
} = require('cosmjs-types/cosmwasm/wasm/v1/proposal');
const { ParameterChangeProposal } = require('cosmjs-types/cosmos/params/v1beta1/params');
+const { QueryCodeRequest, QueryCodeResponse } = require('cosmjs-types/cosmwasm/wasm/v1/query');
const { AccessType } = require('cosmjs-types/cosmwasm/wasm/v1/types');
+const { Tendermint34Client } = require('@cosmjs/tendermint-rpc');
const {
printInfo,
isString,
@@ -932,6 +935,15 @@ const getParameterChangeParams = ({ title, description, changes }) => ({
})),
});
+const getUpdateInstantiateParams = (options) => {
+ const { msg } = options;
+
+ return {
+ ...getSubmitProposalParams(options),
+ accessConfigUpdates: JSON.parse(msg),
+ };
+};
+
const getMigrateContractParams = (config, options) => {
const { msg, chainName } = options;
@@ -1012,6 +1024,15 @@ const encodeParameterChangeProposal = (options) => {
};
};
+const encodeUpdateInstantiateConfigProposal = (options) => {
+ const proposal = UpdateInstantiateConfigProposal.fromPartial(getUpdateInstantiateParams(options));
+
+ return {
+ typeUrl: '/cosmwasm.wasm.v1.UpdateInstantiateConfigProposal',
+ value: Uint8Array.from(UpdateInstantiateConfigProposal.encode(proposal).finish()),
+ };
+};
+
const encodeMigrateContractProposal = (config, options) => {
const proposal = MigrateContractProposal.fromPartial(getMigrateContractParams(config, options));
@@ -1133,6 +1154,31 @@ const validateItsChainChange = async (client, config, chainName, proposedConfig)
}
};
+const getCodeDetails = async (config, codeId) => {
+ const tendermintClient = await Tendermint34Client.connect(config?.axelar?.rpc);
+ let codeInfo;
+
+ try {
+ const data = QueryCodeRequest.encode({
+ codeId: BigInt(codeId),
+ }).finish();
+
+ const { value } = await tendermintClient.abciQuery({
+ path: '/cosmwasm.wasm.v1.Query/Code',
+ data: data,
+ });
+
+ codeInfo = QueryCodeResponse.decode(value)?.codeInfo;
+ if (!codeInfo) {
+ throw new Error(`Info not found for code id ${codeId}`);
+ }
+ } finally {
+ tendermintClient.disconnect();
+ }
+
+ return codeInfo;
+};
+
const CONTRACTS = {
Coordinator: {
scope: CONTRACT_SCOPE_GLOBAL,
@@ -1209,6 +1255,7 @@ module.exports = {
calculateDomainSeparator,
getAmplifierContractConfig,
getCodeId,
+ getCodeDetails,
executeTransaction,
uploadContract,
instantiateContract,
@@ -1223,6 +1270,7 @@ module.exports = {
encodeInstantiate2Proposal,
encodeExecuteContractProposal,
encodeParameterChangeProposal,
+ encodeUpdateInstantiateConfigProposal,
encodeMigrateContractProposal,
submitProposal,
isValidCosmosAddress,
diff --git a/releases/cosmwasm/2025-09-Coordinator-v2.1.1.md b/releases/cosmwasm/2025-09-Coordinator-v2.1.1.md
index e385ad61b..461aa0956 100644
--- a/releases/cosmwasm/2025-09-Coordinator-v2.1.1.md
+++ b/releases/cosmwasm/2025-09-Coordinator-v2.1.1.md
@@ -31,6 +31,7 @@ The coordinator can now deploy a gateway, voting verifier, and multisig prover c
1. The coordinator now stores both the router and multisig contract addresses in its state. This information will be given to the coordinator after it is instantiated using the *RegisterProtocol* message. The service registry address will also be registered using *RegisterProtocol*, where it was previously in the coordinator's instantiate message.
1. Previously, registering a chain with the coordinator involved specifying only the multisig prover's address. Now, registration must also include the corresponding gateway and voting verifier addresses.
+1. The coordinator's migration script will default to using the provided prover/chain pairs instead of the prover/chain pairs registered in its state.
### Multisig v2.3.1
@@ -47,6 +48,11 @@ The coordinator can now deploy a gateway, voting verifier, and multisig prover c
- This rollout upgrades the amplifier coordinator contract from `v1.1.0` to `v2.1.1`, the multisig contract from `v2.1.0` to `v2.3.1`, and the router from `v1.2.0` to `v1.3.0`.
- State migration is required for all three contracts.
+ | Component | Versions Tested
+ | ---------------- | ---------------- |
+ | Cosmos SDK | 0.47.x |
+ | Wasm | 0.34.x |
+
1. Retrieve coordinator address from the appropriate config file for the environment. (ENV: devnet, testnet, stagenet or mainnet)
```bash
@@ -105,7 +111,7 @@ The coordinator can now deploy a gateway, voting verifier, and multisig prover c
--fetchCodeId
```
- Provide coordinator address to the multisig.
+ Provide coordinator address to the multisig, and migrate using the contract deployment scripts.
```bash
ts-node cosmwasm/migrate/migrate.ts migrate \
@@ -125,6 +131,19 @@ The coordinator can now deploy a gateway, voting verifier, and multisig prover c
**Warning:** Using the `--ignoreChains [chains to ignore...]` flag might introduce protocol breaking behaviour, so it should be used only in a test environment. Coordinator v2 requires the gateways, verifiers and provers for each chain to be unique. You may ignore chains in the event that there are multiple chains that use the same verifier. This is possible because the protocol allows different gateways to be instantiated with the same verifier.
+1. (Optional) Give the Coordinator Permission to Instantiate Gateway, Verifier and Prover
+
+ The coordinator needs to have permission from the wasm module to instantiate gateways, verifiers and provers. You may retrieve these code ids by checking `.axelar.contracts.Gateway.$CHAIN.codeId`, `.axelar.contracts.VotingVerifier.$CHAIN.codeId` and `.axelar.contracts.MultisigProver.$CHAIN.codeId` respectively. To get the current instantiate permissions for a particular code id, run the following command:
+
+ ```bash
+ axelard q wasm code-info --node
+ ```
+
+ We have provided a script that submits a proposal to append the coordinator's address to the list of allowed addresses for a given code id. A proposal will not be created if the coordinator is already allowed to instantiate that contract. To execute that script, run:
+
+ ```bash
+ ts-node cosmwasm/submit-proposal.js coordinator-instantiate-permissions --contractName --deposit 100000000 -e $ENV -t $TITLE -d $DESCRIPTION -m $MNEMONIC
+ ```
## Checklist
1. Verify router contract version
@@ -202,4 +221,9 @@ The coordinator can now deploy a gateway, voting verifier, and multisig prover c
ts-node cosmwasm/migrate/migrate.ts check -e $ENV -c Coordinator
```
- You may manually specify the address of the coordinator and multisig by using the `--coordinator` and `--multisig` flags respectively.
+ You may optionally specify the address of the coordinator and multisig by using the `--coordinator` and `--multisig` flags respectively.
+
+ Expected Output
+ ```bash
+ ✅ Migration succeeded!
+ ```
diff --git a/solana/template.env b/solana/template.env
index b1584ee53..f1260783e 100644
--- a/solana/template.env
+++ b/solana/template.env
@@ -31,7 +31,7 @@ WALLET="axelar1zlr7e5qf3sz7yf890rkh9tcnu87234k6k7ytd9"
# Solana
CLUSTER="devnet";
-COMMIT_HASH="a26968488efd41c979d6aa3d56ce85bc1c1556d4"; # https://github.com/axelarnetwork/axelar-amplifier-solana/commit/a26968488efd41c979d6aa3d56ce85bc1c1556d4
+COMMIT_HASH="a26968488efd41c979d6aa3d56ce85bc1c1556d4"; # https://github.com/axelarnetwork/axelar-amplifier-solana/commit/a26968488efd41c979d6aa3d56ce85bc1c1556d4 # skip-check
PRIVATE_KEY="/Users/nbayindirli/.config/solana/upgrade-authority-devnet-amplifier.json";
UPGRADE_AUTHORITY_KEYPAIR_PATH=$PRIVATE_KEY;