Розробка системи airdrop-трекингу
Airdrop-трекінг — завдання, яке на перший погляд здається простим: дивись івенти контракту, записуй адреси, показуй статус. Насправді це повноцінна data-pipeline система, яка працює з тисячами адрес на кількох чейнах одночасно і повинна видавати актуальний стан в real-time. Якщо архітектуру не продумати заздалегідь, система впаде під навантаженням в перший же день розподілу.
Що повинна вміти трекінгова система
Мінімальний набір функціональності для серйозного airdrop:
- Eligibility tracking: хто має право на отримання на основі яких критеріїв (snapshot, активність, holdings)
- Claim status: claimed / unclaimed / expired для кожної адреси
- Multi-chain support: одночасне відстеження Ethereum, Arbitrum, Base, Polygon
- Merkle proof generation: генерація і зберігання proof-ів на льоту або pre-computed
- Real-time sync: оновлення стану без затримки після on-chain подій
- Analytics dashboard: скільки claimed, швидкість розподілу, топ отримувачів
Архітектура: від чейна до UI
On-chain частина: дистрибуція через Merkle tree
Майже всі сучасні airdrop-контракти будуються на Merkle proof схемі — це стандарт після Uniswap v1 airdrop. Контракт зберігає лише один bytes32 merkleRoot, а не всі eligible адреси:
contract MerkleAirdrop {
bytes32 public immutable merkleRoot;
mapping(address => bool) public hasClaimed;
IERC20 public immutable token;
event Claimed(address indexed account, uint256 amount);
function claim(
address account,
uint256 amount,
bytes32[] calldata merkleProof
) external {
require(!hasClaimed[account], "Already claimed");
bytes32 leaf = keccak256(bytes.concat(
keccak256(abi.encode(account, amount))
));
require(
MerkleProof.verify(merkleProof, merkleRoot, leaf),
"Invalid proof"
);
hasClaimed[account] = true;
token.safeTransfer(account, amount);
emit Claimed(account, amount);
}
}
Подвійне хеширання leaf (keccak256(keccak256(...))) — захист від second preimage attack. Це паттерн з OpenZeppelin MerkleProof library, не вигадуйте своє.
Off-chain частина: indexer і API
Трекінгова система тримається на трьох компонентах:
1. Blockchain indexer
Слухає Claimed ївенти через WebSocket RPC (Alchemy/Infura) або власну ноду. Для надійності — два незалежні провайдери з fallback. Дані пишуться в PostgreSQL:
claims(
id, chain_id, tx_hash, block_number,
address, amount, claimed_at, log_index
)
Унікальність: (chain_id, tx_hash, log_index) — захист від дублікатів при reorg.
2. Merkle tree builder
Приймає на вхід snapshot — список (address, amount) — і будує дерево. Для великих airdrop (100k+ адрес) використовуйте @openzeppelin/merkle-tree (TypeScript) або merkle-distributor від Uniswap. Важливо: leaf encoding повинен точно збігатися з контрактом — частий джерело помилок.
3. REST/GraphQL API
Ендпоінти для frontend:
-
GET /eligibility/:address— eligible + amount + proof -
GET /status/:address— claimed чи ні, tx_hash -
GET /stats— aggregate статистика
Proof-и або pre-computed і зберігаються в Redis, або генеруються on-demand з дерева в пам'яті. Для 1M адрес дерево важить ~64MB — цілком реально тримати in-memory.
Snapshot механізм
Snapshot — момент, на який фіксується стан holdings або активності. Два підходи:
| Підхід | Як працює | Інструменти |
|---|---|---|
| Block snapshot | Беремо баланси на конкретному block number | Alchemy getBalance, The Graph |
| Activity-based | Рахуємо транзакції/обсяг за період | Dune Analytics, Flipside |
| NFT holders | Власники конкретного NFT на момент snapshot | Moralis, Alchemy NFT API |
Для ERC-20 snapshot на конкретний блок — використовуйте eth_call з blockNumber параметром або ERC20Snapshot extension від OpenZeppelin, який зберігає історію балансів прямо в контракті.
Обробка edge cases
Re-org protection: транзакції нужно рахувати finalized лише після N підтверджень (12 для Ethereum mainnet, 64 для Polygon). Не помічайте claim як виконаний до finality.
Експірація: якщо airdrop має срок — контракт повинен мати expiry timestamp і функцію reclaim() для повернення неполученних токенів. Трекер повинен показувати expired статус.
Multi-wallet: деякі користувачі намагаються клеймити через proxy-контракти або різні гаманці. Якщо потрібна Sybil-фільтрація — застосуйте її на етапі побудови snapshot, а не в контракті.
Масштабування під навантаженням
В день TGE трекер отримує піковиму навантажену. Підготовка:
- CDN кешування proof-ів: вони immutable, безпечно кешувати на 24h
- Read replicas PostgreSQL для аналітичних запитів
- Rate limiting по IP і по адресі: захист від scrapers
- Pre-warming: побудити дерево і записати proof-и в Redis до старту клейминга
Система трекингу — не прикраса, а критична інфраструктура airdrop. Користувач, який не може перевірити свій статус, сприймає це як проблему самого проекту.







