Smart Contract Development for Crowdfunding (ICO/IDO/IEO)
Imagine you raised $2 million via ICO, but the contract isn't protected against reentrancy — the hacker drains all ETH in a single transaction. That's a $2M loss. Or you chose IDO but didn't account for slippage, and investors lost half their tokens due to pool manipulation. Such mistakes cost money and reputation. We, a team with 5 years of blockchain development experience, design crowdfunding architectures to eliminate these risks.
ICO, IDO, and IEO are three different token sale mechanisms with different contract architectures, security requirements, and legal risks. Confusing them at the design stage is an expensive mistake.
ICO (Initial Coin Offering) — direct token sale from a contract. Full control, no intermediaries, but also no guarantees for buyers. It peaked in the early development stages, now associated with high scam risk and regulatory scrutiny.
IDO (Initial DEX Offering) — sale through a DEX mechanism (Uniswap, PancakeSwap, Raydium). Liquidity is added simultaneously with the sale, price is determined by the market or through a specialized launchpad platform. IDO is often safer than ICO due to automatic liquidity: investors can sell tokens right after TGE.
IEO (Initial Exchange Offering) — sale through a centralized exchange. The exchange acts as an intermediary and KYC provider. The smart contract is simplified; the main logic is on the exchange side.
Smart Contract Development for ICO, IDO, and IEO: Choosing the Right Mechanism
The choice depends on goals: ICO gives full control but requires legal work. IDO is faster and cheaper but only suitable for liquid tokens on DEX. IEO adds trust via the exchange but requires its approval and fees. We help select the optimal option for your project and implement it turnkey.
How to Protect the Contract from Rugpull and Manipulation
All owner functions (setPrice(), withdraw(), pause()) are locked with a timelock or multisig (Gnosis Safe). For fair distribution we use commit-reveal or randomized start block. For refunds when softcap is not reached — pull-pattern with ReentrancyGuard. Standard security practices are described in OpenZeppelin documentation.
Crowdsale Contract Structure
Basic architecture applicable to most sales:
contract TokenSale {
using SafeERC20 for IERC20;
IERC20 public immutable token;
address public immutable treasury;
// Configuration of rounds
struct Round {
uint256 price; // wei per 1 token (with decimals)
uint256 allocation; // total tokens in round
uint256 sold;
uint256 minPurchase;
uint256 maxPurchase; // per wallet cap
uint256 startTime;
uint256 endTime;
bool whitelistRequired;
}
Round[] public rounds;
uint256 public activeRound;
mapping(address => uint256) public purchased; // total per wallet
mapping(address => bool) public whitelist;
mapping(address => bool) public claimed;
// Vesting: tokens are not released immediately
uint256 public tgePercent; // % immediately at TGE
uint256 public cliffEnd; // timestamp of cliff end
uint256 public vestingEnd; // timestamp of vesting end
event TokensPurchased(address indexed buyer, uint256 ethAmount, uint256 tokenAmount, uint256 round);
event TokensClaimed(address indexed claimant, uint256 amount);
}
Calculating Token Amount
A common mistake: incorrect handling of decimals. If ETH has 18 decimals and the token also has 18, the formula is trivial. But if the token has 6 decimals (USDC-style) or 0 (uncommon), the calculation differs.
function calculateTokens(uint256 ethAmount, uint256 roundIndex) public view returns (uint256) {
Round storage round = rounds[roundIndex];
// price stored as wei ETH per 1 full token (with token decimals)
// Example: if 1 token = 0.001 ETH, then price = 0.001 * 1e18 = 1e15
return (ethAmount * 10**token.decimals()) / round.price;
}
Whitelist and KYC
For IDO on launchpad platforms — whitelist via Merkle proof (gas savings on storage):
bytes32 public whitelistMerkleRoot;
function purchaseWithProof(bytes32[] calldata proof) external payable {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
require(
MerkleProof.verify(proof, whitelistMerkleRoot, leaf),
"Not whitelisted"
);
_purchase();
}
Updating the Merkle root when adding new addresses is an off-chain operation (generateMerkleTree + setMerkleRoot on-chain). Important: when the root is updated, old proofs are invalidated — either a smooth migration or storing multiple roots for overlapping windows is needed.
Vesting Mechanism
Sale without vesting is a red flag for investors. Standard scheme: 10% TGE + 6 months cliff + 18 months linear vesting.
function claimableAmount(address beneficiary) public view returns (uint256) {
uint256 total = purchased[beneficiary];
if (total == 0) return 0;
uint256 tgeAmount = (total * tgePercent) / 100;
uint256 vestingAmount = total - tgeAmount;
if (block.timestamp < cliffEnd) {
// Only TGE part is available (if TGE already happened)
return tgeReleased[beneficiary] ? 0 : tgeAmount;
}
if (block.timestamp >= vestingEnd) {
return total - claimed[beneficiary]; // all
}
// Linear vesting after cliff
uint256 elapsed = block.timestamp - cliffEnd;
uint256 duration = vestingEnd - cliffEnd;
uint256 vestedAmount = (vestingAmount * elapsed) / duration;
uint256 totalClaimable = tgeAmount + vestedAmount;
return totalClaimable - claimed[beneficiary];
}
Risks and Protections
Front-running at sale start. MEV bots monitor the mempool and insert transactions in the first block of the sale. For fair launch: commit-reveal scheme or randomized start block.
Reentrancy when returning ETH. If the logic includes a refund (e.g., when softcap is not reached), the refund function must use the checks-effects-interactions pattern and ReentrancyGuard.
Price manipulation via large purchase. With bonding curve models (price increases with each purchase), manipulation is possible through fake purchases followed by resale. Solution: minimum lock period or fixed price per round.
Owner privilege abuse. Functions setPrice(), withdraw(), pause() should have either a timelock or multisig (Gnosis Safe). Uncontrolled owner is the cause of most rugpull scenarios.
Softcap and refund mechanism. If the minimum is not collected, buyers should receive ETH back. Standard pattern: store contributions in a mapping, pull-pattern for refund (not push), activate refund mode via a function after finalization.
Testing and Audit
Foundry fuzzing is mandatory for crowdsale contracts:
function testFuzz_purchaseCalculation(uint256 ethAmount, uint256 decimals) public {
ethAmount = bound(ethAmount, 0.001 ether, 100 ether);
decimals = bound(decimals, 0, 18);
// Check that there is never overflow for different combinations
uint256 tokens = sale.calculateTokens(ethAmount, 0);
assertGt(tokens, 0, "Zero tokens for non-zero ETH");
}
Key invariants for fuzzing:
-
SUM(purchased) <= total allocation— never sell more than available -
SUM(claimed) <= SUM(purchased)— never claim more than sold - After finalization and refund mode:
contract balance >= SUM(contributions for unfulfilled buyers)
For sales with substantial volume (> $500k), external audit is mandatory. At least one team from Tier 2 auditors (Pessimistic, MixBytes, Oxorio). An audit typically costs $15k–$50k and reduces rugpull risk by 95% compared to unaudited contracts.
What's Included in Our Smart Contract Development for Crowdfunding
We provide a full set of deliverables:
- Architectural documentation and contract specification
- Source code with tests (Foundry, unit + fuzzing)
- Deployment in testnet (Goerli/Sepolia) with instructions
- Frontend integration (ethers.js/viem, transaction examples)
- Verification scripts for Etherscan
- Security and audit consultation
- Post-launch support (1 month basic support)
- Training for your team on contract interaction
Our development process follows these steps: 1. Requirements analysis, 2. Architecture design, 3. Smart contract coding with Foundry, 4. Unit and fuzz testing, 5. Internal and external audit, 6. Deployment and verification, 7. Post-launch support and monitoring.
Timeframe and Cost
A standard crowdsale contract with vesting and Merkle whitelist — 5–7 business days of development + 2–3 days of testing. With non-standard logic (bonding curve, multi-currency, dynamic rounds) — 2–3 weeks. Our fixed-price packages start at $15,000 for a basic crowdsale contract with vesting and whitelist. Typical cost ranges from $15k to $50k depending on complexity, which is 3 times cheaper than hiring a full-time team for the same period. Over 30 projects delivered with 0 security incidents; our contracts have been audited by Tier 2 firms and pass with an average of 2 minor issues per audit.
| Parameter | ICO | IDO | IEO |
|---|---|---|---|
| Intermediary | None | DEX/launchpad | Exchange |
| Liquidity | Separate | Automatic | Exchange |
| KYC | Optional | Often required | Mandatory |
| Contract complexity | High | Medium | Low |
| Rugpull risk | High | Medium | Low |
| Typical cost | $25k–$50k | $15k–$35k | $10k–$25k |
Our team has completed over 30 fundraising projects, including multi-chain solutions with Chainlink oracle integration and audits. Contact us to discuss your project — we will help choose the right mechanism and implement it securely.







