IEO Platform Development
IEO (Initial Exchange Offering) is token sale through a centralized trading platform that acts as intermediary between project and investors. Binance Launchpad, Huobi Prime, KuCoin Spotlight are classic examples. Platform assumes KYC/AML, project verification and sale execution, project gets trust of platform's audience and guaranteed listing.
Your own IEO platform—something between exchange and launchpad. It's not just smart contract for sales: it's complete product with project verification, participant allocation mechanism, KYC system, and often—platform's own token giving allocation priority.
System Architecture
Components
| Component | Responsibility |
|---|---|
| Project Registry | Project verification and data storage |
| Allocation Engine | Distribution of lots between participants |
| KYC/AML Gateway | Verification provider integration |
| Token Sale Contract | On-chain sale execution |
| Staking Contract | Platform token staking for tier-system |
| Distribution Contract | Vesting and token claim |
| Admin Dashboard | Project and sale parameter management |
Tier System Based on Staking
Most successful launchpad platforms (Polkastarter, TrustPad, GameFi) use: more user stakes, higher tier, bigger guaranteed allocation.
contract TierStaking {
struct Tier {
uint256 minStake; // minimum stake for this tier
uint256 multiplier; // allocation multiplier (basis points)
uint256 poolWeight; // weight in lottery pool
}
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
}
}
Bonus for long lock—standard practice: 12-month stake gives higher tier than 1-month with same token amount.
Allocation Distribution Mechanism
Two main approaches:
Guaranteed Allocation (FCFS or Weighted)
Each participant gets guaranteed allocation proportional to tier. Whitelist with sizes. Simple, predictable, but may leave tokens unsold if not all whitelist participants buy.
contract GuaranteedSale {
mapping(address => uint256) public maxAllocation; // set offchain
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 with Oversubscription
Fair approach for high demand. Participants register for lottery. After registration closes—random winner selection proportional to tier (high tier = more lottery tickets).
For honest on-chain lottery—Chainlink VRF is mandatory. Pseudorandomness via blockhash or block.timestamp is manipulated by miners/validators:
contract LotteryAllocation is VRFConsumerBaseV2Plus {
mapping(uint256 => address[]) public tierParticipants; // tier => participants
mapping(address => bool) public isWinner;
uint256 public randomSeed;
// Participant registration by tiers
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);
}
// Request randomness from 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 Integration
IEO platform usually works in regulatory space requiring user verification. Variants:
Fractal ID / Synaps / Sumsub — SaaS KYC providers. User verifies in their system, gets verifiable credential or whitelist-add via webhook.
Centrally managed on-chain whitelist. After off-chain KYC — operator adds address to on-chain whitelist. Simplest variant, but centralized.
Soulbound tokens (EIP-5484). KYC verification expressed as non-transferable NFT. Sale contract checks for such token. More Web3-native approach, but needs working SBT infrastructure.
// KYC check via whitelist + optionally SBT
modifier kycVerified() {
require(
kycWhitelist[msg.sender] ||
IKYCSoulbound(kycSBTContract).balanceOf(msg.sender) > 0,
"KYC required"
);
_;
}
Escrow and Fund Distribution
Funds from sale shouldn't go directly to project—protects buyers. Standard scheme:
Escrow with milestones. Funds locked in contract, released in parts as milestones achieved. Milestone confirmation via token holder voting, DAO, or oracle (if measurable on-chain).
contract IEOEscrow {
struct Milestone {
string description;
uint256 releasePercent; // % of amount
uint256 releaseTime; // not earlier
bool approved;
uint256 approvalVotes; // votes for
uint256 rejectionVotes;
}
Milestone[] public milestones;
uint256 public totalRaised;
address public project;
// Token holder voting on milestone
function voteMilestone(uint256 milestoneId, bool approve) external {
require(projectToken.balanceOf(msg.sender) > 0, "Must hold tokens");
// ... voting logic with weight by balance
}
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);
}
}
Listing and Post-Sale Liquidity
Part of IEO platform responsibility—ensure liquidity post-sale. Automatic liquidity addition to DEX from raised funds:
function finalizeAndAddLiquidity() external onlyOwner {
require(saleFinished(), "Sale not finished");
uint256 liquidityETH = (totalRaised * liquidityPercent) / 100;
uint256 liquidityTokens = calculateLiquidityTokens(liquidityETH);
// Approve and add liquidity to Uniswap V2/V3
token.approve(address(uniswapRouter), liquidityTokens);
uniswapRouter.addLiquidityETH{value: liquidityETH}(
address(token),
liquidityTokens,
0, // slippage — use minimums in production
0,
address(this), // LP tokens to contract (locked)
block.timestamp + 3600
);
// Lock LP tokens for certain period
lpLockEnd = block.timestamp + 180 days;
}
Admin Panel and Operational Tools
For platform management—backend with:
- Project management dashboard (verification, sale params)
- Merkle whitelist generation tools (CSV → Merkle root)
- Real-time sale monitoring (raised/cap, participants)
- KYC provider integration (webhooks, API)
- Auto notifications (Telegram, Email) on key events
Development Timeline
| Component | Duration |
|---|---|
| Smart contracts (staking, sale, escrow, distribution) | 6–8 weeks |
| Backend API + admin panel | 4–6 weeks |
| KYC integration | 1–2 weeks |
| Frontend (user interface) | 4–6 weeks |
| Smart contract audit | 3–4 weeks |
| Testing + QA | 2–3 weeks |
Full cycle from spec to launch-ready platform: 4–5 months. Audit for platform managing real funds is mandatory. Budget: $15,000–$50,000 depending on chosen team.







