Разработка платформы для токенсейлов (launchpad)
Launchpad — это не просто «контракт который принимает ETH и отдаёт токены». Это инфраструктура с IDO механикой, whitelist системой, vesting, клэймом, защитой от ботов и иногда — децентрализованным governance для отбора проектов. Основная сложность не в самих смарт-контрактах, а в балансе: сделать систему достаточно открытой для участников, но устойчивой к MEV, снайперингу и sybil-атакам.
Ключевые механики и их реализация
Sale структуры
Три основных модели sale, каждая со своей механикой:
Fixed price sale — самый простой. Цена фиксирована, первые X участников получают аллокации. Проблема: боты снимают аллокации в первом блоке.
Overflow / Oversubscription model — участники вносят любую сумму, финальная цена определяется объёмом. Если собрано в 3 раза больше цели — каждый получает 1/3 от своего вклада обратно, 2/3 конвертируются в токены. Используют Polkastarter, CoinList.
Dutch auction — цена начинается высокой и снижается до момента, когда весь объём раскупается. Находит "справедливую цену" рынка, устойчив к снайперингу.
// Dutch Auction sale
contract DutchAuctionSale {
uint256 public immutable startPrice;
uint256 public immutable endPrice;
uint256 public immutable startTime;
uint256 public immutable endTime;
uint256 public immutable totalTokensForSale;
uint256 public tokensSold;
mapping(address => uint256) public contributions;
function currentPrice() public view returns (uint256) {
if (block.timestamp <= startTime) return startPrice;
if (block.timestamp >= endTime) return endPrice;
uint256 elapsed = block.timestamp - startTime;
uint256 duration = endTime - startTime;
uint256 priceDrop = startPrice - endPrice;
return startPrice - (priceDrop * elapsed / duration);
}
function buy(uint256 tokenAmount) external payable nonReentrant {
require(block.timestamp >= startTime && block.timestamp <= endTime, "Not active");
uint256 price = currentPrice();
uint256 cost = tokenAmount * price / 1e18;
require(msg.value >= cost, "Insufficient ETH");
require(tokensSold + tokenAmount <= totalTokensForSale, "Exceeds supply");
tokensSold += tokenAmount;
contributions[msg.sender] += tokenAmount;
// Возврат излишка
if (msg.value > cost) {
payable(msg.sender).transfer(msg.value - cost);
}
}
}
Whitelist и аллокация
Merkle tree whitelist — стандарт для gas-efficient whitelist. Список адресов хранится off-chain, on-chain хранится только root. Участник предоставляет proof при покупке:
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
bytes32 public merkleRoot;
function buyWithWhitelist(
uint256 tokenAmount,
uint256 maxAllocation, // аллокация для этого адреса (из листа)
bytes32[] calldata merkleProof
) external payable {
// Верифицируем что адрес в whitelist с указанной аллокацией
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, maxAllocation));
require(
MerkleProof.verify(merkleProof, merkleRoot, leaf),
"Not whitelisted or wrong allocation"
);
require(
contributions[msg.sender] + tokenAmount <= maxAllocation,
"Exceeds allocation"
);
// ... логика покупки
}
Merkle root обновляется при изменении whitelist — это дешевле чем хранить mapping всех адресов on-chain.
Tiered allocation — распространённая модель у ведущих launchpads (DAO Maker, GameFi, RedKite): размер аллокации зависит от количества застейканных платформенных токенов. Участники делятся на тиры (Bronze/Silver/Gold/Diamond), каждый тир имеет гарантированную аллокацию пропорционально количеству токенов в стейкинге.
struct TierConfig {
uint256 minStake; // минимальный стейк для входа в тир
uint256 allocationMultiplier; // в basis points, 10000 = 1x
uint256 guaranteedSlots; // гарантированные слоты (0 = lottery)
}
TierConfig[] public tiers;
function getUserTier(address user) public view returns (uint256) {
uint256 staked = stakingContract.stakedAmount(user);
for (uint256 i = tiers.length; i > 0; i--) {
if (staked >= tiers[i-1].minStake) return i-1;
}
return type(uint256).max; // не в тире
}
function getUserAllocation(address user) public view returns (uint256) {
uint256 tierIndex = getUserTier(user);
if (tierIndex == type(uint256).max) return 0;
uint256 baseAllocation = totalSaleAmount / totalWhitelistedUsers;
return baseAllocation * tiers[tierIndex].allocationMultiplier / 10000;
}
Vesting и claim
После IDO токены не выдаются сразу — это стандарт рынка. Типичный вестинг для launchpad: 20% TGE (Token Generation Event), остальное линейно за 6–12 месяцев.
contract TokenVesting {
struct VestingSchedule {
uint256 totalAmount;
uint256 claimedAmount;
uint256 tgePercent; // % доступный сразу после TGE, 10000 = 100%
uint256 cliffDuration; // задержка перед линейным вестингом
uint256 vestingDuration; // длительность линейного вестинга
uint256 tgeTimestamp;
}
mapping(address => VestingSchedule) public schedules;
function claimableAmount(address beneficiary) public view returns (uint256) {
VestingSchedule storage schedule = schedules[beneficiary];
if (schedule.totalAmount == 0) return 0;
uint256 tgeAmount = schedule.totalAmount * schedule.tgePercent / 10000;
if (block.timestamp < schedule.tgeTimestamp) {
return 0;
}
uint256 cliffEnd = schedule.tgeTimestamp + schedule.cliffDuration;
if (block.timestamp < cliffEnd) {
return tgeAmount > schedule.claimedAmount
? tgeAmount - schedule.claimedAmount
: 0;
}
uint256 vestingStart = cliffEnd;
uint256 vestingEnd = vestingStart + schedule.vestingDuration;
uint256 elapsed = min(block.timestamp, vestingEnd) - vestingStart;
uint256 vestedLinear = (schedule.totalAmount - tgeAmount)
* elapsed / schedule.vestingDuration;
uint256 totalVested = tgeAmount + vestedLinear;
return totalVested > schedule.claimedAmount
? totalVested - schedule.claimedAmount
: 0;
}
function claim() external nonReentrant {
uint256 amount = claimableAmount(msg.sender);
require(amount > 0, "Nothing to claim");
schedules[msg.sender].claimedAmount += amount;
saleToken.safeTransfer(msg.sender, amount);
emit TokensClaimed(msg.sender, amount);
}
}
Защита от MEV и ботов
Anti-sniper механики
Боты мониторят mempool и пытаются купить в первом же блоке продажи. Стандартные защиты:
Commit-reveal scheme — участник сначала отправляет commit = keccak256(amount, salt, address), в следующей фазе раскрывает amount и salt. Боты не могут знать точную сумму до reveal.
Randomized start — точное время старта добавляется как случайная задержка (например, случайный offset 0–300 секунд). Chainlink VRF для источника случайности.
Per-block limit — не более X покупок за блок, или максимальная сумма за блок:
uint256 public maxContributionPerBlock;
mapping(uint256 => uint256) public blockContributions;
function buy(uint256 amount) external payable {
require(
blockContributions[block.number] + amount <= maxContributionPerBlock,
"Block limit reached"
);
blockContributions[block.number] += amount;
// ...
}
KYC интеграция
Для регулируемых рынков — интеграция с KYC провайдерами. Фронтенд KYC (Sumsub, Onfido, Synaps) выдаёт подписанный JWT. Backend верифицирует JWT и добавляет адрес в whitelist через транзакцию.
Более on-chain вариант — Verifiable Credentials: KYC провайдер выдаёт VC, пользователь предоставляет ZK-proof что имеет валидный VC без раскрытия персональных данных. Реализации: Polygon ID, Worldcoin (с оговорками о приватности).
Административная панель и листинг
Launchpad — это не только смарт-контракты. Полноценный продукт включает:
Для проектов (листинг):
- Форма подачи заявки с верификацией контракта токена
- Admin workflow для одобрения/отклонения
- Настройка параметров sale: цена, hard cap, soft cap, даты, whitelist, вестинг
- Загрузка whitelist CSV → merkle tree генерация
Для участников:
- Dashboard с активными и прошедшими сейлами
- KYC онбординг
- Whitelist регистрация с подключённым кошельком
- Claim интерфейс с графиком вестинга
Для администраторов:
- Мониторинг прогресса сейла в реальном времени
- Emergency pause
- Управление whitelist (добавление/удаление)
- Вывод raised funds после финализации
Экономика платформы
Launchpad обычно монетизируется через:
- Процент от raise — 3–8% от собранных средств
- Токен аллокация — X% токенов продаваемого проекта
- Платформенный токен — стейкинг для получения аллокаций (lock-in экосистемы)
Этапы разработки
| Фаза | Содержание | Срок |
|---|---|---|
| Design | Sale механики, vesting схемы, tier структура, tokenomics | 2–3 нед |
| Core contracts | Sale, Vesting, Staking, Whitelist | 4–5 нед |
| Testing | Unit, integration, fork tests, fuzz | 2–3 нед |
| Backend | API, merkle tree generation, KYC integration | 3–4 нед |
| Frontend | Участник dashboard, admin panel | 4–5 нед |
| Audit | Контракты + backend | 3–4 нед |
| Testnet pilot | Один тестовый IDO | 2–3 нед |
Итого: 20–27 недель. Аудит критически важен — launchpads держат деньги участников и являются мишенью для атак.







