fix pump upgrade
This commit is contained in:
+17
-149
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+76
-6
@@ -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;
|
||||
|
||||
+10
-172
@@ -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<SolanaRpcClient>,
|
||||
payer: Arc<Keypair>,
|
||||
mint: Arc<Keypair>,
|
||||
ipfs: TokenMetadataIPFS,
|
||||
amount_sol: u64,
|
||||
slippage_basis_points: Option<u64>,
|
||||
) -> Result<Vec<Instruction>, 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<SolanaRpcClient>,
|
||||
payer: Arc<Keypair>,
|
||||
mint: Arc<Pubkey>,
|
||||
amount_sol: u64,
|
||||
slippage_basis_points: Option<u64>,
|
||||
) -> Result<Vec<Instruction>, 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<SolanaRpcClient>,
|
||||
payer: Arc<Keypair>,
|
||||
mint: Arc<Pubkey>,
|
||||
amount_token: u64,
|
||||
slippage_basis_points: Option<u64>,
|
||||
) -> Result<Vec<Instruction>, 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)
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -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,
|
||||
},
|
||||
));
|
||||
|
||||
|
||||
+25
-5
@@ -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<u64, anyhow::Error> {
|
||||
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<Pubkey> {
|
||||
pda.map(|pubkey| pubkey.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_creator_vault_pda(creator: &Pubkey) -> Option<Pubkey> {
|
||||
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<accounts::GlobalAccount>
|
||||
pub async fn get_bonding_curve_account(
|
||||
rpc: &SolanaRpcClient,
|
||||
mint: &Pubkey,
|
||||
) -> Result<Arc<accounts::BondingCurveAccount>, anyhow::Error> {
|
||||
) -> Result<(Arc<accounts::BondingCurveAccount>, 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::<accounts::BondingCurveAccount>(&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<u64>,
|
||||
) -> 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]
|
||||
|
||||
+5
-1
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user