The 17.6 SOL Ghost: Reverse-Engineering Pump.fun's BOOST Migration Arb

It started as a monitoring anomaly. On July 22 our pump.fun indexer and one of our RPC nodes both started sweating: a burst of getProgramAccounts calls hammering the bonding-curve program, and a wave of old tokens — some launched back in early 2025 — suddenly migrating to the AMM. Dead coins don’t migrate. Dead coins sit at 60 SOL on a curve nobody will finish for months.

Except that day, ~230 of them completed in 24 hours, up from the usual 2–6.

Somebody had found an edge. This post is us reverse-engineering it from chain state: what pump.fun’s new BOOST feature actually does to virtual liquidity, where the free 17.6 SOL comes from, the exact formulas, the indexer queries we used to hunt targets, and a profitability table by curve size. Then the honest part — why the theoretical +15 SOL is really +2 to +4, and how you get stuck.

What BOOST is

Pump.fun shipped BOOST on 2026-07-21 (10:23 EST). The mechanic, from their own announcement: for ~5 minutes after every migration, the protocol runs a TWAP buyback-and-burn into the freshly created pool — roughly 17.6 SOL for SOL-paired tokens (≈ $2,516 for USDC pairs). They describe it as recycling “dead liquidity” — about 20% of a migrated pool’s value that, mathematically, can never be traded back out. Tokens migrated before the cutoff, or launched via Mayhem, get no boost.

That “dead liquidity” line is the whole story. Let’s find the 17.6 SOL.

The bonding curve, in numbers

A standard pump.fun curve is a constant-product AMM with virtual reserves. State lives in the BondingCurve account:

offset  0  discriminator            8 bytes  (sha256("account:BondingCurve")[:8])
offset  8  virtual_token_reserves   u64 LE
offset 16  virtual_sol_reserves     u64 LE
offset 24  real_token_reserves      u64 LE
offset 32  real_sol_reserves        u64 LE   <- "S": how full the curve is
offset 40  token_total_supply       u64 LE
offset 48  complete                 u8
offset 49  creator                  pubkey 32 bytes

The invariant is the usual one:

k = virtual_sol_reserves × virtual_token_reserves   (constant)

For the standard curve the launch parameters are:

virtual_sol_reserves₀   = 30 SOL          (pure virtual, no real SOL backing it)
virtual_token_reserves₀ = 1,073 M tokens
k                       = 30 × 1073 ≈ 32,190   (M·SOL)

Price at any point is vsol / vtok, so the opening price is 30 / 1073 = 0.028 SOL/M. As people buy, real SOL flows in, vsol rises, vtok falls along the hyperbola. The curve completes when real SOL hits 85 — i.e. vsol = 30 + 85 = 115:

vtok_at_completion = k / vsol = 32,190 / 115 = 279.9 M tokens

So of the 1,073 M nominal token side, 1073 − 279.9 = 793 M get sold into circulation during the bonding phase. The remaining ~206.9 M tokens are what pump deposits into the new AMM pool.

Finding the ghost: the 17.6 SOL of “dead liquidity”

Here’s the part everyone glosses over. When the curve migrates, pump seeds the AMM pool with those 206.9 M tokens, but it has to open the pool at the same price the curve ended on so there’s no instant gap to arb. The curve’s final price was:

P_final = 115 / 279.9 = 0.411 SOL/M

To make a pool of 206.9 M tokens quote at 0.411 SOL/M, the SOL side must be worth:

quote_needed = 206.9 M × 0.411 = 85 SOL

This is where pump changed something — and where the marketing starts. Until 2026-07-21 the migration handed the pool the full real SOL. Read a pre-cutoff pool from our indexer and the numbers are clean:

