feat: add examples and enhance MEV protection services

This commit adds comprehensive examples for various trading operations including:
- PumpFun copy and sniper trading
- Bonk copy and sniper trading
- PumpSwap trading
- Raydium CPMM and AMM V4 trading
- Middleware system demonstration
- Event subscription

Enhanced MEV protection services with FlashBlock and Node1 integration.
Updated instruction modules for bonk, pumpswap, and raydium_cpmm.
Improved SWQOS modules for better transaction handling.
This commit is contained in:
ysq
2025-08-25 00:21:15 +08:00
parent 90ea46ea3f
commit c10406915c
36 changed files with 2049 additions and 1562 deletions
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "bonk_copy_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
+186
View File
@@ -0,0 +1,186 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use sol_trade_sdk::solana_streamer_sdk::match_event;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
AccountFilter, TransactionFilter,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
trading::{core::params::BonkParams, factory::DexType},
SolanaTrade,
};
use solana_sdk::signer::Signer;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use spl_associated_token_account::get_associated_token_address;
// Global static flag to ensure transaction is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to GRPC events...");
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
)?;
let callback = create_event_callback();
let protocols = vec![Protocol::Bonk];
// Filter accounts
let account_include = vec![
BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID
];
let account_exclude = vec![];
let account_required = vec![];
// Listen to transaction data
let transaction_filter = TransactionFilter {
account_include: account_include.clone(),
account_exclude,
account_required,
};
// Listen to account data belonging to owner programs -> account event monitoring
let account_filter = AccountFilter { account: vec![], owner: vec![] };
// listen to specific event type
let event_type_filter = EventTypeFilter {
include: vec![
EventType::BonkBuyExactIn,
EventType::BonkSellExactIn,
EventType::BonkBuyExactOut,
EventType::BonkSellExactOut,
],
};
grpc.subscribe_events_immediate(
protocols,
None,
transaction_filter,
account_filter,
Some(event_type_filter),
None,
callback,
)
.await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
BonkTradeEvent => |e: BonkTradeEvent| {
// Test code, only test one transaction
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
tokio::spawn(async move {
if let Err(err) = bonk_copy_trade_with_grpc(event_clone).await {
eprintln!("Error in copy trade: {:?}", err);
std::process::exit(0);
}
});
}
},
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Configure according to your needs
priority_fee.rpc_unit_limit = 150000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
/// Bonk sniper trade
/// This function demonstrates how to snipe a new token from a Bonk trade event
async fn bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()> {
println!("Testing Bonk trading...");
let client = create_solana_trade_client().await?;
let mint_pubkey = trade_info.base_token_mint;
let slippage_basis_points = Some(100);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
// Buy tokens
println!("Buying tokens from Bonk...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::Bonk,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(BonkParams::from_trade(trade_info.clone())),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from Bonk...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let account = get_associated_token_address(&payer, &mint_pubkey);
let balance = rpc.get_token_account_balance(&account).await?;
println!("Balance: {:?}", balance);
let amount_token = balance.amount.parse::<u64>().unwrap();
println!("Selling {} tokens", amount_token);
client
.sell(
DexType::Bonk,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(BonkParams::from_trade(trade_info.clone())),
None,
true,
)
.await?;
// Exit program
std::process::exit(0);
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "bonk_sniper_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
+160
View File
@@ -0,0 +1,160 @@
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::streaming::grpc::ClientConfig;
use sol_trade_sdk::solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc};
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
trading::{core::params::BonkParams, factory::DexType},
SolanaTrade,
};
use solana_sdk::signer::Signer;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use spl_associated_token_account::get_associated_token_address;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
/// Atomic flag to ensure the sniper trade is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
/// Main entry point - subscribes to Bonk events and executes sniper trades on token creation
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to ShredStream events...");
let shred_stream = ShredStreamGrpc::new("use_your_shred_stream_url_here".to_string()).await?;
let callback = create_event_callback();
let protocols = vec![Protocol::Bonk];
let event_type_filter = EventTypeFilter {
include: vec![
EventType::BonkBuyExactIn,
EventType::BonkBuyExactOut,
EventType::BonkSellExactIn,
EventType::BonkSellExactOut,
EventType::BonkInitialize,
EventType::BonkInitializeV2,
],
};
println!("Starting to listen for events, press Ctrl+C to stop...");
shred_stream.shredstream_subscribe(protocols, None, Some(event_type_filter), callback).await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
BonkTradeEvent => |e: BonkTradeEvent| {
// Only process developer token creation events
if !e.is_dev_create_token_trade {
return;
}
// Ensure we only execute the trade once using atomic compare-and-swap
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
// Spawn a new task to handle the trading operation
tokio::spawn(async move {
if let Err(err) = bonk_sniper_trade_with_shreds(event_clone).await {
eprintln!("Error in sniper trade: {:?}", err);
std::process::exit(0);
}
});
}
},
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Set RPC unit limit based on your requirements
priority_fee.rpc_unit_limit = 150000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
/// Execute Bonk sniper trading strategy based on received token creation event
/// This function buys tokens immediately after creation and then sells all tokens
async fn bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<()> {
println!("Testing Bonk trading...");
let client = create_solana_trade_client().await?;
let mint_pubkey = trade_info.base_token_mint;
let slippage_basis_points = Some(300);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
// Buy tokens
println!("Buying tokens from Bonk...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::Bonk,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(BonkParams::from_dev_trade(trade_info.clone())),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from Bonk...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let account = get_associated_token_address(&payer, &mint_pubkey);
let balance = rpc.get_token_account_balance(&account).await?;
println!("Balance: {:?}", balance);
let amount_token = balance.amount.parse::<u64>().unwrap();
println!("Selling {} tokens", amount_token);
client
.sell(
DexType::Bonk,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(BonkParams::immediate_sell(
trade_info.base_token_program,
trade_info.platform_config,
trade_info.platform_associated_account,
trade_info.creator_associated_account,
)),
None,
true,
)
.await?;
// Exit program after completing the trade
std::process::exit(0);
}
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "event_subscription"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
tokio = { version = "1", features = ["full"] }
+181
View File
@@ -0,0 +1,181 @@
use sol_trade_sdk::solana_streamer_sdk::{
match_event,
streaming::{
event_parser::{
common::{filter::EventTypeFilter, EventType},
protocols::{
bonk::{parser::BONK_PROGRAM_ID, BonkPoolCreateEvent, BonkTradeEvent},
pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent},
pumpswap::{
parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
},
raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID,
raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID,
raydium_cpmm::{parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmSwapEvent},
},
Protocol, UnifiedEvent,
},
yellowstone_grpc::{AccountFilter, TransactionFilter},
ShredStreamGrpc, YellowstoneGrpc,
},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("This example demonstrates how to subscribe to events using Yellowstone gRPC and ShredStream.");
println!("You can choose which example to run by uncommenting the relevant function call.");
// Uncomment one of these to run the example:
test_grpc().await?; // Use public Yellowstone gRPC endpoint
// test_shreds().await?; // Use local ShredStream endpoint
Ok(())
}
/// Subscribe to events using Yellowstone gRPC
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to GRPC events...");
// Initialize gRPC client with public endpoint
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None, // No auth token needed
)?;
let callback = create_event_callback();
// Define protocols to monitor
let protocols =
vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm];
// Define program IDs to monitor
let account_include = vec![
PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID
PUMPSWAP_PROGRAM_ID.to_string(), // Listen to pumpswap program ID
BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID
RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID
RAYDIUM_CLMM_PROGRAM_ID.to_string(), // Listen to raydium_clmm program ID
RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // Listen to raydium_amm_v4 program ID
];
let account_exclude = vec![];
let account_required = vec![];
// Configure transaction filter
let transaction_filter = TransactionFilter {
account_include: account_include.clone(),
account_exclude,
account_required,
};
// Configure account filter for program-owned accounts
let account_filter = AccountFilter { account: vec![], owner: account_include.clone() };
// Configure event type filter (all events)
let event_type_filter = None;
// For specific events only:
// let event_type_filter =
// EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] };
println!("Starting to listen for events, press Ctrl+C to stop...");
// Start subscription
grpc.subscribe_events_immediate(
protocols,
None,
transaction_filter,
account_filter,
event_type_filter,
None,
callback,
)
.await?;
// Wait for termination signal
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Subscribe to events using ShredStream
async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to ShredStream events...");
// Initialize ShredStream client with local endpoint
let shred_stream = ShredStreamGrpc::new("http://127.0.0.1:10800".to_string()).await?;
let callback = create_event_callback();
// Define protocols to monitor
let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk];
// Configure event type filter (all events)
let event_type_filter = None;
// For specific events only:
// let event_type_filter =
// EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] };
println!("Starting to listen for events, press Ctrl+C to stop...");
// Start subscription
shred_stream
.shredstream_subscribe(
protocols,
None, // No slot range specified
event_type_filter,
callback,
)
.await?;
// Wait for termination signal
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
// Process events using match_event! macro
match_event!(event, {
// Bonk protocol events
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol);
},
BonkTradeEvent => |e: BonkTradeEvent| {
println!("BonkTradeEvent: {:?}", e);
},
// PumpFun protocol events
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
println!("PumpFunTradeEvent: {:?}", e);
},
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
println!("PumpFunCreateTokenEvent: {:?}", e);
},
// PumpSwap protocol events
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
println!("Buy event: {:?}", e);
},
PumpSwapSellEvent => |e: PumpSwapSellEvent| {
println!("Sell event: {:?}", e);
},
PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| {
println!("CreatePool event: {:?}", e);
},
PumpSwapDepositEvent => |e: PumpSwapDepositEvent| {
println!("Deposit event: {:?}", e);
},
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
println!("Withdraw event: {:?}", e);
},
// Raydium protocol events
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
println!("RaydiumCpmmSwapEvent: {:?}", e);
},
// For more events and documentation, please refer to https://github.com/0xfnzero/solana-streamer
});
}
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "middleware_system"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
tokio = { version = "1", features = ["full"] }
anyhow = "1.0.79"
+110
View File
@@ -0,0 +1,110 @@
use anyhow::Result;
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::{SwqosConfig, SwqosRegion},
trading::{
core::params::PumpSwapParams, factory::DexType, middleware::builtin::LoggingMiddleware,
InstructionMiddleware, MiddlewareManager,
},
SolanaTrade,
};
use solana_sdk::{
commitment_config::CommitmentConfig, instruction::Instruction, pubkey::Pubkey,
signature::Keypair,
};
use std::{str::FromStr, sync::Arc};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
test_middleware().await?;
Ok(())
}
/// Custom middleware
#[derive(Clone)]
pub struct CustomMiddleware;
impl InstructionMiddleware for CustomMiddleware {
fn name(&self) -> &'static str {
"CustomMiddleware"
}
fn process_protocol_instructions(
&self,
protocol_instructions: Vec<Instruction>,
protocol_name: String,
is_buy: bool,
) -> Result<Vec<Instruction>> {
// do anything you want here
// you can modify the instructions here
Ok(protocol_instructions)
}
fn process_full_instructions(
&self,
full_instructions: Vec<Instruction>,
protocol_name: String,
is_buy: bool,
) -> Result<Vec<Instruction>> {
// do anything you want here
// you can modify the instructions here
Ok(full_instructions)
}
fn clone_box(&self) -> Box<dyn InstructionMiddleware> {
Box::new(self.clone())
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
// In real transactions, use your own private key to initialize the payer
let payer = Keypair::new();
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: PriorityFee::default(),
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
async fn test_middleware() -> AnyResult<()> {
let mut client = create_solana_trade_client().await?;
// SDK example middleware that prints instruction information
// You can reference LoggingMiddleware to implement the InstructionMiddleware trait for your own middleware
let middleware_manager = MiddlewareManager::new().add_middleware(Box::new(CustomMiddleware));
client = client.with_middleware_manager(middleware_manager);
let mint_pubkey = Pubkey::from_str("pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn")?;
let buy_sol_cost = 100_000;
let slippage_basis_points = Some(100);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
let pool_address = Pubkey::from_str("539m4mVWt6iduB6W8rDGPMarzNCMesuqY5eUTiiYHAgR")?;
client
.buy(
DexType::PumpSwap,
mint_pubkey,
buy_sol_cost,
slippage_basis_points,
recent_blockhash,
None,
Box::new(PumpSwapParams::from_pool_address_by_rpc(&client.rpc, &pool_address).await?),
None,
true,
)
.await?;
println!("tip: This transaction will not succeed because we're using a test account. You can modify the code to initialize the payer with your own private key");
Ok(())
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "pumpfun_copy_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
+183
View File
@@ -0,0 +1,183 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use sol_trade_sdk::solana_streamer_sdk::match_event;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
AccountFilter, TransactionFilter,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
trading::{core::params::PumpFunParams, factory::DexType},
SolanaTrade,
};
use solana_sdk::signer::Signer;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use spl_associated_token_account::get_associated_token_address;
// Global static flag to ensure transaction is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to GRPC events...");
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
)?;
let callback = create_event_callback();
let protocols = vec![Protocol::PumpFun];
// Filter accounts
let account_include = vec![
PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID
];
let account_exclude = vec![];
let account_required = vec![];
// Listen to transaction data
let transaction_filter = TransactionFilter {
account_include: account_include.clone(),
account_exclude,
account_required,
};
// Listen to account data belonging to owner programs -> account event monitoring
let account_filter = AccountFilter { account: vec![], owner: vec![] };
// listen to specific event type
let event_type_filter =
EventTypeFilter { include: vec![EventType::PumpFunBuy, EventType::PumpFunSell] };
grpc.subscribe_events_immediate(
protocols,
None,
transaction_filter,
account_filter,
Some(event_type_filter),
None,
callback,
)
.await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
// Test code, only test one transaction
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
tokio::spawn(async move {
if let Err(err) = pumpfun_copy_trade_with_grpc(event_clone).await {
eprintln!("Error in copy trade: {:?}", err);
std::process::exit(0);
}
});
}
},
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Configure according to your needs
priority_fee.rpc_unit_limit = 100000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
/// PumpFun sniper trade
/// This function demonstrates how to snipe a new token from a PumpFun trade event
async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResult<()> {
println!("Testing PumpFun trading...");
let client = create_solana_trade_client().await?;
let mint_pubkey = trade_info.mint;
let slippage_basis_points = Some(100);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
// Buy tokens
println!("Buying tokens from PumpFun...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::PumpFun,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(PumpFunParams::from_trade(&trade_info, None)),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from PumpFun...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let account = get_associated_token_address(&payer, &mint_pubkey);
let balance = rpc.get_token_account_balance(&account).await?;
println!("Balance: {:?}", balance);
let amount_token = balance.amount.parse::<u64>().unwrap();
println!("Selling {} tokens", amount_token);
client
.sell(
DexType::PumpFun,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(PumpFunParams::from_trade(&trade_info, Some(true))),
None,
true,
)
.await?;
// PumpFunParams can also be set as PumpFunParams::immediate_sell(creator_vault, close_token_account_when_sell)
// creator_vault can be obtained from the trade event
// Exit program
std::process::exit(0);
}
@@ -0,0 +1,10 @@
[package]
name = "pumpfun_sniper_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
+151
View File
@@ -0,0 +1,151 @@
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc};
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
trading::{core::params::PumpFunParams, factory::DexType},
SolanaTrade,
};
use solana_sdk::signer::Signer;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use spl_associated_token_account::get_associated_token_address;
use std::mem::take;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
/// Atomic flag to ensure the sniper trade is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
/// Main entry point - subscribes to PumpFun events and executes sniper trades on token creation
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to ShredStream events...");
let shred_stream = ShredStreamGrpc::new("use_your_shred_stream_url_here".to_string()).await?;
let callback = create_event_callback();
let protocols = vec![Protocol::PumpFun];
let event_type_filter = EventTypeFilter {
include: vec![EventType::PumpFunBuy, EventType::PumpFunSell, EventType::PumpFunCreateToken],
};
println!("Starting to listen for events, press Ctrl+C to stop...");
shred_stream.shredstream_subscribe(protocols, None, Some(event_type_filter), callback).await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
// Only process developer token creation events
if !e.is_dev_create_token_trade {
return;
}
// Ensure we only execute the trade once using atomic compare-and-swap
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
// Spawn a new task to handle the trading operation
tokio::spawn(async move {
if let Err(err) = pumpfun_sniper_trade_with_shreds(event_clone).await {
eprintln!("Error in copy trade: {:?}", err);
std::process::exit(0);
}
});
}
},
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Set RPC unit limit based on your requirements
priority_fee.rpc_unit_limit = 100000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
/// Execute PumpFun sniper trading strategy based on received token creation event
/// This function buys tokens immediately after creation and then sells all tokens
async fn pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyResult<()> {
println!("Testing PumpFun trading...");
let client = create_solana_trade_client().await?;
let mint_pubkey = trade_info.mint;
let slippage_basis_points = Some(300);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
// Buy tokens
println!("Buying tokens from PumpFun...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::PumpFun,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(PumpFunParams::from_dev_trade(&trade_info, None)),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from PumpFun...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let account = get_associated_token_address(&payer, &mint_pubkey);
let balance = rpc.get_token_account_balance(&account).await?;
println!("Balance: {:?}", balance);
let amount_token = balance.amount.parse::<u64>().unwrap();
println!("Selling {} tokens", amount_token);
client
.sell(
DexType::PumpFun,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(PumpFunParams::immediate_sell(trade_info.creator_vault, true)),
None,
true,
)
.await?;
// PumpFunParams can also be set as PumpFunParams::immediate_sell(creator_vault, close_token_account_when_sell)
// creator_vault can be obtained from the trade event
// Exit program after completing the trade
std::process::exit(0);
}
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "pumpswap_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
+224
View File
@@ -0,0 +1,224 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{
common::EventType, protocols::pumpswap::PumpSwapSellEvent,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
AccountFilter, TransactionFilter,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
use sol_trade_sdk::solana_streamer_sdk::{
match_event, streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID,
};
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
trading::{core::params::PumpSwapParams, factory::DexType},
SolanaTrade,
};
use sol_trade_sdk::{
constants::pumpswap::accounts,
solana_streamer_sdk::streaming::event_parser::{
common::filter::EventTypeFilter, protocols::pumpswap::PumpSwapBuyEvent,
},
};
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use solana_sdk::{pubkey::Pubkey, signer::Signer};
use spl_associated_token_account::get_associated_token_address_with_program_id;
// Global static flag to ensure transaction is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to GRPC events...");
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
)?;
let callback = create_event_callback();
let protocols = vec![Protocol::PumpSwap];
// Filter accounts
let account_include = vec![
PUMPSWAP_PROGRAM_ID.to_string(), // Listen to PumpSwap program ID
];
let account_exclude = vec![];
let account_required = vec![];
// Listen to transaction data
let transaction_filter = TransactionFilter {
account_include: account_include.clone(),
account_exclude,
account_required,
};
// Listen to account data belonging to owner programs -> account event monitoring
let account_filter = AccountFilter { account: vec![], owner: vec![] };
// listen to specific event type
let event_type_filter =
EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] };
grpc.subscribe_events_immediate(
protocols,
None,
transaction_filter,
account_filter,
Some(event_type_filter),
None,
callback,
)
.await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
if e.base_mint == accounts::WSOL_TOKEN_ACCOUNT || e.quote_mint == accounts::WSOL_TOKEN_ACCOUNT {
// Test code, only test one transaction
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
tokio::spawn(async move {
if let Err(err) = pumpswap_trade_with_grpc_buy_event(event_clone).await {
eprintln!("Error in trade: {:?}", err);
std::process::exit(0);
}
});
}
}
},
PumpSwapSellEvent => |e: PumpSwapSellEvent| {
if e.base_mint == accounts::WSOL_TOKEN_ACCOUNT || e.quote_mint == accounts::WSOL_TOKEN_ACCOUNT {
// Test code, only test one transaction
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
tokio::spawn(async move {
if let Err(err) = pumpswap_trade_with_grpc_sell_event(event_clone).await {
eprintln!("Error in trade: {:?}", err);
std::process::exit(0);
}
});
}
}
}
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Configure according to your needs
priority_fee.rpc_unit_limit = 150000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
async fn pumpswap_trade_with_grpc_buy_event(trade_info: PumpSwapBuyEvent) -> AnyResult<()> {
let params = PumpSwapParams::from_buy_trade(&trade_info);
let mint = if trade_info.base_mint == accounts::WSOL_TOKEN_ACCOUNT {
trade_info.quote_mint
} else {
trade_info.base_mint
};
pumpswap_trade_with_grpc(mint, params).await?;
Ok(())
}
async fn pumpswap_trade_with_grpc_sell_event(trade_info: PumpSwapSellEvent) -> AnyResult<()> {
let params = PumpSwapParams::from_sell_trade(&trade_info);
let mint = if trade_info.base_mint == accounts::WSOL_TOKEN_ACCOUNT {
trade_info.quote_mint
} else {
trade_info.base_mint
};
pumpswap_trade_with_grpc(mint, params).await?;
Ok(())
}
async fn pumpswap_trade_with_grpc(mint_pubkey: Pubkey, params: PumpSwapParams) -> AnyResult<()> {
println!("Testing PumpSwap trading...");
let client = create_solana_trade_client().await?;
let slippage_basis_points = Some(500);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
// Buy tokens
println!("Buying tokens from PumpSwap...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::PumpSwap,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(params.clone()),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from PumpSwap...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let program_id = if params.base_mint == mint_pubkey {
params.base_token_program
} else {
params.quote_token_program
};
let account = get_associated_token_address_with_program_id(&payer, &mint_pubkey, &program_id);
let balance = rpc.get_token_account_balance(&account).await?;
let amount_token = balance.amount.parse::<u64>().unwrap();
client
.sell(
DexType::PumpSwap,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(params.clone()),
None,
true,
)
.await?;
// Exit program
std::process::exit(0);
}
@@ -0,0 +1,10 @@
[package]
name = "raydium_amm_v4_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
+196
View File
@@ -0,0 +1,196 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use sol_trade_sdk::{constants::raydium_amm_v4::accounts, solana_streamer_sdk::{match_event, streaming::event_parser::protocols::raydium_amm_v4::RaydiumAmmV4SwapEvent}, trading::common::get_multi_token_balances};
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID;
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
AccountFilter, TransactionFilter,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
trading::{core::params::RaydiumAmmV4Params, factory::DexType},
SolanaTrade,
};
use solana_sdk::signer::Signer;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use spl_associated_token_account::get_associated_token_address;
// Global static flag to ensure transaction is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to GRPC events...");
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
)?;
let callback = create_event_callback();
let protocols = vec![Protocol::RaydiumAmmV4];
// Filter accounts
let account_include = vec![
RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // Listen to raydium_amm_v4 program ID
];
let account_exclude = vec![];
let account_required = vec![];
// Listen to transaction data
let transaction_filter = TransactionFilter {
account_include: account_include.clone(),
account_exclude,
account_required,
};
// Listen to account data belonging to owner programs -> account event monitoring
let account_filter = AccountFilter { account: vec![], owner: vec![] };
// listen to specific event type
let event_type_filter = EventTypeFilter {
include: vec![EventType::RaydiumAmmV4SwapBaseIn, EventType::RaydiumAmmV4SwapBaseOut],
};
grpc.subscribe_events_immediate(
protocols,
None,
transaction_filter,
account_filter,
Some(event_type_filter),
None,
callback,
)
.await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
// Test code, only test one transaction
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
tokio::spawn(async move {
if let Err(err) = raydium_amm_v4_copy_trade_with_grpc(event_clone).await {
eprintln!("Error in copy trade: {:?}", err);
std::process::exit(0);
}
});
}
},
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Configure according to your needs
priority_fee.rpc_unit_limit = 150000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
/// Raydium_amm_v4 sniper trade
/// This function demonstrates how to snipe a new token from a Raydium_amm_v4 trade event
async fn raydium_amm_v4_copy_trade_with_grpc(trade_info: RaydiumAmmV4SwapEvent) -> AnyResult<()> {
println!("Testing Raydium_amm_v4 trading...");
let client = create_solana_trade_client().await?;
let slippage_basis_points = Some(100);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
let amm_info =
sol_trade_sdk::trading::raydium_amm_v4::common::fetch_amm_info(&client.rpc, trade_info.amm)
.await?;
let (coin_reserve, pc_reserve) =
get_multi_token_balances(&client.rpc, &amm_info.token_coin, &amm_info.token_pc).await?;
let mint_pubkey = if amm_info.pc_mint == accounts::WSOL_TOKEN_ACCOUNT {
amm_info.coin_mint
} else {
amm_info.pc_mint
};
let params = RaydiumAmmV4Params::from_amm_info_and_reserves(
trade_info.amm,
amm_info,
coin_reserve,
pc_reserve,
);
// Buy tokens
println!("Buying tokens from Raydium_amm_v4...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::RaydiumAmmV4,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(params),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from Raydium_amm_v4...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let account = get_associated_token_address(&payer, &mint_pubkey);
let balance = rpc.get_token_account_balance(&account).await?;
println!("Balance: {:?}", balance);
let amount_token = balance.amount.parse::<u64>().unwrap();
println!("Selling {} tokens", amount_token);
let params = RaydiumAmmV4Params::from_amm_address_by_rpc(&client.rpc, trade_info.amm).await?;
client
.sell(
DexType::RaydiumAmmV4,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(params),
None,
true,
)
.await?;
// Exit program
std::process::exit(0);
}
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "raydium_cpmm_trading"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
spl-associated-token-account = "7.0.0"
tokio = { version = "1", features = ["full"] }
+199
View File
@@ -0,0 +1,199 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
AccountFilter, TransactionFilter,
};
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
use sol_trade_sdk::solana_streamer_sdk::{
match_event, streaming::event_parser::protocols::raydium_cpmm::RaydiumCpmmSwapEvent,
};
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::SwqosConfig,
SolanaTrade,
};
use sol_trade_sdk::{
constants::raydium_cpmm::accounts,
solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID,
};
use sol_trade_sdk::{
solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter,
trading::factory::DexType,
};
use sol_trade_sdk::{
solana_streamer_sdk::streaming::event_parser::common::EventType,
trading::core::params::RaydiumCpmmParams,
};
use solana_sdk::signer::Signer;
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use spl_associated_token_account::get_associated_token_address;
// Global static flag to ensure transaction is executed only once
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Subscribing to GRPC events...");
let grpc = YellowstoneGrpc::new(
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
None,
)?;
let callback = create_event_callback();
let protocols = vec![Protocol::RaydiumCpmm];
// Filter accounts
let account_include = vec![
RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID
];
let account_exclude = vec![];
let account_required = vec![];
// Listen to transaction data
let transaction_filter = TransactionFilter {
account_include: account_include.clone(),
account_exclude,
account_required,
};
// Listen to account data belonging to owner programs -> account event monitoring
let account_filter = AccountFilter { account: vec![], owner: vec![] };
// listen to specific event type
let event_type_filter = EventTypeFilter {
include: vec![EventType::RaydiumCpmmSwapBaseInput, EventType::RaydiumCpmmSwapBaseOutput],
};
grpc.subscribe_events_immediate(
protocols,
None,
transaction_filter,
account_filter,
Some(event_type_filter),
None,
callback,
)
.await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
/// Create an event callback function that handles different types of events
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|event: Box<dyn UnifiedEvent>| {
match_event!(event, {
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
// Test code, only test one transaction
if !ALREADY_EXECUTED.swap(true, Ordering::SeqCst) {
let event_clone = e.clone();
tokio::spawn(async move {
if let Err(err) = raydium_cpmm_copy_trade_with_grpc(event_clone).await {
eprintln!("Error in copy trade: {:?}", err);
std::process::exit(0);
}
});
}
},
});
}
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
let swqos_configs = vec![SwqosConfig::Default(rpc_url.clone())];
let mut priority_fee = PriorityFee::default();
// Configure according to your needs
priority_fee.rpc_unit_limit = 150000;
let trade_config = TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: priority_fee,
swqos_configs,
lookup_table_key: None,
};
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
/// Raydium_cpmm sniper trade
/// This function demonstrates how to snipe a new token from a Raydium_cpmm trade event
async fn raydium_cpmm_copy_trade_with_grpc(trade_info: RaydiumCpmmSwapEvent) -> AnyResult<()> {
println!("Testing Raydium_cpmm trading...");
let client = create_solana_trade_client().await?;
let mint_pubkey = if trade_info.input_token_mint == accounts::WSOL_TOKEN_ACCOUNT {
trade_info.output_token_mint
} else {
trade_info.input_token_mint
};
let slippage_basis_points = Some(100);
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
let buy_params =
RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &trade_info.pool_state).await?;
// Buy tokens
println!("Buying tokens from Raydium_cpmm...");
let buy_sol_amount = 100_000;
client
.buy(
DexType::RaydiumCpmm,
mint_pubkey,
buy_sol_amount,
slippage_basis_points,
recent_blockhash,
None,
Box::new(buy_params),
None,
true,
)
.await?;
// Sell tokens
println!("Selling tokens from Raydium_cpmm...");
let rpc = client.rpc.clone();
let payer = client.payer.pubkey();
let account = get_associated_token_address(&payer, &mint_pubkey);
let balance = rpc.get_token_account_balance(&account).await?;
println!("Balance: {:?}", balance);
let amount_token = balance.amount.parse::<u64>().unwrap();
let sell_params =
RaydiumCpmmParams::from_pool_address_by_rpc(&client.rpc, &trade_info.pool_state).await?;
println!("Selling {} tokens", amount_token);
client
.sell(
DexType::RaydiumCpmm,
mint_pubkey,
amount_token,
slippage_basis_points,
recent_blockhash,
None,
false,
Box::new(sell_params),
None,
true,
)
.await?;
// Exit program
std::process::exit(0);
}
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "trading_client"
version = "0.1.0"
edition = "2021"
[dependencies]
sol-trade-sdk = { path = "../.." }
solana-sdk = "2.3.0"
tokio = { version = "1", features = ["full"] }
+59
View File
@@ -0,0 +1,59 @@
use sol_trade_sdk::{
common::{AnyResult, PriorityFee, TradeConfig},
swqos::{SwqosConfig, SwqosRegion},
SolanaTrade,
};
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = test_create_solana_trade_client().await?;
println!("Successfully created SolanaTrade client!");
Ok(())
}
/// Create SolanaTrade client
/// Initializes a new SolanaTrade client with configuration
async fn test_create_solana_trade_client() -> AnyResult<SolanaTrade> {
println!("Creating SolanaTrade client...");
let payer = Keypair::new();
let rpc_url = "https://mainnet.helius-rpc.com/?api-key=xxxxxx".to_string();
println!("rpc_url: {}", rpc_url);
let swqos_configs = create_swqos_configs(&rpc_url);
let trade_config = create_trade_config(rpc_url, swqos_configs);
let solana_trade_client = SolanaTrade::new(Arc::new(payer), trade_config).await;
println!("SolanaTrade client created successfully!");
Ok(solana_trade_client)
}
fn create_swqos_configs(rpc_url: &str) -> Vec<SwqosConfig> {
vec![
// First parameter is UUID, pass empty string if no UUID
SwqosConfig::Jito("your uuid".to_string(), SwqosRegion::Frankfurt),
SwqosConfig::NextBlock("your api_token".to_string(), SwqosRegion::Frankfurt),
SwqosConfig::Bloxroute("your api_token".to_string(), SwqosRegion::Frankfurt),
SwqosConfig::ZeroSlot("your api_token".to_string(), SwqosRegion::Frankfurt),
SwqosConfig::Temporal("your api_token".to_string(), SwqosRegion::Frankfurt),
// Add tg official customer https://t.me/FlashBlock_Official to get free FlashBlock key
SwqosConfig::FlashBlock("your api_token".to_string(), SwqosRegion::Frankfurt),
// Add tg official customer https://t.me/node1_me to get free Node1 key
SwqosConfig::Node1("your api_token".to_string(), SwqosRegion::Frankfurt),
SwqosConfig::Default(rpc_url.to_string()),
]
}
fn create_trade_config(rpc_url: String, swqos_configs: Vec<SwqosConfig>) -> TradeConfig {
TradeConfig {
rpc_url,
commitment: CommitmentConfig::confirmed(),
priority_fee: PriorityFee::default(),
swqos_configs,
lookup_table_key: None,
}
}