FEE_PROGRAM_README.md
August 29, 2025 ยท View on GitHub
Hello. We pushed again the update to both Pump and PumpSwap programs which adds the 2 new additional accounts on buy / sell.
On Monday, September 1, 20:00 UTC, these 2 accounts will become mandatory and the programs fee structure will change from the existing one to a dynamic fee structure depending on the current market cap of the coin in lamports. This new fee structure applies only to:
- Pump bonding curves
- PumpSwap canonical pools, where a canonical pool is defined as a pool whose
pool.creator(NOTpool.coinCreator) is defined as:
export function isPumpPool(
baseMint: PublicKey,
poolCreator: PublicKey,
): boolean {
return pumpPoolAuthorityPda(baseMint)[0].equals(poolCreator);
}
The new fee structure code is present in both our Typescript SDKs:
- https://www.npmjs.com/package/@pump-fun/pump-sdk?activeTab=code
- https://www.npmjs.com/package/@pump-fun/pump-swap-sdk?activeTab=code
The market cap in lamports for bonding curve is computed as follows:
export function bondingCurveMarketCap({
mintSupply,
virtualSolReserves,
virtualTokenReserves,
}: {
mintSupply: BN;
virtualSolReserves: BN;
virtualTokenReserves: BN;
}): BN {
if (virtualTokenReserves.isZero()) {
throw new Error("Division by zero: virtual token reserves cannot be zero");
}
return virtualSolReserves.mul(mintSupply).div(virtualTokenReserves);
}
The market in lamports for a PumpSwap canonical pool is:
export function poolMarketCap({
baseMintSupply,
baseReserve,
quoteReserve,
}: {
baseMintSupply: BN;
baseReserve: BN;
quoteReserve: BN;
}): BN {
if (baseReserve.isZero()) {
throw new Error(
"Division by zero: pool base token reserves cannot be zero",
);
}
return quoteReserve.mul(baseMintSupply).div(baseReserve);
}
For bonding curve program, the fee bps for protocol and creator fees will be computed using the following logic:
export function computeFeesBps({
global,
feeConfig,
mintSupply,
virtualSolReserves,
virtualTokenReserves,
}: {
global: Global;
feeConfig: FeeConfig | null;
mintSupply: BN;
virtualSolReserves: BN;
virtualTokenReserves: BN;
}): CalculatedFeesBps {
if (feeConfig != null) {
const marketCap = bondingCurveMarketCap({
mintSupply,
virtualSolReserves,
virtualTokenReserves,
});
return calculateFeeTier({
feeTiers: feeConfig.feeTiers,
marketCap,
});
}
return {
protocolFeeBps: global.feeBasisPoints,
creatorFeeBps: global.creatorFeeBasisPoints,
};
}
/// rust reference: pump-fees-math::calculate_fee_tier()
export function calculateFeeTier({
feeTiers,
marketCap,
}: {
feeTiers: FeeTier[];
marketCap: BN;
}): Fees {
const firstTier = feeTiers[0];
if (marketCap.lt(firstTier.marketCapLamportsThreshold)) {
return firstTier.fees;
}
for (const tier of feeTiers.slice().reverse()) {
if (marketCap.gte(tier.marketCapLamportsThreshold)) {
return tier.fees;
}
}
return firstTier.fees;
}
A similar logic will be used for PumpSwap canonical pools too:
export function computeFeesBps({
globalConfig,
feeConfig,
creator,
baseMintSupply,
baseMint,
baseReserve,
quoteReserve,
tradeSize,
}: {
globalConfig: GlobalConfig;
feeConfig: FeeConfig | null;
creator: PublicKey;
baseMintSupply: BN;
baseMint: PublicKey;
baseReserve: BN;
quoteReserve: BN;
tradeSize: BN;
}): Fees {
if (feeConfig != null) {
const marketCap = poolMarketCap({
baseMintSupply,
baseReserve,
quoteReserve,
});
return getFees({
feeConfig,
isPumpPool: isPumpPool(baseMint, creator),
marketCap,
tradeSize,
});
}
return {
lpFeeBps: globalConfig.lpFeeBasisPoints,
protocolFeeBps: globalConfig.protocolFeeBasisPoints,
creatorFeeBps: globalConfig.coinCreatorFeeBasisPoints,
};
}
/// rust reference: pump-fees::get_fees()
function getFees({
feeConfig,
isPumpPool,
marketCap,
}: {
feeConfig: FeeConfig;
isPumpPool: boolean;
marketCap: BN;
tradeSize: BN;
}): Fees {
if (isPumpPool) {
return calculateFeeTier({
feeTiers: feeConfig.feeTiers,
marketCap,
});
} else {
return feeConfig.flatFees;
}
}
/// rust reference: pump-fees-math::calculate_fee_tier()
export function calculateFeeTier({
feeTiers,
marketCap,
}: {
feeTiers: FeeTier[];
marketCap: BN;
}): Fees {
const firstTier = feeTiers[0];
if (marketCap.lt(firstTier.marketCapLamportsThreshold)) {
return firstTier.fees;
}
for (const tier of feeTiers.slice().reverse()) {
if (marketCap.gte(tier.marketCapLamportsThreshold)) {
return tier.fees;
}
}
return firstTier.fees;
}
We will use the following fee tiers starting from Monday:

In order to avoid possible issues created by the new fee structure, until you make sure it's implemented correctly, you can increase the slippage tolerance on buy / sell instructions as a temporary mitigation.
If you implement the fee logic correctly, any future change to the fee tiers structure above should not affect your code.