Разработка контракта для airdrop
Мы часто видим классическую ошибку — пытаться раздать токены через loop с on-chain отправкой каждому адресу. При 10 000 получателей это 10 000 транзакций, десятки тысяч долларов газа и несколько часов работы. Наша команда с 5+ летним опытом в блокчейне рекомендует другой подход — Merkle distributor. Вы один раз on-chain устанавливаете Merkle root, а каждый получатель сам клеймит свои токены, платя только за своё включение. Это снижает ваш счёт за газ в сотни раз — Merkle distributor экономит до 99% газа по сравнению с прямыми переводами. Базовая библиотека для реализации — OpenZeppelin MerkleProof.
Какие проблемы решаем
- Заоблачный газ при большом списке. On-chain airdrop на 50 000 адресов может стоить >$20 000 только на газе. Merkle distributor экономит 99% средств.
- Уязвимость second preimage attack. Наивная реализация Merkle tree позволяет злоумышленнику подменить leaf. Мы используем double-hash leaf как в OpenZeppelin, что полностью блокирует атаку.
- Блокировка токенов без deadline. Токены могут навсегда остаться в контракте, если никто не заклеймил. Добавляем deadline с возможностью возврата оставшихся токенов владельцу.
- Отсутствие вестинга. Если токены должны распределяться постепенно, встраиваем линейный vesting прямо в distributor.
Сравнение подходов к airdrop
| Параметр | On-chain loop | Merkle distributor |
|---|---|---|
| Транзакций | N (каждый получатель — отдельный вызов) | 1 (установка root) + N (каждый claim — оплачивает получатель) |
| Газ (10k чел., ETH mainnet) | ~30 ETH | ~0.1 ETH (за root) + ~0.02 ETH на пользователя |
| Время выполнения | Часы | Минуты (после деплоя) |
| Безопасность | Зависит от газа | Проверенная MerkleProof библиотека |
Фактор экономии газа — до 300 раз при 10 000 адресах. Это особенно важно для команд с ограниченным бюджетом на запуск токена.
Как работает Merkle distributor: сравнение с loop
Переход на L2 снижает затраты на газ ещё на порядок: на Arbitrum или Base claim стоит копейки, а не доллары. Свяжитесь с нами для детального расчёта под вашу аудиторию.
Как мы это делаем
Используем проверенный стек: Solidity 0.8.x + OpenZeppelin библиотеки (MerkleProof, ERC20), для off-chain — TypeScript + @openzeppelin/merkle-tree. Развёртываем на Ethereum L1 или L2 (Arbitrum, Base, Optimism) по вашему выбору. Пример реализации с bit packing для claimed status:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; contract MerkleAirdrop { IERC20 public immutable token; bytes32 public immutable merkleRoot; mapping(uint256 => uint256) private claimedBitMap; constructor(address _token, bytes32 _merkleRoot) { token = IERC20(_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 leaf = keccak256(bytes.concat( keccak256(abi.encode(index, account, amount)) )); // Double-hash против second preimage attack require( MerkleProof.verify(merkleProof, merkleRoot, leaf), "Invalid proof" ); _setClaimed(index); require(token.transfer(account, amount), "Transfer failed"); emit Claimed(index, account, amount); } } Почему double-hash leaf?
Без него — second preimage attack: злоумышленник может подменить leaf данными, которые совпадают с intermediate node в дереве. OpenZeppelin использует keccak256(bytes.concat(keccak256(abi.encode(...)))) именно по этой причине.
Bit packing для claimed
Вместо mapping(address => bool) используем bit array: 256 статусов в одном uint256 slot. Экономия SLOAD газа существенна при большом количестве кламов.
Генерация Merkle tree off-chain
import { StandardMerkleTree } from "@openzeppelin/merkle-tree" const values = [ [0, "0xAddress1...", ethers.parseEther("100")], [1, "0xAddress2...", ethers.parseEther("250")], ] const tree = StandardMerkleTree.of(values, ["uint256", "address", "uint256"]) console.log("Merkle Root:", tree.root) for (const [i, v] of tree.entries()) { if (v[1] === "0xAddress1...") { const proof = tree.getProof(i) } } import fs from "fs" fs.writeFileSync("tree.json", JSON.stringify(tree.dump())) Proofs раздаёте через простой API: GET /proof?address=0x... → возвращает { index, amount, proof[] }. Пользователь вставляет эти данные в UI и вызывает claim. API можно реализовать на Fastify или Express — типовой код занимает около 50 строк.
Когда нужен vesting airdrop?
Если токены не должны быть доступны сразу — встраиваем линейный vesting в distributor. Клейм → токены на vesting schedule → пользователь забирает их по мере времени. Это защищает от мгновенного дампа.
Почему стоит выбрать L2 для airdrop?
На Ethereum mainnet claim стоит ~$2–10 за транзакцию. На Arbitrum, Base или Optimism — центы. Если ваша аудитория массовая, L2 снижают барьер входа. Мы поможем выбрать подходящий роллап и настроить мост для токенов.
| Сеть | Средняя стоимость claim | Время подтверждения |
|---|---|---|
| Ethereum L1 | $2–10 | ~15 секунд |
| Arbitrum | $0.01–0.05 | ~1 минута |
| Base | $0.01–0.03 | ~1 секунда |
| Optimism | $0.01–0.05 | ~1 минута |
Процесс работы
- Аналитика — обсуждаем список адресов, токен, L1/L2, нужен ли vesting, deadline.
- Проектирование — выбираем структуру Merkle tree, пишем спецификацию контракта.
- Разработка — пишем контракт с использованием OpenZeppelin, off‑chain скрипты, API.
- Тестирование — unit тесты, фаззинг (Echidna), симуляция в Tenderly.
- Деплой — на выбранную сеть, верификация контракта, тестовый claim.
- Поддержка — мониторинг, помощь пользователям при проблемах с claim.
Что входит в работу
- Смарт-контракт (Merkle distributor с опциями deadline/vesting)
- Off-chain генератор Merkle tree + TypeScript скрипты
- API для раздачи proofs (Fastify/Express)
- Пример UI интеграции (React + viem)
- Документация по деплою и эксплуатации
- 2 недели пост-запуска поддержки
Мы используем Echidna для fuzzing контракта, проверяя корректность проверки proof, невозможность повторного claim и газовые лимиты. Симуляция в Tenderly позволяет воспроизвести любую транзакцию до деплоя.
Сроки и стоимость
Стандартная реализация под ключ занимает от 1 до 2 недель. Стоимость рассчитывается индивидуально в зависимости от опций (vesting, gasless claim, кастомный UI). Оценим ваш проект за один рабочий день — просто напишите нам, и мы подготовим предложение. Закажите реализацию под ключ уже сегодня.







