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.
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
.
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.
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.
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.
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:
0xa777d0dc
corresponds 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
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
.