Build
Tutorials
Solana

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.

Ensure you have installed and configured the following before starting:

Start by creating a project and installing the necessary dependencies:

npx zetachain@latest new --project call
cd call
yarn

This command brings up the local development environment with ZetaChain and Solana:

npx zetachain localnet start --chains solana

Leave this running in one terminal window.

GATEWAY_ZETACHAIN=$(jq -r '.["31337"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ZETACHAIN
PRIVATE_KEY=$(jq -r '.private_keys[0]' ~/.zetachain/localnet/anvil.json) && echo $PRIVATE_KEY
MNEMONIC="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 $UNIVERSAL

Deposit SOL tokens from Solana to ZetaChain:

npx zetachain solana deposit \
  --recipient $UNIVERSAL \
  --mnemonic $MNEMONIC \
  --amount 0.01 \
  --chain-id 902

Call the deployed universal contract:

npx zetachain solana call \
  --recipient $UNIVERSAL \
  --mnemonic $MNEMONIC \
  --chain-id 902 \
  --types string \
  --values hello

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 hello

This command deposits tokens and triggers the universal contract function with the argument "hello".

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.

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_SPL
cd solana
anchor build
PROGRAM_ID=$(solana program deploy \
  --program-id setup/connected-keypair.json target/deploy/connected.so \
  --url localhost \
  --output json | jq -r .programId) && echo $PROGRAM_ID

After running this, you should see output indicating that the program was successfully deployed, such as:

Program Id: 9BjVGjn28E58LgSi547JYEpqpgRoo1TErkbyXiRSNDQy

Withdraw SOL and Call the Solana Program

PAYLOAD=$(npx zetachain solana encode \
  --connected $PROGRAM_ID \
  --data hello \
  --gateway 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d) && echo $PAYLOAD

Make 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