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 sui example #149

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 20 additions & 6 deletions .github/workflows/local-example-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
3 changes: 3 additions & 0 deletions config/ci.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"aptos": {
"enabled": true
},
"sui": {
"enabled": true
}
}
3 changes: 3 additions & 0 deletions config/default.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"aptos": {
"enabled": false
},
"sui": {
"enabled": true
}
}
38 changes: 38 additions & 0 deletions examples/sui/call-contract/README.md
Original file line number Diff line number Diff line change
@@ -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."
```
42 changes: 42 additions & 0 deletions examples/sui/call-contract/contracts/HelloWorld.sol
Original file line number Diff line number Diff line change
@@ -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();
}
}
90 changes: 90 additions & 0 deletions examples/sui/call-contract/index.js
Original file line number Diff line number Diff line change
@@ -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,
};
31 changes: 31 additions & 0 deletions examples/sui/call-contract/modules/Move.lock
Original file line number Diff line number Diff line change
@@ -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" },
]
11 changes: 11 additions & 0 deletions examples/sui/call-contract/modules/Move.toml
Original file line number Diff line number Diff line change
@@ -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"
63 changes: 63 additions & 0 deletions examples/sui/call-contract/modules/sources/hello_world.move
Original file line number Diff line number Diff line change
@@ -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<u8>,
destination_chain: vector<u8>,
destination_address: vector<u8>,
payload: vector<u8>,
}

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<u8>, destination_address: vector<u8>, payload: vector<u8>, _fee_amount: u64) {
gateway::call_contract(destination_chain, destination_address, payload);
}

public entry fun execute(_command_id: vector<u8>, _source_chain: String, _source_address: String, payload: vector<u8>, 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);
}
}
16 changes: 15 additions & 1 deletion examples/tests/checkExamples.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
});
}
});
});
Loading
Loading