From 3a60969b2278d6bd3583f38c0c912a8dadfe8bf2 Mon Sep 17 00:00:00 2001 From: William Date: Tue, 13 May 2025 06:34:57 +0800 Subject: [PATCH] fix pump upgrade --- src/accounts/bonding_curve.rs | 166 ++++--------------------------- src/constants/mod.rs | 82 +++++++++++++-- src/instruction/mod.rs | 182 ++-------------------------------- src/pumpfun/buy.rs | 23 ++--- src/pumpfun/common.rs | 30 +++++- src/pumpfun/sell.rs | 6 +- 6 files changed, 143 insertions(+), 346 deletions(-) diff --git a/src/accounts/bonding_curve.rs b/src/accounts/bonding_curve.rs index 6747f3b..1b4ae53 100755 --- a/src/accounts/bonding_curve.rs +++ b/src/accounts/bonding_curve.rs @@ -25,10 +25,11 @@ //! - `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 borsh::{BorshDeserialize, BorshSerialize}; +use serde::{Serialize, Deserialize}; +use solana_sdk::pubkey::Pubkey; -/// Represents a bonding curve for token pricing and liquidity management -#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +/// Represents the global configuration account for token pricing and fees +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct BondingCurveAccount { /// Unique identifier for the bonding curve pub discriminator: u64, @@ -44,6 +45,8 @@ pub struct BondingCurveAccount { pub token_total_supply: u64, /// Whether the bonding curve is complete/finalized pub complete: bool, + /// Creator of the bonding curve + pub creator: Pubkey, } impl BondingCurveAccount { @@ -57,25 +60,17 @@ impl BondingCurveAccount { /// * `real_sol_reserves` - Actual SOL reserves available /// * `token_total_supply` - Total supply of tokens /// * `complete` - Whether the curve is complete - pub fn new( - discriminator: u64, - virtual_token_reserves: u64, - virtual_sol_reserves: u64, - real_token_reserves: u64, - real_sol_reserves: u64, - token_total_supply: u64, - complete: bool, - ) -> Self { - Self { - discriminator, - virtual_token_reserves, - virtual_sol_reserves, - real_token_reserves, - real_sol_reserves, - token_total_supply, - complete, - } - } + // pub fn new(mint: &Pubkey, dev_buy_token_amount: u64, dev_buy_sol_amount: u64) -> Self { + // Self { + // // account: get_bonding_curve_pda(mint).unwrap(), + // virtual_token_reserves: INITIAL_VIRTUAL_TOKEN_RESERVES - dev_buy_token_amount, + // virtual_sol_reserves: INITIAL_VIRTUAL_SOL_RESERVES + dev_buy_sol_amount, + // real_token_reserves: INITIAL_REAL_TOKEN_RESERVES - dev_buy_token_amount, + // real_sol_reserves: dev_buy_sol_amount, + // token_total_supply: TOKEN_TOTAL_SUPPLY, + // complete: false, + // } + // } /// Calculates the amount of tokens received for a given SOL amount /// @@ -204,130 +199,3 @@ impl BondingCurveAccount { token_price } } - -#[cfg(test)] -mod tests { - use super::*; - - fn get_bonding_curve() -> BondingCurveAccount { - BondingCurveAccount::new( - 1, // discriminator - 1000, // virtual_token_reserves - 1000, // virtual_sol_reserves - 500, // real_token_reserves - 500, // real_sol_reserves - 1000, // token_total_supply - false, // complete - ) - } - - fn get_large_bonding_curve() -> BondingCurveAccount { - BondingCurveAccount::new( - 1, // discriminator - u64::MAX / 2, // virtual_token_reserves - u64::MAX / 2, // virtual_sol_reserves - u64::MAX / 4, // real_token_reserves - u64::MAX / 4, // real_sol_reserves - u64::MAX / 2, // token_total_supply - false, // complete - ) - } - - #[test] - fn test_bonding_curve_account() { - let bonding_curve: BondingCurveAccount = get_bonding_curve(); - - // Test buy price calculation - assert_eq!(bonding_curve.get_buy_price(0).unwrap(), 0); - - let buy_price = bonding_curve.get_buy_price(100).unwrap(); - assert!(buy_price > 0); - assert!(buy_price <= bonding_curve.real_token_reserves); - - // Test sell price calculation - assert_eq!(bonding_curve.get_sell_price(0, 250).unwrap(), 0); - - let sell_price = bonding_curve.get_sell_price(100, 250).unwrap(); - assert!(sell_price > 0); - } - - #[test] - fn test_bonding_curve_complete() { - let mut bonding_curve: BondingCurveAccount = get_bonding_curve(); - - // Test operations work when not complete - assert!(bonding_curve.get_buy_price(100).is_ok()); - assert!(bonding_curve.get_sell_price(100, 250).is_ok()); - - // Set curve to complete - bonding_curve.complete = true; - - // Test operations fail when complete - assert!(bonding_curve.get_buy_price(100).is_err()); - assert!(bonding_curve.get_sell_price(100, 250).is_err()); - } - - #[test] - fn test_market_cap_calculations() { - let bonding_curve: BondingCurveAccount = get_bonding_curve(); - - // Test market cap calculations - let market_cap = bonding_curve.get_market_cap_sol(); - assert!(market_cap > 0); - - let final_market_cap = bonding_curve.get_final_market_cap_sol(250); - assert!(final_market_cap > 0); - } - - #[test] - fn test_buy_out_price() { - let bonding_curve: BondingCurveAccount = get_bonding_curve(); - - let buy_out_price = bonding_curve.get_buy_out_price(100, 250); - assert!(buy_out_price > 0); - - // Test with amount less than real_sol_reserves - let small_buy_out = bonding_curve.get_buy_out_price(400, 250); - assert!(small_buy_out > 0); - } - - #[test] - fn test_overflow_buy_price() { - let bonding_curve = get_large_bonding_curve(); - - // Test buying with large SOL amount - let buy_price = bonding_curve.get_buy_price(u64::MAX).unwrap(); - assert!(buy_price > 0); - assert!(buy_price <= bonding_curve.real_token_reserves); - } - - #[test] - fn test_overflow_sell_price() { - let bonding_curve = get_large_bonding_curve(); - - // Test selling large token amount - let sell_price = bonding_curve.get_sell_price(u64::MAX / 4, 250).unwrap(); - assert!(sell_price > 0); - } - - #[test] - fn test_overflow_market_cap() { - let bonding_curve = get_large_bonding_curve(); - - // Test market cap with large values - let market_cap = bonding_curve.get_market_cap_sol(); - assert!(market_cap > 0); - - let final_market_cap = bonding_curve.get_final_market_cap_sol(250); - assert!(final_market_cap > 0); - } - - #[test] - fn test_overflow_buy_out_price() { - let bonding_curve = get_large_bonding_curve(); - - // Test buy out with large token amount - let buy_out_price = bonding_curve.get_buy_out_price(u64::MAX / 4, 250); - assert!(buy_out_price > 0); - } -} diff --git a/src/constants/mod.rs b/src/constants/mod.rs index 645b3f7..762b3be 100755 --- a/src/constants/mod.rs +++ b/src/constants/mod.rs @@ -21,14 +21,66 @@ pub mod seeds { /// 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"; } +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 TOTAL_SUPPLY: u64 = 1_000_000_000 * SCALE; // 1 billion tokens + + pub const BONDING_CURVE_SUPPLY: u64 = 793_100_000 * SCALE; // total supply of bonding curve tokens + + 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 std::collections::{HashMap, HashSet}; - use solana_sdk::{pubkey, pubkey::Pubkey}; /// Public key for the Pump.fun program @@ -47,8 +99,7 @@ pub mod accounts { pub const TOKEN_PROGRAM: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); /// Associated Token Program ID - pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey = - pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); + pub const ASSOCIATED_TOKEN_PROGRAM: Pubkey = pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); /// Rent Sysvar ID pub const RENT: Pubkey = pubkey!("SysvarRent111111111111111111111111111111111"); @@ -64,7 +115,6 @@ pub mod accounts { "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT", ]; - /// Tip accounts pub const NEXTBLOCK_TIP_ACCOUNTS: &[&str] = &[ "NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE", @@ -85,12 +135,32 @@ pub mod accounts { "Cix2bHfqPcKcM233mzxbLk14kSggUUiz2A87fJtGivXr", ]; + pub const NOZOMI_TIP_ACCOUNTS: &[&str] = &[ + "TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq", + "noz3jAjPiHuBPqiSPkkugaJDkJscPuRhYnSpbi8UvC4", + "noz3str9KXfpKknefHji8L1mPgimezaiUyCHYMDv1GE", + "noz6uoYCDijhu1V7cutCpwxNiSovEwLdRHPwmgCGDNo", + "noz9EPNcT7WH6Sou3sr3GGjHQYVkN3DNirpbvDkv9YJ", + "nozc5yT15LazbLTFVZzoNZCwjh3yUtW86LoUyqsBu4L", + "nozFrhfnNGoyqwVuwPAW4aaGqempx4PU6g6D9CJMv7Z", + "nozievPk7HyK1Rqy1MPJwVQ7qQg2QoJGyP71oeDwbsu", + "noznbgwYnBLDHu8wcQVCEw6kDrXkPdKkydGJGNXGvL7", + "nozNVWs5N8mgzuD3qigrCG2UoKxZttxzZ85pvAQVrbP", + "nozpEGbwx4BcGp6pvEdAh1JoC2CQGZdU6HbNP1v2p6P", + "nozrhjhkCr3zXT3BiT4WCodYCUFeQvcdUkM7MqhKqge", + "nozrwQtWhEdrA6W8dkbt9gnUaMs52PdAv5byipnadq3", + "nozUacTVWub3cL4mJmGCYjKZTnE9RbdY5AP46iQgbPJ", + "nozWCyTPppJjRuw2fpzDhhWbW355fzosWSzrrMYB1Qk", + "nozWNju6dY353eMkMqURqwQEoM3SFgEKC6psLCSfUne", + "nozxNBgWohjR75vdspfxR5H9ceC7XXH99xpxhVGt3Bb" + ]; + pub const AMM_PROGRAM: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"); } pub mod trade { pub const TRADER_TIP_AMOUNT: f64 = 0.0001; - pub const DEFAULT_SLIPPAGE: u64 = 3000; // 30% + pub const DEFAULT_SLIPPAGE: u64 = 1000; // 10% pub const DEFAULT_COMPUTE_UNIT_LIMIT: u32 = 78000; pub const DEFAULT_COMPUTE_UNIT_PRICE: u64 = 500000; pub const DEFAULT_BUY_TIP_FEE: f64 = 0.0006; diff --git a/src/instruction/mod.rs b/src/instruction/mod.rs index bde237f..bc076d0 100755 --- a/src/instruction/mod.rs +++ b/src/instruction/mod.rs @@ -9,15 +9,6 @@ //! - `create`: Instruction to create a new token with an associated bonding curve. //! - `buy`: Instruction to buy tokens from a bonding curve by providing SOL. //! - `sell`: Instruction to sell tokens back to the bonding curve in exchange for SOL. - -use std::sync::Arc; - -use spl_associated_token_account::instruction::create_associated_token_account; -use spl_token::instruction::close_account; -use crate::common::SolanaRpcClient; -use crate::constants::trade::DEFAULT_SLIPPAGE; -use crate::ipfs::TokenMetadataIPFS; -use crate::pumpfun::common::{calculate_with_slippage_buy, calculate_with_slippage_sell, get_bonding_curve_account, get_buy_amount_with_slippage, get_global_account, get_initial_buy_price, get_token_balance, get_token_balance_and_ata}; use crate::{ constants, pumpfun::common::{ @@ -33,7 +24,6 @@ use solana_sdk::{ signer::Signer, }; -use anyhow::{anyhow, Result}; pub struct Create { pub _name: String, pub _symbol: String, @@ -156,24 +146,25 @@ pub fn create(payer: &Keypair, mint: &Keypair, args: Create) -> Instruction { pub fn buy( payer: &Keypair, mint: &Pubkey, + bonding_curve: &Pubkey, + creator_vault: &Pubkey, fee_recipient: &Pubkey, args: Buy, ) -> Instruction { - let bonding_curve: Pubkey = get_bonding_curve_pda(mint).unwrap(); Instruction::new_with_bytes( constants::accounts::PUMPFUN, &args.data(), vec![ - AccountMeta::new_readonly(get_global_pda(), false), + AccountMeta::new_readonly(constants::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(*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::accounts::SYSTEM_PROGRAM, false), AccountMeta::new_readonly(constants::accounts::TOKEN_PROGRAM, false), - AccountMeta::new_readonly(constants::accounts::RENT, false), + AccountMeta::new(*creator_vault, false), AccountMeta::new_readonly(constants::accounts::EVENT_AUTHORITY, false), AccountMeta::new_readonly(constants::accounts::PUMPFUN, false), ], @@ -199,15 +190,16 @@ pub fn buy( pub fn sell( payer: &Keypair, mint: &Pubkey, + bonding_curve: &Pubkey, + creator_vault: &Pubkey, fee_recipient: &Pubkey, args: Sell, ) -> Instruction { - let bonding_curve: Pubkey = get_bonding_curve_pda(mint).unwrap(); Instruction::new_with_bytes( constants::accounts::PUMPFUN, &args.data(), vec![ - AccountMeta::new_readonly(get_global_pda(), false), + AccountMeta::new_readonly(constants::global_constants::GLOBAL_ACCOUNT, false), AccountMeta::new(*fee_recipient, false), AccountMeta::new_readonly(*mint, false), AccountMeta::new(bonding_curve, false), @@ -215,7 +207,7 @@ pub fn sell( AccountMeta::new(get_associated_token_address(&payer.pubkey(), mint), false), AccountMeta::new(payer.pubkey(), true), AccountMeta::new_readonly(constants::accounts::SYSTEM_PROGRAM, false), - AccountMeta::new_readonly(constants::accounts::ASSOCIATED_TOKEN_PROGRAM, false), + AccountMeta::new(*creator_vault, false), AccountMeta::new_readonly(constants::accounts::TOKEN_PROGRAM, false), AccountMeta::new_readonly(constants::accounts::EVENT_AUTHORITY, false), AccountMeta::new_readonly(constants::accounts::PUMPFUN, false), @@ -223,157 +215,3 @@ pub fn sell( ) } -pub async fn build_create_and_buy_instructions( - rpc: Arc, - payer: Arc, - mint: Arc, - ipfs: TokenMetadataIPFS, - amount_sol: u64, - slippage_basis_points: Option, -) -> Result, anyhow::Error> { - if amount_sol == 0 { - return Err(anyhow!("build_create_and_buy_instructions: Amount cannot be zero")); - } - - let rpc = rpc.as_ref(); - let global_account = get_global_account(&rpc).await?; - let buy_amount = global_account.get_initial_buy_price(amount_sol); - let buy_amount_with_slippage = - get_buy_amount_with_slippage(amount_sol, slippage_basis_points); - - let mut instructions = vec![]; - - instructions.push(create( - payer.as_ref(), - mint.as_ref(), - Create { - _name: ipfs.metadata.name.clone(), - _symbol: ipfs.metadata.symbol.clone(), - _uri: ipfs.metadata_uri.clone(), - _creator: payer.pubkey(), - }, - )); - - let ata = get_associated_token_address(&payer.pubkey(), &mint.pubkey()); - instructions.push(create_associated_token_account( - &payer.pubkey(), - &payer.pubkey(), - &mint.pubkey(), - &constants::accounts::TOKEN_PROGRAM, - )); - - instructions.push(buy( - payer.as_ref(), - &mint.pubkey(), - &global_account.fee_recipient, - Buy { - _amount: buy_amount, - _max_sol_cost: buy_amount_with_slippage, - }, - )); - - Ok(instructions) -} - -pub async fn build_buy_instructions( - rpc: Arc, - payer: Arc, - mint: Arc, - amount_sol: u64, - slippage_basis_points: Option, -) -> Result, anyhow::Error> { - if amount_sol == 0 { - return Err(anyhow!("build_buy_instructions:Amount cannot be zero")); - } - - let global_account = get_global_account(&rpc).await?; - let buy_amount = match get_bonding_curve_account(&rpc, mint.as_ref()).await { - Ok(account) => { - account.get_buy_price(amount_sol).map_err(|e| anyhow!(e))? - }, - Err(_e) => { - let initial_buy_amount = get_initial_buy_price(&global_account, amount_sol).await?; - initial_buy_amount * 80 / 100 - } - }; - - let buy_amount_with_slippage = calculate_with_slippage_buy(amount_sol, slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE)); - let mut instructions = vec![]; - // let ata = get_associated_token_address(&payer.pubkey(), &mint); - // match rpc.get_account(&ata).await { - // Ok(_) => {}, - // Err(_) => { - // instructions.push(create_associated_token_account( - // &payer.pubkey(), - // &payer.pubkey(), - // &mint, - // &constants::accounts::TOKEN_PROGRAM, - // )); - // } - // } - - instructions.push(create_associated_token_account( - &payer.pubkey(), - &payer.pubkey(), - &mint, - &constants::accounts::TOKEN_PROGRAM, - )); - - instructions.push(buy( - payer.as_ref(), - &mint, - &global_account.fee_recipient, - Buy { - _amount: buy_amount, - _max_sol_cost: buy_amount_with_slippage, - }, - )); - - Ok(instructions) -} - -pub async fn build_sell_instructions( - rpc: Arc, - payer: Arc, - mint: Arc, - amount_token: u64, - slippage_basis_points: Option, -) -> Result, anyhow::Error> { - if amount_token == 0 { - return Err(anyhow!("build_sell_instructions: Amount cannot be zero")); - } - - let ata = get_associated_token_address(&payer.pubkey(), mint.as_ref()); - let global_account = get_global_account(&rpc).await?; - let bonding_curve_account = get_bonding_curve_account(&rpc, mint.as_ref()).await?; - let min_sol_output = bonding_curve_account - .get_sell_price(amount_token, global_account.fee_basis_points) - .map_err(|e| anyhow!(e))?; - let min_sol_output_with_slippage = calculate_with_slippage_sell( - min_sol_output, - slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), - ); - - let mut instructions = vec![]; - - instructions.push(sell( - payer.as_ref(), - &mint, - &global_account.fee_recipient, - Sell { - _amount: amount_token, - _min_sol_output: min_sol_output_with_slippage, - }, - )); - - instructions.push(close_account( - &spl_token::ID, - &ata, - &payer.pubkey(), - &payer.pubkey(), - &[&payer.pubkey()], - )?); - - Ok(instructions) -} - diff --git a/src/pumpfun/buy.rs b/src/pumpfun/buy.rs index 6e076a5..16a93cc 100755 --- a/src/pumpfun/buy.rs +++ b/src/pumpfun/buy.rs @@ -146,17 +146,12 @@ pub async fn build_buy_instructions( return Err(anyhow!("Amount cannot be zero")); } - let rpc = rpc.as_ref(); - let global_account = get_global_account(rpc).await?; - let buy_amount = match get_bonding_curve_account(rpc, mint.as_ref()).await { - Ok(account) => account.get_buy_price(amount_sol).map_err(|e| anyhow!(e))?, - Err(_e) => { - println!("Bonding curve account not found, using initial buy price: {}", _e); - let initial_buy_amount = get_initial_buy_price(&global_account, amount_sol).await?; - initial_buy_amount * 80 / 100 - } - }; - let buy_amount_with_slippage = calculate_with_slippage_buy(amount_sol, slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE)); + let (bonding_curve_account, bonding_curve_pda) = get_bonding_curve_account(&rpc, &mint).await?; + + let creator_vault_pda = get_creator_vault_pda(&bonding_curve_account.creator).unwrap(); + + let (buy_token_amount, max_sol_cost) = get_buy_token_amount(&bonding_curve_account, buy_sol_cost, slippage_basis_points)?; + let mut instructions = vec![]; instructions.push(create_associated_token_account( &payer.pubkey(), @@ -168,10 +163,12 @@ pub async fn build_buy_instructions( instructions.push(instruction::buy( payer.as_ref(), &mint, + &bonding_curve_pda, + &creator_vault_pda, &global_account.fee_recipient, instruction::Buy { - _amount: buy_amount, - _max_sol_cost: buy_amount_with_slippage, + _amount: buy_token_amount, + _max_sol_cost: max_sol_cost, }, )); diff --git a/src/pumpfun/common.rs b/src/pumpfun/common.rs index 523a050..981252b 100755 --- a/src/pumpfun/common.rs +++ b/src/pumpfun/common.rs @@ -88,9 +88,7 @@ pub async fn get_token_balance_and_ata(rpc: &SolanaRpcClient, payer: &Keypair, m #[inline] pub async fn get_sol_balance(rpc: &SolanaRpcClient, account: &Pubkey) -> Result { - println!("get_sol_balance account: {}", account); let balance = rpc.get_balance(account).await?; - println!("get_sol_balance balance: {}", balance); Ok(balance) } @@ -118,6 +116,14 @@ pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option { pda.map(|pubkey| pubkey.0) } +#[inline] +pub fn get_creator_vault_pda(creator: &Pubkey) -> Option { + let seeds: &[&[u8]; 2] = &[constants::seeds::CREATOR_VAULT_SEED, creator.as_ref()]; + let program_id: &Pubkey = &constants::accounts::PUMPFUN; + 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( @@ -155,7 +161,7 @@ pub async fn get_initial_buy_price(global_account: &Arc pub async fn get_bonding_curve_account( rpc: &SolanaRpcClient, mint: &Pubkey, -) -> Result, anyhow::Error> { +) -> Result<(Arc, Pubkey), anyhow::Error> { let bonding_curve_pda = get_bonding_curve_pda(mint) .ok_or(anyhow!("Bonding curve not found"))?; @@ -164,8 +170,22 @@ pub async fn get_bonding_curve_account( return Err(anyhow!("Bonding curve not found")); } - let bonding_curve = Arc::new(accounts::BondingCurveAccount::try_from_slice(&account.data)?); - Ok(bonding_curve) + let bonding_curve = Arc::new(bincode::deserialize::(&account.data)?); + + Ok((bonding_curve, bonding_curve_pda)) +} + +#[inline] +pub fn get_buy_token_amount( + bonding_curve_account: &BondingCurveAccount, + buy_sol_cost: u64, + slippage_basis_points: Option, +) -> anyhow::Result<(u64, u64)> { + let buy_token = bonding_curve_account.get_buy_price(buy_sol_cost).map_err(|e| anyhow!(e))?; + + let max_sol_cost = calculate_with_slippage_buy(buy_sol_cost, slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE)); + + Ok((buy_token, max_sol_cost)) } #[inline] diff --git a/src/pumpfun/sell.rs b/src/pumpfun/sell.rs index 799af37..88a966c 100755 --- a/src/pumpfun/sell.rs +++ b/src/pumpfun/sell.rs @@ -196,7 +196,7 @@ pub async fn build_sell_instructions( } let global_account = get_global_account(rpc.as_ref()).await?; - let bonding_curve_account = get_bonding_curve_account(rpc.as_ref(), &mint).await?; + let (bonding_curve_account, bonding_curve_pda) = get_bonding_curve_account(&rpc, &mint).await?; let min_sol_output = bonding_curve_account .get_sell_price(amount, global_account.fee_basis_points) .map_err(|e| anyhow!(e))?; @@ -205,10 +205,14 @@ pub async fn build_sell_instructions( slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE), ); + let creator_vault_pda = get_creator_vault_pda(&bonding_curve_account.creator).unwrap(); + let instructions = vec![ instruction::sell( payer.as_ref(), &mint, + &bonding_curve_pda, + &creator_vault_pda, &global_account.fee_recipient, instruction::Sell { _amount: amount,