This is a proof of concept of an airdrop with Sybil-resistance built-in. Leveraging Humanbound Tokens, it allows administrators to set a limit on the number of eligible addresses one single human can claim an airdrop for.
This airdrop was designed to work in 2 phases: a registration period and a claiming period.
During the registration period, any address holding a Humanbound Token can register up to X eligible addresses (X being a number that can be configured in shared -> config -> sharedConfig.ts) for the airdrop. These addresses get added to a database along with how much token they’re entitled to claim. Signatures from both the HBT-holding address (via SIWE) and the future airdrop recipient need to be provided for a registration to complete.
Once the registration period is closed, the “administrator” generates a Merkle tree with all these registered addresses and their respective amount. The ERC20MerkleDrop smart contract is then deployed with the root of this Merkle tree.
Then, the claiming period begins. The frontend exposes a new page where users can claim their airdrop. Users can connect with an address which was registered, receive a Merkle proof from the backend and submit it to claim their airdrop.
- The security of the system relies heavily on the entries in the database, containing addresses and amounts, being correct at the time of creation of the Merkle tree. If the database is compromised, an attacker could insert claims with their addresses and arbitrary amounts of token.
- The backend provides an endpoint to retrieve a Merkle proof out of convenience. The Merkle tree could very well be made public. This way users could construct their own proofs and potentially interact with the smart contract directly.
It's required that you have yarn installed. If you don't, simply run npm i -g yarn.
Additionally, an instance of MongoDB should be running locally on "mongodb://localhost:27017.
If you don't have MongoDB installed, please install it locally.
Mac OS installation steps with Homebrew
Run the following commands:brew tap mongodb/brew
brew update
brew install mongodb-community@6.0
Start mongo with:
brew services start mongodb-community@6.0
Alternative installation
Follow the instructions here: https://www.mongodb.com/docs/manual/installation/At the root of the project, install all the dependencies:
yarn install
Let’s make sure the types generated by typechain (smart contract typing) are copied in the backend and frontend. Inside the hardhat folder, run:
yarn run typechainThen, you can run the backend, frontend, and local blockchain node all from 1 single terminal instance. To do so, run the following command from the project's root directory.
yarn devThis uses a package called concurrently that allows you to concurrently run all instances from a single terminal.
Once the app is running, you can view at:
- Frontend http://localhost:3000
- Backend http://localhost:5000
It also starts a local network with Hardhat. You can connect to it via Metamask using this RPC url: http://127.0.0.1:8545
You'll need funds for your address to interact with the smart contracts locally, so in the hardhat folder run:
npx hardhat --network localhost fund --to <your-address>Now in the same folder, deploy the mock Humanbound Token contract by running:
npx hardhat run --network localhost scripts/deploy-mock-hbt.tsThis will print out the address the mock HBT contract is deployed to. Make sure it’s the same in shared > config > hbtContracts for Web3ChainReference.EIP155_HARDHAT_LOCAL.
We’re ready to go! 🚀
Go to the hidden page http://localhost:3000/mint-hbt 🤫 to mint yourself a “mock HBT”. Note: This mock HBT is a simple ERC721 token contract which provides the interface necessary for this demo. In reality, HBTs are not so easily transferable and unique to a human.
Now it’s time to register some addresses for the airdrop!
In order to demonstrate the concept of Sybil resistance, the project is configured such that any address is eligible to a minimum of 5 tokens. (This value can be changed in shared → config → sharedConfig.ts)
Click on “Register” in the Nav bar to head over to http://localhost:3000/eligible.
Follow the instructions to register some addresses.
Next, we can generate the merkle tree. In the backend folder run:
yarn generate-merkle-treeThis should save a file called merkle.json at the root of the shared folder.
Then, deploy the smart contract with the tokens to aidrop. In the hardhat folder run
npx hardhat run --network localhost scripts/deploy-merkle-drop.tsThis outputs the address the contract was deployed. Make sure the same address is provided in shared > config > sharedConfig as merkleDropContractAddress.
Enable the “claiming phase” by changing the value of NEXT_PUBLIC_CLAIMING_PERIOD_STARTED to “true” in frontend > .env.
Thanks to Next.js fast refresh feature, the frontend should automatically refresh and a new page at http://localhost:3000/claim should now be accessible.
Click on the “claim” button and submit the transaction to claim an airdrop.
And voilà!
- Balances appearing incorrect
⇒ Switch network on your wallet and switch back to the local network.
- "Received invalid block tag X. Latest block number is Y”
⇒ Switch network with your wallet and switch back to the local network. See this answer.
- Issue with nonces
⇒ Enable custom nonces in Metamask and manually enter the right nonce value when submitting a transaction.