Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
dc76d03
add example contract
lumtis Apr 8, 2025
5075844
add example
lumtis Apr 9, 2025
f3d230c
add ptb building for withdraw and call
lumtis Apr 9, 2025
df06a9b
lint
lumtis Apr 9, 2025
ff0e859
changelogs
lumtis Apr 9, 2025
a6732de
add todos
lumtis Apr 9, 2025
c6412cd
Merge branch 'develop' of https://github.com/zeta-chain/node into sui…
ws4charlie Apr 10, 2025
c20b6e3
make e2e sui setup cleaner; deploy example package
ws4charlie Apr 14, 2025
b956ae3
Merge branch 'develop' of https://github.com/zeta-chain/node into sui…
ws4charlie Apr 14, 2025
c63161f
add sui Example package information to E2E runner
ws4charlie Apr 15, 2025
eeee8cf
sui withdraw and call e2e using example contract; refactor sui signer…
ws4charlie Apr 16, 2025
10827dc
use example package id as receiver address in e2e; fix missing logger…
ws4charlie Apr 17, 2025
b0f2762
uncomment sui e2e tests; remove redundant debug log print
ws4charlie Apr 17, 2025
d964e45
remove redundant log print; use hardcoded version to replicate Invali…
ws4charlie Apr 17, 2025
0e376ef
fix PTB withdraw and call InvalidBCSBytes and other execution errors
ws4charlie Apr 17, 2025
80aa52e
make Sui withdraw and call E2E test working
ws4charlie Apr 18, 2025
1d8d7ab
Merge branch 'develop' into sui-ptb
ws4charlie Apr 21, 2025
e3e995e
Merge branch 'develop' into sui-ptb
ws4charlie Apr 21, 2025
dfb2c38
improve sui withdraw and call e2e test
ws4charlie Apr 21, 2025
351f607
simplify json integer extraction; fix code generate; improve comment
ws4charlie Apr 21, 2025
3b165a8
improve readability of function getWithdrawAndCallObjectRefs
ws4charlie Apr 21, 2025
8c58f15
move PTB argument related function to pkg contracts
ws4charlie Apr 21, 2025
6189872
add pkg contracts for sui withdraw and call
ws4charlie Apr 22, 2025
57ff9ae
fix gosec
ws4charlie Apr 22, 2025
a4123de
simplify the code for sui contract fields assignment
ws4charlie Apr 22, 2025
385b6ab
use actual gas budget in the CCTX for PTB transaction
ws4charlie Apr 22, 2025
ad9e246
Merge branch 'develop' of https://github.com/zeta-chain/node into sui…
ws4charlie Apr 22, 2025
1cfda65
get latest version of TSS SUI coin object for gas payment
ws4charlie Apr 22, 2025
ee2268c
improve naming and add additional sui contract pkg unit test
ws4charlie Apr 22, 2025
b047c7a
add unit test to ptb tx builder; split ptb tx builder into small func…
ws4charlie Apr 22, 2025
7007c0a
Merge branch 'develop' of https://github.com/zeta-chain/node into sui…
ws4charlie Apr 22, 2025
60642a2
fix unit test compile
ws4charlie Apr 23, 2025
c032afa
add withdraw and call signer test
ws4charlie Apr 23, 2025
2cc71fa
Merge branch 'develop' into sui-ptb
ws4charlie Apr 23, 2025
b77955d
use a struct to hold the config of Sui example package; a name revert
ws4charlie Apr 23, 2025
06e75d6
use the r.chain.method pattern for SUI contract deployment related fu…
ws4charlie Apr 23, 2025
65086b3
use hardcoded address in the E2E test to receive withdawAndCall funds
ws4charlie Apr 23, 2025
0adb35a
add unit test for gateway pure method extractInteger
ws4charlie Apr 23, 2025
edc687f
return error if SUI coin object version is invalid
ws4charlie Apr 23, 2025
c61476e
use error wrapping; remove TODO
ws4charlie Apr 23, 2025
eb06d0a
removed TODO in sui e2e withdraw and call test
ws4charlie Apr 23, 2025
003d155
clean redundant comment; use name suiptb for pattonkan sui go library…
ws4charlie Apr 23, 2025
5516185
unexport typeSeparator; switch return type from pointer to value; rem…
ws4charlie Apr 23, 2025
8a9ce13
create a MoveCall struct to hold parsed packageID, module and functio…
ws4charlie Apr 24, 2025
31a92f2
use ptb as prefix for tx building functions; pass gasBudget param as …
ws4charlie Apr 24, 2025
dc0951b
move withdrawAndCallPTB as Signer method; group withdrawAndCallPTB ar…
ws4charlie Apr 24, 2025
63e4bd6
move getWithdrawAndCallObjectRefs to a Signer method
ws4charlie Apr 24, 2025
fcb377f
add Makefile to rebuild example package and update compiled mv files
ws4charlie Apr 24, 2025
05986c4
ensure the on_call method gets called in withdraw and call E2E test
ws4charlie Apr 24, 2025
2376ca9
cancel withdrawAndCall if on_call object is not shared or immutable
ws4charlie Apr 24, 2025
bd2f5da
add sui withdrawAndCall revert E2E test
ws4charlie Apr 24, 2025
b96c4d4
add readme file to briefly describe how sui withdrawAndCall works usi…
ws4charlie Apr 24, 2025
1d6a126
Merge branch 'develop' into sui-ptb
ws4charlie Apr 24, 2025
e2a9995
fix CI unit test failure
ws4charlie Apr 24, 2025
356092d
Merge branch 'develop' into sui-ptb
swift1337 Apr 25, 2025
177b255
name and log improvement
ws4charlie Apr 25, 2025
08cf305
Merge branch 'develop' into sui-ptb
ws4charlie Apr 25, 2025
d557692
move GetSuiCoinObjectRef RPC call into getWithdrawAndCallObjectRefs; …
ws4charlie Apr 25, 2025
85449be
fix CI unit test
ws4charlie Apr 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* [3790](https://github.com/zeta-chain/node/pull/3790) - integrate execute revert
* [3797](https://github.com/zeta-chain/node/pull/3797) - integrate execute SPL revert
* [3807](https://github.com/zeta-chain/node/pull/3807) - integrate ZEVM to Solana call
* [3793](https://github.com/zeta-chain/node/pull/3793) - support Sui withdrawAndCall using the PTB transaction

### Refactor

Expand Down
1 change: 1 addition & 0 deletions cmd/zetae2e/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C

conf.Contracts.Sui.FungibleTokenCoinType = config.DoubleQuotedString(r.SuiTokenCoinType)
conf.Contracts.Sui.FungibleTokenTreasuryCap = config.DoubleQuotedString(r.SuiTokenTreasuryCap)
conf.Contracts.Sui.Example = r.SuiExample

conf.Contracts.EVM.ZetaEthAddr = config.DoubleQuotedString(r.ZetaEthAddr.Hex())
conf.Contracts.EVM.ConnectorEthAddr = config.DoubleQuotedString(r.ConnectorEthAddr.Hex())
Expand Down
3 changes: 2 additions & 1 deletion cmd/zetae2e/config/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,15 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error {
if suiPackageID != "" && suiGatewayID != "" {
r.SuiGateway = sui.NewGateway(suiPackageID.String(), suiGatewayID.String())
}

if c := conf.Contracts.Sui.FungibleTokenCoinType; c != "" {
r.SuiTokenCoinType = c.String()
}
if c := conf.Contracts.Sui.FungibleTokenTreasuryCap; c != "" {
r.SuiTokenTreasuryCap = c.String()
}
r.SuiExample = conf.Contracts.Sui.Example

// set EVM contracts
evmChainID, err := r.EVMClient.ChainID(r.Ctx)
require.NoError(r, err, "get evm chain ID")
evmChainParams := chainParamsByChainID(chainParams, evmChainID.Int64())
Expand Down
7 changes: 3 additions & 4 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
e2etests.TestSuiWithdrawName,
e2etests.TestSuiWithdrawRevertWithCallName,
e2etests.TestSuiTokenWithdrawName,
// https://github.com/zeta-chain/node/issues/3742
e2etests.TestSuiWithdrawAndCallName,
e2etests.TestSuiWithdrawAndCallRevertWithCallName,
e2etests.TestSuiDepositRestrictedName,
e2etests.TestSuiWithdrawRestrictedName,

// TODO: enable withdraw and call test
// https://github.com/zeta-chain/node/issues/3742
//e2etests.TestSuiWithdrawAndCallName,
}
eg.Go(suiTestRoutine(conf, deployerRunner, verbose, suiTests...))
}
Expand Down
11 changes: 11 additions & 0 deletions e2e/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,23 @@ type TON struct {
GatewayAccountID DoubleQuotedString `yaml:"gateway_account_id"`
}

// SuiExample contains the object IDs in the example package
type SuiExample struct {
PackageID DoubleQuotedString `yaml:"package_id"`
TokenType DoubleQuotedString `yaml:"token_type"`
GlobalConfigID DoubleQuotedString `yaml:"global_config_id"`
PartnerID DoubleQuotedString `yaml:"partner_id"`
ClockID DoubleQuotedString `yaml:"clock_id"`
PoolID DoubleQuotedString `yaml:"pool_id"`
}

// Sui contains the addresses of predeployed contracts on the Sui chain
type Sui struct {
GatewayPackageID DoubleQuotedString `yaml:"gateway_package_id"`
GatewayObjectID DoubleQuotedString `yaml:"gateway_object_id"`
FungibleTokenCoinType DoubleQuotedString `yaml:"fungible_token_coin_type"`
FungibleTokenTreasuryCap DoubleQuotedString `yaml:"fungible_token_treasury_cap"`
Example SuiExample `yaml:"example"`
}

// EVM contains the addresses of predeployed contracts on the EVM chain
Expand Down
16 changes: 16 additions & 0 deletions e2e/contracts/sui/bin.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ var fakeUSDC []byte
//go:embed evm.mv
var evmBinary []byte

//go:embed token.mv
var tokenBinary []byte

//go:embed connected.mv
var connectedBinary []byte

// GatewayBytecodeBase64 gets the gateway binary encoded as base64 for deployment
func GatewayBytecodeBase64() string {
return base64.StdEncoding.EncodeToString(gatewayBinary)
Expand All @@ -28,3 +34,13 @@ func FakeUSDCBytecodeBase64() string {
func EVMBytecodeBase64() string {
return base64.StdEncoding.EncodeToString(evmBinary)
}

// ExampleFungibleTokenBytecodeBase64 gets the example package's fungible token binary encoded as base64 for deployment
func ExampleFungibleTokenBytecodeBase64() string {
return base64.StdEncoding.EncodeToString(tokenBinary)
}

// ExampleConnectedBytecodeBase64 gets the example package's connected binary encoded as base64 for deployment
func ExampleConnectedBytecodeBase64() string {
return base64.StdEncoding.EncodeToString(connectedBinary)
}
Binary file added e2e/contracts/sui/connected.mv
Binary file not shown.
1 change: 1 addition & 0 deletions e2e/contracts/sui/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/*
22 changes: 22 additions & 0 deletions e2e/contracts/sui/example/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.PHONY: clean build all

# Default target
all: clean build

# Clean build directory
clean:
rm -rf build/

# Build the package and generate bytecode
build:
sui move build
cp build/example/bytecode_modules/token.mv .
cp build/example/bytecode_modules/connected.mv .

# Help target
help:
@echo "Available targets:"
@echo " all - Clean and build everything (default)"
@echo " clean - Remove build directory"
@echo " build - Build the package and generate bytecode"
@echo " help - Show this help message"
58 changes: 58 additions & 0 deletions e2e/contracts/sui/example/Move.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# @generated by Move, please check-in and do not edit manually.

[move]
version = 3
manifest_digest = "795BC4DE1AC42C1015B85547951A89D7F9AD63EA50982960E095E815C560BFCC"
deps_digest = "397E6A9F7A624706DBDFEE056CE88391A15876868FD18A88504DA74EB458D697"
dependencies = [
{ id = "Bridge", name = "Bridge" },
{ id = "DeepBook", name = "DeepBook" },
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
{ id = "SuiSystem", name = "SuiSystem" },
]

[[move.package]]
id = "Bridge"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "9c04e1840eb5", subdir = "crates/sui-framework/packages/bridge" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
{ id = "SuiSystem", name = "SuiSystem" },
]

[[move.package]]
id = "DeepBook"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "9c04e1840eb5", subdir = "crates/sui-framework/packages/deepbook" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
]

[[move.package]]
id = "MoveStdlib"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "9c04e1840eb5", subdir = "crates/sui-framework/packages/move-stdlib" }

[[move.package]]
id = "Sui"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "9c04e1840eb5", subdir = "crates/sui-framework/packages/sui-framework" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
]

[[move.package]]
id = "SuiSystem"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "9c04e1840eb5", subdir = "crates/sui-framework/packages/sui-system" }

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
{ id = "Sui", name = "Sui" },
]

[move.toolchain-version]
compiler-version = "1.45.0"
edition = "2024.beta"
flavor = "sui"
37 changes: 37 additions & 0 deletions e2e/contracts/sui/example/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "example"
edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move
# license = "" # e.g., "MIT", "GPL", "Apache 2.0"
# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"]

[dependencies]
#Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.41.1" }

# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`.
# Revision can be a branch, a tag, and a commit hash.
# MyRemotePackage = { git = "https://some.xwremote/host.git", subdir = "remote/path", rev = "main" }

# For local dependencies use `local = path`. Path is relative to the package root
# Local = { local = "../path/to" }

# To resolve a version conflict and force a specific version for dependency
# override use `override = true`
# Override = { local = "../conflicting/version", override = true }

[addresses]
example = "0x0"

# Named addresses will be accessible in Move as `@name`. They're also exported:
# for example, `std = "0x1"` is exported by the Standard Library.
# alice = "0xA11CE"

[dev-dependencies]
# The dev-dependencies section allows overriding dependencies for `--test` and
# `--dev` modes. You can introduce test-only dependencies here.
# Local = { local = "../path/to/dev-build" }

[dev-addresses]
# The dev-addresses section allows overwriting named addresses for the `--test`
# and `--dev` modes.
# alice = "0xB0B"

69 changes: 69 additions & 0 deletions e2e/contracts/sui/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SUI WithdrawAndCall with PTB Transactions

This document explains how the SUI `withdrawAndCall` functionality works using Programmable Transaction Blocks (PTB) in the ZetaChain protocol.

## Overview

The `withdrawAndCall` operation in ZetaChain allows users to withdraw tokens from ZEVM to the Sui blockchain and simultaneously calls a `on_call` function in the `connected` module on the Sui side.

This is implemented as a single atomic transaction using Sui's Programmable Transaction Blocks (PTB).

## Transaction Flow

1. **User Initiates Withdrawal**: A user initiates a withdrawal from ZEVM to Sui with a `on_call` payload.

2. **ZEVM Processing**: The ZEVM gateway processes the withdrawal request and prepares the transaction.

3. **PTB Construction**: A Programmable Transaction Block is constructed with the following steps:
- **Withdraw**: The first command in the PTB is the `withdraw_impl` function call, which:
- Verifies the withdrawal parameters
- Withdraw and returns two coin objects: the main withdrawn coins and the gas budget coins
- **Gas Budget Transfer**: The second command transfers the gas budget coins to the TSS address to cover transaction fees.
- The gas budget is the SUI coin withdrawn from sui vault, together with withdrawn CCTX's coinType.
- The gas budget needs to be forwarded to TSS address to cover the transaction fee.
- **Connected Module Call**: The third command calls the `on_call` function in the connected module, passing:
- The withdrawn coins
- The call payload from the user
- Any additional parameters required by the connected module

4. **Transaction Execution**: The entire PTB is executed atomically on the Sui blockchain.

## PTB Structure

The PTB for a `withdrawAndCall` transaction consists of three main commands:

```
PTB {
// Command 0: Withdraw Implementation
MoveCall {
package: gateway_package_id,
module: gateway_module,
function: withdraw_impl,
arguments: [
gateway_object_ref,
withdraw_cap_object_ref,
coin_type,
amount,
nonce,
gas_budget
]
}

// Command 1: Gas Budget Transfer
TransferObjects {
from: withdraw_impl_result[1], // Gas budget coins
to: tss_address
}

// Command 2: Connected Module Call
MoveCall {
package: target_package_id,
module: connected_module,
function: on_call,
arguments: [
withdraw_impl_result[0], // Main withdrawn coins
on_call_payload
]
}
}
```
Binary file added e2e/contracts/sui/example/connected.mv
Binary file not shown.
66 changes: 66 additions & 0 deletions e2e/contracts/sui/example/sources/example.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module example::connected;

use sui::address::from_bytes;
use sui::coin::Coin;

// stub for shared objects
public struct GlobalConfig has key {
id: UID,
called_count: u64,
}

public struct Partner has key {
id: UID,
}

public struct Clock has key {
id: UID,
}

public struct Pool<phantom CoinA, phantom CoinB> has key {
id: UID,
}

// share objects
fun init(ctx: &mut TxContext) {
let global_config = GlobalConfig {
id: object::new(ctx),
called_count: 0,
};
let pool = Pool<sui::sui::SUI, example::token::TOKEN> {
id: object::new(ctx),
};
let partner = Partner {
id: object::new(ctx),
};
let clock = Clock {
id: object::new(ctx),
};

transfer::share_object(global_config);
transfer::share_object(pool);
transfer::share_object(partner);
transfer::share_object(clock);
}

public entry fun on_call<SOURCE_COIN, TARGET_COIN>(
in_coins: Coin<SOURCE_COIN>,
cetus_config: &mut GlobalConfig,
_pool: &mut Pool<SOURCE_COIN, TARGET_COIN>,
_cetus_partner: &mut Partner,
_clock: &Clock,
data: vector<u8>,
_ctx: &mut TxContext,
) {
let receiver = decode_receiver(data);

// transfer the coins to the provided address
transfer::public_transfer(in_coins, receiver);

// increment the called count
cetus_config.called_count = cetus_config.called_count + 1;
}

fun decode_receiver(data: vector<u8>): address {
from_bytes(data)
}
29 changes: 29 additions & 0 deletions e2e/contracts/sui/example/sources/token.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module example::token;

use sui::coin::{Self, TreasuryCap};

public struct TOKEN has drop {}

fun init(witness: TOKEN, ctx: &mut TxContext) {
let (treasury, metadata) = coin::create_currency(
witness,
6,
b"TOKEN",
b"",
b"",
option::none(),
ctx,
);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury, ctx.sender())
}

public entry fun mint(
treasury_cap: &mut TreasuryCap<TOKEN>,
amount: u64,
recipient: address,
ctx: &mut TxContext,
) {
let coin = coin::mint(treasury_cap, amount, ctx);
transfer::public_transfer(coin, recipient)
}
Binary file added e2e/contracts/sui/example/token.mv
Binary file not shown.
Binary file added e2e/contracts/sui/token.mv
Binary file not shown.
Loading