Token Sale Launchpad Platform Development
Launchpad is not just "a contract that accepts ETH and sends tokens." It's infrastructure with IDO mechanics, whitelist systems, vesting, claiming, bot protection, and sometimes—decentralized governance for project selection. The main complexity isn't in smart contracts themselves, but in balance: make the system open enough for participants yet resilient against MEV, sniping, and Sybil attacks.
Key Mechanics and Implementation
Sale Structures
Three main sale models, each with its own mechanics:
Fixed price sale — simplest. Price is fixed, first X participants get allocations. Problem: bots snipe allocations in first block.
Overflow / Oversubscription model — participants contribute any amount, final price determined by volume. If 3x goal raised — everyone gets 1/3 back, 2/3 converts to tokens. Used by Polkastarter, CoinList.
Dutch auction — price starts high and decreases until all supply is bought. Finds "fair market price," resistant to sniping.
// 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;
// Refund excess
if (msg.value > cost) {
payable(msg.sender).transfer(msg.value - cost);
}
}
}
Whitelist and Allocation
Merkle tree whitelist — gas-efficient whitelist standard. Address list stored off-chain, only root on-chain. Participant provides proof on purchase:
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
bytes32 public merkleRoot;
function buyWithWhitelist(
uint256 tokenAmount,
uint256 maxAllocation, // allocation from list
bytes32[] calldata merkleProof
) external payable {
// Verify address in whitelist with allocation
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"
);
// ... purchase logic
}
Merkle root updates when whitelist changes — cheaper than storing all addresses on-chain.
Tiered allocation — common model at leading launchpads (DAO Maker, GameFi, RedKite): allocation size depends on platform tokens staked. Participants split into tiers (Bronze/Silver/Gold/Diamond), each tier has guaranteed allocation proportional to staked tokens.
Vesting and Claim
After IDO, tokens don't release immediately—market standard. Typical launchpad vesting: 20% TGE (Token Generation Event), rest linear over 6–12 months.
contract TokenVesting {
struct VestingSchedule {
uint256 totalAmount;
uint256 claimedAmount;
uint256 tgePercent; // % available after TGE, 10000 = 100%
uint256 cliffDuration; // delay before linear vesting
uint256 vestingDuration; // linear vesting length
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 and Bot Protection
Anti-Sniper Mechanics
Bots monitor mempool and try to buy in first block of sale. Standard protections:
Commit-reveal scheme — participant first sends commit = keccak256(amount, salt, address), in next phase reveals amount and salt. Bots can't know exact amount before reveal.
Randomized start — exact start time has random delay (e.g., random offset 0–300 seconds). Chainlink VRF for randomness.
Per-block limit — max X purchases per block, or max amount per block:
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 Integration
For regulated markets—KYC provider integration. Frontend KYC (Sumsub, Onfido, Synaps) issues signed JWT. Backend verifies JWT and adds address to whitelist via transaction.
More on-chain variant—Verifiable Credentials: KYC provider issues VC, user provides ZK-proof of valid VC without revealing personal data. Implementations: Polygon ID, Worldcoin (with privacy caveats).
Admin Panel and Listing
Launchpad is not just smart contracts. Complete product includes:
For projects (listing):
- Application form with contract verification
- Admin workflow for approval/rejection
- Sale parameter setup: price, hard cap, soft cap, dates, whitelist, vesting
- CSV whitelist → merkle tree generation
For participants:
- Dashboard with active and past sales
- KYC onboarding
- Whitelist registration with wallet
- Claim interface with vesting schedule
For administrators:
- Real-time sale progress monitoring
- Emergency pause
- Whitelist management (add/remove)
- Fund withdrawal after finalization
Platform Economics
Launchpad typically monetized through:
- Percentage of raise — 3–8% of raised funds
- Token allocation — X% of selling project tokens
- Platform token — staking for allocation access (ecosystem lock-in)
Development Phases
| Phase | Content | Duration |
|---|---|---|
| Design | Sale mechanics, vesting schemes, tier structure, tokenomics | 2–3 weeks |
| Core contracts | Sale, Vesting, Staking, Whitelist | 4–5 weeks |
| Testing | Unit, integration, fork tests, fuzz | 2–3 weeks |
| Backend | API, merkle tree generation, KYC integration | 3–4 weeks |
| Frontend | Participant dashboard, admin panel | 4–5 weeks |
| Audit | Contracts + backend | 3–4 weeks |
| Testnet pilot | One test IDO | 2–3 weeks |
Total: 20–27 weeks. Audit critical—launchpads hold participant money and are attack targets.







