Skip to main content

Bonding Curves: The Mathematics of Fair Launches

Bonding curves are smart contract mechanisms that automatically determine token prices based on supply and demand. They enable fair launches where everyone trades against the same mathematical formula, eliminating the need for market makers and preventing price manipulation.
Why it matters: Understanding bonding curve math helps you predict price movements, identify bonding thresholds, and build trading strategies around launchpad tokens.

What is a Bonding Curve?

A bonding curve is a mathematical function that defines the relationship between a token’s price and its supply. As tokens are bought, the price increases along the curve. As tokens are sold, the price decreases.
Price = f(Supply)
The most common types are:
Curve TypeFormulaCharacteristics
Constant Product (x*y=k)price = k / supplyHyperbolic curve, used by Pumpfun
Exponentialprice = base^supplyAggressive price growth
Linearprice = m * supply + bPredictable, steady growth
Sqrt Priceprice = sqrtPrice² / 2^128Concentrated liquidity, used by Meteora DBC

Pumpfun: Constant Product AMM

Pumpfun uses a constant product Automated Market Maker (AMM) with virtual reserves. This is the same formula popularized by Uniswap V2.

Core Formula

x * y = k (constant)
Where:
  • x = Virtual SOL reserves (reserve0)
  • y = Virtual Token reserves (reserve1)
  • k = Constant product (invariant)

Understanding Virtual vs Real Reserves

Critical Concept: Pumpfun uses TWO types of reserves:
  • Virtual Reserves: Used for price calculation (includes “phantom” liquidity)
  • Real Reserves: Actual tokens/SOL locked in the contract
Reserve TypeSOLTokenPurpose
VirtualvirtualSolReservesvirtualTokenReservesPrice calculation via AMM formula
RealrealSolReservesrealTokenReservesActual assets in contract
Initial Virtual30 SOL1,073,000,000,000,000Starting point for price curve
Initial Real Token0 SOL793,100,000,000,000Tokens available for sale
Why virtual reserves? The virtual reserves create artificial depth at launch, preventing the first buyer from getting 100% of tokens for pennies. The 30 SOL virtual reserve means the price starts as if there’s already 30 SOL of liquidity.

Price Calculation

The price (exchange rate) is calculated as:
// Returns: how many tokens you get per 1 SOL
exchangeRate = virtualTokenReserves / virtualSolReserves
In code:
getPrice(pool: PumpfunPool): number {
  if (pool.reserve0 === 0n || pool.reserve1 === 0n) {
    return 0;
  }
  // reserve0 = virtualSolReserves, reserve1 = virtualTokenReserves
  // Returns tokens per SOL (exchange rate)
  return bigIntDiv(pool.reserve1, pool.reserve0);
}
To get the token price in SOL:
tokenPriceInSOL = virtualSolReserves / virtualTokenReserves
// Or simply: 1 / exchangeRate

Initial Parameters

When a Pumpfun token is created, the bonding curve initializes with:
ParameterValueDescription
initialVirtualSolReserves30 SOL (30,000,000,000 lamports)Virtual SOL for price calculation
initialVirtualTokenReserves1,073,000,000,000,000Virtual tokens (6 decimals = ~1.073B)
initialRealTokenReserves793,100,000,000,000Actual tokens for sale (~793.1M)
bondedCriteria85.005 SOLReal SOL to trigger migration
Note: The total supply is 1B tokens, but only ~793.1M are available for purchase. The difference (~280M) is the virtual reserve that creates initial price depth.

How Buying Works

When you buy tokens:
  1. You send SOL to the bonding curve
  2. The curve calculates tokens using the constant product formula:
// Classic AMM swap formula
tokens_out = (sol_in * virtualTokenReserves) / (virtualSolReserves + sol_in)
  1. Both virtual AND real reserves update:
    • virtualSolReserves += sol_in
    • virtualTokenReserves -= tokens_out
    • realSolReserves += sol_in
    • realTokenReserves -= tokens_out

How Selling Works

When you sell tokens:
sol_out = (tokens_in * virtualSolReserves) / (virtualTokenReserves + tokens_in)

Bonding Percentage

The bonding progress shows how close the token is to migration:
// How many tokens have been sold vs total available
bondingPercentage = (initialVirtualTokenReserves - currentVirtualTokenReserves) * 100 
                    / initialRealTokenReserves
When tokens are bought, virtualTokenReserves decreases. The bonding percentage tracks what % of the initialRealTokenReserves has been purchased.

Bonding (Migration) Threshold

