Program Notes
Program Notes

What a Launchpad Migration Writes to the Chain

Migration is the one moment in a launchpad token's life when the venue changes underneath everyone. A flag flips in program state, curve trading stops being accepted, reserves leave the curve accounts, and a pool is initialised somewhere else. All four are recorded, and all four can be checked from a signature rather than taken on trust.

BH-03 The Program Notes Desk 2159 words 10 min read Updated 13 August 2026

Summary of this note

Question
What does the chain actually record when a bonding curve completes and liquidity moves
Inputs
The curve state account, the migration transaction, and the initialisation of the destination pool
Rule
The flag in program state is the authority; a dashboard label is a description of it at best
Failure mode
Adding curve activity and pool activity together as if they were one continuous market

When a bonding curve completes, four things are written: a completion flag is set in the launchpad's state account for that mint, further curve trades begin failing, the token and lamport reserves are moved out of the curve-owned accounts, and a pool is created or funded on an automated market maker so the pair can keep trading. Every one of those is a recorded state change with a signature attached to it.

That matters because migration is the moment when most published figures about a token quietly stop meaning what they meant. Activity before the handover happened against a curve with a formula and no counterparties; activity afterwards happens in a pool with liquidity providers and a different fee structure. Adding the two together produces a number that describes nothing.

What completion means in program state

A launchpad's state account for a mint holds the reserve figures its pricing formula reads, plus flags describing the token's phase. Completion is one of those flags. It is not a label applied by a front end and it is not derived from a price; it is a boolean in an account that the program itself checks at the top of every trade instruction.

The condition that triggers it is a program constant compared against the accumulated reserves, and that constant has differed between deployed versions of launchpad programs. This is precisely the kind of value that should never be quoted as permanent. If you need the current threshold, read the program's own configuration account, or infer it from the reserve figures of a curve that completed recently. Both approaches survive a redeployment; a memorised figure does not.

The flag, and what it forbids

Once the flag is set, the trade instruction that worked a moment earlier starts failing with a constraint error. This is the single most confusing failure mode for anyone running automation across the boundary, because nothing about the sender changed. The account list is identical, the arguments are well formed, the wallet is funded, and the program refuses anyway.

Reading it correctly takes ten seconds if you know what to look for: a confirmed failure with a custom error, on a mint whose state account shows completion set. The remedy is not to retry harder. It is to stop sending curve instructions for that mint entirely, because there is no longer a curve to trade against, and every further attempt is a fee paid for a guaranteed rejection.

An automation system that keeps retrying after completion is not merely wasteful; it is evidence that the system reads its own errors as transient by default. Distinguishing a transient failure from a terminal one is a design decision, and the completion boundary is the cleanest example of why it has to be made explicitly.

The write sequence of a handover

The handover is usually a small number of transactions rather than one. The exact grouping depends on the deployed program, but the logical order is consistent, because each step depends on the one before it.

  1. A trade pushes the accumulated reserves past the program's completion condition, and the flag is set in the same instruction that processed the trade.
  2. Curve trading closes. Any instruction arriving after that write fails the completion check regardless of its contents.
  3. The token account owned by the curve is drained of the tokens reserved for the destination pool.
  4. Lamports held on the curve side are moved out, minus whatever the deployment retains as fees.
  5. A pool account is created or funded on the destination automated market maker, holding both sides of the pair.
  6. Liquidity provider tokens representing that position are minted to whatever account the program designates, and their handling varies by deployment.

Reading the inner instructions

Steps three through six are where the interesting reading is. They appear as inner instructions under one or two outer calls, and they include token program transfers, system program transfers, and an initialisation call against the destination market maker's program. The token movements themselves follow the standard token program interface documented in the Solana program library reference, which is why they are legible even when the launchpad-specific parts are not.

What pool initialisation looks like

Creating an automated market maker pool is more accounts than a curve trade, because a pool needs its own state account, two vault accounts to hold the paired assets, a mint for the liquidity provider position, and often a fee configuration account. Initialisation writes all of them, funds the vaults, and records the initial deposit.

For a reader, the practical identification steps are simple. Find the account in the migration transaction that did not exist before and is owned by the market maker program: that is the pool state. Find the two token accounts it owns: those are the vaults. Compare their post balances against the pre balances of the curve accounts that were drained, and the two should reconcile within whatever the deployment kept as fees.

What you should not do is take a pool address from a chat message or an aggregator without checking it. Deriving or discovering the pool from the migration transaction itself takes a minute and removes an entire class of mistake, including the common one of ending up looking at a different pool for the same pair.

There is a further wrinkle worth knowing. Pool designs differ in how they hold assets and how they price them, and a launchpad's chosen destination is a specific design rather than a generic venue. A constant-product pool with two vaults behaves differently from a concentrated-liquidity design where providers choose ranges, and the account layout of the initialisation transaction is the fastest way to tell which family you are looking at. Counting vault accounts and looking for a range or bin parameter takes a few seconds and settles the question.

What changes about fees

The fee model changes at the same moment, and it changes in kind rather than in size. Curve fees are a program parameter applied identically to every trade. Pool fees are paid to whoever supplied the liquidity, which means part of what a trader pays after migration is going to other market participants rather than to the protocol. Any cost comparison across the boundary that ignores that distinction is comparing two different things and calling the difference a saving.

