Interacting with ZetaChain universal apps directly from the Sui blockchain is straightforward and intuitive. You can deposit SUI tokens to ZetaChain addresses—both externally owned accounts (EOAs) and smart contracts. Universal apps deployed on ZetaChain can accept these token deposits and handle contract calls originating from Sui.
In this tutorial, you'll:
- Set up a local development environment using localnet.
- Deploy a universal contract on ZetaChain.
- Deposit SUI tokens from Sui to ZetaChain.
- Execute deposit-and-call to deposit SUI tokens and call a universal app in a single transaction.
By the end, you'll clearly understand how Sui-based applications and contracts can integrate seamlessly with the ZetaChain ecosystem.
Prerequisites
Ensure you have installed and configured the following tools before starting:
- Sui CLI (opens in a new tab): Required for starting a local Sui node and interacting with it.
- Foundry (opens in a new tab): Needed for encoding payload data using ABI.
Clone the Example Project
Begin by cloning the example contracts repository and installing its dependencies:
git clone https://github.com/zeta-chain/example-contracts
cd example-contracts/examples/call
yarn
Launch Localnet
Start your local development environment, which sets up instances of ZetaChain and Sui:
npx hardhat localnet
Keep this terminal window open. You should see a table displaying the deployment details, including Gateway module and object IDs:
SUI
┌─────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│ (index) │ Values │
├─────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ gatewayModuleID │ '0xc478ce1cbbeb090889a0e728af4fd25d80993a653b5f03090dbf77e5a9476bcd' │
│ gatewayObjectId │ '0x332483152b600f388b64adb4d98509ded379167df6047c49a564a4ed62f8bb71' │
│ userMnemonic │ 'grape subway rack mean march bubble carry avoid muffin consider thing street' │
│ userAddress │ '0x2fec3fafe08d2928a6b8d9a6a77590856c458d984ae090ccbd4177ac13729e65' │
└─────────────────┴────────────────────────────────────────────────────────────────────────────────┘
Deploying a Universal Contract
Open a new terminal window to compile and deploy your Universal Contract:
npx hardhat compile --force
npx hardhat deploy --name Universal --network localhost --gateway 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707
A successful deployment will output:
🚀 Successfully deployed "Universal" contract on localhost.
📜 Contract address: 0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7
Deposit to ZetaChain
To deposit tokens from your local Sui Gateway contract to ZetaChain, use:
npx hardhat localnet:sui-deposit \
--mnemonic "grape subway rack mean march bubble carry avoid muffin consider thing street" \
--gateway 0x332483152b600f388b64adb4d98509ded379167df6047c49a564a4ed62f8bb71 \
--module 0xc478ce1cbbeb090889a0e728af4fd25d80993a653b5f03090dbf77e5a9476bcd \
--receiver 0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7 \
--amount 100000000
In this example, a client makes a call on the Sui blockchain to the local Gateway contract. The local development environment (Localnet) observes this transaction similarly to how ZetaChain would in a live scenario, and as a result, deposits ZRC-20 SUI tokens to the specified receiver address on ZetaChain.
Deposit and Call
Execute a deposit and simultaneously make a function call to a universal contract on ZetaChain:
npx hardhat localnet:sui-deposit-and-call \
--mnemonic "grape subway rack mean march bubble carry avoid muffin consider thing street" \
--gateway 0x332483152b600f388b64adb4d98509ded379167df6047c49a564a4ed62f8bb71 \
--module 0xc478ce1cbbeb090889a0e728af4fd25d80993a653b5f03090dbf77e5a9476bcd \
--receiver 0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7 \
--amount 1000000 \
--types '["string"]' alice
In this example, a call is made from a client on the Sui blockchain to the local Gateway contract. The local development environment (Localnet) observes this call, deposits ZRC-20 SUI tokens to the specified receiver address on ZetaChain, and then triggers the receiver contract on ZetaChain with the provided payload (alice).
Build and Deploy a Sui Contract
cd sui
The example is already configured to import the Gateway from a local directory, from which it's published on localnet. On testnet and mainnet the Gateway will be imported from a public repo, instead.
[dependencies]
gateway = { local = "/usr/local/share/localnet/protocol-contracts-sui"}
Build:
sui move build --force
Deploy:
sui client publish \
--skip-dependency-verification \
--json 2>/dev/null | jq -r '.objectChanges[] | select(.type == "published") | .packageId'
0xdefdceb18b96981b3635688ec93838845eb3534991d312b7562e2c1c6e4367a0
We're using jq
to get the published module ID, but you can skip this to see
the whole output.
Get Coins
Get a list of coins for the currently active address:
sui client objects \
$(sui client active-address) \
--json | jq -r '.[] | select(.data.type == "0x2::coin::Coin<0x2::sui::SUI>") | .data.objectId'
0x561e2c0f84601d7a97bf57ed3b4258bbd60741a3ebecb5865e6dd92833e33c58
0x7589bdfab5ccd5bc9ce40eefeb883c9fd817fbe74c89613e4d48fbcd90b12cf6
0x7b18cbdb427965e851e6a128e43744f3452898c82605d5c7a4db681c9a7ffb91
0xbd5270f4b2f90786cf9ab56b03baff734dad7e10a1288e9d0f4e188e37ed5209
0xe0736dace410531fe970f03e45bb2797be0bcce083091f6b774c2ccc3132921c
Deposit from a Sui Contract
sui client call \
--package 0xdefdceb18b96981b3635688ec93838845eb3534991d312b7562e2c1c6e4367a0 \
--module connected \
--function deposit \
--type-args 0x2::sui::SUI \
--args 0x332483152b600f388b64adb4d98509ded379167df6047c49a564a4ed62f8bb71 \
0x561e2c0f84601d7a97bf57ed3b4258bbd60741a3ebecb5865e6dd92833e33c58 \
0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7
--package
: package ID of the Sui contract deployed in the previous step--args
:- Gateway Object ID from the table in the output of localnet
- One of the coins owned by the current address
- Recipient address on ZetaChain
Deposit and Call a Universal Contract from Sui Contract
sui client call \
--package 0xdefdceb18b96981b3635688ec93838845eb3534991d312b7562e2c1c6e4367a0 \
--module connected \
--function deposit_and_call \
--type-args 0x2::sui::SUI \
--args 0x332483152b600f388b64adb4d98509ded379167df6047c49a564a4ed62f8bb71 \
0x7589bdfab5ccd5bc9ce40eefeb883c9fd817fbe74c89613e4d48fbcd90b12cf6\
0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7 \
$(cast abi-encode "function(string)" "alice")
Note that in this example we're calling the deposit_and_call
function and
passing one more argument: the payload. ZetaChain does not impose any specific
encoding on the payload, but since you're calling an EVM universal contract on
ZetaChain, it's easier to use ABI
encoding (opens in a new tab). Call
Foundry's cast
(or any other tool) to encode a string.