refactor: rename PumpFun to SolanaTrade and improve API consistency

- Rename PumpFun class to SolanaTrade to better reflect its expanded functionality
- Rename copy_buy methods to buy for better API clarity
- Update ShredStream and Yellowstone gRPC subscription examples
- Add bonding_curve parameter to trading methods
- Improve documentation with updated code examples
- Update README files with new features and API changes
This commit is contained in:
ysq
2025-06-24 20:56:15 +08:00
parent a0579a338b
commit bfd6809855
5 changed files with 496 additions and 302 deletions
+67 -8
View File
@@ -37,7 +37,7 @@ use crate::trading::BuyWithTipParams;
use crate::trading::SellParams;
use crate::trading::SellWithTipParams;
pub struct PumpFun {
pub struct SolanaTrade {
pub payer: Arc<Keypair>,
pub rpc: Arc<SolanaRpcClient>,
pub fee_clients: Vec<Arc<FeeClient>>,
@@ -45,9 +45,9 @@ pub struct PumpFun {
pub cluster: Cluster,
}
static INSTANCE: Mutex<Option<Arc<PumpFun>>> = Mutex::new(None);
static INSTANCE: Mutex<Option<Arc<SolanaTrade>>> = Mutex::new(None);
impl Clone for PumpFun {
impl Clone for SolanaTrade {
fn clone(&self) -> Self {
Self {
payer: self.payer.clone(),
@@ -59,7 +59,7 @@ impl Clone for PumpFun {
}
}
impl PumpFun {
impl SolanaTrade {
#[inline]
pub async fn new(
payer: Arc<Keypair>,
@@ -211,6 +211,7 @@ impl PumpFun {
buy_sol_cost: u64,
slippage_basis_points: Option<u64>,
recent_blockhash: Hash,
bonding_curve: Option<Arc<BondingCurveAccount>>,
) -> Result<(), anyhow::Error> {
pumpfun::buy::buy(
self.rpc.clone(),
@@ -222,12 +223,12 @@ impl PumpFun {
self.priority_fee.clone(),
self.cluster.clone().lookup_table_key,
recent_blockhash,
None,
bonding_curve,
SNIPER_BUY.to_string(),
).await
}
pub async fn copy_buy(
pub async fn buy(
&self,
mint: Pubkey,
creator: Pubkey,
@@ -274,6 +275,64 @@ impl PumpFun {
}
}
pub async fn buy_use_buy_params(
&self,
buy_params: BuyWithTipParams,
custom_buy_tip_fee: Option<f64>,
) -> Result<(), anyhow::Error> {
let mut priority_fee = buy_params.priority_fee.clone();
if custom_buy_tip_fee.is_some() {
priority_fee.buy_tip_fee = custom_buy_tip_fee.unwrap();
priority_fee.buy_tip_fees = vec![custom_buy_tip_fee.unwrap()];
}
let mint = buy_params.mint;
let creator = buy_params.creator;
let buy_sol_cost = buy_params.amount_sol;
let slippage_basis_points = buy_params.slippage_basis_points;
let recent_blockhash = buy_params.recent_blockhash;
if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<PumpFunParams>() {
pumpfun::buy::buy(
self.rpc.clone(),
self.payer.clone(),
mint,
creator,
buy_sol_cost,
slippage_basis_points,
self.priority_fee.clone(),
self.cluster.clone().lookup_table_key,
recent_blockhash,
protocol_params.bonding_curve.clone(),
COPY_BUY.to_string(),
).await
} else if let Some(protocol_params) = buy_params
.protocol_params
.as_any()
.downcast_ref::<PumpSwapParams>() {
pumpswap::buy::buy(
self.rpc.clone(),
self.payer.clone(),
mint,
creator,
buy_sol_cost,
slippage_basis_points,
self.priority_fee.clone(),
self.cluster.clone().lookup_table_key,
recent_blockhash,
protocol_params.pool.clone(),
protocol_params.pool_base_token_account.clone(),
protocol_params.pool_quote_token_account.clone(),
protocol_params.user_base_token_account.clone(),
protocol_params.user_quote_token_account.clone(),
protocol_params.auto_handle_wsol,
).await
} else {
return Err(anyhow::anyhow!("Invalid protocol params for PumpFun"));
}
}
/// Buy tokens using Jito
pub async fn sniper_buy_with_tip(
&self,
@@ -299,7 +358,7 @@ impl PumpFun {
).await
}
pub async fn copy_buy_with_tip_use_buy_params(
pub async fn buy_with_tip_use_buy_params(
&self,
buy_params: BuyWithTipParams,
custom_buy_tip_fee: Option<f64>,
@@ -358,7 +417,7 @@ impl PumpFun {
}
}
pub async fn copy_buy_with_tip(
pub async fn buy_with_tip(
&self,
mint: Pubkey,
creator: Pubkey,
+135 -55
View File
@@ -1,18 +1,16 @@
use std::{str::FromStr, sync::Arc};
use sol_trade_sdk::{
common::{
accounts::BondingCurveAccount, common::{
pumpfun::{
self,
logs_events::PumpfunEvent,
logs_subscribe::{stop_subscription, tokens_subscription},
logs_subscribe::{stop_subscription, tokens_subscription}, TradeInfo,
},
pumpswap::{self, PumpSwapEvent},
raydium::{self, RaydiumEvent},
AnyResult, Cluster, PriorityFee,
},
grpc::{ShredStreamGrpc, YellowstoneGrpc},
PumpFun,
}, constants::pumpfun::global_constants::TOKEN_TOTAL_SUPPLY, grpc::{ShredStreamGrpc, YellowstoneGrpc}, pumpfun::common::get_bonding_curve_pda, SolanaTrade
};
use solana_client::rpc_client::RpcClient;
use solana_hash::Hash;
@@ -29,7 +27,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// test_pumpswap_with_grpc().await?;
// test_raydium_with_shreds().await?;
// test_raydium_with_grpc().await?;
test_sell().await?;
// test_pumpfun_sniper().await?;
// test_pumpfun().await?;
test_pumpswap().await?;
Ok(())
}
@@ -257,7 +257,7 @@ async fn test_raydium_with_grpc() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
async fn test_sell() -> AnyResult<()> {
async fn test_pumpfun_sniper() -> AnyResult<()> {
let payer = Keypair::new();
// Define cluster configuration
let cluster = Cluster {
@@ -279,17 +279,137 @@ async fn test_sell() -> AnyResult<()> {
lookup_table_key: None,
use_rpc: true,
};
let pumpfun_client = PumpFun::new(Arc::new(payer), &cluster).await;
let creator = Pubkey::from_str("43tFsRkZyhE1JXGivxWthApHPqWCnDqs7E1ZNdy7gkNz")?;
let solana_trade_client = SolanaTrade::new(Arc::new(payer), &cluster).await;
let creator = Pubkey::from_str("xxx")?; // dev account
let buy_sol_cost = 500_000; // 0.0005 SOL
let slippage_basis_points = Some(100);
let rpc = RpcClient::new(cluster.rpc_url);
let recent_blockhash = rpc.get_latest_blockhash().unwrap();
let mint_pubkey = Pubkey::from_str("xxx")?; // token mint
println!("Sniping buy tokens from PumpFun...");
// get bonding curve
let dev_buy_token = 100_000; // test value
let dev_cost_sol = 100_000; // test value
let bonding_curve = BondingCurveAccount::new(&mint_pubkey, dev_buy_token, dev_cost_sol, creator);
solana_trade_client
.sniper_buy(
mint_pubkey,
creator,
buy_sol_cost,
slippage_basis_points,
recent_blockhash,
Some(Arc::new(bonding_curve)),
)
.await?;
Ok(())
}
async fn test_pumpfun() -> AnyResult<()> {
let payer = Keypair::new();
// Define cluster configuration
let cluster = Cluster {
rpc_url: "https://mainnet.helius-rpc.com/?api-key=f2f194bb-6bd6-4f20-9a94-7fe0799ade0b"
.to_string(),
commitment: CommitmentConfig::confirmed(),
priority_fee: PriorityFee::default(),
use_jito: false,
use_zeroslot: false,
use_nozomi: false,
use_nextblock: false,
block_engine_url: "".to_string(),
zeroslot_url: "".to_string(),
zeroslot_auth_token: "".to_string(),
nozomi_url: "".to_string(),
nozomi_auth_token: "".to_string(),
nextblock_url: "".to_string(),
nextblock_auth_token: "".to_string(),
lookup_table_key: None,
use_rpc: true,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), &cluster).await;
let creator = Pubkey::from_str("xxx")?; // dev account
let buy_sol_cost = 500_000; // 0.0005 SOL
let slippage_basis_points = Some(100);
let rpc = RpcClient::new(cluster.rpc_url);
let recent_blockhash = rpc.get_latest_blockhash().unwrap();
let trade_platform = "pumpfun".to_string();
let mint_pubkey = Pubkey::from_str("xxx")?; // token mint
println!("Buying tokens from PumpFun...");
// get bonding curve
// Relevant on-chain information can be obtained from rpc/grpc
let virtual_token_reserves = 0;
let virtual_sol_reserves = 0;
let real_token_reserves = 0;
let real_sol_reserves = 0;
let bonding_curve = BondingCurveAccount {
discriminator: 0,
account: get_bonding_curve_pda(&mint_pubkey).unwrap(),
virtual_token_reserves: virtual_token_reserves,
virtual_sol_reserves: virtual_sol_reserves,
real_token_reserves: real_token_reserves,
real_sol_reserves: real_sol_reserves,
token_total_supply: TOKEN_TOTAL_SUPPLY,
complete: false,
creator: creator,
};
solana_trade_client
.buy(
mint_pubkey,
creator,
buy_sol_cost,
slippage_basis_points,
recent_blockhash,
Some(Arc::new(bonding_curve)),
trade_platform.clone(),
)
.await?;
// Sell 30% * amount_token quantity
// solana_trade_client
// .sell_by_percent(
// mint_pubkey,
// creator,
// 30,
// 100,
// recent_blockhash,
// trade_platform.clone(),
// )
// .await?;
Ok(())
}
async fn test_pumpswap() -> AnyResult<()> {
let payer = Keypair::new();
// Define cluster configuration
let cluster = Cluster {
rpc_url: "https://mainnet.helius-rpc.com/?api-key=f2f194bb-6bd6-4f20-9a94-7fe0799ade0b"
.to_string(),
commitment: CommitmentConfig::confirmed(),
priority_fee: PriorityFee::default(),
use_jito: false,
use_zeroslot: false,
use_nozomi: false,
use_nextblock: false,
block_engine_url: "".to_string(),
zeroslot_url: "".to_string(),
zeroslot_auth_token: "".to_string(),
nozomi_url: "".to_string(),
nozomi_auth_token: "".to_string(),
nextblock_url: "".to_string(),
nextblock_auth_token: "".to_string(),
lookup_table_key: None,
use_rpc: true,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), &cluster).await;
let creator = Pubkey::from_str("11111111111111111111111111111111")?; // dev account
let buy_sol_cost = 500_000; // 0.0005 SOL
let slippage_basis_points = Some(100);
let rpc = RpcClient::new(cluster.rpc_url);
let recent_blockhash = rpc.get_latest_blockhash().unwrap();
let trade_platform = "pumpswap".to_string();
let mint_pubkey = Pubkey::from_str("FMnWxuES8X7n33SJryf9MKbZNH57tPREtjWqCTMupump")?;
let mint_pubkey = Pubkey::from_str("2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv")?; // token mint
println!("Buying tokens from PumpSwap...");
pumpfun_client
.copy_buy(
solana_trade_client
.buy(
mint_pubkey,
creator,
buy_sol_cost,
@@ -299,56 +419,16 @@ async fn test_sell() -> AnyResult<()> {
trade_platform.clone(),
)
.await?;
// pumpfun_client
// Sell 30% * amount_token quantity
// solana_trade_client
// .sell_by_percent(
// mint_pubkey,
// creator,
// 30,
// 100,
// 0,
// recent_blockhash,
// trade_platform.clone(),
// )
// .await?;
Ok(())
}
async fn test_wss() -> AnyResult<()> {
println!("Starting token subscription\n");
let ws_url = "wss://api.mainnet-beta.solana.com";
// Set commitment
let commitment = CommitmentConfig::confirmed();
// Define callback function
let callback = |event: PumpfunEvent| match event {
PumpfunEvent::NewDevTrade(trade_info) => {
println!("Received new dev trade event: {:?}", trade_info);
}
PumpfunEvent::NewToken(token_info) => {
println!("Received new token event: {:?}", token_info);
}
PumpfunEvent::NewUserTrade(trade_info) => {
println!("Received new trade event: {:?}", trade_info);
}
PumpfunEvent::NewBotTrade(trade_info) => {
println!("Received new bot trade event: {:?}", trade_info);
}
PumpfunEvent::Error(err) => {
println!("Received error: {}", err);
}
};
// Start subscription
let subscription = tokens_subscription(ws_url, commitment, callback, None)
.await
.unwrap();
// Wait for a while to receive events
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
// Stop subscription
stop_subscription(subscription).await;
Ok(())
}
-1
View File
@@ -21,7 +21,6 @@ use crate::{
params::{BuyParams, PumpFunParams, SellParams},
traits::InstructionBuilder,
},
PumpFun,
};
/// PumpFun协议的指令构建器