Files
sol-trade-sdk/src/trading/core/params/pumpswap.rs
T
0xfnzeroandCursor fda211ea87 refactor: extract TradingClient façade to client/mod.rs
refactor: split trading/core/params into per-protocol modules + dex_swap
build: crate-type rlib only (drop unused cdylib)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-04 13:46:11 +08:00

221 lines
8.8 KiB
Rust

use crate::common::spl_associated_token_account::get_associated_token_address_with_program_id;
use crate::common::SolanaRpcClient;
use crate::instruction::utils::pumpswap::accounts::MAYHEM_FEE_RECIPIENT as MAYHEM_FEE_RECIPIENT_SWAP;
use solana_sdk::pubkey::Pubkey;
/// PumpSwap Protocol Specific Parameters
///
/// Parameters for configuring PumpSwap trading protocol, including liquidity pool information,
/// token configuration, and transaction amounts.
///
/// **Performance Note**: If these parameters are not provided, the system will attempt to
/// retrieve the relevant information from RPC, which will increase transaction time.
/// For optimal performance, it is recommended to provide all necessary parameters in advance.
#[derive(Clone)]
pub struct PumpSwapParams {
/// Liquidity pool address
pub pool: Pubkey,
/// Base token mint address
/// The mint account address of the base token in the trading pair
pub base_mint: Pubkey,
/// Quote token mint address
/// The mint account address of the quote token in the trading pair, usually SOL or USDC
pub quote_mint: Pubkey,
/// Pool base token account
pub pool_base_token_account: Pubkey,
/// Pool quote token account
pub pool_quote_token_account: Pubkey,
/// Base token reserves in the pool
pub pool_base_token_reserves: u64,
/// Quote token reserves in the pool
pub pool_quote_token_reserves: u64,
/// Coin creator vault ATA
pub coin_creator_vault_ata: Pubkey,
/// Coin creator vault authority
pub coin_creator_vault_authority: Pubkey,
/// Token program ID
pub base_token_program: Pubkey,
/// Quote token program ID
pub quote_token_program: Pubkey,
/// Whether the pool is in mayhem mode
pub is_mayhem_mode: bool,
/// Pool [`Pool::coin_creator`](crate::instruction::utils::pumpswap_types::Pool). Used for PumpSwap
/// `remaining_accounts`: **`pool-v2` is appended only when this is not `Pubkey::default()`
/// (matches `@pump-fun/pump-swap-sdk`); wrong flag causes buys to revert with buyback recipient errors (e.g. 6053).
pub coin_creator: Pubkey,
/// Whether the pool's coin has cashback enabled
pub is_cashback_coin: bool,
/// Cashback fee in basis points (from trade events / sol-parser-sdk). For quote-in buy and base-in sell
/// math, this is summed with [`COIN_CREATOR_FEE_BASIS_POINTS`](crate::instruction::utils::pumpswap::accounts::COIN_CREATOR_FEE_BASIS_POINTS)
/// when a creator vault applies — matching on-chain treating creator + cashback as one fee bucket.
/// Use `0` when unknown (e.g. RPC-only pool decode has no per-mint cashback bps).
pub cashback_fee_basis_points: u64,
}
impl PumpSwapParams {
pub fn new(
pool: Pubkey,
base_mint: Pubkey,
quote_mint: Pubkey,
pool_base_token_account: Pubkey,
pool_quote_token_account: Pubkey,
pool_base_token_reserves: u64,
pool_quote_token_reserves: u64,
coin_creator_vault_ata: Pubkey,
coin_creator_vault_authority: Pubkey,
base_token_program: Pubkey,
quote_token_program: Pubkey,
fee_recipient: Pubkey,
coin_creator: Pubkey,
is_cashback_coin: bool,
cashback_fee_basis_points: u64,
) -> Self {
let is_mayhem_mode = fee_recipient == MAYHEM_FEE_RECIPIENT_SWAP;
Self {
pool,
base_mint,
quote_mint,
pool_base_token_account,
pool_quote_token_account,
pool_base_token_reserves,
pool_quote_token_reserves,
coin_creator_vault_ata,
coin_creator_vault_authority,
base_token_program,
quote_token_program,
is_mayhem_mode,
coin_creator,
is_cashback_coin,
cashback_fee_basis_points,
}
}
/// Fast-path constructor for building PumpSwap parameters directly from decoded
/// trade/event data and the accompanying instruction accounts, avoiding RPC
/// lookups and associated latency. Token program IDs should be sourced from
/// the instruction accounts themselves to respect Token Program vs Token-2022
/// differences.
///
/// When building from event/parser (e.g. sol-parser-sdk), pass `is_cashback_coin`
/// from the event so that buy/sell instructions include the correct remaining
/// accounts for cashback.
pub fn from_trade(
pool: Pubkey,
base_mint: Pubkey,
quote_mint: Pubkey,
pool_base_token_account: Pubkey,
pool_quote_token_account: Pubkey,
pool_base_token_reserves: u64,
pool_quote_token_reserves: u64,
coin_creator_vault_ata: Pubkey,
coin_creator_vault_authority: Pubkey,
base_token_program: Pubkey,
quote_token_program: Pubkey,
fee_recipient: Pubkey,
coin_creator: Pubkey,
is_cashback_coin: bool,
cashback_fee_basis_points: u64,
) -> Self {
Self::new(
pool,
base_mint,
quote_mint,
pool_base_token_account,
pool_quote_token_account,
pool_base_token_reserves,
pool_quote_token_reserves,
coin_creator_vault_ata,
coin_creator_vault_authority,
base_token_program,
quote_token_program,
fee_recipient,
coin_creator,
is_cashback_coin,
cashback_fee_basis_points,
)
}
pub async fn from_mint_by_rpc(
rpc: &SolanaRpcClient,
mint: &Pubkey,
) -> Result<Self, anyhow::Error> {
if let Ok((pool_address, _)) =
crate::instruction::utils::pumpswap::find_by_base_mint(rpc, mint).await
{
Self::from_pool_address_by_rpc(rpc, &pool_address).await
} else if let Ok((pool_address, _)) =
crate::instruction::utils::pumpswap::find_by_quote_mint(rpc, mint).await
{
Self::from_pool_address_by_rpc(rpc, &pool_address).await
} else {
return Err(anyhow::anyhow!("No pool found for mint"));
}
}
pub async fn from_pool_address_by_rpc(
rpc: &SolanaRpcClient,
pool_address: &Pubkey,
) -> Result<Self, anyhow::Error> {
let pool_data = crate::instruction::utils::pumpswap::fetch_pool(rpc, pool_address).await?;
Self::from_pool_data(rpc, pool_address, &pool_data).await
}
/// Build params from an already-decoded Pool, only fetching token balances.
///
/// Saves 1 RPC `getAccount` call vs `from_pool_address_by_rpc` when pool data
/// is already available (e.g. from `pumpswap::find_by_mint` which returns the
/// decoded Pool).
pub async fn from_pool_data(
rpc: &SolanaRpcClient,
pool_address: &Pubkey,
pool_data: &crate::instruction::utils::pumpswap_types::Pool,
) -> Result<Self, anyhow::Error> {
let (pool_base_token_reserves, pool_quote_token_reserves) =
crate::instruction::utils::pumpswap::get_token_balances(pool_data, rpc).await?;
let creator = pool_data.coin_creator;
let coin_creator_vault_ata = crate::instruction::utils::pumpswap::coin_creator_vault_ata(
creator,
pool_data.quote_mint,
);
let coin_creator_vault_authority =
crate::instruction::utils::pumpswap::coin_creator_vault_authority(creator);
let base_token_program_ata = get_associated_token_address_with_program_id(
pool_address,
&pool_data.base_mint,
&crate::constants::TOKEN_PROGRAM,
);
let quote_token_program_ata = get_associated_token_address_with_program_id(
pool_address,
&pool_data.quote_mint,
&crate::constants::TOKEN_PROGRAM,
);
Ok(Self {
pool: *pool_address,
base_mint: pool_data.base_mint,
quote_mint: pool_data.quote_mint,
pool_base_token_account: pool_data.pool_base_token_account,
pool_quote_token_account: pool_data.pool_quote_token_account,
pool_base_token_reserves,
pool_quote_token_reserves,
coin_creator_vault_ata,
coin_creator_vault_authority,
base_token_program: if pool_data.pool_base_token_account == base_token_program_ata {
crate::constants::TOKEN_PROGRAM
} else {
crate::constants::TOKEN_PROGRAM_2022
},
is_cashback_coin: pool_data.is_cashback_coin,
quote_token_program: if pool_data.pool_quote_token_account == quote_token_program_ata {
crate::constants::TOKEN_PROGRAM
} else {
crate::constants::TOKEN_PROGRAM_2022
},
is_mayhem_mode: pool_data.is_mayhem_mode,
coin_creator: pool_data.coin_creator,
cashback_fee_basis_points: 0,
})
}
}