Разработка ICO-платформы
ICO в 2024 году — не то же самое, что в 2017-м. Сырой сайт с MetaMask кнопкой и whitepaper в PDF уже не продаёт. Сегодня ICO платформа — это полноценная инфраструктура: KYC/AML верификация участников, мультисетевые смарт-контракты, регулируемая структура токена, и аналитика для команды проекта. Строить это с нуля имеет смысл только для платформы, которая будет обслуживать множество проектов. Для одного проекта проще использовать существующие launchpad протоколы (Polkastarter, DAO Maker). Если вы строите платформу — тогда поговорим о реальной архитектуре.
Архитектура мультипроектной платформы
ICO платформа состоит из нескольких независимых слоёв:
┌─────────────────────────────────────────────────────┐
│ Project Onboarding │ Investor Portal │ Admin │
├─────────────────────────────────────────────────────┤
│ Business Logic Layer │
│ Project Registry │ Sale Engine │ Vesting │ KYC │
├─────────────────────────────────────────────────────┤
│ Smart Contract Layer │
│ SaleFactory │ VestingVault │ AllocationRegistry │
├─────────────────────────────────────────────────────┤
│ Infrastructure │
│ Multichain indexer │ Price feeds │ Compliance API │
└─────────────────────────────────────────────────────┘
Factory pattern для деплоя контрактов
Каждый проект на платформе получает свой набор смарт-контрактов. Factory исключает ручной деплой и снижает риск ошибок:
contract ICOFactory {
address public immutable saleImplementation; // EIP-1167 minimal proxy
address public immutable vestingImplementation;
struct DeployedProject {
address saleContract;
address vestingContract;
address projectToken;
uint256 deployedAt;
address projectOwner;
}
mapping(bytes32 => DeployedProject) public projects;
mapping(address => bytes32) public contractToProject;
event ProjectDeployed(
bytes32 indexed projectId,
address saleContract,
address vestingContract,
address projectOwner
);
function deployProject(
bytes32 projectId,
address projectToken,
SaleConfig calldata saleConfig,
VestingConfig calldata vestingConfig
) external onlyVerifiedProject(projectId) returns (address sale, address vesting) {
// EIP-1167 minimal proxy — клонирование реализации
// Экономит gas: вместо деплоя полного контракта — 45-байтный proxy
sale = Clones.clone(saleImplementation);
vesting = Clones.clone(vestingImplementation);
// Инициализация (вместо constructor, так как proxy)
ICOSale(sale).initialize(
projectToken,
saleConfig,
vesting,
address(this) // платформа как admin
);
IVestingVault(vesting).initialize(
projectToken,
vestingConfig,
sale // только sale contract может добавлять получателей
);
projects[projectId] = DeployedProject({
saleContract: sale,
vestingContract: vesting,
projectToken: projectToken,
deployedAt: block.timestamp,
projectOwner: msg.sender
});
contractToProject[sale] = projectId;
contractToProject[vesting] = projectId;
emit ProjectDeployed(projectId, sale, vesting, msg.sender);
}
}
EIP-1167 minimal proxy снижает стоимость деплоя с ~2–3M gas до ~45k gas. При 100 проектах в год экономия ощутима.
KYC/AML интеграция
Регуляторная реальность: большинство ICO сегодня требует хотя бы базового KYC для участников, а для US пользователей — геоблокировку или аккредитованный инвестор статус.
On-chain KYC registry
contract KYCRegistry {
enum KYCStatus {
None,
PendingVerification,
Approved,
Rejected,
Expired
}
struct KYCRecord {
KYCStatus status;
uint8 tier; // 1 = базовый, 2 = enhanced
bytes3 countryCode; // ISO 3166-1 alpha-3
uint256 verifiedAt;
uint256 expiresAt; // KYC устаревает, нужно обновление
bool isAccreditedInvestor; // для US accredited investor rule
}
mapping(address => KYCRecord) public records;
mapping(address => bool) public kycProviders; // Sumsub, Onfido etc
// KYC провайдер обновляет статус после верификации off-chain
function updateKYCStatus(
address user,
KYCStatus status,
uint8 tier,
bytes3 countryCode,
bool isAccredited,
uint256 validityPeriod // обычно 1 год
) external onlyKYCProvider {
records[user] = KYCRecord({
status: status,
tier: tier,
countryCode: countryCode,
verifiedAt: block.timestamp,
expiresAt: block.timestamp + validityPeriod,
isAccreditedInvestor: isAccredited
});
emit KYCUpdated(user, status, countryCode);
}
function isEligible(
address user,
uint8 requiredTier,
bytes3[] memory blockedCountries
) external view returns (bool) {
KYCRecord memory record = records[user];
if (record.status != KYCStatus.Approved) return false;
if (block.timestamp > record.expiresAt) return false;
if (record.tier < requiredTier) return false;
// Проверка геоблокировки
for (uint i = 0; i < blockedCountries.length; i++) {
if (record.countryCode == blockedCountries[i]) return false;
}
return true;
}
}
Интеграция с Sumsub (API)
import requests
import hmac
import hashlib
import time
class SumsubClient:
BASE_URL = "https://api.sumsub.com"
def __init__(self, app_token: str, secret_key: str):
self.app_token = app_token
self.secret_key = secret_key
def create_applicant(self, external_user_id: str, level_name: str = "basic-kyc") -> dict:
endpoint = f"/resources/applicants?levelName={level_name}"
payload = {
"externalUserId": external_user_id,
"fixedInfo": {},
"requiredIdDocs": {
"docSets": [{"idDocSetType": "IDENTITY", "types": ["PASSPORT", "ID_CARD"]}]
}
}
return self._make_request("POST", endpoint, payload)
def get_applicant_status(self, applicant_id: str) -> dict:
endpoint = f"/resources/applicants/{applicant_id}/status"
return self._make_request("GET", endpoint)
def _make_request(self, method: str, endpoint: str, payload: dict = None) -> dict:
ts = str(int(time.time()))
body = json.dumps(payload) if payload else ""
sign_str = ts + method + endpoint + body
signature = hmac.new(
self.secret_key.encode(), sign_str.encode(), hashlib.sha256
).hexdigest()
headers = {
"X-App-Token": self.app_token,
"X-App-Access-Ts": ts,
"X-App-Access-Sig": signature,
"Content-Type": "application/json"
}
response = requests.request(
method, self.BASE_URL + endpoint,
headers=headers, data=body
)
return response.json()
После одобрения KYC провайдер вызывает webhook → ваш backend вызывает kycRegistry.updateKYCStatus() → пользователь может участвовать в ICO.
Мультисетевая архитектура
ICO платформа должна поддерживать несколько сетей: Ethereum mainnet (крупные инвесторы), Polygon/Base (розничные участники, меньший gas), BSC (азиатская аудитория). Одни контракты для всех сетей — через deterministicAddress деплой.
// Единый deployment скрипт для всех сетей
// CREATE2 позволяет иметь одинаковый адрес в разных сетях
const { ethers } = require("hardhat");
async function deployToChain(chainConfig) {
const Factory = await ethers.getContractFactory("ICOFactory");
// Deterministicly same address across chains
const salt = ethers.utils.id("ICO_PLATFORM_V1");
const factory = await Factory.deploy({
gasPrice: chainConfig.gasPrice
});
console.log(`Deployed to ${chainConfig.name}: ${factory.address}`);
return factory.address;
}
// Запускаем параллельно
const deployments = await Promise.all([
deployToChain({ name: "ethereum", gasPrice: "20gwei" }),
deployToChain({ name: "polygon", gasPrice: "50gwei" }),
deployToChain({ name: "base", gasPrice: "0.1gwei" }),
]);
Агрегация данных из нескольких сетей
Фронтенд должен показывать суммарный raised across all chains. Архитектура индексера:
Chain 1 (Ethereum) ──┐
Chain 2 (Polygon) ──┼──→ Event Indexer (Goldsky/Ponder) ──→ PostgreSQL
Chain 3 (Base) ──┘ ↓
GraphQL API
↓
Frontend Dashboard
Ponder (open-source TypeScript indexer) или Goldsky Mirror для мультичейн индексирования. The Graph поддерживает мультисетевые subgraphs, но объединение в единый API требует отдельного агрегатора.
Управление платформой: комиссии и governance
contract PlatformFeeManager {
uint256 public platformFeePercent = 250; // 2.5% от raised
address public feeRecipient; // multisig платформы
// Проекты могут предложить белый список для снижения комиссии
mapping(bytes32 => uint256) public projectFeeOverride;
function calculateFee(bytes32 projectId, uint256 amount)
public view returns (uint256)
{
uint256 feePercent = projectFeeOverride[projectId] > 0
? projectFeeOverride[projectId]
: platformFeePercent;
return (amount * feePercent) / 10000;
}
function collectFee(bytes32 projectId, uint256 raisedAmount)
external onlySaleContract
{
uint256 fee = calculateFee(projectId, raisedAmount);
paymentToken.transferFrom(
address(saleContracts[projectId]),
feeRecipient,
fee
);
}
}
Backend: ключевые сервисы
Project verification service — проверка команды проекта (KYB, smart contract audit requirement, tokenomics review). Не каждый проект, заплативший listing fee, должен появляться на платформе.
Price calculation service — при multi-currency payments (ETH, BNB, MATIC, USDC) нужно конвертировать contribution в базовую единицу расчёта. Chainlink для on-chain, CoinGecko API для off-chain отображения.
Notification service — email/telegram уведомления: KYC одобрен, раунд открылся, транзакция подтверждена, vesting claim available. Без этого conversion rate participation резко падает.
Регуляторные соображения
US пользователи — классический Reg D exemption (accredited investors only) или Reg S (только non-US). Геоблокировка US IP + KYC страна не USA. Reg A+ позволяет retail US участие, но требует SEC filing и обойдётся в $500k+ в legal fees.
EU (MiCA) — с декабря 2024 utility tokens требуют whitepaper согласно формату MiCA, некоторые категории требуют authorization. Asset-referenced tokens и e-money tokens требуют лицензирования.
Офферинг через LP токены или defi wrappers не снимает регуляторную ответственность, если сделка по существу является investment contract (тест Howey применяется в US).
Сроки и этапы
| Фаза | Содержание | Срок |
|---|---|---|
| Architecture & legal design | Регуляторная структура, смарт-контракт архитектура | 3–4 нед |
| Smart contracts | Factory, Sale, Vesting, KYC Registry | 5–7 нед |
| Backend services | Project onboarding, KYC integration, indexer | 6–8 нед |
| Frontend | Investor portal, project dashboard, admin | 6–10 нед |
| Security audit | Contracts + backend | 4–6 нед |
| Testnet & QA | 3–4 нед | |
| Launch & monitoring | 2 нед |
Полный цикл до production: 7–10 месяцев. Regulatory framework установить до начала разработки — не после. Retroactive compliance обходится дороже изначально правильной структуры.







