Building a Token Presale Platform: Smart Contract Plus Next.js Frontend
SolidityWeb3Next.js

Building a Token Presale Platform: Smart Contract Plus Next.js Frontend

End-to-end build notes for a production token presale platform — Solidity contract with tiered pricing, Next.js frontend with wallet UX, and the security gotchas that bit me.

HJ
Hassan Javed
February 2026
11 min read

What I'm covering

I've shipped 8 token presale platforms in the last 18 months — different chains, different tokenomics, but the architecture rhymes. This post is the canonical pattern I now reach for, plus the security gotchas that bit me on early ones.

It's an end-to-end build: Solidity contract on EVM, Next.js frontend with wallet UX, deployment, and ops.

The product

A token presale platform lets early supporters buy a project's token at a fixed price, before it lists on a DEX. Features I implement in v1:

Tiered pricing — early buyers get a lower price; price increases as tiers fill
Whitelist support — optional early access for KYC'd buyers
Vesting — bought tokens unlock over time (anti-dump)
Hardcap or softcap — refund if softcap not reached
Multi-currency — accept ETH or BNB and stablecoins (USDC, USDT)

Smart contract architecture

Three contracts:

1.Presale.sol — handles buys, tracks contributions
2.Token.sol — the ERC-20 being sold (standard OpenZeppelin)
3.Vesting.sol — linear or cliff vesting for bought tokens

Why three? Separation of concerns. Token logic is standard. Presale logic is custom. Vesting logic has its own security surface. Keeping them separate makes audits easier and lets you upgrade vesting (via a new contract address) without redeploying the token.

Presale.sol key functions

The minimum interface:

buyWithETH payable — buy with native gas token
buyWithStablecoin with IERC20 token and amount — buy with USDC or USDT
claim — claim purchased tokens (transfers to vesting contract)
refund — refund if softcap not met
addToWhitelist with addresses array — admin
setTierPricing with tiers and prices arrays — admin

Tiered pricing logic

Stored as two arrays: thresholds (how many tokens sold) and prices (token price at that tier). Buy logic:

1.Compute current tier from total tokens sold
2.Calculate tokens user can buy at current tier's price
3.If buy exceeds current tier, split across tiers (some at this price, some at next)
4.Charge buyer for the total, credit them with the token amount

This split-across-tiers logic is where I see most amateur presales fail. They buy at one fixed price and ignore tier boundaries.

Whitelist

Two patterns:

On-chain whitelist — store addresses in a mapping. Costs ~22K gas per add. Fine for under 1000 addresses.
Merkle proof whitelist — store only the Merkle root. Buyers submit proof when buying. Free to add unlimited whitelist; ~5K gas per proof verification.

For most projects, Merkle is the right call. The frontend builds the proof; the contract verifies it.

Vesting

Linear vesting with cliff is the standard:

Cliff — no tokens unlock for N days
Duration — total unlock period (e.g., 12 months)
Per-second drip — after cliff, tokens unlock linearly

The Vesting.sol contract holds the bought tokens, releases them to claimants on release calls.

Security gotchas (the part you should not skip)

What bit me on early builds:

1. Reentrancy on refund

A naive refund function that does transfer before updating state can be re-entered by a malicious contract and drained. Solution: update state first, then transfer (checks-effects-interactions), or use OpenZeppelin's ReentrancyGuard. I do both.

2. Front-running the price tier transition

If tier 1 has 10 tokens left at 10 cents and tier 2 starts at 15 cents, an attacker can front-run a large buy by buying first at 10 cents and immediately reselling at 15 cents.

Mitigations: maximum buy per transaction (e.g., 1 percent of supply per tx), per-address cap, or for high-value presales, off-chain order matching with on-chain settlement.

3. Stablecoin decimal mismatch

USDC is 6 decimals on most chains. USDT is 6 on most chains. DAI is 18. Your token is probably 18. Hardcoded decimals in your price calculation will break. Always read decimals from the stablecoin contract at runtime.

4. Rug pull detection

Investors are wary. Reduce rug pull risk by hardcoding the vesting and unlock schedule into the contract (no admin update), locking team allocation in the same vesting contract, and using a timelock for any admin functions. These cost nothing but build trust.

5. Refund precision loss

If users contributed in stablecoin units and your math divides somewhere, refunds can leave dust. Round in the user's favor (always slightly over-refund) and recover dust into the contract reserve.

The frontend (Next.js 14 App Router)

Stack:

Wagmi v2 for wallet connection and contract reads or writes
RainbowKit for the wallet UX (MetaMask, WalletConnect, Coinbase Wallet, etc.)
Viem as the lower-level interface (Wagmi uses Viem)
TanStack Query for caching contract reads
Tailwind plus Framer Motion for design

Key screens:

1.Hero — countdown to launch, total raised, percent of cap reached
2.Buy widget — select currency, enter amount, see tokens you'll receive, confirm
3.My contribution — total bought, tokens claimable, next vesting unlock
4.Whitelist check — enter address, see if eligible

The Buy widget is where 80 percent of the UX engineering goes. It needs to detect connected wallet's chain (warn if wrong), pre-approve stablecoin spend if buying with USDC or USDT, show real-time tier transitions, handle slippage or partial fills gracefully, and show clear error messages for known failures.

Deployment

I use Hardhat with hardhat-deploy for deterministic deployments. Deploy order: Token contract (record address), Vesting contract (constructor needs token address), Presale contract (constructor needs token plus vesting addresses), transfer presale-allocated tokens from deployer to presale contract, verify all three on the explorer.

The verify step is critical. Unverified contracts kill investor confidence.

Pre-launch checklist (this is what my clients pay me for)

Before going live on mainnet:

All contracts source-verified on the explorer
Admin keys moved to a multisig (Safe or Gnosis)
Emergency pause function tested (and the multisig knows how to use it)
Vesting schedule documented and matches whitepaper
Stablecoin contract addresses confirmed for the deployment chain
Front-end shows clear chain warnings if user is on wrong network
Buy flow tested with real (small) amounts on mainnet
Whitelist Merkle tree verified — random addresses pass or fail correctly
Refund flow tested
Vesting claim tested after a simulated cliff
Analytics plus alerting hooked up (Tenderly is great here)

Cost reality

For a typical client engagement (audit-ready contracts plus Next.js frontend plus deployment):

Smart contracts: 3 weeks
Frontend: 2-3 weeks
Integration plus testing: 1 week
Deployment plus verify plus handoff: 3 days

Total: ~6-7 weeks for v1. Add an external audit (Code4rena, Hashlock, etc.) on top — another 2-4 weeks before mainnet.

TL;DR

Three contracts: Token, Presale, Vesting — separation aids security and upgrades
Tiered pricing needs split-across-tiers logic
Merkle whitelist beats on-chain for over 100 addresses
Reentrancy, front-running, decimal mismatch are the top three security mistakes
Frontend complexity centers on the Buy widget — chain detection, approvals, tier transitions, errors

If you're launching a token and want production-grade contracts plus frontend, contact me.

Related Reads

You might also like