To make a call from a universal app to a contract on a connected chain or to withdraw tokens, use the ZetaChain gateway.
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 to 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, call the
withdraw
function of the gateway contract.
function withdraw(bytes memory receiver, uint256 amount, address zrc20, RevertOptions calldata revertOptions) external;
The receiver
is either an externally-owned account (EOA) or a contract on a
connected chain. Even if the receiver is a smart contract with the standard
receive
function, the withdraw
function will not trigger a contract call. If
you want to withdraw and call a contract on a connected chain, please, use the
withdrawAndCall
function, instead.
The receiver
is of type bytes
, because the receiver may be on a chain that
uses a different address type, for example, bech32 on Bitcoin. bytes
allow the
receiver address to be chain agnostic. When withdrawing to a receiver on an EVM
chain make sure that you convert address
to bytes
.
The amount
is the amount and zrc20
is the ZRC-20 address of the token that
is being withdrawn.
You don't need to specify which chain to withdraw to, because each ZRC-20 has an associated chain from which it was deposited. A ZRC-20 token can be withdrawn only to the chain from which it was originally deposited. This means that if you want to withdraw ZRC-20 USDC.ETH to the BNB chain, you first have to swap it to ZRC-20 USDC.BNB.
Withdraw ZRC-20 Tokens and Call a Contract on Connected Chain
To withdraw ZRC-20 tokens and make a call from a universal app to a contract on
a connected chain use the withdrawAndCall
function of the gateway contract.
function withdrawAndCall(bytes memory receiver, uint256 amount, address zrc20, bytes calldata message, CallOptions calldata callOptions, RevertOptions calldata revertOptions)
The tokens are withdrawn and a call is made to a contract on the connected chain
identified by the zrc20
address. For example, if ZRC-20 ETH is being
withdrawn, then 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)
zrc20
represents the ZRC-20 token address of the gas token of the destination
chain. In the context of this function zrc20
address acts as an identifier for
the chain to which the call is made. For example, to make a call to Ethereum,
use ZRC-20 ETH token address.
Call Options
The CallOptions
parameter provides details for making calls to contracts on
connected chains. CallOptions
is applied in both call
and withdrawAndCall
functions.
struct CallOptions {
uint256 gasLimit;
bool isArbitraryCall;
}
gasLimit
specifies the maximum amount of gas that a cross-chain contract call
can consume. If the gas usage exceeds this limit, the cross-chain transaction
will revert.
isArbitraryCall
determines whether the call is "arbitrary" (true
) or
"authenticated" (false
).
An arbitrary call can invoke any function of any contract on a connected chain.
For instance, a universal app might call a hello(string)
function on an
Ethereum contract. However, an arbitrary call does not retain the original
caller's identity—msg.sender
within the called contract will point to the
Gateway address rather than the universal contract that initiated the call. This
lack of caller identification is often acceptable for operations like a swap in
a withdrawAndCall
, where the swap function only needs to perform the token
swap and send it to the designated address, without needing to verify the
caller.
An authenticated call targets the onCall
function on a connected chain’s
contract. Here, "authentication" is achieved because one of the onCall
parameters, context.sender
, directly references the originating universal
contract. This enables the called contract to verify and establish a trusted
connection with the initiating universal contract, rejecting calls from any
unauthorized third-party contracts.
Format of the message
when Calling Contracts
The withdrawAndCall
and call
functions have a bytes calldata message
parameter. This parameter contains the function selector and the encoded
arguments necessary to call a specific function in the target contract.
The message parameter should contain:
- Function selector: the first 8 bytes represent the function selector, which is the first 4 bytes of the Keccak-256 hash of the function signature.
- Arguments: the remaining bytes in the message correspond to the arguments passed to the function, encoded according to Ethereum's ABI encoding rules. These arguments can vary in length depending on the data types.
For example, consider the following message:
0xa777d0dc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005616c696365000000000000000000000000000000000000000000000000000000
- Function Selector:
0xa777d0dc
. This corresponds to the functionhello(string)
. - Arguments: The remaining data
(
0000000000000000000000000000000000000000000000000000000000000020...
) is the ABI-encoded argument passed to thehello(string)
function. Specifically:616c696365
represents the stringalice
in hexadecimal.
Revert Transactions
The RevertOptions
struct defines how assets should be handled in the event of
a cross-chain transaction (CCTX) reverting:
struct RevertOptions {
address revertAddress;
bool callOnRevert;
address abortAddress;
bytes revertMessage;
uint256 onRevertGasLimit;
}
revertAddress
: the address that should get assets back if the CCTX reverts.
For example, if a smart contract using ZetaChain wants to send assets back to
the sender upon a revert, revertAddress
should be set to msg.sender
.
callOnRevert
: a boolean flag indicating whether the onRevert
function should
be called on the revertAddress
. For example, a smart contract may want to
execute custom logic when a revert occurs, such as unlocking tokens. In this
case, the contract would set callOnRevert
to true and assign revertAddress
to address(this)
.
abortAddress
: the address that will receive the funds on ZetaChain if the CCTX
aborts. This feature is not currently used.
revertMessage
: message sent back to the onRevert
function. This allows
additional context to be provided for handling the revert.
onRevertGasLimit
: the gas limit to be used when executing the onRevert
function.
Contracts that implement the onRevert
functionality are referred to as
Revertable
contracts. These contracts should conform to the following
interface:
struct RevertContext {
address asset;
uint64 amount;
bytes revertMessage;
}
interface Revertable {
function onRevert(RevertContext calldata revertContext) external;
}
This interface allows the contract to handle reverts in a customized way, based
on the context provided through the RevertContext
struct.