How the Pump protocol dynamically adjusts fees based on a token's market cap.
The fee system uses market-cap-based tiers to determine protocol and creator fees. As a token's market cap grows, different fee rates may apply. The SDK handles this automatically when you use getBuyTokenAmountFromSolAmount, getSellSolAmountFromTokenAmount, and related functions.
Each trade incurs two types of fees:
| Fee Type | Recipient | Description |
|---|---|---|
| Protocol fee | Pump protocol | Goes to the protocol treasury |
| Creator fee | Token creator | Goes to the creator's vault (or fee sharing config) |
Both are expressed in basis points (bps), where 1 bps = 0.01%.
Note: LP fees (
lpFeeBps) are also defined in the fee config but only apply after graduation to the AMM pool.
The SDK selects fees in this order:
-
If
FeeConfigis available (fetched viasdk.fetchFeeConfig()):- Compute the token's current market cap from its bonding curve reserves
- Walk through
feeTiers(highest threshold first) to find the matching tier - If market cap is below the first tier's threshold, use the first tier's fees
-
If
FeeConfigis null (fallback):- Use
global.feeBasisPointsfor protocol fees - Use
global.creatorFeeBasisPointsfor creator fees
- Use
// Internal logic — you don't need to call this directly
import { calculateFeeTier, computeFeesBps } from "@nirholas/pump-sdk";
// computeFeesBps selects the right tier automatically
const { protocolFeeBps, creatorFeeBps } = computeFeesBps({
global,
feeConfig,
mintSupply,
virtualSolReserves,
virtualTokenReserves,
});Fetched from on-chain state:
interface FeeConfig {
admin: PublicKey; // Admin authority
flatFees: Fees; // Default flat fee rates
feeTiers: FeeTier[]; // Market-cap-dependent tiers
}
interface FeeTier {
marketCapLamportsThreshold: BN; // Market cap threshold in lamports
fees: Fees; // Fee rates for this tier
}
interface Fees {
lpFeeBps: BN; // LP fee (AMM only, post-graduation)
protocolFeeBps: BN; // Protocol fee in basis points
creatorFeeBps: BN; // Creator fee in basis points
}const feeConfig = await sdk.fetchFeeConfig();The FeeConfig is stored at the PUMP_FEE_CONFIG_PDA address and can be decoded offline:
const accountInfo = await connection.getAccountInfo(PUMP_FEE_CONFIG_PDA);
const feeConfig = PUMP_SDK.decodeFeeConfig(accountInfo);The calculateFeeTier function processes tiers as follows:
function calculateFeeTier({ feeTiers, marketCap }): Fees {
const firstTier = feeTiers[0];
// If below the first tier's threshold, use the first tier
if (marketCap < firstTier.marketCapLamportsThreshold) {
return firstTier.fees;
}
// Walk tiers from highest to lowest, find the first one where
// marketCap >= threshold
for (const tier of feeTiers.reverse()) {
if (marketCap >= tier.marketCapLamportsThreshold) {
return tier.fees;
}
}
// Fallback to first tier
return firstTier.fees;
}In practice, feeTiers is sorted by ascending marketCapLamportsThreshold. The algorithm finds the highest tier whose threshold the token has exceeded.
The market cap used for tier selection is computed from bonding curve reserves:
import { bondingCurveMarketCap } from "@nirholas/pump-sdk";
const mcap = bondingCurveMarketCap({
mintSupply: bondingCurve.tokenTotalSupply,
virtualSolReserves: bondingCurve.virtualSolReserves,
virtualTokenReserves: bondingCurve.virtualTokenReserves,
});Fees are applied differently for buys and sells:
Fees are deducted from the input SOL before calculating how many tokens the user receives:
Input SOL → deduct fees → remaining SOL buys tokens from the curve
Fees are deducted from the output SOL after calculating how much SOL the tokens are worth:
Tokens sold into the curve → SOL amount → deduct fees → user receives remainder
Creator fees are only charged when a creator address is set on the bonding curve:
const creatorFee = isNewBondingCurve || !PublicKey.default.equals(bondingCurve.creator)
? fee(amount, creatorFeeBps)
: new BN(0);- New tokens (created in the same transaction): always charge creator fee
- Tokens with a creator set (
creator !== PublicKey.default): charge creator fee - Tokens with no creator: no creator fee
Protocol fees are sent to a randomly selected fee recipient from the global config:
import { getStaticRandomFeeRecipient } from "@nirholas/pump-sdk";
// Selects from a hardcoded list of protocol fee recipients
const recipient = getStaticRandomFeeRecipient();In Mayhem mode, fees are routed to reservedFeeRecipient / reservedFeeRecipients instead.
You never need to call fee functions directly. Pass feeConfig to the math functions and they handle everything:
const tokenAmount = getBuyTokenAmountFromSolAmount({
global,
feeConfig, // ← tier selection happens here
mintSupply: bondingCurve.tokenTotalSupply,
bondingCurve,
amount: solAmount,
});If you pass feeConfig: null, the SDK falls back to the global flat fee rates.
- Bonding Curve Math — Price calculation formulas
- Fee Sharing — Splitting creator fees among shareholders
- Mayhem Mode — Alternate fee routing
- API Reference — Full function signatures