This repository contains the Zer0 Token and other utility contracts for airdropping and vesting ERC20 tokens.
Check out the docs folder for more information on what they do.
Follow this guide to learn how to get started with building and developing locally.
This guide will assume you are using Visual Studio Code as an IDE, however you can use whatever IDE you would like.
This project requires yarn to be installed on your system, as well as node.js. Please ensure those are installed on your system before continuing.
In the command line terminal, run the following:
yarnThis will:
- Install required node packages (this may take awhile)
- Compile the smart contracts
- Generate typechain helper types
This step should complete without any errors.
If you encounter errors there may be something wrong with your node or yarn installation, ensure you have node version 14 or higher.
If you are still encountering errors please file a GitHub issue with as much detail as possible.
To run the tests for the smart contracts simply run
yarn testYou can check test coverage by running
yarn coverageYou may get a warning about contract code size exceeding 24576 bytes, you can ignore this.
If you ever need to recompile the smart contracts you can run
yarn compileTo recompile the smart contracts.
Running
yarn compilewill also re-generate the typechain helpers
In some cases you may need to clean previously built artifacts, if you run into errors you can try running
yarn buildwhich will clean and re-compile the smart contracts
You can lint any Solidity and TypeScript files in the project by running:
yarn lintMany linting issues can be solved automatically by running:
yarn fixEnsure you run both
yarn fixandyarn lintbefore creating a pull request.
You may get some line-ending errors, this project expects that line-endings are LF and not CRLF.
Both the MerkleTokenAirdrop and MerkleTokenVesting contracts require a valid Merkle Tree to run properly.
If you aren't familiar with Merkle Tree's as a concept, check out these resources:
- Wikipedia: https://en.wikipedia.org/wiki/Merkle_tree
- ERC20 Snapshot with Merkle Trees: https://medium.com/coinmonks/erc20-snapshot-using-merkle-trees-aeeac48ce925
- Merkling in Ethereum: https://medium.com/coinmonks/erc20-snapshot-using-merkle-trees-aeeac48ce925
Without explaining how the concept works, and instead what it enables, consider this statement:
We can use a Merkle Tree to distribute tokens to thousands of addresses with minimal gas costs.
We create a Merkle Tree out of a list of all the addresses we need to send tokens to, each address is a node in this tree. A
Merkle Rootis also created when we make this tree, we must store this root in our smart contract.Using the smart contract, a user an make a call, with the data from their node on the Merkle Tree we created. Through a mathematical operation, we can verify that the data from the node is valid (part of the tree) by using the
Merkle Rootin the smart contract.After verifying the data, we can distribute tokens.
In short:
- We make a
Merkle Treewhich has a root key, and child keys- We deploy a smart contract which only stores the root key
- Users can call the smart contract with a child key
- The child key is verified against the root key using 'math'
- If verification is successful, we distribute tokens
- We also store a flag indicating that a child key has been used so it can't be used twice
One important feature of a
Merkle Treeis that any data can be stored in the tree on a child key. This allows us to store a user address and an owed amount inside of a child key.
There are two different types of Merkle Trees used in this project, Airdrop and Vesting.
The MerkleTokenAirdrop uses Airdrop trees and MerkleTokenVesting uses Vesting trees.
You can find examples of these trees in the ./merkle/airdrop/example-merkleTree.json and ./merkle/vesting/example-merkleTree.json files.
There are scripts to generate both of these types of Merkle Trees in this project.
To create a Merkle Tree you must create an input json file:
It is recommended you create this file in the ./merkle/<type>/ directory where <type> is the type of tree you are making.
The json file should follow this schema:
{
"<account address>": {
"amount": number,
"revocable": boolean
}
}Where:
<account address>is the string of the account address (ie:0x70997970C51812dc3A010C7d01b50e0d17dc79C8)amountis the number of tokens to be vested (ie:5000)- Note: You most likely will need to pad zeros to account for the decimal places in ERC20 tokens more info here
revocablewhether this vesting award is revocable
You can have as many addresses as you want in this file.
The json file should follow this schema:
{
"<account address>": {
"amount": number
}
}Where:
<account address>is the string of the account address (ie:0x70997970C51812dc3A010C7d01b50e0d17dc79C8)amountis the number of tokens to be air dropped (ie:5000)- Note: You most likely will need to pad zeros to account for the decimal places in ERC20 tokens more info here
You can have as many addresses as you want in this file.
To generate a merkle tree you can run the following command:
yarn merkle generate <type> <input file>Where:
<type>is the type of merkle tree (vestingorairdrop)<input file>is the path to the input file (ie:"./merkle/vesting/example.json")
This will generate and output a json file in the same directory and name as the input file but with -merkleTree appended on the end of the filename.
Given the input
"./merkle/vesting/example.json"the generate command will output"./merkle/vesting/example-merkleTree.json"
In case you wish to verify that a Merkle Tree has been properly generated, you can use the following command to verify the tree:
yarn merkle verify <type> <merkle tree file>Where:
<type>is the type of merkle tree (vestingorairdrop)<merkle tree file>is the path to the merkle tree file (ie:"./merkle/vesting/example-merkleTree.json")
Use the
XXX-merkleTree.jsonfile here instead of the input file
You can use the yarn hardhat deploy task to deploy contracts.
Information about deployments are stored in the deployments/ folder where the name of the file is the network you deployed to.
Run the this command to deploy a token contract:
yarn hardhat deploy token --tag "<tag>" --name "<name>" --symbol "<symbol>" --network <network>
Where:
<tag>is a user friendly tag (used in the deployment file)<name>is the token name to be used<symbol>is the token symbol to be used<network>is the network to deploy to
Run the this command to deploy a Merkle Token Airdrop contract:
yarn hardhat deploy airdrop --tag "<tag>" --merkle "<merkle file>" --token "<token>"
Where:
<tag>is a user friendly tag (used in the deployment file)<merkle file>is the path to the merkle file to use<token>is the address of the token that is being airdropped
You will need to transfer the proper quantity of tokens to the contract once it has been deployed so users can claim their tokens.
Make sure that
<merkle_file>is a path to a JSON file which is the proper type (airdrop, not vesting)
You can find<token>(the address) in the deployment file if you deployed a token wih the deployment scripts
Run the this command to deploy a Merkle Token Airdrop contract:
yarn hardhat deploy vesting --tag "<tag>" --merkle "<merkle file>" --token "<token>" --start <start> --duration <duration> --cliff <cliff>
Where:
<tag>is a user friendly tag (used in the deployment file)<merkle file>is the path to the merkle file to use<token>is the address of the token that is being airdropped<start>is what block number vesting should start on<duration>is how many blocks until all tokens are vested (linearly)<cliff>is how many blocks until the vesting cliff is reached
You will need to transfer the proper quantity of tokens to the contract once it has been deployed so users can claim their tokens.
Make sure that
<merkle_file>is a path to a JSON file which is the proper type (vesting, not airdrop)
You can find<token>(the address) in the deployment file if you deployed a token wih the deployment scripts
A slightly modified version of the ERC20 Open Zeppelin Contracts are used in this project. For more information check this file.
These modifications have been deemed minor, so no additional tests have been written for the contracts in the ./contracts/oz folder.