HelloERC20
HelloERC20
is an example ERC20
token implemented in Solidity, designed for cross-chain operations without relying on traditional bridge mechanisms. Utilizing the Via Labs Solidity package and MessageClient extension, it demonstrates a bridgeless approach to native token minting and burning across different blockchain networks.
Prerequisites
Node.js and npm
# Install using APT
sudo apt update
sudo apt install nodejs npm
Git
# Install using APT
sudo apt install git
Testnet Tokens
- Ethereum Sepolia: Ethereum Testnet Faucet
- Polygon Amoy: Polygon Testnet Faucet
Installation
Open a terminal to run the following commands. You can use any terminal of your choice, including the built in terminal in (vscode) (Terminal -> New Terminal)
- Clone the Repository:
git clone https://github.com/VIALabs-io/hello-erc20.git
- Change into Project Directory:
cd hello-erc20
- Install Dependencies:
npm install
- Set Up Environment Variables:
Create a new.env
file to set your EVM private key for contract deployment or copy and edit the existing.env.example
to.env
PRIVATE_KEY=0000000000000000000000000000
Deployment
Deploy the HelloERC20
contract to your desired networks. This must be done for each network you wish to operate on. You can see a list of our networks in the Package documentation
- Ethereum Sepolia Testnet:
npx hardhat --network ethereum-sepolia deploy
- Polygon Testnet:
npx hardhat --network polygon-amoy deploy
Configuration
Once all contracts are deployed across the desired networks and listed in networks-testnet.json
, configure them using the provided script. Remember, if a new network is added later, all contracts must be reconfigured.
- Ethereum Sepolia Testnet:
npx hardhat --network ethereum-sepolia configure
- Polygon Testnet:
npx hardhat --network polygon-amoy configure
Usage
Checking Token Balance
To check the balance of tokens on a particular chain:
npx hardhat --network ethereum-sepolia get-token-balance
Bridging Tokens to Another Chain
To send tokens to another chain it is required to set the --dest
parameter to the destination chain id. The example below uses the id for the Polygon Testnet. Chain IDs can be looked up in the Package documentation.
npx hardhat --network ethereum-sepolia bridge-token --dest 80002 --amount 5
Check Tokens on Destination Chain
You can now check the balance on the destination chain. Please give the transaction some time to process. You should see your balance increase on the destination chain:
npx hardhat --network polygon-amoy get-token-balance
Contract Breakdown of HelloERC20
This contract is an ERC20
token with additional functionalities for cross-chain operations. It inherits from ERC20Burnable
for standard ERC20
functionality with burn capabilities and from MessageClient
for handling cross-chain messages.
pragma solidity 0.8.17;
import "@vialabs-io/contracts/message/MessageClient.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
contract HelloERC20 is ERC20Burnable, MessageClient {
constructor() ERC20("HelloERC20", "HELLO") {
_mint(msg.sender, 1_000_000 ether);
}
function bridge(uint _destChainId, address _recipient, uint _amount) external onlyActiveChain(_destChainId) {
// burn tokens
_burn(msg.sender, _amount);
// send cross chain message
_sendMessage(_destChainId, abi.encode(_recipient, _amount));
}
function messageProcess(uint, uint _sourceChainId, address _sender, address, uint, bytes calldata _data) external override onlySelf(_sender, _sourceChainId) {
// decode message
(address _recipient, uint _amount) = abi.decode(_data, (address, uint));
// mint tokens
_mint(_recipient, _amount);
}
}
Constructor
constructor() ERC20("HelloERC20", "HELLO") {
_mint(msg.sender, 1_000_000 ether);
}
- Initializes the token with the name "HelloERC20" and symbol "HELLO".
- Mints 1,000,000 tokens to the address deploying the contract.
Function: bridge
function bridge(
uint _destChainId,
address _recipient,
uint _amount
) external onlyActiveChain(_destChainId) {
// burn tokens
_burn(msg.sender, _amount);
// send cross chain message
_sendMessage(_destChainId, abi.encode(_recipient, _amount));
}
- Allows a user to send tokens to another chain.
- Parameters:
_destChainId
: The ID of the destination chain._recipient
: The recipient's address on the destination chain._amount
: The amount of tokens to send.
- Process:
- Burns the specified
_amount
of tokens from the sender's balance. - Encodes the
_recipient
address and_amount
into bytes (_data
). - Calls
_sendMessage
, a function fromMessageClient
, to send this encoded data to the destination chain.
- Burns the specified
- Modifiers:
onlyActiveChain
: A modifier restricts this function to be called only if the destination chain is active.
Function: messageProcess
function messageProcess(
uint,
uint _sourceChainId,
address _sender,
address,
uint,
bytes calldata _data
) external override onlySelf(_sender, _sourceChainId) {
// decode message
(address _recipient, uint _amount) = abi.decode(_data, (address, uint));
// mint tokens
_mint(_recipient, _amount);
}
- Handles incoming messages from other chains.
- Parameters:
_sourceChainId
: The ID of the source chain from where the message is sent._data
: Encoded data containing the recipient's address and the amount.
- Process:
- Decodes
_data
to extract_recipient
and_amount
. - Mints
_amount
of tokens to_recipient
's address on the current chain.
- Decodes
- Modifiers:
onlySelf
: A modifier restricts this function to be called only if the message was sent from the corresponding deployments of this contract on the source chain.