Разработка системы ретроактивного airdrop
Ретроактивный airdrop — один из самых эффективных механизмов распределения токенов среди реальных пользователей протокола. Идея простая: вознаградить тех, кто пользовался продуктом до того, как появился токен. Uniswap раздал UNI в сентябре 2020 — каждый кто делал swap получил 400 UNI. dYdX, Optimism, Arbitrum, ENS — все построили похожие механики, но с разной степенью сложности eligibility критериев.
Реализация выглядит простой только на первый взгляд. За ней стоит нетривиальная инфраструктура: сбор и обработка on-chain данных, snapshot системы, проверка Sybil-активности, Merkle tree дистрибуция, и UI для claim. Каждый из этих этапов может стать bottleneck или точкой уязвимости.
Сбор данных и snapshot
Источники данных
Основа ретроактивного airdrop — исторические on-chain данные. Нужно ответить на вопрос: кто делал что, когда, и в каком объёме? Для этого используют несколько подходов.
The Graph — индексация событий контрактов через subgraph. Если у протокола уже есть subgraph, исторические события (Swap, Deposit, Borrow) доступны через GraphQL. Проблема: The Graph хранит данные с момента деплоя subgraph, а не с начала истории блокчейна. Если subgraph появился позже протокола — ранние события могут быть недоступны.
Direct RPC indexing — собственный скрипт, который обходит блоки через eth_getLogs и собирает события. Медленно (Ethereum ~19M блоков на момент написания), требует архивной ноды или платного RPC (Alchemy, Infura с архивным доступом). Но полный контроль над данными.
Dune Analytics — SQL-запросы к индексированным данным Ethereum. Быстро для exploration, но CSV экспорт ограничен для больших датасетов. Подходит для прототипирования критериев.
Для продакшн airdrop на несколько тысяч адресов — рекомендую собственный индексер. Стек: TypeScript + viem + PostgreSQL. Вытягиваем события батчами по 2000 блоков, храним в БД, строим агрегации.
Критерии eligibility
Критерии определяют кто и сколько получает. Типовые подходы:
| Действие | Вес | Пример |
|---|---|---|
| Количество транзакций | Базовый | ≥3 транзакций = eligible |
| Объём торговли | Линейный/логарифмический | $1000 объёма = 1 балл |
| Время активности | Временные окна | Использовал в 3+ разных месяцах |
| Ранний пользователь | Бонус | До блока X = 2x |
| Предоставление ликвидности | Объём × время | TVL × дни |
Логарифмическое масштабирование объёма предотвращает доминирование китов: score = log10(volume_usd + 1). Без этого топ-10 адресов получат половину airdrop.
Антисибил-защита
Проблема Sybil-атак
Sybil — создание множества адресов для получения большего количества токенов. Если критерий «1 транзакция = eligible», атакующий создаёт 1000 кошельков и делает по одной транзакции с каждого. Это реальная проблема: при Optimism airdrop ~17% от первоначально eligible адресов были отфильтрованы как Sybil.
Методы детекции
Финансирование адресов. Все адреса Sybil-кластера получают ETH на газ от одного источника. Граф финансирования строится как дерево: корень — CEX вывод или известный кошелёк, листья — Sybil адреса. Если N адресов получили ETH от одного адреса и все N делали похожие действия в похожее время — это кластер.
Временные паттерны. Sybil-скрипт создаёт транзакции в определённом временном окне (пока скрипт работает). Кластер из 50 адресов, все сделавшие первую транзакцию в течение 10 минут — подозрительно.
On-chain identity. ENS имя, Lens Protocol профиль, Gitcoin Passport score — индикаторы реального пользователя. Адрес с ENS именем почти никогда не является Sybil.
Минимальный ETH баланс. Нулевые кошельки после airdrop claim — признак Sybil. Добавить требование минимального баланса (0.001 ETH) в момент snapshot отсекает большинство фейковых адресов.
Merkle Tree дистрибуция
Почему Merkle Tree
Наивный подход — хранить список eligible адресов и суммы on-chain. При 100,000 адресах это ~3MB on-chain данных, стоимость деплоя будет астрономической. Merkle Tree решает это: корень дерева (32 байта) хранится в контракте, пользователь предоставляет proof при claim.
Proof — массив хешей, которые вместе с адресом и суммой позволяют воссоздать корень дерева. Для N листьев размер proof — O(log N). При 1M пользователей proof содержит ~20 элементов по 32 байта = 640 байт calldata.
Реализация
Merkle tree строится off-chain из списка пар [address, amount]. Стандарт — OpenZeppelin MerkleProof библиотека с keccak256 хешированием:
// leaf = keccak256(abi.encodePacked(account, amount))
// двойное хеширование предотвращает second preimage attack
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(account, amount))));
require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof");
Claim контракт хранит маппинг claimed[address] => bool для предотвращения повторного claim. После claim — transfer токенов с контракта на пользователя.
Временное окно claim. Ретроактивный airdrop должен иметь deadline: 3-6 месяцев достаточно. Некlaimed токены возвращаются в treasury или сжигаются. Это предотвращает вечно висящие allocations.
Frontend и UX
Минимальный claim UI: подключение кошелька → проверка eligibility (запрос к off-chain API или вычисление proof на клиенте) → отображение суммы → транзакция claim. Proof можно хранить в IPFS (публичный JSON файл) или генерировать на сервере.
Важно: не показывать суммы до публичного анонса. Проверка eligibility через API (без раскрытия суммы) позволяет пользователям проверить статус, не раскрывая полный список до старта.
Стек и сроки
Смарт-контракт (Solidity + Foundry) + off-chain индексер (TypeScript + PostgreSQL) + Merkle tree генератор + claim UI (React + wagmi).
MVP для простого airdrop с готовыми данными — 2-3 недели. Полная система с собственным индексером, Sybil-детекцией и claim UI — 5-8 недель.







