May 1, 2026
Pump.fun Bonding Curve Explained
Every token on Pump.fun starts on a bonding curve. The curve determines the price, handles buys and sells, and eventually graduates the token to a real AMM. If you want to analyze Pump.fun data or build trading tools, you need to understand how this curve actually works. This post breaks it down using the raw on-chain fields from our data files.
What is a bonding curve
A bonding curve is a smart contract that acts as an automated market maker for a brand new token. There is no order book and no liquidity providers. The contract itself holds a pool of SOL and a pool of tokens, and it uses a mathematical formula to set the price based on how much of each is in the pool.
When someone buys, SOL goes into the pool and tokens come out. The price goes up. When someone sells, tokens go back into the pool and SOL comes out. The price goes down. The more tokens that have been bought, the higher the price gets. This is what makes early buyers profitable if the token gains traction.
Virtual reserves vs real reserves
Pump.fun uses a constant product formula, similar to Uniswap. But there is a twist. The contract maintains two sets of reserves for each token.
Real reserves are the actual SOL and tokens sitting in the pool. These are the fields real_lamports_reserve and real_token_reserve in our data. When a token first launches, the real SOL reserve is zero because nobody has bought yet.
Virtual reserves are the numbers the pricing formula actually uses. These are virtual_lamports_reserve and virtual_token_reserve. The virtual reserves start with a non-zero SOL amount even before anyone buys. This is what gives the token a starting price instead of pricing it at zero.
Think of the virtual reserves as the real reserves plus an offset. The offset creates the initial price point. As people trade, the virtual and real reserves both move, but the gap between them stays roughly the same.
How the price is calculated
The spot price of a token in SOL is the ratio of the virtual reserves. Every swap event in our data includes the virtual reserves after the trade, so you can compute the price at any point in the token's history.
LAMPORTS_PER_SOL = 1_000_000_000
def price_in_sol(virtual_lamports_reserve, virtual_token_reserve): """Spot price per token in SOL after a swap.""" if virtual_token_reserve == 0: return 0 return (virtual_lamports_reserve / virtual_token_reserve) / LAMPORTS_PER_SOLBoth reserve fields are stored in their smallest units. SOL is in lamports (1 SOL = 1,000,000,000 lamports) and tokens are in their raw integer amount. The division gives you the price per token in SOL.
You can also compute the market cap at any swap by multiplying the price by the total token supply (usually 1 billion on Pump.fun).
TOKEN_SUPPLY = 1_000_000_000 # most pump.fun tokens
def market_cap_sol(virtual_lamports_reserve, virtual_token_reserve): """Approximate market cap in SOL.""" price = price_in_sol(virtual_lamports_reserve, virtual_token_reserve) return price * TOKEN_SUPPLYEvent types in the data
Each row in a PumpFunData parquet file has an event_type field. For the pump_fun exchange, you will see three types.
- create.A new token was launched. This event has the token's
name,symbol, anduri(metadata link). Thetoken_creatorfield tells you which wallet deployed it. - swap. Someone bought or sold the token. The
actionfield is either "buy" or "sell". You get the exacttoken_amount,lamports_amount(SOL traded),fee_lamports, and both reserve snapshots after the trade. - bonding_complete. The token hit the graduation threshold and is moving off the bonding curve. After this event, trading continues on the
pump_ammexchange.
Here is how to filter for each type and see what a typical day of activity looks like.
import pandas as pd
df = pd.read_parquet("pump_fun_2026-04-15_12.parquet")
creates = df[df["event_type"] == "create"]swaps = df[df["event_type"] == "swap"]graduations = df[df["event_type"] == "bonding_complete"]
print(f"New tokens: {len(creates)}")print(f"Swaps: {len(swaps)}")print(f"Graduations: {len(graduations)}")print(f"Unique tokens traded: {swaps['token_mint'].nunique()}")What happens at graduation
When enough SOL has been deposited into the bonding curve, the token graduates. The bonding_completeevent fires, and the contract creates a liquidity pool on Pump.fun's own AMM. From that point forward, the token trades on the AMM with traditional constant product (x×y=k) mechanics and liquidity providers.
In our data, post-graduation trading shows up in the pump_amm exchange. The AMM data uses real_lamports_reserve and real_token_reserve instead of virtual reserves, because the AMM does not need the virtual offset anymore.
If you want to track a token across its entire lifecycle, you need to query both exchanges. Use token_mint to join the data. The bonding curve phase lives in pump_fun and the AMM phase lives in pump_amm.
Track a token from launch to graduation
Here is a complete example that finds a token that graduated and prints its price history on the bonding curve.
import pandas as pdimport glob
# Load a full dayfiles = sorted(glob.glob("data/2026-04-15/*.parquet"))df = pd.concat([pd.read_parquet(f) for f in files], ignore_index=True)
# Find tokens that graduatedgraduated = df[df["event_type"] == "bonding_complete"]["token_mint"].unique()print(f"Tokens that graduated today: {len(graduated)}")
if len(graduated) > 0: # Pick the first graduated token mint = graduated[0] token_events = df[df["token_mint"] == mint].sort_values("timestamp")
# Get swaps and compute price token_swaps = token_events[token_events["event_type"] == "swap"].copy() token_swaps["price_sol"] = ( token_swaps["virtual_lamports_reserve"] / token_swaps["virtual_token_reserve"] / 1_000_000_000 ) token_swaps["time"] = pd.to_datetime(token_swaps["timestamp"], unit="s")
# Find the creation event create_event = token_events[token_events["event_type"] == "create"].iloc[0] print(f"Token: {create_event['name']} ({create_event['symbol']})") print(f"Creator: {create_event['token_creator']}") print(f"Total swaps: {len(token_swaps)}") print(f"Price range: {token_swaps['price_sol'].min():.10f} - {token_swaps['price_sol'].max():.10f} SOL") print(f"Unique traders: {token_swaps['user_wallet'].nunique()}")Key fields for bonding curve analysis
Here is a quick reference for the fields that matter most when working with bonding curve data. The full schema is in our documentation.
| Field | What it tells you |
|---|---|
| virtual_lamports_reserve | SOL side of the pricing formula (in lamports) |
| virtual_token_reserve | Token side of the pricing formula |
| real_lamports_reserve | Actual SOL deposited into the pool |
| real_token_reserve | Actual tokens remaining in the pool |
| token_amount | How many tokens were bought or sold |
| lamports_amount | How much SOL was spent or received |
| fee_lamports | Platform fee taken from the trade |
| user_wallet | The wallet that made the trade |
Want to explore the data yourself?
PumpFunData has every Pump.fun and Pump.fun AMM event since February 2026, in hourly Parquet files.