Skip to content

Commit

Permalink
Gateway: Interacting with Universal Apps (#433)
Browse files Browse the repository at this point in the history
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
fadeev and coderabbitai[bot] authored Aug 22, 2024
1 parent 46a1da6 commit d303316
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 77 deletions.
4 changes: 4 additions & 0 deletions src/pages/developers/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"title": "Universal EVM",
"description": "EVM enhanced with omnichain interoperability features, enabling the development of robust universal apps."
},
"chains": {
"title": "Connected Blockchains",
"description": "Use Gateway to make calls to and from universal apps, deposit and withdraw tokens."
},
"tutorials": {
"title": "Tutorials",
"description": "Step-by-step guides to help you master building on ZetaChain."
Expand Down
2 changes: 1 addition & 1 deletion src/pages/developers/apps/_meta.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"intro": {
"title": "Universal App Basics",
"description": "What is a universal app and what makes it different"
"description": "Build universal apps that can be called from any blockchain"
}
}
12 changes: 6 additions & 6 deletions src/pages/developers/apps/intro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ gain powerful omnichain capabilities.
Here's an example universal app:

```solidity
pragma solidity 0.8.7;
pragma solidity 0.8.26;
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/zContract.sol";
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/UniversalContract.sol";
contract UniversalApp is zContract {
contract UniversalApp is UniversalContract {
function onCrossChainCall(
zContext calldata context,
address zrc20,
Expand Down Expand Up @@ -146,8 +146,8 @@ choosing), the gas fees will be covered.
chains
- can trigger contract calls and token transfers to connected chains
- can automatically handle gas for cross-chain transactions
- are fully compatible with EVM chains (like Ethereum and BNB), Bitcoin.
Support for Polygon, Solana, Cosmos (through IBC) and other chains is coming
soon.
- are fully compatible with EVM chains (like Ethereum, BNB, and Polygon),
Bitcoin, Solana. Support for TON and Cosmos (through IBC) and other chains
is coming soon.
- Native gas and supported ERC-20 tokens are represented as ZRC-20 tokens. A
ZRC-20 token can be permissionlessly withdrawn back to its original chain.
18 changes: 18 additions & 0 deletions src/pages/developers/chains/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"zetachain": {
"title": "ZetaChain",
"description": "Make calls from universal apps and withdraw tokens to connected chains"
},
"evm": {
"title": "EVM Blockchains",
"description": "Make calls to universal apps and deposit tokens from Ethereum, BNB, Polygon, Base and more"
},
"solana": {
"title": "Solana",
"description": "Make calls to universal apps and deposit SOL from Solana"
},
"bitcoin": {
"title": "Bitcoin",
"description": "Make calls to universal apps and deposit BTC from Bitcoin"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ title: Call Universal Apps from Bitcoin

import { Alert } from "~/components/shared";

On Bitcoin the gateway is implemented as an TSS MPC account. The private key
securing the account is split into fragments and distributed among ZetaChain
observer-signer validators.

Bitcoin gateway supports:

- depositing BTC to a universal app or an account on ZetaChain
- depositing BTC and calling a universal app

## Overview

To deposit BTC into ZetaChain's Universal EVM (and optionally call a smart
Expand Down
124 changes: 124 additions & 0 deletions src/pages/developers/chains/evm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
To interact with universal apps from EVM chains (like Ethereum, BNB, Polygon,
etc.) 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

## Deposit Gas Tokens

To deposit tokens to an EOA or a universal contract call the `deposit` function
of the Gateway contract.

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

The `deposit` function is payable, which means it accepts native gas tokens (for
example, ETH on Ethereum), which will then be sent to a `receiver` on ZetaChain.

The `receiver` is 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, please, use the
`depositAndCall` function, instead.

After the deposit is processed, the receiver gets [ZRC-20
version](/developers/tokens/zrc20) of the deposited token, for example (ZRC-20
ETH).

## Deposit ERC-20 Tokens

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

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

Only [supported ERC-20 assets](/developers/tokens/zrc20) can be deposited. The
receiver gets ZRC-20 version of the deposited token (for example, ZRC-20
USDC.ETH).

The `amount` is the amount and `asset` is the token address of the ERC-20 that
is being deposited.

## Deposit Gas Tokens and Call a Universal App

To deposit tokens and call a universal app contract use the `depositAndCall`
function.

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

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

The `receiver` must be a universal app contract address.

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

`onCrossChainCall` receives:

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

When calling a universal app, the payload contains bytes passed to
`onCrossChainCall` as `message`. You don't need to pass a function selector in
the payload as the only function that can be called from a connected chain is
`onCrossChainCall`.

## Deposit ERC-20 Tokens and Call a Universal App

`depositAndCall` can also be used to call a universal app contract and send
ERC-20 tokens.

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

The `amount` is the amount and `asset` is the token address of the ERC-20 that
is being deposited.

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

## Call a Universal App

To call a universal app (without depositing tokens), use the `call` function.

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

## Revert Transactions

For information on `RevertOptions` refer to the [ZetaChain "Revert Transactions"
doc](/developers/chains/zetachain#revert-transactions).
9 changes: 9 additions & 0 deletions src/pages/developers/chains/solana.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
On Solana the gateway is implemented as a Solana program.

Solana gateway supports:

- depositing SOL to a universal app or an account on ZetaChain
- depositing SOL and calling a universal app
- depositing SPL tokens (coming soon)

https://github.com/zeta-chain/protocol-contracts-solana/
163 changes: 163 additions & 0 deletions src/pages/developers/chains/zetachain.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
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.

```solidity
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 ZETA Tokens

The `withdraw` function can also be used to withdraw ZETA tokens to a connected
chain.

```
withdraw(bytes memory receiver, uint256 amount, uint256 chainId, RevertOptions calldata revertOptions) external;
```

## 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.

```solidity
function withdrawAndCall(bytes memory receiver, uint256 amount, address zrc20, bytes calldata message, uint256 gasLimit, RevertOptions calldata revertOptions) external;
```

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.

## Withdraw ZETA Tokens and Call a Contract on Connected Chain

The `withdrawAndCall` function can also be used to withdraw ZETA tokens and make
a call from a universal app to a contract on a connected chain.

```solidity
withdrawAndCall(bytes memory receiver, uint256 amount, uint256 chainId, bytes calldata message, RevertOptions calldata revertOptions) external;
```

## Call a Contract on a Connected Chain

To call a contract on a connected chain (without withdrawing tokens), use the
`call` function.

```solidity
function call(bytes memory receiver, address zrc20, bytes calldata message, uint256 gasLimit, RevertOptions calldata revertOptions) external;
```

`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.

## 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 function
`hello(string)`.
- Arguments: The remaining data
(`0000000000000000000000000000000000000000000000000000000000000020...`) is the
ABI-encoded argument passed to the `hello(string)` function. Specifically:
`616c696365` represents the string `alice` in hexadecimal.

## Revert Transactions

The `RevertOptions` struct defines how assets should be handled in the event of
a cross-chain transaction (CCTX) reverting:

```solidity
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:

```solidity
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.
Loading

0 comments on commit d303316

Please sign in to comment.