How to Fix PumpFun Custom:6024 Overflow Error After the Cashback Upgrade

A complete guide to the February 2026 PumpFun bonding curve program upgrade that broke buy/sell transactions. Covers the new bonding_curve_v2 PDA requirement, the cashback_enabled flag, and correct account layouts.

  • March 1, 2026
  • MEV
  • AllenHark Team
A quiet stack of pagesAn abstract drawing of light on the page's dark ground. A neat stack of thin, solid pages, the top one lit, with faint streams of light drifting toward it from the left.A quiet stack of pagesAn abstract drawing of light on the page's dark ground. A neat stack of thin, solid pages, the top one lit, with faint streams of light drifting toward it from the left.

AnchorError thrown in programs/pump/src/lib.rs:764. Error Code: Overflow. Error Number: 6024. Error Message: Overflow. reference.::

A complete guide to the February 2026 PumpFun bonding curve program upgrade that broke buy/sell transactions. Covers the new bonding_curve_v2 PDA requirement, the cashback_enabled flag, and correct account layouts for both cashback and non-cashback tokens — with working code in Rust, TypeScript, and Python.


Table of Contents

  1. The Problem
  2. Root Cause
  3. Key Discovery: The cashback_enabled Flag
  4. Account Layouts After the Upgrade
  5. Detecting Cashback Status On-Chain
  6. The bonding_curve_v2 PDA
  7. Complete Working Examples
  8. PumpSwap AMM (pool_v2)
  9. Common Mistakes
  10. Summary

The Problem

Starting late February 2026, PumpFun bonding curve buy and sell transactions began failing with:

Text
InstructionError(N, Custom(6024))
AnchorError thrown in programs/pump/src/buy.rs:181. Error Code: Overflow.

or on sell:

Text
InstructionError(N, Custom(6024))
AnchorError thrown in programs/pump/src/lib.rs:764. Error Code: Overflow.

This affected all tokens on the bonding curve program (6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P), regardless of trade size. Even 0.001 SOL buys would fail. The error is misleading — it says "Overflow" but the actual cause is missing accounts introduced by a silent program upgrade.


Root Cause

PumpFun deployed a cashback program upgrade that introduced a new required account: the bonding_curve_v2 PDA. Without this account appended to the instruction's account list, the on-chain program reads data from the wrong account indices, causing u64 arithmetic to operate on garbage data — hence the "Overflow" error.

The upgrade also introduced a distinction between cashback-enabled and non-cashback tokens, which affects the sell instruction's account layout.

Key facts:

  • The bonding_curve_v2 PDA must be appended as the last remaining account for all buy and sell instructions — cashback and non-cashback alike.
  • The sell instruction additionally requires a user_volume_accumulator account only for cashback-enabled tokens.
  • Being a Token-2022 token does NOT mean cashback is enabled. The authoritative check is byte[82] in the bonding curve's on-chain account data.

Key Discovery: The cashback_enabled Flag

This is the critical insight that isn't documented anywhere in the official PumpFun docs:

The bonding curve account data contains a cashback_enabled boolean flag at byte offset 82.

The bonding curve account layout (151 bytes after the cashback upgrade):

OffsetSizeFieldType
08Discriminator[u8; 8]
88virtual_token_reservesu64
168virtual_sol_reservesu64
248real_token_reservesu64
328real_sol_reservesu64
408token_total_supplyu64
481completebool
4932creatorPubkey
811(reserved)u8
821cashback_enabledbool
83–15068(extended fields)—

This flag determines the sell account layout:

  • byte[82] == 0 → Non-cashback → 15 accounts on sell
  • byte[82] == 1 → Cashback enabled → 16 accounts on sell (includes user_volume_accumulator)

Warning: Do NOT use the token's program ID (Token-2022 vs spl-token) as a proxy for cashback status. Many Token-2022 tokens have cashback_enabled = false. Using Token-2022 status will cause sells to fail with overflow on non-cashback Token-2022 tokens.


Account Layouts After the Upgrade

Buy (buy_exact_sol_in) — 17 accounts (same for all tokens)

