From e2b346688c3df43b147b2f169ce4e042d7920480 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Tue, 24 Dec 2024 01:19:08 +0100 Subject: [PATCH 1/5] feat: add stellar upgrader contract to the deploy-contract.js script --- stellar/deploy-contract.js | 59 ++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/stellar/deploy-contract.js b/stellar/deploy-contract.js index 23b23f59..86d30b83 100644 --- a/stellar/deploy-contract.js +++ b/stellar/deploy-contract.js @@ -1,9 +1,9 @@ 'use strict'; -const { Address, nativeToScVal, scValToNative, Operation, StrKey } = require('@stellar/stellar-sdk'); +const { Address, nativeToScVal, scValToNative, Operation, StrKey, xdr, authorizeInvocation, rpc } = require('@stellar/stellar-sdk'); const { Command, Option } = require('commander'); const { loadConfig, printInfo, saveConfig } = require('../evm/utils'); -const { getWallet, broadcast, serializeValue, addBaseOptions } = require('./utils'); +const { getWallet, broadcast, serializeValue, addBaseOptions, getNetworkPassphrase } = require('./utils'); const { getDomainSeparator, getChainConfig } = require('../common'); const { prompt, validateParameters } = require('../common/utils'); const { weightedSignersToScVal } = require('./type-utils'); @@ -74,6 +74,10 @@ async function getInitializeArgs(config, chain, contractName, wallet, options) { return { owner, gasCollector }; } + case 'upgrader': { + return {}; + } + case 'example': { const gatewayAddress = nativeToScVal(Address.fromString(chain?.contracts?.axelar_gateway?.address), { type: 'address' }); const gasServiceAddress = nativeToScVal(Address.fromString(chain?.contracts?.axelar_gas_service?.address), { type: 'address' }); @@ -139,7 +143,7 @@ async function uploadWasm(filePath, wallet, chain) { async function upgrade(options, _, chain, contractName) { const { wasmPath, yes } = options; - const contractAddress = chain.contracts[contractName].address; + const uncheckedContractAddress = chain.contracts[contractName].address; const wallet = await getWallet(chain, options); if (prompt(`Proceed with upgrade on ${chain.name}?`, yes)) { @@ -147,22 +151,63 @@ async function upgrade(options, _, chain, contractName) { } validateParameters({ - isNonEmptyString: { contractAddress }, + isNonEmptyString: { uncheckedContractAddress }, }); + const contractAddress = Address.fromString(uncheckedContractAddress); + const newWasmHash = await uploadWasm(wasmPath, wallet, chain); printInfo('New Wasm hash', serializeValue(newWasmHash)); const operation = Operation.invokeContractFunction({ - contract: contractAddress, + contract: chain.contracts.upgrader.address, function: 'upgrade', - args: [nativeToScVal(newWasmHash)], + args: [ + nativeToScVal(contractAddress), + nativeToScVal(options.newVersion), + nativeToScVal(newWasmHash), + nativeToScVal([options.migrationData]), + ], + auth: await createUpgradeAuths(contractAddress, newWasmHash, options.migrationData, chain, wallet), }); + await broadcast(operation, wallet, chain, 'Upgraded contract', options); chain.contracts[contractName].wasmHash = serializeValue(newWasmHash); printInfo('Contract upgraded successfully!', contractAddress); } +async function createUpgradeAuths(contractAddress, newWasmHash, migrationData, chain, wallet) { + // 20 seems a reasonable number of ledgers to allow for the upgrade to take effect + const validUntil = await new rpc.Server(chain.rpc).getLatestLedger().then((info) => info.sequence + 20); + + return Promise.all( + [ + createAuthorizedFunc(contractAddress, 'upgrade', [nativeToScVal(newWasmHash)]), + createAuthorizedFunc(contractAddress, 'migrate', [nativeToScVal(migrationData)]), + ].map((auth) => + authorizeInvocation( + wallet, + validUntil, + new xdr.SorobanAuthorizedInvocation({ + function: auth, + subInvocations: [], + }), + wallet.publicKey(), + getNetworkPassphrase(chain.networkType), + ), + ), + ); +} + +const createAuthorizedFunc = (contractAddress, functionName, args) => + xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn( + new xdr.InvokeContractArgs({ + contractAddress: contractAddress.toScAddress(), + functionName, + args, + }), + ); + async function mainProcessor(options, processor, contractName) { const config = loadConfig(options.env); const chain = getChainConfig(config, options.chainName); @@ -200,6 +245,8 @@ function main() { .description('Upgrade a Stellar contract') .argument('', 'contract name to deploy') .addOption(new Option('--wasm-path ', 'path to the WASM file')) + .addOption(new Option('--new-version ', 'new version of the contract')) + .addOption(new Option('--migration-data ', 'migration data')) .action((contractName, options) => { mainProcessor(options, upgrade, contractName); }); From eaa929c557819e05317ea0c50fa56af865974b1e Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 2 Jan 2025 17:13:53 +0100 Subject: [PATCH 2/5] adressed comments --- .gitignore | 3 +++ stellar/README.md | 25 ++++++++++++++++--------- stellar/deploy-contract.js | 22 +++++++--------------- stellar/utils.js | 11 ++++++++++- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 1ede6657..1fafc5ca 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,6 @@ sui/move # VSCode .vscode .DS_Store + +# Jetbrains +.idea \ No newline at end of file diff --git a/stellar/README.md b/stellar/README.md index c03f2f19..839d41e6 100644 --- a/stellar/README.md +++ b/stellar/README.md @@ -90,7 +90,6 @@ node stellar/deploy-contract.js deploy axelar_gas_service --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/interchain_token.optimized.wasm -```bash node stellar/deploy-contract.js deploy interchain_token_service --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/interchain_token_service.optimized.wasm ``` @@ -100,6 +99,22 @@ node stellar/deploy-contract.js deploy interchain_token_service --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/example.optimized.wasm ``` +### Contract upgrades +To facilitate contract upgrades, the `upgrader` contract needs to be deployed first. + +```bash +node stellar/deploy-contract.js deploy upgrader --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/upgrader.optimized.wasm +``` + +After the `upgrader` is deployed, any other instantiated contract can be upgraded by calling the `upgrade` function + +```bash +node stellar/deploy-contract.js upgrade --chain-name --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/.optimized.wasm --new-version --migration-data +``` + +where `` is the name of the contract to be upgraded and `--wasm-path` points to the upgraded bytecode. As a sanity check, `` must match the version number defined by the provided bytecode, so upgrading to the wrong version can be prevented. `` is the json encoded data that will be passed to the contract's `migrate` function. + + ## Generate bindings Generate TypeScript bindings for the contract @@ -166,14 +181,6 @@ node stellar/gateway.js rotate --new-nonce test --signers wallet node stellar/gateway.js rotate --new-nonce test2 --current-nonce test --signers wallet ``` -#### Upgrade Gateway - -To upgrade the gateway, run the following command: - -```bash -node stellar/deploy-contract.js upgrade axelar_gateway --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/axelar_gateway.optimized.wasm -``` - ### Interchain Token Service _Note_: Stellar ITS runs only in Hub mode. P2P connections are not supported. Therefore, rather than setting trusted ITS addresses, we set trusted chains (chains which are also registered with ITS Hub). The ITS Hub chain (axelar) itself is not a valid source/destination for direct ITS messages and so shouldn't be set as a trusted chain. All ITS messages must be sent to and received from the ITS Hub. diff --git a/stellar/deploy-contract.js b/stellar/deploy-contract.js index 86d30b83..21917bc5 100644 --- a/stellar/deploy-contract.js +++ b/stellar/deploy-contract.js @@ -3,7 +3,7 @@ const { Address, nativeToScVal, scValToNative, Operation, StrKey, xdr, authorizeInvocation, rpc } = require('@stellar/stellar-sdk'); const { Command, Option } = require('commander'); const { loadConfig, printInfo, saveConfig } = require('../evm/utils'); -const { getWallet, broadcast, serializeValue, addBaseOptions, getNetworkPassphrase } = require('./utils'); +const { getWallet, broadcast, serializeValue, addBaseOptions, getNetworkPassphrase, createAuthorizedFunc } = require('./utils'); const { getDomainSeparator, getChainConfig } = require('../common'); const { prompt, validateParameters } = require('../common/utils'); const { weightedSignersToScVal } = require('./type-utils'); @@ -151,7 +151,7 @@ async function upgrade(options, _, chain, contractName) { } validateParameters({ - isNonEmptyString: { uncheckedContractAddress }, + isNonEmptyString: { uncheckedContractAddress, chain.contracts.upgrader.address }, }); const contractAddress = Address.fromString(uncheckedContractAddress); @@ -163,11 +163,11 @@ async function upgrade(options, _, chain, contractName) { contract: chain.contracts.upgrader.address, function: 'upgrade', args: [ - nativeToScVal(contractAddress), - nativeToScVal(options.newVersion), - nativeToScVal(newWasmHash), - nativeToScVal([options.migrationData]), - ], + contractAddress, + options.newVersion, + newWasmHash, + [options.migrationData], + ].map(nativeToScVal), auth: await createUpgradeAuths(contractAddress, newWasmHash, options.migrationData, chain, wallet), }); @@ -199,14 +199,6 @@ async function createUpgradeAuths(contractAddress, newWasmHash, migrationData, c ); } -const createAuthorizedFunc = (contractAddress, functionName, args) => - xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn( - new xdr.InvokeContractArgs({ - contractAddress: contractAddress.toScAddress(), - functionName, - args, - }), - ); async function mainProcessor(options, processor, contractName) { const config = loadConfig(options.env); diff --git a/stellar/utils.js b/stellar/utils.js index df034b46..74ce2b75 100644 --- a/stellar/utils.js +++ b/stellar/utils.js @@ -8,7 +8,7 @@ const { Networks, BASE_FEE, xdr: { DiagnosticEvent, SorobanTransactionData }, - Address, + Address, xdr, } = require('@stellar/stellar-sdk'); const { printInfo, sleep, addEnvOption } = require('../common'); const { Option } = require('commander'); @@ -278,6 +278,15 @@ function serializeValue(value) { return value; } +export const createAuthorizedFunc = (contractAddress, functionName, args) => + xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn( + new xdr.InvokeContractArgs({ + contractAddress: contractAddress.toScAddress(), + functionName, + args, + }), + ); + module.exports = { stellarCmd, ASSET_TYPE_NATIVE, From 2217259fcb5543bed76e106cdc22b229eef22faa Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 2 Jan 2025 19:00:13 +0100 Subject: [PATCH 3/5] fix bug --- package.json | 1 + stellar/deploy-contract.js | 15 +++++---------- stellar/utils.js | 3 ++- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 7ada3c84..92303be6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "@axelar-network/axelar-contract-deployments", + "type": "commonjs", "version": "1.4.0", "description": "Axelar contract deployment scripts", "main": "index.js", diff --git a/stellar/deploy-contract.js b/stellar/deploy-contract.js index 21917bc5..998a29f5 100644 --- a/stellar/deploy-contract.js +++ b/stellar/deploy-contract.js @@ -143,7 +143,8 @@ async function uploadWasm(filePath, wallet, chain) { async function upgrade(options, _, chain, contractName) { const { wasmPath, yes } = options; - const uncheckedContractAddress = chain.contracts[contractName].address; + let contractAddress = chain.contracts[contractName]?.address; + const upgraderAddress = chain.contracts.upgrader?.address; const wallet = await getWallet(chain, options); if (prompt(`Proceed with upgrade on ${chain.name}?`, yes)) { @@ -151,10 +152,10 @@ async function upgrade(options, _, chain, contractName) { } validateParameters({ - isNonEmptyString: { uncheckedContractAddress, chain.contracts.upgrader.address }, + isNonEmptyString: { contractAddress, upgraderAddress }, }); - const contractAddress = Address.fromString(uncheckedContractAddress); + contractAddress = Address.fromString(contractAddress); const newWasmHash = await uploadWasm(wasmPath, wallet, chain); printInfo('New Wasm hash', serializeValue(newWasmHash)); @@ -162,12 +163,7 @@ async function upgrade(options, _, chain, contractName) { const operation = Operation.invokeContractFunction({ contract: chain.contracts.upgrader.address, function: 'upgrade', - args: [ - contractAddress, - options.newVersion, - newWasmHash, - [options.migrationData], - ].map(nativeToScVal), + args: [contractAddress, options.newVersion, newWasmHash, [options.migrationData]].map(nativeToScVal), auth: await createUpgradeAuths(contractAddress, newWasmHash, options.migrationData, chain, wallet), }); @@ -199,7 +195,6 @@ async function createUpgradeAuths(contractAddress, newWasmHash, migrationData, c ); } - async function mainProcessor(options, processor, contractName) { const config = loadConfig(options.env); const chain = getChainConfig(config, options.chainName); diff --git a/stellar/utils.js b/stellar/utils.js index 74ce2b75..19751677 100644 --- a/stellar/utils.js +++ b/stellar/utils.js @@ -278,7 +278,7 @@ function serializeValue(value) { return value; } -export const createAuthorizedFunc = (contractAddress, functionName, args) => +const createAuthorizedFunc = (contractAddress, functionName, args) => xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn( new xdr.InvokeContractArgs({ contractAddress: contractAddress.toScAddress(), @@ -301,4 +301,5 @@ module.exports = { getAmplifierVerifiers, serializeValue, getBalances, + createAuthorizedFunc, }; From a5c1433108f9ba5971f1df681f4209f47af4767b Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 2 Jan 2025 19:01:48 +0100 Subject: [PATCH 4/5] lint --- stellar/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stellar/utils.js b/stellar/utils.js index 19751677..d71589cc 100644 --- a/stellar/utils.js +++ b/stellar/utils.js @@ -8,7 +8,8 @@ const { Networks, BASE_FEE, xdr: { DiagnosticEvent, SorobanTransactionData }, - Address, xdr, + Address, + xdr, } = require('@stellar/stellar-sdk'); const { printInfo, sleep, addEnvOption } = require('../common'); const { Option } = require('commander'); From b55af11ccb8477ec091add92b0c37724d581b184 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Thu, 2 Jan 2025 19:26:35 +0100 Subject: [PATCH 5/5] new line --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1fafc5ca..c7bc0590 100644 --- a/.gitignore +++ b/.gitignore @@ -121,4 +121,4 @@ sui/move .DS_Store # Jetbrains -.idea \ No newline at end of file +.idea