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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you mention how MIGRATION_DATA is encoded for the no migration case and a more complex migration case?



## 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a global constant for it. Could also create an option with a default of 20 though I don't see us configuring it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean for the 20? What would be the benefit of that? It's literally just used here once, and, as you point out, that doesn't seem like something we would ever parameterize

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, parameterization seems overkill. Would still be nice to define a constant variable for it though for style

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