use borsh::{BorshDeserialize, BorshSerialize};
use solana_sdk::pubkey::Pubkey;
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct GlobalAccount {
pub discriminator: u64,
pub initialized: bool,
pub authority: Pubkey,
pub fee_recipient: Pubkey,
pub initial_virtual_token_reserves: u64,
pub initial_virtual_sol_reserves: u64,
pub initial_real_token_reserves: u64,
pub token_total_supply: u64,
pub fee_basis_points: u64,
pub withdraw_authority: Pubkey,
pub enable_migrate: bool,
pub pool_migration_fee: u64,
pub creator_fee_basis_points: u64,
pub fee_recipients: [Pubkey; 7],
pub set_creator_authority: Pubkey,
}
impl GlobalAccount {
#[allow(clippy::too_many_arguments)]
pub fn new(
discriminator: u64,
initialized: bool,
authority: Pubkey,
fee_recipient: Pubkey,
initial_virtual_token_reserves: u64,
initial_virtual_sol_reserves: u64,
initial_real_token_reserves: u64,
token_total_supply: u64,
fee_basis_points: u64,
withdraw_authority: Pubkey,
enable_migrate: bool,
pool_migration_fee: u64,
creator_fee_basis_points: u64,
fee_recipients: [Pubkey; 7],
set_creator_authority: Pubkey,
) -> Self {
Self {
discriminator,
initialized,
authority,
fee_recipient,
initial_virtual_token_reserves,
initial_virtual_sol_reserves,
initial_real_token_reserves,
token_total_supply,
fee_basis_points,
withdraw_authority,
enable_migrate,
pool_migration_fee,
creator_fee_basis_points,
fee_recipients,
set_creator_authority,
}
}
pub fn get_initial_buy_price(&self, amount: u64) -> u64 {
if amount == 0 {
return 0;
}
let n: u128 = (self.initial_virtual_sol_reserves as u128)
* (self.initial_virtual_token_reserves as u128);
let i: u128 = (self.initial_virtual_sol_reserves as u128) + (amount as u128);
let r: u128 = n / i + 1;
let s: u128 = (self.initial_virtual_token_reserves as u128) - r;
if s < (self.initial_real_token_reserves as u128) {
s as u64
} else {
self.initial_real_token_reserves
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn get_global() -> GlobalAccount {
GlobalAccount::new(
1,
true,
Pubkey::new_unique(),
Pubkey::new_unique(),
1000,
1000,
500,
1000,
250,
Pubkey::new_unique(),
true,
100,
0,
[Pubkey::new_unique(); 7],
Pubkey::new_unique(),
)
}
fn get_large_global() -> GlobalAccount {
GlobalAccount::new(
1,
true,
Pubkey::new_unique(),
Pubkey::new_unique(),
u64::MAX,
u64::MAX,
u64::MAX / 2,
u64::MAX,
250,
Pubkey::new_unique(),
true,
u64::MAX,
u64::MAX,
[Pubkey::new_unique(); 7],
Pubkey::new_unique(),
)
}
#[test]
fn test_global_account() {
let global: GlobalAccount = get_global();
assert_eq!(global.get_initial_buy_price(0), 0);
let price: u64 = global.get_initial_buy_price(100);
assert!(price > 0);
assert!(price <= global.initial_real_token_reserves);
}
#[test]
fn test_global_account_max_reserves() {
let mut global: GlobalAccount = get_global();
global.initial_real_token_reserves = 100;
let price: u64 = global.get_initial_buy_price(1000);
assert_eq!(price, global.initial_real_token_reserves);
}
#[test]
fn test_global_account_overflow() {
let global: GlobalAccount = get_large_global();
let price: u64 = global.get_initial_buy_price(u64::MAX);
assert!(price > 0);
assert!(price <= global.initial_real_token_reserves);
let price: u64 = global.get_initial_buy_price(u64::MAX / 2);
assert!(price > 0);
assert!(price <= global.initial_real_token_reserves);
}
#[test]
fn test_global_account_overflow_edge_cases() {
let mut global: GlobalAccount = get_large_global();
global.initial_virtual_sol_reserves = u64::MAX - 1000;
global.initial_virtual_token_reserves = u64::MAX - 1000;
global.initial_real_token_reserves = u64::MAX / 4;
let price: u64 = global.get_initial_buy_price(u64::MAX - 1);
assert!(price > 0);
assert!(price <= global.initial_real_token_reserves);
let price: u64 = global.get_initial_buy_price(u64::MAX - 1000);
assert!(price > 0);
assert!(price <= global.initial_real_token_reserves);
}
}