When virtualSolReserves >= ~85 SOL, the token “bonds” and migrates to Raydium:
bondedCriteria = 86000000000n; // ~86 SOL in lamports

async isBonded(pool: PumpfunPool): Promise<boolean> {
  if (pool.bonded) return true;
  if (pool.reserve0 >= this.bondedCriteria) return true;
  return false;
}
Since virtual SOL starts at 30 SOL, approximately 55-56 SOL of real purchases triggers migration.

Price Impact Formula

For a given SOL input, the price impact is:
priceImpact = sol_in / (virtualSolReserves + sol_in)
Example: If virtual SOL reserves = 30 SOL and you buy with 3 SOL, your price impact is ~9.1%. Early buys have massive impact because of the relatively low virtual reserve.

Numerical Example

At launch:
  • Virtual SOL: 30,000,000,000 lamports (30 SOL)
  • Virtual Tokens: 1,073,000,000,000,000 (1.073B with 6 decimals)
  • Exchange rate: 1,073B / 30 = 35,766,666,666 tokens per SOL
  • Token price: 30 / 1,073B = 0.000000028 SOL per token
After 10 SOL buy:
  • Tokens received: (10 × 1,073B) / (30 + 10) = 268,250,000,000 tokens (~268M)
  • New Virtual SOL: 40 SOL
  • New Virtual Tokens: 804,750,000,000,000 (804.75B)
  • New exchange rate: 804.75B / 40 = 20,118,750,000 tokens per SOL
  • Price increased by ~44%

Meteora DBC: Dynamic Bonding Curve

Meteora’s Dynamic Bonding Curve (DBC) uses a sqrt price model with concentrated liquidity, similar to Uniswap V3 but optimized for launchpads.

Core Concept: Sqrt Price

Instead of storing price directly, Meteora DBC stores the square root of price as a Q64.64 fixed-point number:
sqrtPrice = sqrt(price) * 2^64
To get the actual price:
getPrice(pool: MeteoraDBCPool): number {
  const sqrtPrice = pool.extra?.sqrtPriceX64 ?? 0n;
  if (sqrtPrice === 0n) return 0;
  
  // Convert from Q64.64 fixed point
  return getPriceFromSqrtX(sqrtPrice, 64);
}

// Where getPriceFromSqrtX does:
function getPriceFromSqrtX(sqrtPrice: bigint, resolution: number): number {
  const price = (sqrtPrice * sqrtPrice) / (1n << BigInt(resolution * 2));
  return Number(price) / (2 ** resolution);
}

The Curve Structure

Meteora DBC defines a curve as an array of price/liquidity points:
type Curve = Array<{
  sqrtPrice: bigint;   // Price at this point
  liquidity: bigint;   // Liquidity available
}>;
This creates a piecewise linear bonding curve where:
  • Each segment has different liquidity concentration
  • Price moves faster in segments with lower liquidity
  • Price moves slower in segments with higher liquidity

Quote Reserve Calculation

The virtual quote (SOL) reserve is calculated by integrating along the curve:
calculateQuoteVirtualReserve(pool: MeteoraDBCPool): bigint {
  const curve = pool.extra.curve;
  const nextSqrtPrice = pool.extra.sqrtPriceX64;
  const sqrtStartPrice = pool.extra.sqrtPriceStart;
  
  let totalAmount = 0n;
  
  for (let i = 0; i < curve.length; i++) {
    const currentPoint = curve[i];
    const lowerSqrtPrice = i === 0 
      ? sqrtStartPrice 
      : curve[i - 1].sqrtPrice;
    
    if (nextSqrtPrice > lowerSqrtPrice) {
      const upperSqrtPrice = nextSqrtPrice < currentPoint.sqrtPrice 
        ? nextSqrtPrice 
        : currentPoint.sqrtPrice;
      
      // Calculate delta amount for this segment
      const maxAmountIn = getDeltaAmountQuoteUnsigned(
        lowerSqrtPrice,
        upperSqrtPrice,
        currentPoint.liquidity
      );
      
      totalAmount += maxAmountIn;
    }
  }
  
  return totalAmount;
}

Delta Amount Formula

For each curve segment:
getDeltaAmountQuoteUnsigned(
  lowerSqrtPrice: bigint,
  upperSqrtPrice: bigint,
  liquidity: bigint
): bigint {
  // Δquote = L * (√P_upper - √P_lower) / 2^128
  const deltaSqrtPrice = upperSqrtPrice - lowerSqrtPrice;
  const prod = liquidity * deltaSqrtPrice;
  return prod >> BigInt(RESOLUTION * 2); // RESOLUTION = 64
}

