Mochilab
Published in

Mochilab

Merkle Airdrop: One of the best Airdrop Solution for Token Issues

What is Merkle Airdrop?

1. What’s the problem?

Let’s say, we are about to issue an ERC-20 coin on Ethereum and need “Airdrop Coin” for some initial users, in order to increase the number of token holders as well as to promote the upcoming token more widely.

Basically, we will need to do these following steps to build the an Airdrop feature:

1. Of course, you have to deploy the ERC-20 token contract first.

2. Submit an Airdrop contract: The contract stores a list of addresses registered to receive tokens. After the end of the Airdrop registration period, users can call that contract to claim the token.

3. In addition, we can build more bots (for example, Telegram BOT) to track users with the condition of following your social channels, sharing your posts etc….in order to register for Airdrop.

Merkle Airdrop

With the above Airdrop contract design, we have 1 problem. With Airdrop phases, the number of people signing up to receive tokens depending on the size can range from hundreds, thousands or even tons of thousands of people…

Ethereum’s storage and transaction fees are not cheap at the moment. Let’s magine an array in a contract that stores thousands of registered addresses, it will definitely cost an arm and a leg, not to mention new transactions address into the array again. In general, the solution to design the Airdrop contract as above is not optimal at all for your wallet.

With Merkle Airdrop, we won’t have to worry about saving a large number of registered addresses in the contract anymore, while still ensuring the verification of whether the claim address has been registered before or not? Thereby, we can save a lot of money in Airdrop.

2. Theoretical basis

Merkle Tree

The Merkle Tree is simply a binary tree data structure where the values of the nodes and leaves are the hash of the data.

To create a Merkle Tree, from the data we have, use the hash function to calculate the corresponding hash value of the data, these values will be the leaf nodes of the tree. Keep hashing adjacent values until there is a unique hash value (the root of the Merkle tree). The figure below shows how a Merkle Tree is calculated.

Merkle Tree helps verify, check data integrity while taking only a small amount of storage space (due to the small hash size). In Blockchain, Merkle Tree is very commonly used to verify transactions (Used in Bitcoin, Ethereum, etc.).

Merkle Proof

Merkle Proof is used to check whether the input data belongs to the Merkle Tree or not without having to reveal all the data that make up the Merkle Tree.

Let’s take a look at the example illustrated in the image above to understand what Merkle Proof is? In this example we need to prove that the data K belongs to the Merkle Tree. We need to calculate the Hash of K and then gradually climb to the root of the Merkle Tree. If the value of the calculated Merkle Tree root matches the given Merkle Root value, then K belongs to the Merkle Tree.

Instead of using all the data from A-P to recalculate the Merkle Root to see if it is the same as the original Merkle Root? We will only need to take the following nodes of the tree to prove that K belongs to the Merkle Tree.

Hash of L from which to calculate hash KL

HJ’s hash from which IJKL . hash is calculated

MNOP’s hash from which IJKLMNOP . hash is calculated

Hash of ABCDEFGH

From there, we can completely calculate the Merkle Root, but only need to know the 4 node values in the Merkle Tree

3. Merkle Airdrop

The basic flow that we will implement Airdrop is as follows:

  • Let users register for the airdrop and save the list as follows (We can save it in the server, cloud or IPFS wherever you want). The first value is the registered address and the second is the number of airdrop tokens for that address.

0x19171a5da52276b6a034CB859ddA1e905739F8B2 10000000000000000000

0x04d1eC716Fe9AC219D59b9E4f0D64D3B4339642e 10000000000000000000

0x14C06EC9402f7CD52dd0AF02979a350EAF133F76 10000000000000000000

  • After the end of the airdrop registration period, from the list above, we calculate the Merkle Root and save it on the smart contract.
  • Based on the number of airdrop subscribers, we will send the corresponding amount of ERC-20 tokens to the Merkle Airdrop smart contract to be able to airdrop to users.
  • Users will then call the Merkle Airdrop contract to claim the registered token amount. Based on the Merkle Proof, the contract will calculate whether this address has registered for the airdrop and the number of tokens claimed is okay or not? If true, the contract will send the corresponding amount of tokens to users.

Specific examples

We have also built an Airdrop page and so far it is working quite well.

Registration: Users will register to receive airdrops via BOT on Telegram with some conditions such as joining Telegram chat box, following twitter or retweeting. When the user completes the registration steps, the BOT will call the Node.js server and save the user’s information to MongoDB.

Claim: After closing the airdrop registration and allowing users to claim the token. Users will go to the airdrop page. Server will calculate and return Merkle Proof based on users’ addresses. After that, users signing the transaction calls the smart contract to claim, if users have registered before, they will receive the token when completing the transaction.

4. Smart Contracts

// MerkleAirdrop.sol

pragma solidity ^0.6.0;

pragma experimental ABIEncoderV2;

import “@openzeppelin/contracts/cryptography/MerkleProof.sol”;

import “@openzeppelin/contracts/token/ERC20/IERC20.sol”;

import “@openzeppelin/contracts/token/ERC20/SafeERC20.sol”;

