Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add stellar upgrader contract to the deploy-contract.js script #487

Merged
merged 11 commits into from
Jan 9, 2025
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,6 @@ sui/move
# VSCode
.vscode
.DS_Store

# Jetbrains
.idea
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "@axelar-network/axelar-contract-deployments",
"type": "commonjs",
"version": "1.4.0",
"description": "Axelar contract deployment scripts",
"main": "index.js",
Expand Down
25 changes: 16 additions & 9 deletions stellar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ node stellar/deploy-contract.js deploy axelar_gas_service --chain-name <CHAIN_NA
Deploy Interchain Token wasm first.
```bash
node stellar/deploy-contract.js deploy interchain_token --chain-name <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 <CHAIN_NAME> --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/interchain_token_service.optimized.wasm
```

Expand All @@ -100,6 +99,22 @@ node stellar/deploy-contract.js deploy interchain_token_service --chain-name <CH
node stellar/deploy-contract.js deploy example --chain-name <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 <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 <CONTRACT_NAME> --chain-name <CHAIN_NAME> --wasm-path ../axelar-cgp-soroban/target/wasm32-unknown-unknown/release/<CONTRACT_NAME>.optimized.wasm --new-version <NEW_VERSION> --migration-data <MIGRATION_DATA>
```

where `<CONTRACT_NAME>` is the name of the contract to be upgraded and `--wasm-path` points to the upgraded bytecode. As a sanity check, `<NEW_VERSION>` must match the version number defined by the provided bytecode, so upgrading to the wrong version can be prevented. `<MIGRATION_DATA>` is the json encoded data that will be passed to the contract's `migrate` function.
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved


## Generate bindings

Generate TypeScript bindings for the contract
Expand Down Expand Up @@ -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.
Expand Down
46 changes: 40 additions & 6 deletions stellar/deploy-contract.js
Original file line number Diff line number Diff line change
@@ -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, createAuthorizedFunc } = require('./utils');
const { getDomainSeparator, getChainConfig } = require('../common');
const { prompt, validateParameters } = require('../common/utils');
const { weightedSignersToScVal } = require('./type-utils');
Expand Down Expand Up @@ -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' });
Expand Down Expand Up @@ -139,30 +143,58 @@ async function uploadWasm(filePath, wallet, chain) {

async function upgrade(options, _, chain, contractName) {
const { wasmPath, yes } = options;
const contractAddress = 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)) {
return;
}

validateParameters({
isNonEmptyString: { contractAddress },
isNonEmptyString: { contractAddress, upgraderAddress },
});

contractAddress = Address.fromString(contractAddress);

const newWasmHash = await uploadWasm(wasmPath, wallet, chain);
printInfo('New Wasm hash', serializeValue(newWasmHash));

const operation = Operation.invokeContractFunction({
contract: contractAddress,
contract: chain.contracts.upgrader.address,
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
function: 'upgrade',
args: [nativeToScVal(newWasmHash)],
args: [contractAddress, options.newVersion, newWasmHash, [options.migrationData]].map(nativeToScVal),
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
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
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),
),
),
);
}

async function mainProcessor(options, processor, contractName) {
const config = loadConfig(options.env);
const chain = getChainConfig(config, options.chainName);
Expand Down Expand Up @@ -200,6 +232,8 @@ function main() {
.description('Upgrade a Stellar contract')
.argument('<contract-name>', 'contract name to deploy')
.addOption(new Option('--wasm-path <wasmPath>', 'path to the WASM file'))
.addOption(new Option('--new-version <newVersion>', 'new version of the contract'))
.addOption(new Option('--migration-data <migrationData>', 'migration data'))
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
.action((contractName, options) => {
mainProcessor(options, upgrade, contractName);
});
Expand Down
11 changes: 11 additions & 0 deletions stellar/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
BASE_FEE,
xdr: { DiagnosticEvent, SorobanTransactionData },
Address,
xdr,
} = require('@stellar/stellar-sdk');
const { printInfo, sleep, addEnvOption } = require('../common');
const { Option } = require('commander');
Expand Down Expand Up @@ -278,6 +279,15 @@ function serializeValue(value) {
return value;
}

const createAuthorizedFunc = (contractAddress, functionName, args) =>
xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn(
new xdr.InvokeContractArgs({
contractAddress: contractAddress.toScAddress(),
functionName,
args,
}),
);

module.exports = {
stellarCmd,
ASSET_TYPE_NATIVE,
Expand All @@ -292,4 +302,5 @@ module.exports = {
getAmplifierVerifiers,
serializeValue,
getBalances,
createAuthorizedFunc,
};
Loading