Retroactive Airdrop Points System Development
Retroactive airdrop — rewarding users who already interacted with protocol before snapshot. Idea: instead of distributing tokens to everyone on whitelist, project rewards real participants — those who traded, provided liquidity, used governance. First large precedent — Uniswap in 2020. After it, points + airdrop model became industry standard.
Problem is naive implementation ("grab on-chain data, calculate points, distribute tokens") doesn't work. Sybil attacks — one wallet farm with hundreds of wallets eats 30–60% of allocation. Tokens without vesting immediately dump. So points system — not just SQL queries on on-chain data, but infrastructure with multi-layer protection and thoughtful mechanics.
Points System Architecture
Off-chain vs On-chain Calculation
Most production systems calculate points off-chain and publish Merkle root to smart contract. Reason simple: full on-chain calculation with historical data unrealistic by gas. Scheme:
- Index on-chain events via The Graph or own indexer (PostgreSQL + event model)
- Calculate points by rules (Python/TypeScript script, reproducible and auditable)
- Publish final table
address → pointsto IPFS - Build Merkle tree from this table
- Write Merkle root to airdrop contract
User at claim provides Merkle proof of their address and points amount. Contract verifies proof against root — no need to store all 500k+ addresses on-chain.
Points Accrual Rules Structure
Good points system multidimensional. Example for DEX protocol:
| Action | Base | Multiplier | Note |
|---|---|---|---|
| Trading volume | 1 point / $100 | × 1.5 for early user | Before certain date |
| LP position | 0.1 point / day / $1000 TVL | × 2 for concentrated range | Uniswap V3 style |
| Governance vote | 50 points / vote | × 1.2 for delegation | Only real proposals |
| Referral | 10% of referral points | — | Limited to depth 1 |
Temporal decay — important parameter. Early users get multiplier that decreases approaching snapshot. This incentivizes interaction early, not last-minute rush.
Sybil Protection
Most technically complex component. Standard approaches:
On-chain cluster analysis. Addresses funded from one source, using identical gas patterns, interacting at identical UTC times — flagged as potential clusters. Tools: Hop Protocol used transaction graph analysis; Arbitrum applied combination of financial analysis and activity.
Minimum thresholds. Address with volume under $500 and under 5 transactions — excluded completely. This cuts most automatic farms with minimal deposits.
Verification via Gitcoin Passport or Worldcoin. Optional: user with verified identity gets bonus multiplier (1.5–2x). This incentivizes verification without hard requirement.
Decay for fast wallets. If wallet appeared 2 weeks before snapshot and immediately hit maximum — points slashed 50–80%. Organic users interact over months.
Airdrop Contract with Vesting
Basic Merkle-Based Structure
contract AirdropWithVesting {
bytes32 public merkleRoot;
mapping(address => uint256) public claimedAmount;
mapping(address => uint256) public vestingStart;
uint256 public constant CLIFF = 30 days;
uint256 public constant VESTING_DURATION = 180 days;
function claim(
uint256 totalPoints,
uint256 tokenAmount,
bytes32[] calldata proof
) external {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, tokenAmount));
require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof");
require(vestingStart[msg.sender] == 0, "Already claimed");
vestingStart[msg.sender] = block.timestamp;
// tokens go to vesting contract, not directly to user
_startVesting(msg.sender, tokenAmount);
}
}
Vesting instead of instant release — standard after airdrop without vesting (e.g., new L2 project token distributions) immediately dumped 80–90% first day.
Linear Vesting with Cliff
After cliff (30–90 days) linear vesting starts for 3–6 months. Claim anytime releases accumulated part:
function claimable(address user) public view returns (uint256) {
if (block.timestamp < vestingStart[user] + CLIFF) return 0;
uint256 elapsed = block.timestamp - (vestingStart[user] + CLIFF);
uint256 vested = (totalAllocation[user] * min(elapsed, VESTING_DURATION)) / VESTING_DURATION;
return vested - claimedAmount[user];
}
Indexing and Calculation Tools
The Graph — standard for event indexing. Subgraph on AssemblyScript describes handlers for each protocol event, data stored in GraphQL-accessible database. Queries for point calculation — GraphQL with aggregation.
Dune Analytics — fast way to prototype accrual rules via SQL without own infrastructure. Doesn't fit production calculations (no real-time guarantee), but great for validating methodology.
Own indexer — PostgreSQL + event listener (ethers.js or viem). Full data control, complex JOIN queries possible. Requires infrastructure support.
Stack and Timeline
Calculation backend: Python (pandas for transformations) or TypeScript. Merkle tree: OpenZeppelin merkle-tree library (JavaScript). Smart contract: Solidity 0.8.x + Foundry. Testing: generate test Merkle proofs in Foundry tests.
Typical timelines: designing rules and data analysis — 2–3 weeks, contract and indexer development — 3–4 weeks, sybil analysis and final snapshot — 1–2 weeks.
Cost determined after protocol clarification, address count and Sybil-protection requirements.







