Skip to content

Commit

Permalink
feat: initial sui deployment scripts (#240)
Browse files Browse the repository at this point in the history
* added a script to publish the gateway

* added a publish-test script

* added dependancy

* refactor script

* refactor script

* add faucet script

* rename script

* fix faucet script

* prettier and lint

* contract address

* update sui dep

* fix sign utils

* fix errors and add README

* add readme

* temp

* deploy gateway now works

* test deployment works now too

* lint

* update package.json

* fix scripts

* import fix

* prettier

* fix pubkey in readme

* improve utils

* lint

---------

Co-authored-by: Milap Sheth <milap@interoplabs.io>
  • Loading branch information
Foivos and milapsheth authored May 31, 2024
1 parent aef9eda commit 6d2ef94
Show file tree
Hide file tree
Showing 11 changed files with 1,291 additions and 92 deletions.
12 changes: 12 additions & 0 deletions axelar-chains-config/info/testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,18 @@
"tokenSymbol": "XLM",
"contracts": {}
},
"sui": {
"name": "Sui",
"axelarId": "sui",
"networkType": "testnet",
"rpc": "https://fullnode.testnet.sui.io:443",
"tokenSymbol": "SUI",
"explorer": {
"name": "Suiscan",
"url": "https://suiscan.xyz"
},
"contracts": {}
},
"axelar": {
"id": "Axelarnet",
"axelarId": "Axelarnet",
Expand Down
799 changes: 707 additions & 92 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"homepage": "https://github.com/axelarnetwork/axelar-contract-deployments#readme",
"dependencies": {
"@0xpolygonhermez/zkevm-commonjs": "github:0xpolygonhermez/zkevm-commonjs#v1.0.0",
"@axelar-network/axelar-cgp-sui": "https://github.com/axelarnetwork/axelar-cgp-sui.git#main",
"@axelar-network/axelar-cgp-solidity": "6.3.1",
"@axelar-network/axelar-gmp-sdk-solidity": "5.9.0",
"@axelar-network/interchain-token-service": "1.2.4",
Expand Down
61 changes: 61 additions & 0 deletions sui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Sui deployment scripts

## Prerequisites

Install Sui CLI: `brew install sui`

A Sui keypair can be created as follows.

1. Using Sui CLI:

```bash
sui client new-address secp256k1 wallet

# Export private key in bech32 format
sui keytool export --key-identity wallet
```

2. Using the script

```bash
node sui/generate-keypair.js
```

Set `PRIVATE_KEY=[suiprivkey...]` in your `.env` file. Other private key types are supported via `--privateKeyType` and `--signatureScheme` flags.

If you want to run against a local Sui network, then create a `local.json` config containing:

```bash
{
"sui": {
"name": "Sui",
"axelarId": "sui",
"networkType": "localnet",
"tokenSymbol": "SUI",
"rpc": "[local rpc]",
"contracts": {}
}
}
```

Use the `-e local` (or `ENV=local` in the `.env` config) flag with scripts to run against the local network.

## Scripts

1. Faucet: To get test SUI coins to your address.

```bash
node sui/faucet.js
```

2. Deploy the gateway:

```bash
node sui/deploy-gateway.js -e testnet --signers '{"signers": [{"pubkey": "0x020194ead85b350d90472117e6122cf1764d93bf17d6de4b51b03d19afc4d6302b", "weight": 1}], "threshold": 1, "nonce": ""}'
```

3. Deploy the test GMP package:

```bash
node sui/deploy-test.js
```
46 changes: 46 additions & 0 deletions sui/cli-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

require('dotenv').config();

const { Option } = require('commander');

const addBaseOptions = (program, options = {}) => {
program.addOption(
new Option('-e, --env <env>', 'environment')
.choices(['local', 'devnet', 'devnet-amplifier', 'devnet-verifiers', 'stagenet', 'testnet', 'mainnet'])
.default('testnet')
.makeOptionMandatory(true)
.env('ENV'),
);
program.addOption(new Option('-y, --yes', 'skip deployment prompt confirmation').env('YES'));
program.addOption(new Option('--gasOptions <gasOptions>', 'gas options cli override'));

if (!options.ignorePrivateKey) {
program.addOption(new Option('-p, --privateKey <privateKey>', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY'));

program.addOption(
new Option('--privateKeyType <privateKeyType>', 'private key type')
.makeOptionMandatory(true)
.choices(['bech32', 'mnemonic', 'hex'])
.default('bech32')
.env('PRIVATE_KEY_TYPE'),
);

program.addOption(
new Option('--signatureScheme <signatureScheme>', 'signature scheme to use')
.choices(['secp256k1', 'ed25519', 'secp256r1'])
.default('secp256k1')
.env('SIGNATURE_SCHEME'),
);
}

if (options.address) {
program.addOption(new Option('--address <address>', 'override contract address'));
}

return program;
};

module.exports = {
addBaseOptions,
};
153 changes: 153 additions & 0 deletions sui/deploy-gateway.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
const { saveConfig, prompt, printInfo } = require('../evm/utils');
const { Command, Option } = require('commander');
const { publishPackage, updateMoveToml } = require('@axelar-network/axelar-cgp-sui/scripts/publish-package');
const { TransactionBlock } = require('@mysten/sui.js/transactions');
const { bcs } = require('@mysten/sui.js/bcs');
const { ethers } = require('hardhat');
const {
utils: { arrayify, hexlify },
constants: { HashZero },
} = ethers;

const { addBaseOptions } = require('./cli-utils');
const { getWallet, printWalletInfo } = require('./sign-utils');
const { getAmplifierSigners, loadSuiConfig } = require('./utils');

async function getSigners(config, chain, options) {
if (options.signers) {
printInfo('Using provided signers', options.signers);

const signers = JSON.parse(options.signers);
return {
signers: signers.signers.map(({ pubkey, weight }) => {
return { pubkey: arrayify(pubkey), weight };
}),
threshold: signers.threshold,
nonce: signers.nonce || HashZero,
};
}

return getAmplifierSigners(config, chain);
}

async function processCommand(config, chain, options) {
const [keypair, client] = getWallet(chain, options);

await printWalletInfo(keypair, client, chain, options);

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

const contractConfig = chain.contracts.axelar_gateway;
const { minimumRotationDelay, domainSeparator } = options;
const signers = await getSigners(config, chain, options);
const operator = options.operator || keypair.toSuiAddress();

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

const published = await publishPackage('axelar_gateway', client, keypair);
const packageId = published.packageId;

updateMoveToml('axelar_gateway', packageId);

const creatorCap = published.publishTxn.objectChanges.find((change) => change.objectType === `${packageId}::gateway::CreatorCap`);
const relayerDiscovery = published.publishTxn.objectChanges.find(
(change) => change.objectType === `${packageId}::discovery::RelayerDiscovery`,
);

const signerStruct = bcs.struct('WeightedSigner', {
pubkey: bcs.vector(bcs.u8()),
weight: bcs.u128(),
});
const bytes32Struct = bcs.fixedArray(32, bcs.u8()).transform({
input: (id) => arrayify(id),
output: (id) => hexlify(id),
});

const signersStruct = bcs.struct('WeightedSigners', {
signers: bcs.vector(signerStruct),
threshold: bcs.u128(),
nonce: bytes32Struct,
});

const encodedSigners = signersStruct
.serialize({
...signers,
nonce: bytes32Struct.serialize(signers.nonce).toBytes(),
})
.toBytes();

const tx = new TransactionBlock();

const separator = tx.moveCall({
target: `${packageId}::bytes32::new`,
arguments: [tx.pure(arrayify(domainSeparator))],
});

tx.moveCall({
target: `${packageId}::gateway::setup`,
arguments: [
tx.object(creatorCap.objectId),
tx.pure.address(operator),
separator,
tx.pure(minimumRotationDelay),
tx.pure(bcs.vector(bcs.u8()).serialize(encodedSigners).toBytes()),
tx.object('0x6'),
],
});
const result = await client.signAndExecuteTransactionBlock({
transactionBlock: tx,
signer: keypair,
options: {
showEffects: true,
showObjectChanges: true,
showContent: true,
},
});

const gateway = result.objectChanges.find((change) => change.objectType === `${packageId}::gateway::Gateway`);

contractConfig.address = packageId;
contractConfig.objects = {
gateway: gateway.objectId,
relayerDiscovery: relayerDiscovery.objectId,
};
contractConfig.domainSeparator = domainSeparator;
contractConfig.operator = operator;
contractConfig.minimumRotationDelay = minimumRotationDelay;

printInfo('Gateway deployed', JSON.stringify(contractConfig, null, 2));
}

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

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

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

program.name('deploy-gateway').description('Deploys/publishes the Sui gateway');

addBaseOptions(program);

program.addOption(new Option('--signers <signers>', 'JSON with the initial signer set').env('SIGNERS'));
program.addOption(new Option('--operator <operator>', 'operator for the gateway (defaults to the deployer address)').env('OPERATOR'));
program.addOption(
new Option('--minimumRotationDelay <minimumRotationDelay>', 'minium delay for signer rotations (in ms)').default(
24 * 60 * 60 * 1000,
),
); // 1 day (in ms)
program.addOption(new Option('--domainSeparator <domainSeparator>', 'domain separator').default(HashZero));

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

program.parse();
}
89 changes: 89 additions & 0 deletions sui/deploy-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const { saveConfig, prompt, printInfo } = require('../evm/utils');
const { Command, Option } = require('commander');
const { publishPackage, updateMoveToml } = require('@axelar-network/axelar-cgp-sui/scripts/publish-package');
const { TransactionBlock } = require('@mysten/sui.js/transactions');
const { ethers } = require('hardhat');
const {
constants: { HashZero },
} = ethers;
const { loadSuiConfig } = require('./utils');

const { addBaseOptions } = require('./cli-utils');
const { getWallet, printWalletInfo } = require('./sign-utils');

async function processCommand(config, chain, options) {
const [keypair, client] = getWallet(chain, options);

await printWalletInfo(keypair, client, chain, options);

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

const relayerDiscovery = config.sui.contracts.axelar_gateway?.objects?.relayerDiscovery;

if (!relayerDiscovery) {
throw new Error('Relayer discovery object not found');
}

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

const published = await publishPackage('test', client, keypair);
updateMoveToml('test', published.packageId);

const singleton = published.publishTxn.objectChanges.find((change) => change.objectType === `${published.packageId}::test::Singleton`);

const tx = new TransactionBlock();

tx.moveCall({
target: `${published.packageId}::test::register_transaction`,
arguments: [tx.object(relayerDiscovery), tx.object(singleton.objectId)],
});

await client.signAndExecuteTransactionBlock({
transactionBlock: tx,
signer: keypair,
options: {
showEffects: true,
showObjectChanges: true,
showContent: true,
},
});

chain.contracts.test.address = published.packageId;
chain.contracts.test.objects = { singleton: singleton.objectId };

printInfo('Test package deployed', JSON.stringify(chain.contracts.test, null, 2));
}

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

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

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

program.name('deploy-gateway').description('Deploys/publishes the Sui gateway');

addBaseOptions(program);

program.addOption(new Option('--signers <signers>', 'JSON with the initial signer set').env('SIGNERS'));
program.addOption(new Option('--operator <operator>', 'operator for the gateway (defaults to the deployer address)'));
program.addOption(
new Option('--minimumRotationDelay <minimumRotationDelay>', 'minium delay for signer rotations (in ms)').default(
24 * 60 * 60 * 1000,
),
); // 1 day (in ms)
program.addOption(new Option('--domainSeparator <domainSeparator>', 'domain separator').default(HashZero));

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

program.parse();
}
Loading

0 comments on commit 6d2ef94

Please sign in to comment.