Build
Universal Assets
Universal Token

Universal Tokens are fully interoperable ERC-20 tokens that can be minted and transferred across any connected chain without wrapping or bridging. Each token retains its supply and metadata across chains, enabling true chain-agnostic fungibility and seamless use in multichain DeFi, payments, and governance systems.

Universal Tokens on ZetaChain are built on the standard OpenZeppelin ERC-20 (opens in a new tab) implementation and use UUPS upgradeable (opens in a new tab) proxy patterns, allowing developers to extend and upgrade token logic safely over time.

Create a new Universal Token project:

npx zetachain@next new --project token

Install dependencies:

cd token
yarn

Compile Contracts:

npx hardhat compile --force

You can upgrade your existing ERC-20 project to become a Universal Token by installing the official standard contracts package:

yarn add @zetachain/standard-contracts

Then, update your contract using the example implementation (opens in a new tab) as a referenceβ€”see the commented lines that include Universal Token-specific logic for ZetaChain integration.

This allows your token to support cross-chain minting, transfers, and persistent supply tracking across ZetaChain and connected EVM chains.

Deploy contracts on ZetaChain, Base and Ethereum.

ZETACHAIN_TOKEN=$(npx hardhat token:deploy \
  --network zeta_testnet \
  --uniswap-router 0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe \
  --name ZetaChainUniversalToken \
  --json | jq -r .contractAddress) && echo $ZETACHAIN_TOKEN
BASE_TOKEN=$(npx hardhat token:deploy \
  --network base_sepolia \
  --gateway 0x0c487a766110c85d301d96e33579c5b317fa4995 \
  --name EVMUniversalToken \
  --json | jq -r .contractAddress) && echo $BASE_TOKEN
ETHEREUM_TOKEN=$(npx hardhat token:deploy \
  --network sepolia_testnet \
  --gateway 0x0c487a766110c85d301d96e33579c5b317fa4995 \
  --name EVMUniversalToken \
  --json | jq -r .contractAddress) && echo $ETHEREUM_TOKEN

Connect Contracts

After deployment, link the contracts so they can trust each other for cross-chain communication. Use setConnected on ZetaChain to register Connected contracts by their ZRC-20 gas token (used to identify the chain):

npx hardhat token:set-connected \
  --contract $ZETACHAIN_TOKEN \
  --connected $BASE_TOKEN \
  --zrc20 $BASE_ZRC20 \
  --network zeta_testnet \
  --json
npx hardhat token:set-connected \
  --contract $ZETACHAIN_TOKEN \
  --connected $ETHEREUM_TOKEN \
  --zrc20 $ETHEREUM_ZRC20 \
  --network zeta_testnet

Then, on each connected chain, use setUniversal to point back to the Universal contract on ZetaChain:

npx hardhat token:set-universal \
  --contract $BASE_TOKEN \
  --universal $ZETACHAIN_TOKEN \
  --network base_sepolia
npx hardhat token:set-universal \
  --contract $ETHEREUM_TOKEN \
  --universal $ZETACHAIN_TOKEN \
  --network sepolia_testnet

This ensures only authorized contracts can send and receive token transfers across chains.

Mint on ZetaChain

npx hardhat token:mint \
  --contract $ZETACHAIN_TOKEN \
  --amount 10
  --network zeta_testnet

https://zetachain-testnet.blockscout.com/tx/0xc9f8e3a8b3e1f1e2511fae649d510f0ce483dd1b3c481c8b01f066a0ca342458 (opens in a new tab)

Transfer from ZetaChain to Base

Transfer the token from ZetaChain to Base. Gas amount (specified in ZETA) is an estimate. Unused tokens are refunded to the user.

Use ZRC-20 Base ETH as the destination address to specify the chain to which the tokens will be transferred.

npx hardhat token:transfer \
  --from $ZETACHAIN_TOKEN \
  --to $BASE_ZRC20 \
  --amount 10 \
  --network zeta_testnet \
  --gas-amount 5
πŸš€ Successfully transferred token.
πŸ“œ Contract address: 0x22a4E73909bAB067B05185879Cd7FAF335f41920
πŸ‘€ Amount: 10
πŸ”— Transaction hash: 0xe8b682b0501532154b49a852853bcdc22f843e23068c88c319699975ecef6cc2
β›½ Gas used: 500000

Outgoing cross-chain transaction from ZetaChain to Base:

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0xe8b682b0501532154b49a852853bcdc22f843e23068c88c319699975ecef6cc2 (opens in a new tab)

https://sepolia.basescan.org/tx/0xd48bb35c371570e7c3769d4ebd75f157ac4ddd4f081aedac1e9d4b6a452460db (opens in a new tab)

Transfer from Base to Ethereum

Let’s move the token again β€” this time from Base to Ethereum.

npx hardhat token:transfer \
  --from $BASE_TOKEN \
  --network base_sepolia \
  --to $ETHEREUM_ZRC20 \
  --amount 10 \
  --gas-amount 0.005
πŸš€ Successfully transferred token to the contract.
πŸ“œ Contract address: 0x7a72AE51CCfAda57B20f8C7d8b138d35E46a2D60
πŸ–Ό Token Contract address: 0x7a72AE51CCfAda57B20f8C7d8b138d35E46a2D60
πŸ†” Token ID: 269200511667900488584833727349313006688770271102
πŸ”— Transaction hash: 0x27c3ca27da7576e8b00ceb588c9aa5e5622dcf03273d22c58660268149c445a4
β›½ Gas used: 118428

Incoming cross-chain transaction from Base to ZetaChain:

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0xdb7b9a8ab3b7c59e0877f403a8ece258ffff2539ebbe4c19d2089f6657855374 (opens in a new tab)

Outgoing cross-chain transaction from ZetaChain to Ethereum:

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x686f3b46bde24ca54394a9f782a4517b42e4032a5be3ec7b715d13b6be1481f4 (opens in a new tab)

https://sepolia.etherscan.io/tx/0x14348a0acce19ae3e2bc52d1b7be71d6612a5454a0bfcda48323ace5a84781c8 (opens in a new tab)

https://github.com/zeta-chain/standard-contracts/tree/main/contracts/token (opens in a new tab)