refactor: reorganize code structure and optimize modular design
- Remove src/common/address_lookup.rs and tip_cache.rs to simplify common modules - Move protocol-specific utility functions from trading/ to instruction/utils/ - Refactor utility functions for PumpFun, PumpSwap, Raydium AMM V4, and Raydium CPMM - Consolidate constant definitions by inlining seeds and accounts modules into respective utility files - Update all import paths to ensure code consistency - Optimize trading parameter construction and executor logic - Improve address lookup cache and nonce management mechanisms
This commit is contained in:
@@ -21,7 +21,7 @@ use sol_trade_sdk::{
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
constants::pumpswap::accounts,
|
||||
instruction::utils::pumpswap::accounts,
|
||||
solana_streamer_sdk::streaming::event_parser::{
|
||||
common::filter::EventTypeFilter, protocols::pumpswap::PumpSwapBuyEvent,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::{
|
||||
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::{instruction::utils::raydium_amm_v4::{accounts, fetch_amm_info}, 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;
|
||||
@@ -131,9 +131,7 @@ async fn raydium_amm_v4_copy_trade_with_grpc(trade_info: RaydiumAmmV4SwapEvent)
|
||||
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 amm_info = 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 {
|
||||
|
||||
@@ -17,7 +17,7 @@ use sol_trade_sdk::{
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
constants::raydium_cpmm::accounts,
|
||||
instruction::utils::raydium_cpmm::accounts,
|
||||
solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
|
||||
@@ -25,10 +25,14 @@
|
||||
//! - `get_final_market_cap_sol`: Calculates the final market cap in SOL after all tokens are sold
|
||||
//! - `get_buy_out_price`: Calculates the price to buy out all remaining tokens
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
use crate::{constants::pumpfun::global_constants::{INITIAL_REAL_TOKEN_RESERVES, INITIAL_VIRTUAL_SOL_RESERVES, INITIAL_VIRTUAL_TOKEN_RESERVES, TOKEN_TOTAL_SUPPLY}, trading::pumpfun::common::{get_bonding_curve_pda, get_creator_vault_pda}};
|
||||
use crate::instruction::utils::pumpfun::global_constants::{
|
||||
INITIAL_REAL_TOKEN_RESERVES, INITIAL_VIRTUAL_SOL_RESERVES, INITIAL_VIRTUAL_TOKEN_RESERVES,
|
||||
TOKEN_TOTAL_SUPPLY,
|
||||
};
|
||||
use crate::instruction::utils::pumpfun::{get_bonding_curve_pda, get_creator_vault_pda};
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
|
||||
/// Represents the global configuration account for token pricing and fees
|
||||
@@ -55,7 +59,12 @@ pub struct BondingCurveAccount {
|
||||
}
|
||||
|
||||
impl BondingCurveAccount {
|
||||
pub fn from_dev_trade(mint: &Pubkey, dev_token_amount: u64, dev_sol_amount: u64, creator: Pubkey) -> Self {
|
||||
pub fn from_dev_trade(
|
||||
mint: &Pubkey,
|
||||
dev_token_amount: u64,
|
||||
dev_sol_amount: u64,
|
||||
creator: Pubkey,
|
||||
) -> Self {
|
||||
Self {
|
||||
discriminator: 0,
|
||||
account: get_bonding_curve_pda(mint).unwrap(),
|
||||
@@ -118,11 +127,7 @@ impl BondingCurveAccount {
|
||||
|
||||
// Convert back to u64 and return the minimum of calculated tokens and real reserves
|
||||
let s_u64 = s as u64;
|
||||
Ok(if s_u64 < self.real_token_reserves {
|
||||
s_u64
|
||||
} else {
|
||||
self.real_token_reserves
|
||||
})
|
||||
Ok(if s_u64 < self.real_token_reserves { s_u64 } else { self.real_token_reserves })
|
||||
}
|
||||
|
||||
/// Calculates the amount of SOL received for selling tokens
|
||||
|
||||
@@ -23,10 +23,16 @@
|
||||
//! - `creator_fee`: Fee for creators
|
||||
//! - `fee_recipients`: Array of fee recipient accounts
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::constants::pumpfun::global_constants::*;
|
||||
use crate::instruction::utils::pumpfun::global_constants::{
|
||||
AUTHORITY, CREATOR_FEE, ENABLE_MIGRATE, FEE_BASIS_POINTS, FEE_RECIPIENT, GLOBAL_ACCOUNT,
|
||||
INITIAL_REAL_TOKEN_RESERVES, INITIAL_VIRTUAL_SOL_RESERVES, INITIAL_VIRTUAL_TOKEN_RESERVES,
|
||||
POOL_MIGRATION_FEE, PUMPFUN_AMM_FEE_1, PUMPFUN_AMM_FEE_2, PUMPFUN_AMM_FEE_3, PUMPFUN_AMM_FEE_4,
|
||||
PUMPFUN_AMM_FEE_5, PUMPFUN_AMM_FEE_6, PUMPFUN_AMM_FEE_7, TOKEN_TOTAL_SUPPLY,
|
||||
WITHDRAW_AUTHORITY,
|
||||
};
|
||||
|
||||
/// Represents the global configuration account for token pricing and fees
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
//! Constants used by the crate.
|
||||
//!
|
||||
//! This module contains various constants used throughout the crate, including:
|
||||
//!
|
||||
//! - Seeds for deriving Program Derived Addresses (PDAs)
|
||||
//! - Program account addresses and public keys
|
||||
//!
|
||||
//! The constants are organized into submodules for better organization:
|
||||
//!
|
||||
//! - `seeds`: Contains seed values used for PDA derivation
|
||||
//! - `accounts`: Contains important program account addresses
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
pub const POOL_SEED: &[u8] = b"pool";
|
||||
pub const POOL_VAULT_SEED: &[u8] = b"pool_vault";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
pub const AUTHORITY: Pubkey = pubkey!("WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh");
|
||||
pub const GLOBAL_CONFIG: Pubkey = pubkey!("6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX");
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
pub const EVENT_AUTHORITY: Pubkey = pubkey!("2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr");
|
||||
pub const WSOL_TOKEN_ACCOUNT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
|
||||
pub const BONK: Pubkey = pubkey!("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj");
|
||||
pub const SYSTEM_PROGRAM: Pubkey = solana_sdk::system_program::ID;
|
||||
|
||||
pub const PLATFORM_FEE_RATE: u128 = 100; // 1%
|
||||
pub const PROTOCOL_FEE_RATE: u128 = 25; // 0.25%
|
||||
pub const SHARE_FEE_RATE: u128 = 0; // 0%
|
||||
}
|
||||
|
||||
pub const BUY_EXECT_IN_DISCRIMINATOR: [u8; 8] = [250, 234, 13, 123, 213, 156, 19, 236];
|
||||
pub const SELL_EXECT_IN_DISCRIMINATOR: [u8; 8] = [149, 39, 222, 155, 211, 124, 152, 26];
|
||||
+2
-15
@@ -1,17 +1,4 @@
|
||||
pub mod bonk;
|
||||
pub mod pumpfun;
|
||||
pub mod pumpswap;
|
||||
pub mod decimals;
|
||||
pub mod swqos;
|
||||
pub mod trade;
|
||||
pub mod raydium_cpmm;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod decimals;
|
||||
|
||||
pub mod trade_platform {
|
||||
pub const PUMPFUN: &'static str = "pumpfun";
|
||||
pub const PUMPFUN_SWAP: &'static str = "pumpswap";
|
||||
pub const BONK: &'static str = "bonk";
|
||||
pub const RAYDIUM_CPMM: &'static str = "raydium_cpmm";
|
||||
pub const RAYDIUM_CLMM: &'static str = "raydium_clmm";
|
||||
pub const RAYDIUM_AMM_V4: &'static str = "raydium_amm_v4";
|
||||
}
|
||||
pub mod trade_platform;
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
//! Constants used by the crate.
|
||||
//!
|
||||
//! This module contains various constants used throughout the crate, including:
|
||||
//!
|
||||
//! - Seeds for deriving Program Derived Addresses (PDAs)
|
||||
//! - Program account addresses and public keys
|
||||
//!
|
||||
//! The constants are organized into submodules for better organization:
|
||||
//!
|
||||
//! - `seeds`: Contains seed values used for PDA derivation
|
||||
//! - `accounts`: Contains important program account addresses
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
/// Seed for the global state PDA
|
||||
pub const GLOBAL_SEED: &[u8] = b"global";
|
||||
|
||||
/// Seed for the mint authority PDA
|
||||
pub const MINT_AUTHORITY_SEED: &[u8] = b"mint-authority";
|
||||
|
||||
/// Seed for bonding curve PDAs
|
||||
pub const BONDING_CURVE_SEED: &[u8] = b"bonding-curve";
|
||||
|
||||
/// Seed for creator vault PDAs
|
||||
pub const CREATOR_VAULT_SEED: &[u8] = b"creator-vault";
|
||||
|
||||
/// Seed for metadata PDAs
|
||||
pub const METADATA_SEED: &[u8] = b"metadata";
|
||||
|
||||
/// Seed for user volume accumulator PDAs
|
||||
pub const USER_VOLUME_ACCUMULATOR_SEED: &[u8] = b"user_volume_accumulator";
|
||||
|
||||
/// Seed for global volume accumulator PDAs
|
||||
pub const GLOBAL_VOLUME_ACCUMULATOR_SEED: &[u8] = b"global_volume_accumulator";
|
||||
|
||||
pub const FEE_CONFIG_SEED: &[u8] = b"fee_config";
|
||||
}
|
||||
|
||||
pub mod global_constants {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
pub const INITIAL_VIRTUAL_TOKEN_RESERVES: u64 = 1_073_000_000_000_000;
|
||||
|
||||
pub const INITIAL_VIRTUAL_SOL_RESERVES: u64 = 30_000_000_000;
|
||||
|
||||
pub const INITIAL_REAL_TOKEN_RESERVES: u64 = 793_100_000_000_000;
|
||||
|
||||
pub const TOKEN_TOTAL_SUPPLY: u64 = 1_000_000_000_000_000;
|
||||
|
||||
pub const FEE_BASIS_POINTS: u64 = 95;
|
||||
|
||||
pub const ENABLE_MIGRATE: bool = false;
|
||||
|
||||
pub const POOL_MIGRATION_FEE: u64 = 15_000_001;
|
||||
|
||||
pub const CREATOR_FEE: u64 = 5;
|
||||
|
||||
pub const SCALE: u64 = 1_000_000; // 10^6 for token decimals
|
||||
|
||||
pub const LAMPORTS_PER_SOL: u64 = 1_000_000_000; // 10^9 for solana lamports
|
||||
|
||||
pub const COMPLETION_LAMPORTS: u64 = 85 * LAMPORTS_PER_SOL; // ~ 85 SOL
|
||||
|
||||
/// Public key for the fee recipient
|
||||
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
/// Public key for the global PDA
|
||||
pub const GLOBAL_ACCOUNT: Pubkey = pubkey!("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf");
|
||||
|
||||
/// Public key for the authority
|
||||
pub const AUTHORITY: Pubkey = pubkey!("FFWtrEQ4B4PKQoVuHYzZq8FabGkVatYzDpEVHsK5rrhF");
|
||||
|
||||
/// Public key for the withdraw authority
|
||||
pub const WITHDRAW_AUTHORITY: Pubkey = pubkey!("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg");
|
||||
|
||||
pub const PUMPFUN_AMM_FEE_1: Pubkey = pubkey!("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"); // Pump.fun AMM: Protocol Fee 1
|
||||
pub const PUMPFUN_AMM_FEE_2: Pubkey = pubkey!("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"); // Pump.fun AMM: Protocol Fee 2
|
||||
pub const PUMPFUN_AMM_FEE_3: Pubkey = pubkey!("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"); // Pump.fun AMM: Protocol Fee 3
|
||||
pub const PUMPFUN_AMM_FEE_4: Pubkey = pubkey!("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"); // Pump.fun AMM: Protocol Fee 4
|
||||
pub const PUMPFUN_AMM_FEE_5: Pubkey = pubkey!("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"); // Pump.fun AMM: Protocol Fee 5
|
||||
pub const PUMPFUN_AMM_FEE_6: Pubkey = pubkey!("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"); // Pump.fun AMM: Protocol Fee 6
|
||||
pub const PUMPFUN_AMM_FEE_7: Pubkey = pubkey!("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP");
|
||||
// Pump.fun AMM: Protocol Fee 7
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
/// Public key for the Pump.fun program
|
||||
pub const PUMPFUN: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
|
||||
|
||||
/// Public key for the MPL Token Metadata program
|
||||
pub const MPL_TOKEN_METADATA: Pubkey = pubkey!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
||||
|
||||
/// Authority for program events
|
||||
pub const EVENT_AUTHORITY: Pubkey = pubkey!("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1");
|
||||
|
||||
/// System Program ID
|
||||
pub const SYSTEM_PROGRAM: Pubkey = pubkey!("11111111111111111111111111111111");
|
||||
|
||||
/// Token Program ID
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
|
||||
/// Associated Token Program ID
|
||||
pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey =
|
||||
pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
|
||||
/// Rent Sysvar ID
|
||||
pub const RENT: Pubkey = pubkey!("SysvarRent111111111111111111111111111111111");
|
||||
|
||||
pub const AMM_PROGRAM: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
|
||||
|
||||
pub const FEE_PROGRAM: Pubkey = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
||||
}
|
||||
|
||||
pub struct Symbol;
|
||||
|
||||
impl Symbol {
|
||||
pub const SOLANA: &'static str = "solana";
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//! Constants used by the crate.
|
||||
//!
|
||||
//! This module contains various constants used throughout the crate, including:
|
||||
//!
|
||||
//! - Seeds for deriving Program Derived Addresses (PDAs)
|
||||
//! - Program account addresses and public keys
|
||||
//!
|
||||
//! The constants are organized into submodules for better organization:
|
||||
//!
|
||||
//! - `seeds`: Contains seed values used for PDA derivation
|
||||
//! - `accounts`: Contains important program account addresses
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
/// Seed for the global state PDA
|
||||
pub const GLOBAL_SEED: &[u8] = b"global";
|
||||
|
||||
/// Seed for the mint authority PDA
|
||||
pub const MINT_AUTHORITY_SEED: &[u8] = b"mint-authority";
|
||||
|
||||
/// Seed for bonding curve PDAs
|
||||
pub const BONDING_CURVE_SEED: &[u8] = b"bonding-curve";
|
||||
|
||||
/// Seed for metadata PDAs
|
||||
pub const METADATA_SEED: &[u8] = b"metadata";
|
||||
|
||||
pub const USER_VOLUME_ACCUMULATOR_SEED: &[u8] = b"user_volume_accumulator";
|
||||
pub const GLOBAL_VOLUME_ACCUMULATOR_SEED: &[u8] = b"global_volume_accumulator";
|
||||
pub const FEE_CONFIG_SEED: &[u8] = b"fee_config";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
/// Public key for the fee recipient
|
||||
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
/// Public key for the global PDA
|
||||
pub const GLOBAL_ACCOUNT: Pubkey = pubkey!("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw");
|
||||
|
||||
/// Authority for program events
|
||||
pub const EVENT_AUTHORITY: Pubkey = pubkey!("GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR");
|
||||
|
||||
pub const WSOL_TOKEN_ACCOUNT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
|
||||
|
||||
/// System Program ID
|
||||
pub const SYSTEM_PROGRAM: Pubkey = pubkey!("11111111111111111111111111111111");
|
||||
|
||||
/// Token Program ID
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
|
||||
/// Associated Token Program ID
|
||||
pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey =
|
||||
pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
|
||||
// PumpSwap 协议费用接收者
|
||||
pub const PROTOCOL_FEE_RECIPIENT: Pubkey =
|
||||
pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
/// Rent Sysvar ID
|
||||
pub const RENT: Pubkey = pubkey!("SysvarRent111111111111111111111111111111111");
|
||||
|
||||
pub const AMM_PROGRAM: Pubkey = pubkey!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA");
|
||||
|
||||
pub const LP_FEE_BASIS_POINTS: u64 = 20;
|
||||
pub const PROTOCOL_FEE_BASIS_POINTS: u64 = 5;
|
||||
pub const COIN_CREATOR_FEE_BASIS_POINTS: u64 = 5;
|
||||
|
||||
pub const FEE_PROGRAM: Pubkey = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
||||
}
|
||||
|
||||
pub const BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
|
||||
pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
|
||||
@@ -1,37 +0,0 @@
|
||||
//! Constants used by the crate.
|
||||
//!
|
||||
//! This module contains various constants used throughout the crate, including:
|
||||
//!
|
||||
//! - Seeds for deriving Program Derived Addresses (PDAs)
|
||||
//! - Program account addresses and public keys
|
||||
//!
|
||||
//! The constants are organized into submodules for better organization:
|
||||
//!
|
||||
//! - `seeds`: Contains seed values used for PDA derivation
|
||||
//! - `accounts`: Contains important program account addresses
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
pub const POOL_SEED: &[u8] = b"pool";
|
||||
pub const POOL_VAULT_SEED: &[u8] = b"pool_vault";
|
||||
pub const OBSERVATION_STATE_SEED: &[u8] = b"observation";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
pub const AUTHORITY: Pubkey = pubkey!("GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL");
|
||||
pub const AMM_CONFIG: Pubkey = pubkey!("D4FPEruKEHrG5TenZ2mpDGEfu1iUvTiqBxvpU8HLBvC2");
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
pub const WSOL_TOKEN_ACCOUNT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
|
||||
pub const RAYDIUM_CPMM: Pubkey = pubkey!("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C");
|
||||
|
||||
pub const FEE_RATE_DENOMINATOR_VALUE: u128 = 1_000_000;
|
||||
pub const TRADE_FEE_RATE: u64 = 2500;
|
||||
pub const CREATOR_FEE_RATE: u64 = 0;
|
||||
pub const PROTOCOL_FEE_RATE: u64 = 120000;
|
||||
pub const FUND_FEE_RATE: u64 = 40000;
|
||||
}
|
||||
|
||||
pub const SWAP_BASE_IN_DISCRIMINATOR: &[u8] = &[143, 190, 90, 218, 196, 30, 51, 222];
|
||||
pub const SWAP_BASE_OUT_DISCRIMINATOR: &[u8] = &[55, 217, 98, 86, 163, 74, 180, 173];
|
||||
@@ -6,4 +6,4 @@ pub mod trade {
|
||||
pub const DEFAULT_SELL_TIP_FEE: f64 = 0.0001;
|
||||
pub const DEFAULT_RPC_UNIT_LIMIT: u32 = 78000;
|
||||
pub const DEFAULT_RPC_UNIT_PRICE: u64 = 500000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
pub mod trade_platform {
|
||||
pub const PUMPFUN: &'static str = "pumpfun";
|
||||
pub const PUMPFUN_SWAP: &'static str = "pumpswap";
|
||||
pub const BONK: &'static str = "bonk";
|
||||
pub const RAYDIUM_CPMM: &'static str = "raydium_cpmm";
|
||||
pub const RAYDIUM_CLMM: &'static str = "raydium_clmm";
|
||||
pub const RAYDIUM_AMM_V4: &'static str = "raydium_amm_v4";
|
||||
}
|
||||
@@ -5,12 +5,12 @@ use spl_associated_token_account::instruction::create_associated_token_account_i
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
bonk::{accounts, BUY_EXECT_IN_DISCRIMINATOR, SELL_EXECT_IN_DISCRIMINATOR},
|
||||
trade::trade::DEFAULT_SLIPPAGE,
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
instruction::utils::bonk::{
|
||||
accounts, get_pool_pda, get_vault_pda, BUY_EXECT_IN_DISCRIMINATOR,
|
||||
SELL_EXECT_IN_DISCRIMINATOR,
|
||||
},
|
||||
trading::{
|
||||
bonk::common::{get_pool_pda, get_vault_pda},
|
||||
common::utils::get_token_balance,
|
||||
core::{
|
||||
params::{BonkParams, BuyParams, SellParams},
|
||||
|
||||
@@ -2,4 +2,5 @@ pub mod pumpfun;
|
||||
pub mod pumpswap;
|
||||
pub mod bonk;
|
||||
pub mod raydium_cpmm;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod utils;
|
||||
+21
-22
@@ -6,9 +6,10 @@ use spl_associated_token_account::{
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::{
|
||||
constants,
|
||||
trading::pumpfun::common::{
|
||||
get_bonding_curve_pda, get_fee_config_pda, get_global_volume_accumulator_pda, get_user_volume_accumulator_pda
|
||||
instruction::utils::pumpfun::{
|
||||
accounts, get_bonding_curve_pda, get_creator_vault_pda, get_fee_config_pda,
|
||||
get_global_volume_accumulator_pda, get_user_volume_accumulator_pda,
|
||||
global_constants::{self, FEE_RECIPIENT},
|
||||
},
|
||||
utils::calc::{
|
||||
common::{calculate_with_slippage_buy, calculate_with_slippage_sell},
|
||||
@@ -19,13 +20,11 @@ use crate::{
|
||||
use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, signature::Keypair, signer::Signer};
|
||||
|
||||
use crate::{
|
||||
constants::pumpfun::global_constants::FEE_RECIPIENT,
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
trading::core::{
|
||||
params::{BuyParams, PumpFunParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
trading::pumpfun::common::get_creator_vault_pda,
|
||||
};
|
||||
|
||||
/// Instruction builder for PumpFun protocol
|
||||
@@ -58,9 +57,9 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
Pubkey::default()
|
||||
} else {
|
||||
// Fast check against cached default creator vault
|
||||
static DEFAULT_CREATOR_VAULT: std::sync::LazyLock<Option<Pubkey>> =
|
||||
static DEFAULT_CREATOR_VAULT: std::sync::LazyLock<Option<Pubkey>> =
|
||||
std::sync::LazyLock::new(|| get_creator_vault_pda(&Pubkey::default()));
|
||||
|
||||
|
||||
if Some(creator_vault_pda) == *DEFAULT_CREATOR_VAULT {
|
||||
Pubkey::default()
|
||||
} else {
|
||||
@@ -83,7 +82,7 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.mint,
|
||||
&constants::pumpfun::accounts::TOKEN_PROGRAM,
|
||||
&accounts::TOKEN_PROGRAM,
|
||||
));
|
||||
|
||||
// Create buy instruction
|
||||
@@ -200,25 +199,25 @@ pub fn buy(
|
||||
args: Buy,
|
||||
) -> Instruction {
|
||||
Instruction::new_with_bytes(
|
||||
constants::pumpfun::accounts::PUMPFUN,
|
||||
accounts::PUMPFUN,
|
||||
&args.data(),
|
||||
vec![
|
||||
AccountMeta::new_readonly(constants::pumpfun::global_constants::GLOBAL_ACCOUNT, false),
|
||||
AccountMeta::new_readonly(global_constants::GLOBAL_ACCOUNT, false),
|
||||
AccountMeta::new(*fee_recipient, false),
|
||||
AccountMeta::new_readonly(*mint, false),
|
||||
AccountMeta::new(*bonding_curve_pda, false),
|
||||
AccountMeta::new(get_associated_token_address(bonding_curve_pda, mint), false),
|
||||
AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false),
|
||||
AccountMeta::new(payer.pubkey(), true),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::SYSTEM_PROGRAM, false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::TOKEN_PROGRAM, false),
|
||||
AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false),
|
||||
AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false),
|
||||
AccountMeta::new(*creator_vault_pda, false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::EVENT_AUTHORITY, false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::PUMPFUN, false),
|
||||
AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false),
|
||||
AccountMeta::new_readonly(accounts::PUMPFUN, false),
|
||||
AccountMeta::new(get_global_volume_accumulator_pda().unwrap(), false),
|
||||
AccountMeta::new(get_user_volume_accumulator_pda(&payer.pubkey()).unwrap(), false),
|
||||
AccountMeta::new_readonly(get_fee_config_pda().unwrap(), false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::FEE_PROGRAM, false),
|
||||
AccountMeta::new_readonly(accounts::FEE_PROGRAM, false),
|
||||
],
|
||||
)
|
||||
}
|
||||
@@ -232,23 +231,23 @@ pub fn sell(
|
||||
) -> Instruction {
|
||||
let bonding_curve: Pubkey = get_bonding_curve_pda(mint).unwrap();
|
||||
Instruction::new_with_bytes(
|
||||
constants::pumpfun::accounts::PUMPFUN,
|
||||
accounts::PUMPFUN,
|
||||
&args.data(),
|
||||
vec![
|
||||
AccountMeta::new_readonly(constants::pumpfun::global_constants::GLOBAL_ACCOUNT, false),
|
||||
AccountMeta::new_readonly(global_constants::GLOBAL_ACCOUNT, false),
|
||||
AccountMeta::new(*fee_recipient, false),
|
||||
AccountMeta::new_readonly(*mint, false),
|
||||
AccountMeta::new(bonding_curve, false),
|
||||
AccountMeta::new(get_associated_token_address(&bonding_curve, mint), false),
|
||||
AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false),
|
||||
AccountMeta::new(payer.pubkey(), true),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::SYSTEM_PROGRAM, false),
|
||||
AccountMeta::new_readonly(accounts::SYSTEM_PROGRAM, false),
|
||||
AccountMeta::new(*creator_vault_pda, false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::TOKEN_PROGRAM, false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::EVENT_AUTHORITY, false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::PUMPFUN, false),
|
||||
AccountMeta::new_readonly(accounts::TOKEN_PROGRAM, false),
|
||||
AccountMeta::new_readonly(accounts::EVENT_AUTHORITY, false),
|
||||
AccountMeta::new_readonly(accounts::PUMPFUN, false),
|
||||
AccountMeta::new_readonly(get_fee_config_pda().unwrap(), false),
|
||||
AccountMeta::new_readonly(constants::pumpfun::accounts::FEE_PROGRAM, false),
|
||||
AccountMeta::new_readonly(accounts::FEE_PROGRAM, false),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,19 +5,15 @@ use spl_associated_token_account::instruction::create_associated_token_account_i
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
pumpswap::{accounts, BUY_DISCRIMINATOR, SELL_DISCRIMINATOR},
|
||||
trade::trade::DEFAULT_SLIPPAGE,
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
instruction::utils::pumpswap::{
|
||||
accounts, coin_creator_vault_ata, fee_recipient_ata, get_fee_config_pda,
|
||||
get_global_volume_accumulator_pda, get_user_volume_accumulator_pda, BUY_DISCRIMINATOR,
|
||||
SELL_DISCRIMINATOR,
|
||||
},
|
||||
trading::{
|
||||
core::{
|
||||
params::{BuyParams, PumpSwapParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
pumpswap::common::{
|
||||
coin_creator_vault_ata, coin_creator_vault_authority, fee_recipient_ata,
|
||||
get_fee_config_pda, get_global_volume_accumulator_pda, get_user_volume_accumulator_pda,
|
||||
},
|
||||
trading::core::{
|
||||
params::{BuyParams, PumpSwapParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
utils::calc::pumpswap::{buy_quote_input_internal, sell_base_input_internal},
|
||||
};
|
||||
|
||||
@@ -5,10 +5,8 @@ use spl_associated_token_account::instruction::create_associated_token_account_i
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
raydium_amm_v4::{accounts, SWAP_BASE_IN_DISCRIMINATOR},
|
||||
trade::trade::DEFAULT_SLIPPAGE,
|
||||
},
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
instruction::utils::raydium_amm_v4::{accounts, SWAP_BASE_IN_DISCRIMINATOR},
|
||||
trading::core::{
|
||||
params::{BuyParams, RaydiumAmmV4Params, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
|
||||
@@ -5,16 +5,14 @@ use spl_associated_token_account::instruction::create_associated_token_account_i
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
raydium_cpmm::{accounts, SWAP_BASE_IN_DISCRIMINATOR},
|
||||
trade::trade::DEFAULT_SLIPPAGE,
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
instruction::utils::raydium_cpmm::{
|
||||
accounts, get_observation_state_pda, get_pool_pda, get_vault_pda,
|
||||
SWAP_BASE_IN_DISCRIMINATOR,
|
||||
},
|
||||
trading::{
|
||||
core::{
|
||||
params::{BuyParams, RaydiumCpmmParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
raydium_cpmm::common::{get_observation_state_pda, get_pool_pda, get_vault_pda},
|
||||
trading::core::{
|
||||
params::{BuyParams, RaydiumCpmmParams, SellParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
utils::calc::raydium_cpmm::compute_swap_amount,
|
||||
};
|
||||
|
||||
Executable → Regular
+33
-66
@@ -1,13 +1,36 @@
|
||||
use crate::{
|
||||
common::SolanaRpcClient,
|
||||
constants::{self, bonk::accounts},
|
||||
};
|
||||
use crate::common::SolanaRpcClient;
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::{
|
||||
pool_state_decode, types::PoolState,
|
||||
};
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
pub const POOL_SEED: &[u8] = b"pool";
|
||||
pub const POOL_VAULT_SEED: &[u8] = b"pool_vault";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
pub const AUTHORITY: Pubkey = pubkey!("WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh");
|
||||
pub const GLOBAL_CONFIG: Pubkey = pubkey!("6s1xP3hpbAfFoNtUNF8mfHsjr2Bd97JxFJRWLbL6aHuX");
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
pub const EVENT_AUTHORITY: Pubkey = pubkey!("2DPAtwB8L12vrMRExbLuyGnC7n2J5LNoZQSejeQGpwkr");
|
||||
pub const WSOL_TOKEN_ACCOUNT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
|
||||
pub const BONK: Pubkey = pubkey!("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj");
|
||||
pub const SYSTEM_PROGRAM: Pubkey = solana_sdk::system_program::ID;
|
||||
|
||||
pub const PLATFORM_FEE_RATE: u128 = 100; // 1%
|
||||
pub const PROTOCOL_FEE_RATE: u128 = 25; // 0.25%
|
||||
pub const SHARE_FEE_RATE: u128 = 0; // 0%
|
||||
}
|
||||
|
||||
pub const BUY_EXECT_IN_DISCRIMINATOR: [u8; 8] = [250, 234, 13, 123, 213, 156, 19, 236];
|
||||
pub const SELL_EXECT_IN_DISCRIMINATOR: [u8; 8] = [149, 39, 222, 155, 211, 124, 152, 26];
|
||||
|
||||
pub async fn fetch_pool_state(
|
||||
rpc: &SolanaRpcClient,
|
||||
pool_address: &Pubkey,
|
||||
@@ -105,85 +128,29 @@ pub fn get_amount_out(
|
||||
}
|
||||
|
||||
pub fn get_pool_pda(base_mint: &Pubkey, quote_mint: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 3] =
|
||||
&[constants::bonk::seeds::POOL_SEED, base_mint.as_ref(), quote_mint.as_ref()];
|
||||
let program_id: &Pubkey = &constants::bonk::accounts::BONK;
|
||||
let seeds: &[&[u8]; 3] = &[seeds::POOL_SEED, base_mint.as_ref(), quote_mint.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::BONK;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
pub fn get_vault_pda(pool_state: &Pubkey, mint: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 3] =
|
||||
&[constants::bonk::seeds::POOL_VAULT_SEED, pool_state.as_ref(), mint.as_ref()];
|
||||
let program_id: &Pubkey = &constants::bonk::accounts::BONK;
|
||||
let seeds: &[&[u8]; 3] = &[seeds::POOL_VAULT_SEED, pool_state.as_ref(), mint.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::BONK;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
pub fn get_platform_associated_account(platform_config: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[platform_config.as_ref(), accounts::WSOL_TOKEN_ACCOUNT.as_ref()];
|
||||
let program_id: &Pubkey = &constants::bonk::accounts::BONK;
|
||||
let program_id: &Pubkey = &accounts::BONK;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
pub fn get_creator_associated_account(creator: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[creator.as_ref(), accounts::WSOL_TOKEN_ACCOUNT.as_ref()];
|
||||
let program_id: &Pubkey = &constants::bonk::accounts::BONK;
|
||||
let program_id: &Pubkey = &accounts::BONK;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::constants::bonk::accounts::{PLATFORM_FEE_RATE, PROTOCOL_FEE_RATE, SHARE_FEE_RATE};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_amount_in_out_consistency() {
|
||||
// 测试参数
|
||||
let protocol_fee_rate = PROTOCOL_FEE_RATE;
|
||||
let platform_fee_rate = PLATFORM_FEE_RATE;
|
||||
let share_fee_rate = SHARE_FEE_RATE;
|
||||
let virtual_base = 1073025605596382;
|
||||
let virtual_quote = 30000852951;
|
||||
let real_base = 0;
|
||||
let real_quote = 0;
|
||||
let slippage_basis_points = 0;
|
||||
|
||||
let original_amount_in = 2000000000;
|
||||
|
||||
let geet_amount_out_result = get_amount_out(
|
||||
original_amount_in,
|
||||
protocol_fee_rate,
|
||||
platform_fee_rate,
|
||||
share_fee_rate,
|
||||
virtual_base,
|
||||
virtual_quote,
|
||||
real_base,
|
||||
real_quote,
|
||||
slippage_basis_points,
|
||||
);
|
||||
|
||||
let amount_out = 25959582643397;
|
||||
let get_amount_in_result = get_amount_in(
|
||||
amount_out,
|
||||
protocol_fee_rate,
|
||||
platform_fee_rate,
|
||||
share_fee_rate,
|
||||
virtual_base,
|
||||
virtual_quote,
|
||||
real_base,
|
||||
real_quote,
|
||||
slippage_basis_points,
|
||||
);
|
||||
|
||||
println!("Original amount_in: {}", original_amount_in);
|
||||
println!("Amount_out: {}", geet_amount_out_result);
|
||||
println!("Calculated amount_in: {}", get_amount_in_result);
|
||||
|
||||
assert!(geet_amount_out_result == 66275810509273);
|
||||
assert!(get_amount_in_result == 753217040);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod bonk;
|
||||
pub mod pumpfun;
|
||||
pub mod pumpswap;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod raydium_cpmm;
|
||||
@@ -0,0 +1,259 @@
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use crate::{
|
||||
common::{bonding_curve::BondingCurveAccount, global::GlobalAccount, SolanaRpcClient},
|
||||
constants::{self, trade::trade::DEFAULT_SLIPPAGE},
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
/// Seed for the global state PDA
|
||||
pub const GLOBAL_SEED: &[u8] = b"global";
|
||||
|
||||
/// Seed for the mint authority PDA
|
||||
pub const MINT_AUTHORITY_SEED: &[u8] = b"mint-authority";
|
||||
|
||||
/// Seed for bonding curve PDAs
|
||||
pub const BONDING_CURVE_SEED: &[u8] = b"bonding-curve";
|
||||
|
||||
/// Seed for creator vault PDAs
|
||||
pub const CREATOR_VAULT_SEED: &[u8] = b"creator-vault";
|
||||
|
||||
/// Seed for metadata PDAs
|
||||
pub const METADATA_SEED: &[u8] = b"metadata";
|
||||
|
||||
/// Seed for user volume accumulator PDAs
|
||||
pub const USER_VOLUME_ACCUMULATOR_SEED: &[u8] = b"user_volume_accumulator";
|
||||
|
||||
/// Seed for global volume accumulator PDAs
|
||||
pub const GLOBAL_VOLUME_ACCUMULATOR_SEED: &[u8] = b"global_volume_accumulator";
|
||||
|
||||
pub const FEE_CONFIG_SEED: &[u8] = b"fee_config";
|
||||
}
|
||||
|
||||
pub mod global_constants {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
pub const INITIAL_VIRTUAL_TOKEN_RESERVES: u64 = 1_073_000_000_000_000;
|
||||
|
||||
pub const INITIAL_VIRTUAL_SOL_RESERVES: u64 = 30_000_000_000;
|
||||
|
||||
pub const INITIAL_REAL_TOKEN_RESERVES: u64 = 793_100_000_000_000;
|
||||
|
||||
pub const TOKEN_TOTAL_SUPPLY: u64 = 1_000_000_000_000_000;
|
||||
|
||||
pub const FEE_BASIS_POINTS: u64 = 95;
|
||||
|
||||
pub const ENABLE_MIGRATE: bool = false;
|
||||
|
||||
pub const POOL_MIGRATION_FEE: u64 = 15_000_001;
|
||||
|
||||
pub const CREATOR_FEE: u64 = 5;
|
||||
|
||||
pub const SCALE: u64 = 1_000_000; // 10^6 for token decimals
|
||||
|
||||
pub const LAMPORTS_PER_SOL: u64 = 1_000_000_000; // 10^9 for solana lamports
|
||||
|
||||
pub const COMPLETION_LAMPORTS: u64 = 85 * LAMPORTS_PER_SOL; // ~ 85 SOL
|
||||
|
||||
/// Public key for the fee recipient
|
||||
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
/// Public key for the global PDA
|
||||
pub const GLOBAL_ACCOUNT: Pubkey = pubkey!("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf");
|
||||
|
||||
/// Public key for the authority
|
||||
pub const AUTHORITY: Pubkey = pubkey!("FFWtrEQ4B4PKQoVuHYzZq8FabGkVatYzDpEVHsK5rrhF");
|
||||
|
||||
/// Public key for the withdraw authority
|
||||
pub const WITHDRAW_AUTHORITY: Pubkey = pubkey!("39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg");
|
||||
|
||||
pub const PUMPFUN_AMM_FEE_1: Pubkey = pubkey!("7VtfL8fvgNfhz17qKRMjzQEXgbdpnHHHQRh54R9jP2RJ"); // Pump.fun AMM: Protocol Fee 1
|
||||
pub const PUMPFUN_AMM_FEE_2: Pubkey = pubkey!("7hTckgnGnLQR6sdH7YkqFTAA7VwTfYFaZ6EhEsU3saCX"); // Pump.fun AMM: Protocol Fee 2
|
||||
pub const PUMPFUN_AMM_FEE_3: Pubkey = pubkey!("9rPYyANsfQZw3DnDmKE3YCQF5E8oD89UXoHn9JFEhJUz"); // Pump.fun AMM: Protocol Fee 3
|
||||
pub const PUMPFUN_AMM_FEE_4: Pubkey = pubkey!("AVmoTthdrX6tKt4nDjco2D775W2YK3sDhxPcMmzUAmTY"); // Pump.fun AMM: Protocol Fee 4
|
||||
pub const PUMPFUN_AMM_FEE_5: Pubkey = pubkey!("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM"); // Pump.fun AMM: Protocol Fee 5
|
||||
pub const PUMPFUN_AMM_FEE_6: Pubkey = pubkey!("FWsW1xNtWscwNmKv6wVsU1iTzRN6wmmk3MjxRP5tT7hz"); // Pump.fun AMM: Protocol Fee 6
|
||||
pub const PUMPFUN_AMM_FEE_7: Pubkey = pubkey!("G5UZAVbAf46s7cKWoyKu8kYTip9DGTpbLZ2qa9Aq69dP");
|
||||
// Pump.fun AMM: Protocol Fee 7
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
/// Public key for the Pump.fun program
|
||||
pub const PUMPFUN: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");
|
||||
|
||||
/// Public key for the MPL Token Metadata program
|
||||
pub const MPL_TOKEN_METADATA: Pubkey = pubkey!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
||||
|
||||
/// Authority for program events
|
||||
pub const EVENT_AUTHORITY: Pubkey = pubkey!("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1");
|
||||
|
||||
/// System Program ID
|
||||
pub const SYSTEM_PROGRAM: Pubkey = pubkey!("11111111111111111111111111111111");
|
||||
|
||||
/// Token Program ID
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
|
||||
/// Associated Token Program ID
|
||||
pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey =
|
||||
pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
|
||||
/// Rent Sysvar ID
|
||||
pub const RENT: Pubkey = pubkey!("SysvarRent111111111111111111111111111111111");
|
||||
|
||||
pub const AMM_PROGRAM: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
|
||||
|
||||
pub const FEE_PROGRAM: Pubkey = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
||||
}
|
||||
|
||||
pub struct Symbol;
|
||||
|
||||
impl Symbol {
|
||||
pub const SOLANA: &'static str = "solana";
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref ACCOUNT_CACHE: RwLock<HashMap<Pubkey, Arc<GlobalAccount>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_global_pda() -> Pubkey {
|
||||
static GLOBAL_PDA: once_cell::sync::Lazy<Pubkey> = once_cell::sync::Lazy::new(|| {
|
||||
Pubkey::find_program_address(&[seeds::GLOBAL_SEED], &accounts::PUMPFUN).0
|
||||
});
|
||||
*GLOBAL_PDA
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mint_authority_pda() -> Pubkey {
|
||||
static MINT_AUTHORITY_PDA: once_cell::sync::Lazy<Pubkey> = once_cell::sync::Lazy::new(|| {
|
||||
Pubkey::find_program_address(&[seeds::MINT_AUTHORITY_SEED], &accounts::PUMPFUN).0
|
||||
});
|
||||
*MINT_AUTHORITY_PDA
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::BONDING_CURVE_SEED, mint.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::CREATOR_VAULT_SEED, creator.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_global_volume_accumulator_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 1] = &[seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
||||
let program_id: &Pubkey = &accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_fee_config_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[seeds::FEE_CONFIG_SEED, accounts::PUMPFUN.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::FEE_PROGRAM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_metadata_pda(mint: &Pubkey) -> Pubkey {
|
||||
Pubkey::find_program_address(
|
||||
&[seeds::METADATA_SEED, accounts::MPL_TOKEN_METADATA.as_ref(), mint.as_ref()],
|
||||
&accounts::MPL_TOKEN_METADATA,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_global_account(/*rpc: &SolanaRpcClient*/
|
||||
) -> Result<Arc<GlobalAccount>, anyhow::Error> {
|
||||
let global_account = GlobalAccount::new();
|
||||
let global_account = Arc::new(global_account);
|
||||
Ok(global_account)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_initial_buy_price(
|
||||
global_account: &Arc<GlobalAccount>,
|
||||
amount_sol: u64,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let buy_amount = global_account.get_initial_buy_price(amount_sol);
|
||||
Ok(buy_amount)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn fetch_bonding_curve_account(
|
||||
rpc: &SolanaRpcClient,
|
||||
mint: &Pubkey,
|
||||
) -> Result<(Arc<crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>, Pubkey), anyhow::Error>{
|
||||
let bonding_curve_pda: Pubkey =
|
||||
get_bonding_curve_pda(mint).ok_or(anyhow!("Bonding curve not found"))?;
|
||||
|
||||
let account = rpc.get_account(&bonding_curve_pda).await?;
|
||||
if account.data.is_empty() {
|
||||
return Err(anyhow!("Bonding curve not found"));
|
||||
}
|
||||
|
||||
let bonding_curve = solana_sdk::borsh1::try_from_slice_unchecked::<crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>(&account.data[8..])
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize bonding curve account: {}", e))?;
|
||||
|
||||
Ok((Arc::new(bonding_curve), bonding_curve_pda))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn init_bonding_curve_account(
|
||||
mint: &Pubkey,
|
||||
dev_buy_token: u64,
|
||||
dev_sol_cost: u64,
|
||||
creator: Pubkey,
|
||||
) -> Result<Arc<BondingCurveAccount>, anyhow::Error> {
|
||||
let bonding_curve =
|
||||
BondingCurveAccount::from_dev_trade(mint, dev_buy_token, dev_sol_cost, creator);
|
||||
let bonding_curve = Arc::new(bonding_curve);
|
||||
Ok(bonding_curve)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_buy_amount_with_slippage(amount_sol: u64, slippage_basis_points: Option<u64>) -> u64 {
|
||||
let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
||||
amount_sol + (amount_sol * slippage / 10000)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_buy_price(amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
||||
if amount == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let n: u128 =
|
||||
(trade_info.virtual_sol_reserves as u128) * (trade_info.virtual_token_reserves as u128);
|
||||
let i: u128 = (trade_info.virtual_sol_reserves as u128) + (amount as u128);
|
||||
let r: u128 = n / i + 1;
|
||||
let s: u128 = (trade_info.virtual_token_reserves as u128) - r;
|
||||
let s_u64 = s as u64;
|
||||
|
||||
s_u64.min(trade_info.real_token_reserves)
|
||||
}
|
||||
Executable → Regular
+74
-16
@@ -1,10 +1,72 @@
|
||||
use crate::constants::pumpswap::accounts;
|
||||
use crate::{common::SolanaRpcClient, constants};
|
||||
use anyhow::anyhow;
|
||||
use solana_account_decoder::UiAccountEncoding;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpswap::types::{pool_decode, Pool};
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
/// Seed for the global state PDA
|
||||
pub const GLOBAL_SEED: &[u8] = b"global";
|
||||
|
||||
/// Seed for the mint authority PDA
|
||||
pub const MINT_AUTHORITY_SEED: &[u8] = b"mint-authority";
|
||||
|
||||
/// Seed for bonding curve PDAs
|
||||
pub const BONDING_CURVE_SEED: &[u8] = b"bonding-curve";
|
||||
|
||||
/// Seed for metadata PDAs
|
||||
pub const METADATA_SEED: &[u8] = b"metadata";
|
||||
|
||||
pub const USER_VOLUME_ACCUMULATOR_SEED: &[u8] = b"user_volume_accumulator";
|
||||
pub const GLOBAL_VOLUME_ACCUMULATOR_SEED: &[u8] = b"global_volume_accumulator";
|
||||
pub const FEE_CONFIG_SEED: &[u8] = b"fee_config";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
/// Public key for the fee recipient
|
||||
pub const FEE_RECIPIENT: Pubkey = pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
/// Public key for the global PDA
|
||||
pub const GLOBAL_ACCOUNT: Pubkey = pubkey!("ADyA8hdefvWN2dbGGWFotbzWxrAvLW83WG6QCVXvJKqw");
|
||||
|
||||
/// Authority for program events
|
||||
pub const EVENT_AUTHORITY: Pubkey = pubkey!("GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR");
|
||||
|
||||
pub const WSOL_TOKEN_ACCOUNT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
|
||||
|
||||
/// System Program ID
|
||||
pub const SYSTEM_PROGRAM: Pubkey = pubkey!("11111111111111111111111111111111");
|
||||
|
||||
/// Token Program ID
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
|
||||
/// Associated Token Program ID
|
||||
pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey =
|
||||
pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
|
||||
// PumpSwap 协议费用接收者
|
||||
pub const PROTOCOL_FEE_RECIPIENT: Pubkey =
|
||||
pubkey!("62qc2CNXwrYqQScmEdiZFFAnJR262PxWEuNQtxfafNgV");
|
||||
|
||||
/// Rent Sysvar ID
|
||||
pub const RENT: Pubkey = pubkey!("SysvarRent111111111111111111111111111111111");
|
||||
|
||||
pub const AMM_PROGRAM: Pubkey = pubkey!("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA");
|
||||
|
||||
pub const LP_FEE_BASIS_POINTS: u64 = 20;
|
||||
pub const PROTOCOL_FEE_BASIS_POINTS: u64 = 5;
|
||||
pub const COIN_CREATOR_FEE_BASIS_POINTS: u64 = 5;
|
||||
|
||||
pub const FEE_PROGRAM: Pubkey = pubkey!("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ");
|
||||
}
|
||||
|
||||
pub const BUY_DISCRIMINATOR: [u8; 8] = [102, 6, 61, 18, 1, 218, 235, 234];
|
||||
pub const SELL_DISCRIMINATOR: [u8; 8] = [51, 230, 133, 164, 1, 127, 131, 173];
|
||||
|
||||
// Find a pool for a specific mint
|
||||
pub async fn find_pool(rpc: &SolanaRpcClient, mint: &Pubkey) -> Result<Pubkey, anyhow::Error> {
|
||||
let (pool_address, _) = find_by_mint(rpc, mint).await?;
|
||||
@@ -14,7 +76,7 @@ pub async fn find_pool(rpc: &SolanaRpcClient, mint: &Pubkey) -> Result<Pubkey, a
|
||||
pub(crate) fn coin_creator_vault_authority(coin_creator: Pubkey) -> Pubkey {
|
||||
let (pump_pool_authority, _) = Pubkey::find_program_address(
|
||||
&[b"creator_vault", &coin_creator.to_bytes()],
|
||||
&crate::constants::pumpswap::accounts::AMM_PROGRAM,
|
||||
&accounts::AMM_PROGRAM,
|
||||
);
|
||||
pump_pool_authority
|
||||
}
|
||||
@@ -25,7 +87,7 @@ pub(crate) fn coin_creator_vault_ata(coin_creator: Pubkey, quote_mint: Pubkey) -
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&creator_vault_authority,
|
||||
"e_mint,
|
||||
&crate::constants::pumpswap::accounts::TOKEN_PROGRAM,
|
||||
&accounts::TOKEN_PROGRAM,
|
||||
);
|
||||
associated_token_creator_vault_authority
|
||||
}
|
||||
@@ -35,22 +97,21 @@ pub(crate) fn fee_recipient_ata(fee_recipient: Pubkey, quote_mint: Pubkey) -> Pu
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&fee_recipient,
|
||||
"e_mint,
|
||||
&crate::constants::pumpswap::accounts::TOKEN_PROGRAM,
|
||||
&accounts::TOKEN_PROGRAM,
|
||||
);
|
||||
associated_token_fee_recipient
|
||||
}
|
||||
|
||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] =
|
||||
&[&crate::constants::pumpswap::seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
||||
let program_id: &Pubkey = &&crate::constants::pumpswap::accounts::AMM_PROGRAM;
|
||||
let seeds: &[&[u8]; 2] = &[&seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
||||
let program_id: &Pubkey = &&accounts::AMM_PROGRAM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
pub fn get_global_volume_accumulator_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 1] = &[&crate::constants::pumpswap::seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
||||
let program_id: &Pubkey = &&crate::constants::pumpswap::accounts::AMM_PROGRAM;
|
||||
let seeds: &[&[u8]; 1] = &[&seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
||||
let program_id: &Pubkey = &&accounts::AMM_PROGRAM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
@@ -89,7 +150,7 @@ pub async fn find_by_base_mint(
|
||||
with_context: None,
|
||||
sort_results: None,
|
||||
};
|
||||
let program_id = crate::constants::pumpswap::accounts::AMM_PROGRAM;
|
||||
let program_id = accounts::AMM_PROGRAM;
|
||||
let accounts = rpc.get_program_accounts_with_config(&program_id, config).await?;
|
||||
if accounts.is_empty() {
|
||||
return Err(anyhow!("No pool found for mint {}", base_mint));
|
||||
@@ -125,7 +186,7 @@ pub async fn find_by_quote_mint(
|
||||
with_context: None,
|
||||
sort_results: None,
|
||||
};
|
||||
let program_id = crate::constants::pumpswap::accounts::AMM_PROGRAM;
|
||||
let program_id = accounts::AMM_PROGRAM;
|
||||
let accounts = rpc.get_program_accounts_with_config(&program_id, config).await?;
|
||||
if accounts.is_empty() {
|
||||
return Err(anyhow!("No pool found for mint {}", quote_mint));
|
||||
@@ -167,11 +228,8 @@ pub async fn get_token_balances(
|
||||
|
||||
#[inline]
|
||||
pub fn get_fee_config_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[
|
||||
constants::pumpswap::seeds::FEE_CONFIG_SEED,
|
||||
constants::pumpswap::accounts::AMM_PROGRAM.as_ref(),
|
||||
];
|
||||
let program_id: &Pubkey = &constants::pumpswap::accounts::FEE_PROGRAM;
|
||||
let seeds: &[&[u8]; 2] = &[seeds::FEE_CONFIG_SEED, accounts::AMM_PROGRAM.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::FEE_PROGRAM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
Executable → Regular
+14
-11
@@ -1,14 +1,10 @@
|
||||
//! Constants used by the crate.
|
||||
//!
|
||||
//! This module contains various constants used throughout the crate, including:
|
||||
//!
|
||||
//! - Seeds for deriving Program Derived Addresses (PDAs)
|
||||
//! - Program account addresses and public keys
|
||||
//!
|
||||
//! The constants are organized into submodules for better organization:
|
||||
//!
|
||||
//! - `seeds`: Contains seed values used for PDA derivation
|
||||
//! - `accounts`: Contains important program account addresses
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::types::{
|
||||
amm_info_decode, AmmInfo,
|
||||
};
|
||||
|
||||
use crate::common::SolanaRpcClient;
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
@@ -31,3 +27,10 @@ pub mod accounts {
|
||||
|
||||
pub const SWAP_BASE_IN_DISCRIMINATOR: &[u8] = &[9];
|
||||
pub const SWAP_BASE_OUT_DISCRIMINATOR: &[u8] = &[11];
|
||||
|
||||
pub async fn fetch_amm_info(rpc: &SolanaRpcClient, amm: Pubkey) -> Result<AmmInfo, anyhow::Error> {
|
||||
let amm_info = rpc.get_account_data(&amm).await?;
|
||||
let amm_info =
|
||||
amm_info_decode(&amm_info).ok_or_else(|| anyhow!("Failed to decode amm info"))?;
|
||||
Ok(amm_info)
|
||||
}
|
||||
Executable → Regular
+34
-55
@@ -1,16 +1,36 @@
|
||||
use crate::{
|
||||
common::SolanaRpcClient,
|
||||
constants::{
|
||||
self,
|
||||
raydium_cpmm::accounts::{self},
|
||||
},
|
||||
};
|
||||
use crate::common::SolanaRpcClient;
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::types::{
|
||||
pool_state_decode, PoolState,
|
||||
};
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
pub const POOL_SEED: &[u8] = b"pool";
|
||||
pub const POOL_VAULT_SEED: &[u8] = b"pool_vault";
|
||||
pub const OBSERVATION_STATE_SEED: &[u8] = b"observation";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
pub const AUTHORITY: Pubkey = pubkey!("GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL");
|
||||
pub const AMM_CONFIG: Pubkey = pubkey!("D4FPEruKEHrG5TenZ2mpDGEfu1iUvTiqBxvpU8HLBvC2");
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
pub const WSOL_TOKEN_ACCOUNT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
|
||||
pub const RAYDIUM_CPMM: Pubkey = pubkey!("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C");
|
||||
|
||||
pub const FEE_RATE_DENOMINATOR_VALUE: u128 = 1_000_000;
|
||||
pub const TRADE_FEE_RATE: u64 = 2500;
|
||||
pub const CREATOR_FEE_RATE: u64 = 0;
|
||||
pub const PROTOCOL_FEE_RATE: u64 = 120000;
|
||||
pub const FUND_FEE_RATE: u64 = 40000;
|
||||
}
|
||||
|
||||
pub const SWAP_BASE_IN_DISCRIMINATOR: &[u8] = &[143, 190, 90, 218, 196, 30, 51, 222];
|
||||
pub const SWAP_BASE_OUT_DISCRIMINATOR: &[u8] = &[55, 217, 98, 86, 163, 74, 180, 173];
|
||||
|
||||
pub async fn fetch_pool_state(
|
||||
rpc: &SolanaRpcClient,
|
||||
pool_address: &Pubkey,
|
||||
@@ -25,29 +45,23 @@ pub async fn fetch_pool_state(
|
||||
}
|
||||
|
||||
pub fn get_pool_pda(amm_config: &Pubkey, mint1: &Pubkey, mint2: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 4] = &[
|
||||
constants::raydium_cpmm::seeds::POOL_SEED,
|
||||
amm_config.as_ref(),
|
||||
mint1.as_ref(),
|
||||
mint2.as_ref(),
|
||||
];
|
||||
let program_id: &Pubkey = &constants::raydium_cpmm::accounts::RAYDIUM_CPMM;
|
||||
let seeds: &[&[u8]; 4] =
|
||||
&[seeds::POOL_SEED, amm_config.as_ref(), mint1.as_ref(), mint2.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::RAYDIUM_CPMM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
pub fn get_vault_pda(pool_state: &Pubkey, mint: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 3] =
|
||||
&[constants::raydium_cpmm::seeds::POOL_VAULT_SEED, pool_state.as_ref(), mint.as_ref()];
|
||||
let program_id: &Pubkey = &constants::raydium_cpmm::accounts::RAYDIUM_CPMM;
|
||||
let seeds: &[&[u8]; 3] = &[seeds::POOL_VAULT_SEED, pool_state.as_ref(), mint.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::RAYDIUM_CPMM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
pub fn get_observation_state_pda(pool_state: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] =
|
||||
&[constants::raydium_cpmm::seeds::OBSERVATION_STATE_SEED, pool_state.as_ref()];
|
||||
let program_id: &Pubkey = &constants::raydium_cpmm::accounts::RAYDIUM_CPMM;
|
||||
let seeds: &[&[u8]; 2] = &[seeds::OBSERVATION_STATE_SEED, pool_state.as_ref()];
|
||||
let program_id: &Pubkey = &accounts::RAYDIUM_CPMM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
@@ -96,38 +110,3 @@ pub async fn calculate_price(
|
||||
let price = token1_adjusted / token0_adjusted;
|
||||
Ok(price)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_sdk::pubkey;
|
||||
|
||||
#[test]
|
||||
fn test_get_pool_pda() {
|
||||
// 测试get_pool_pda函数
|
||||
let amm_config = constants::raydium_cpmm::accounts::AMM_CONFIG;
|
||||
let input_mint = pubkey!("So11111111111111111111111111111111111111112"); // WSOL
|
||||
let output_mint = pubkey!("BnwbwoqPm5ZNx7YTJ8g9jR2qCpYeHBC7xxpU8zEtbonk"); // USDC
|
||||
let pool_state = pubkey!("E9rRRpcdsKAseeLFbwC1Ewxd3aYG27meqwTTrMfCTbSG");
|
||||
let result = get_pool_pda(&amm_config, &input_mint, &output_mint);
|
||||
assert_eq!(result, Some(pool_state));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_vault_pda() {
|
||||
// 测试get_vault_pda函数
|
||||
let pool_state = pubkey!("HBMkgQvt4NAFx6XzNav23bNcv6K3oiC5UfY3JsE22scY");
|
||||
let mint = pubkey!("DeESECsL3cLXno1LFquss98kNQSno1xpQC2ERCqSbonk"); // WSOL
|
||||
let vault_pda = pubkey!("7rkgNG3A8z636DuzhchKeqAJTaH3H5ZFWmBQeStydovA");
|
||||
let result = get_vault_pda(&pool_state, &mint);
|
||||
assert_eq!(result, Some(vault_pda));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_observation_state_pda() {
|
||||
let pool_state = pubkey!("HBMkgQvt4NAFx6XzNav23bNcv6K3oiC5UfY3JsE22scY");
|
||||
let observation_state_pda = pubkey!("Gq8u9N18ASjq3AK2gCk6RtGSNyjXZf9EZDb6vTtB9JRs");
|
||||
let result = get_observation_state_pda(&pool_state);
|
||||
assert_eq!(result, Some(observation_state_pda));
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod common;
|
||||
+47
-40
@@ -10,20 +10,9 @@ use std::sync::Arc;
|
||||
use super::traits::ProtocolParams;
|
||||
use crate::common::bonding_curve::BondingCurveAccount;
|
||||
use crate::common::{PriorityFee, SolanaRpcClient};
|
||||
use crate::constants::bonk::accounts::{
|
||||
self, PLATFORM_FEE_RATE, PROTOCOL_FEE_RATE, SHARE_FEE_RATE,
|
||||
};
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use crate::trading::bonk::common::{
|
||||
get_amount_in, get_amount_in_net, get_amount_out, get_creator_associated_account,
|
||||
get_platform_associated_account,
|
||||
};
|
||||
use crate::trading::common::get_multi_token_balances;
|
||||
use crate::trading::pumpswap::common::{
|
||||
coin_creator_vault_ata, coin_creator_vault_authority, get_token_balances,
|
||||
};
|
||||
use crate::trading::raydium_cpmm::common::get_pool_token_balances;
|
||||
/// Buy parameters
|
||||
#[derive(Clone)]
|
||||
pub struct BuyParams {
|
||||
@@ -185,24 +174,28 @@ impl PumpSwapParams {
|
||||
rpc: &SolanaRpcClient,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let pool_data = crate::trading::pumpswap::common::fetch_pool(rpc, pool_address).await?;
|
||||
let pool_data = crate::instruction::utils::pumpswap::fetch_pool(rpc, pool_address).await?;
|
||||
let (pool_base_token_reserves, pool_quote_token_reserves) =
|
||||
get_token_balances(&pool_data, rpc).await?;
|
||||
crate::instruction::utils::pumpswap::get_token_balances(&pool_data, rpc).await?;
|
||||
let creator = pool_data.creator;
|
||||
let coin_creator_vault_ata = coin_creator_vault_ata(creator, pool_data.quote_mint);
|
||||
let coin_creator_vault_authority = coin_creator_vault_authority(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 =
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&pool_address,
|
||||
&pool_data.base_mint,
|
||||
&accounts::TOKEN_PROGRAM,
|
||||
&crate::instruction::utils::pumpswap::accounts::TOKEN_PROGRAM,
|
||||
);
|
||||
let quote_token_program_ata =
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&pool_address,
|
||||
&pool_data.quote_mint,
|
||||
&accounts::TOKEN_PROGRAM,
|
||||
&crate::instruction::utils::pumpswap::accounts::TOKEN_PROGRAM,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
@@ -214,12 +207,12 @@ impl PumpSwapParams {
|
||||
coin_creator_vault_ata: coin_creator_vault_ata,
|
||||
coin_creator_vault_authority: coin_creator_vault_authority,
|
||||
base_token_program: if pool_data.pool_base_token_account == base_token_program_ata {
|
||||
accounts::TOKEN_PROGRAM
|
||||
crate::instruction::utils::pumpswap::accounts::TOKEN_PROGRAM
|
||||
} else {
|
||||
spl_token_2022::ID
|
||||
},
|
||||
quote_token_program: if pool_data.pool_quote_token_account == quote_token_program_ata {
|
||||
accounts::TOKEN_PROGRAM
|
||||
crate::instruction::utils::pumpswap::accounts::TOKEN_PROGRAM
|
||||
} else {
|
||||
spl_token_2022::ID
|
||||
},
|
||||
@@ -291,11 +284,11 @@ impl BonkParams {
|
||||
let amount_in = if trade_info.metadata.event_type == EventType::BonkBuyExactIn {
|
||||
trade_info.amount_in
|
||||
} else {
|
||||
get_amount_in(
|
||||
crate::instruction::utils::bonk::get_amount_in(
|
||||
trade_info.amount_out,
|
||||
PROTOCOL_FEE_RATE,
|
||||
PLATFORM_FEE_RATE,
|
||||
SHARE_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::PROTOCOL_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::PLATFORM_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::SHARE_FEE_RATE,
|
||||
DEFAULT_VIRTUAL_BASE,
|
||||
DEFAULT_VIRTUAL_QUOTE,
|
||||
0,
|
||||
@@ -303,15 +296,18 @@ impl BonkParams {
|
||||
0,
|
||||
)
|
||||
};
|
||||
let real_quote =
|
||||
get_amount_in_net(amount_in, PROTOCOL_FEE_RATE, PLATFORM_FEE_RATE, SHARE_FEE_RATE)
|
||||
as u128;
|
||||
let real_quote = crate::instruction::utils::bonk::get_amount_in_net(
|
||||
amount_in,
|
||||
crate::instruction::utils::bonk::accounts::PROTOCOL_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::PLATFORM_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::SHARE_FEE_RATE,
|
||||
) as u128;
|
||||
let amount_out = if trade_info.metadata.event_type == EventType::BonkBuyExactIn {
|
||||
get_amount_out(
|
||||
crate::instruction::utils::bonk::get_amount_out(
|
||||
trade_info.amount_in,
|
||||
PROTOCOL_FEE_RATE,
|
||||
PLATFORM_FEE_RATE,
|
||||
SHARE_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::PROTOCOL_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::PLATFORM_FEE_RATE,
|
||||
crate::instruction::utils::bonk::accounts::SHARE_FEE_RATE,
|
||||
DEFAULT_VIRTUAL_BASE,
|
||||
DEFAULT_VIRTUAL_QUOTE,
|
||||
0,
|
||||
@@ -339,14 +335,20 @@ impl BonkParams {
|
||||
rpc: &SolanaRpcClient,
|
||||
mint: &Pubkey,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let pool_address =
|
||||
crate::trading::bonk::common::get_pool_pda(mint, &accounts::WSOL_TOKEN_ACCOUNT)
|
||||
.unwrap();
|
||||
let pool_data = crate::trading::bonk::common::fetch_pool_state(rpc, &pool_address).await?;
|
||||
let pool_address = crate::instruction::utils::bonk::get_pool_pda(
|
||||
mint,
|
||||
&crate::instruction::utils::bonk::accounts::WSOL_TOKEN_ACCOUNT,
|
||||
)
|
||||
.unwrap();
|
||||
let pool_data =
|
||||
crate::instruction::utils::bonk::fetch_pool_state(rpc, &pool_address).await?;
|
||||
let token_account = rpc.get_account(&pool_data.base_mint).await?;
|
||||
let platform_associated_account =
|
||||
get_platform_associated_account(&pool_data.platform_config);
|
||||
let creator_associated_account = get_creator_associated_account(&pool_data.creator);
|
||||
crate::instruction::utils::bonk::get_platform_associated_account(
|
||||
&pool_data.platform_config,
|
||||
);
|
||||
let creator_associated_account =
|
||||
crate::instruction::utils::bonk::get_creator_associated_account(&pool_data.creator);
|
||||
let platform_associated_account = platform_associated_account.unwrap();
|
||||
let creator_associated_account = creator_associated_account.unwrap();
|
||||
Ok(Self {
|
||||
@@ -399,10 +401,15 @@ impl RaydiumCpmmParams {
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let pool =
|
||||
crate::trading::raydium_cpmm::common::fetch_pool_state(rpc, pool_address).await?;
|
||||
crate::instruction::utils::raydium_cpmm::fetch_pool_state(rpc, pool_address).await?;
|
||||
let (token0_balance, token1_balance) =
|
||||
get_pool_token_balances(rpc, pool_address, &pool.token0_mint, &pool.token1_mint)
|
||||
.await?;
|
||||
crate::instruction::utils::raydium_cpmm::get_pool_token_balances(
|
||||
rpc,
|
||||
pool_address,
|
||||
&pool.token0_mint,
|
||||
&pool.token1_mint,
|
||||
)
|
||||
.await?;
|
||||
Ok(Self {
|
||||
base_mint: pool.token0_mint,
|
||||
quote_mint: pool.token1_mint,
|
||||
@@ -469,7 +476,7 @@ impl RaydiumAmmV4Params {
|
||||
rpc: &SolanaRpcClient,
|
||||
amm: Pubkey,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let amm_info = crate::trading::raydium_amm_v4::common::fetch_amm_info(rpc, amm).await?;
|
||||
let amm_info = crate::instruction::utils::raydium_amm_v4::fetch_amm_info(rpc, amm).await?;
|
||||
let (coin_reserve, pc_reserve) =
|
||||
get_multi_token_balances(rpc, &amm_info.token_coin, &amm_info.token_pc).await?;
|
||||
Ok(Self {
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
pub mod bonk;
|
||||
pub mod common;
|
||||
pub mod core;
|
||||
pub mod factory;
|
||||
pub mod middleware;
|
||||
pub mod pumpfun;
|
||||
pub mod pumpswap;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod raydium_cpmm;
|
||||
|
||||
pub use core::params::{BuyParams, SellParams};
|
||||
pub use core::traits::{InstructionBuilder, TradeExecutor};
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use crate::{
|
||||
common::{
|
||||
bonding_curve::BondingCurveAccount, global::GlobalAccount, PriorityFee, SolanaRpcClient,
|
||||
},
|
||||
constants::{self, trade::trade::DEFAULT_SLIPPAGE},
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::{
|
||||
compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey,
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref ACCOUNT_CACHE: RwLock<HashMap<Pubkey, Arc<GlobalAccount>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_global_pda() -> Pubkey {
|
||||
static GLOBAL_PDA: once_cell::sync::Lazy<Pubkey> = once_cell::sync::Lazy::new(|| {
|
||||
Pubkey::find_program_address(
|
||||
&[constants::pumpfun::seeds::GLOBAL_SEED],
|
||||
&constants::pumpfun::accounts::PUMPFUN,
|
||||
)
|
||||
.0
|
||||
});
|
||||
*GLOBAL_PDA
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mint_authority_pda() -> Pubkey {
|
||||
static MINT_AUTHORITY_PDA: once_cell::sync::Lazy<Pubkey> = once_cell::sync::Lazy::new(|| {
|
||||
Pubkey::find_program_address(
|
||||
&[constants::pumpfun::seeds::MINT_AUTHORITY_SEED],
|
||||
&constants::pumpfun::accounts::PUMPFUN,
|
||||
)
|
||||
.0
|
||||
});
|
||||
*MINT_AUTHORITY_PDA
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[constants::pumpfun::seeds::BONDING_CURVE_SEED, mint.as_ref()];
|
||||
let program_id: &Pubkey = &constants::pumpfun::accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[constants::pumpfun::seeds::CREATOR_VAULT_SEED, creator.as_ref()];
|
||||
let program_id: &Pubkey = &constants::pumpfun::accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] =
|
||||
&[constants::pumpfun::seeds::USER_VOLUME_ACCUMULATOR_SEED, user.as_ref()];
|
||||
let program_id: &Pubkey = &constants::pumpfun::accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_global_volume_accumulator_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 1] = &[constants::pumpfun::seeds::GLOBAL_VOLUME_ACCUMULATOR_SEED];
|
||||
let program_id: &Pubkey = &constants::pumpfun::accounts::PUMPFUN;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_fee_config_pda() -> Option<Pubkey> {
|
||||
let seeds: &[&[u8]; 2] = &[
|
||||
constants::pumpfun::seeds::FEE_CONFIG_SEED,
|
||||
constants::pumpfun::accounts::PUMPFUN.as_ref(),
|
||||
];
|
||||
let program_id: &Pubkey = &constants::pumpfun::accounts::FEE_PROGRAM;
|
||||
let pda: Option<(Pubkey, u8)> = Pubkey::try_find_program_address(seeds, program_id);
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_metadata_pda(mint: &Pubkey) -> Pubkey {
|
||||
Pubkey::find_program_address(
|
||||
&[
|
||||
constants::pumpfun::seeds::METADATA_SEED,
|
||||
constants::pumpfun::accounts::MPL_TOKEN_METADATA.as_ref(),
|
||||
mint.as_ref(),
|
||||
],
|
||||
&constants::pumpfun::accounts::MPL_TOKEN_METADATA,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_global_account(/*rpc: &SolanaRpcClient*/
|
||||
) -> Result<Arc<GlobalAccount>, anyhow::Error> {
|
||||
let global_account = GlobalAccount::new();
|
||||
let global_account = Arc::new(global_account);
|
||||
Ok(global_account)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_initial_buy_price(
|
||||
global_account: &Arc<GlobalAccount>,
|
||||
amount_sol: u64,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let buy_amount = global_account.get_initial_buy_price(amount_sol);
|
||||
Ok(buy_amount)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn fetch_bonding_curve_account(
|
||||
rpc: &SolanaRpcClient,
|
||||
mint: &Pubkey,
|
||||
) -> Result<(Arc<crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>, Pubkey), anyhow::Error>{
|
||||
let bonding_curve_pda: Pubkey =
|
||||
get_bonding_curve_pda(mint).ok_or(anyhow!("Bonding curve not found"))?;
|
||||
|
||||
let account = rpc.get_account(&bonding_curve_pda).await?;
|
||||
if account.data.is_empty() {
|
||||
return Err(anyhow!("Bonding curve not found"));
|
||||
}
|
||||
|
||||
let bonding_curve = solana_sdk::borsh1::try_from_slice_unchecked::<crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>(&account.data[8..])
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize bonding curve account: {}", e))?;
|
||||
|
||||
Ok((Arc::new(bonding_curve), bonding_curve_pda))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn init_bonding_curve_account(
|
||||
mint: &Pubkey,
|
||||
dev_buy_token: u64,
|
||||
dev_sol_cost: u64,
|
||||
creator: Pubkey,
|
||||
) -> Result<Arc<BondingCurveAccount>, anyhow::Error> {
|
||||
let bonding_curve =
|
||||
BondingCurveAccount::from_dev_trade(mint, dev_buy_token, dev_sol_cost, creator);
|
||||
let bonding_curve = Arc::new(bonding_curve);
|
||||
Ok(bonding_curve)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_buy_amount_with_slippage(amount_sol: u64, slippage_basis_points: Option<u64>) -> u64 {
|
||||
let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE);
|
||||
amount_sol + (amount_sol * slippage / 10000)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_buy_price(amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
||||
if amount == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let n: u128 =
|
||||
(trade_info.virtual_sol_reserves as u128) * (trade_info.virtual_token_reserves as u128);
|
||||
let i: u128 = (trade_info.virtual_sol_reserves as u128) + (amount as u128);
|
||||
let r: u128 = n / i + 1;
|
||||
let s: u128 = (trade_info.virtual_token_reserves as u128) - r;
|
||||
let s_u64 = s as u64;
|
||||
|
||||
s_u64.min(trade_info.real_token_reserves)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod common;
|
||||
@@ -1 +0,0 @@
|
||||
pub mod common;
|
||||
@@ -1,14 +0,0 @@
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::types::{
|
||||
amm_info_decode, AmmInfo,
|
||||
};
|
||||
|
||||
use crate::common::SolanaRpcClient;
|
||||
|
||||
pub async fn fetch_amm_info(rpc: &SolanaRpcClient, amm: Pubkey) -> Result<AmmInfo, anyhow::Error> {
|
||||
let amm_info = rpc.get_account_data(&amm).await?;
|
||||
let amm_info =
|
||||
amm_info_decode(&amm_info).ok_or_else(|| anyhow!("Failed to decode amm info"))?;
|
||||
Ok(amm_info)
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod common;
|
||||
@@ -1 +0,0 @@
|
||||
pub mod common;
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::constants::bonk::accounts;
|
||||
use crate::instruction::utils::bonk::accounts;
|
||||
|
||||
/// Calculates the amount of tokens to receive when buying with SOL
|
||||
///
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use solana_sdk::{native_token::sol_str_to_lamports, pubkey::Pubkey};
|
||||
|
||||
use crate::{
|
||||
constants::pumpfun::global_constants::{CREATOR_FEE, FEE_BASIS_POINTS},
|
||||
instruction::utils::pumpfun::global_constants::{CREATOR_FEE, FEE_BASIS_POINTS},
|
||||
utils::calc::common::compute_fee,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::common::{
|
||||
calculate_with_slippage_buy, calculate_with_slippage_sell, ceil_div, compute_fee,
|
||||
};
|
||||
use crate::constants::pumpswap::accounts::{
|
||||
use crate::instruction::utils::pumpswap::accounts::{
|
||||
COIN_CREATOR_FEE_BASIS_POINTS, LP_FEE_BASIS_POINTS, PROTOCOL_FEE_BASIS_POINTS,
|
||||
};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::constants::raydium_amm_v4::accounts::{
|
||||
use crate::instruction::utils::raydium_amm_v4::accounts::{
|
||||
SWAP_FEE_DENOMINATOR, SWAP_FEE_NUMERATOR, TRADE_FEE_DENOMINATOR, TRADE_FEE_NUMERATOR,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::constants::raydium_cpmm::accounts::{
|
||||
use crate::instruction::utils::raydium_cpmm::accounts::{
|
||||
CREATOR_FEE_RATE, FEE_RATE_DENOMINATOR_VALUE, FUND_FEE_RATE, PROTOCOL_FEE_RATE, TRADE_FEE_RATE,
|
||||
};
|
||||
|
||||
@@ -176,7 +176,8 @@ pub fn compute_swap_amount(
|
||||
true,
|
||||
);
|
||||
|
||||
let min_amount_out = ((swap_result.output_amount as f64) * (1.0 - (slippage_basis_points as f64) / 10000.0)) as u64;
|
||||
let min_amount_out = ((swap_result.output_amount as f64)
|
||||
* (1.0 - (slippage_basis_points as f64) / 10000.0)) as u64;
|
||||
|
||||
let all_trade = swap_result.input_amount == amount_in;
|
||||
|
||||
|
||||
+14
-11
@@ -1,5 +1,5 @@
|
||||
pub mod price;
|
||||
pub mod calc;
|
||||
pub mod price;
|
||||
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use crate::trading;
|
||||
@@ -62,7 +62,7 @@ impl SolanaTrade {
|
||||
|
||||
#[inline]
|
||||
pub fn get_pumpfun_token_buy_price(&self, amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
||||
trading::pumpfun::common::get_buy_price(amount, trade_info)
|
||||
crate::instruction::utils::pumpfun::get_buy_price(amount, trade_info)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -71,7 +71,8 @@ impl SolanaTrade {
|
||||
mint: &Pubkey,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let (bonding_curve, _) =
|
||||
trading::pumpfun::common::fetch_bonding_curve_account(&self.rpc, mint).await?;
|
||||
crate::instruction::utils::pumpfun::fetch_bonding_curve_account(&self.rpc, mint)
|
||||
.await?;
|
||||
|
||||
let virtual_sol_reserves = bonding_curve.virtual_sol_reserves;
|
||||
let virtual_token_reserves = bonding_curve.virtual_token_reserves;
|
||||
@@ -85,7 +86,8 @@ impl SolanaTrade {
|
||||
mint: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let (bonding_curve, _) =
|
||||
trading::pumpfun::common::fetch_bonding_curve_account(&self.rpc, mint).await?;
|
||||
crate::instruction::utils::pumpfun::fetch_bonding_curve_account(&self.rpc, mint)
|
||||
.await?;
|
||||
|
||||
let actual_sol_reserves = bonding_curve.real_sol_reserves;
|
||||
|
||||
@@ -95,7 +97,8 @@ impl SolanaTrade {
|
||||
#[inline]
|
||||
pub async fn get_pumpfun_token_creator(&self, mint: &Pubkey) -> Result<Pubkey, anyhow::Error> {
|
||||
let (bonding_curve, _) =
|
||||
trading::pumpfun::common::fetch_bonding_curve_account(&self.rpc, mint).await?;
|
||||
crate::instruction::utils::pumpfun::fetch_bonding_curve_account(&self.rpc, mint)
|
||||
.await?;
|
||||
|
||||
let creator = bonding_curve.creator;
|
||||
|
||||
@@ -109,10 +112,10 @@ impl SolanaTrade {
|
||||
&self,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let pool = trading::pumpswap::common::fetch_pool(&self.rpc, pool_address).await?;
|
||||
let pool = crate::instruction::utils::pumpswap::fetch_pool(&self.rpc, pool_address).await?;
|
||||
|
||||
let (base_amount, quote_amount) =
|
||||
trading::pumpswap::common::get_token_balances(&pool, &self.rpc).await?;
|
||||
crate::instruction::utils::pumpswap::get_token_balances(&pool, &self.rpc).await?;
|
||||
|
||||
// Calculate price using constant product formula (x * y = k)
|
||||
// Price = quote_amount / base_amount
|
||||
@@ -130,10 +133,10 @@ impl SolanaTrade {
|
||||
&self,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let pool = trading::pumpswap::common::fetch_pool(&self.rpc, pool_address).await?;
|
||||
let pool = crate::instruction::utils::pumpswap::fetch_pool(&self.rpc, pool_address).await?;
|
||||
|
||||
let (_, quote_amount) =
|
||||
trading::pumpswap::common::get_token_balances(&pool, &self.rpc).await?;
|
||||
crate::instruction::utils::pumpswap::get_token_balances(&pool, &self.rpc).await?;
|
||||
|
||||
Ok(quote_amount)
|
||||
}
|
||||
@@ -143,10 +146,10 @@ impl SolanaTrade {
|
||||
&self,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let pool = trading::pumpswap::common::fetch_pool(&self.rpc, pool_address).await?;
|
||||
let pool = crate::instruction::utils::pumpswap::fetch_pool(&self.rpc, pool_address).await?;
|
||||
|
||||
let (base_amount, _) =
|
||||
trading::pumpswap::common::get_token_balances(&pool, &self.rpc).await?;
|
||||
crate::instruction::utils::pumpswap::get_token_balances(&pool, &self.rpc).await?;
|
||||
|
||||
Ok(base_amount)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::types::PoolState;
|
||||
|
||||
use crate::constants::{
|
||||
bonk::accounts::WSOL_TOKEN_ACCOUNT,
|
||||
decimals::{DEFAULT_TOKEN_DECIMALS, SOL_DECIMALS},
|
||||
use crate::{
|
||||
constants::decimals::{DEFAULT_TOKEN_DECIMALS, SOL_DECIMALS},
|
||||
instruction::utils::bonk::accounts::WSOL_TOKEN_ACCOUNT,
|
||||
};
|
||||
|
||||
/// Calculate the token price in WSOL based on pool state
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve;
|
||||
|
||||
use crate::constants::pumpfun::global_constants::{LAMPORTS_PER_SOL, SCALE};
|
||||
use crate::instruction::utils::pumpfun::global_constants::{LAMPORTS_PER_SOL, SCALE};
|
||||
|
||||
/// Calculate the token price in SOL based on virtual reserves
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user