Розроблення White-label launchpad
White-label launchpad — це не просто "зробіть як Polkastarter, але з вашим логотипом". Клієнти, які приходять з такою формулюванням, зазвичай не враховують головне: Polkastarter та TrustPad працюють тому, що за ними стоїть ліквідність та комьюніті. Технічно відтворити платформу — не складно. Але white-label launchpad цінний тільки коли є вхідний потік проектів, які хочуть провести IDO, та комьюніті, готове в них брати участь. Тому технічне рішення повинно бути гнучким, щоб власник платформи міг диференціюватися не функціями, а exлюзивними проектами та deal flow.
Архітектура white-label рішення
Правильна архітектура будується навколо параметризації, а не форків. Кожен клієнт отримує:
- Власний набір смарт-контрактів (не ділиться контрактами з іншими клієнтами)
- Настраюваний frontend з брендингом
- Admin panel для управління пулами та tier-системою
- Власний платформенний токен (опційонально)
Альтернатива — SaaS-модель на спільній інфраструктурі з ізоляцією по даних, але це не white-label у повному сенсі.
Базова контрактна система
// Фабрика пулів — центральний контракт платформи
contract LaunchpadFactory is AccessControl, Pausable {
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
// Реєстр усіх пулів, створених через цю фабрику
address[] public allPools;
mapping(address => bool) public isValidPool;
mapping(address => address[]) public projectPools; // project → їхні пулі
// Параметри платформи
address public feeRecipient;
uint256 public platformFee; // basis points (200 = 2% від raise)
// Whitelist схвалених токенів продажу
mapping(address => bool) public approvedTokens;
event PoolCreated(
address indexed pool,
address indexed saleToken,
address indexed creator,
PoolType poolType
);
enum PoolType { FIXED_PRICE, DUTCH_AUCTION, OVERFLOW }
function createPool(
PoolType poolType,
bytes calldata poolParams
) external onlyRole(OPERATOR_ROLE) whenNotPaused returns (address pool) {
if (poolType == PoolType.FIXED_PRICE) {
FixedPricePool.Config memory config = abi.decode(poolParams, (FixedPricePool.Config));
require(approvedTokens[address(config.saleToken)], "Token not approved");
pool = address(new FixedPricePool(config, feeRecipient, platformFee));
} else if (poolType == PoolType.DUTCH_AUCTION) {
pool = address(new DutchAuctionPool(abi.decode(poolParams, (DutchAuctionPool.Config)), feeRecipient, platformFee));
} else {
pool = address(new OverflowPool(abi.decode(poolParams, (OverflowPool.Config)), feeRecipient, platformFee));
}
allPools.push(pool);
isValidPool[pool] = true;
emit PoolCreated(pool, address(0), msg.sender, poolType);
return pool;
}
}
Tier система з платформенним токеном
Стейкинг платформенного токена — основний механізм утримання користувачів та створення цінності для власного токена launchpad:
contract LaunchpadStaking is ReentrancyGuard, Ownable {
IERC20 public immutable platformToken;
struct TierConfig {
string name; // "Bronze", "Silver", "Gold", "Diamond"
uint256 minStake; // мінімальний stake в platform token
uint256 weight; // вага при розподілі аллокацій (basis points)
bool guaranteed; // гарантована аллокація або lottery
uint256 multiplier; // множник аллокації (10000 = 1x)
}
TierConfig[] public tiers;
struct StakeInfo {
uint256 amount;
uint256 stakedAt;
uint256 lockUntil; // lock період перед IDO snapshots
}
mapping(address => StakeInfo) public stakes;
uint256 public snapshotBlock; // блок для snapshot перед IDO
mapping(uint256 => mapping(address => uint256)) public snapshotStakes;
// snapshot tier для конкретного IDO
function takeSnapshot(uint256 poolId) external onlyOwner {
// Фіксуємо балансі на момент snapshot
// Подальші зміни не впливають на аллокацію в цьому IDO
snapshotBlock = block.number;
emit SnapshotTaken(poolId, block.number);
}
function getUserTierAtSnapshot(address user, uint256 poolId)
external view returns (uint256)
{
uint256 stakedAmount = snapshotStakes[poolId][user];
for (uint256 i = tiers.length; i > 0; i--) {
if (stakedAmount >= tiers[i-1].minStake) return i - 1;
}
return type(uint256).max;
}
}
Lottery для нижніх тиров
Для Tier 1/2 (низький stake) зазвичай неможливо дати всім гарантовану аллокацію. Використовуємо lottery:
contract AllocationLottery {
// Chainlink VRF для верифікованої випадковості
VRFCoordinatorV2Interface public coordinator;
bytes32 public keyHash;
uint64 public subscriptionId;
mapping(uint256 => address[]) public lotteryParticipants; // poolId → учасники
mapping(uint256 => uint256) public requestToPool;
function requestLotteryResult(uint256 poolId) external onlyOwner returns (uint256 requestId) {
requestId = coordinator.requestRandomWords(
keyHash,
subscriptionId,
3, // confirmations
100000, // gas limit для callback
1 // numWords
);
requestToPool[requestId] = poolId;
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
uint256 poolId = requestToPool[requestId];
address[] storage participants = lotteryParticipants[poolId];
uint256 winners = winnersCount[poolId];
uint256 rand = randomWords[0];
// Fisher-Yates shuffle для чесного вибору переможців
for (uint256 i = 0; i < winners && i < participants.length; i++) {
uint256 j = i + (rand % (participants.length - i));
(participants[i], participants[j]) = (participants[j], participants[i]);
rand = uint256(keccak256(abi.encode(rand, i)));
}
// Перші `winners` адрес — переможці
emit LotteryCompleted(poolId, winners);
}
}
Multi-chain підтримка
Сучасний white-label launchpad повинен підтримувати кілька мереж — EVM-сумісні (Ethereum, BNB Chain, Polygon, Arbitrum, Avalanche) мінімум. Архітектурний підхід:
- Однакова кодова база контрактів деплоїться на кожну мережу
- Frontend переключається між мережами через wagmi chain config
- Subgraph (The Graph) деплоїться на кожну мережу окремо
- Backend API агрегує дані з кількох мереж через multicall
// wagmi config для multi-chain
import { createConfig, http } from "wagmi";
import { mainnet, polygon, bsc, arbitrum, avalanche } from "wagmi/chains";
export const config = createConfig({
chains: [mainnet, polygon, bsc, arbitrum, avalanche],
transports: {
[mainnet.id]: http(process.env.ETH_RPC),
[polygon.id]: http(process.env.POLYGON_RPC),
[bsc.id]: http(process.env.BSC_RPC),
[arbitrum.id]: http(process.env.ARB_RPC),
[avalanche.id]: http(process.env.AVAX_RPC),
},
});
KYC/AML інтеграція
Більшість юрисдикцій потребує KYC для участі в token sale. Інтеграція з Sumsub або Synaps:
// API endpoint для отримання KYC статусу
app.get("/api/kyc/status/:address", async (req, res) => {
const { address } = req.params;
// Перевіряємо статус у базі даних
const kycRecord = await db.kyc.findOne({ walletAddress: address.toLowerCase() });
if (!kycRecord || kycRecord.status !== "approved") {
return res.json({ approved: false, reason: kycRecord?.rejectionReason });
}
// Опційонально: записуємо on-chain через верифікований backend
// для платформ з on-chain KYC verification
res.json({ approved: true, tier: kycRecord.accreditationLevel });
});
Admin Panel
Функціональність для оператора платформи:
| Розділ | Функції |
|---|---|
| Pool management | Створення/редагування/закриття пулів |
| Project KYC | Верифікація проектів, які запитують IDO |
| Whitelist | Завантаження та управління whitelist'ами |
| Allocation | Ручна коректировка аллокацій |
| Tier config | Настройка рівнів та мінімального stake |
| Analytics | Raised по пулам, активні користувачі, конверсії |
| Fee management | Настройка платформенних комісій |
Моделі монетизації
White-label launchpad може монетизуватися кількома способами:
- Platform fee: 1.5–3% від зібраної суми при успішному IDO
- Token allocation: 3–5% токенів сейлу за послуги платформи
- Staking APY: дохідність для staker'ів частково фінансується з platform fees
- Premium listing: підвищена видимість для платіжних проектів
- White-label licensing: якщо продаєте саму платформу іншим операторам