OLD regime (migrated before 2026-07-21):
pool_base_token_reserves   = 206.90 M tokens
pool_quote_token_reserves  =  84.99 SOL      (all REAL)
virtual_quote_reserves     =   0.00 SOL      (didn't exist)

Now read the very first swap of a post-cutoff pool (pumpswap_all_swaps, token EkLNaDq…):

NEW regime (BOOST):
pool_base_token_reserves   = 206.90 M tokens
pool_quote_token_reserves  =  67.41 SOL      (REAL sol in the pool)
virtual_quote_reserves     =  17.58 SOL      (phantom — Int128 field, constant for life)
--------------------------------------------------------------------
effective quote            =  84.99 SOL   ->  price = 84.99 / 206.9 = 0.411 SOL/M  ✓

Same price, same token side. The only change: pump carved 17.58 real SOL out of the pool and replaced it with a virtual quote reserve — an entry that props the quoted price up but can never be withdrawn. The curve still raised ~85 (pfamm_migrations.sol_amount = 84.99) and the migration fee is a rounding error (pool_migration_fee = 0.015 SOL); the withheld 17.58 gets drip-fed back over the next 5 minutes as the BOOST buyback. Same 17.58, three ways, all measured on-chain:

carved out     = old_real − new_real = 84.99 − 67.41 ≈ 17.58 SOL   (what BOOST spends)
virtual_quote_reserves (read off the new pool)      = 17.58 SOL     (where it's now booked)
residual       = pooled_tokens × P_final − new_real = 17.58 SOL     ("dead liquidity")

Here’s the honest part pump’s announcement skips: an AMM pool is just another bonding curve — same constant-product shape, different parameters. And that ~17.58 SOL was never extractable, in either regime. A constant-product pool holding 206.9 M tokens can never pay out its whole SOL side, because price → the floor as the circulating 793 M floods in (proof below). In the old regime that ~17.58 sat locked in the pool as genuinely stuck real SOL. Pump didn’t fix dead liquidity — it relabeled the stuck real SOL as virtual, pocketed the freed 17.58, and re-spends it as a buyback that arbers and snipers farm. Calling that “reinjecting dead liquidity” is marketing; the dead liquidity is still dead. For 5 minutes there’s simply now a price-insensitive buyer with 17.6 SOL — and that’s the whole edge.

Proof the migration does not preserve k

Here’s the structural flaw the whole edge rests on. To actually continue the curve — same price and same liquidity depth — the pool would need the curve’s ending reserves: vsol = 115, vtok = 279.9 M, i.e. k = 32,190. It doesn’t get them. The pool’s effective reserves are 85 × 206.9:

k_curve = 115   × 279.9 = 32,190
k_pool  = 84.99 × 206.9 = 17,585      ->  k_pool / k_curve = 0.546

The pool is only 55% as deep as the curve it replaced, at the same price. Price is preserved; k is not. Both sides are scaled down by √0.546 ≈ 0.74 (quote 85 vs 115, base 206.9 vs 279.9). Note this was already true in the old regime — 84.99 × 206.9 is the same k_pool — so the shallowness, and the dead ~17.58, predate BOOST entirely. That’s exactly why the ~793 M tokens already in circulation can never come home: the pool holds only 206.9 M tokens against them. BOOST didn’t change the depth or the trapped SOL; it just changed whether that trapped 17.58 is real and locked (old) or virtual and re-spent as a buyback (new).

The arbitrage

If a fresh pool comes with a guaranteed ~17.6 SOL buyer, the play writes itself:

  1. Find a stalled curve already holding a decent chunk of real SOL S.
  2. Complete it yourself — buy the last 85 − S SOL worth of the curve. This sweeps a bag of bag_M tokens and triggers migration.
  3. Migration seeds the pool and BOOST starts buying.
  4. Dump your bag into the boosted pool. The TWAP buyback (plus organic snipers) absorbs it.

You captured the boost, minus your completion cost, minus slippage and fees. The completion cost 85 − S is smaller the fuller the curve already is — so old, half-finished curves that stalled at 50–70 SOL are ideal: cheap to finish, and the bag you sweep is small enough to sell without cratering the pool.

The formulas

Given a curve’s on-chain state (vsol, vtok, S=real_sol_reserves):

cost      = 85 − S                                  # SOL to finish the curve
vsol_end  = vsol + cost
bag_M     = vtok − k / vsol_end                     # tokens you sweep en route to completion
dump      = 85 × bag_M / (206.9 + bag_M)            # SOL back from selling bag into the pool
boost     = min(17.6, dump)                         # boost you actually soak up
net       = dump + boost − cost − fees

The dump term is just the constant-product output of selling bag_M tokens into a pool of (206.9 M tokens, 85 eff-SOL). Note the two-sided tension baked in:

  • Emptier curve (low S) → cheap-ish to finish but you sweep a huge bag; selling it moves the pool hard, so slippage eats you.
  • Fuller curve (high S) → tiny bag, low slippage, but the bag is too small to soak up the full 17.6 SOL boost.

The optimum is where your bag’s sale value just saturates the 17.6 boost with minimal slippage.

What you’re actually arbing: a double-counted 17.6 SOL

Strip away the mechanics and the edge is an accounting overlap. The same 17.58 SOL is booked twice, and the arb front-runs everyone else to collect both copies:

  1. As virtual quote reserves (17.58, phantom). It isn’t in the pool — it’s a number that holds the quoted price at 0.411 instead of the real-backed 67.41 / 206.9 = 0.326. It inflates the depth you sell against.
  2. As the real BOOST buyback (17.58, cash). The identical 17.58 that was withheld from the raise is drip-fed back in as a live, price-insensitive buyer.

During the 5-minute window the pool therefore presents ~85 effective quote plus an incoming ~17.58 of real buys — call it ~102 of apparent liquidity — while only ~85 real SOL will ever exist for it (67.41 deposited + 17.58 returned). That extra 17.58 is pure phantom. Whoever completes the curve and dumps into the boost window sells at prices held up by the virtual copy and gets paid in real SOL by the buyback copy — banking the 17.58 twice over before the virtual is exposed as empty.

Who eats it? The pre-migration bagholders. The virtual 17.58 never converts to cash; once the real 67.41 is drained and the boost is skimmed, late sellers hit the virtual floor and get crumbs (quantified in “what happens if everyone sells”, below). The arb isn’t creating SOL from nothing — the books balance at ~85 real — it’s timing the overlap to capture the returning 17.58 before the holders it was nominally protecting can touch it.

Profitability by curve size

Running the model across the whole range of S (fee = 1 SOL for priority + tip + curve fees):

S (real SOL on curve)cost to finishbag swept (M)dump back (SOL)boost soakedslippagenet (SOL)
3055.0256.647.0617.6055%+8.66
4045.0179.939.5417.6047%+11.14
4540.0149.335.6317.6042%+12.23
5035.0122.531.6017.6037%+13.20
5530.098.827.4717.6032%+14.07
6025.077.823.2217.6027%+14.82
6322.066.220.6117.6024%+15.21
6619.055.417.9517.6021%+15.55 ← peak
6817.048.616.1616.1619%+14.31
7015.042.014.3414.3417%+12.68
7510.026.79.709.7011%+8.40
805.012.74.924.926%+3.85
841.02.51.001.001%−0.01

Modelled profit by curve size

The peak sits right at S ≈ 66 SOL: completion cost only 19 SOL, you sweep ~55 M tokens, and that bag sells for ~17.95 SOL — just barely enough to swallow the full 17.6 boost. The right panel shows why: the boost captured line rides flat at 17.6 until S≈66, then peels off and dives — past that point your bag is too small to soak the whole buyback. Below S≈66 you’re overpaying in slippage on an oversized bag; above it you’re leaving boost on the table. Everything past ~83 SOL is a loss: nothing left to sweep. (The shaded green band on the left is where real completions actually landed — see below.)

So the target profile is unambiguous: incomplete curves holding ~50–70 real SOL, stalled (nobody’s going to finish them organically). Now we just have to find them.

Finding targets — the indexer path

You can do this two ways. The brute-force way is getProgramAccountsV2 on the pump program with a memcmp on the discriminator and complete = 0, then filter reserves client-side. That works, but pump has >10 M accounts, sweet-spot curves are rarer than 1 in 15,000, and V2 paginates over storage (a reserve memcmp just yields empty pages) — so you walk millions of accounts per scan. That’s the load we saw others generating. Ground-truth, but brutal on an RPC.

The fast path is an indexer. Every swap we store carries virtual_sol_balance_after / virtual_token_balance_after, so the latest swap per token reconstructs the current curve state for free. One query finds every standard curve stalled in the band:

SELECT
    base_coin,
    argMax(virtual_sol_balance_after,  block_time) / 1e9  AS vsol,
    argMax(virtual_token_balance_after, block_time) / 1e12 AS vtok_M,
    max(block_time) AS last_swap,
    count()         AS swaps
FROM pumpfun_all_swaps
GROUP BY base_coin
HAVING vsol BETWEEN 80 AND 100          -- 30 virtual + 50..70 real SOL
   AND vsol * vtok_M BETWEEN 28000 AND 36000   -- ~k, keeps only standard curves
   AND last_swap < now() - INTERVAL 2 DAY       -- stalled = nobody finishing it

vsol * vtok_M ≈ k ≈ 32,190 is the trick that throws out non-standard curves (custom params, Mayhem, weird launches) in one predicate. On our data this returned 9,207 standard curves stalled in the 50–70 SOL band — plenty of inventory.

Then rank by the model first, and only enrich the top slice (a big IN (...) clause hits ClickHouse’s query-size limit fast). Two cheap lookups drop the disqualified:

-- Mayhem tokens get NO boost; drop them
SELECT mint, mayhem_mode
FROM pumpfun_token_creation
WHERE mint IN ('<top-N mints>');

-- already migrated? then it's gone
SELECT DISTINCT mint
FROM pfamm_migrations
WHERE mint IN ('<top-N mints>');

One critical gotcha: the indexer state is stale by construction. If the completing swap hasn’t been ingested yet, a curve that already migrated still looks open. Every one of our top math-optimal candidates read S ≈ 66 in the index but came back complete = 1, S = 0 when we checked chain directly. So the last step is mandatory — verify live against the bonding-curve PDA before you touch it:

from solders.pubkey import Pubkey
bc, _ = Pubkey.find_program_address(
    [b"bonding-curve", bytes(Pubkey.from_string(mint))], PUMP)
# getAccountInfo(bc) -> unpack "<QQQQQ" at offset 8 -> check byte[48] (complete) + real_sol

The indexer tells you where to look; chain state tells you whether it’s still there.

How much actually gets burned

BOOST buys and burns, so the burn shows up directly in the base_supply field — it ticks down swap by swap during the ~5-minute window, then flatlines. How much it burns depends entirely on the price it’s forced to buy at, because tokens_burned ≈ boost_sol / avg_boost_price. Two real tokens, same 17.58 SOL budget, opposite outcomes:

tokenprice action in boost windowbase_supply burned% of supplyimplied avg buy price
EkLNaDq…mooned (0.33 → 6,356 SOL/M peak)0.083 M0.008%~211 SOL/M
Eknkq…dumped (0.33 → 13.7 SOL/M)119.8 M12.0%~0.15 SOL/M

That’s a 1,400× spread in tokens burned from the same buyback budget. The burn is a deflation dividend that pays out inversely to price: if the token rips, BOOST buys almost nothing (and burns almost nothing — 83k tokens); if the token craters, BOOST scoops up cheap supply and torches 12% of it. For the arber this is a second-order tailwind on the dumped tokens — every token burned lifts the value of the bag they’re still holding — but it’s tiny next to the SOL flows.

What the 793 M holders actually get

This is the stuck-bag question made exact, and it’s the cleanest proof that BOOST changed nothing for holders. Push the entire circulating 793.1 M through the opening pool (k = base × eff_quote; extraction floored by the virtual reserve):

base_after      = 206.9 + 793.1 = 1000 M
eff_quote_after = k / base_after
real SOL paid   = eff_quote_open − eff_quote_after   (capped at the real reserve)

Run it for both regimes:

regimepool at openall 793 M sell → receivedead
OLD (pre-07-21)84.99 real + 0 virtual67.41 SOL17.58 (locked real)
NEW (BOOST)67.41 real + 17.58 virtual67.41 SOL17.58 (virtual floor)

Identical. In both worlds the entire circulating supply, dumping together, extracts exactly 67.41 SOL for 793.1 M tokens — an average of 0.085 SOL/M, a 79% haircut to the migration price — and ~17.58 SOL is dead. The only thing BOOST changed is who touches the dead 17.58: in the old regime it sat locked forever; now pump re-spends it as a buyback that the completer/snipers skim during the 5-minute window. The pre-migration holders it was nominally “reinjecting liquidity” for get the same 67.41 they always would have.

Burned vs received, per token

The burn (from base_supply) swings wildly with price; the holder receipt does not move at all. A random sample of boosted migrations:

tokentokens burned% of supplypeak priceholders’ all-sell receipt
j8uYHd1F0.178 M0.018%197.0 SOL/M67.41 SOL
ZZXBcwSC11.51 M1.15%3.2 SOL/M67.41 SOL
6ND8GsVr19.51 M1.95%8.5 SOL/M67.41 SOL
CVdByCD730.79 M3.08%2.2 SOL/M67.41 SOL
GBDW5Mkc37.08 M3.71%0.7 SOL/M67.41 SOL
DPoN5nP351.85 M5.19%0.4 SOL/M67.41 SOL
73Ldwtam94.86 M9.49%0.3 SOL/M67.41 SOL

Burn spans 0.018% to 9.5% of supply (earlier we saw one hit 12%) — it’s boost_sol / avg_boost_price, so mooning tokens burn almost nothing and dumping tokens burn a lot. But holder receipt is pinned at 67.41 SOL in every single row, because that number is set by the pool’s real reserve, not by anything the burn does. The deflation is real but second-order: torching 9.5% of supply lifts each surviving token’s claim, yet the SOL that claim can be redeemed for is still capped at ~67 for the whole crowd. You can always sell — into a fixed, tiny pot, in queue order.

The honest part: +15 on paper, +2–4 in the wild

Now the reality check, because the table above is a ceiling, not an expectation.

We traced completions on-chain, matching tokens in and out. A clean example — token 7Rauo... (launched Jan 2025, genuinely dead): the completer bought 486.53 M tokens for 73.0 SOL, then sold the identical 486.53 M for 75.7 SOL. Net +2.7 SOL. Another, BxLo81...: bought 410.8 M for 68.4, sold all 410.8 M for 72.55 — +4.15 SOL.

Real, repeatable, positive — but a far cry from +15. Where does the other ~11 SOL go?

  1. The boost is a 5-minute TWAP, not a lump. You can’t dump into all 17.6 SOL instantly; it trickles, and you’re selling against the trickle.
  2. You’re not alone. The same core sniper wallets show up on nearly every one of these completions (one wallet hit 13 of 14 tokens we traced), triggered within 0–6 s of pool creation. They eat boost alongside you.
  3. Slippage is real, and the model’s dump is optimistic about how much the pool absorbs before the price caves.

Stuck-bag risk — and the thing that actually kills you

Our first instinct was “you can get stuck holding an unsellable bag.” Digging in, that was half wrong, and the correction is the most important lesson here.

We initially flagged wallet 6QE83Z as a −60 SOL disaster. It wasn’t — that was a measurement artifact: we’d summed its AMM sells inside a 6-hour window that cut off before it finished unwinding. Pull the full history and it sold every token, for +4 SOL, just slowly. You can almost always sell. The naive fear is wrong.

The real risk is subtler and it’s a competition for a finite pot. As the math above shows, the migrated pool holds only ~85 real SOL of extractable value (67.41 real + the 17.6 boost) — the virtual 17.58 is a floor nobody can withdraw. Everyone selling shares that pot. Your bag is worth ~your bag — if you’re the only seller. The dangerous competitor isn’t the snipers (they flip small); it’s the old bagholders. When a genuinely old token migrates, every wallet that bought its curve in 2024–25 and got stranded can finally sell into the same pool. If a large dormant float wakes up, they drain the 85 SOL alongside you and your later sells fetch dust.

Why the arb works at all: on truly dead tokens, those old holders stay asleep — they abandoned the coin months ago and aren’t watching. That dormancy is the edge. Which flips the target-selection criterion: you don’t just want a curve at ~66 SOL, you want one whose non-completer float is small or provably inactive. The ideal victim is a curve that stalled because everyone already left — not one with a wall of stranded holders about to get a liquidity event courtesy of your completion buy.

Takeaways

  • “Dead liquidity” is marketing. An AMM pool is just another constant-product curve. The ~17.58 SOL was never extractable in either regime — old pools locked it as real SOL (measured: 84.99 real, 0 virtual); new pools relabel it virtual, pump pockets the freed 17.58 and re-spends it as a buyback. The dead liquidity is still dead.
  • Holders get the same deal before and after. All 793 M circulating, dumping together, extract exactly 67.41 SOL (a 79% haircut) in both regimes; ~17.58 is dead either way. BOOST only changed who skims the freed 17.58 — arbers and snipers, not the holders it “reinjects” for.
  • Migration never preserved k. The pool is seeded at 55% of the curve’s depth (17,585 vs 32,190) — true pre-BOOST too. That structural shallowness is the whole reason the circulating float can’t come home.
  • You’re arbing a double count. During the 5-min window the same 17.58 SOL is booked as phantom virtual reserves and re-injected as a real buyback; the arber times the overlap to grab both before holders can.
  • The burn is inverse to price. Same 17.58 budget torched 0.018% of supply on a mooning token and up to 12% on a dumping one — while holder receipt stayed pinned at 67.41 SOL regardless.
  • The sweet spot is a curve at ~50–70 real SOL, stalled. Peak model EV ~+15 at S≈66; real world is +2 to +4 after TWAP timing, snipers, and slippage.
  • Indexer to find, chain to confirm. vsol*vtok ≈ k filters standard curves in one line; live PDA reads are mandatory because index state lags the completing swap.
  • The kill risk is old-holder overhang, not “can’t sell.” All sellers combined can extract only ~85 real SOL; target curves whose stranded float is dormant.

This is research tooling and market-structure analysis, not trading advice. The edge is real, small, crowded, and entirely at pump.fun’s discretion — they invented the 17.6 SOL and they can un-invent it in a single deploy.


Analysis code lives in our research repo at experiments/pumpfun_boost_arb/. Data pulled from our pump.fun swap/creation/migration indexer and live Helius RPC. Questions? Find us on X or Discord.