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 tokens and making a contract call on connected chains.
- Calling contracts on connected chains.
Note: Withdrawing ZETA tokens is currently not supported and will revert with
ZETANotSupported().
Withdraw ZRC-20 Tokens
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.
When withdrawing to non-EVM chains make sure to encode the receiver address to
bytes as string, meaning you take an address as a string of characters
and convert it into bytes.
For example, if the receiver address on Solana is:
GBwCxLUt5qn12aCD4uVKMWnoXPn2DoH126p8FrFmGNUyThe receiver bytes should be:
0x47427743784c557435716e31326143443475564b4d576e6f58506e32446f4831323670384672466d474e5579The amount specifies the quantity to withdraw, and zrc20 is the ZRC-20
address of the token being withdrawn.
The revertOptions.revertMessage must not exceed 1024 bytes in length.
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.
Withdraw ZRC-20 Tokens and Call a Contract on a Connected Chain
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.
The combined length of message and revertOptions.revertMessage must not
exceed 1024 bytes.
Call a Contract on a Connected Chain
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 combined length of message and revertOptions.revertMessage must not
exceed 1024 bytes.
Call Options
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.
Format of the message Parameter
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:
0xa777d0dccorresponds tohello(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).
Revert Transactions
When a cross-chain transaction (CCTX) fails, ZetaChain uses the RevertOptions struct to determine how to handle the failure. The behavior depends on the direction of the transaction — whether it's from a connected chain to ZetaChain or from ZetaChain to a connected chain.
Connected Chain → ZetaChain (Incoming)
This scenario happens when a contract on a connected chain sends tokens or a
message to ZetaChain using the depositAndCall or call function on the
Gateway.
- A contract on a connected chain calls
depositAndCallorcallon the Gateway. - The Gateway forwards the call to a universal contract on ZetaChain.
- If the
onCallfunction reverts, the protocol initiates the revert process.
Revert Behavior:
-
If the
amountsent with the original call is enough to cover revert gas fees:- ZetaChain swaps part of the amount into gas tokens (ZRC-20) for the connected chain.
- The protocol sends the remaining tokens and revert message to the
revertAddresson the connected chain. - If
callOnRevertistrue, the Gateway invokes theonRevertfunction.
-
If the
amountis insufficient or zero (for example, when it's a no-assetcall) the Gateway callsonAborton theabortAddresson ZetaChain.
ZetaChain → Connected Chain (Outgoing)
This scenario occurs when a universal contract on ZetaChain calls
withdrawAndCall or call on the Gateway to interact with a contract on a
connected chain.
Flow:
- A universal contract on ZetaChain initiates a call to a connected chain via
withdrawAndCallorcall. - The Gateway forwards the message and/or tokens to the target contract on the connected chain.
- If the target contract reverts, ZetaChain initiates the revert process.
Revert Behavior:
-
If
callOnRevertistrue:- The Gateway invokes the
onRevertfunction on therevertAddresson ZetaChain. - The remaining ZRC-20 tokens are passed along with the revert context.
- If the
onRevertcall itself reverts, the Gateway transfers ZRC-20 tokens to and callsonAborton theabortAddresson ZetaChain.
- The Gateway invokes the
-
If
callOnRevertisfalse:- The Gateway transfers tokens to the
revertAddresswithout invoking any function.
- The Gateway transfers tokens to the
The RevertOptions struct specifies how assets are handled in case of a
cross-chain transaction (CCTX) revert.
RevertOptions Struct
struct RevertOptions {
address revertAddress;
bool callOnRevert;
address abortAddress;
bytes revertMessage;
uint256 onRevertGasLimit;
}revertAddress: The address that receives tokens or revert logic. If revert address is zero, reverted tokens are transferred to the original sender of the call.callOnRevert: Whether the Gateway should callonRevert.abortAddress: Address to call ifonCallreverts (for a no-asset call) or reverting fails (for an asset call)revertMessage: Message passed toonRevertandonAbort.onRevertGasLimit: Max gas allowed foronRevert. Determines the amount of tokens that will be used
onRevert
struct RevertContext {
address asset;
uint64 amount;
bytes revertMessage;
}
interface Revertable {
function onRevert(RevertContext calldata revertContext) external;
}- On a connected chain,
assetis the ERC-20 originally deposited (or zero address for gas assets). - On ZetaChain,
assetis the ZRC-20 withdrawn during the original call.
onAbort
struct AbortContext {
bytes sender;
address asset;
uint256 amount;
bool outgoing;
uint256 chainID;
bytes revertMessage;
}
interface Abortable {
function onAbort(AbortContext calldata abortContext) external;
}- Called on ZetaChain as a fallback when revert execution fails.
- Used in both incoming and outgoing transactions.
Summary
| Direction | Trigger | Revert Path | Fallback if Revert Fails |
|---|---|---|---|
| Connected → ZetaChain | onCall() in universal contract fails | onRevert() on connected chain (if funded) | onAbort() on ZetaChain |
| ZetaChain → Connected | Target contract on connected chain fails | onRevert() on ZetaChain (if callOnRevert) | onAbort() on ZetaChain |