feat(pumpfun): runtime V2 flag, buyback fee pool fix, legacy ix encoding

- Replace compile-time `pumpfun-v2` feature flag with runtime
  `TradeConfig::use_pumpfun_v2(bool)` for flexible V1/V2 switching
- Fix BUYBACK_FEE_RECIPIENTS pool: replace wrong addresses (was reusing
  standard pool + FEE_CONFIG) with official buyback pool from
  FEE_RECIPIENTS.md
- Fix legacy buy/buy_exact_sol_in ix data encoding: use 2-byte
  Option<bool> (option tag + value) matching official Pump SDK, 26 bytes
  total instead of broken 25
- Add `SwapParams.use_pumpfun_v2` field and wire through TradingClient
  buy/sell paths
- V2 instructions read `quote_mint` from `PumpFunParams` (not
  BondingCurveAccount which lacks the field)
- Fix `fetch_bonding_curve_account` to use BorshDeserialize instead of
  non-existent `decode_from_chain_account_data`
- Update all examples with `use_pumpfun_v2: false` field
- Update README with unified V1/V2 section showing both enabling methods
This commit is contained in:
0xfnzero
2026-05-09 07:15:07 +08:00
parent 0311a0b876
commit 1cc051a874
21 changed files with 841 additions and 513 deletions
+32 -21
View File
@@ -53,6 +53,7 @@
- [🔍 Address Lookup Tables](#-address-lookup-tables)
- [🔍 Nonce Cache](#-nonce-cache)
- [💰 Cashback Support (PumpFun / PumpSwap)](#-cashback-support-pumpfun--pumpswap)
- [🔄 PumpFun V1 vs V2 Instructions](#-pumpfun-v1-vs-v2-instructions)
- [🛡️ MEV Protection Services](#-mev-protection-services)
- [📁 Project Structure](#-project-structure)
- [📄 License](#-license)
@@ -145,6 +146,7 @@ let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment)
// .check_min_tip(false) // default: false - filter SWQOS below min tip
// .swqos_cores_from_end(false) // default: false - bind SWQOS to last N CPU cores
// .mev_protection(false) // default: false - MEV (Astralane QUIC :9000 or HTTP mev-protect / BlockRazor)
// .use_pumpfun_v2(false) // default: false - V1 (18 accounts); set true for V2 (27 accounts, quote_mint) when PumpFun deploys V2
.build();
// Create TradingClient
@@ -347,49 +349,58 @@ Some PumpFun coins use **Creator Rewards Sharing**, so the on-chain `creator_vau
The SDK does not fetch creator_vault from RPC on every sell (to avoid latency); pass the up-to-date vault from gRPC/events when available.
#### PumpSwap: coin_creator_vault from events (no RPC)
#### PumpFun V1 vs V2 Instructions
For **PumpSwap** (Pump AMM), `coin_creator_vault_ata` and `coin_creator_vault_authority` are required in buy/sell instructions. Both are available from parsed events without RPC:
PumpFun has two instruction sets for bonding-curve trading:
- **sol-parser-sdk**: Instruction parser sets them from accounts 17 and 18; the account filler also fills them when the event comes from logs. Use `PumpSwapParams::from_trade(..., e.coin_creator_vault_ata, e.coin_creator_vault_authority, ...)` with the buy/sell event `e`.
- **solana-streamer**: Instruction parser sets them from `accounts.get(17)` and `accounts.get(18)`. Use the same `from_trade` with the events `coin_creator_vault_ata` and `coin_creator_vault_authority`.
| | V1 (default) | V2 (opt-in) |
|---|---|---|
| Instructions | `buy` / `buy_exact_sol_in` / `sell` | `buy_v2` / `buy_exact_quote_in_v2` / `sell_v2` |
| Account metas | 18 | 27 |
| Quote mint | SOL only (legacy) | SOL or USDC (via `quote_mint` field) |
| Transaction size | Smaller (fits `PACKET_DATA_SIZE` without LUT) | Larger (requires LUT for most transactions) |
### Pump.fun Bonding Curve v2 (buy_v2 / sell_v2 / buy_exact_quote_in_v2)
Pump.fun has upgraded the Bonding Curve contract with **unified v2 instructions** that support both SOL-paired and USDC-paired coins through a single fixed account layout. The legacy `buy`/`sell`/`buy_exact_sol_in` instructions continue to work for SOL-paired coins and remain the default.
**Default: V1** (`use_pumpfun_v2 = false`). The SDK uses V1 instructions which produce smaller transactions that fit within the 1232-byte `PACKET_DATA_SIZE` limit without requiring an Address Lookup Table.
**Key changes in v2 instructions:**
- `quote_mint` parameter — pass wrapped SOL (`So11111111111111111111111111111111111111112`) for SOL-paired, or USDC mint for USDC-paired
- `quote_mint` parameter — pass wrapped SOL for SOL-paired, or USDC mint for USDC-paired
- 27 fixed accounts (buy) / 26 fixed accounts (sell) — **no optional accounts**
- `buyback_fee_recipient`, `sharing_config`, and 6 `associated_quote_*` ATAs are now mandatory
- Same pricing and cost as legacy instructions for SOL-paired coins
**Using v2 instructions:**
**How to enable V2:**
Set `quote_mint` on your `PumpFunParams`. The SDK automatically switches to `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` discriminators with the 27/26-account layout:
**Method 1 — Global runtime flag** (recommended when PumpFun officially deploys V2 on mainnet):
```rust
let trade_config = TradeConfig::builder(rpc_url, swqos_configs, commitment)
.use_pumpfun_v2(true) // Switch all PumpFun trades to V2 instructions (27 accounts)
.build();
```
**Method 2 — Per-trade via `quote_mint`** (for USDC-paired coins or mixed V1/V2 scenarios):
```rust
use sol_trade_sdk::constants::WSOL_TOKEN_ACCOUNT;
use sol_trade_sdk::constants::USDC_TOKEN_ACCOUNT;
// SOL-paired coin — use wrapped SOL mint
// SOL-paired coin with v2 layout
let params = PumpFunParams::from_trade(/* ... */)
.with_quote_mint(WSOL_TOKEN_ACCOUNT);
// USDC-paired coin (coming soon — requires v2)
// USDC-paired coin (requires v2)
let params = PumpFunParams::from_trade(/* ... */)
.with_quote_mint(USDC_TOKEN_ACCOUNT);
// Then trade as usual
client.buy(buy_params).await?;
client.sell(sell_params).await?;
```
| Quote Mint | `use_v2_ix` | Instruction Used | Notes |
|-----------|-------------|-----------------|-------|
| Not set (default) | `false` | Legacy `buy`/`sell`/`buy_exact_sol_in` | Backward compatible, SOL-only |
| `WSOL_TOKEN_ACCOUNT` | `true` | `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` | SOL-paired, unified layout |
| `USDC_TOKEN_ACCOUNT` | `true` | `buy_v2`/`sell_v2`/`buy_exact_quote_in_v2` | USDC-paired (requires v2) |
> **Note**: V2 transactions with ATA creation + durable nonce may exceed `PACKET_DATA_SIZE`. Enable an Address Lookup Table (`address_lookup_table_account`) when using V2.
#### PumpSwap: coin_creator_vault from events (no RPC)
For **PumpSwap** (Pump AMM), `coin_creator_vault_ata` and `coin_creator_vault_authority` are required in buy/sell instructions. Both are available from parsed events without RPC:
- **sol-parser-sdk**: Instruction parser sets them from accounts 17 and 18; the account filler also fills them when the event comes from logs. Use `PumpSwapParams::from_trade(..., e.coin_creator_vault_ata, e.coin_creator_vault_authority, ...)` with the buy/sell event `e`.
- **solana-streamer**: Instruction parser sets them from `accounts.get(17)` and `accounts.get(18)`. Use the same `from_trade` with the event's `coin_creator_vault_ata` and `coin_creator_vault_authority`.
## 🛡️ MEV Protection Services
+1
View File
@@ -176,6 +176,7 @@ async fn pumpfun_copy_trade_with_grpc(
gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+1
View File
@@ -179,6 +179,7 @@ async fn bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()>
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+1
View File
@@ -147,6 +147,7 @@ async fn bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+5
View File
@@ -636,6 +636,7 @@ async fn handle_buy_pumpfun(
gas_fee_strategy: gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
@@ -692,6 +693,7 @@ async fn handle_buy_pumpswap(
gas_fee_strategy: gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
@@ -748,6 +750,7 @@ async fn handle_buy_bonk(
gas_fee_strategy: gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
@@ -808,6 +811,7 @@ async fn handle_buy_raydium_v4(
gas_fee_strategy: gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
@@ -869,6 +873,7 @@ async fn handle_buy_raydium_cpmm(
gas_fee_strategy: gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
match client.buy(buy_params).await {
@@ -51,6 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+1
View File
@@ -112,6 +112,7 @@ async fn test_middleware() -> AnyResult<()> {
gas_fee_strategy: gas_fee_strategy,
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+1
View File
@@ -172,6 +172,7 @@ async fn pumpfun_copy_trade_with_grpc(
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
@@ -169,6 +169,7 @@ async fn pumpfun_copy_trade(e: sol_parser_sdk::core::events::PumpFunTradeEvent)
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
@@ -159,6 +159,7 @@ async fn pumpfun_sniper_trade(e: sol_parser_sdk::core::events::PumpFunTradeEvent
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
@@ -50,6 +50,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+1
View File
@@ -244,6 +244,7 @@ async fn pumpswap_trade_with_grpc(
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
@@ -181,6 +181,7 @@ async fn raydium_amm_v4_copy_trade_with_grpc(trade_info: RaydiumAmmV4SwapEvent)
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
@@ -169,6 +169,7 @@ async fn raydium_cpmm_copy_trade_with_grpc(trade_info: RaydiumCpmmSwapEvent) ->
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+1
View File
@@ -50,6 +50,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
gas_fee_strategy: gas_fee_strategy.clone(),
simulate: false,
use_exact_sol_amount: None,
use_pumpfun_v2: false,
grpc_recv_us: None,
};
client.buy(buy_params).await?;
+8
View File
@@ -303,6 +303,8 @@ pub struct TradingClient {
pub log_enabled: bool,
/// Whether to check minimum tip per SWQOS (from TradeConfig.check_min_tip). Default false for lower latency.
pub check_min_tip: bool,
/// Use PumpFun V2 instructions (buy_v2 / sell_v2, 27-account metas). Default false.
pub use_pumpfun_v2: bool,
}
static INSTANCE: Mutex<Option<Arc<TradingClient>>> = Mutex::new(None);
@@ -323,6 +325,7 @@ impl Clone for TradingClient {
effective_core_ids: self.effective_core_ids.clone(),
log_enabled: self.log_enabled,
check_min_tip: self.check_min_tip,
use_pumpfun_v2: self.use_pumpfun_v2,
}
}
}
@@ -457,6 +460,7 @@ impl TradingClient {
effective_core_ids,
log_enabled: true,
check_min_tip: false,
use_pumpfun_v2: false,
}
}
@@ -503,6 +507,7 @@ impl TradingClient {
effective_core_ids,
log_enabled: true,
check_min_tip: false,
use_pumpfun_v2: false,
}
}
@@ -682,6 +687,7 @@ impl TradingClient {
effective_core_ids: infrastructure.effective_core_ids.clone(),
log_enabled: trade_config.log_enabled,
check_min_tip: trade_config.check_min_tip,
use_pumpfun_v2: trade_config.use_pumpfun_v2,
};
let mut current = INSTANCE.lock();
@@ -860,6 +866,7 @@ impl TradingClient {
check_min_tip: self.check_min_tip,
grpc_recv_us: params.grpc_recv_us,
use_exact_sol_amount: params.use_exact_sol_amount,
use_pumpfun_v2: self.use_pumpfun_v2,
};
let swap_result = executor.swap(buy_params).await;
@@ -967,6 +974,7 @@ impl TradingClient {
check_min_tip: self.check_min_tip,
grpc_recv_us: params.grpc_recv_us,
use_exact_sol_amount: None,
use_pumpfun_v2: self.use_pumpfun_v2,
};
let swap_result = executor.swap(sell_params).await;
+15
View File
@@ -95,6 +95,10 @@ pub struct TradeConfig {
/// (Astralane QUIC `:9000` or Plain/Binary HTTP `mev-protect=true`, BlockRazor sandwichMitigation)
/// use their MEV-protected endpoints/modes. Default false (no MEV protection, lower latency).
pub mev_protection: bool,
/// Use PumpFun V2 instructions (buy_v2 / sell_v2, 27-account metas, quote_mint support).
/// Default: `false` — uses V1 instructions (18-account metas, legacy SOL-paired, smaller transaction).
/// Set to `true` when PumpFun officially deploys V2 on mainnet.
pub use_pumpfun_v2: bool,
}
impl TradeConfig {
@@ -149,6 +153,7 @@ pub struct TradeConfigBuilder {
check_min_tip: bool,
swqos_cores_from_end: bool,
mev_protection: bool,
use_pumpfun_v2: bool,
}
impl TradeConfigBuilder {
@@ -163,6 +168,7 @@ impl TradeConfigBuilder {
check_min_tip: false,
swqos_cores_from_end: false,
mev_protection: false,
use_pumpfun_v2: false,
}
}
@@ -208,6 +214,14 @@ impl TradeConfigBuilder {
self
}
/// Use PumpFun V2 instructions (`buy_v2` / `sell_v2`, 27-account metas, `quote_mint` support).
/// Default: `false` (V1 — 18-account metas, legacy SOL-paired, smaller transaction).
/// Set to `true` when PumpFun officially deploys V2 on mainnet.
pub fn use_pumpfun_v2(mut self, v: bool) -> Self {
self.use_pumpfun_v2 = v;
self
}
/// Consume the builder and produce a [`TradeConfig`].
pub fn build(self) -> TradeConfig {
TradeConfig {
@@ -220,6 +234,7 @@ impl TradeConfigBuilder {
check_min_tip: self.check_min_tip,
swqos_cores_from_end: self.swqos_cores_from_end,
mev_protection: self.mev_protection,
use_pumpfun_v2: self.use_pumpfun_v2,
}
}
}
+736 -466
View File
File diff suppressed because it is too large Load Diff
+7 -4
View File
@@ -1,6 +1,9 @@
//! Pump.fun 曲线 `buy` / `buy_exact_sol_in` / `sell` **instruction data** 栈上编码(热路径零堆分配)。
//! Pump.fun 曲线 **legacy** `buy` / `buy_exact_sol_in` / `sell` **`buy_v2` / `sell_v2` / `buy_exact_quote_in_v2`**
//! 的 instruction data 栈上编码(热路径零堆分配)。
//!
//! 与 `@pump-fun/pump-sdk` Anchor `coder.instruction.encode` 对齐:`OptionBool` 在 ix 参数中为 **1 字节**。
//! Legacy `buy` / `buy_exact_sol_in` 与 `@pump-fun/pump-sdk` 对齐:`OptionBool` 在 ix 参数中为
//! **1 字节 option tag + 1 字节值**Anchor `Option<bool>` = 2 字节),共 26 字节 ix data。
//! `*_v2` 指令无 `track_volume` 字节(见 [pump-public-docs](https://github.com/pump-fun/pump-public-docs))。
use crate::instruction::utils::pumpfun::{
BUY_DISCRIMINATOR, BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR, BUY_EXACT_SOL_IN_DISCRIMINATOR,
@@ -74,10 +77,10 @@ pub fn encode_pumpfun_buy_exact_quote_in_v2_ix_data(
}
#[inline(always)]
pub fn encode_pumpfun_sell_v2_ix_data(amount: u64, min_sol_output: u64) -> [u8; 24] {
pub fn encode_pumpfun_sell_v2_ix_data(token_amount: u64, min_sol_output: u64) -> [u8; 24] {
let mut d = [0u8; 24];
d[..8].copy_from_slice(&SELL_V2_DISCRIMINATOR);
d[8..16].copy_from_slice(&amount.to_le_bytes());
d[8..16].copy_from_slice(&token_amount.to_le_bytes());
d[16..24].copy_from_slice(&min_sol_output.to_le_bytes());
d
}
+21 -22
View File
@@ -4,6 +4,7 @@
use crate::common::{bonding_curve::BondingCurveAccount, SolanaRpcClient};
use anyhow::anyhow;
use borsh::BorshDeserialize;
use rand::seq::IndexedRandom;
use solana_sdk::{
instruction::{AccountMeta, Instruction},
@@ -108,16 +109,16 @@ pub const PROTOCOL_EXTRA_FEE_RECIPIENTS: [Pubkey; 8] = [
];
/// Buyback fee recipients (v2 account #9 in buy_v2/sell_v2).
/// Selected randomly — distinct from main fee recipients.
/// 对应官方 FEE_RECIPIENTS.md "Buyback (Applies to All)" 池,与主 fee_recipient 池互斥。
pub const BUYBACK_FEE_RECIPIENTS: [Pubkey; 8] = [
pubkey!("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"),
pubkey!("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"),
pubkey!("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP"),
pubkey!("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"),
pubkey!("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"),
pubkey!("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"),
pubkey!("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"),
pubkey!("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt"),
pubkey!("5YxQFdt3Tr9zJLvkFccqXVUwhdTWJQc1fFg2YPbxvxeD"),
pubkey!("9M4giFFMxmFGXtc3feFzRai56WbBqehoSeRE5GK7gf7"),
pubkey!("GXPFM2caqTtQYC2cJ5yJRi9VDkpsYZXzYdwYpGnLmtDL"),
pubkey!("3BpXnfJaUTiwXnJNe7Ej1rcbzqTTQUvLShZaWazebsVR"),
pubkey!("5cjcW9wExnJJiqgLjq7DEG75Pm6JBgE1hNv4B2vHXUW6"),
pubkey!("EHAAiTxcdDwQ3U4bU6YcMsQGaekdzLS3B5SmYo46kJtL"),
pubkey!("5eHhjP8JaYkz83CWwvGU2uMUXefd3AazWGx4gpcuEEYD"),
pubkey!("A7hAgCzFw14fejgCp387JUJRMNyz4j89JKnhtKU8piqW"),
];
}
@@ -184,11 +185,12 @@ pub const PUMP_BONDING_CURVE_MIN_DATA_LEN: usize = 151;
pub const BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
pub const BUY_EXACT_SOL_IN_DISCRIMINATOR: [u8; 8] = [56, 252, 116, 8, 158, 223, 205, 95];
pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
/// buy_v2: unified buy with quote_mint support (SOL + USDC), 27 fixed accounts, 2 args (no track_volume)
/// `buy_v2` — unified SOL/USDC quote interface ([pump-public-docs](https://github.com/pump-fun/pump-public-docs)).
pub const BUY_V2_DISCRIMINATOR: [u8; 8] = [184, 23, 238, 97, 103, 197, 211, 61];
/// sell_v2: unified sell with quote_mint support (SOL + USDC), 26 fixed accounts, 2 args
/// `sell_v2`
pub const SELL_V2_DISCRIMINATOR: [u8; 8] = [93, 246, 130, 60, 231, 233, 64, 178];
/// buy_exact_quote_in_v2: spend exact quote amount for min tokens out (SOL + USDC), 27 fixed accounts, 2 args
/// `buy_exact_quote_in_v2` (native SOL spend for SOL-paired coins when `quote_mint` is WSOL)
pub const BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR: [u8; 8] = [194, 171, 28, 70, 104, 77, 91, 47];
pub const EXTEND_ACCOUNT_DISCRIMINATOR: [u8; 8] = [234, 102, 194, 203, 150, 72, 62, 229];
@@ -297,7 +299,7 @@ pub fn get_protocol_extra_fee_recipient_random() -> Pubkey {
.unwrap_or(&global_constants::PROTOCOL_EXTRA_FEE_RECIPIENTS[0])
}
/// Random buyback fee recipient from static pool (v2 account #9 in buy_v2/sell_v2).
/// Buyback fee recipient (#9 in buy_v2/sell_v2) — dedicated pool, distinct from protocol extra fee recipients.
#[inline]
pub fn get_buyback_fee_recipient_random() -> Pubkey {
*global_constants::BUYBACK_FEE_RECIPIENTS
@@ -305,12 +307,6 @@ pub fn get_buyback_fee_recipient_random() -> Pubkey {
.unwrap_or(&global_constants::BUYBACK_FEE_RECIPIENTS[0])
}
/// Quote token program id for a given quote_mint (both WSOL and USDC use the legacy Token Program).
#[inline]
pub fn get_quote_token_program(_quote_mint: &Pubkey) -> Pubkey {
crate::constants::TOKEN_PROGRAM
}
#[inline]
pub fn pump_fun_fee_recipient_meta(observed_fee_recipient: Pubkey, is_mayhem_mode: bool) -> AccountMeta {
let trust_observation = observed_fee_recipient != Pubkey::default()
@@ -549,9 +545,9 @@ pub async fn fetch_bonding_curve_account(
return Err(anyhow!("Bonding curve not found"));
}
let bonding_curve =
solana_sdk::borsh1::try_from_slice_unchecked::<BondingCurveAccount>(&account.data[8..])
.map_err(|e| anyhow::anyhow!("Failed to deserialize bonding curve account: {}", e))?;
let mut bonding_curve = BondingCurveAccount::try_from_slice(&account.data[8..])
.map_err(|e| anyhow::anyhow!("Failed to decode bonding curve account: {}", e))?;
bonding_curve.account = bonding_curve_pda;
Ok((Arc::new(bonding_curve), bonding_curve_pda))
}
@@ -573,6 +569,9 @@ mod tests {
assert_eq!(BUY_DISCRIMINATOR.len(), 8);
assert_eq!(BUY_EXACT_SOL_IN_DISCRIMINATOR.len(), 8);
assert_eq!(SELL_DISCRIMINATOR.len(), 8);
assert_eq!(BUY_V2_DISCRIMINATOR.len(), 8);
assert_eq!(SELL_V2_DISCRIMINATOR.len(), 8);
assert_eq!(BUY_EXACT_QUOTE_IN_V2_DISCRIMINATOR.len(), 8);
}
#[test]
+4
View File
@@ -97,6 +97,10 @@ pub struct SwapParams {
/// When Some(false), uses regular buy instruction where slippage is applied to SOL/quote input.
/// This option only applies to PumpFun and PumpSwap DEXes; it is ignored for other DEXes.
pub use_exact_sol_amount: Option<bool>,
/// Use PumpFun V2 instructions (buy_v2 / sell_v2 / buy_exact_quote_in_v2, 27-account metas, quote_mint support).
/// Default: `false` — uses V1 instructions (18-account metas, legacy SOL-paired).
/// Set to `true` when PumpFun officially deploys V2 on mainnet. Until then, keep `false` for smaller transaction size.
pub use_pumpfun_v2: bool,
}
impl SwapParams {