diff --git a/.env.example b/.env.example index b139edca..a7a646e3 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,5 @@ EVM_PRIVATE_KEY=YOUR_PRIVATE_KEY_HERE APTOS_URL=http://0.0.0.0:8080 APTOS_ADDRESS=0x8ac1b8ff9583ac8e661c7f0ee462698c57bb7fc454f587e3fa25a57f9406acc0 APTOS_TOKEN_LINKER_ADDRESS=0x1641cde81bb0ffd52cce178f6ab4f1fc86fe451de189ffc71298aaf2e74a7a15 +SUI_URL=http://localhost:9000 +SUI_FAUCET_URL=http://localhost:9123 diff --git a/.github/workflows/local-example-tests.yaml b/.github/workflows/local-example-tests.yaml index 761954bd..08f1814b 100644 --- a/.github/workflows/local-example-tests.yaml +++ b/.github/workflows/local-example-tests.yaml @@ -13,17 +13,29 @@ jobs: - name: Setup Node uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 16 - name: Checkout code uses: actions/checkout@v3 - - name: Prerequisites + - name: Download and Install Aptos Binary run: | - wget --no-check-certificate https://github.com/aptos-labs/aptos-core/releases/download/aptos-cli-v1.0.4/aptos-cli-1.0.4-Ubuntu-22.04-x86_64.zip + wget --no-check-certificate https://github.com/aptos-labs/aptos-core/releases/download/aptos-cli-v1.0.4/aptos-cli-1.0.4-Ubuntu-22.04-x86_64.zip unzip aptos-cli-1.0.4-Ubuntu-22.04-x86_64.zip - chmod +x aptos - cp aptos /usr/local/bin + sudo mv aptos /usr/local/bin + + - name: Setup Dependencies for Sui Binary + run: sudo apt-get update && sudo apt-get install -y libpq-dev + + - name: Download and Install Sui Binary + run: | + wget https://github.com/MystenLabs/sui/releases/download/devnet-v1.7.0/sui-devnet-v1.7.0-ubuntu-x86_64.tgz + tar -xvf sui-devnet-v1.7.0-ubuntu-x86_64.tgz + sudo mv ./target/release/sui-test-validator-ubuntu-x86_64 /usr/local/bin/sui-test-validator + sudo mv ./target/release/sui-ubuntu-x86_64 /usr/local/bin/sui + + - name: Cleanup + run: rm -rf target aptos-cli-1.0.4-Ubuntu-22.04-x86_64.zip sui-devnet-v1.7.0-ubuntu-x86_64.tgz - name: Install run: npm ci @@ -42,7 +54,9 @@ jobs: npm run build-aptos - name: Test + timeout-minutes: 15 run: | + nohup sh -c "sui-test-validator" > nohup.out 2> nohup.err < /dev/null & nohup sh -c "aptos node run-local-testnet --with-faucet" > nohup.out 2> nohup.err < /dev/null & sleep 10 - NODE_ENV=ci npm run test + npm run test diff --git a/config/ci.json b/config/ci.json index ccdbe825..11b17131 100644 --- a/config/ci.json +++ b/config/ci.json @@ -1,5 +1,8 @@ { "aptos": { "enabled": true + }, + "sui": { + "enabled": true } } diff --git a/config/default.json b/config/default.json index 81e0877f..2418c211 100644 --- a/config/default.json +++ b/config/default.json @@ -1,5 +1,8 @@ { "aptos": { "enabled": false + }, + "sui": { + "enabled": true } } diff --git a/examples/sui/call-contract/README.md b/examples/sui/call-contract/README.md new file mode 100644 index 00000000..bac3e028 --- /dev/null +++ b/examples/sui/call-contract/README.md @@ -0,0 +1,38 @@ +# Call Contract + +Relay a message bi-directional between Sui and an EVM chain. + +Deploy: + +```bash +npm run deploy sui/call-contract local +``` + +Run the test: + +```bash +npm run execute sui/call-contract local ${evmChain} ${message} +``` + +**Default Values**: + +- `evmChain` is `Avalanche`. Valid values are Moonbeam, Avalanche, Fantom, Ethereum, and Polygon +- `message` is `Hello World` + +## Example + +```bash +npm run deploy sui/call-contract local +npm run execute sui/call-contract local "Avalanche" 'Hello World' +``` + +Output: + +``` +--- Initially --- +value at Avalanche is "" +value at Sui is "Hello Sui from Ethereum, it is 1:26:06 PM." +--- After --- +value at Avalanche is "Hello Avalanche from Sui, it is 7:05:48 PM." +value at Sui is "Hello Sui from Avalanche, it is 7:05:48 PM." +``` diff --git a/examples/sui/call-contract/contracts/HelloWorld.sol b/examples/sui/call-contract/contracts/HelloWorld.sol new file mode 100644 index 00000000..b7e204e7 --- /dev/null +++ b/examples/sui/call-contract/contracts/HelloWorld.sol @@ -0,0 +1,42 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol'; +import { IAxelarGateway } from '@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol'; +import { IAxelarGasService } from '@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol'; + +contract HelloWorld is AxelarExecutable { + string public value; + string public sourceChain; + string public sourceAddress; + IAxelarGasService gasService; + + constructor(address _gateway, address _gasReceiver) AxelarExecutable(_gateway) { + gasService = IAxelarGasService(_gasReceiver); + } + + event Executed(); + + function setRemoteValue(string memory destinationChain, string memory destinationAddress, string calldata message) external payable { + require(msg.value > 0, 'Gas payment is required'); + + bytes memory payload = abi.encodePacked(message); + gasService.payNativeGasForContractCall{ value: msg.value }( + address(this), + destinationChain, + destinationAddress, + payload, + msg.sender + ); + gateway.callContract(destinationChain, destinationAddress, payload); + } + + // Handles calls created by setAndSend. Updates this contract's value + function _execute(string calldata sourceChain_, string calldata sourceAddress_, bytes calldata payload_) internal override { + (value) = abi.decode(payload_, (string)); + sourceChain = sourceChain_; + sourceAddress = sourceAddress_; + + emit Executed(); + } +} diff --git a/examples/sui/call-contract/index.js b/examples/sui/call-contract/index.js new file mode 100644 index 00000000..a5675c39 --- /dev/null +++ b/examples/sui/call-contract/index.js @@ -0,0 +1,90 @@ +'use strict'; + +const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); +const { + utils: { deployContract }, +} = require('@axelar-network/axelar-local-dev'); +const { ethers } = require('ethers'); +const { TransactionBlock } = require('@mysten/sui.js/transactions'); +const { SuiNetwork } = require('@axelar-network/axelar-local-dev-sui'); +const path = require('path'); +const HelloWorld = rootRequire('./artifacts/examples/sui/call-contract/contracts/HelloWorld.sol/HelloWorld.json'); + +async function preDeploy(chains) { + console.log(`Deploying HelloWorld for Sui.`); + const client = new SuiNetwork(process.env.SUI_URL, process.env.SUI_FAUCET_URL); + await client.init(); + const response = await client.deploy(path.join(__dirname, 'modules')); + + for (const chain of chains) { + chain.sui = { + packageId: response.packages[0].packageId, + }; + } + + console.log(`Deployed HelloWorld module for Sui.`); +} + +async function deploy(chain, wallet) { + console.log(`Deploying HelloWorld for ${chain.name}.`); + chain.contract = await deployContract(wallet, HelloWorld, [chain.gateway, chain.gasService]); + console.log(`Deployed HelloWorld for ${chain.name} at ${chain.contract.address}.`); +} + +async function execute(evmChain, wallet, options) { + const args = options.args || []; + const client = new SuiNetwork(process.env.SUI_URL, process.env.SUI_FAUCET_URL); + const sender = new Ed25519Keypair(); + await client.fundWallet(sender.toSuiAddress()); + + const messageEvmToSui = args[1] || `Hello Sui from ${evmChain.name}, it is ${new Date().toLocaleTimeString()}.`; + const messageSuiToEvm = args[2] || `Hello ${evmChain.name} from Sui, it is ${new Date().toLocaleTimeString()}.`; + + async function logValue() { + console.log(`value at ${evmChain.name} is "${await evmChain.contract.value()}"`); + const { data } = await client.queryEvents({ + query: { + MoveModule: { + module: `hello_world`, + package: evmChain.sui.packageId, + }, + }, + limit: 1, + }); + + const msg = data[0].parsedJson.updated_message; + + console.log(`value at Sui is "${msg}"`); + } + + console.log('--- Initially ---'); + await logValue(); + + // Send message from EVM to Sui. + const tx = await evmChain.contract.setRemoteValue('sui', `${evmChain.sui.packageId}::hello_world`, messageEvmToSui, { + value: 1, // Currently, we didn't check for the fee, so we set it to 1. + }); + await tx.wait(); + + // Send message from Sui to EVM. + const payload = ethers.utils.defaultAbiCoder.encode(['string'], [messageSuiToEvm]); + const tb = new TransactionBlock(); + tb.moveCall({ + target: `${evmChain.sui.packageId}::hello_world::call`, + arguments: [tb.pure(evmChain.name), tb.pure(evmChain.contract.address), tb.pure(payload), tb.pure(1)], + }); + await client.execute(tb, sender); + + const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + await sleep(5000); + + console.log('--- After ---'); + await logValue(); +} + +module.exports = { + preDeploy, + deploy, + execute, +}; diff --git a/examples/sui/call-contract/modules/Move.lock b/examples/sui/call-contract/modules/Move.lock new file mode 100644 index 00000000..05cec2f0 --- /dev/null +++ b/examples/sui/call-contract/modules/Move.lock @@ -0,0 +1,31 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 0 +manifest_digest = "6D8F6C70FF086B6824ECACDB1112B6ECA42BB6B0668268E94CE8F6FE228A144C" +deps_digest = "C6C93A018C0B43F8AA8899B23B42E757DF9300785637696481F59EBFA35625D2" + +dependencies = [ + { name = "Sui" }, + { name = "axelar" }, +] + +[[move.package]] +name = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "77a9e0d", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +name = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "77a9e0d", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { name = "MoveStdlib" }, +] + +[[move.package]] +name = "axelar" +source = { git = "https://github.com/axelarnetwork/axelar-local-dev.git", rev = "ae68c9c", subdir = "packages/axelar-local-dev-sui/move/axelar" } + +dependencies = [ + { name = "Sui" }, +] diff --git a/examples/sui/call-contract/modules/Move.toml b/examples/sui/call-contract/modules/Move.toml new file mode 100644 index 00000000..8e070c1b --- /dev/null +++ b/examples/sui/call-contract/modules/Move.toml @@ -0,0 +1,11 @@ +[package] +name = "sui-example" +version = "0.0.1" + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "77a9e0d"} +axelar = { git = "https://github.com/axelarnetwork/axelar-local-dev.git", subdir = "packages/axelar-local-dev-sui/move/axelar", rev = "ae68c9c" } + +[addresses] +sui_example = "0x0" +sui = "0x2" diff --git a/examples/sui/call-contract/modules/sources/hello_world.move b/examples/sui/call-contract/modules/sources/hello_world.move new file mode 100644 index 00000000..ef488191 --- /dev/null +++ b/examples/sui/call-contract/modules/sources/hello_world.move @@ -0,0 +1,63 @@ +module sui_example::hello_world { + use std::string::{utf8, String}; + use sui::object::{Self, ID, UID}; + use sui::transfer; + use sui::hex::{decode}; + use sui::event::emit; + use axelar::gateway; + use sui::tx_context::{TxContext}; + + struct MessageChangeEvent has copy, drop { + id: ID, + updated_message: String, + } + + struct ContractCall has copy, drop { + source: vector, + destination_chain: vector, + destination_address: vector, + payload: vector, + } + + struct MessageHolder has key { + id: UID, + message: String, + } + + fun init(tx: &mut TxContext) { + let msg_holder = MessageHolder { + id: object::new(tx), + message: utf8(b"init"), + }; + + emit(MessageChangeEvent { + id: object::uid_to_inner(&msg_holder.id), + updated_message: msg_holder.message, + }); + + transfer::share_object(msg_holder); + } + + public fun get_message(messageHolder: &MessageHolder): String { + messageHolder.message + } + + public entry fun call(destination_chain: vector, destination_address: vector, payload: vector, _fee_amount: u64) { + gateway::call_contract(destination_chain, destination_address, payload); + } + + public entry fun execute(_command_id: vector, _source_chain: String, _source_address: String, payload: vector, ctx: &mut TxContext) { + let message = utf8(decode(payload)); + let event = MessageHolder { + id: object::new(ctx), + message + }; + + emit(MessageChangeEvent { + id: object::uid_to_inner(&event.id), + updated_message: event.message, + }); + + transfer::share_object(event); + } +} diff --git a/examples/tests/checkExamples.js b/examples/tests/checkExamples.js index eaf9151b..37ddc9b5 100644 --- a/examples/tests/checkExamples.js +++ b/examples/tests/checkExamples.js @@ -2,7 +2,7 @@ require('dotenv').config(); -const { start, deploy, executeEVMExample, executeAptosExample, getWallet, getEVMChains, relayers } = require('../../scripts/libs'); +const { start, deploy, executeEVMExample, executeAptosExample, getWallet, getEVMChains, relayers, executeSuiExample } = require('../../scripts/libs'); const { destroyExported, utils: { setLogger }, @@ -32,6 +32,7 @@ const examples = [ ]; const aptosExamples = ['call-contract', 'token-linker']; +const suiExamples = ['call-contract']; describe('Check Examples Execution', function () { // marked as slow if it takes longer than 15 seconds to run each test. @@ -80,4 +81,17 @@ describe('Check Examples Execution', function () { }); } }); + + describe('Sui Examples', function () { + for (const exampleName of suiExamples) { + it(exampleName, async function () { + const example = rootRequire(`examples/sui/${exampleName}/index.js`); + const chains = getEVMChains('local', testChains); + + if (example.deploy) await deploy('local', chains, wallet, example); + + await executeSuiExample(chains, [], wallet, example); + }); + } + }); }); diff --git a/package-lock.json b/package-lock.json index e4ce6bc0..798d5840 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@axelar-network/axelar-cgp-solidity": "^4.5.0", "@axelar-network/axelar-gmp-sdk-solidity": "^5.0.0", - "@axelar-network/axelar-local-dev": "^2.0.5", + "@axelar-network/axelar-local-dev": "^2.1.0", "@axelar-network/axelarjs-sdk": "^0.12.8", "@openzeppelin/contracts": "^4.5.0", "axios": "^0.27.2", @@ -19,6 +19,7 @@ "config": "^3.3.9", "dotenv": "^16.0.2", "ethers": "^5.6.2", + "fs-extra": "^11.1.1", "uuid": "^8.3.2" }, "devDependencies": { @@ -35,7 +36,8 @@ "node": "^16.0.0 || ^18.0.0" }, "optionalDependencies": { - "@axelar-network/axelar-local-dev-aptos": "^2.0.2" + "@axelar-network/axelar-local-dev-aptos": "^2.1.0", + "@axelar-network/axelar-local-dev-sui": "^2.1.0" } }, "node_modules/@axelar-network/axelar-cgp-aptos": { @@ -72,9 +74,9 @@ } }, "node_modules/@axelar-network/axelar-local-dev": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-local-dev/-/axelar-local-dev-2.0.5.tgz", - "integrity": "sha512-aSAygCP7tzw7quAcylNOfCFFIIL/WpKWaDFiYV7Xwgf+R9OzoygWdMO5B1ZpiG6w6N2wjBAaKjPwpVXg5WIQQw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-local-dev/-/axelar-local-dev-2.1.0.tgz", + "integrity": "sha512-knU3Ao5+Lt9IYSTN0/ddEK4e9JQr1N1XOQaIqnbBxOiDd6IQigR10rN5oTe6Noom+FTly4/nQiqMVeyyMvWSRg==", "dependencies": { "@axelar-network/axelar-cgp-solidity": "^5.0.0", "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.2", @@ -91,16 +93,26 @@ } }, "node_modules/@axelar-network/axelar-local-dev-aptos": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-local-dev-aptos/-/axelar-local-dev-aptos-2.0.5.tgz", - "integrity": "sha512-10+uDSJP3/ytg5XHr9EtAXv4lmVpFIyxTPKfzYo6WIU7f40fnkpiW9D+kdUSybJDpdfm6sHG32exVkHnuOb+8A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-local-dev-aptos/-/axelar-local-dev-aptos-2.1.0.tgz", + "integrity": "sha512-8RH/HHbtsMlu7CTTY9pO1xgB8YxtbnJtvLSxXXqjbu51tSPKxnqW9LXOFjTV93Pb9twYFc9o4EIJqb4BVEU9pQ==", "optional": true, "dependencies": { "@axelar-network/axelar-cgp-aptos": "^1.0.5", - "@axelar-network/axelar-local-dev": "2.0.5", + "@axelar-network/axelar-local-dev": "2.1.0", "aptos": "1.3.16" } }, + "node_modules/@axelar-network/axelar-local-dev-sui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-local-dev-sui/-/axelar-local-dev-sui-2.1.0.tgz", + "integrity": "sha512-QQDim3Ogv4KAw5FwNwkzlOgqxpLnLxvEPgop+p77jPAGPZsmYtpAHwWPqmaKlgg10fgSBZiUgWw+IHr4x3SNyQ==", + "optional": true, + "dependencies": { + "@axelar-network/axelar-local-dev": "2.1.0", + "@mysten/sui.js": "^0.41.0" + } + }, "node_modules/@axelar-network/axelar-local-dev/node_modules/@axelar-network/axelar-cgp-solidity": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-solidity/-/axelar-cgp-solidity-5.0.1.tgz", @@ -120,6 +132,19 @@ "node": ">=16" } }, + "node_modules/@axelar-network/axelar-local-dev/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@axelar-network/axelarjs-sdk": { "version": "0.12.8", "resolved": "https://registry.npmjs.org/@axelar-network/axelarjs-sdk/-/axelarjs-sdk-0.12.8.tgz", @@ -1242,6 +1267,114 @@ "node": ">=12.0.0" } }, + "node_modules/@mysten/bcs": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.7.3.tgz", + "integrity": "sha512-fbusBfsyc2MpTACi72H5edWJ670T84va+qn9jSPpb5BzZ+pzUM1Q0ApPrF5OT+mB1o5Ng+mxPQpBCZQkfiV2TA==", + "optional": true, + "dependencies": { + "bs58": "^5.0.0" + } + }, + "node_modules/@mysten/bcs/node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==", + "optional": true + }, + "node_modules/@mysten/bcs/node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "optional": true, + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/@mysten/sui.js": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.41.1.tgz", + "integrity": "sha512-qSFVqr6Z2LboHhiaP51kluZQaW2oCY4gOF8JBZOl/cB/tvOPoQvdvtfNqViDkTZcaJrYRk8gOCYjozvW4VGX0A==", + "optional": true, + "dependencies": { + "@mysten/bcs": "0.7.3", + "@noble/curves": "^1.1.0", + "@noble/hashes": "^1.3.1", + "@open-rpc/client-js": "^1.8.1", + "@scure/bip32": "^1.3.1", + "@scure/bip39": "^1.2.1", + "@suchipi/femver": "^1.0.0", + "events": "^3.3.0", + "superstruct": "^1.0.3", + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@mysten/sui.js/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "optional": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@mysten/sui.js/node_modules/@scure/bip32": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", + "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "optional": true, + "dependencies": { + "@noble/curves": "~1.2.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@mysten/sui.js/node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "optional": true, + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "optional": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "optional": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/hashes": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.4.tgz", @@ -1892,6 +2025,27 @@ "node": ">= 10" } }, + "node_modules/@open-rpc/client-js": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.8.1.tgz", + "integrity": "sha512-vV+Hetl688nY/oWI9IFY0iKDrWuLdYhf7OIKI6U1DcnJV7r4gAgwRJjEr1QVYszUc0gjkHoQJzqevmXMGLyA0g==", + "optional": true, + "dependencies": { + "isomorphic-fetch": "^3.0.0", + "isomorphic-ws": "^5.0.0", + "strict-event-emitter-types": "^2.0.0", + "ws": "^7.0.0" + } + }, + "node_modules/@open-rpc/client-js/node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "optional": true, + "peerDependencies": { + "ws": "*" + } + }, "node_modules/@openzeppelin/contracts": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.8.3.tgz", @@ -1952,16 +2106,13 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", + "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==", "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@scure/bip32": { "version": "1.1.0", @@ -2124,6 +2275,12 @@ "antlr4ts": "^0.5.0-alpha.4" } }, + "node_modules/@suchipi/femver": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz", + "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==", + "optional": true + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -5357,6 +5514,15 @@ "npm": ">=3" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "optional": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -5597,16 +5763,16 @@ "dev": true }, "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=14.14" } }, "node_modules/fs-minipass": { @@ -7712,6 +7878,16 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "optional": true, + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -8513,6 +8689,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/near-workspaces/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "optional": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -11408,6 +11598,12 @@ "node": ">=10.0.0" } }, + "node_modules/strict-event-emitter-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", + "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==", + "optional": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -11529,6 +11725,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superstruct": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz", + "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12056,6 +12261,12 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/whatwg-fetch": { + "version": "3.6.18", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.18.tgz", + "integrity": "sha512-ltN7j66EneWn5TFDO4L9inYC1D+Czsxlrw2SalgjMmEMkLfA5SIZxEFdE6QtHFiiM6Q7WL32c7AkI3w6yxM84Q==", + "optional": true + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 01ac81e7..9eac68a8 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "dependencies": { "@axelar-network/axelar-cgp-solidity": "^4.5.0", "@axelar-network/axelar-gmp-sdk-solidity": "^5.0.0", - "@axelar-network/axelar-local-dev": "^2.0.5", + "@axelar-network/axelar-local-dev": "^2.1.0", "@axelar-network/axelarjs-sdk": "^0.12.8", "@openzeppelin/contracts": "^4.5.0", "axios": "^0.27.2", @@ -28,6 +28,7 @@ "config": "^3.3.9", "dotenv": "^16.0.2", "ethers": "^5.6.2", + "fs-extra": "^11.1.1", "uuid": "^8.3.2" }, "devDependencies": { @@ -41,6 +42,7 @@ "solidity-coverage": "^0.8.2" }, "optionalDependencies": { - "@axelar-network/axelar-local-dev-aptos": "^2.0.2" + "@axelar-network/axelar-local-dev-aptos": "^2.1.0", + "@axelar-network/axelar-local-dev-sui": "^2.1.0" } } diff --git a/scripts/libs/config.js b/scripts/libs/config.js index d1f4f43c..4916cc6c 100644 --- a/scripts/libs/config.js +++ b/scripts/libs/config.js @@ -2,4 +2,5 @@ const config = require('config'); module.exports = { enabledAptos: config.get('aptos.enabled'), + enabledSui: config.get('sui.enabled'), }; diff --git a/scripts/libs/execute.js b/scripts/libs/execute.js index e680a759..5e09e525 100644 --- a/scripts/libs/execute.js +++ b/scripts/libs/execute.js @@ -30,6 +30,27 @@ async function executeAptosExample(chains, args, wallet, example) { }); } +async function executeSuiExample(chains, args, wallet, example) { + const evmChain = getSourceChain(chains, args, example.sourceChain); + + evmChain.provider = getDefaultProvider(evmChain.rpc); + const connectedWallet = wallet.connect(evmChain.provider); + + // Initialize contracts to chain object. + deserializeContract(evmChain, connectedWallet); + + // Recover axelar contracts to chain object. + evmChain.gateway = new Contract(evmChain.gateway, AxelarGatewayContract.abi, connectedWallet); + evmChain.gasService = new Contract(evmChain.gasService, AxelarGasServiceContract.abi, connectedWallet); + const tokenAddress = await evmChain.gateway.tokenAddresses('aUSDC'); + evmChain.usdc = new Contract(tokenAddress, IERC20.abi, connectedWallet); + + await example.execute(evmChain, wallet, { + args, + }); +} + + /** * Execute an example script. The example script must have an `execute` function. * @param {*} env - The environment to execute on. @@ -153,4 +174,5 @@ function listenForGMPEvent(env, source, startBlockNumber) { module.exports = { executeAptosExample, executeEVMExample, + executeSuiExample, }; diff --git a/scripts/libs/start.js b/scripts/libs/start.js index c222e418..153cb34c 100644 --- a/scripts/libs/start.js +++ b/scripts/libs/start.js @@ -1,8 +1,10 @@ const { ethers } = require('ethers'); const { createAndExport } = require('@axelar-network/axelar-local-dev'); -const { enabledAptos } = require('./config'); +const { enabledAptos, enabledSui } = require('./config'); +const dns = require('dns'); const path = require('path'); const { EvmRelayer } = require('@axelar-network/axelar-local-dev/dist/relay/EvmRelayer'); +const { RelayerType } = require('@axelar-network/axelar-local-dev'); const evmRelayer = new EvmRelayer(); @@ -15,6 +17,9 @@ const relayers = { evm: evmRelayer }; * @param {*} chains - chains to start. All chains are started if not specified (Avalanche, Moonbeam, Polygon, Fantom, Ethereum). */ async function start(fundAddresses = [], chains = [], options = {}) { + // Set default DNS lookup order to ipv4 first to resolve this issue https://github.com/node-fetch/node-fetch/issues/1624 + dns.setDefaultResultOrder('ipv4first'); + if (enabledAptos) { const { AptosRelayer, createAptosNetwork } = require('@axelar-network/axelar-local-dev-aptos'); await initAptos(createAptosNetwork); @@ -22,6 +27,13 @@ async function start(fundAddresses = [], chains = [], options = {}) { evmRelayer.setRelayer('aptos', relayers.aptos); } + if (enabledSui) { + const { initSui } = require('@axelar-network/axelar-local-dev-sui'); + const { suiRelayer } = await initSui(process.env.SUI_URL, process.env.SUI_FAUCET_URL); + relayers.sui = suiRelayer; + evmRelayer.setRelayer(RelayerType.Sui, suiRelayer); + } + const pathname = path.resolve(__dirname, '../..', 'chain-config', 'local.json'); await createAndExport({ diff --git a/scripts/runExecute.js b/scripts/runExecute.js index 9d98de9c..bd4f4a91 100644 --- a/scripts/runExecute.js +++ b/scripts/runExecute.js @@ -1,6 +1,6 @@ 'use strict'; require('dotenv').config(); -const { executeEVMExample, executeAptosExample, checkEnv, getExamplePath, getWallet, getEVMChains } = require('./libs'); +const { executeEVMExample, executeSuiExample, executeAptosExample, checkEnv, getExamplePath, getWallet, getEVMChains } = require('./libs'); const exampleName = process.argv[2]; const env = process.argv[3]; @@ -30,4 +30,7 @@ if (exampleName.split('/')[0] === 'evm') { } else if (exampleName.split('/')[0] === 'aptos') { const chains = getEVMChains(env); executeAptosExample(chains, args, wallet, example); +} else if (exampleName.split('/')[0] === 'sui') { + const chains = getEVMChains(env); + executeSuiExample(chains, args, wallet, example); }