Where the tokens and lamports end up

AssetBefore completionAfter the handoverHow to check it
Unsold tokensIn the token account owned by the curve stateIn the pool's token vault, or partly burned depending on the deploymentCompare pre and post token balances of both accounts in the migration transaction
Accumulated lamportsHeld against the curve state accountIn the pool's paired vault, less retained feesCompare pre and post lamport balances across the same accounts
Liquidity positionDoes not existRepresented by provider tokens minted at initialisationFind the newly created mint in the transaction and read who received the supply
Curve state accountLive, updated by every tradeRetained with the completion flag set, no longer accepting tradesRead the account directly; it remains queryable after completion

The row that generates the most questions is the liquidity position. What happens to those provider tokens has varied between launchpad designs and between versions of the same launchpad: some arrangements burn them, some retain them under program control, and some assign them elsewhere. This is a design choice with real consequences for whether the pool's liquidity can be withdrawn, and it is one of the first things worth establishing for any specific deployment, from the transaction rather than from a description.

After: price discovery moves

Before completion, price is a function. The curve's formula turns reserve figures into a quote, there is no order book, no liquidity provider and no counterparty in the usual sense, and every trade moves the price deterministically. After completion, price is a market. Providers can add or remove liquidity, arbitrage connects the pair to the rest of the ecosystem, and depth is no longer a program constant.

This is why the two phases should never be summed. A figure that adds curve activity to pool activity is adding two quantities that were produced by different mechanisms, with different fee treatment and different meaning. If you need one number across the whole life of a token, it has to be labelled as two segments, and the boundary slot has to be stated.

It also changes what automation on the token is doing. Curve-phase activity is a trade against a formula with a known impact for a given size. Pool-phase activity is a trade against whatever depth providers have chosen to supply at that moment. Any tool that operates on both sides, including volume tooling built for the Pump.fun curve, is doing two structurally different jobs either side of that flag, and the reporting it returns should make clear which venue each signature landed on.

Verifying a migration from a signature

The verification procedure is short and worth internalising, because it replaces every second-hand claim about whether a token has migrated with a first-hand answer. It uses only the mint address as trusted input.

Derive the launchpad state account for the mint and read it. If the completion flag is set, curve trading is closed regardless of what any interface shows. Then walk that account's transaction history back to the write that set the flag; that signature is the migration event. Open it, expand the inner instructions, and read which programs were invoked and which accounts changed balance. The destination pool is whichever account the value arrived in.

Two habits make this reliable. First, always reconcile balances rather than reading log text, because logs describe what a program wanted to say and balances describe what moved. Second, record the boundary slot, because every later question about the token's activity depends on which side of that slot a transaction landed. Account queries and transaction history are both standard node interfaces described in the Solana documentation.

Things that go wrong at the boundary

  • Late curve trades. Transactions built before the flag flipped and landing after it will fail. The fee is spent, and the failure is correct behaviour rather than a bug.
  • Automation that does not switch venues. A system that only knows curve instructions goes silent on a migrated token while reporting no errors, because it is not sending anything at all.
  • Stale pool addresses. A pair can end up with more than one pool over time. An address cached from an earlier discovery may point at a pool nobody is using.
  • Double counting the handover. The migration transaction itself moves large balances. Treating it as trading activity inflates the token's numbers on both sides of the boundary at once.
  • Assuming an instant transition. Completion and pool availability are separate writes, and there is a short interval where the curve is closed and the pool is not yet usable.

The last one is the most operationally relevant. During that interval a token is not tradable anywhere, and any system that measures continuous availability will record it as an outage. It is not an outage; it is the handover.

Reading activity across the boundary

If you are trying to describe a token's life honestly, the boundary slot becomes the organising fact. Everything before it is curve activity: a series of trades against a formula, each one moving reserves deterministically, with no liquidity providers involved. Everything after it is pool activity: trades against supplied depth, with providers earning fees and able to withdraw.

The practical reporting rule that follows is to segment first and aggregate never. Report curve-phase and pool-phase figures separately, state the boundary slot, and exclude the migration transaction itself from both. That produces three numbers instead of one, and all three mean something, which is a better trade than one number that means nothing.

This also affects how you read the wallet patterns in the note on bot classes in a trace. A cluster of wallets that traded a mint heavily during its curve phase and vanished at completion is a different operation from one that continued into the pool. The boundary is a natural filter for separating those, and it is free.

Why the destination is not fixed

The last point is the one this desk repeats most often. Where a completed curve hands its liquidity is a decision encoded in a deployed program, and it has changed. Launchpads have migrated to third-party automated market makers and have later moved to first-party ones, and there is no reason to expect the current arrangement to be the final one.

The consequence is that a note describing the destination by name would be a note with an expiry date. What does not expire is the method: read the flag, find the write that set it, follow the inner instructions to whichever program received the value, and reconcile the balances. That procedure produces the correct answer for whatever the destination happens to be on the day you run it, which is the only kind of answer worth writing down.

Filed under Behaviour. Account lists, discriminators and program constants described here belong to the program version that was live when the note was written, and a launchpad deployment can change any of them without warning. If a signature you are looking at disagrees with this page, the signature is right: send it to the desk and the page gets corrected in the open.

Read next