import “@openzeppelin/contracts/math/SafeMath.sol”;

import “@openzeppelin/contracts/proxy/Initializable.sol”;

import “@openzeppelin/contracts/access/Ownable.sol”;

contract PhoneAirdrop is Ownable {

using SafeERC20 for IERC20;

using SafeMath for uint256;

event Claimed(address claimant, uint256 week, uint256 balance);

event TrancheAdded(uint256 tranche, bytes32 merkleRoot, uint256 totalAmount);

event TrancheExpired(uint256 tranche);

event RemovedFunder(address indexed _address);

IERC20 public token;

mapping(uint256 => bytes32) public merkleRoots;

mapping(uint256 => mapping(address => bool)) public claimed;

uint256 public tranches;

constructor(IERC20 _token) public {

token = _token;

}

function seedNewAllocations(bytes32 _merkleRoot, uint256 _totalAllocation)

public

onlyOwner

returns (uint256 trancheId)

{

token.safeTransferFrom(msg.sender, address(this), _totalAllocation);

trancheId = tranches;

merkleRoots[trancheId] = _merkleRoot;

tranches = tranches.add(1);

emit TrancheAdded(trancheId, _merkleRoot, _totalAllocation);

}

function expireTranche(uint256 _trancheId)

public

onlyOwner

{

merkleRoots[_trancheId] = bytes32(0);

emit TrancheExpired(_trancheId);

}

function claimWeek(

address _liquidityProvider,

uint256 _tranche,

uint256 _balance,

bytes32[] memory _merkleProof

)

public

{

_claimWeek(_liquidityProvider, _tranche, _balance, _merkleProof);

_disburse(_liquidityProvider, _balance);

}

function claimWeeks(

address _liquidityProvider,

uint256[] memory _tranches,

uint256[] memory _balances,

bytes32[][] memory _merkleProofs

)

public

{

uint256 len = _tranches.length;

require(len == _balances.length && len == _merkleProofs.length, “Mismatching inputs”);

uint256 totalBalance = 0;

for(uint256 i = 0; i < len; i++) {

_claimWeek(_liquidityProvider, _tranches[i], _balances[i], _merkleProofs[i]);

totalBalance = totalBalance.add(_balances[i]);

}

_disburse(_liquidityProvider, totalBalance);

}

function verifyClaim(

address _liquidityProvider,

uint256 _tranche,

uint256 _balance,

bytes32[] memory _merkleProof

)

public

view

returns (bool valid)

{

return _verifyClaim(_liquidityProvider, _tranche, _balance, _merkleProof);

}

function _claimWeek(

address _liquidityProvider,

uint256 _tranche,

uint256 _balance,

bytes32[] memory _merkleProof

)

private

{

require(_tranche < tranches, “Week cannot be in the future”);

require(!claimed[_tranche][_liquidityProvider], “LP has already claimed”);

require(_verifyClaim(_liquidityProvider, _tranche, _balance, _merkleProof), “Incorrect merkle proof”);

claimed[_tranche][_liquidityProvider] = true;

emit Claimed(_liquidityProvider, _tranche, _balance);

}

function _verifyClaim(

address _liquidityProvider,

uint256 _tranche,

uint256 _balance,

bytes32[] memory _merkleProof

)

private

view

returns (bool valid)

{

bytes32 leaf = keccak256(abi.encodePacked(_liquidityProvider, _balance));

return MerkleProof.verify(_merkleProof, merkleRoots[_tranche], leaf);

}

function _disburse(address _liquidityProvider, uint256 _balance) private {

if (_balance > 0) {

token.safeTransfer(_liquidityProvider, _balance);

} else {

revert(“No balance would be transferred — not going to waste your gas”);

}

}

}

Let’s learn a little bit about the logic of the Merkle Airdrop contract

  • The tranches variable stores the id of the Airdrop (we can open different airdrops)
  • Mapping merkelRoots stores the Merkle Root value of the respective Airdrop.
  • Mapping claimed is used to check if in a specific airdrop the address has been claimed or not?
  • The seedNewAllocations function is the init function of the Airdrop, after the end of the airdrop registration, the owner of the contract will call this function to transfer the token to the contract as well as save the Merkle Root value.
  • The private _claimWeek function will check the conditions to see if the user’s address has been claimed or not? Is the tranches id valid or not?
  • The _verifyClaim function will rely on the Merkle Proof submitted by the user to calculate whether the address is correct or not.
  • Finally, the _disburse function is the function that will send the token from the contract to the user when all conditions are satisfied.

References

Evolution of Airdrop: from Common Spam to the Merkle Tree

Wikipedia

Merkle proofs Explained

— — — — — — — — — — — — — — — — — — — — — — -

Follow MochiLab here:

Website | Twitter

Follow Mochi.Market here:

Website | Twitter | Telegram Chat | Announcement | Medium

In upcoming days, MochiLab will boost our interaction on existing channels to create more practical values for the community, on the other hand, expand new channels such as Tiktok, Youtube…

Let’s look forward to our next information!

MochiLab — Where NFT innovation is incubated

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store