Разработка контракта для 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). Оценим ваш проект за один рабочий день — просто напишите нам, и мы подготовим предложение. Закажите реализацию под ключ уже сегодня.
Разработка токенов: ERC-20, токеномика, вестинг
«ERC-20 — это просто» — фраза, после которой начинаются проблемы. Базовый transfer написать несложно. Но токен, у которого через шесть месяцев не происходит инфляционный коллапс, governance работает как задумано, а вестинг нельзя обойти через хитрую схему с делегированием — это уже проектирование.
ERC-20: что под капотом
Стандарт ERC-20 — девять функций. Сложность начинается с расширений:
ERC-20Permit (EIP-2612) — gasless approve через подпись. Пользователь подписывает permit(owner, spender, value, deadline, v, r, s) off-chain, spender вызывает permit() + transferFrom() в одной транзакции. Это убирает отдельный approve step. Но: подпись можно перехватить и использовать — нужен deadline и проверка nonce.
ERC-20Votes (EIP-5805) — snapshot балансов для governance. Checkpoint-система хранит историю балансов по номеру блока. getPastVotes(address, blockNumber) — баланс на момент создания proposal, а не текущий. Это предотвращает flash loan governance attack: нельзя занять токены и проголосовать ими в одной транзакции.
Rebasing токены (stETH, Ampleforth) — balanceOf меняется автоматически через изменение internal shares ratio. Высокая сложность интеграции: большинство DeFi протоколов не работают корректно с rebasing без wrapping в non-rebasing версию.
Fee-on-transfer токены — при каждом transfer снимается процент. Ломают AMM расчёты: пул получает меньше, чем ожидал. Uniswap v2/v3 не поддерживают fee-on-transfer нативно — нужны специальные pair/router.
Tokenomics: где математика превращается в экономику
Токеномика — это не таблица в Excel с суммой 100%. Это модель инцентивов, которая либо работает в долгосрочной перспективе, либо создаёт давление продаж которое убьёт проект.
Emission schedule и инфляция
Фиксированный supply (Bitcoin-модель) — deflation через burn механику или просто ограниченное количество. Подходит для store-of-value или utility токенов с ограниченным спросом на новые токены.
Инфляционная модель (Ethereum post-Merge, Curve) — новые токены выпускаются для стимулирования участников. Нужен баланс: emission должен быть ниже или равен value capture протоколом. Если протокол зарабатывает $100k/месяц, а эмиссия в рыночной стоимости $500k/месяц — постоянное давление продаж неизбежно.
Halving schedules (Bitcoin-style) — уменьшение emission со временем. Создаёт предсказуемость, но требует что утилити токена росла чтобы компенсировать падающие rewards для stakers/validators.
Supply distribution
| Категория |
Типичный диапазон |
Риск |
| Команда + advisors |
15–20% |
Dumping при unlock |
| Investors (seed, private) |
15–25% |
Координированный выход |
| Treasury / DAO |
20–35% |
Governance capture |
| Ecosystem / grants |
10–20% |
Неэффективное распределение |
| Public sale / LBP |
5–15% |
Недооценка на LBP → whale capture |
| Liquidity provision |
5–10% |
Mercenary capital |
Нет универсальной формулы. Есть принцип: никакой одной сущности не должно принадлежать >33% voting power при запуске. Иначе governance — фикция.
Vesting контракты: детали имеют значение
Linear vesting с cliff — стандарт для команды и инвесторов. cliff — период после TGE, в течение которого ничего не доступно. После cliff: линейный unlock до duration.
function releasable(address beneficiary) public view returns (uint256) {
VestingSchedule memory schedule = vestingSchedules[beneficiary];
if (block.timestamp < schedule.cliff) return 0;
uint256 elapsed = block.timestamp - schedule.cliff;
uint256 vestingDuration = schedule.duration - (schedule.cliff - schedule.start);
uint256 vested = schedule.totalAmount * elapsed / vestingDuration;
return vested - schedule.released;
}
Типичные ошибки при реализации:
Revocable vesting без timelock — owner может отозвать vesting мгновенно. Если owner key скомпрометирован или команда недобросовестна — все unvested токены могут быть отозваны. Решение: revocation через multisig + governance vote.
Cliff не блокирует governance права — если используется ERC-20Votes, recipient может делегировать voting power с первого дня, даже если токены ещё не unlocked. Нужно явно разделить voting power и claim logic.
Отсутствие emergency pause — если обнаружена уязвимость в vesting контракте, нужна возможность приостановить claim. Pausable + timelock на unpause.
Liquidity Bootstrapping
Запуск ликвидности — критический момент. Три основных подхода:
Balancer LBP (Liquidity Bootstrapping Pool) — временный Balancer пул с высоким начальным весом токена (90/10 проект-токен/USDC) который автоматически снижается до 50/50 за несколько дней. Создаёт нисходящее ценовое давление, препятствуя ботам скупить всё по одной цене. После LBP ликвидность переносится в постоянный пул.
Fjord Foundry — специализированная платформа для LBP и fair launches. Меньше операционного overhead чем прямая интеграция с Balancer.
Uniswap v3 с ограниченным range — добавить ликвидность в узкий диапазон вокруг начальной цены. Высокая capital efficiency, но требует активного управления range.
TWAMM (Time-Weighted AMM) — механика для постепенной продажи/покупки больших объёмов без slippage. Paradigm предложил, реализован в FraxSwap.
Governance токены и voting механики
OpenZeppelin Governor — стандартная реализация on-chain governance. Модульная архитектура: GovernorVotes для counting, GovernorTimelockControl для timelock execution, GovernorSettings для изменяемых параметров.
Quorum — минимальный процент supply для валидности голосования. Слишком высокий quorum = apathy failure (не набирается голосов). Слишком низкий = whale capture. Compound установил quorum 400k COMP (4% supply) — на практике достигается редко без координации крупных holders.
Flash loan governance attack — атакующий занимает токены через flash loan, делегирует их себе, создаёт proposal или голосует, возвращает токены. ERC-20Votes с snapshot по номеру блока полностью блокирует это: нужно иметь токены на момент создания snapshot, который берётся в момент создания proposal.
Delegation — пользователи с малыми балансами часто не голосуют. Liquid delegation (как в Optimism) позволяет делегировать voting power конкретным addresses (delegates) без передачи ownership токенов.
Стек для токен-разработки
Контракты: Solidity 0.8.x, OpenZeppelin Contracts 5.x (ERC20, ERC20Permit, ERC20Votes, Governor, TimelockController, TokenVesting)
Аудит токеномики: Python модели с симуляцией emission/demand, cadCAD для complex systems modeling
Деплой и управление: Foundry scripts, Gnosis Safe для treasury, OpenZeppelin Defender для автоматизации
Аналитика: Dune Analytics для on-chain метрик, Token Terminal для protocol revenue
Процесс
Tokenomics design — модель supply, allocation, emission schedule, vesting. Стресс-тестирование сценариев (bear market, whale exit, governance capture attempt).
Контракт разработка — ERC-20 + extensions, vesting, governance. Foundry fuzz тесты на vesting calculations, governance thresholds.
Аудит — особое внимание на governance attack vectors, vesting bypass, permit replay attacks.
LBP / launch — выбор механики, настройка параметров, мониторинг первых 24 часов.
Post-launch — мониторинг supply distribution через Dune, governance participation metrics, treasury management.
Сроки
- ERC-20 с permit и basic governance: 2–3 недели
- Vesting контракт с revocation и cliff: 2–4 недели
- Полный governance (Governor + Timelock + Token): 4–7 недель
- Токен + LBP + governance + vesting: 8–14 недель