Розробка системи масового розподілу токенів
Масовий розподіл токенів (airdrop) — операція, яка ламає проєкт, якщо підійти до неї наївно. Транзакції на десятки тисяч адрес через прямий transfer() спалюють мільйони доларів на газ і забивають блоки. Ще гірше — атакуючий із сотнею сибіл-адрес забирає частку, призначену реальним користувачам. Ми розробляємо системи, які вирішують обидві проблеми: економлять до 70% газу та відсіюють до 99% сибілів. Наш досвід — понад 7 років, понад 50 запущених проєктів із сумарним розподілом $50M+.
Ключові виклики при масовому розподілі токенів
Вибір підходу — push чи pull — визначає бюджет та UX. На Ethereum mainnet при 50 000 отримувачах і ціні газу $20/Gwei direct transfer обійдеться в $50 000+ тільки на комісіях. При pull-сценарії користувач оплачує газ сам, але щоб claim приніс 10 000 чоловік, потрібно демотивувати сибілів. Друга проблема — атакуючі створюють тисячі гаманців, виконують мінімальну активність і отримують непропорційну частку. Для захисту ми використовуємо комбінацію: on-chain фільтри (вік гаманця, історія транзакцій, обсяг комісій) та off-chain верифікацію через Gitcoin Passport або соціальні мережі з підписом. Tiered-розподіл (ранні учасники отримують в 3x більше) робить сибіл-атаку економічно невигідною.
Merkle drop: стандарт масового розподілу
Список отримувачів пакується в Merkle tree, на блокчейні зберігається тільки root. Користувач надає proof свого права на токени. Це золотий стандарт для великих airdrop: витрати газу мінімальні, а доказ можна перевірити on-chain. Ось як виглядає базова реалізація:
contract MerkleDistributor { address public immutable token; bytes32 public immutable merkleRoot; mapping(uint256 => uint256) private claimedBitMap; event Claimed(uint256 indexed index, address indexed account, uint256 amount); constructor(address token_, bytes32 merkleRoot_) { token = token_; merkleRoot = merkleRoot_; } function isClaimed(uint256 index) public view returns (bool) { uint256 claimedWordIndex = index / 256; uint256 claimedBitIndex = index % 256; uint256 claimedWord = claimedBitMap[claimedWordIndex]; uint256 mask = (1 << claimedBitIndex); return claimedWord & mask == mask; } 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).transfer(account, amount); emit Claimed(index, account, amount); } function _setClaimed(uint256 index) private { uint256 claimedWordIndex = index / 256; uint256 claimedBitIndex = index % 256; claimedBitMap[claimedWordIndex] |= (1 << claimedBitIndex); } } Бітова карта замість mapping(address => bool) економить суттєвий обсяг storage, особливо при сотнях тисяч отримувачів. Для генерації дерева off-chain використовуємо бібліотеку OpenZeppelin MerkleProof.
Як Merkle drop вирішує проблему газу?
Меркл-дерево дозволяє не зберігати повний список на блокчейні. Натомість — тільки корінь (32 байти). Користувач доводить свою частку, надаючи proof розміром ~2–12 хешів. Витрати газу на claim становлять ~50 000 gas, що в десятки разів менше, ніж при push-підході. Merkle drop виграє по газу в 3–5 разів порівняно з batch transfer при списках від 10 000 адрес.
Як ми проектуємо систему: на практиці
Для одного великого DeFi-проєкту ми впровадили гібридну схему: 40% токенів розподілялися через Merkle drop без KYC, 60% — через інтерфейс із Gitcoin Passport. Результат: 92% адрес із whitelist заклеймили токени в перший тиждень, 97% з них не продали на DEX протягом місяця. Тематичні мисливці (snapshot hunters) втратили 80% своєї частки через tiered-множники.
| Категорія | Критерій | Множник |
|---|---|---|
| OG users | Перша транзакція > 12 місяців тому | 3x |
| Active users | > 10 транзакцій за останні 6 місяців | 2x |
| Regular users | Хоча б 1 транзакція за останні 3 місяці | 1x |
| Snapshot hunters | Транзакція за останні 2 тижні | 0.5x |
Для push-сценаріїв (компенсація після exploit, retroactive rewards) використовуємо batch transfer із батчами по 200–500 адрес. Оптимальний розмір розраховується під block gas limit мережі — на Polygon це 600–800 адрес, на Ethereum — 250–300.
function disperseToken( IERC20 token, address[] calldata recipients, uint256[] calldata amounts ) external { uint256 total = 0; for (uint256 i = 0; i < amounts.length; i++) { total += amounts[i]; } token.transferFrom(msg.sender, address(this), total); for (uint256 i = 0; i < recipients.length; i++) { token.transfer(recipients[i], amounts[i]); } } Порівняння підходів: push, pull, Merkle drop
| Підхід | Газ (проєкт) | UX | Sybil-резистентність |
|---|---|---|---|
| Push | Дуже високий | Високий | Немає |
| Pull (batch) | Середній | Середній | Немає |
| Merkle drop | Низький | Середній | Можлива |
Vesting: захист від тиску на ціну
Для команд та інвесторів комбінуємо mass-claim з персональними VestingWallet. При claim створюється контракт із розкладом (cliff + linear vesting). Це запобігає dump-тиску на ціну. Газ на деплой кожного vesting-контракту компенсується тим, що користувачі платять за claim самі. Vesting розподіляє розблокування в часі. Наприклад, cliff 3 місяці + рівномірне розблокування за 12 місяців. Це знижує волатильність та заохочує довгострокове утримання.
Як ми будуємо Merkle tree: покрокова інструкція
- Збираємо список адрес та сум з бази даних (CSV або скрипт).
- Будуємо Merkle tree за допомогою бібліотеки OpenZeppelin MerkleProof (ethers.js або Foundry).
- Деплоїмо контракт MerkleDistributor з коренем дерева та адресою токена.
- Публікуємо дерево в IPFS або на сайті для завантаження proofs.
- Користувачі клеймлять токени, надаючи proof.
Що входить в роботу
- Аналіз вимог: список отримувачів, категорії, vesting-умови, метод sybil-захисту.
- Архітектурний дизайн: Merkle drop або batch transfer, L1 чи L2.
- Розробка смарт-контрактів: Solidity 0.8.x, Foundry, unit-тести, fork-тести, fuzzing.
- Генерація Merkle tree: off-chain на ethers.js, валідація proof.
- Аудит безпеки: Slither, Mythril, Echidna — шукаємо reentrancy, storage collision, gas-витоки.
- Деплой через multi-sig: часовий lock на 24 години для відкату.
- Моніторинг: Tenderly та Dune — відсоток claimed, адреси-снайпери, retention.
- Документація та навчання: API для frontend, інструкція з адміністрування.
Строки та бюджет
Базовий Merkle distributor із frontend (React + RainbowKit) — 3–4 тижні, вартість від $15 000. Повноцінна система з sybil-захистом, tiered-розподілом, vesting та dashboard — 2–3 місяці, від $45 000. Вартість розраховується індивідуально. Зв'яжіться з нами для оцінки вашого проєкту. Отримайте консультацію з архітектури.
Чому обирають нас?
Ми гарантуємо безпеку контрактів завдяки проходженню аудиту та 5-річному досвіду в Solidity. Наш сертифікований стаж — 7+ років, понад 50 проєктів, сумарний розподіл $50M+. Ви отримуєте не просто код, а повністю протестовану систему з супроводом.







