Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add sendaifun/skills --skill "pumpfun"
Install specific skill from multi-skill repository
# Description
Complete PumpFun Protocol guide for building token launches, bonding curves, and AMM integrations on Solana. Covers Pump Program (token creation, buy/sell on bonding curves), PumpSwap AMM (liquidity pools, swaps), fee structures, creator fees, and SDK integration.
# SKILL.md
name: pumpfun
creator: raunit-dev
description: Complete PumpFun Protocol guide for building token launches, bonding curves, and AMM integrations on Solana. Covers Pump Program (token creation, buy/sell on bonding curves), PumpSwap AMM (liquidity pools, swaps), fee structures, creator fees, and SDK integration.
PumpFun Protocol Integration Guide
A comprehensive guide for building applications with PumpFun - Solana's leading token launch and AMM protocol enabling instant trading without initial liquidity.
Overview
PumpFun is a token launch and trading protocol on Solana offering:
- Pump Program - Create SPL tokens with instant trading on bonding curves
- PumpSwap (AMM) - Constant-product automated market maker for graduated tokens
- Creator Fees - Automatic fee distribution to token creators
- Token2022 Support - Modern token standard via create_v2 instruction
- Mayhem Mode - Special mode for enhanced token launches
Program IDs
| Program | Address |
|---|---|
| Pump Program | 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P |
| PumpSwap AMM | pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA |
| Pump Fees | pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ |
| Mayhem Program | MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e |
Quick Start
Installation
# Install PumpFun SDKs
npm install @pump-fun/pump-sdk @pump-fun/pump-swap-sdk
# Or with pnpm
pnpm add @pump-fun/pump-sdk @pump-fun/pump-swap-sdk
Basic Setup
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import bs58 from 'bs58';
// Setup connection
const connection = new Connection('https://api.mainnet-beta.solana.com');
const wallet = Keypair.fromSecretKey(bs58.decode('YOUR_SECRET_KEY'));
// Program addresses
const PUMP_PROGRAM_ID = new PublicKey('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P');
const PUMP_AMM_PROGRAM_ID = new PublicKey('pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA');
const PUMP_FEES_PROGRAM_ID = new PublicKey('pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ');
Pump Program (Bonding Curves)
The Pump program enables creation of SPL tokens with instant trading on a bonding curve without requiring initial liquidity.
How Bonding Curves Work
- Token Creation - Create a token with initial virtual reserves
- Trading - Users buy/sell on the bonding curve using Uniswap V2 formula
- Graduation - When market cap threshold is reached, liquidity migrates to PumpSwap
- LP Burn - LP tokens are burned, making liquidity permanent
Global Configuration
interface Global {
initialized: boolean;
authority: PublicKey;
feeRecipient: PublicKey;
initialVirtualTokenReserves: bigint; // Default: 1,073,000,000,000,000
initialVirtualSolReserves: bigint; // Default: 30,000,000,000 (30 SOL)
initialRealTokenReserves: bigint; // Default: 793,100,000,000,000
tokenTotalSupply: bigint; // Default: 1,000,000,000,000,000
feeBasisPoints: bigint; // Default: 100 (1%)
creatorFeeBasisPoints: bigint; // Creator fee percentage
}
Bonding Curve Account
interface BondingCurve {
virtualTokenReserves: bigint;
virtualSolReserves: bigint;
realTokenReserves: bigint;
realSolReserves: bigint;
tokenTotalSupply: bigint;
complete: boolean;
creator: PublicKey; // Token creator address
isMayhemMode: boolean; // Mayhem mode flag
}
Deriving PDAs
import { PublicKey } from '@solana/web3.js';
const PUMP_PROGRAM_ID = new PublicKey('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P');
// Derive bonding curve PDA
function getBondingCurvePDA(mint: PublicKey): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('bonding-curve'), mint.toBuffer()],
PUMP_PROGRAM_ID
);
}
// Derive associated bonding curve (token account)
function getAssociatedBondingCurve(mint: PublicKey): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[
Buffer.from('associated-bonding-curve'),
mint.toBuffer()
],
PUMP_PROGRAM_ID
);
}
// Derive creator vault PDA
function getCreatorVaultPDA(creator: PublicKey): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('creator-vault'), creator.toBuffer()],
PUMP_PROGRAM_ID
);
}
// Derive global account PDA
function getGlobalPDA(): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('global')],
PUMP_PROGRAM_ID
);
}
Create Token (Legacy SPL)
import {
Connection,
Keypair,
PublicKey,
Transaction,
TransactionInstruction,
SystemProgram,
SYSVAR_RENT_PUBKEY,
} from '@solana/web3.js';
import {
TOKEN_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID,
getAssociatedTokenAddressSync,
} from '@solana/spl-token';
async function createToken(
connection: Connection,
payer: Keypair,
name: string,
symbol: string,
uri: string
): Promise<string> {
const mint = Keypair.generate();
const [bondingCurve] = getBondingCurvePDA(mint.publicKey);
const [associatedBondingCurve] = getAssociatedBondingCurve(mint.publicKey);
const [global] = getGlobalPDA();
// Metaplex metadata PDA
const METADATA_PROGRAM_ID = new PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
const [metadata] = PublicKey.findProgramAddressSync(
[
Buffer.from('metadata'),
METADATA_PROGRAM_ID.toBuffer(),
mint.publicKey.toBuffer(),
],
METADATA_PROGRAM_ID
);
// Build create instruction data
const nameBuffer = Buffer.from(name);
const symbolBuffer = Buffer.from(symbol);
const uriBuffer = Buffer.from(uri);
const data = Buffer.alloc(
8 + 4 + nameBuffer.length + 4 + symbolBuffer.length + 4 + uriBuffer.length
);
// Write discriminator for 'create' instruction
const discriminator = Buffer.from([0x18, 0x1e, 0xc8, 0x28, 0x05, 0x1c, 0x07, 0x77]);
discriminator.copy(data, 0);
let offset = 8;
data.writeUInt32LE(nameBuffer.length, offset);
offset += 4;
nameBuffer.copy(data, offset);
offset += nameBuffer.length;
data.writeUInt32LE(symbolBuffer.length, offset);
offset += 4;
symbolBuffer.copy(data, offset);
offset += symbolBuffer.length;
data.writeUInt32LE(uriBuffer.length, offset);
offset += 4;
uriBuffer.copy(data, offset);
const instruction = new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
{ pubkey: mint.publicKey, isSigner: true, isWritable: true },
{ pubkey: payer.publicKey, isSigner: false, isWritable: true },
{ pubkey: bondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
{ pubkey: global, isSigner: false, isWritable: false },
{ pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: metadata, isSigner: false, isWritable: true },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
],
data,
});
const tx = new Transaction().add(instruction);
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer, mint);
const signature = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(signature);
return mint.publicKey.toBase58();
}
Create Token V2 (Token2022)
// create_v2 uses Token2022 program instead of legacy Metaplex
// Account structure (14 accounts):
const createV2Accounts = [
'mint', // 0: New token mint (signer, writable)
'mintAuthority', // 1: Mint authority PDA
'bondingCurve', // 2: Bonding curve PDA (writable)
'associatedBondingCurve', // 3: Token account for bonding curve (writable)
'global', // 4: Global config
'user', // 5: Creator/payer (signer, writable)
'systemProgram', // 6: System program
'token2022Program', // 7: Token2022 program
'associatedTokenProgram', // 8: Associated token program
'rent', // 9: Rent sysvar
'mayhemProgram', // 10: Mayhem program ID (for mayhem mode)
'mayhemFeeRecipient', // 11: Mayhem fee recipient
'eventAuthority', // 12: Event authority
'program', // 13: Pump program
];
Buy Tokens on Bonding Curve
interface BuyArgs {
amount: bigint; // Token amount to buy
maxSolCost: bigint; // Maximum SOL to spend (slippage protection)
}
async function buyTokens(
connection: Connection,
payer: Keypair,
mint: PublicKey,
amount: bigint,
maxSolCost: bigint
): Promise<string> {
const [bondingCurve] = getBondingCurvePDA(mint);
const [associatedBondingCurve] = getAssociatedBondingCurve(mint);
const [global] = getGlobalPDA();
// Get bonding curve data to find creator
const bondingCurveData = await connection.getAccountInfo(bondingCurve);
// Parse to get creator address...
const creator = parseCreatorFromBondingCurve(bondingCurveData);
const [creatorVault] = getCreatorVaultPDA(creator);
// User's associated token account
const userAta = getAssociatedTokenAddressSync(mint, payer.publicKey);
// Fee recipient (from global config)
const feeRecipient = new PublicKey('FEE_RECIPIENT_ADDRESS');
// Build buy instruction
const data = Buffer.alloc(8 + 8 + 8);
const discriminator = Buffer.from([0x66, 0x06, 0x3d, 0x12, 0x01, 0xda, 0xeb, 0xea]);
discriminator.copy(data, 0);
data.writeBigUInt64LE(amount, 8);
data.writeBigUInt64LE(maxSolCost, 16);
const instruction = new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
{ pubkey: global, isSigner: false, isWritable: false },
{ pubkey: feeRecipient, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: bondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
{ pubkey: userAta, isSigner: false, isWritable: true },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
{ pubkey: creatorVault, isSigner: false, isWritable: true },
// Fee config accounts (required since September 2025)
{ pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: feeConfigPDA, isSigner: false, isWritable: false },
],
data,
});
// Check if account extension needed (account size < 150 bytes)
const accountInfo = await connection.getAccountInfo(bondingCurve);
if (accountInfo && accountInfo.data.length < 150) {
// Prepend extendAccount instruction
const extendIx = createExtendAccountInstruction(bondingCurve);
const tx = new Transaction().add(extendIx, instruction);
} else {
const tx = new Transaction().add(instruction);
}
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
const signature = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(signature);
return signature;
}
Sell Tokens on Bonding Curve
interface SellArgs {
amount: bigint; // Token amount to sell
minSolOutput: bigint; // Minimum SOL to receive (slippage protection)
}
async function sellTokens(
connection: Connection,
payer: Keypair,
mint: PublicKey,
amount: bigint,
minSolOutput: bigint
): Promise<string> {
const [bondingCurve] = getBondingCurvePDA(mint);
const [associatedBondingCurve] = getAssociatedBondingCurve(mint);
const [global] = getGlobalPDA();
const bondingCurveData = await connection.getAccountInfo(bondingCurve);
const creator = parseCreatorFromBondingCurve(bondingCurveData);
const [creatorVault] = getCreatorVaultPDA(creator);
const userAta = getAssociatedTokenAddressSync(mint, payer.publicKey);
const feeRecipient = new PublicKey('FEE_RECIPIENT_ADDRESS');
const data = Buffer.alloc(8 + 8 + 8);
const discriminator = Buffer.from([0x33, 0xe6, 0x85, 0xa4, 0x01, 0x7f, 0x83, 0xad]);
discriminator.copy(data, 0);
data.writeBigUInt64LE(amount, 8);
data.writeBigUInt64LE(minSolOutput, 16);
const instruction = new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
{ pubkey: global, isSigner: false, isWritable: false },
{ pubkey: feeRecipient, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: bondingCurve, isSigner: false, isWritable: true },
{ pubkey: associatedBondingCurve, isSigner: false, isWritable: true },
{ pubkey: userAta, isSigner: false, isWritable: true },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
{ pubkey: creatorVault, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
// Fee config accounts
{ pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: feeConfigPDA, isSigner: false, isWritable: false },
],
data,
});
const tx = new Transaction().add(instruction);
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
const signature = await connection.sendRawTransaction(tx.serialize());
return signature;
}
Calculate Price (Bonding Curve Math)
// Uniswap V2 constant product formula: x * y = k
// Quote: SOL -> Tokens
interface BondingCurveState {
virtualTokenReserves: bigint;
virtualSolReserves: bigint;
realTokenReserves: bigint;
realSolReserves: bigint;
}
function calculateBuyQuote(
state: BondingCurveState,
solAmountIn: bigint,
feeBasisPoints: bigint = 100n
): bigint {
// Calculate net SOL after fees
const netSol = (solAmountIn * 10000n) / (10000n + feeBasisPoints);
// Calculate tokens out using constant product formula
// tokensOut = (netSol * virtualTokenReserves) / (virtualSolReserves + netSol)
const tokensOut = (netSol * state.virtualTokenReserves) /
(state.virtualSolReserves + netSol);
// Cap at real token reserves
return tokensOut > state.realTokenReserves ? state.realTokenReserves : tokensOut;
}
function calculateSellQuote(
state: BondingCurveState,
tokenAmountIn: bigint,
feeBasisPoints: bigint = 100n
): bigint {
// Calculate SOL out using constant product formula
// solOut = (tokenAmountIn * virtualSolReserves) / (virtualTokenReserves + tokenAmountIn)
const grossSol = (tokenAmountIn * state.virtualSolReserves) /
(state.virtualTokenReserves + tokenAmountIn);
// Deduct fees
const netSol = (grossSol * (10000n - feeBasisPoints)) / 10000n;
// Cap at real SOL reserves
return netSol > state.realSolReserves ? state.realSolReserves : netSol;
}
function calculateMarketCap(state: BondingCurveState, tokenSupply: bigint): bigint {
// marketCap = virtualSolReserves * mintSupply / virtualTokenReserves
return (state.virtualSolReserves * tokenSupply) / state.virtualTokenReserves;
}
Migration to PumpSwap
When a bonding curve completes (real tokens exhausted), liquidity can be migrated to PumpSwap:
async function migrateToAMM(
connection: Connection,
payer: Keypair,
mint: PublicKey
): Promise<string> {
const [bondingCurve] = getBondingCurvePDA(mint);
// Migrate instruction is permissionless - anyone can call it
// after bonding curve is complete
const discriminator = Buffer.from([0x9d, 0xaf, 0x1e, 0x65, 0xe8, 0x69, 0x9b, 0x26]);
const instruction = new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
// ... migration accounts including PumpSwap pool creation
],
data: discriminator,
});
const tx = new Transaction().add(instruction);
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
return await connection.sendRawTransaction(tx.serialize());
}
PumpSwap (AMM)
PumpSwap is a constant-product AMM for tokens that have graduated from the bonding curve.
Global Config
// GlobalConfig address
const GLOBAL_CONFIG = new PublicKey('ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw');
interface GlobalConfig {
admin: PublicKey;
lpFeeBasisPoints: number; // 20 bps (0.2%)
protocolFeeBasisPoints: number; // 5 bps (0.05%)
coinCreatorFeeBasisPoints: number; // Creator fee
disableFlags: number; // Operation disable flags
protocolFeeRecipients: PublicKey[]; // 8 fee recipients for load balancing
tokenIncentivesEnabled: boolean;
}
Pool Account
interface Pool {
bump: number;
poolCreator: PublicKey;
baseMint: PublicKey; // Token mint
quoteMint: PublicKey; // Usually WSOL
lpMint: PublicKey; // LP token mint
poolBaseTokenAccount: PublicKey;
poolQuoteTokenAccount: PublicKey;
lpSupply: bigint; // Tracks original LP supply (independent of burns)
coinCreator: PublicKey; // Creator for fee distribution
isMayhemMode: boolean; // Mayhem mode flag
}
Deriving Pool PDAs
const PUMP_AMM_PROGRAM_ID = new PublicKey('pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA');
// Derive pool PDA
function getPoolPDA(
index: number,
creator: PublicKey,
baseMint: PublicKey,
quoteMint: PublicKey
): [PublicKey, number] {
const indexBuffer = Buffer.alloc(2);
indexBuffer.writeUInt16LE(index);
return PublicKey.findProgramAddressSync(
[
Buffer.from('pool'),
indexBuffer,
creator.toBuffer(),
baseMint.toBuffer(),
quoteMint.toBuffer(),
],
PUMP_AMM_PROGRAM_ID
);
}
// Derive LP mint PDA
function getLpMintPDA(pool: PublicKey): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('pool_lp_mint'), pool.toBuffer()],
PUMP_AMM_PROGRAM_ID
);
}
// Derive pool token account PDA
function getPoolTokenAccountPDA(pool: PublicKey, mint: PublicKey): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('pool_token_account'), pool.toBuffer(), mint.toBuffer()],
PUMP_AMM_PROGRAM_ID
);
}
// Derive creator vault authority PDA
function getCreatorVaultAuthorityPDA(coinCreator: PublicKey): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from('creator_vault'), coinCreator.toBuffer()],
PUMP_AMM_PROGRAM_ID
);
}
Swap on PumpSwap
interface SwapArgs {
baseAmountIn: bigint; // Amount of base token (for sell)
quoteAmountIn: bigint; // Amount of quote token (for buy)
minAmountOut: bigint; // Minimum output (slippage protection)
}
async function swapOnPumpSwap(
connection: Connection,
payer: Keypair,
pool: PublicKey,
amountIn: bigint,
minAmountOut: bigint,
isBuy: boolean
): Promise<string> {
const poolData = await fetchPoolData(connection, pool);
const userBaseAta = getAssociatedTokenAddressSync(
poolData.baseMint,
payer.publicKey
);
const userQuoteAta = getAssociatedTokenAddressSync(
poolData.quoteMint,
payer.publicKey
);
const [creatorVaultAuthority] = getCreatorVaultAuthorityPDA(poolData.coinCreator);
const creatorVaultAta = getAssociatedTokenAddressSync(
poolData.quoteMint,
creatorVaultAuthority,
true // allowOwnerOffCurve
);
// Build swap instruction
const data = Buffer.alloc(8 + 8 + 8 + 8);
const discriminator = isBuy
? Buffer.from([0x66, 0x06, 0x3d, 0x12, 0x01, 0xda, 0xeb, 0xea]) // buy
: Buffer.from([0x33, 0xe6, 0x85, 0xa4, 0x01, 0x7f, 0x83, 0xad]); // sell
discriminator.copy(data, 0);
data.writeBigUInt64LE(isBuy ? 0n : amountIn, 8); // baseAmountIn
data.writeBigUInt64LE(isBuy ? amountIn : 0n, 16); // quoteAmountIn
data.writeBigUInt64LE(minAmountOut, 24); // minAmountOut
const instruction = new TransactionInstruction({
programId: PUMP_AMM_PROGRAM_ID,
keys: [
{ pubkey: pool, isSigner: false, isWritable: true },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
{ pubkey: GLOBAL_CONFIG, isSigner: false, isWritable: false },
{ pubkey: poolData.baseMint, isSigner: false, isWritable: false },
{ pubkey: poolData.quoteMint, isSigner: false, isWritable: false },
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
{ pubkey: poolData.poolBaseTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolData.poolQuoteTokenAccount, isSigner: false, isWritable: true },
{ pubkey: protocolFeeRecipient, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
// Creator fee accounts (indexes 17-18, required if pool.dataLen < 300)
{ pubkey: creatorVaultAta, isSigner: false, isWritable: true },
{ pubkey: creatorVaultAuthority, isSigner: false, isWritable: false },
// Fee config
{ pubkey: PUMP_FEES_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: feeConfigPDA, isSigner: false, isWritable: false },
],
data,
});
// Check if account extension needed
const poolInfo = await connection.getAccountInfo(pool);
if (poolInfo && poolInfo.data.length < 300) {
const extendIx = createExtendAccountInstruction(pool);
const tx = new Transaction().add(extendIx, instruction);
} else {
const tx = new Transaction().add(instruction);
}
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
return await connection.sendRawTransaction(tx.serialize());
}
Add Liquidity
async function addLiquidity(
connection: Connection,
payer: Keypair,
pool: PublicKey,
baseAmount: bigint,
quoteAmount: bigint,
minLpTokens: bigint
): Promise<string> {
const poolData = await fetchPoolData(connection, pool);
const userBaseAta = getAssociatedTokenAddressSync(poolData.baseMint, payer.publicKey);
const userQuoteAta = getAssociatedTokenAddressSync(poolData.quoteMint, payer.publicKey);
const userLpAta = getAssociatedTokenAddressSync(poolData.lpMint, payer.publicKey);
const data = Buffer.alloc(8 + 8 + 8 + 8);
const discriminator = Buffer.from([0xf2, 0x23, 0xc6, 0x89, 0x52, 0xe1, 0xf2, 0xb6]);
discriminator.copy(data, 0);
data.writeBigUInt64LE(baseAmount, 8);
data.writeBigUInt64LE(quoteAmount, 16);
data.writeBigUInt64LE(minLpTokens, 24);
const instruction = new TransactionInstruction({
programId: PUMP_AMM_PROGRAM_ID,
keys: [
{ pubkey: pool, isSigner: false, isWritable: true },
{ pubkey: GLOBAL_CONFIG, isSigner: false, isWritable: false },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
{ pubkey: poolData.baseMint, isSigner: false, isWritable: false },
{ pubkey: poolData.quoteMint, isSigner: false, isWritable: false },
{ pubkey: poolData.lpMint, isSigner: false, isWritable: true },
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
{ pubkey: userLpAta, isSigner: false, isWritable: true },
{ pubkey: poolData.poolBaseTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolData.poolQuoteTokenAccount, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
],
data,
});
const tx = new Transaction().add(instruction);
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
return await connection.sendRawTransaction(tx.serialize());
}
Remove Liquidity
async function removeLiquidity(
connection: Connection,
payer: Keypair,
pool: PublicKey,
lpTokenAmount: bigint,
minBaseOut: bigint,
minQuoteOut: bigint
): Promise<string> {
const poolData = await fetchPoolData(connection, pool);
const userBaseAta = getAssociatedTokenAddressSync(poolData.baseMint, payer.publicKey);
const userQuoteAta = getAssociatedTokenAddressSync(poolData.quoteMint, payer.publicKey);
const userLpAta = getAssociatedTokenAddressSync(poolData.lpMint, payer.publicKey);
const data = Buffer.alloc(8 + 8 + 8 + 8);
const discriminator = Buffer.from([0xb7, 0x12, 0x46, 0x9c, 0x94, 0x6d, 0xa1, 0x22]);
discriminator.copy(data, 0);
data.writeBigUInt64LE(lpTokenAmount, 8);
data.writeBigUInt64LE(minBaseOut, 16);
data.writeBigUInt64LE(minQuoteOut, 24);
const instruction = new TransactionInstruction({
programId: PUMP_AMM_PROGRAM_ID,
keys: [
{ pubkey: pool, isSigner: false, isWritable: true },
{ pubkey: GLOBAL_CONFIG, isSigner: false, isWritable: false },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
{ pubkey: poolData.baseMint, isSigner: false, isWritable: false },
{ pubkey: poolData.quoteMint, isSigner: false, isWritable: false },
{ pubkey: poolData.lpMint, isSigner: false, isWritable: true },
{ pubkey: userBaseAta, isSigner: false, isWritable: true },
{ pubkey: userQuoteAta, isSigner: false, isWritable: true },
{ pubkey: userLpAta, isSigner: false, isWritable: true },
{ pubkey: poolData.poolBaseTokenAccount, isSigner: false, isWritable: true },
{ pubkey: poolData.poolQuoteTokenAccount, isSigner: false, isWritable: true },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
],
data,
});
const tx = new Transaction().add(instruction);
tx.feePayer = payer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(payer);
return await connection.sendRawTransaction(tx.serialize());
}
Fee Structure
Dynamic Fee Tiers
Fees are calculated based on market capitalization in lamports:
interface FeeTier {
marketCapLamportsThreshold: bigint;
fees: Fees;
}
interface Fees {
lpFeeBps: number; // LP provider fee
protocolFeeBps: number; // Protocol fee
creatorFeeBps: number; // Creator fee
}
// Fee calculation for bonding curves
function calculateBondingCurveMarketCap(
virtualSolReserves: bigint,
mintSupply: bigint,
virtualTokenReserves: bigint
): bigint {
return (virtualSolReserves * mintSupply) / virtualTokenReserves;
}
// Fee calculation for AMM pools
function calculatePoolMarketCap(
quoteReserve: bigint,
baseMintSupply: bigint,
baseReserve: bigint
): bigint {
return (quoteReserve * baseMintSupply) / baseReserve;
}
// Get applicable fee tier
function getFeeTier(marketCap: bigint, feeTiers: FeeTier[]): Fees {
// Sort tiers by threshold descending
const sortedTiers = [...feeTiers].sort(
(a, b) => Number(b.marketCapLamportsThreshold - a.marketCapLamportsThreshold)
);
for (const tier of sortedTiers) {
if (marketCap >= tier.marketCapLamportsThreshold) {
return tier.fees;
}
}
// Return default/lowest tier
return sortedTiers[sortedTiers.length - 1].fees;
}
Fee Sharing Configuration
interface SharingConfig {
status: 'Active' | 'Paused';
mint: PublicKey;
admin: PublicKey;
adminRevoked: boolean;
shareholders: Shareholder[];
}
interface Shareholder {
address: PublicKey;
shareBps: number; // Share in basis points (total must equal 10000)
}
// Create fee sharing config
async function createFeeSharingConfig(
connection: Connection,
payer: Keypair,
mint: PublicKey,
shareholders: Shareholder[]
): Promise<string> {
// Validate shares sum to 10000 bps (100%)
const totalShares = shareholders.reduce((sum, s) => sum + s.shareBps, 0);
if (totalShares !== 10000) {
throw new Error('Shareholder shares must sum to 10000 bps');
}
// Build create_fee_sharing_config instruction
// ...
}
Collecting Creator Fees
// Collect fees from bonding curve
async function collectBondingCurveCreatorFee(
connection: Connection,
creator: Keypair
): Promise<string> {
const [creatorVault] = PublicKey.findProgramAddressSync(
[Buffer.from('creator-vault'), creator.publicKey.toBuffer()],
PUMP_PROGRAM_ID
);
const discriminator = Buffer.from([0x85, 0xb1, 0x29, 0x6d, 0x3a, 0x47, 0x2c, 0x5e]);
const instruction = new TransactionInstruction({
programId: PUMP_PROGRAM_ID,
keys: [
{ pubkey: creatorVault, isSigner: false, isWritable: true },
{ pubkey: creator.publicKey, isSigner: true, isWritable: true },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
],
data: discriminator,
});
const tx = new Transaction().add(instruction);
tx.feePayer = creator.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(creator);
return await connection.sendRawTransaction(tx.serialize());
}
// Collect fees from PumpSwap pool
async function collectPoolCreatorFee(
connection: Connection,
coinCreator: Keypair,
pool: PublicKey
): Promise<string> {
const poolData = await fetchPoolData(connection, pool);
const [creatorVaultAuthority] = getCreatorVaultAuthorityPDA(coinCreator.publicKey);
const creatorVaultAta = getAssociatedTokenAddressSync(
poolData.quoteMint,
creatorVaultAuthority,
true
);
const creatorWalletAta = getAssociatedTokenAddressSync(
poolData.quoteMint,
coinCreator.publicKey
);
// Build collect_coin_creator_fee instruction
// ...
}
SDK Usage
Using PumpAmmSdk
import { PumpAmmSdk } from '@pump-fun/pump-swap-sdk';
const sdk = new PumpAmmSdk(connection);
// Pool creation with autocomplete
const initialPrice = sdk.createAutocompleteInitialPoolPrice(
baseAmount,
quoteAmount
);
const createIxs = await sdk.createPoolInstructions({
index: 0,
creator: payer.publicKey,
baseMint: tokenMint,
quoteMint: WSOL_MINT,
baseAmountIn: baseAmount,
quoteAmountIn: quoteAmount,
});
// Deposit autocomplete (base input changed)
const depositCalc = sdk.depositAutocompleteBaseInput({
pool: poolAddress,
baseAmountIn: userBaseInput,
slippageBps: 50, // 0.5%
});
console.log('Quote needed:', depositCalc.quoteAmountIn);
console.log('LP tokens:', depositCalc.lpTokensOut);
// Deposit autocomplete (quote input changed)
const depositCalc2 = sdk.depositAutocompleteQuoteInput({
pool: poolAddress,
quoteAmountIn: userQuoteInput,
slippageBps: 50,
});
// Execute deposit
const depositIxs = await sdk.depositInstructions({
pool: poolAddress,
user: payer.publicKey,
lpTokenAmountOut: depositCalc.lpTokensOut,
maxBaseAmountIn: depositCalc.baseAmountIn,
maxQuoteAmountIn: depositCalc.quoteAmountIn,
});
// Swap autocomplete
const swapCalc = sdk.swapAutocompleteBaseInput({
pool: poolAddress,
baseAmountIn: sellAmount,
slippageBps: 100, // 1%
});
console.log('Quote out:', swapCalc.quoteAmountOut);
// Execute swap
const swapIxs = await sdk.swapInstructions({
pool: poolAddress,
user: payer.publicKey,
baseAmountIn: sellAmount,
quoteAmountIn: 0n,
minAmountOut: swapCalc.minQuoteAmountOut,
});
// Withdraw
const withdrawCalc = sdk.withdrawAutocomplete({
pool: poolAddress,
lpTokenAmountIn: lpTokens,
slippageBps: 50,
});
console.log('Base out:', withdrawCalc.baseAmountOut);
console.log('Quote out:', withdrawCalc.quoteAmountOut);
const withdrawIxs = await sdk.withdrawInstructions({
pool: poolAddress,
user: payer.publicKey,
lpTokenAmountIn: lpTokens,
minBaseAmountOut: withdrawCalc.minBaseAmountOut,
minQuoteAmountOut: withdrawCalc.minQuoteAmountOut,
});
Account Extension
Both Pump and PumpSwap programs require account extension when account sizes have been increased:
// Check and extend bonding curve account (size < 150 bytes)
async function ensureBondingCurveExtended(
connection: Connection,
bondingCurve: PublicKey
): Promise<TransactionInstruction | null> {
const accountInfo = await connection.getAccountInfo(bondingCurve);
if (accountInfo && accountInfo.data.length < 150) {
return createExtendAccountInstruction(PUMP_PROGRAM_ID, bondingCurve);
}
return null;
}
// Check and extend pool account (size < 300 bytes)
async function ensurePoolExtended(
connection: Connection,
pool: PublicKey
): Promise<TransactionInstruction | null> {
const accountInfo = await connection.getAccountInfo(pool);
if (accountInfo && accountInfo.data.length < 300) {
return createExtendAccountInstruction(PUMP_AMM_PROGRAM_ID, pool);
}
return null;
}
function createExtendAccountInstruction(
programId: PublicKey,
account: PublicKey
): TransactionInstruction {
const discriminator = Buffer.from([0x9a, 0x3f, 0x2c, 0x8b, 0x45, 0xe7, 0x12, 0xd6]);
return new TransactionInstruction({
programId,
keys: [
{ pubkey: account, isSigner: false, isWritable: true },
],
data: discriminator,
});
}
Mayhem Mode
Mayhem mode is a special token launch mode with different fee recipients:
const MAYHEM_PROGRAM_ID = new PublicKey('MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e');
// Mayhem fee recipients (randomly rotated for load balancing)
const MAYHEM_FEE_RECIPIENTS = [
'DzPPWKfYYMuHxR98xhPkYSp7KHsLpcLZU8tHCqXjC3HG',
'5AbGBKS6NHKcTFyJaZCk3dbMRNFG3y6kkN4y7Rp3iHCk',
'DWM9EuZ3e9cRYdHYRKwsCqXFKgn4jUNkUPEAyNE2Dxnc',
'9BUPJ65gFVaGQKyXiR6xSE1DdLRqkUMv9HJvL8wGfJaL',
];
// Check if token is in mayhem mode
function isMayhemMode(bondingCurve: BondingCurve): boolean {
return bondingCurve.isMayhemMode;
}
// Get correct fee recipient based on mayhem mode
function getFeeRecipient(
bondingCurve: BondingCurve,
defaultFeeRecipient: PublicKey
): PublicKey {
if (bondingCurve.isMayhemMode) {
// Randomly select from mayhem recipients
const index = Math.floor(Math.random() * MAYHEM_FEE_RECIPIENTS.length);
return new PublicKey(MAYHEM_FEE_RECIPIENTS[index]);
}
return defaultFeeRecipient;
}
Token Incentives
PumpFun offers volume-based token incentives:
// Initialize user volume accumulator
async function initUserVolumeAccumulator(
connection: Connection,
payer: Keypair
): Promise<string> {
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
[Buffer.from('user_volume'), payer.publicKey.toBuffer()],
PUMP_PROGRAM_ID
);
// Build init_user_volume_accumulator instruction
// ...
}
// Claim token incentives
async function claimTokenIncentives(
connection: Connection,
user: Keypair
): Promise<string> {
const [userVolumeAccumulator] = PublicKey.findProgramAddressSync(
[Buffer.from('user_volume'), user.publicKey.toBuffer()],
PUMP_PROGRAM_ID
);
// Build claim_token_incentives instruction
// ...
}
Compute Units Optimization
// Recommended static CU limit for buy/sell operations
const RECOMMENDED_CU_LIMIT = 100_000;
// Add compute budget instructions
import { ComputeBudgetProgram } from '@solana/web3.js';
function addComputeBudget(
tx: Transaction,
units: number = 100_000,
microLamports: number = 10_000
): Transaction {
tx.add(
ComputeBudgetProgram.setComputeUnitLimit({ units }),
ComputeBudgetProgram.setComputeUnitPrice({ microLamports })
);
return tx;
}
Error Handling
// Common PumpFun errors
enum PumpError {
SlippageExceeded = 6001,
InsufficientFunds = 6002,
BondingCurveComplete = 6003,
BondingCurveNotComplete = 6004,
InvalidAmount = 6005,
MathOverflow = 6006,
Unauthorized = 6007,
}
function handlePumpError(error: any): string {
const code = error?.code || error?.message?.match(/custom program error: 0x(\w+)/)?.[1];
switch (parseInt(code, 16)) {
case PumpError.SlippageExceeded:
return 'Transaction failed: Slippage tolerance exceeded. Try increasing slippage.';
case PumpError.InsufficientFunds:
return 'Insufficient funds for this transaction.';
case PumpError.BondingCurveComplete:
return 'Bonding curve is complete. Trade on PumpSwap instead.';
case PumpError.BondingCurveNotComplete:
return 'Bonding curve not yet complete. Cannot migrate.';
default:
return `Transaction failed: ${error.message}`;
}
}
Best Practices
Transaction Building
- Always check account sizes before buy/sell and prepend
extendAccountif needed - Use static CU limit of 100,000 for buy/sell operations
- Include proper slippage protection (1-5% recommended)
- Use appropriate fee recipients based on mayhem mode status
Fee Handling
- Always include fee config accounts (required since September 2025)
- For mayhem mode tokens, pass mayhem fee recipient at correct account index
- Check market cap tier for accurate fee calculation
Creator Fees
- Creator fees accumulate in creator vault PDAs
- Use
collectCreatorFeeto withdraw accumulated fees - Fees apply to non-completed bonding curves and canonical PumpSwap pools
Security
- Never expose private keys
- Validate all user inputs
- Use devnet for testing before mainnet
- Implement proper error handling
Resources
- Public Docs: https://github.com/pump-fun/pump-public-docs
- IDL Files: https://github.com/pump-fun/pump-public-docs/tree/main/idl
- Pump SDK:
@pump-fun/pump-sdkon npm - PumpSwap SDK:
@pump-fun/pump-swap-sdkon npm
Skill Structure
pumpfun/
├── SKILL.md # This file
├── resources/
│ ├── pump-program-reference.md # Bonding curve program API
│ ├── pump-swap-reference.md # AMM program API
│ ├── fee-structure.md # Fee calculation and tiers
│ └── program-addresses.md # All program addresses
├── examples/
│ ├── bonding-curve/
│ │ ├── create-token.ts # Token creation example
│ │ └── buy-sell.ts # Buy/sell on bonding curves
│ ├── swap/
│ │ └── swap.ts # PumpSwap trading
│ ├── liquidity/
│ │ └── add-remove.ts # Add/remove liquidity
│ └── creator-fees/
│ └── collect-fees.ts # Fee collection
├── templates/
│ └── pumpfun-setup.ts # Starter template
└── docs/
└── troubleshooting.md # Common issues
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.