-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial sui deployment scripts (#240)
* 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
1 parent
aef9eda
commit 6d2ef94
Showing
11 changed files
with
1,291 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
Oops, something went wrong.