IndexAccountMutableSigner
0global (PDA: ["global"])NoNo
1fee_recipient (from GlobalConfig)YesNo
2mintNoNo
3bonding_curve (PDA: ["bonding-curve", mint])YesNo
4associated_bonding_curve (ATA of bonding_curve for mint)YesNo
5associated_user (user's ATA for mint)YesNo
6user (payer)YesYes
7system_programNoNo
8token_program (spl-token or Token-2022)NoNo
9creator_vault (PDA: ["creator-vault", creator])YesNo
10event_authority (PDA: ["__event_authority"])NoNo
11program (6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P)NoNo
12global_volume_accumulator (PDA: ["global_volume_accumulator"])NoNo
13user_volume_accumulator (PDA: ["user_volume_accumulator", user])YesNo
14fee_config (PDA on fee program)NoNo
15fee_program (pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ)NoNo
16bonding_curve_v2 (PDA: ["bonding-curve-v2", mint])NoNo

Sell — Non-cashback tokens (15 accounts)

IndexAccountMutableSigner
0globalNoNo
1fee_recipientYesNo
2mintNoNo
3bonding_curveYesNo
4associated_bonding_curveYesNo
5associated_userYesNo
6userYesYes
7system_programNoNo
8creator_vaultYesNo
9token_programNoNo
10event_authorityNoNo
11programNoNo
12fee_configNoNo
13fee_programNoNo
14bonding_curve_v2NoNo

Sell — Cashback-enabled tokens (16 accounts)

IndexAccountMutableSigner
0–13(same as non-cashback)——
14user_volume_accumulator (PDA: ["user_volume_accumulator", user])YesNo
15bonding_curve_v2NoNo

bonding_curve_v2 must always be the last account.


Detecting Cashback Status On-Chain

Reading byte[82] from the bonding curve account

The bonding curve PDA is derived as:

Text
seeds = ["bonding-curve", mint_pubkey]
program = 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P

After fetching the account data, check byte 82:

Text
cashback_enabled = data.length > 82 && data[82] != 0

Fee Config PDA

Both buy and sell use the same fee_config PDA. The seed key for the bonding curve program's fee config is:

Text
key = [1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
       81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176]

seeds = ["fee_config", key]
program = pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ

Warning: The PumpSwap AMM program uses a different fee config key for sell. Do not mix them up. The bonding curve program uses the same fee config for both buy and sell.


The bonding_curve_v2 PDA

Derived from:

Text
seeds = ["bonding-curve-v2", mint_pubkey]
program = 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P

This PDA does not need to exist on-chain (it can be an uninitialized account). The program just needs it in the accounts list to properly index the remaining accounts. Pass it as read-only.


Complete Working Examples

Rust

Rust
use solana_sdk::instruction::{AccountMeta, Instruction};
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;

const PUMP_PROGRAM: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
const FEE_PROGRAM: &str = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ";

/// Detect cashback status from bonding curve on-chain data.
/// byte[82] == 1 means cashback is enabled for this token.
fn is_cashback_enabled(bonding_curve_data: &[u8]) -> bool {
    bonding_curve_data.len() > 82 && bonding_curve_data[82] != 0
}

/// Derive the bonding_curve_v2 PDA (required for ALL tokens after Feb 2026 upgrade).
fn derive_bonding_curve_v2(mint: &Pubkey) -> Pubkey {
    let program = Pubkey::from_str(PUMP_PROGRAM).unwrap();
    let (pda, _) = Pubkey::find_program_address(
        &[b"bonding-curve-v2", mint.as_ref()],
        &program,
    );
    pda
}

/// Derive the fee_config PDA (same for buy AND sell on the bonding curve program).
fn derive_fee_config() -> Pubkey {
    let fee_program = Pubkey::from_str(FEE_PROGRAM).unwrap();
    let key: [u8; 32] = [
        1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
        81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176,
    ];
    let (pda, _) = Pubkey::find_program_address(&[b"fee_config", &key], &fee_program);
    pda
}

/// Build a buy_exact_sol_in instruction (17 accounts for all tokens).
fn build_buy_ix(
    mint: &Pubkey,
    payer: &Pubkey,
    sol_amount: u64,
    min_tokens_out: u64,
    fee_recipient: &Pubkey,
    creator: &Pubkey,
    token_program: &Pubkey,
) -> Instruction {
    let program_id = Pubkey::from_str(PUMP_PROGRAM).unwrap();

    let (global, _) = Pubkey::find_program_address(&[b"global"], &program_id);
    let (bonding_curve, _) = Pubkey::find_program_address(
        &[b"bonding-curve", mint.as_ref()], &program_id,
    );
    let associated_bonding_curve = derive_ata(bonding_curve, mint, token_program);
    let user_ata = derive_ata(*payer, mint, token_program);
    let (creator_vault, _) = Pubkey::find_program_address(
        &[b"creator-vault", creator.as_ref()], &program_id,
    );
    let (event_authority, _) = Pubkey::find_program_address(
        &[b"__event_authority"], &program_id,
    );
    let (global_vol, _) = Pubkey::find_program_address(
        &[b"global_volume_accumulator"], &program_id,
    );
    let (user_vol, _) = Pubkey::find_program_address(
        &[b"user_volume_accumulator", payer.as_ref()], &program_id,
    );
    let fee_config = derive_fee_config();
    let fee_program = Pubkey::from_str(FEE_PROGRAM).unwrap();
    let bonding_curve_v2 = derive_bonding_curve_v2(mint);

    // buy_exact_sol_in discriminator
    let disc: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
    let mut data = Vec::with_capacity(24);
    data.extend_from_slice(&disc);
    data.extend_from_slice(&sol_amount.to_le_bytes());
    data.extend_from_slice(&min_tokens_out.to_le_bytes());

    let accounts = vec![
        AccountMeta::new_readonly(global, false),                         // 0
        AccountMeta::new(*fee_recipient, false),                          // 1
        AccountMeta::new_readonly(*mint, false),                          // 2
        AccountMeta::new(bonding_curve, false),                           // 3
        AccountMeta::new(associated_bonding_curve, false),                // 4
        AccountMeta::new(user_ata, false),                                // 5
        AccountMeta::new(*payer, true),                                   // 6
        AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // 7
        AccountMeta::new_readonly(*token_program, false),                 // 8
        AccountMeta::new(creator_vault, false),                           // 9
        AccountMeta::new_readonly(event_authority, false),                // 10
        AccountMeta::new_readonly(program_id, false),                     // 11
        AccountMeta::new_readonly(global_vol, false),                     // 12
        AccountMeta::new(user_vol, false),                                // 13
        AccountMeta::new_readonly(fee_config, false),                     // 14
        AccountMeta::new_readonly(fee_program, false),                    // 15
        AccountMeta::new_readonly(bonding_curve_v2, false),               // 16 ← NEW
    ];

    Instruction { program_id, accounts, data }
}

/// Build a sell instruction.
/// Non-cashback: 15 accounts. Cashback-enabled: 16 accounts.
fn build_sell_ix(
    mint: &Pubkey,
    payer: &Pubkey,
    token_amount: u64,
    min_sol_out: u64,
    fee_recipient: &Pubkey,
    creator: &Pubkey,
    token_program: &Pubkey,
    cashback_enabled: bool, // ← Read from bonding curve byte[82]
) -> Instruction {
    let program_id = Pubkey::from_str(PUMP_PROGRAM).unwrap();

    let (global, _) = Pubkey::find_program_address(&[b"global"], &program_id);
    let (bonding_curve, _) = Pubkey::find_program_address(
        &[b"bonding-curve", mint.as_ref()], &program_id,
    );
    let associated_bonding_curve = derive_ata(bonding_curve, mint, token_program);
    let user_ata = derive_ata(*payer, mint, token_program);
    let (creator_vault, _) = Pubkey::find_program_address(
        &[b"creator-vault", creator.as_ref()], &program_id,
    );
    let (event_authority, _) = Pubkey::find_program_address(
        &[b"__event_authority"], &program_id,
    );
    let fee_config = derive_fee_config();
    let fee_program = Pubkey::from_str(FEE_PROGRAM).unwrap();
    let bonding_curve_v2 = derive_bonding_curve_v2(mint);

    // sell discriminator
    let disc: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
    let mut data = Vec::with_capacity(24);
    data.extend_from_slice(&disc);
    data.extend_from_slice(&token_amount.to_le_bytes());
    data.extend_from_slice(&min_sol_out.to_le_bytes());

    let mut accounts = vec![
        AccountMeta::new_readonly(global, false),                         // 0
        AccountMeta::new(*fee_recipient, false),                          // 1
        AccountMeta::new_readonly(*mint, false),                          // 2
        AccountMeta::new(bonding_curve, false),                           // 3
        AccountMeta::new(associated_bonding_curve, false),                // 4
        AccountMeta::new(user_ata, false),                                // 5
        AccountMeta::new(*payer, true),                                   // 6
        AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // 7
        AccountMeta::new(creator_vault, false),                           // 8
        AccountMeta::new_readonly(*token_program, false),                 // 9
        AccountMeta::new_readonly(event_authority, false),                // 10
        AccountMeta::new_readonly(program_id, false),                     // 11
        AccountMeta::new_readonly(fee_config, false),                     // 12
        AccountMeta::new_readonly(fee_program, false),                    // 13
    ];

    // Cashback-enabled tokens need user_volume_accumulator BEFORE bonding_curve_v2
    if cashback_enabled {
        let (user_vol, _) = Pubkey::find_program_address(
            &[b"user_volume_accumulator", payer.as_ref()], &program_id,
        );
        accounts.push(AccountMeta::new(user_vol, false));                 // 14
    }

    // bonding_curve_v2 is ALWAYS the last account
    accounts.push(AccountMeta::new_readonly(bonding_curve_v2, false));    // 14 or 15

    Instruction { program_id, accounts, data }
}

/// Derive ATA with a specific token program.
fn derive_ata(owner: Pubkey, mint: &Pubkey, token_program: &Pubkey) -> Pubkey {
    let (ata, _) = Pubkey::find_program_address(
        &[owner.as_ref(), token_program.as_ref(), mint.as_ref()],
        &spl_associated_token_account::ID,
    );
    ata
}

TypeScript

TypeScript
import {
  Connection,
  PublicKey,
  TransactionInstruction,
  AccountMeta,
  SystemProgram,
} from "@solana/web3.js";

const PUMP_PROGRAM = new PublicKey("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
const FEE_PROGRAM = new PublicKey("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
const ATA_PROGRAM = new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");

// Same key for buy AND sell fee_config on the bonding curve program
const FEE_CONFIG_KEY = Buffer.from([
  1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
  81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176,
]);

function deriveAta(owner: PublicKey, mint: PublicKey, tokenProgram: PublicKey): PublicKey {
  const [ata] = PublicKey.findProgramAddressSync(
    [owner.toBuffer(), tokenProgram.toBuffer(), mint.toBuffer()],
    ATA_PROGRAM
  );
  return ata;
}

function deriveBondingCurve(mint: PublicKey): PublicKey {
  const [pda] = PublicKey.findProgramAddressSync(
    [Buffer.from("bonding-curve"), mint.toBuffer()],
    PUMP_PROGRAM
  );
  return pda;
}

function deriveBondingCurveV2(mint: PublicKey): PublicKey {
  const [pda] = PublicKey.findProgramAddressSync(
    [Buffer.from("bonding-curve-v2"), mint.toBuffer()],
    PUMP_PROGRAM
  );
  return pda;
}

function deriveFeeConfig(): PublicKey {
  const [pda] = PublicKey.findProgramAddressSync(
    [Buffer.from("fee_config"), FEE_CONFIG_KEY],
    FEE_PROGRAM
  );
  return pda;
}

/**
 * Detect cashback status from bonding curve account data.
 * byte[82] == 1 means cashback is enabled.
 *
 * WARNING: Do NOT use the token program (Token-2022 vs spl-token) as a proxy.
 * Many Token-2022 tokens have cashback_enabled = false.
 */
function isCashbackEnabled(bondingCurveData: Buffer): boolean {
  return bondingCurveData.length > 82 && bondingCurveData[82] !== 0;
}

/**
 * Read the creator pubkey from bonding curve data (offset 49, 32 bytes).
 */
function readCreator(bondingCurveData: Buffer): PublicKey {
  return new PublicKey(bondingCurveData.subarray(49, 81));
}

/**
 * Build a buy_exact_sol_in instruction (17 accounts for ALL tokens).
 */
function buildBuyInstruction(
  mint: PublicKey,
  payer: PublicKey,
  solAmount: bigint,
  minTokensOut: bigint,
  feeRecipient: PublicKey,
  creator: PublicKey,
  tokenProgram: PublicKey
): TransactionInstruction {
  const bondingCurve = deriveBondingCurve(mint);
  const [global] = PublicKey.findProgramAddressSync([Buffer.from("global")], PUMP_PROGRAM);
  const [creatorVault] = PublicKey.findProgramAddressSync(
    [Buffer.from("creator-vault"), creator.toBuffer()],
    PUMP_PROGRAM
  );
  const [eventAuth] = PublicKey.findProgramAddressSync(
    [Buffer.from("__event_authority")],
    PUMP_PROGRAM
  );
  const [globalVol] = PublicKey.findProgramAddressSync(
    [Buffer.from("global_volume_accumulator")],
    PUMP_PROGRAM
  );
  const [userVol] = PublicKey.findProgramAddressSync(
    [Buffer.from("user_volume_accumulator"), payer.toBuffer()],
    PUMP_PROGRAM
  );

  // buy_exact_sol_in discriminator: sha256("global:buy_exact_sol_in")[..8]
  const disc = Buffer.from([56, 252, 116, 8, 158, 223, 205, 95]);
  const data = Buffer.alloc(24);
  disc.copy(data, 0);
  data.writeBigUInt64LE(solAmount, 8);
  data.writeBigUInt64LE(minTokensOut, 16);

  const keys: AccountMeta[] = [
    { pubkey: global, isSigner: false, isWritable: false },                       // 0
    { pubkey: feeRecipient, isSigner: false, isWritable: true },                  // 1
    { pubkey: mint, isSigner: false, isWritable: false },                         // 2
    { pubkey: bondingCurve, isSigner: false, isWritable: true },                  // 3
    { pubkey: deriveAta(bondingCurve, mint, tokenProgram), isSigner: false, isWritable: true },  // 4
    { pubkey: deriveAta(payer, mint, tokenProgram), isSigner: false, isWritable: true },         // 5
    { pubkey: payer, isSigner: true, isWritable: true },                          // 6
    { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },      // 7
    { pubkey: tokenProgram, isSigner: false, isWritable: false },                 // 8
    { pubkey: creatorVault, isSigner: false, isWritable: true },                  // 9
    { pubkey: eventAuth, isSigner: false, isWritable: false },                    // 10
    { pubkey: PUMP_PROGRAM, isSigner: false, isWritable: false },                 // 11
    { pubkey: globalVol, isSigner: false, isWritable: false },                    // 12
    { pubkey: userVol, isSigner: false, isWritable: true },                       // 13
    { pubkey: deriveFeeConfig(), isSigner: false, isWritable: false },            // 14
    { pubkey: FEE_PROGRAM, isSigner: false, isWritable: false },                  // 15
    { pubkey: deriveBondingCurveV2(mint), isSigner: false, isWritable: false },   // 16 ← NEW
  ];

  return new TransactionInstruction({ programId: PUMP_PROGRAM, keys, data });
}

/**
 * Build a sell instruction.
 * Non-cashback: 15 accounts. Cashback: 16 accounts.
 */
function buildSellInstruction(
  mint: PublicKey,
  payer: PublicKey,
  tokenAmount: bigint,
  minSolOut: bigint,
  feeRecipient: PublicKey,
  creator: PublicKey,
  tokenProgram: PublicKey,
  cashbackEnabled: boolean // ← from bonding curve byte[82]
): TransactionInstruction {
  const bondingCurve = deriveBondingCurve(mint);
  const [global] = PublicKey.findProgramAddressSync([Buffer.from("global")], PUMP_PROGRAM);
  const [creatorVault] = PublicKey.findProgramAddressSync(
    [Buffer.from("creator-vault"), creator.toBuffer()],
    PUMP_PROGRAM
  );
  const [eventAuth] = PublicKey.findProgramAddressSync(
    [Buffer.from("__event_authority")],
    PUMP_PROGRAM
  );

  // sell discriminator
  const disc = Buffer.from([51, 230, 133, 164, 1, 127, 131, 173]);
  const data = Buffer.alloc(24);
  disc.copy(data, 0);
  data.writeBigUInt64LE(tokenAmount, 8);
  data.writeBigUInt64LE(minSolOut, 16);

  const keys: AccountMeta[] = [
    { pubkey: global, isSigner: false, isWritable: false },                       // 0
    { pubkey: feeRecipient, isSigner: false, isWritable: true },                  // 1
    { pubkey: mint, isSigner: false, isWritable: false },                         // 2
    { pubkey: bondingCurve, isSigner: false, isWritable: true },                  // 3
    { pubkey: deriveAta(bondingCurve, mint, tokenProgram), isSigner: false, isWritable: true },  // 4
    { pubkey: deriveAta(payer, mint, tokenProgram), isSigner: false, isWritable: true },         // 5
    { pubkey: payer, isSigner: true, isWritable: true },                          // 6
    { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },      // 7
    { pubkey: creatorVault, isSigner: false, isWritable: true },                  // 8
    { pubkey: tokenProgram, isSigner: false, isWritable: false },                 // 9
    { pubkey: eventAuth, isSigner: false, isWritable: false },                    // 10
    { pubkey: PUMP_PROGRAM, isSigner: false, isWritable: false },                 // 11
    { pubkey: deriveFeeConfig(), isSigner: false, isWritable: false },            // 12
    { pubkey: FEE_PROGRAM, isSigner: false, isWritable: false },                  // 13
  ];

  // Cashback tokens need user_volume_accumulator BEFORE bonding_curve_v2
  if (cashbackEnabled) {
    const [userVol] = PublicKey.findProgramAddressSync(
      [Buffer.from("user_volume_accumulator"), payer.toBuffer()],
      PUMP_PROGRAM
    );
    keys.push({ pubkey: userVol, isSigner: false, isWritable: true });            // 14
  }

  // bonding_curve_v2 is ALWAYS the last account
  keys.push({
    pubkey: deriveBondingCurveV2(mint),
    isSigner: false,
    isWritable: false,
  }); // 14 or 15

  return new TransactionInstruction({ programId: PUMP_PROGRAM, keys, data });
}

// ----- Usage Example -----

async function example() {
  const connection = new Connection("https://api.mainnet-beta.solana.com");
  const mint = new PublicKey("HhEdFsuJ88cFsUFT5LyqDCEdsbYzWiHs2MBMucJtpump");

  // 1. Fetch bonding curve data
  const bondingCurve = deriveBondingCurve(mint);
  const accountInfo = await connection.getAccountInfo(bondingCurve);
  if (!accountInfo) throw new Error("Bonding curve not found");

  // 2. Read cashback flag from byte[82]
  const cashbackEnabled = isCashbackEnabled(accountInfo.data as Buffer);
  console.log(`Cashback enabled: ${cashbackEnabled}`);

  // 3. Read creator from bonding curve data
  const creator = readCreator(accountInfo.data as Buffer);

  // 4. Detect token program
  const mintInfo = await connection.getAccountInfo(mint);
  const tokenProgram = mintInfo!.owner;

  // 5. Build instructions using cashbackEnabled flag
  // ... buildBuyInstruction(...) or buildSellInstruction(..., cashbackEnabled)
}

Python

Python
from solders.pubkey import Pubkey
from solders.instruction import Instruction, AccountMeta
from solders.system_program import ID as SYSTEM_PROGRAM
from solana.rpc.api import Client
import struct

PUMP_PROGRAM = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
FEE_PROGRAM = Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ")
ATA_PROGRAM = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
SPL_TOKEN = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
TOKEN_2022 = Pubkey.from_string("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")

# Same key for buy AND sell fee_config
FEE_CONFIG_KEY = bytes([
    1, 86, 224, 246, 147, 102, 90, 207, 68, 219, 21, 104, 191, 23, 91, 170,
    81, 137, 203, 151, 245, 210, 255, 59, 101, 93, 43, 182, 253, 109, 24, 176,
])


def derive_pda(seeds: list[bytes], program: Pubkey) -> Pubkey:
    """Derive a PDA from seeds and program ID."""
    pda, _ = Pubkey.find_program_address(seeds, program)
    return pda


def derive_ata(owner: Pubkey, mint: Pubkey, token_program: Pubkey) -> Pubkey:
    return derive_pda(
        [bytes(owner), bytes(token_program), bytes(mint)],
        ATA_PROGRAM,
    )


def derive_bonding_curve(mint: Pubkey) -> Pubkey:
    return derive_pda([b"bonding-curve", bytes(mint)], PUMP_PROGRAM)


def derive_bonding_curve_v2(mint: Pubkey) -> Pubkey:
    return derive_pda([b"bonding-curve-v2", bytes(mint)], PUMP_PROGRAM)


def derive_fee_config() -> Pubkey:
    return derive_pda([b"fee_config", FEE_CONFIG_KEY], FEE_PROGRAM)


def is_cashback_enabled(bonding_curve_data: bytes) -> bool:
    """
    Check byte[82] of the bonding curve account data.
    Returns True if cashback is enabled for this token.

    WARNING: Do NOT use the token program ID as a proxy for cashback.
    Many Token-2022 tokens have cashback_enabled = False.
    """
    return len(bonding_curve_data) > 82 and bonding_curve_data[82] != 0


def read_creator(bonding_curve_data: bytes) -> Pubkey:
    """Read the creator pubkey from bonding curve data (offset 49)."""
    return Pubkey.from_bytes(bonding_curve_data[49:81])


def build_buy_instruction(
    mint: Pubkey,
    payer: Pubkey,
    sol_amount: int,
    min_tokens_out: int,
    fee_recipient: Pubkey,
    creator: Pubkey,
    token_program: Pubkey,
) -> Instruction:
    """
    Build a buy_exact_sol_in instruction.
    Always 17 accounts (same for cashback and non-cashback tokens).
    """
    bonding_curve = derive_bonding_curve(mint)
    global_pda = derive_pda([b"global"], PUMP_PROGRAM)
    creator_vault = derive_pda([b"creator-vault", bytes(creator)], PUMP_PROGRAM)
    event_auth = derive_pda([b"__event_authority"], PUMP_PROGRAM)
    global_vol = derive_pda([b"global_volume_accumulator"], PUMP_PROGRAM)
    user_vol = derive_pda([b"user_volume_accumulator", bytes(payer)], PUMP_PROGRAM)
    fee_config = derive_fee_config()
    bonding_curve_v2 = derive_bonding_curve_v2(mint)

    # buy_exact_sol_in discriminator
    disc = bytes([56, 252, 116, 8, 158, 223, 205, 95])
    data = disc + struct.pack("<QQ", sol_amount, min_tokens_out)

    accounts = [
        AccountMeta(global_pda, is_signer=False, is_writable=False),                          # 0
        AccountMeta(fee_recipient, is_signer=False, is_writable=True),                         # 1
        AccountMeta(mint, is_signer=False, is_writable=False),                                 # 2
        AccountMeta(bonding_curve, is_signer=False, is_writable=True),                         # 3
        AccountMeta(derive_ata(bonding_curve, mint, token_program), is_signer=False, is_writable=True),  # 4
        AccountMeta(derive_ata(payer, mint, token_program), is_signer=False, is_writable=True),          # 5
        AccountMeta(payer, is_signer=True, is_writable=True),                                  # 6
        AccountMeta(SYSTEM_PROGRAM, is_signer=False, is_writable=False),                       # 7
        AccountMeta(token_program, is_signer=False, is_writable=False),                        # 8
        AccountMeta(creator_vault, is_signer=False, is_writable=True),                         # 9
        AccountMeta(event_auth, is_signer=False, is_writable=False),                           # 10
        AccountMeta(PUMP_PROGRAM, is_signer=False, is_writable=False),                         # 11
        AccountMeta(global_vol, is_signer=False, is_writable=False),                           # 12
        AccountMeta(user_vol, is_signer=False, is_writable=True),                              # 13
        AccountMeta(fee_config, is_signer=False, is_writable=False),                           # 14
        AccountMeta(FEE_PROGRAM, is_signer=False, is_writable=False),                          # 15
        AccountMeta(bonding_curve_v2, is_signer=False, is_writable=False),                     # 16 ← NEW
    ]

    return Instruction(PUMP_PROGRAM, data, accounts)


def build_sell_instruction(
    mint: Pubkey,
    payer: Pubkey,
    token_amount: int,
    min_sol_out: int,
    fee_recipient: Pubkey,
    creator: Pubkey,
    token_program: Pubkey,
    cashback_enabled: bool,  # ← from bonding curve byte[82]
) -> Instruction:
    """
    Build a sell instruction.
    Non-cashback: 15 accounts. Cashback: 16 accounts.
    """
    bonding_curve = derive_bonding_curve(mint)
    global_pda = derive_pda([b"global"], PUMP_PROGRAM)
    creator_vault = derive_pda([b"creator-vault", bytes(creator)], PUMP_PROGRAM)
    event_auth = derive_pda([b"__event_authority"], PUMP_PROGRAM)
    fee_config = derive_fee_config()
    bonding_curve_v2 = derive_bonding_curve_v2(mint)

    # sell discriminator
    disc = bytes([51, 230, 133, 164, 1, 127, 131, 173])
    data = disc + struct.pack("<QQ", token_amount, min_sol_out)

    accounts = [
        AccountMeta(global_pda, is_signer=False, is_writable=False),                          # 0
        AccountMeta(fee_recipient, is_signer=False, is_writable=True),                         # 1
        AccountMeta(mint, is_signer=False, is_writable=False),                                 # 2
        AccountMeta(bonding_curve, is_signer=False, is_writable=True),                         # 3
        AccountMeta(derive_ata(bonding_curve, mint, token_program), is_signer=False, is_writable=True),  # 4
        AccountMeta(derive_ata(payer, mint, token_program), is_signer=False, is_writable=True),          # 5
        AccountMeta(payer, is_signer=True, is_writable=True),                                  # 6
        AccountMeta(SYSTEM_PROGRAM, is_signer=False, is_writable=False),                       # 7
        AccountMeta(creator_vault, is_signer=False, is_writable=True),                         # 8
        AccountMeta(token_program, is_signer=False, is_writable=False),                        # 9
        AccountMeta(event_auth, is_signer=False, is_writable=False),                           # 10
        AccountMeta(PUMP_PROGRAM, is_signer=False, is_writable=False),                         # 11
        AccountMeta(fee_config, is_signer=False, is_writable=False),                           # 12
        AccountMeta(FEE_PROGRAM, is_signer=False, is_writable=False),                          # 13
    ]

    # Cashback tokens need user_volume_accumulator BEFORE bonding_curve_v2
    if cashback_enabled:
        user_vol = derive_pda(
            [b"user_volume_accumulator", bytes(payer)],
            PUMP_PROGRAM,
        )
        accounts.append(AccountMeta(user_vol, is_signer=False, is_writable=True))              # 14

    # bonding_curve_v2 is ALWAYS the last account
    accounts.append(AccountMeta(bonding_curve_v2, is_signer=False, is_writable=False))         # 14 or 15

    return Instruction(PUMP_PROGRAM, data, accounts)


# ----- Usage Example -----

def example():
    client = Client("https://api.mainnet-beta.solana.com")
    mint = Pubkey.from_string("HhEdFsuJ88cFsUFT5LyqDCEdsbYzWiHs2MBMucJtpump")

    # 1. Fetch bonding curve data
    bonding_curve = derive_bonding_curve(mint)
    resp = client.get_account_info(bonding_curve)
    bc_data = bytes(resp.value.data)

    # 2. Read cashback flag from byte[82]
    cashback = is_cashback_enabled(bc_data)
    print(f"Cashback enabled: ${cashback}")

    # 3. Read creator
    creator = read_creator(bc_data)

    # 4. Detect token program
    mint_resp = client.get_account_info(mint)
    token_program = mint_resp.value.owner  # spl-token or Token-2022

    # 5. Build sell instruction with correct cashback flag
    # sell_ix = build_sell_instruction(mint, payer, amount, 0, fee_recipient, creator, token_program, cashback)

PumpSwap AMM (pool_v2)

The PumpSwap AMM program (pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA) requires a similar fix. Instead of bonding_curve_v2, it uses a pool_v2 PDA:

Text
seeds = ["pool-v2", base_mint]
program = pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA

The PumpSwap pool account has its own is_cashback field within the pool data (at a different offset than the bonding curve). Check the pool data directly rather than inferring from token type.


Common Mistakes

1. Using Token-2022 as a proxy for cashback

TypeScript
// WRONG — many Token-2022 tokens are NOT cashback-enabled
const isCashback = mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID);

// CORRECT — read the flag from the bonding curve data
const isCashback = bondingCurveData[82] !== 0;

2. Using a different fee_config for sell vs buy

Rust
// WRONG — the bonding curve program uses the SAME fee_config for buy and sell
let sell_fee_key: [u8; 32] = [12, 20, 222, ...]; // This is the PumpSwap AMM key!

// CORRECT — same key for both buy and sell on the bonding curve program
let fee_key: [u8; 32] = [1, 86, 224, 246, 147, 102, 90, 207, ...];

3. Forgetting bonding_curve_v2 entirely

The overflow error message is misleading. The program doesn't say "missing account" — it says "Overflow" because it reads the wrong data from shifted account indices.

4. Putting bonding_curve_v2 before user_volume_accumulator

The order matters on sell for cashback tokens:

Text
// WRONG order
accounts.push(bonding_curve_v2);      // ← this must be LAST
accounts.push(user_volume_accumulator);

// CORRECT order
accounts.push(user_volume_accumulator); // cashback tokens only
accounts.push(bonding_curve_v2);        // ALWAYS last

5. Always including user_volume_accumulator on sell

Rust
// WRONG — causes overflow on non-cashback tokens
accounts.push(user_volume_accumulator); // only for cashback!
accounts.push(bonding_curve_v2);

// CORRECT — conditional on byte[82]
if cashback_enabled {
    accounts.push(user_volume_accumulator);
}
accounts.push(bonding_curve_v2); // always

Summary

InstructionCashback StatusTotal AccountsExtra Remaining Accounts
buy_exact_sol_inAny17bonding_curve_v2
sellNon-cashback (byte[82]=0)15bonding_curve_v2
sellCashback (byte[82]=1)16user_volume_accumulator + bonding_curve_v2

Three rules to remember:

  1. Always append bonding_curve_v2 (PDA: ["bonding-curve-v2", mint]) as the last account on every buy and sell instruction.
  2. Read byte[82] from the bonding curve account data to determine cashback status — not the token program.
  3. Cashback sell needs user_volume_accumulator inserted before bonding_curve_v2.

*Last updated: March 2026. Based on the PumpFun bonding curve program at 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P. See pump-fun/pump-public-docs#30 for the original discussion.