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 script to deploy/upgrade ITS #285

Closed
wants to merge 11 commits into from
17 changes: 17 additions & 0 deletions sui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ node sui/gateway.js rotate --signers wallet --proof wallet --currentNonce test -

Use the same nonce for `--currentNonce` as the `--nonce` when deploying the gateway.

### Deploy/Upgrade ITS

Set required dependencies for ITS in contract config file.
Provide --txFilePath with --offline to generate tx data file for offline signing.

Deploy the ITS package:

```bash
node sui/deploy-its.js
```

Upgrade the ITS package:

```bash
node sui/deploy-its.js --upgrade --policy <policy>
```
use --digest to override digest generate from module build

### Multisig

Expand Down
165 changes: 165 additions & 0 deletions sui/deploy-its.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
const { Command, Option } = require('commander');
const { TxBuilder, updateMoveToml } = require('@axelar-network/axelar-cgp-sui');
const { bcs } = require('@mysten/sui.js/bcs');
const { fromB64, toB64 } = require('@mysten/bcs');
const { saveConfig, printInfo, validateParameters, prompt, writeJSON } = require('../evm/utils');
const { addBaseOptions } = require('./cli-utils');
const { getWallet } = require('./sign-utils');
const { loadSuiConfig } = require('./utils');

async function upgradePackage(client, keypair, packageConfig, builder, options) {
const { modules, dependencies, digest } = await builder.getContractBuild('its');
const { policy, offline, sender } = options;

const upgradeCap = packageConfig.objects?.UpgradeCap;
const digestHash = options.digest ? fromB64(options.digest) : digest;

validateParameters({ isNonEmptyString: { upgradeCap, policy }, isNonEmptyStringArray: { modules, dependencies } });

const tx = builder.tx;
const cap = tx.object(upgradeCap);

const ticket = tx.moveCall({
target: `0x2::package::authorize_upgrade`,
arguments: [cap, tx.pure(policy), tx.pure(bcs.vector(bcs.u8()).serialize(digestHash).toBytes())],
});

const receipt = tx.upgrade({
modules,
dependencies,
packageId: packageConfig.address,
ticket,
});

tx.moveCall({
target: `0x2::package::commit_upgrade`,
arguments: [cap, receipt],
});

sender ? tx.setSender(sender) : tx.setSender(keypair.toSuiAddress());
const txBytes = await tx.build({ client });

if (offline) {
options.txBytes = txBytes;
} else {
const signature = (await keypair.signTransactionBlock(txBytes)).signature;
const result = await client.executeTransactionBlock({
transactionBlock: txBytes,
signature,
options: {
showEffects: true,
showObjectChanges: true,
showEvents: true,
},
});

const packageId = (result.objectChanges?.filter((a) => a.type === 'published') ?? [])[0].packageId;
packageConfig.address = packageId;
printInfo('Transaction result', JSON.stringify(result, null, 2));
printInfo(`ITS upgraded`, packageId);
}
}

async function deployPackage(chain, client, keypair, itsContractConfig, builder, options) {
const { offline, sender } = options;

const address = sender || keypair.toSuiAddress();
await builder.publishPackageAndTransferCap('its', address);
const tx = builder.tx;
tx.setSender(address);
const txBytes = await tx.build({ client });

if (offline) {
options.txBytes = txBytes;
} else {
if (prompt(`Proceed with deployment on ${chain.name}?`, options.yes)) {
return;
}

const signature = (await keypair.signTransactionBlock(txBytes)).signature;
const publishTxn = await client.executeTransactionBlock({
transactionBlock: txBytes,
signature,
options: {
showEffects: true,
showObjectChanges: true,
showEvents: true,
},
});

const packageId = (publishTxn.objectChanges?.find((a) => a.type === 'published') ?? []).packageId;
itsContractConfig.address = packageId;
const ITS = publishTxn.objectChanges.find((change) => change.objectType === `${packageId}::its::ITS`);
const upgradeCap = publishTxn.objectChanges.find((change) => change.objectType === `0x2::package::UpgradeCap`);
itsContractConfig.objects = {
ITS: ITS.objectId,
UpgradeCap: upgradeCap.objectId,
};

printInfo(`ITS deployed`, JSON.stringify(itsContractConfig, null, 2));
}
}

async function processCommand(chain, options) {
const [keypair, client] = getWallet(chain, options);
const { upgrade, offline, txFilePath } = options;

printInfo('Wallet address', keypair.toSuiAddress());

if (!chain.contracts.its) {
chain.contracts.its = {};
}

const contractsConfig = chain.contracts;
const itsContractConfig = contractsConfig.its;

for (const dependencies of ['axelar_gateway', 'abi', 'governance']) {
const packageId = contractsConfig[dependencies]?.address;
updateMoveToml(dependencies, packageId);
}

const builder = new TxBuilder(client);

if (upgrade) {
await upgradePackage(client, keypair, itsContractConfig, builder, options);
} else {
await deployPackage(chain, client, keypair, itsContractConfig, builder, options);
}

if (offline) {
validateParameters({ isNonEmptyString: { txFilePath } });

const txB64Bytes = toB64(options.txBytes);

writeJSON({ status: 'PENDING', bytes: txB64Bytes }, txFilePath);
printInfo(`The unsigned transaction is`, txB64Bytes);
}
}

async function mainProcessor(options, processor) {
const config = loadSuiConfig(options.env);

await processor(config.sui, options);
saveConfig(config, options.env);
}

if (require.main === module) {
const program = new Command();

program.name('deploy-its').description('Deploy/Upgrade the Sui ITS');

addBaseOptions(program);

program.addOption(new Option('--upgrade', 'deploy or upgrade ITS'));
program.addOption(new Option('--policy <policy>', 'new policy to upgrade'));
program.addOption(new Option('--sender <sender>', 'transaction sender'));
program.addOption(new Option('--digest <digest>', 'digest hash for upgrade'));
program.addOption(new Option('--offline', 'store tx block for sign'));
program.addOption(new Option('--txFilePath <file>', 'unsigned transaction will be stored'));

program.action((options) => {
mainProcessor(options, processCommand);
});

program.parse();
}
Loading