Build
Connected Chains
EVM Blockchains

To interact with universal applications from EVM-compatible chains like Ethereum, BNB, Polygon, and others, use the EVM gateway.

EVM gateway supports:

  • Depositing gas tokens to a universal app or an account on ZetaChain.
  • Depositing supported ERC-20 tokens (including ZETA tokens).
  • Depositing gas tokens and calling a universal app.
  • Depositing supported ERC-20 tokens and calling a universal app.
  • Calling a universal app.

To deposit tokens to an EOA or a universal contract, call the deposit function of the Gateway contract:

deposit(address receiver, RevertOptions calldata revertOptions) external payable;

The deposit function is payable, meaning it accepts native gas tokens (e.g., ETH on Ethereum), which will then be sent to a receiver on ZetaChain.

The receiver can be either an externally-owned account (EOA) or a universal app address on ZetaChain. Even if the receiver is a universal app contract with the standard receive function, the deposit function will not trigger a contract call. If you want to deposit and call a universal app, use the depositAndCall function instead.

After the deposit is processed, the receiver receives the ZRC-20 version of the deposited token—for example, ZRC-20 ETH.

The deposit function can also be used to send supported ERC-20 tokens to EOAs and universal apps on ZetaChain:

deposit(address receiver, uint256 amount, address asset, RevertOptions calldata revertOptions) external;

Only supported ERC-20 assets can be deposited. The receiver gets the ZRC-20 version of the deposited token (e.g., ZRC-20 USDC.ETH).

The amount specifies the quantity, and asset is the token address of the ERC-20 being deposited.

To deposit tokens and call a universal app contract, use the depositAndCall function:

depositAndCall(address receiver, bytes calldata payload, RevertOptions calldata revertOptions) external payable;

After the cross-chain transaction is processed, the onCall function of the universal app contract is executed.

The receiver must be the address of a universal app contract.

pragma solidity 0.8.26;
 
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/UniversalContract.sol";
 
contract UniversalApp is UniversalContract {
    function onCall(
        MessageContext calldata context,
        address zrc20,
        uint256 amount,
        bytes calldata message
    ) external virtual override {
        // ...
    }
}

In the onCall function, the parameters are as follows:

  • message: the value of the payload.
  • amount: the amount of deposited tokens.
  • zrc20: the ZRC-20 address of the deposited tokens (e.g., the contract address of ZRC-20 ETH).
  • context:
    • context.sender: the sender address on the connected chain (the EOA or contract that called the Gateway).
    • context.chainID: the chain ID of the connected chain from which the call was made.

When calling a universal app, the payload is passed to onCall as message. You do not need to include a function selector in the payload, since onCall is the only function that can be called from a connected chain.

The depositAndCall function can also be used to call a universal app contract and send ERC-20 tokens:

depositAndCall(address receiver, uint256 amount, address asset, bytes calldata payload, RevertOptions calldata revertOptions) external;

Here, amount specifies the quantity, and asset is the token address of the ERC-20 being deposited.

In the current version of the protocol, only one ERC-20 asset can be deposited at a time.

To call a universal app without depositing tokens, use the call function:

call(address receiver, bytes calldata payload, RevertOptions calldata revertOptions) external;

The call function invokes the onCall function of the specified receiver universal contract and passes the payload as the message parameter.

The call function doesn't support revert handling. If revertOptions.callOnRevert is set to true, the transaction will fail. This is because executing a contract call on revert requires tokens to cover gas fees on ZetaChain, and the call function doesn't transfer any assets. If you need to handle reverts, use depositAndCall instead and ensure sufficient tokens are deposited to cover potential gas fees.

For information on RevertOptions, refer to the ZetaChain "Revert Transactions" documentation.