Bonding Percentage

Meteora DBC calculates bonding progress as:
bondingPercentage = (quoteReserve / migrationQuoteThreshold) * 100
When bondingPercentage >= 100%, the token migrates to Meteora DAMM.

Migration Thresholds

ParameterDescription
migrationQuoteThresholdSOL amount needed to trigger migration
sqrtPriceStartInitial sqrt price
curveArray of (sqrtPrice, liquidity) points defining the curve shape

Comparison: Pumpfun vs Meteora DBC

AspectPumpfunMeteora DBC
Curve TypeConstant Product (x*y=k)Sqrt Price with Concentrated Liquidity
Price FormulaSOL / TokensqrtPrice² / 2^128
Liquidity DistributionUniform across all pricesConcentrated in curve segments
Price ImpactHigher for same volumeVariable based on liquidity concentration
Bonding Trigger86 SOL raisedCustom migrationQuoteThreshold
Migration TargetRaydiumMeteora DAMM
ComplexitySimpleMore complex, more flexible

Practical Applications

1. Predicting Bonding Time

For Pumpfun:
// Bonding happens when virtualSolReserves reaches ~86 SOL
// Since it starts at 30 SOL, you need ~56 SOL of real purchases
const INITIAL_VIRTUAL_SOL = 30_000_000_000n; // 30 SOL
const BONDED_CRITERIA = 86_000_000_000n;     // 86 SOL

// Current progress (using virtual reserves)
const currentVirtualSol = pool.reserve0;
const progress = Number(currentVirtualSol - INITIAL_VIRTUAL_SOL) * 100 
                 / Number(BONDED_CRITERIA - INITIAL_VIRTUAL_SOL);

// Real SOL needed to bond
const solNeeded = BONDED_CRITERIA - currentVirtualSol;
For Meteora DBC:
// Current progress
const quoteReserve = calculateQuoteVirtualReserve(pool);
const progress = (quoteReserve * 100n) / migrationQuoteThreshold;

// SOL needed to bond
const solNeeded = migrationQuoteThreshold - quoteReserve;

2. Price Impact Estimation

For a buy of X SOL on Pumpfun:
// Current exchange rate (tokens per SOL)
const currentRate = virtualTokenReserves / virtualSolReserves;

// After your buy
const newSolReserves = virtualSolReserves + X;
const tokensReceived = (X * virtualTokenReserves) / newSolReserves;

// Your effective rate (tokens per SOL)
const effectiveRate = tokensReceived / X;

// Price impact (how much worse your rate is vs spot)
const priceImpact = (currentRate - effectiveRate) / currentRate;
// Or equivalently: X / (virtualSolReserves + X)

3. Slippage Calculation

// Expected tokens at current spot rate (no slippage)
const expectedTokens = (solIn * virtualTokenReserves) / virtualSolReserves;

// Actual tokens (accounting for price impact)
const actualTokens = (solIn * virtualTokenReserves) / (virtualSolReserves + solIn);

// Slippage percentage
const slippage = (expectedTokens - actualTokens) / expectedTokens * 100;
// This equals: solIn / (virtualSolReserves + solIn) * 100

4. Calculating Token Price in USD

// Get SOL price from oracle
const solPriceUSD = 150; // example: $150 per SOL

// Token price in SOL
const tokenPriceInSOL = virtualSolReserves / virtualTokenReserves;

// Token price in USD
const tokenPriceUSD = tokenPriceInSOL * solPriceUSD;

API Access

Query bonding curve data through Mobula’s API:
# Get token details for a Pumpfun token (Fartcoin)
curl -X GET "https://demo-api.mobula.io/api/2/token/details?blockchain=solana&address=9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump" \
  -H "Authorization: YOUR_API_KEY"

# Get token details for Wojak
curl -X GET "https://demo-api.mobula.io/api/2/token/details?blockchain=solana&address=8J69rbLTzWWgUJziFY8jeu5tDwEPBwUz4pKBMr5rpump" \
  -H "Authorization: YOUR_API_KEY"
The response includes:
  • bonded - Whether the token has migrated to Raydium/Meteora
  • bondingPercentage - Progress toward migration (0-100)
  • bondingCurveAddress - Address of the bonding curve contract
  • preBondingFactory - Original launchpad (e.g., Pumpfun, Meteora DBC)
  • liquidityUSD - Current liquidity in the curve
For real-time bonding curve updates, use the WSS Market Details WebSocket stream.