Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/pages/developers/standards/nft.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,11 @@ RPC_BASE=$(zetachain q chains show --chain-id 84532 -f rpc)
RPC_ZETACHAIN=$(zetachain q chains show --chain-id 7001 -f rpc)

ZRC20_ETHEREUM=$(zetachain q tokens show -s ETH.ETHSEP -f zrc20)
ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20)
ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20)

GATEWAY_ETHEREUM=0x0c487a766110c85d301d96e33579c5b317fa4995
GATEWAY_BASE=0x0c487a766110c85d301d96e33579c5b317fa4995
GATEWAY_ZETACHAIN=0x6c533f7fe93fae114d0954697069df33c9b74fd7
UNISWAP_ROUTER=0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe

GAS_LIMIT=1000000
```
Expand All @@ -80,8 +79,6 @@ NFT_ZETACHAIN=$(npx tsx commands deploy \
--rpc $RPC_ZETACHAIN \
--private-key $PRIVATE_KEY \
--name ZetaChainUniversalNFT \
--uniswap-router $UNISWAP_ROUTER \
--gateway $GATEWAY_ZETACHAIN \
--gas-limit $GAS_LIMIT | jq -r .contractAddress) && echo $NFT_ZETACHAIN
```

Expand Down
5 changes: 1 addition & 4 deletions src/pages/developers/standards/token.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,11 @@ RPC_BASE=$(zetachain q chains show --chain-id 84532 -f rpc)
RPC_ZETACHAIN=$(zetachain q chains show --chain-id 7001 -f rpc)

ZRC20_ETHEREUM=$(zetachain q tokens show -s ETH.ETHSEP -f zrc20)
ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20)
ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20)

GATEWAY_ETHEREUM=0x0c487a766110c85d301d96e33579c5b317fa4995
GATEWAY_BASE=0x0c487a766110c85d301d96e33579c5b317fa4995
GATEWAY_ZETACHAIN=0x6c533f7fe93fae114d0954697069df33c9b74fd7
UNISWAP_ROUTER=0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe

GAS_LIMIT=1000000
```
Expand All @@ -79,8 +78,6 @@ ZETACHAIN_TOKEN=$(npx tsx commands deploy \
--rpc $RPC_ZETACHAIN \
--private-key $PRIVATE_KEY \
--name ZetaChainUniversalToken \
--uniswap-router $UNISWAP_ROUTER \
--gateway $GATEWAY_ZETACHAIN \
--gas-limit $GAS_LIMIT | jq -r .contractAddress) && echo $ZETACHAIN_TOKEN
```

