@pump-fun/pump-swap-sdk
v1.20.0
Published
Official SDK for interacting with Pump Swap AMM protocol on Solana
Readme
Pump Swap SDK
TypeScript SDK for the Pump Swap AMM program (pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA).
PumpAmmSdk(singletonPUMP_AMM_SDK) builds instructions and decodes accounts fully offline from a "Solana state" object.OnlinePumpAmmSdkfetches those state objects over RPC (swapSolanaState,liquiditySolanaState,createPoolSolanaState,collectCoinCreatorFeeSolanaState) and reads balances.PumpAmmAdminSdkbuilds the admin-gated instructions.- Pure pricing functions (
buyBaseInput,buyQuoteInput,sellBaseInput,sellQuoteInput) and fee helpers (computeFeesBps,feesForQuoteMint,poolMarketCap, ...) are exported from the package root for callers that keep their own state.
Installation
npm install @pump-fun/pump-swap-sdkUsage
import { Connection } from "@solana/web3.js";
import { OnlinePumpAmmSdk, PUMP_AMM_SDK } from "@pump-fun/pump-swap-sdk";
const connection = new Connection(rpcUrl);
const onlineSdk = new OnlinePumpAmmSdk(connection);All amounts are BNs in the raw base units of the mint they refer to (lamports for wSOL,
micro-units for USDC, ...). slippage is a percentage: 1 means 1%.
Create pool
const createPoolSolanaState = await onlineSdk.createPoolSolanaState(
index,
creator,
baseMint,
quoteMint,
);
const createPoolInstructions = await PUMP_AMM_SDK.createPoolInstructions(
createPoolSolanaState,
baseIn,
quoteIn,
);
// Initial pool price for the UI
const initialPoolPrice = await PUMP_AMM_SDK.createAutocompleteInitialPoolPrice(
initialBase,
initialQuote,
);Deposit
const liquiditySolanaState = await onlineSdk.liquiditySolanaState(
poolKey,
user,
);
// When the base input changes
const { quote, lpToken } =
PUMP_AMM_SDK.depositAutocompleteQuoteAndLpTokenFromBase(
liquiditySolanaState,
base,
slippage,
);
// When the quote input changes
const { base, lpToken } =
PUMP_AMM_SDK.depositAutocompleteBaseAndLpTokenFromQuote(
liquiditySolanaState,
quote,
slippage,
);
const depositInstructions = await PUMP_AMM_SDK.depositInstructions(
liquiditySolanaState,
lpToken,
slippage,
);Swap
swapSolanaState fetches the pool, its reserves, the global and fee configs, the two token
programs (read from the mint owners, so Token-2022 quotes work) and the user's token accounts.
The four PUMP_AMM_SDK swap builders price the trade with the pool's quote mint and mayhem
flag, apply slippage, wrap and unwrap wSOL when the quote is legacy WSOL, and create the user's
missing base ATA (buy) or quote ATA (sell); a buy on a non-wSOL quote expects the user's quote
token account to exist.
const swapSolanaState = await onlineSdk.swapSolanaState(poolKey, user);
// Buy `base` tokens, paying at most quote + slippage
const buyInstructions = await PUMP_AMM_SDK.buyBaseInput(
swapSolanaState,
base,
slippage,
);
// Spend `quote` tokens, receiving base - slippage
const buyInstructions2 = await PUMP_AMM_SDK.buyQuoteInput(
swapSolanaState,
quote,
slippage,
);
// Sell `base` tokens, receiving at least quote - slippage
const sellInstructions = await PUMP_AMM_SDK.sellBaseInput(
swapSolanaState,
base,
slippage,
);
// Receive `quote` tokens, selling at most base + slippage
const sellInstructions2 = await PUMP_AMM_SDK.sellQuoteInput(
swapSolanaState,
quote,
slippage,
);To show a quote before building, or to build with explicit limits, use the pure functions and
buyInstructions / sellInstructions:
import { buyBaseInput, sellBaseInput } from "@pump-fun/pump-swap-sdk";
const {
pool,
poolBaseAmount,
poolQuoteAmount,
globalConfig,
feeConfig,
baseMint,
baseMintAccount,
} = swapSolanaState;
const poolArgs = {
baseReserve: poolBaseAmount,
quoteReserve: poolQuoteAmount,
virtualQuoteReserves: pool.virtualQuoteReserves,
globalConfig,
feeConfig,
baseMint,
baseMintAccount,
coinCreator: pool.coinCreator,
creator: pool.creator,
quoteMint: pool.quoteMint, // selects the fee schedule, see below
isMayhemMode: pool.isMayhemMode, // selects the market-cap basis
creatorFeeBps: pool.creatorFeeBps, // per-pool creator fee, see "Configurable creator fee"
};
const { uiQuote, maxQuote } = buyBaseInput({ ...poolArgs, base, slippage });
const buyInstructions = await PUMP_AMM_SDK.buyInstructions(
swapSolanaState,
base,
maxQuote,
);
const { uiQuote: quoteOut, minQuote } = sellBaseInput({
...poolArgs,
base,
slippage,
});
const sellInstructions = await PUMP_AMM_SDK.sellInstructions(
swapSolanaState,
base,
minQuote,
);buyQuoteInput returns { base, maxQuote } and sellQuoteInput returns { base, minQuote }
for the quote-driven directions.
Withdraw
const liquiditySolanaState = await onlineSdk.liquiditySolanaState(
poolKey,
user,
);
const { base, quote } =
PUMP_AMM_SDK.withdrawAutoCompleteBaseAndQuoteFromLpToken(
liquiditySolanaState,
lpAmount,
slippage,
);
const withdrawInstructions = await PUMP_AMM_SDK.withdrawInstructions(
liquiditySolanaState,
lpToken,
slippage,
);Creator fees
A coin creator's AMM fees accumulate in a vault ATA per quote mint
(coinCreatorVaultAtaPda(coinCreatorVaultAuthorityPda(coinCreator), quoteMint, quoteTokenProgram)).
collectCoinCreatorFeeSolanaState and getCoinCreatorVaultBalance default the quote mint to
legacy WSOL, so existing SOL-only callers are unchanged; getCoinCreatorVaultBalances and
transferCreatorFeesToPumpV2Instruction take it explicitly (offline, together with the quote
token program). The online methods accept a bonding curve's zero key as WSOL (like
canonicalPumpPoolPda) and, when the token program is not passed, read it from the mint
account's owner, which must be SPL Token or Token-2022.
import {
NATIVE_MINT,
TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { USDC_MINT } from "@pump-fun/pump-swap-sdk";
// Collect: SOL-quoted coins (unchanged call) ...
const solState = await onlineSdk.collectCoinCreatorFeeSolanaState(coinCreator);
// ... or a USDC / quote-control / Token-2022-quoted coin
const usdcState = await onlineSdk.collectCoinCreatorFeeSolanaState(
coinCreator,
undefined, // destination token account; defaults to the creator's ATA
USDC_MINT,
);
const collectInstructions = await PUMP_AMM_SDK.collectCoinCreatorFee(
usdcState,
payer, // optional; defaults to the creator
);
// Balances
const solFees = await onlineSdk.getCoinCreatorVaultBalance(coinCreator);
const usdcFees = await onlineSdk.getCoinCreatorVaultBalance(
coinCreator,
USDC_MINT,
);
const allFees = await onlineSdk.getCoinCreatorVaultBalances(coinCreator, [
{ mint: NATIVE_MINT, tokenProgram: TOKEN_PROGRAM_ID },
{ mint: USDC_MINT, tokenProgram: TOKEN_PROGRAM_ID },
{ mint: xStockMint, tokenProgram: TOKEN_2022_PROGRAM_ID },
]); // Map<base58 quote mint, BN>
// Fee-sharing coins: move the AMM vault into the pump program's creator vault
const transferInstruction =
await onlineSdk.transferCreatorFeesToPumpV2Instruction(
payer,
coinCreator, // the coin's sharing config PDA once fee sharing is enabled
quoteMint,
);
// Offline: PUMP_AMM_SDK.transferCreatorFeesToPumpV2Instruction({ payer, coinCreator, quoteMint, quoteTokenProgram })collectCoinCreatorFee transfers between two token accounts the program does not create: the
builder creates the vault ATA and the creator's destination ATA (rent paid by payer) when they
are missing, for any quote mint and either token program. A custom destination
(coinCreatorTokenAccount, any token account the creator owns) is accepted but must already
exist. A wSOL payout is unwrapped by closing the creator's wSOL ATA, only when the creator is
the payer.
transferCreatorFeesToPumpV2 unwraps a wSOL vault into the pump creator vault PDA and moves
any other quote into that PDA's quote ATA, which the program creates when missing (rent paid by
payer). Its account list carries pump_creator_vault_ata even for wSOL; quoteTokenProgram
must be the quote mint's owner program.
Which quote mints a creator's vaults may hold is decided by the pump program (its Global
whitelist and QuoteControl list, read with @pump-fun/pump-sdk); this SDK keeps no static
list of quote mints.
Canonical pump pools
Coins that graduate from a pump bonding curve trade in a canonical pool keyed by the base mint and the quote mint:
import {
canonicalPumpPoolPda,
canonicalPoolQuoteMint,
} from "@pump-fun/pump-swap-sdk";
canonicalPumpPoolPda(mint); // SOL coin (quote = legacy WSOL)
canonicalPumpPoolPda(mint, USDC_MINT); // USDC coin
canonicalPumpPoolPda(mint, bondingCurve.quoteMint); // the curve's zero key means WSOLcanonicalPoolQuoteMint maps a bonding curve's quote_mint to the pool's quote mint (the zero
key SOL curves store becomes legacy WSOL). PumpAmmAdminSdk.adminSetCoinCreator(mint,
newCoinCreator, quoteMint = NATIVE_MINT) targets the pool of the given quote.
A canonical pool without a coin creator gets one through the permissionless set_coin_creator
(from the coin's Metaplex metadata, else its bonding curve):
// Reads the pool (any length), derives metadata / bonding_curve from its base mint and
// prepends extend_account (rent paid by payer) when the pool is not grown yet
const setCoinCreator = await onlineSdk.setCoinCreatorInstructions(
poolKey,
payer,
);
// Offline: PUMP_AMM_SDK.setCoinCreator(poolKey, baseMint)Quote mints and fees
pump-fees selects a trade's fee schedule from the pool and its quote mint
(FeeConfig::fees_for_quote_mint); feesForQuoteMint mirrors it and the pure pricing
functions apply it when given quoteMint:
| Pool | Quote mint | Schedule |
| ------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| not a canonical pump pool | any | flatFees |
| canonical pump pool | SOL-like (SOL_LIKE_QUOTE_MINTS: the zero key, legacy WSOL So111…112, Token-2022 native 9pan9…) | feeTiers by market cap |
| canonical pump pool | listed stable (STABLE_QUOTE_MINTS: USDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) | stableFeeTiers by market cap (feeTiers when the FeeConfig predates stable tiers) |
| canonical pump pool | anything else (quote-control mints, xStock Token-2022 quotes, devnet USDC) | exoticFlatFees, or flatFees while the exotic schedule is unset (all zero) |
Market cap is poolMarketCap({ baseMintSupply, baseReserve, quoteReserve, isMayhemMode }) =
quoteReserve * circulatingSupply / baseReserve, with circulatingSupply the live mint supply,
or the fixed PUMP_AMM_TOTAL_TOKEN_SUPPLY (1e15) for mayhem pools. It is denominated in the
quote's base units, and so is FeeTier.marketCapLamportsThreshold of the matching tier set:
lamports for feeTiers, USDC micro-units for stableFeeTiers. The stable set is hardcoded here
because it is hardcoded on-chain; changing it is a program upgrade.
Things to know when a pool is not quoted in SOL:
- Devnet USDC (
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU) is whitelisted by the devnet pump program but is not a pump-fees stable, so a devnet USDC pool pays exotic/flat fees once the quote-control fee program is deployed there. The SDK mirrors the program; it does not special-case devnet. - Amounts are raw base units; the SDK does no decimal scaling. xStock Token-2022 quotes carry
the
ScaledUiAmountextension (a UI-only multiplier that leaves raw amounts and on-chain math untouched) andPausable(every trade fails while the issuer has the mint paused). - Token-2022 quote token accounts carry extensions and are 170–182 bytes, not 165. Decoding the
fixed 165-byte prefix for
amountis fine; asserting the length or passing the SPL Token program id togetAccountis not.getCoinCreatorVaultBalanceusesunpackAccount. - Trades on Token-2022-quoted pools use more compute than SPL-quoted ones. Set an explicit compute budget of roughly 250–300k CU for them.
- The program validates the buyback fee recipient's quote ATA (the recipient's associated token account for the quote mint under the quote token program) but does not create it; the protocol fee recipient's ATA and the creator vault ATA are created on demand with the trader paying rent. Operators must provision the buyback recipients' ATAs for every new quote mint before the first trade, or every swap on that pool fails.
FeeConfighas three layouts.FEE_CONFIG_SIZE_PRE_STABLE(2512),FEE_CONFIG_SIZE_POST_STABLE(4073) andFEE_CONFIG_SIZE_POST_EXOTIC(4097) are the account lengths of each;PUMP_AMM_SDK.decodeFeeConfigreads only the fields the account's length carries, so older accounts decode withstableFeeTiers = []andexoticFlatFeesall zero.
Migration notes
- Direct callers of
buyBaseInput,buyQuoteInput,sellBaseInput,sellQuoteInputandcomputeFeesBpsshould passquoteMint: pool.quoteMint,isMayhemMode: pool.isMayhemMode,virtualQuoteReserves: pool.virtualQuoteReservesandcreatorFeeBps: pool.creatorFeeBps. OmittingquoteMintprices with the SOL schedule (byte-identical to earlier versions), which is wrong for USDC and other non-SOL pools; omittingcreatorFeeBpsprices with the schedule's creator rate, which is wrong for a pool with a configured creator fee. ThePUMP_AMM_SDKbuilders pass them for you. canonicalPumpPoolPda(mint, PublicKey.default)now derives the WSOL pool (it used to derive a pool that does not exist).collectCoinCreatorFeeSolanaState,getCoinCreatorVaultBalanceandPumpAmmAdminSdk.adminSetCoinCreatorgained trailing quote-mint parameters that default to WSOL.collectCoinCreatorFeenow creates missing vault and destination ATAs for non-SOL quotes too (it used to create them for wSOL only).
Configurable creator fee
A canonical pump pool can carry its own creator fee rate, Pool.creatorFeeBps, in place of the
creator rate of the pump-fees schedule its trades would otherwise pay (the bonding curve the
coin graduated from has the same field on the pump side). The rule, mirrored by computeFeesBps
from pump-amm compute_fees:
GlobalConfig.creatorFeeConfigurableis a program-wide gate. While it is off, stored per-pool rates are neither accepted by the setters nor read by trades.Pool.creatorFeeBps == 0means "not configured": the schedule's creator rate applies. Any nonzero value replaces it. LP and protocol rates are never touched, and the has-coin-creator rule still zeroes the creator fee of a pool without one.- Cashback coins are excluded: the setters refuse a rate on a
Pool.isCashbackCoinpool. - Editing is one-shot per CTO flip:
admin_set_coin_creator_fee_editable(signed byGlobalConfig.adminSetCoinCreatorAuthority) setsPool.canEditCreatorFee; oneset_coin_creator_fee_bpsby the coin creator then writes a rate in1..=GlobalConfig.maxConfigurableCreatorFeeBpsand clears the flag again. - When the coin creator is the coin's pump-fees
SharingConfig(feeSharingConfigPda(baseMint), fee-sharing coins), the config's admin signs instead and the config is passed as remaining account 0 (it must be Active with the admin not revoked). - A new canonical pool receives the bonding curve's values through
create_pool's trailingcreator_fee_bps/can_edit_creator_feearguments, passed by pump'smigrate_v2CPI (a canonical pool's creator is pump's pool-authority PDA, which signs only there).PUMP_AMM_SDK.createPoolInstructionscreates permissionless pools, so it always encodes the two arguments as 0 / false and accepts{ creatorFeeBps, canEditCreatorFee }only to keep the encoding aligned with the IDL: a nonzero / true value built through it fails on-chain withOnlyCanonicalPumpPoolsCanHaveCoinCreator.
import { PumpAmmAdminSdk } from "@pump-fun/pump-swap-sdk";
// Admin: turn the feature on (also grows the GlobalConfig account after the upgrade)
const adminSdk = new PumpAmmAdminSdk(connection);
const enable = await adminSdk.updateCreatorFeeConfig(true, new BN(500)); // max 5%
// CTO authority: let the creator of `poolKey` set the fee once
const [editable] =
await onlineSdk.adminSetCoinCreatorFeeEditableInstructions(poolKey);
// Creator (or the SharingConfig admin): set 2.5%; rejects gate off / cashback / out of range
const [setFee] = await onlineSdk.setCoinCreatorFeeBpsInstructions(
creator,
poolKey,
new BN(250),
);
// Offline equivalents
PUMP_AMM_SDK.setCoinCreatorFeeBpsInstruction({
coinCreator,
pool,
creatorFeeBps,
sharingConfig, // only when the coin creator is the SharingConfig PDA
});
PUMP_AMM_SDK.adminSetCoinCreatorFeeEditableInstruction({
adminSetCoinCreatorAuthority,
pool,
});The PUMP_AMM_SDK swap builders price a configured pool correctly. Direct callers of the pure
pricing functions and computeFeesBps pass creatorFeeBps: pool.creatorFeeBps; omitting it
prices with the schedule, exactly as before.
Deploy-order caveat: accounts written before the upgrade are shorter (Pool 261 instead of
POOL_SIZE = 270 bytes, GlobalConfig 940 instead of GLOBAL_CONFIG_SIZE = 949) and read as
"not configured" (creatorFeeBps 0, canEditCreatorFee false, gate off). They keep trading, but
program instructions that serialize the whole struct fail on them until they are grown:
update_creator_fee_config and admin_set_coin_creator_fee_editable grow their own account (the
signer pays the rent), and the PUMP_AMM_SDK swap and liquidity builders already prepend the
permissionless extend_account when a pool is shorter than POOL_ACCOUNT_NEW_SIZE. decodePool,
decodeGlobalConfig and PumpAmmAdminSdk.fetchGlobalConfigAccount read every historical length.
Migration notes (creator fee)
PoolgainedcreatorFeeBps: BNandcanEditCreatorFee: boolean;GlobalConfiggainedcreatorFeeConfigurable: booleanandmaxConfigurableCreatorFeeBps: BN. All four are required (the decoders always populate them), so hand-built literals need them.- Every
create_poolinstruction is 9 bytes longer: the two trailing arguments are always encoded, as 0 / false when unset. The deployed program ignores trailing instruction bytes; the upgraded one reads them. PumpAmmAdminSdk.fetchGlobalConfigAccountreturns the SDKGlobalConfigtype (same field names as before) and reads pre-upgrade accounts; Anchor's rawfetchthrows on them with this IDL.CreatePoolEventlogs emitted before the two trailing fields existed no longer decode with the vendored IDL (the trailingboolis read past the end of the log). Append 9 zero bytes before decoding such historical logs; they then readcreatorFeeBps0 andcanEditCreatorFeefalse.- With this IDL, Anchor's account resolver decodes a fetched
Poolwith the 270-byte layout, so agetPumpAmmProgram(connection).methods.*.accountsPartial({ pool })call that leaves a pool-seeded account to Anchor (set_coin_creator'smetadata/bonding_curve,migrate_pool_coin_creator'spool/sharing_config, a swap'scoin_creator_vault_authority) fails on a pre-upgrade 261-byte pool with "Reached maximum depth for account resolution". Pass those accounts explicitly, or decode the pool withdecodePool; everyPUMP_AMM_SDKbuilder does. PumpAmmSdk.setCoinCreator(pool, baseMint?)gainedbaseMint, from which it derivesmetadataandbonding_curve(metadataPda,bondingCurvePda); without it the call still needs a connection, which the offline program does not have.OnlinePumpAmmSdk.setCoinCreatorInstructions(poolKey, payer)reads the pool (any length) and prependsextend_accountwhen it is not grown.claim_cashback.user_wsol_token_accountlost its ATA constraint in the IDL (any token account of the quote mint owned by the user);accountsPartialcallers must now pass it explicitly. No builder in this SDK usesclaimCashback.
License
MIT
