Summary: Developed a smart contract that enables the GovernorDAO Community to un-rug the CBDAO Rug Pull victims by providing them with a Merkle Distribution Airdrop. Over 1/3 of an already-minted supply of tokens are on the line ($2M value) and I have been tasked with covering the Airdrop (after a developer promised to do so and caught COVID and hasn't been heard from).
Design Specifications: an airdrop to over 3K recipients where claims may be made at a rate of 10% of the eligible amount with an additional 1% available each day until 90 days have elapsed (100%). For each claim < 100%, the forfeited amount is split thusly, 50% burned, 50% rewards pool. Note: the rewards pool is shared amongst those who waited the full 90 days to claim.
Design Specifications: Summary
- N of(Recipients, Amount): 3,716
- Available on Day 1: 10% of Amount
- Daily Reward Available: 1% of Amount
- Day 90: 100% Available
- Day 100: Airdrop Claiming Terminates
Progress: Smart Contract is 95% complete and may be found here. Pre-configured tests from the original Uniswap Repo passes with MerkleDistributor.sol updates, but it fails when an attempt to move funds anywhere other than account or deployer.
Assistance Requested: (1) An explanation as to how to create the Merkle Root that is referenced in the smart contract and deploy on the blockchain. (2) Assurance that only the account may request a claim and such claims cannot be made on behalf of another (to avoid malicious claims). (3) Logic and bug testing from any devs willing and able to try to break it.
Claims Code Snippet
// SET CLAIM AND SEND FUNDS.
_setClaimed(index);
// TIME SETTINGS
// [P] Start (unix): 1607990400 | Tuesday, December 15th, 2020 @ 12:00AM GMT
uint256 startTime = 1607495500;
// [P] End (unix): 1616630400 | Thursday, March 25th, 2021 @ 12:00AM GMT
uint256 endTime = 1616630400;
// CLAIMS PERIOD
uint256 nowTime = block.timestamp;
uint256 duraTime = nowTime - startTime;
require(nowTime >= startTime, 'MerkleDistributor: Too soon');
require(nowTime <= endTime, 'MerkleDistributor: Too late');
// MAX TIME
uint256 duraDays = duraTime / 86400 >= 90 ? 90 : duraTime / 86400; // divided by the number of seconds per day
require(duraDays <= 100, 'MerkleDistributor: Too late'); // double check days
// AVAILABILITY CALCULATION
uint256 availAmount = amount * (10 + duraDays) / 100;// 10% + 1% daily
require(availAmount <= amount, 'MerkleDistributor: Slow your roll');// do not over-distribute
uint256 forfeitedAmount = amount - availAmount;
// ACCOUNT TRANSFERS
require(IERC20(token).transfer(account, availAmount), 'MerkleDistributor: Transfer to Account failed.');
// [P] Update: include 50% transfer to rewardsPool and 50% burn | 50% of 'forfeitedAmount'
require(IERC20(token).transfer(rewardsPool, forfeitedAmount), 'MerkleDistributor: Transfer to Deployer failed.');