Understanding what happens when a token graduates from the bonding curve to the PumpAMM pool.
When enough SOL has been deposited into a bonding curve, the token "graduates" — it migrates from the bonding curve to a constant-product AMM pool (PumpAMM). This happens automatically when the bonding curve fills up.
After migration:
bondingCurve.complete === true- Trading happens on the PumpAMM pool instead of the bonding curve
- The token becomes tradeable on standard DEX interfaces
import { Connection, PublicKey } from "@solana/web3.js";
import { OnlinePumpSdk, getGraduationProgress } from "@nirholas/pump-sdk";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const onlineSdk = new OnlinePumpSdk(connection);
const mint = new PublicKey("YOUR_TOKEN_MINT_ADDRESS");
const bondingCurve = await onlineSdk.fetchBondingCurve(mint);
if (bondingCurve.complete) {
console.log("Token has graduated to PumpAMM!");
console.log("Bonding curve is now closed.");
} else {
console.log("Token is still on the bonding curve.");
console.log("Real SOL collected:", bondingCurve.realSolReserves.toString());
}Use the analytics module to see how close a token is to graduating:
const global = await onlineSdk.fetchGlobal();
const feeConfig = await onlineSdk.fetchFeeConfig();
// Quick way — use the online SDK directly
const progress = await onlineSdk.fetchGraduationProgress(mint);
console.log(`Graduation progress: ${(progress.progressBps / 100).toFixed(1)}%`);
console.log(`SOL accumulated: ${progress.solAccumulated.toNumber() / 1e9} SOL`);
console.log(`Tokens remaining: ${progress.tokensRemaining.toString()}`);
console.log(`Already graduated: ${progress.isGraduated}`);
// Or use the offline function when you already have the bonding curve data
const offlineProgress = getGraduationProgress(global, bondingCurve);function renderProgressBar(progressBps: number): string {
const pct = progressBps / 100;
const filled = Math.floor(pct / 10);
const bar = "█".repeat(filled) + "░".repeat(10 - filled);
return `[${bar}] ${pct.toFixed(1)}%`;
}
const progress = await onlineSdk.fetchGraduationProgress(mint);
console.log(renderProgressBar(progress.progressBps));
// [████████░░] 82.3%Migration is triggered by a permissioned authority. The SDK provides migrateInstruction:
import { PUMP_SDK } from "@nirholas/pump-sdk";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
// Only the withdraw authority can call this
const migrateIx = await PUMP_SDK.migrateInstruction({
withdrawAuthority: withdrawAuthorityPubkey,
mint: mintPubkey,
user: userPubkey,
tokenProgram: TOKEN_PROGRAM_ID,
});- Remaining tokens from the bonding curve are moved to a PumpAMM pool
- Collected SOL is deposited as the other side of the AMM pair
bondingCurve.completeis set totrue- A new pool account is created at the canonical PDA
import { canonicalPumpPoolPda, pumpPoolAuthorityPda, bondingCurvePda } from "@nirholas/pump-sdk";
const mint = new PublicKey("YOUR_MINT");
// The bonding curve (still exists, but complete = true)
const bc = bondingCurvePda(mint);
// The AMM pool address
const pool = canonicalPumpPoolPda(mint);
// The pool authority (owns the pool's token accounts)
const poolAuth = pumpPoolAuthorityPda(mint);
console.log("Bonding curve:", bc.toBase58());
console.log("AMM pool:", pool.toBase58());
console.log("Pool authority:", poolAuth.toBase58());After migration, use the PumpAMM instructions for trading. The SDK provides AMM instruction builders:
import { PUMP_SDK } from "@nirholas/pump-sdk";
// Buy on AMM pool (post-graduation)
const ammBuyIx = await PUMP_SDK.ammBuyInstruction({
pool: poolAddress,
user: userPubkey,
// ... AMM-specific parameters
});
// Sell on AMM pool
const ammSellIx = await PUMP_SDK.ammSellInstruction({
pool: poolAddress,
user: userPubkey,
// ... AMM-specific parameters
});For tokens that may or may not have graduated, the OnlinePumpSdk provides BothPrograms methods that handle routing automatically:
// These work regardless of whether the token is on the bonding curve or AMM
const unclaimed = await onlineSdk.getTotalUnclaimedTokensBothPrograms(user);
const todayTokens = await onlineSdk.getCurrentDayTokensBothPrograms(user);
const claimIxs = await onlineSdk.claimTokenIncentivesBothPrograms(user);import { OnlinePumpSdk, bondingCurvePda } from "@nirholas/pump-sdk";
import { Connection, PublicKey } from "@solana/web3.js";
async function getTokenState(mint: PublicKey) {
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const onlineSdk = new OnlinePumpSdk(connection);
try {
const bc = await onlineSdk.fetchBondingCurve(mint);
if (bc.complete) {
return {
state: "graduated",
message: "Trading on PumpAMM pool",
creator: bc.creator.toBase58(),
};
}
if (bc.virtualTokenReserves.isZero()) {
return {
state: "migrated",
message: "Fully migrated, bonding curve empty",
};
}
return {
state: "active",
message: "Trading on bonding curve",
realSolReserves: bc.realSolReserves.toString(),
realTokenReserves: bc.realTokenReserves.toString(),
creator: bc.creator.toBase58(),
};
} catch {
return { state: "not_found", message: "Token not found on Pump" };
}
}
const state = await getTokenState(new PublicKey("YOUR_MINT"));
console.log(state);