Memecoin Launch Platform Development
Pump.fun processed over $1B in transactions in first year. Key insight of the team: token launch barrier was too high (contract deploy, LP creation, listing), and bonding curve pump mechanism—predictable and understandable to users. Result—machine for token launches, generating millions in protocol revenue monthly.
Platform like this technically non-trivial: bonding curve with specific math, automatic DEX transition upon liquidity threshold, anti-rug mechanics, and all working instantly under high competition.
Bonding Curve: Math and Implementation
Constant Product Curve (Simplified Pump.fun)
Pump.fun uses virtual AMM with constant product. On launch token has no real liquidity—virtual reserves define starting price.
contract BondingCurve {
uint256 public constant VIRTUAL_SOL_RESERVE = 30_000_000_000; // 30 SOL virtual
uint256 public constant VIRTUAL_TOKEN_RESERVE = 1_073_000_000 * 10**6; // 1.073B tokens
uint256 public constant TOTAL_SUPPLY = 1_000_000_000 * 10**6;
uint256 public constant GRADUATION_THRESHOLD = 85_000_000_000; // 85 SOL raised
uint256 public realSolReserve; // actually deposited SOL
uint256 public realTokenReserve; // tokens in curve
// k = (virtual_sol + real_sol) * (virtual_token + real_token) = const
function buy(uint256 solIn) external payable returns (uint256 tokensOut) {
require(msg.value == solIn, "Value mismatch");
require(!graduated, "Already on DEX");
uint256 virtualSol = VIRTUAL_SOL_RESERVE + realSolReserve;
uint256 virtualToken = VIRTUAL_TOKEN_RESERVE - (TOTAL_SUPPLY - realTokenReserve);
// Constant product: k = virtualSol * virtualToken
// After buy: (virtualSol + solIn) * (virtualToken - tokensOut) = k
// tokensOut = virtualToken - k / (virtualSol + solIn)
uint256 k = virtualSol * virtualToken;
tokensOut = virtualToken - (k / (virtualSol + solIn));
require(tokensOut <= realTokenReserve, "Not enough tokens");
realSolReserve += solIn;
realTokenReserve -= tokensOut;
token.transfer(msg.sender, tokensOut);
// Check graduation
if (realSolReserve >= GRADUATION_THRESHOLD) {
_graduate();
}
emit TokensPurchased(msg.sender, solIn, tokensOut, currentPrice());
}
function currentPrice() public view returns (uint256) {
uint256 virtualSol = VIRTUAL_SOL_RESERVE + realSolReserve;
uint256 virtualToken = VIRTUAL_TOKEN_RESERVE - (TOTAL_SUPPLY - realTokenReserve);
// price in SOL per token (lamports)
return (virtualSol * 10**6) / virtualToken;
}
}
Virtual reserves—key element. Without them starting price at zero liquidity would be 0. Virtual reserves create artificial "depth" to curve, forming starting price and controlling price impact of first buys.
Graduation: Transition to Raydium/Uniswap
When platform collects enough SOL/ETH—token "graduates": contract automatically creates LP pool on DEX and adds liquidity.
function _graduate() internal {
graduated = true;
uint256 solForLiquidity = realSolReserve; // all collected SOL
uint256 tokensForLiquidity = realTokenReserve; // remaining tokens
// Create LP on Uniswap V2 (or Raydium on Solana)
IUniswapV2Router router = IUniswapV2Router(ROUTER_ADDRESS);
token.approve(address(router), tokensForLiquidity);
(uint amountToken, uint amountETH, uint liquidity) = router.addLiquidityETH{
value: solForLiquidity
}(
address(token),
tokensForLiquidity,
tokensForLiquidity * 99 / 100, // 1% slippage
solForLiquidity * 99 / 100,
address(0), // LP tokens to address(0) — burn liquidity
block.timestamp + 300
);
// Burn LP tokens—liquidity permanent, can't rug pull
emit Graduated(address(token), amountToken, amountETH, liquidity);
}
Burning LP tokens on graduation—critical anti-rug mechanism. Creator can't withdraw liquidity and escape. This—key trust advantage of Pump.fun over self-deploying.
Solana: Why Most Memecoin Platforms Live There
Pump.fun on Solana not by accident:
- Transaction: ~$0.0001 vs $0.5-5 on Ethereum mainnet
- Finality: ~400ms vs 12+ seconds
- On high-frequency launch events (hundreds of tokens hourly)—decisive UX advantage
Solana Anchor framework for contracts:
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Mint, Token, TokenAccount};
#[program]
pub mod pump_clone {
use super::*;
pub fn create_token(
ctx: Context<CreateToken>,
name: String,
symbol: String,
uri: String,
total_supply: u64,
) -> Result<()> {
// Mint all tokens into bonding curve vault
token::mint_to(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token::MintTo {
mint: ctx.accounts.mint.to_account_info(),
to: ctx.accounts.bonding_curve_vault.to_account_info(),
authority: ctx.accounts.mint_authority.to_account_info(),
},
&[&[b"mint_authority", &[ctx.bumps.mint_authority]]],
),
total_supply,
)?;
// Initialize bonding curve account
let curve = &mut ctx.accounts.bonding_curve;
curve.total_supply = total_supply;
curve.virtual_sol_reserves = 30_000_000_000; // 30 SOL
curve.virtual_token_reserves = total_supply;
curve.real_sol_reserves = 0;
curve.real_token_reserves = total_supply;
curve.graduated = false;
curve.creator = ctx.accounts.creator.key();
emit!(TokenCreated {
mint: ctx.accounts.mint.key(),
creator: ctx.accounts.creator.key(),
name,
symbol,
uri,
});
Ok(())
}
pub fn buy(ctx: Context<Buy>, sol_amount: u64, min_tokens: u64) -> Result<()> {
let curve = &mut ctx.accounts.bonding_curve;
require!(!curve.graduated, ErrorCode::AlreadyGraduated);
let tokens_out = curve.calculate_buy(sol_amount)?;
require!(tokens_out >= min_tokens, ErrorCode::SlippageExceeded);
// Transfer SOL from buyer to vault
anchor_lang::system_program::transfer(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
anchor_lang::system_program::Transfer {
from: ctx.accounts.buyer.to_account_info(),
to: ctx.accounts.sol_vault.to_account_info(),
},
),
sol_amount,
)?;
// Transfer tokens from vault to buyer
token::transfer(
CpiContext::new_with_signer(/* ... */),
tokens_out,
)?;
curve.real_sol_reserves += sol_amount;
curve.real_token_reserves -= tokens_out;
// Check graduation
if curve.real_sol_reserves >= GRADUATION_SOL_THRESHOLD {
ctx.accounts.bonding_curve.graduated = true;
// Call Raydium CPI to create LP...
}
Ok(())
}
}
Anti-rug and Fair Launch Mechanics
Creator Lock
// Creator can't sell in first X blocks/minutes
mapping(address => uint256) public creatorLockExpiry;
function createToken(...) external {
// ...
creatorLockExpiry[newToken] = block.timestamp + 24 hours;
}
// Override transfer for creator
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
if (from == tokenCreator[address(this)]) {
require(block.timestamp >= creatorLockExpiry[address(this)], "Creator locked");
}
}
Max Buy Per Wallet
Limits reduce premine scenarios where creator bulk-buys before public launch:
uint256 public constant MAX_BUY_PERCENT = 200; // 2% total supply
uint256 public constant MAX_BUY_AMOUNT = TOTAL_SUPPLY * MAX_BUY_PERCENT / 10000;
mapping(address => uint256) public totalBought;
function buy(uint256 solIn) external {
// ...
require(totalBought[msg.sender] + tokensOut <= MAX_BUY_AMOUNT, "Max buy exceeded");
totalBought[msg.sender] += tokensOut;
}
Platform Revenue Model
Platform earns on every bonding curve transaction. Pump.fun takes 1% on each buy/sell. At $1M daily volume—$10k daily revenue just from trading fee. Plus graduation fee on DEX listing.
uint256 public constant PLATFORM_FEE_BPS = 100; // 1%
address public immutable feeRecipient;
function buy(uint256 solIn) external payable {
uint256 platformFee = (solIn * PLATFORM_FEE_BPS) / 10000;
uint256 effectiveSolIn = solIn - platformFee;
// platformFee sent to feeRecipient
payable(feeRecipient).transfer(platformFee);
// Remainder goes to bonding curve
_processBuy(effectiveSolIn);
}
Frontend: Real-time UX
Users expect instant feedback: chart updates after each swap, activity feed shows last 10 transactions literally.
WebSocket for real-time. Subscribe to on-chain events via Helius (Solana) or Alchemy (EVM). On each TokensPurchased event—update chart, add to activity feed.
// Real-time price updates via WebSocket
const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=...')
ws.send(JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'logsSubscribe',
params: [
{ mentions: [PROGRAM_ID] },
{ commitment: 'confirmed' }
]
}))
ws.onmessage = (event) => {
const { result } = JSON.parse(event.data)
if (result?.value?.logs) {
const parsed = parsePurchaseEvent(result.value.logs)
if (parsed) updateChart(parsed)
}
}
OHLCV chart. For each token build OHLCV from transaction history. Aggregate by 1-minute candles. TradingView Lightweight Charts—standard for crypto projects.
Moderation and Compliance
Token metadata verification. IPFS storage of metadata (name, symbol, image). Platform shouldn't host illegal content—need reporting system and ability to hide token from UI (not on-chain, just frontend).
Spam prevention. Creation fee (small SOL amount) reduces spam launches. Pump.fun charges ~0.02 SOL per creation.
Development Process
Smart contracts (4-6 weeks). Bonding curve contract → graduation mechanism → fee collection → Anchor/Foundry tests with edge cases (buying last token, graduation edge cases, reentrancy).
Indexer (2-3 weeks). Off-chain service parsing on-chain events and building database of tokens, prices, transactions. PostgreSQL + Redis cache for quick API—on-chain query too slow.
API and frontend (3-4 weeks). REST API for token lists, WebSocket for real-time updates, React frontend with TradingView charts.
Launch and monitoring. Alerts on anomalous activity (one address bought 10%+ supply), monitoring gas/compute on Solana, circuit breaker if graduation mechanism stresses Raydium API.
Full platform—3-4 months. MVP without graduation and simplified UI—6-8 weeks.







