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.
Prerequisites
Ensure you have installed and configured the following before starting:
Clone the Example Project
Start by creating a project and installing the necessary dependencies:
npx zetachain@latest new --project call
cd call
yarnLaunch Localnet
This command brings up the local development environment with ZetaChain and Solana:
npx zetachain localnet start --chains solanaLeave this running in one terminal window.
Compile and Deploy an Example Universal Contract
GATEWAY_ZETACHAIN=$(jq -r '.["31337"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ZETACHAINPRIVATE_KEY=$(jq -r '.private_keys[0]' ~/.zetachain/localnet/anvil.json) && echo $PRIVATE_KEYMNEMONIC="grape subway rack mean march bubble carry avoid muffin consider thing street"In a new terminal window, compile and deploy the universal contract:
UNIVERSAL=$(forge create Universal \
--rpc-url http://localhost:8545 \
--private-key $PRIVATE_KEY \
--evm-version paris \
--broadcast \
--json \
--constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSALDeposit
Deposit SOL tokens from Solana to ZetaChain:
npx zetachain solana deposit \
--recipient $UNIVERSAL \
--mnemonic $MNEMONIC \
--amount 0.01 \
--chain-id 902Call
Call the deployed universal contract:
npx zetachain solana call \
--recipient $UNIVERSAL \
--mnemonic $MNEMONIC \
--chain-id 902 \
--types string \
--values helloDeposit and Call
Deposit tokens and simultaneously call the deployed universal contract:
npx zetachain solana deposit-and-call \
--recipient $UNIVERSAL \
--mnemonic $MNEMONIC \
--amount 0.01 \
--chain-id 902 \
--types string \
--values helloThis command deposits tokens and triggers the universal contract function with the argument "hello".
Withdraw Tokens to Solana
Withdraw tokens from ZetaChain back to Solana:
npx zetachain z withdraw \
--receiver DrexsvCMH9WWjgnjVbx1iFf3YZcKadupFmxnZLfSyotd \
--zrc20 0x777915D031d1e8144c90D025C594b3b8Bf07a08d \
--amount 0.1 \
--rpc http://localhost:8545 \
--gateway $GATEWAY_ZETACHAIN \
--private-key $PRIVATE_KEY--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:
USDC_SPL=$(jq -r '.["902"].contracts[] | select(.contractType == "userTokenAccountUSDC") | .address' ~/.zetachain/localnet/registry.json) && echo $USDC_SPLcd solanaanchor buildPROGRAM_ID=$(solana program deploy \
--program-id setup/connected-keypair.json target/deploy/connected.so \
--url localhost \
--output json | jq -r .programId) && echo $PROGRAM_IDAfter running this, you should see output indicating that the program was successfully deployed, such as:
Program Id: 9BjVGjn28E58LgSi547JYEpqpgRoo1TErkbyXiRSNDQyWithdraw SOL and Call the Solana Program
PAYLOAD=$(npx zetachain solana encode \
--connected $PROGRAM_ID \
--data hello \
--gateway 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d) && echo $PAYLOADMake a call to the ZetaChain Gateway to withdraw SOL and call a program on Solana:
npx zetachain z withdraw-and-call \
--amount 0.001 \
--receiver 9BjVGjn28E58LgSi547JYEpqpgRoo1TErkbyXiRSNDQy \
--data $PAYLOAD \
--private-key $PRIVATE_KEY \
--rpc http://localhost:8545 \
--zrc20 0x777915D031d1e8144c90D025C594b3b8Bf07a08d \
--gateway $GATEWAY_ZETACHAIN