Crowdfunding Contract Development (ICO/IDO/IEO)

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Crowdfunding Contract Development (ICO/IDO/IEO)
Medium
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1215
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1043
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Development of Crowdfunding Contract (ICO/IDO/IEO)

ICO, IDO and IEO — three different token sale mechanisms with different smart contract architecture, different security requirements and different legal risks. Confusing them at design stage is costly mistake.

ICO (Initial Coin Offering) — direct token sale from contract. Full control, no intermediaries, but no buyer guarantees. Heyday 2017–2018, now associated with high fraud risk and regulatory attention.

IDO (Initial DEX Offering) — sale via DEX mechanism (Uniswap, PancakeSwap, Raydium). Liquidity added simultaneously with sale, price determined by market or specialized launchpad platform.

IEO (Initial Exchange Offering) — sale via centralized exchange. Exchange acts as intermediary and KYC provider. Smart contract in this case simplified, main logic on exchange side.

Structure of Crowdsale Contract

Basic architecture applicable to most sales:

contract TokenSale {
    using SafeERC20 for IERC20;

    IERC20 public immutable token;
    address public immutable treasury;

    // Round configuration
    struct Round {
        uint256 price;          // wei per 1 token (accounting for 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 issued gradually
    uint256 public tgePercent;      // % immediately at TGE
    uint256 public cliffEnd;        // timestamp cliff end
    uint256 public vestingEnd;      // timestamp vesting end

    event TokensPurchased(address indexed buyer, uint256 ethAmount, uint256 tokenAmount, uint256 round);
    event TokensClaimed(address indexed claimant, uint256 amount);
}

Token Amount Calculation

Common mistake: improper decimals handling. If ETH has 18 decimals and token also 18, formula trivial. But if token has 6 decimals (USDC-style) or 0 (rare) — calculation different.

function calculateTokens(uint256 ethAmount, uint256 roundIndex) public view returns (uint256) {
    Round storage round = rounds[roundIndex];
    // price stored as wei ETH per 1 full token (accounting for 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 launchpad IDOs — whitelist via merkle proof (gas savings for 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();
}

Merkle root update when adding new addresses — off-chain operation (generateMerkleTree + setMerkleRoot on-chain). Important: on root update old proofs invalidated — need either smooth migration or multiple root storage for overlapping windows.

Vesting Mechanism

Sale without vesting — red flag for investors. Standard scheme: 10% TGE (token generation event) + 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 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 track mempool and insert transactions in first block of sale. For fair launch: commit-reveal scheme or randomized start block.

Reentrancy on ETH refund. If logic includes refund (e.g., on softcap miss), refund function must use checks-effects-interactions pattern and ReentrancyGuard.

Price manipulation via large purchase. In bonding curve models (price rises with each purchase) — possible manipulation via dummy purchases with subsequent resale. Solution: minimum lock period or fixed price per round.

Owner privilege abuse. Functions setPrice(), withdraw(), pause() must have either timelock or multisig (Gnosis Safe). Uncontrolled owner — reason for most rugpull scenarios.

Softcap and refund mechanism. If minimum not raised — buyers must get ETH back. Standard pattern: store contributions in mapping, pull-pattern for refund (not push), activate refund mode via function after finalization.

Testing and Audit

Foundry fuzzing 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 no overflow at 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 issue more than sold
  • After finalization and refund mode: contract balance >= SUM(contributions for unfulfilled buyers)

For sales with significant volume (from $500K) — external audit mandatory. Minimum one Tier 2 audit team (Pessimistic, MixBytes, Oxorio).

Timeline

Standard crowdsale contract with vesting and merkle whitelist — 5–7 working days development + 2–3 days testing. With non-standard logic (bonding curve, multi-currency, dynamic rounds) — 2–3 weeks.