diff --git a/src/pages/developers/chains/solana.mdx b/src/pages/developers/chains/solana.mdx index 8475c573e..3cdfe0a19 100644 --- a/src/pages/developers/chains/solana.mdx +++ b/src/pages/developers/chains/solana.mdx @@ -133,3 +133,6 @@ The Solana Gateway provides a seamless way to interact with universal applications on ZetaChain from Solana. By using the Gateway instruction, you can deposit SOL and make cross-chain contract calls to universal applications on ZetaChain. + +To learn more about using the Solana Gateway in practice to deposit to and call +universal apps, check out [the Solana tutorial](/developers/tutorials/solana). diff --git a/src/pages/developers/tutorials/_meta.json b/src/pages/developers/tutorials/_meta.json index 6d64d4828..7def65f30 100644 --- a/src/pages/developers/tutorials/_meta.json +++ b/src/pages/developers/tutorials/_meta.json @@ -14,16 +14,21 @@ "readTime": "30 min", "description": "Learn the fundamentals of message passing and cross-chain contract calls" }, + "solana": { + "title": "Solana", + "readTime": "60 min", + "description": "Deposit assets and call universal apps from Solana, make outgoing calls to Solana" + }, + "sui": { + "title": "Sui", + "readTime": "60 min", + "description": "Deposit assets and call universal apps from Sui" + }, "swap": { "title": "Swap", "readTime": "30 min", "description": "Implement a universal swap app compatible with chains like Ethereum, Solana and Bitcoin" }, - "nft": { - "title": "NFT", - "readTime": "60 min", - "description": "Mint a universal NFT, which can be transferred between connected chains" - }, "localnet": { "title": "Localnet Setup", "readTime": "30 min", diff --git a/src/pages/developers/tutorials/solana.mdx b/src/pages/developers/tutorials/solana.mdx new file mode 100644 index 000000000..4104db48e --- /dev/null +++ b/src/pages/developers/tutorials/solana.mdx @@ -0,0 +1,187 @@ +Building universal applications with ZetaChain and Solana is easy. You can +deposit SOL and SPL-20 tokens directly from Solana into accounts or smart +contracts on ZetaChain. Universal contracts on ZetaChain can handle these +deposits and execute contract calls initiated from the Solana blockchain. + +- In this tutorial, you'll: +- Set up a local development environment using localnet. +- Deploy a universal contract on ZetaChain. +- Deposit tokens (SOL and SPL-20) from Solana to ZetaChain. +- Execute deposit-and-call transactions, depositing tokens and calling a + universal app simultaneously. +- Withdraw tokens back to Solana, optionally calling Solana programs as part of + the withdrawal. + +Interactions with universal apps from Solana are handled by the Solana Gateway +program, [learn more about it in the docs](/developers/chains/solana). + +## Prerequisites + +Ensure you have installed and configured the following before starting: + +- [`solana` CLI](https://solana.com/docs/intro/installation) + +## Clone the Example Project + +```bash +git clone https://github.com/zeta-chain/example-contracts +cd example-contracts/examples/call +yarn +``` + +## Launch Localnet + +This command brings up the local development environment with ZetaChain and +Solana: + +```bash +npx hardhat localnet +``` + +Leave this running in one terminal window. + +## Compile and Deploy an Example Universal Contract + +In a new terminal window, compile and deploy the universal contract: + +```bash +npx hardhat compile --force +npx hardhat deploy --name Universal --network localhost --gateway 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 +``` + +You will see a confirmation with your contract address: + +``` +πŸš€ Successfully deployed "Universal" contract on localhost. +πŸ“œ Contract address: 0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7 +``` + +Keep track of the contract address for later (in this example, +`0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7`). + +## Deposit + +Deposit SOL tokens from Solana to ZetaChain: + +``` +npx hardhat localnet:solana-deposit \ + --receiver 0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7 \ + --amount 0.1 +``` + +--receiver: Universal contract address on ZetaChain. + +--amount: Amount of SOL to deposit. + +## Deposit and Call + +Deposit tokens and simultaneously call the deployed universal contract: + +```bash +npx hardhat localnet:solana-deposit-and-call \ + --receiver 0x8198f5d8F8CfFE8f9C413d98a0A55aEB8ab9FbB7 \ + --amount 0.1 \ + --types '["string"]' hello +``` + +This command deposits tokens and triggers the universal contract function with +the argument "hello". + +## Withdraw Tokens to Solana + +Withdraw tokens from ZetaChain back to Solana: + +```bash +npx hardhat zetachain-withdraw \ + --gateway-zeta-chain 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 \ + --receiver DrexsvCMH9WWjgnjVbx1iFf3YZcKadupFmxnZLfSyotd \ + --network localhost \ + --zrc20 0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891 \ + --amount 0.1 +``` + +- `--gateway-zeta-chain`: Address of the ZetaChain gateway. +- `--receiver`: A Solana wallet address to receive the withdrawn tokens. +- `--zrc20`: The ZetaChain representation of the token you want to withdraw + (ZRC-20 address). +- `--amount`: The amount to withdraw. + +## Withdraw and Call a Program on Solana + +Beyond simply withdrawing tokens from ZetaChain back to Solana, you can also +execute a Solana program as part of the withdrawal process. This allows for more +complex interactions, such as triggering on-chain logic immediately upon +receiving funds. For example, you can withdraw SOL or SPL-20 tokens and call a +Solana program in a single transaction, enabling use cases like automatic +staking, swaps, or contract executions. + +The `solana` directory contains an example Solana program with an `on_call` +function, which can be invoked by a universal app on ZetaChain during the +withdrawal process. + +The following steps will guide you through setting up an example Solana program +and using the ZetaChain Gateway to perform a "withdraw and call". + +### Build and Set Up the Example Solana Program + +Set the SPL-20 USDC address. You can find this address in a table in the output +of `localnet`: + +```bash +USDC_SPL=3Kx5SY7SwzdUZSorLVSpPgxBL8DZFiu8mg4FWduu2tQp +``` + +```bash +cd solana && anchor build && npx ts-node setup/main.ts "$USDC_SPL" && cd - +``` + +After running this, you should see output indicating that the program was +successfully deployed, such as: + +``` +Connected program deployment output: Program Id: 9BjVGjn28E58LgSi547JYEpqpgRoo1TErkbyXiRSNDQy +``` + +### Withdraw SOL and Call the Solana Program + +Make a call to the ZetaChain Gateway to withdraw SOL and call a program on +Solana: + +```bash +npx hardhat zetachain-withdraw-and-call \ + --receiver 9BjVGjn28E58LgSi547JYEpqpgRoo1TErkbyXiRSNDQy \ + --gateway-zeta-chain 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 \ + --zrc20 0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891 \ + --amount 0.01 \ + --network localhost \ + --types '["bytes"]' $(npx ts-node solana/setup/encodeCallArgs.ts "sol" "$USDC_SPL") +``` + +- `--receiver`: The Solana program ID you want to call. +- `--types '["bytes"]'`: Specifies that the contract call argument is a bytes + array (the encoded Solana call data). +- `"$ENCODED_ACCOUNTS_AND_DATA"`: The encoded Solana program call arguments we + generated in the previous step. +- `--zrc20`: ZRC-20 address of SOL on ZetaChain. + +### Withdraw SPL-20 and Call the Solana Program + +Make a call to the ZetaChain Gateway to withdraw ZRC-20 SPL-20 USDC and call a +program on Solana: + +```bash +npx hardhat zetachain-withdraw-and-call \ + --receiver 9BjVGjn28E58LgSi547JYEpqpgRoo1TErkbyXiRSNDQy \ + --gateway-zeta-chain 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 \ + --zrc20 0x48f80608B672DC30DC7e3dbBd0343c5F02C738Eb \ + --amount 0.01 \ + --network localhost \ + --types '["bytes"]' $(npx ts-node solana/setup/encodeCallArgs.ts "spl" "$USDC_SPL") +``` + +- `--zrc20`: ZRC-20 address of SPL-20 USDC on ZetaChain. + +In this tutorial you’ve learned how to deploy a universal contract on ZetaChain, +deposit SOL and SPL-20 tokens from Solana, call contracts during deposits, +withdraw assets back to Solana, and even trigger Solana programs upon +withdrawal. diff --git a/src/pages/developers/tutorials/sui.mdx b/src/pages/developers/tutorials/sui.mdx new file mode 100644 index 000000000..bb6054d77 --- /dev/null +++ b/src/pages/developers/tutorials/sui.mdx @@ -0,0 +1,210 @@ +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](/developers/tutorials/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](https://docs.sui.io/references/cli): Required for starting a local + Sui node and interacting with it. +- [Foundry](https://getfoundry.sh/): Needed for encoding payload data using ABI. + +## Clone the Example Project + +Begin by cloning the example contracts repository and installing its +dependencies: + +```bash +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: + +```bash +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: + +```bash +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. + +```toml filename="sui/Move.toml" +[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](https://docs.soliditylang.org/en/develop/abi-spec.html). Call +Foundry's `cast` (or any other tool) to encode a string.