Build
Connected Chains
ZetaChain

To make a call from a universal app to a contract on a connected chain or withdraw tokens, use the ZetaChain gateway.

The ZetaChain gateway supports:

  • Withdrawing ZRC-20 tokens as native gas or ERC-20 tokens to connected chains.
  • Withdrawing ZETA tokens to connected chains.
  • Withdrawing tokens and making a contract call on connected chains.
  • Calling contracts on connected chains.

To withdraw ZRC-20 tokens to an EOA or a contract on a connected chain, use the withdraw function of the gateway contract:

function withdraw(bytes memory receiver, uint256 amount, address zrc20, RevertOptions calldata revertOptions) external;

The receiver can be either an externally-owned account (EOA) or a contract on a connected chain. Even if the receiver is a smart contract with a standard receive function, the withdraw function will not trigger a contract call. If you need to withdraw and call a contract on a connected chain, use the withdrawAndCall function instead.

The receiver is of type bytes to accommodate different address formats used by various chains (e.g., Bech32 for Bitcoin). This type ensures the receiver address is chain-agnostic. When withdrawing to an EVM chain, ensure you convert address to bytes.

The amount specifies the quantity to withdraw, and zrc20 is the ZRC-20 address of the token being withdrawn.

You don’t need to specify the destination chain since each ZRC-20 token is tied to the chain from which it was deposited. A ZRC-20 token can only be withdrawn to its originating chain. For example, to withdraw ZRC-20 USDC.ETH to the BNB chain, you must first swap it to ZRC-20 USDC.BNB.

To withdraw ZRC-20 tokens and call a contract on a connected chain, use the withdrawAndCall function:

function withdrawAndCall(bytes memory receiver, uint256 amount, address zrc20, bytes calldata message, CallOptions calldata callOptions, RevertOptions calldata revertOptions) external;

This function withdraws tokens and makes a call to a contract on the connected chain identified by the zrc20 address. For instance, if ZRC-20 ETH is withdrawn, the call is made to a contract on Ethereum.

To call a contract on a connected chain without withdrawing tokens, use the call function:

function call(bytes memory receiver, address zrc20, bytes calldata message, CallOptions calldata callOptions, RevertOptions calldata revertOptions) external;

Here, zrc20 represents the ZRC-20 token address of the gas token for the destination chain. This address acts as an identifier for the target chain. For example, to call a contract on Ethereum, use the ZRC-20 ETH token address.

The CallOptions parameter specifies details for making calls to contracts on connected chains. It is used in both the call and withdrawAndCall functions:

struct CallOptions {
    uint256 gasLimit;
    bool isArbitraryCall;
}
  • gasLimit: The maximum gas the cross-chain contract call can consume. If the gas usage exceeds this limit, the transaction reverts.
  • isArbitraryCall: Determines whether the call is "arbitrary" (true) or "authenticated" (false).

An arbitrary call invokes any function on a connected chain but does not retain the original caller's identity—within the target contract, msg.sender is the Gateway address, not the originating universal contract. This is suitable for scenarios like token swaps, where the caller’s identity is unnecessary.

An authenticated call specifically targets the onCall function of a contract on the connected chain. Authentication is achieved because the onCall function receives the context.sender parameter, referencing the originating universal contract. This allows the target contract to verify and trust the initiating universal app, rejecting unauthorized calls.

For arbitrary calls (when isArbitraryCall is true) the message parameter in the withdrawAndCall and call functions contains the encoded function selector and arguments for the target contract:

  • Function Selector: The first 4 bytes of the Keccak-256 hash of the function signature.
  • Arguments: The remaining bytes, ABI-encoded according to Ethereum’s rules.

For example:

0xa777d0dc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005616c696365000000000000000000000000000000000000000000000000000000
  • Function Selector: 0xa777d0dc corresponds to hello(string).
  • Arguments: The remaining data represents the argument alice, encoded in hexadecimal (616c696365).

For authenticated calls the message is just ABI-encoded arguments (no function selector in the beginning, because authenticated calls are routed to a specific onCall function).

The RevertOptions struct specifies how assets are handled in case of a cross-chain transaction (CCTX) revert:

struct RevertOptions {
    address revertAddress;
    bool callOnRevert;
    address abortAddress;
    bytes revertMessage;
    uint256 onRevertGasLimit;
}

revertAddress is the address of an EOA or contract, which will receive the tokens if a cross-chain transaction reverts. If a zero address is specified, the tokens will be transferred the the msg.sender that initiated the Gateway call. It's recommended to always specify the revert address to make sure tokens are refunded to the correct address.

callOnRevert is a flag indicating whether a reverted cross-chain transaction should result in a contract call on the chain on which the Gateway call was made. If call on revert is true, the onRevert function of the revertAddress contract is called. If call on revert is false, then a reverted cross-chain transaction will make a token transfer to the revert address without a contract call.

abortAddress is the address to receive funds on ZetaChain if the CCTX aborts (this functionality is not implemented yet).

revertMessage is a message passed to the onRevert function inside the revert context.

onRevertGasLimit is a limit for executing the onRevert function.

For CCTXs from ZetaChain to a connected chain, revert execution is free, so the same amount that has been withdrawn will be returned back as revertContext.amount in ZRC-20 tokens.

For CCTXS from a connected chain to ZetaChain, however, gas fees for revert execution are deducted from the deposit amount. If a CCTX reverts, the protocol will swap a fraction of the deposit amount into ZRC-20 gas tokens of the chain on which the revert is to be executed. If the deposit amount is insufficient, the CCTX will abort and onRevert will not be called.

Gateway call (no asset call) from a connected chain to ZetaChain does not support reverts, because there are no assets to cover the revert gas costs.

Revertable Contracts

Contracts implementing the onRevert function must conform to this interface:

struct RevertContext {
    address asset;
    uint64 amount;
    bytes revertMessage;
}
 
interface Revertable {
    function onRevert(RevertContext calldata revertContext) external;
}

On a connected chain asset is an address of an ERC-20 deposited using Gateway's deposit or depositAndCall. If a gas token was deposited, the asset is a zero address.

On ZetaChain asset is an address of a ZRC-20 withdrawn using Gateway's withdraw or withdrawAndCall.