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 (NOT pool.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:

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: Fee Tiers

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.