Build
Tutorials
Connector: ERC-20

Cross-Chain ERC-20

In this tutorial you will learn how to create a contract capable of minting ERC-20 tokens and sending them to a contract on a different chain using cross-chain messaging.

Set up your environment

git clone https://github.com/zeta-chain/template
cd template
yarn

Create the Contract

To create a new cross-chain messaging contract you will use the messaging Hardhat task available by default in the template.

npx hardhat messaging CrossChainERC20 to:address value:uint256
  • to: address of the recipient on the destination chain
  • value: the amount of ERC-20 tokens transferred

Cross-Chain Messaging Contract

Let's review and make changes to the contract:

contracts/CrossChainERC20.sol

By default the generated contract is a standard cross-chain messaging contract with functions to send a message, message handler and revert handler.

Let's modify the contract to make it ERC-20 compatible.

To enable ERC-20 token features, import the necessary OpenZeppelin contracts: ERC20 for standard token functionality and ERC20Burnable to enable burning tokens.

In the contract's constructor, initialize the ERC-20 token by setting a name and a symbol using the ERC20 constructor. Then, pre-mint an initial supply of tokens to the deploying address by calling the _mint function.

In the sendMessage function, ensure the sender has sufficient tokens to send by checking their balance. If they have enough, burn the required amount from their balance using _burn. Add msg.sender to the message by passing it as a third argument to abi.decode. This is necessary in case the transaction is reverted, so the sender can be reimbursed.

To handle the cross-chain message, decode the incoming message in onZetaMessage and mint tokens to the intended recipient using the _mint function.

In the event of a message revert, decode the original message in onZetaRevert to identify the sender and the amount. Reimburse the sender by minting the tokens back to their account with _mint. Use the from address, as this is the original sender who should receive the reimbursement.

Deploy the Contract

Clear the cache and artifacts, then compile the contract:

npx hardhat compile --force

Run the following command to deploy the contract to two networks:

npx hardhat deploy --networks sepolia_testnet,bsc_testnet
🚀 Successfully deployed contract on sepolia_testnet
📜 Contract address: 0x18B6f0aB98429F00eDD44D5900090D71e3747e10

🚀 Successfully deployed contract on bsc_testnet
📜 Contract address: 0x7eA3054e5086FeE43D745975d171d3a850A94304

🔗 Setting interactors for a contract on sepolia_testnet
✅ Interactor address for 97 (bsc_testnet) is set to 0x7ea3054e5086fee43d745975d171d3a850a94304

🔗 Setting interactors for a contract on bsc_testnet
✅ Interactor address for 11155111 (sepolia_testnet) is set to 0x18b6f0ab98429f00edd44d5900090d71e3747e10

Send an ERC-20 Token

Use the interact task to call the sendMessage method of the contract and send ERC-20 tokens from Sepolia to BSC Testnet

npx hardhat interact --to 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32 --value 1000000000 --contract 0x18B6f0aB98429F00eDD44D5900090D71e3747e10 --network sepolia_testnet --amount 0.01 --destination bsc_testnet
🔑 Using account: 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32

✅ The transaction has been broadcasted to sepolia_testnet
📝 Transaction hash: 0x25cfda972e255480e15b47472ae6c8d8842201855776fedaa446f75cab7a0e86

You can check the broadcasted transaction on Sepolia's Etherscan:

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

Next, you can track the progress of the cross-chain transaction:

npx hardhat cctx 0x25cfda972e255480e15b47472ae6c8d8842201855776fedaa446f75cab7a0e86
✓ CCTXs on ZetaChain found.

✓ 0x258d3ad9adf92ac5b7d59354435adfadac0af6430737f388ebf2664c22c71158: 11155111 → 97: OutboundMined

Source Code

You can find the source code for the example in this tutorial here:

https://github.com/zeta-chain/example-contracts/tree/main/messaging/erc20 (opens in a new tab)