Разработка IEO-платформы
IEO (Initial Exchange Offering) — продажа токенов через централизованную торговую площадку, которая выступает посредником между проектом и инвесторами. Binance Launchpad, Huobi Prime, KuCoin Spotlight — классические примеры. Платформа берёт на себя KYC/AML, верификацию проекта и проведение продажи, проект получает доверие аудитории биржи и гарантированный листинг.
Собственная IEO-платформа — это что-то среднее между биржей и launchpad. Это не просто смарт-контракт продажи: это полноценный продукт с верификацией проектов, аллокационным механизмом для участников, KYC системой, и часто — собственным токеном платформы, который даёт приоритет в аллокациях.
Архитектура системы
Компоненты платформы
| Компонент | Ответственность |
|---|---|
| Project Registry | Верификация и хранение данных проектов |
| Allocation Engine | Распределение лотов между участниками |
| KYC/AML Gateway | Интеграция с провайдером верификации |
| Token Sale Contract | Исполнение продажи on-chain |
| Staking Contract | Стейкинг платформенного токена для tier-системы |
| Distribution Contract | Vesting и клейм токенов |
| Admin Dashboard | Управление проектами, параметрами продаж |
Tier-система на основе стейкинга
Большинство успешных launchpad-платформ (Polkastarter, TrustPad, GameFi) используют модель: чем больше пользователь застейкал платформенный токен, тем выше его tier и тем больше гарантированная аллокация.
contract TierStaking {
struct Tier {
uint256 minStake; // минимальный стейк для этого тира
uint256 multiplier; // множитель аллокации (в basis points)
uint256 poolWeight; // вес в lottery пуле
}
Tier[] public tiers; // [Bronze, Silver, Gold, Platinum, Diamond]
struct StakeInfo {
uint256 amount;
uint256 stakedAt;
uint256 lockEnd;
uint8 tier;
}
mapping(address => StakeInfo) public stakes;
function stake(uint256 amount, uint256 lockDuration) external {
require(lockDuration >= MIN_LOCK, "Lock too short");
token.safeTransferFrom(msg.sender, address(this), amount);
uint8 tier = calculateTier(amount);
stakes[msg.sender] = StakeInfo({
amount: amount,
stakedAt: block.timestamp,
lockEnd: block.timestamp + lockDuration,
tier: tier
});
emit Staked(msg.sender, amount, tier);
}
function calculateTier(uint256 amount) public view returns (uint8) {
for (uint8 i = uint8(tiers.length - 1); i >= 0; i--) {
if (amount >= tiers[i].minStake) return i;
}
return 0; // no tier
}
}
Бонус за длительный лок — стандартная практика: стейк на 12 месяцев даёт higher tier чем стейк на 1 месяц с тем же количеством токенов.
Механизм распределения аллокаций
Это наиболее технически интересная часть. Существуют два основных подхода:
Guaranteed allocation (FCFS или weighted)
Каждый участник получает гарантированную аллокацию пропорционально тиру. Фактически — whitelist с размерами аллокации. Просто, предсказуемо, но может оставить часть токенов непроданными если не все whitelist участники купят.
contract GuaranteedSale {
mapping(address => uint256) public maxAllocation; // установлено оффчейн
mapping(address => uint256) public purchased;
function buy(uint256 amount) external payable {
require(saleActive(), "Sale not active");
require(purchased[msg.sender] + amount <= maxAllocation[msg.sender], "Exceeds allocation");
uint256 cost = amount * price;
require(msg.value >= cost, "Insufficient ETH");
purchased[msg.sender] += amount;
totalSold += amount;
// refund excess
if (msg.value > cost) payable(msg.sender).transfer(msg.value - cost);
}
}
Lottery с oversubscription
Более справедливый подход для высокого спроса. Участники регистрируются на участие в лотерее. После окончания регистрации — случайный выбор победителей пропорционально тиру (высокий тир = больше лотерейных билетов).
Для честной лотереи on-chain — Chainlink VRF обязателен. Псевдослучайность через blockhash или block.timestamp манипулируется майнерами/валидаторами:
contract LotteryAllocation is VRFConsumerBaseV2Plus {
mapping(uint256 => address[]) public tierParticipants; // tier => participants
mapping(address => bool) public isWinner;
uint256 public randomSeed;
// Регистрация участников по тирам
function register() external {
require(registrationActive(), "Registration closed");
StakeInfo memory info = staking.stakes(msg.sender);
require(info.tier > 0, "No tier");
tierParticipants[info.tier].push(msg.sender);
}
// Запрос случайности у Chainlink VRF
function requestRandomness() external onlyOwner {
uint256 requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: keyHash,
subId: subscriptionId,
requestConfirmations: 3,
callbackGasLimit: 500000,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
})
);
}
function fulfillRandomWords(uint256, uint256[] calldata randomWords) internal override {
randomSeed = randomWords[0];
_selectWinners();
}
function _selectWinners() internal {
uint256 seed = randomSeed;
for (uint8 tier = 5; tier >= 1; tier--) {
uint256 winnersForTier = tierWinners[tier];
address[] storage participants = tierParticipants[tier];
uint256 shuffleLen = participants.length;
// Fisher-Yates shuffle with random seed
for (uint256 i = 0; i < winnersForTier && i < shuffleLen; i++) {
uint256 j = i + (uint256(keccak256(abi.encodePacked(seed, tier, i))) % (shuffleLen - i));
(participants[i], participants[j]) = (participants[j], participants[i]);
isWinner[participants[i]] = true;
seed = uint256(keccak256(abi.encodePacked(seed, i)));
}
}
}
}
KYC/AML интеграция
IEO-платформа как правило работает с регуляторным пространством, требующим верификации пользователей. Варианты:
Fractal ID / Synaps / Sumsub — SaaS KYC провайдеры. Пользователь проходит верификацию в их системе, получает verifiable credential или whitelist-добавление через webhook.
On-chain whitelist управляемый централизованно. После KYC вне блокчейна — оператор добавляет адрес в on-chain whitelist. Простейший вариант, но централизованный.
Soulbound токены (EIP-5484). KYC верификация выражается как non-transferable NFT. Контракт продажи проверяет наличие такого токена. Более Web3-native подход, но требует работающей SBT-инфраструктуры.
// Проверка KYC через whitelist + необязательно SBT
modifier kycVerified() {
require(
kycWhitelist[msg.sender] ||
IKYCSoulbound(kycSBTContract).balanceOf(msg.sender) > 0,
"KYC required"
);
_;
}
Escrow и распределение средств
Средства от продажи не должны поступать напрямую проекту — это защита покупателей. Стандартная схема:
Escrow с milestones. Средства блокируются в контракте, освобождаются частями по мере достижения milestone. Подтверждение milestone — либо через голосование держателей токенов, либо через DAO, либо через оракул (если milestone измерим on-chain).
contract IEOEscrow {
struct Milestone {
string description;
uint256 releasePercent; // % от суммы
uint256 releaseTime; // не раньше этого времени
bool approved;
uint256 approvalVotes; // голосов за
uint256 rejectionVotes;
}
Milestone[] public milestones;
uint256 public totalRaised;
address public project;
// Голосование токен-холдеров по milestone
function voteMilestone(uint256 milestoneId, bool approve) external {
require(projectToken.balanceOf(msg.sender) > 0, "Must hold tokens");
// ... логика голосования с weight по балансу
}
function releaseFunds(uint256 milestoneId) external {
Milestone storage ms = milestones[milestoneId];
require(ms.approved, "Not approved");
require(block.timestamp >= ms.releaseTime, "Too early");
uint256 amount = (totalRaised * ms.releasePercent) / 100;
payable(project).transfer(amount);
}
}
Листинг и post-sale ликвидность
Для IEO-платформы часть ответственности — обеспечить ликвидность после продажи. Автоматическое добавление ликвидности в DEX из части raised средств:
function finalizeAndAddLiquidity() external onlyOwner {
require(saleFinished(), "Sale not finished");
uint256 liquidityETH = (totalRaised * liquidityPercent) / 100;
uint256 liquidityTokens = calculateLiquidityTokens(liquidityETH);
// Approve и добавление ликвидности в Uniswap V2/V3
token.approve(address(uniswapRouter), liquidityTokens);
uniswapRouter.addLiquidityETH{value: liquidityETH}(
address(token),
liquidityTokens,
0, // slippage — в production использовать минимальные значения
0,
address(this), // LP токены к контракту (залочены)
block.timestamp + 3600
);
// Лок LP токенов на определённый срок
lpLockEnd = block.timestamp + 180 days;
}
Admin panel и операционные инструменты
Для управления платформой — backend с:
- Dashboard управления проектами (верификация, параметры продажи)
- Инструменты для merkle whitelist generation (CSV → Merkle root)
- Мониторинг продажи в реальном времени (raised/cap, участники)
- Интеграция с KYC провайдером (webhooks, API)
- Автоматические уведомления (Telegram, Email) при ключевых событиях
Сроки разработки
| Компонент | Срок |
|---|---|
| Смарт-контракты (стейкинг, продажа, эскроу, дистрибуция) | 6–8 недель |
| Backend API + admin panel | 4–6 недель |
| KYC интеграция | 1–2 недели |
| Frontend (пользовательский интерфейс) | 4–6 недель |
| Аудит смарт-контрактов | 3–4 недели |
| Тестирование + QA | 2–3 недели |
Полный цикл от ТЗ до launch-ready платформы: 4–5 месяцев. Аудит для платформы, которая будет управлять реальными средствами — обязателен. Бюджет на аудит: $15,000–$50,000 в зависимости от выбранной команды.