Expand Down
17 changes: 8 additions & 9 deletions src/pages/developers/tutorials/call.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,13 @@ function onCall(

### Making outgoing calls

To call a contract on a connected chain from your Universal App, you must first
approve the Gateway to spend the gas token:
To call a contract on a connected chain from your Universal App, first quote the
destination gas fee for your intended gas limit, pull that fee from the caller,
and approve the Gateway to spend it:

```solidity
(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(callOptions.gasLimit);
IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee);
IZRC20(zrc20).approve(address(gateway), gasFee);
```

Expand Down Expand Up @@ -267,7 +270,6 @@ your connected EVM testnet (for example, Base Sepolia) and the Gateway addresses
for each chain.

```bash
GATEWAY_ZETACHAIN=0x6c533f7fe93fae114d0954697069df33c9b74fd7
GATEWAY_BASE=0x0c487a766110c85d301d96e33579c5b317fa4995

RPC_ZETACHAIN=https://zetachain-athens-evm.blockpi.network/v1/rpc/public
Expand All @@ -281,8 +283,7 @@ UNIVERSAL=$(forge create Universal \
--rpc-url $RPC_ZETACHAIN \
--private-key $PRIVATE_KEY \
--broadcast \
--json \
--constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSAL
--json | jq -r .deployedTo) && echo $UNIVERSAL
```

### Deploy Connected to Base Sepolia
Expand Down Expand Up @@ -451,10 +452,9 @@ from the localnet registry:

```bash
RPC=http://localhost:8545
ZRC20_ETHEREUM=$(jq -r '."11155112".chainInfo.gasZRC20' ~/.zetachain/localnet/registry.json) && echo $ZRC20_ETHEREUM
ZRC20_ETHEREUM=$(jq -r '."11155112".zrc20Tokens[] | select(.coinType == "gas" and .originChainId == "11155112") | .address' ~/.zetachain/localnet/registry.json) && echo $ZRC20_ETHEREUM
PRIVATE_KEY=$(jq -r '.private_keys[0]' ~/.zetachain/localnet/anvil.json) && echo $PRIVATE_KEY
GATEWAY_ETHEREUM=$(jq -r '.["11155112"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ETHEREUM
GATEWAY_ZETACHAIN=$(jq -r '.["31337"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ZETACHAIN
```

Deploy the Universal App:
Expand All @@ -464,8 +464,7 @@ UNIVERSAL=$(forge create Universal \
--rpc-url $RPC \
--private-key $PRIVATE_KEY \
--broadcast \
--json \
--constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSAL
--json | jq -r .deployedTo) && echo $UNIVERSAL
```

Deploy the Connected contract:
Expand Down
58 changes: 7 additions & 51 deletions src/pages/developers/tutorials/hello.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,10 @@ A Universal App is a contract that implements the `UniversalContract` interface.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";
import "@zetachain/protocol-contracts/contracts/zevm/interfaces/UniversalContract.sol";

contract Universal is UniversalContract {
GatewayZEVM public immutable gateway;

event HelloEvent(string, string);
error Unauthorized();

modifier onlyGateway() {
if (msg.sender != address(gateway)) revert Unauthorized();
_;
}

constructor(address payable gatewayAddress) {
gateway = GatewayZEVM(gatewayAddress);
}

function onCall(
MessageContext calldata context,
Expand All @@ -86,10 +74,6 @@ contract Universal is UniversalContract {
}
```

The constructor takes ZetaChain’s Gateway address and stores it in a state
variable. The Gateway is used for making outbound contract calls and token
withdrawals.

A universal contract must implement the `onCall` function. This function is
triggered when the contract receives a call from a connected chain via the
Gateway. The function processes incoming data, which includes:
Expand All @@ -109,7 +93,8 @@ In this example, `onCall` decodes the message into a string and emits an event.

`onCall` should only be called by the Gateway to ensure that it is only called
as a response to a call on a connected chain and that you can trust the values
of the function parameters.
of the function parameters. This is enforced by the `onlyGateway` modifier,
which is inherited from `UniversalContract`.

## Option 1: Deploy on Localnet

Expand Down Expand Up @@ -160,48 +145,21 @@ The `forge build` command tells Foundry to compile all Solidity smart contracts
within your project, ensuring you're working with the latest compiled versions.
Successful compilation will generate bytecode for your contracts.

To deploy and interact with contracts on the ZetaChain localnet, you'll need the
address of the ZetaChain Gateway contract. This contract acts as the entry point
for cross-chain interactions on ZetaChain.

- While your localnet is still running in its dedicated terminal, carefully
examine its output.
- Copy the Gateway contract address from the localnet terminal output. Look for
the row labeled `gateway` under the `ZETACHAIN` section. It will typically
appear in a format similar to this:

```
| gateway │ '0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6' |
```

Copy only the hexadecimal address (e.g.,
`0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6`). Do not include the single quotes
or any other surrounding text.

Alternatively, run the following command to get the Gateway address
programmatically:

```
GATEWAY_ZETACHAIN=$(jq -r '.["31337"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ZETACHAIN
```

Fetch a private key with pre-funded tokens on the connected chain:

```
PRIVATE_KEY=$(jq -r '.private_keys[0]' ~/.zetachain/localnet/anvil.json) && echo $PRIVATE_KEY
```

Deploy the universal contract and provide the Gateway address in the
constructor:
Deploy the universal contract:

```
UNIVERSAL=$(forge create Universal \
--rpc-url http://localhost:8545 \
--private-key $PRIVATE_KEY \
--evm-version paris \
--broadcast \
--json \
--constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSAL
--json | jq -r .deployedTo) && echo $UNIVERSAL
```

### Make a Call to the Universal App
Expand Down Expand Up @@ -263,16 +221,14 @@ string.

### Deploy the Contract on ZetaChain

Deploy the contract to ZetaChain’s testnet using the Gateway address from the
[Contract Addresses page](/reference/network/contracts/):
Deploy the contract to ZetaChain’s testnet:

```
UNIVERSAL=$(forge create Universal \
--rpc-url https://zetachain-athens-evm.blockpi.network/v1/rpc/public \
--private-key $PRIVATE_KEY \
--broadcast \
--json \
--constructor-args 0x6c533f7fe93fae114d0954697069df33c9b74fd7 | jq -r .deployedTo)
--json | jq -r .deployedTo)
```

### Call a Universal Contract from Base
Expand Down
13 changes: 10 additions & 3 deletions src/pages/developers/tutorials/swap.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ The Swap contract performs the following steps:

3. Queries the withdrawal gas fee required to send the target token back to the
destination chain.
4. Swaps a portion of the incoming tokens for ZRC-20 gas tokens to cover the
withdrawal fee using Uniswap v2 pools.
4. If withdrawing to a connected chain, swaps a portion of the incoming tokens
for ZRC-20 gas tokens to cover the withdrawal fee using Uniswap v2 pools.
5. Swaps the remaining balance into the target token.
6. Withdraws the swapped tokens to the recipient on the destination chain.

Expand Down Expand Up @@ -93,7 +93,7 @@ to perform token swaps across blockchains with a single cross-chain call. Tokens
are received as ZRC-20s, optionally swapped using Uniswap v2 liquidity, and
withdrawn back to a connected chain.

### Universal App entrypoint: on_call
### Universal App entrypoint: onCall

The contract is deployed on ZetaChain and implements `UniversalContract`,
exposing a single entrypoint. Cross-chain deliveries are executed only via the
Expand Down Expand Up @@ -145,6 +145,10 @@ gateway.withdraw(
);
```

If the destination gas token (`gasZRC20`) is the same as the target token, the
contract approves a single combined allowance of `out + gasFee` for the Gateway
instead of two separate approvals.

### Funding destination execution from the user’s input

The app provisions destination gas out of the input, so users don’t need to
Expand Down Expand Up @@ -260,6 +264,9 @@ SwapHelperLib.swapExactTokensForTokens(
);
```

Note: If `inputToken == gasZRC20`, the contract uses `gasFee` directly (no swap
for gas is required) and only swaps the remaining balance to the target token.

You’re free to replace uniswapRouter and the helper calls with any DEX interface
or custom routing logic—only the ZRC-20 token flow and the Gateway withdraw
semantics are assumed by the rest of the contract.
Expand Down