Multi-Criteria Airdrop Distribution Development
Simple snapshot-based airdrops no longer work: bots grab the lion's share, real users get crumbs, team wastes gas on addresses that will dump tokens first hour. Multi-criteria distribution — system where final allocation of each address computed through several independent filters and weighting coefficients. Goal: reward those who really interacted with protocol, exclude Sybil accounts.
Classic examples — ENS airdrop 2021 and Uniswap 2020. Both gave tokens to all addresses with certain history. More complex approach at Arbitrum STIP and Optimism RPGF: used retrospective activity metrics over long period with multiple weight groups.
System Architecture
Data Collection Layer (Off-chain)
All analytics done off-chain. Storing complete criteria on-chain impossible — computation and storage costs prohibitive. Scheme: data from nodes (via Alchemy/QuickNode archive nodes or The Graph subgraph) → analytics pipeline → Merkle tree → root published on-chain → claim via proof.
Typical sources:
- Transaction history — all address transactions with protocol over period
-
Event logs —
Swap,Deposit,Borrow,Repayfrom protocol contracts - Token balances at snapshot — ERC-20 balances at specific blocks
- ENS / Lens / Farcaster — verification of real identity
- Cross-chain activity — activity on other chains for anti-Sybil
Criteria and Weights
Each address gets score on several axes. Example structure:
| Criterion | Weight | Calculation Method |
|---|---|---|
| Trading volume (USD) | 30% | log-scale normalization |
| Unique active days count | 25% | raw count, cap at 180 |
| LP position retention (days) | 20% | total days in pool |
| Early user (first 3 months) | 15% | binary flag |
| Verified identity | 10% | ENS/Gitcoin Passport score |
Log-scale important for volume metrics: without it whale with $10M volume gets 10000x more than user with $1000. With log-scale — 4x. This is more correct from goal perspective (reward participation, not capital).
Anti-Sybil Filtering
Sybil — main pain point of any airdrop. Farms create thousands of wallets with similar pattern: recently created, several protocol transactions, no other activity, token receipt → immediate sale.
Technical cluster signs:
- Identical account creation time (within one block or hour)
- Common source of funds (all funded from one address)
- Identical transaction patterns (same contracts, same amounts)
- No activity outside target protocol
Tools: Sardine and Chainalysis for commercial Sybil-detection, MACI (Minimum Anti-Collusion Infrastructure) for complex cases. For own implementation — cluster analysis via funding graph: if 50 addresses form star with one central sponsor wallet, this is cluster.
Merkle-Distributor
Standard implementation — MerkleDistributor (like Uniswap). Algorithm:
- Calculate allocations for all addresses off-chain
- Build Merkle tree: each leaf =
keccak256(abi.encodePacked(address, amount)) - Write root to contract
- User claims providing proof (array of sibling hashes)
function claim(uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) external {
require(!isClaimed(index), "Already claimed");
bytes32 node = keccak256(abi.encodePacked(index, account, amount));
require(MerkleProof.verify(merkleProof, merkleRoot, node), "Invalid proof");
_setClaimed(index);
IERC20(token).safeTransfer(account, amount);
emit Claimed(index, account, amount);
}
Claimed bits packed in mapping(uint256 => uint256) — 256 addresses per uint256, gas saved on storage.
Vesting Option
Immediate full amount claim causes dump. Alternative: linear vesting via TokenVesting contract or cliff + linear scheme.
Example: 10% immediately, rest linearly over 6 months. Implemented either with separate vesting contract (user claims → tokens go to vesting stream), or via integration with Sablier v2 / LlamaPayV2 for stream-based distribution.
Development Process
Analytics (1-2 weeks). Criteria definition with protocol team, snapshot block selection, writing SQL/GraphQL queries for data extraction.
Pipeline and Sybil-filtering (1-2 weeks). Python/TypeScript scripts: data collection, normalization, clustering, final allocation calculation. Verification: manually review top-10 and bottom-10 addresses.
Smart contracts (1 week). MerkleDistributor with optional vesting, testnet deployment, verification on Etherscan/Arbiscan.
Claim frontend (3-5 days). Simple interface: address input → check allocation → claim via wagmi/viem. Proof generation on client from publicly available Merkle tree.
Total duration from kick-off to production: 3-5 weeks depending on on-chain data volume and criteria complexity.







