use borsh::BorshDeserialize;
use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;
use crate::instruction::utils::pumpfun::global_constants::{
INITIAL_REAL_TOKEN_RESERVES, INITIAL_VIRTUAL_SOL_RESERVES, INITIAL_VIRTUAL_TOKEN_RESERVES,
INITIAL_VIRTUAL_USDC_RESERVES, TOKEN_TOTAL_SUPPLY,
};
use crate::instruction::utils::pumpfun::{get_bonding_curve_pda, get_creator_vault_pda};
#[derive(Debug, Clone, Serialize, Deserialize, Default, BorshDeserialize)]
pub struct BondingCurveAccount {
#[borsh(skip)]
pub discriminator: u64,
#[borsh(skip)]
pub account: Pubkey,
pub virtual_token_reserves: u64,
pub virtual_sol_reserves: u64,
pub real_token_reserves: u64,
pub real_sol_reserves: u64,
pub token_total_supply: u64,
pub complete: bool,
pub creator: Pubkey,
pub is_mayhem_mode: bool,
pub is_cashback_coin: bool,
pub quote_mint: Pubkey,
}
impl BondingCurveAccount {
#[inline]
pub fn normalize_quote_mint(quote_mint: Pubkey) -> Pubkey {
if quote_mint == Pubkey::default() || quote_mint == crate::constants::SOL_TOKEN_ACCOUNT {
crate::constants::WSOL_TOKEN_ACCOUNT
} else {
quote_mint
}
}
#[inline]
pub fn effective_quote_mint(&self) -> Pubkey {
Self::normalize_quote_mint(self.quote_mint)
}
#[inline]
pub fn initial_virtual_quote_reserves_for_quote_mint(quote_mint: &Pubkey) -> u64 {
if *quote_mint == crate::constants::USDC_TOKEN_ACCOUNT {
INITIAL_VIRTUAL_USDC_RESERVES
} else {
INITIAL_VIRTUAL_SOL_RESERVES
}
}
#[inline]
pub fn virtual_quote_reserves(&self) -> u64 {
self.virtual_sol_reserves
}
#[inline]
pub fn real_quote_reserves(&self) -> u64 {
self.real_sol_reserves
}
#[inline]
pub fn with_quote_mint(mut self, quote_mint: Pubkey) -> Self {
let quote_mint = Self::normalize_quote_mint(quote_mint);
let old_initial =
Self::initial_virtual_quote_reserves_for_quote_mint(&self.effective_quote_mint());
let new_initial = Self::initial_virtual_quote_reserves_for_quote_mint("e_mint);
if self.virtual_sol_reserves == old_initial.saturating_add(self.real_sol_reserves) {
self.virtual_sol_reserves = new_initial.saturating_add(self.real_sol_reserves);
}
self.quote_mint = quote_mint;
self
}
pub fn from_dev_trade(
bonding_curve: Pubkey,
mint: &Pubkey,
dev_token_amount: u64,
dev_sol_amount: u64,
creator: Pubkey,
is_mayhem_mode: bool,
is_cashback_coin: bool,
) -> Self {
Self::from_dev_trade_with_quote_mint(
bonding_curve,
mint,
dev_token_amount,
dev_sol_amount,
creator,
is_mayhem_mode,
is_cashback_coin,
crate::constants::WSOL_TOKEN_ACCOUNT,
)
}
pub fn from_dev_trade_with_quote_mint(
bonding_curve: Pubkey,
mint: &Pubkey,
dev_token_amount: u64,
dev_quote_amount: u64,
creator: Pubkey,
is_mayhem_mode: bool,
is_cashback_coin: bool,
quote_mint: Pubkey,
) -> Self {
let account = if bonding_curve != Pubkey::default() {
bonding_curve
} else {
get_bonding_curve_pda(&mint).unwrap()
};
let quote_mint = Self::normalize_quote_mint(quote_mint);
Self {
discriminator: 0,
account: account,
virtual_token_reserves: INITIAL_VIRTUAL_TOKEN_RESERVES - dev_token_amount,
virtual_sol_reserves: Self::initial_virtual_quote_reserves_for_quote_mint("e_mint)
+ dev_quote_amount,
real_token_reserves: INITIAL_REAL_TOKEN_RESERVES - dev_token_amount,
real_sol_reserves: dev_quote_amount,
token_total_supply: TOKEN_TOTAL_SUPPLY,
complete: false,
creator: creator,
is_mayhem_mode: is_mayhem_mode,
is_cashback_coin,
quote_mint,
}
}
pub fn from_trade(
bonding_curve: Pubkey,
mint: Pubkey,
creator: Pubkey,
virtual_token_reserves: u64,
virtual_sol_reserves: u64,
real_token_reserves: u64,
real_sol_reserves: u64,
is_mayhem_mode: bool,
is_cashback_coin: bool,
) -> Self {
Self::from_trade_with_quote_mint(
bonding_curve,
mint,
creator,
virtual_token_reserves,
virtual_sol_reserves,
real_token_reserves,
real_sol_reserves,
is_mayhem_mode,
is_cashback_coin,
crate::constants::WSOL_TOKEN_ACCOUNT,
)
}
pub fn from_trade_with_quote_mint(
bonding_curve: Pubkey,
mint: Pubkey,
creator: Pubkey,
virtual_token_reserves: u64,
virtual_quote_reserves: u64,
real_token_reserves: u64,
real_quote_reserves: u64,
is_mayhem_mode: bool,
is_cashback_coin: bool,
quote_mint: Pubkey,
) -> Self {
let account = if bonding_curve != Pubkey::default() {
bonding_curve
} else {
get_bonding_curve_pda(&mint).unwrap()
};
let quote_mint = Self::normalize_quote_mint(quote_mint);
Self {
discriminator: 0,
account: account,
virtual_token_reserves: virtual_token_reserves,
virtual_sol_reserves: virtual_quote_reserves,
real_token_reserves: real_token_reserves,
real_sol_reserves: real_quote_reserves,
token_total_supply: TOKEN_TOTAL_SUPPLY,
complete: false,
creator: creator,
is_mayhem_mode: is_mayhem_mode,
is_cashback_coin,
quote_mint,
}
}
pub fn get_creator_vault_pda(&self) -> Pubkey {
get_creator_vault_pda(&self.creator).unwrap()
}
pub fn get_buy_price(&self, amount: u64) -> Result<u64, &'static str> {
if self.complete {
return Err("Curve is complete");
}
if amount == 0 {
return Ok(0);
}
let n: u128 = (self.virtual_sol_reserves as u128) * (self.virtual_token_reserves as u128);
let i: u128 = (self.virtual_sol_reserves as u128) + (amount as u128);
let r: u128 = n / i + 1;
let s: u128 = (self.virtual_token_reserves as u128) - r;
let s_u64 = s as u64;
Ok(if s_u64 < self.real_token_reserves { s_u64 } else { self.real_token_reserves })
}
pub fn get_sell_price(&self, amount: u64, fee_basis_points: u64) -> Result<u64, &'static str> {
if self.complete {
return Err("Curve is complete");
}
if amount == 0 {
return Ok(0);
}
let n: u128 = ((amount as u128) * (self.virtual_sol_reserves as u128))
/ ((self.virtual_token_reserves as u128) + (amount as u128));
let a: u128 = (n * (fee_basis_points as u128)) / 10000;
Ok((n - a) as u64)
}
pub fn get_market_cap_sol(&self) -> u64 {
if self.virtual_token_reserves == 0 {
return 0;
}
((self.token_total_supply as u128) * (self.virtual_sol_reserves as u128)
/ (self.virtual_token_reserves as u128)) as u64
}
pub fn get_final_market_cap_sol(&self, fee_basis_points: u64) -> u64 {
let total_sell_value: u128 =
self.get_buy_out_price(self.real_token_reserves, fee_basis_points) as u128;
let total_virtual_value: u128 = (self.virtual_sol_reserves as u128) + total_sell_value;
let total_virtual_tokens: u128 =
(self.virtual_token_reserves as u128) - (self.real_token_reserves as u128);
if total_virtual_tokens == 0 {
return 0;
}
((self.token_total_supply as u128) * total_virtual_value / total_virtual_tokens) as u64
}
pub fn get_buy_out_price(&self, amount: u64, fee_basis_points: u64) -> u64 {
let sol_tokens: u128 = if amount < self.real_sol_reserves {
self.real_sol_reserves as u128
} else {
amount as u128
};
let total_sell_value: u128 = (sol_tokens * (self.virtual_sol_reserves as u128))
/ ((self.virtual_token_reserves as u128) - sol_tokens)
+ 1;
let fee: u128 = (total_sell_value * (fee_basis_points as u128)) / 10000;
(total_sell_value + fee) as u64
}
pub fn get_token_price(&self) -> f64 {
let v_sol = self.virtual_sol_reserves as f64 / 100_000_000.0;
let v_tokens = self.virtual_token_reserves as f64 / 100_000.0;
let token_price = v_sol / v_tokens;
token_price
}
}