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
Create a new Universal Token project:
npx zetachain@next new --project token
Install dependencies:
cd token
yarn
Compile Contracts:
npx hardhat compile --force
Upgrade an Existing ERC-20 Project
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 on Testnet
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
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:
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:
Outgoing cross-chain transaction from ZetaChain to Ethereum:
Source Code
https://github.com/zeta-chain/standard-contracts/tree/main/contracts/token (opens in a new tab)