2025-07-09 22:29:29 +08:00
pub mod common ;
2024-12-31 16:51:58 +08:00
pub mod constants ;
2025-07-09 22:29:29 +08:00
pub mod instruction ;
pub mod protos ;
2025-07-10 18:14:21 +08:00
pub mod swqos ;
2025-06-17 23:32:20 +08:00
pub mod trading ;
2025-07-10 18:14:21 +08:00
pub mod utils ;
2025-09-20 14:00:45 +08:00
use crate ::common ::nonce_cache ::DurableNonceInfo ;
2025-09-22 00:05:20 +08:00
use crate ::common ::TradeConfig ;
2025-09-07 17:22:58 +08:00
use crate ::constants ::trade ::trade ::DEFAULT_SLIPPAGE ;
2025-09-22 00:05:20 +08:00
use crate ::constants ::SOL_TOKEN_ACCOUNT ;
2025-09-22 01:09:41 +08:00
use crate ::constants ::USD1_TOKEN_ACCOUNT ;
2025-09-22 00:05:20 +08:00
use crate ::constants ::WSOL_TOKEN_ACCOUNT ;
2025-09-19 00:49:45 +08:00
use crate ::swqos ::SwqosClient ;
2025-09-17 11:14:15 +08:00
use crate ::swqos ::SwqosConfig ;
2025-07-10 18:14:21 +08:00
use crate ::trading ::core ::params ::BonkParams ;
use crate ::trading ::core ::params ::PumpFunParams ;
use crate ::trading ::core ::params ::PumpSwapParams ;
2025-08-19 18:07:21 +08:00
use crate ::trading ::core ::params ::RaydiumAmmV4Params ;
2025-07-16 22:54:23 +08:00
use crate ::trading ::core ::params ::RaydiumCpmmParams ;
2025-07-10 18:14:21 +08:00
use crate ::trading ::core ::traits ::ProtocolParams ;
2025-07-10 22:15:53 +08:00
use crate ::trading ::factory ::DexType ;
2025-09-19 00:49:45 +08:00
use crate ::trading ::MiddlewareManager ;
2025-09-22 00:05:20 +08:00
use crate ::trading ::SwapParams ;
2025-07-10 18:14:21 +08:00
use crate ::trading ::TradeFactory ;
2025-09-19 00:49:45 +08:00
use common ::SolanaRpcClient ;
2025-09-09 17:02:24 +08:00
use parking_lot ::Mutex ;
2025-07-10 18:14:21 +08:00
use rustls ::crypto ::{ ring ::default_provider , CryptoProvider };
use solana_sdk ::hash ::Hash ;
2025-09-19 00:49:45 +08:00
use solana_sdk ::signer ::Signer ;
2025-09-09 12:03:42 +08:00
use solana_sdk ::{ pubkey ::Pubkey , signature ::Keypair , signature ::Signature };
2025-09-19 00:49:45 +08:00
pub use solana_streamer_sdk ;
2025-02-21 20:47:28 +08:00
use std ::sync ::Arc ;
2025-01-14 19:18:48 +08:00
2025-09-19 00:49:45 +08:00
/// Main trading client for Solana DeFi protocols
///
/// `SolanaTrade` provides a unified interface for trading across multiple Solana DEXs
/// including PumpFun, PumpSwap, Bonk, Raydium AMM V4, and Raydium CPMM.
/// It manages RPC connections, transaction signing, and SWQOS (Solana Web Quality of Service) settings.
2025-06-24 20:56:15 +08:00
pub struct SolanaTrade {
2025-09-19 00:49:45 +08:00
/// The keypair used for signing all transactions
2024-12-31 16:51:58 +08:00
pub payer : Arc < Keypair > ,
2025-09-19 00:49:45 +08:00
/// RPC client for blockchain interactions
2025-04-02 13:39:59 +08:00
pub rpc : Arc < SolanaRpcClient > ,
2025-09-19 00:49:45 +08:00
/// SWQOS clients for transaction priority and routing
2025-09-17 11:14:15 +08:00
pub swqos_clients : Vec < Arc < SwqosClient >> ,
2025-09-19 00:49:45 +08:00
/// Optional middleware manager for custom transaction processing
2025-08-19 18:07:21 +08:00
pub middleware_manager : Option < Arc < MiddlewareManager >> ,
2025-04-02 13:39:59 +08:00
}
2025-06-24 20:56:15 +08:00
static INSTANCE : Mutex < Option < Arc < SolanaTrade >>> = Mutex ::new ( None );
2025-06-07 23:16:46 +08:00
2025-06-24 20:56:15 +08:00
impl Clone for SolanaTrade {
2025-04-02 13:39:59 +08:00
fn clone ( & self ) -> Self {
Self {
payer : self . payer . clone (),
rpc : self . rpc . clone (),
2025-09-17 11:14:15 +08:00
swqos_clients : self . swqos_clients . clone (),
2025-08-19 18:07:21 +08:00
middleware_manager : self . middleware_manager . clone (),
2025-04-02 13:39:59 +08:00
}
}
2025-01-09 00:12:43 +08:00
}
2025-09-22 01:09:41 +08:00
/// Parameters for executing swap orders across different DEX protocols
///
/// Contains all necessary configuration for swapping tokens, including
/// protocol-specific settings, account management options, and transaction preferences.
#[derive(Clone)]
pub struct TradeSwapParams {
// Trading configuration
/// The DEX protocol to use for the trade
pub dex_type : DexType ,
/// Public key of the token to purchase
pub input_mint : Pubkey ,
/// Public key of the token to sell
pub output_mint : Pubkey ,
/// Public key of the token program to use for the input token
pub input_token_program : Pubkey ,
/// Public key of the token program to use for the output token
pub output_token_program : Pubkey ,
/// Amount of input token to spend (in lamports)
pub input_amount : u64 ,
/// Optional slippage tolerance in basis points (e.g., 100 = 1%)
pub slippage_basis_points : Option < u64 > ,
/// Recent blockhash for transaction validity
pub recent_blockhash : Option < Hash > ,
/// Protocol-specific parameters (PumpFun, Raydium, etc.)
pub extension_params : Box < dyn ProtocolParams > ,
// Extended configuration
/// Optional address lookup table for transaction size optimization
pub lookup_table_key : Option < Pubkey > ,
/// Whether to wait for transaction confirmation before returning
pub wait_transaction_confirmed : bool ,
/// Whether to create wrapped SOL associated token account
pub create_input_mint_ata : bool ,
/// Whether to close wrapped SOL associated token account after trade
pub close_input_mint_ata : bool ,
/// Whether to create token mint associated token account
pub create_output_mint_ata : bool ,
/// Whether to close token mint associated token account after trade
pub close_output_mint_ata : bool ,
/// Whether to enable seed-based optimization for account creation
pub open_seed_optimize : bool ,
/// Durable nonce information
pub durable_nonce : Option < DurableNonceInfo > ,
/// Whether to include tip for transaction priority
pub with_tip : bool ,
}
2025-09-19 00:49:45 +08:00
/// Parameters for executing buy orders across different DEX protocols
///
/// Contains all necessary configuration for purchasing tokens, including
/// protocol-specific settings, account management options, and transaction preferences.
#[derive(Clone)]
pub struct TradeBuyParams {
// Trading configuration
/// The DEX protocol to use for the trade
pub dex_type : DexType ,
/// Public key of the token to purchase
pub mint : Pubkey ,
/// Amount of SOL to spend (in lamports)
pub sol_amount : u64 ,
/// Optional slippage tolerance in basis points (e.g., 100 = 1%)
pub slippage_basis_points : Option < u64 > ,
/// Recent blockhash for transaction validity
2025-09-20 14:00:45 +08:00
pub recent_blockhash : Option < Hash > ,
2025-09-19 00:49:45 +08:00
/// Protocol-specific parameters (PumpFun, Raydium, etc.)
pub extension_params : Box < dyn ProtocolParams > ,
// Extended configuration
/// Optional address lookup table for transaction size optimization
pub lookup_table_key : Option < Pubkey > ,
/// Whether to wait for transaction confirmation before returning
pub wait_transaction_confirmed : bool ,
/// Whether to create wrapped SOL associated token account
pub create_wsol_ata : bool ,
/// Whether to close wrapped SOL associated token account after trade
pub close_wsol_ata : bool ,
/// Whether to create token mint associated token account
pub create_mint_ata : bool ,
/// Whether to enable seed-based optimization for account creation
pub open_seed_optimize : bool ,
2025-09-20 14:00:45 +08:00
/// Durable nonce information
pub durable_nonce : Option < DurableNonceInfo > ,
2025-09-19 00:49:45 +08:00
}
/// Parameters for executing sell orders across different DEX protocols
///
/// Contains all necessary configuration for selling tokens, including
/// protocol-specific settings, tip preferences, account management options, and transaction preferences.
#[derive(Clone)]
pub struct TradeSellParams {
// Trading configuration
/// The DEX protocol to use for the trade
pub dex_type : DexType ,
/// Public key of the token to sell
pub mint : Pubkey ,
/// Amount of tokens to sell (in smallest token units)
pub token_amount : u64 ,
/// Optional slippage tolerance in basis points (e.g., 100 = 1%)
pub slippage_basis_points : Option < u64 > ,
/// Recent blockhash for transaction validity
2025-09-20 14:00:45 +08:00
pub recent_blockhash : Option < Hash > ,
2025-09-19 00:49:45 +08:00
/// Whether to include tip for transaction priority
pub with_tip : bool ,
/// Protocol-specific parameters (PumpFun, Raydium, etc.)
pub extension_params : Box < dyn ProtocolParams > ,
// Extended configuration
/// Optional address lookup table for transaction size optimization
pub lookup_table_key : Option < Pubkey > ,
/// Whether to wait for transaction confirmation before returning
pub wait_transaction_confirmed : bool ,
/// Whether to create wrapped SOL associated token account
pub create_wsol_ata : bool ,
/// Whether to close wrapped SOL associated token account after trade
pub close_wsol_ata : bool ,
/// Whether to enable seed-based optimization for account creation
pub open_seed_optimize : bool ,
2025-09-20 14:00:45 +08:00
/// Durable nonce information
pub durable_nonce : Option < DurableNonceInfo > ,
2025-09-19 00:49:45 +08:00
}
2025-06-24 20:56:15 +08:00
impl SolanaTrade {
2025-09-19 00:49:45 +08:00
/// Creates a new SolanaTrade instance with the specified configuration
///
/// This function initializes the trading system with RPC connection, SWQOS settings,
/// and sets up necessary components for trading operations.
///
/// # Arguments
/// * `payer` - The keypair used for signing transactions
/// * `rpc_url` - Solana RPC endpoint URL
/// * `commitment` - Transaction commitment level for RPC calls
/// * `swqos_settings` - List of SWQOS (Solana Web Quality of Service) configurations
///
/// # Returns
/// Returns a configured `SolanaTrade` instance ready for trading operations
2025-02-15 18:53:17 +08:00
#[inline]
2025-09-17 11:14:15 +08:00
pub async fn new ( payer : Arc < Keypair > , trade_config : TradeConfig ) -> Self {
2025-09-08 17:12:29 +08:00
crate ::common ::fast_fn ::fast_init ( & payer . try_pubkey (). unwrap ());
2025-04-02 13:39:59 +08:00
if CryptoProvider ::get_default (). is_none () {
let _ = default_provider ()
. install_default ()
. map_err ( | e | anyhow ::anyhow! ( "Failed to install crypto provider: {:?}" , e ));
}
2024-12-31 16:51:58 +08:00
2025-09-17 11:14:15 +08:00
let rpc_url = trade_config . rpc_url . clone ();
let swqos_configs = trade_config . swqos_configs . clone ();
let commitment = trade_config . commitment . clone ();
let mut swqos_clients : Vec < Arc < SwqosClient >> = vec! [];
2025-04-02 13:39:59 +08:00
2025-09-17 11:14:15 +08:00
for swqos in swqos_configs {
let swqos_client =
SwqosConfig ::get_swqos_client ( rpc_url . clone (), commitment . clone (), swqos . clone ());
swqos_clients . push ( swqos_client );
2025-05-29 18:53:58 +08:00
}
2025-09-19 00:49:45 +08:00
let rpc =
Arc ::new ( SolanaRpcClient ::new_with_commitment ( rpc_url . clone (), commitment . clone ()));
2025-09-09 17:02:24 +08:00
common ::seed ::update_rents ( & rpc ). await . unwrap ();
common ::seed ::start_rent_updater ( rpc . clone ());
2025-07-07 03:28:02 +08:00
2025-09-19 00:49:45 +08:00
let instance = Self { payer , rpc , swqos_clients , middleware_manager : None };
2025-06-07 23:16:46 +08:00
2025-09-08 17:12:29 +08:00
let mut current = INSTANCE . lock ();
2025-06-07 23:16:46 +08:00
* current = Some ( Arc ::new ( instance . clone ()));
instance
}
2025-09-19 00:49:45 +08:00
/// Adds a middleware manager to the SolanaTrade instance
///
/// Middleware managers can be used to implement custom logic that runs before or after trading operations,
/// such as logging, monitoring, or custom validation.
///
/// # Arguments
/// * `middleware_manager` - The middleware manager to attach
///
/// # Returns
/// Returns the modified SolanaTrade instance with middleware manager attached
2025-08-19 18:07:21 +08:00
pub fn with_middleware_manager ( mut self , middleware_manager : MiddlewareManager ) -> Self {
self . middleware_manager = Some ( Arc ::new ( middleware_manager ));
self
}
2025-09-19 00:49:45 +08:00
/// Gets the RPC client instance for direct Solana blockchain interactions
///
/// This provides access to the underlying Solana RPC client that can be used
/// for custom blockchain operations outside of the trading framework.
///
/// # Returns
/// Returns a reference to the Arc-wrapped SolanaRpcClient instance
2025-06-07 23:16:46 +08:00
pub fn get_rpc ( & self ) -> & Arc < SolanaRpcClient > {
& self . rpc
}
2025-09-19 00:49:45 +08:00
/// Gets the current globally shared SolanaTrade instance
///
/// This provides access to the singleton instance that was created with `new()`.
/// Useful for accessing the trading instance from different parts of the application.
///
/// # Returns
/// Returns the Arc-wrapped SolanaTrade instance
///
/// # Panics
/// Panics if no instance has been initialized yet. Make sure to call `new()` first.
2025-06-07 23:16:46 +08:00
pub fn get_instance () -> Arc < Self > {
2025-09-08 17:12:29 +08:00
let instance = INSTANCE . lock ();
2025-07-09 22:29:29 +08:00
instance
. as_ref ()
2025-09-19 00:49:45 +08:00
. expect ( "SolanaTrade instance not initialized. Please call new() first." )
2025-07-09 22:29:29 +08:00
. clone ()
2024-12-31 16:51:58 +08:00
}
2025-09-22 01:09:41 +08:00
/// Execute a swap order for a specified token
///
/// # Arguments
///
/// * `params` - Swap trade parameters containing all necessary trading configuration
///
/// # Returns
///
/// Returns `Ok(Signature)` with the transaction signature if the swap order is successfully executed,
/// or an error if the transaction fails.
///
/// # Errors
///
/// This function will return an error if:
/// - Invalid protocol parameters are provided for the specified DEX type
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient token balance for the sale
/// - Token account doesn't exist or is not properly initialized
/// - Required accounts cannot be created or accessed
pub async fn swap ( & self , params : TradeSwapParams ) -> Result < Signature , anyhow ::Error > {
if params . slippage_basis_points . is_none () {
println! (
"slippage_basis_points is none, use default slippage basis points: {} " ,
DEFAULT_SLIPPAGE
);
}
let executor = TradeFactory ::create_executor ( params . dex_type . clone ());
let protocol_params = params . extension_params ;
let buy_params = SwapParams {
rpc : Some ( self . rpc . clone ()),
payer : self . payer . clone (),
input_mint : params . input_mint ,
output_mint : params . output_mint ,
input_token_program : Some ( params . input_token_program ),
output_token_program : Some ( params . output_token_program ),
input_amount : Some ( params . input_amount ),
slippage_basis_points : params . slippage_basis_points ,
lookup_table_key : params . lookup_table_key ,
recent_blockhash : params . recent_blockhash ,
data_size_limit : 256 * 1024 ,
wait_transaction_confirmed : params . wait_transaction_confirmed ,
protocol_params : protocol_params . clone (),
open_seed_optimize : params . open_seed_optimize ,
swqos_clients : self . swqos_clients . clone (),
middleware_manager : self . middleware_manager . clone (),
durable_nonce : params . durable_nonce ,
with_tip : params . with_tip ,
create_input_mint_ata : params . create_input_mint_ata ,
close_input_mint_ata : params . close_input_mint_ata ,
create_output_mint_ata : params . create_output_mint_ata ,
close_output_mint_ata : params . close_output_mint_ata ,
};
// Validate protocol params
let is_valid_params = match params . dex_type {
DexType ::PumpFun => protocol_params . as_any (). downcast_ref ::< PumpFunParams > (). is_some (),
DexType ::PumpSwap => {
protocol_params . as_any (). downcast_ref ::< PumpSwapParams > (). is_some ()
}
DexType ::Bonk => protocol_params . as_any (). downcast_ref ::< BonkParams > (). is_some (),
DexType ::RaydiumCpmm => {
protocol_params . as_any (). downcast_ref ::< RaydiumCpmmParams > (). is_some ()
}
DexType ::RaydiumAmmV4 => {
protocol_params . as_any (). downcast_ref ::< RaydiumAmmV4Params > (). is_some ()
}
};
if ! is_valid_params {
return Err ( anyhow ::anyhow! ( "Invalid protocol params for Trade" ));
}
let mut no_support_mint = false ;
// 检查是否至少有一个代币是支持的基础代币(SOL、WSOL、USD1)
let has_supported_base_token = params . input_mint == SOL_TOKEN_ACCOUNT
|| params . output_mint == SOL_TOKEN_ACCOUNT
|| params . input_mint == WSOL_TOKEN_ACCOUNT
|| params . output_mint == WSOL_TOKEN_ACCOUNT
|| params . input_mint == USD1_TOKEN_ACCOUNT
|| params . output_mint == USD1_TOKEN_ACCOUNT ;
if ! has_supported_base_token {
no_support_mint = true ;
}
// USD1 代币暂支持在 Bonk 协议上交易
if ( params . input_mint == USD1_TOKEN_ACCOUNT || params . output_mint == USD1_TOKEN_ACCOUNT )
&& params . dex_type != DexType ::Bonk
{
no_support_mint = true ;
}
if no_support_mint {
return Err ( anyhow ::anyhow! ( "Currently only supports swap trading between (SOL、WSOL、USD1) and other tokens. USD1 swap trading is currently only supported on the Bonk protocol." ));
}
executor . swap ( buy_params ). await
}
2025-07-10 18:14:21 +08:00
/// Execute a buy order for a specified token
///
/// # Arguments
///
2025-09-19 00:49:45 +08:00
/// * `params` - Buy trade parameters containing all necessary trading configuration
2025-07-10 18:14:21 +08:00
///
/// # Returns
///
2025-09-19 00:49:45 +08:00
/// Returns `Ok(Signature)` with the transaction signature if the buy order is successfully executed,
/// or an error if the transaction fails.
2025-07-10 18:14:21 +08:00
///
/// # Errors
///
/// This function will return an error if:
2025-09-19 00:49:45 +08:00
/// - Invalid protocol parameters are provided for the specified DEX type
2025-07-10 18:14:21 +08:00
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient SOL balance for the purchase
2025-09-19 00:49:45 +08:00
/// - Required accounts cannot be created or accessed
pub async fn buy ( & self , params : TradeBuyParams ) -> Result < Signature , anyhow ::Error > {
if params . slippage_basis_points . is_none () {
2025-09-07 17:22:58 +08:00
println! (
"slippage_basis_points is none, use default slippage basis points: {} " ,
DEFAULT_SLIPPAGE
);
}
2025-09-19 00:49:45 +08:00
let executor = TradeFactory ::create_executor ( params . dex_type . clone ());
let protocol_params = params . extension_params ;
2025-09-22 00:05:20 +08:00
let input_mint = if params . dex_type == DexType ::PumpFun {
SOL_TOKEN_ACCOUNT
} else {
WSOL_TOKEN_ACCOUNT
};
let buy_params = SwapParams {
2025-07-10 18:14:21 +08:00
rpc : Some ( self . rpc . clone ()),
payer : self . payer . clone (),
2025-09-22 00:05:20 +08:00
input_mint : input_mint ,
output_mint : params . mint ,
input_token_program : None ,
output_token_program : None ,
input_amount : Some ( params . sol_amount ),
2025-09-19 00:49:45 +08:00
slippage_basis_points : params . slippage_basis_points ,
lookup_table_key : params . lookup_table_key ,
recent_blockhash : params . recent_blockhash ,
2025-09-09 17:02:24 +08:00
data_size_limit : 256 * 1024 ,
2025-09-19 00:49:45 +08:00
wait_transaction_confirmed : params . wait_transaction_confirmed ,
2025-07-10 18:14:21 +08:00
protocol_params : protocol_params . clone (),
2025-09-19 00:49:45 +08:00
open_seed_optimize : params . open_seed_optimize ,
2025-09-17 11:14:15 +08:00
swqos_clients : self . swqos_clients . clone (),
2025-09-09 17:02:24 +08:00
middleware_manager : self . middleware_manager . clone (),
2025-09-20 14:00:45 +08:00
durable_nonce : params . durable_nonce ,
2025-09-22 00:05:20 +08:00
with_tip : true ,
create_input_mint_ata : params . create_wsol_ata ,
close_input_mint_ata : params . close_wsol_ata ,
create_output_mint_ata : params . create_mint_ata ,
close_output_mint_ata : false ,
2025-07-10 18:14:21 +08:00
};
// Validate protocol params
2025-09-19 00:49:45 +08:00
let is_valid_params = match params . dex_type {
2025-08-19 18:07:21 +08:00
DexType ::PumpFun => protocol_params . as_any (). downcast_ref ::< PumpFunParams > (). is_some (),
DexType ::PumpSwap => {
protocol_params . as_any (). downcast_ref ::< PumpSwapParams > (). is_some ()
}
DexType ::Bonk => protocol_params . as_any (). downcast_ref ::< BonkParams > (). is_some (),
DexType ::RaydiumCpmm => {
protocol_params . as_any (). downcast_ref ::< RaydiumCpmmParams > (). is_some ()
}
DexType ::RaydiumAmmV4 => {
protocol_params . as_any (). downcast_ref ::< RaydiumAmmV4Params > (). is_some ()
}
2025-07-10 18:14:21 +08:00
};
if ! is_valid_params {
2025-07-09 22:29:29 +08:00
return Err ( anyhow ::anyhow! ( "Invalid protocol params for Trade" ));
2025-06-24 20:56:15 +08:00
}
2025-07-10 18:14:21 +08:00
2025-09-22 00:05:20 +08:00
executor . swap ( buy_params ). await
2025-06-24 20:56:15 +08:00
}
2025-07-10 18:14:21 +08:00
/// Execute a sell order for a specified token
///
/// # Arguments
///
2025-09-19 00:49:45 +08:00
/// * `params` - Sell trade parameters containing all necessary trading configuration
2025-07-10 18:14:21 +08:00
///
/// # Returns
///
2025-09-19 00:49:45 +08:00
/// Returns `Ok(Signature)` with the transaction signature if the sell order is successfully executed,
/// or an error if the transaction fails.
2025-07-10 18:14:21 +08:00
///
/// # Errors
///
/// This function will return an error if:
2025-09-19 00:49:45 +08:00
/// - Invalid protocol parameters are provided for the specified DEX type
2025-07-10 18:14:21 +08:00
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient token balance for the sale
/// - Token account doesn't exist or is not properly initialized
2025-09-19 00:49:45 +08:00
/// - Required accounts cannot be created or accessed
pub async fn sell ( & self , params : TradeSellParams ) -> Result < Signature , anyhow ::Error > {
if params . slippage_basis_points . is_none () {
2025-09-07 17:22:58 +08:00
println! (
"slippage_basis_points is none, use default slippage basis points: {} " ,
DEFAULT_SLIPPAGE
);
}
2025-09-19 00:49:45 +08:00
let executor = TradeFactory ::create_executor ( params . dex_type . clone ());
let protocol_params = params . extension_params ;
2025-09-22 00:05:20 +08:00
let output_mint = if params . dex_type == DexType ::PumpFun {
SOL_TOKEN_ACCOUNT
} else {
WSOL_TOKEN_ACCOUNT
};
let sell_params = SwapParams {
2025-07-10 18:14:21 +08:00
rpc : Some ( self . rpc . clone ()),
payer : self . payer . clone (),
2025-09-22 00:05:20 +08:00
input_mint : params . mint ,
output_mint : output_mint ,
input_token_program : None ,
output_token_program : None ,
input_amount : Some ( params . token_amount ),
2025-09-19 00:49:45 +08:00
slippage_basis_points : params . slippage_basis_points ,
lookup_table_key : params . lookup_table_key ,
recent_blockhash : params . recent_blockhash ,
wait_transaction_confirmed : params . wait_transaction_confirmed ,
2025-07-10 18:14:21 +08:00
protocol_params : protocol_params . clone (),
2025-09-19 00:49:45 +08:00
with_tip : params . with_tip ,
open_seed_optimize : params . open_seed_optimize ,
swqos_clients : self . swqos_clients . clone (),
2025-09-09 17:02:24 +08:00
middleware_manager : self . middleware_manager . clone (),
2025-09-20 14:00:45 +08:00
durable_nonce : params . durable_nonce ,
2025-09-22 00:05:20 +08:00
data_size_limit : 0 ,
create_input_mint_ata : false ,
close_input_mint_ata : false ,
create_output_mint_ata : params . create_wsol_ata ,
close_output_mint_ata : params . close_wsol_ata ,
2025-07-10 18:14:21 +08:00
};
2025-09-12 04:29:47 +08:00
2025-07-10 18:14:21 +08:00
// Validate protocol params
2025-09-19 00:49:45 +08:00
let is_valid_params = match params . dex_type {
2025-08-19 18:07:21 +08:00
DexType ::PumpFun => protocol_params . as_any (). downcast_ref ::< PumpFunParams > (). is_some (),
DexType ::PumpSwap => {
protocol_params . as_any (). downcast_ref ::< PumpSwapParams > (). is_some ()
}
DexType ::Bonk => protocol_params . as_any (). downcast_ref ::< BonkParams > (). is_some (),
DexType ::RaydiumCpmm => {
protocol_params . as_any (). downcast_ref ::< RaydiumCpmmParams > (). is_some ()
}
DexType ::RaydiumAmmV4 => {
protocol_params . as_any (). downcast_ref ::< RaydiumAmmV4Params > (). is_some ()
}
2025-07-10 18:14:21 +08:00
};
if ! is_valid_params {
2025-07-09 22:29:29 +08:00
return Err ( anyhow ::anyhow! ( "Invalid protocol params for Trade" ));
2025-06-10 19:39:06 +08:00
}
2025-07-10 18:14:21 +08:00
// Execute sell based on tip preference
2025-09-22 00:05:20 +08:00
executor . swap ( sell_params ). await
2025-01-03 14:43:26 +08:00
}
2025-07-10 18:14:21 +08:00
/// Execute a sell order for a percentage of the specified token amount
///
/// This is a convenience function that calculates the exact amount to sell based on
/// a percentage of the total token amount and then calls the `sell` function.
///
/// # Arguments
///
2025-09-19 00:49:45 +08:00
/// * `params` - Sell trade parameters (will be modified with calculated token amount)
2025-07-10 18:14:21 +08:00
/// * `amount_token` - Total amount of tokens available (in smallest token units)
/// * `percent` - Percentage of tokens to sell (1-100, where 100 = 100%)
///
/// # Returns
///
2025-09-19 00:49:45 +08:00
/// Returns `Ok(Signature)` with the transaction signature if the sell order is successfully executed,
/// or an error if the transaction fails.
2025-07-10 18:14:21 +08:00
///
/// # Errors
///
/// This function will return an error if:
/// - `percent` is 0 or greater than 100
2025-09-19 00:49:45 +08:00
/// - Invalid protocol parameters are provided for the specified DEX type
2025-07-10 18:14:21 +08:00
/// - The transaction fails to execute
/// - Network or RPC errors occur
/// - Insufficient token balance for the calculated sale amount
/// - Token account doesn't exist or is not properly initialized
2025-09-19 00:49:45 +08:00
/// - Required accounts cannot be created or accessed
2025-07-10 18:14:21 +08:00
pub async fn sell_by_percent (
2025-06-22 23:05:54 +08:00
& self ,
2025-09-19 00:49:45 +08:00
mut params : TradeSellParams ,
2025-07-10 18:14:21 +08:00
amount_token : u64 ,
2025-06-22 23:05:54 +08:00
percent : u64 ,
2025-09-09 12:03:42 +08:00
) -> Result < Signature , anyhow ::Error > {
2025-07-10 18:14:21 +08:00
if percent == 0 || percent > 100 {
return Err ( anyhow ::anyhow! ( "Percentage must be between 1 and 100" ));
2025-06-22 23:05:54 +08:00
}
2025-07-10 18:14:21 +08:00
let amount = amount_token * percent / 100 ;
2025-09-19 00:49:45 +08:00
params . token_amount = amount ;
self . sell ( params ). await
2025-06-15 20:50:51 +08:00
}
2025-09-09 17:02:24 +08:00
2025-09-19 00:49:45 +08:00
/// Wraps native SOL into wSOL (Wrapped SOL) for use in SPL token operations
2025-09-09 17:02:24 +08:00
///
/// This function creates a wSOL associated token account (if it doesn't exist),
/// transfers the specified amount of SOL to that account, and then syncs the native
2025-09-19 00:49:45 +08:00
/// token balance to make SOL usable as an SPL token in trading operations.
2025-09-09 17:02:24 +08:00
///
/// # Arguments
2025-09-19 00:49:45 +08:00
/// * `amount` - The amount of SOL to wrap (in lamports)
2025-09-09 17:02:24 +08:00
///
/// # Returns
2025-09-19 00:49:45 +08:00
/// * `Ok(String)` - Transaction signature if successful
/// * `Err(anyhow::Error)` - If the transaction fails to execute
///
/// # Errors
///
/// This function will return an error if:
/// - Insufficient SOL balance for the wrap operation
/// - wSOL associated token account creation fails
/// - Transaction fails to execute or confirm
/// - Network or RPC errors occur
2025-09-09 17:02:24 +08:00
pub async fn wrap_sol_to_wsol ( & self , amount : u64 ) -> Result < String , anyhow ::Error > {
use crate ::trading ::common ::wsol_manager ::handle_wsol ;
use solana_sdk ::transaction ::Transaction ;
let recent_blockhash = self . rpc . get_latest_blockhash (). await ? ;
let instructions = handle_wsol ( & self . payer . pubkey (), amount );
let mut transaction =
Transaction ::new_with_payer ( & instructions , Some ( & self . payer . pubkey ()));
transaction . sign ( & [ &* self . payer ], recent_blockhash );
let signature = self . rpc . send_and_confirm_transaction ( & transaction ). await ? ;
Ok ( signature . to_string ())
}
2025-09-19 00:49:45 +08:00
/// Closes the wSOL associated token account and unwraps remaining balance to native SOL
2025-09-09 17:02:24 +08:00
///
/// This function closes the wSOL associated token account, which automatically
/// transfers any remaining wSOL balance back to the account owner as native SOL.
2025-09-19 00:49:45 +08:00
/// This is useful for cleaning up wSOL accounts and recovering wrapped SOL after trading operations.
2025-09-09 17:02:24 +08:00
///
/// # Returns
2025-09-19 00:49:45 +08:00
/// * `Ok(String)` - Transaction signature if successful
/// * `Err(anyhow::Error)` - If the transaction fails to execute
///
/// # Errors
///
/// This function will return an error if:
/// - wSOL associated token account doesn't exist
/// - Account closure fails due to insufficient permissions
/// - Transaction fails to execute or confirm
/// - Network or RPC errors occur
2025-09-09 17:02:24 +08:00
pub async fn close_wsol ( & self ) -> Result < String , anyhow ::Error > {
use crate ::trading ::common ::wsol_manager ::close_wsol ;
use solana_sdk ::transaction ::Transaction ;
let recent_blockhash = self . rpc . get_latest_blockhash (). await ? ;
let instructions = close_wsol ( & self . payer . pubkey ());
let mut transaction =
Transaction ::new_with_payer ( & instructions , Some ( & self . payer . pubkey ()));
transaction . sign ( & [ &* self . payer ], recent_blockhash );
let signature = self . rpc . send_and_confirm_transaction ( & transaction ). await ? ;
Ok ( signature . to_string ())
}
2024-12-31 16:51:58 +08:00
}