Files
sol-trade-sdk/src/accounts/global.rs
T
2025-04-02 13:39:59 +08:00

202 lines
6.7 KiB
Rust
Executable File

//! Global account for the Pump.fun Solana Program
//!
//! This module contains the definition for the global configuration account.
//!
//! # Global Account
//!
//! The global account is used to store the global configuration for the Pump.fun program.
//!
//! # Fields
//!
//! - `discriminator`: Unique identifier for the global account
//! - `initialized`: Whether the global account has been initialized
//! - `authority`: Authority pubkey that can modify settings
//! - `fee_recipient`: Account that receives fees
//! - `initial_virtual_token_reserves`: Initial virtual token reserves for price calculations
//! - `initial_virtual_sol_reserves`: Initial virtual SOL reserves for price calculations
//! - `initial_real_token_reserves`: Initial actual token reserves available for trading
//! - `token_total_supply`: Total supply of tokens
//! - `fee_basis_points`: Fee in basis points (1/100th of a percent)
//!
//! # Methods
//!
//! - `new`: Creates a new global account instance
//! - `get_initial_buy_price`: Calculates the initial amount of tokens received for a given SOL amount
use solana_sdk::pubkey::Pubkey;
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Serialize, Deserialize};
/// Represents the global configuration account for token pricing and fees
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalAccount {
/// Unique identifier for the global account
pub discriminator: u64,
/// Whether the global account has been initialized
pub initialized: bool,
/// Authority that can modify global settings
pub authority: Pubkey,
/// Account that receives fees
pub fee_recipient: Pubkey,
/// Initial virtual token reserves for price calculations
pub initial_virtual_token_reserves: u64,
/// Initial virtual SOL reserves for price calculations
pub initial_virtual_sol_reserves: u64,
/// Initial actual token reserves available for trading
pub initial_real_token_reserves: u64,
/// Total supply of tokens
pub token_total_supply: u64,
/// Fee in basis points (1/100th of a percent)
pub fee_basis_points: u64,
}
impl GlobalAccount {
/// Creates a new global account instance
///
/// # Arguments
/// * `discriminator` - Unique identifier for the account
/// * `initialized` - Whether the account is initialized
/// * `authority` - Authority pubkey that can modify settings
/// * `fee_recipient` - Account that receives fees
/// * `initial_virtual_token_reserves` - Initial virtual token reserves
/// * `initial_virtual_sol_reserves` - Initial virtual SOL reserves
/// * `initial_real_token_reserves` - Initial actual token reserves
/// * `token_total_supply` - Total supply of tokens
/// * `fee_basis_points` - Fee in basis points
#[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,
) -> 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,
}
}
/// Calculates the initial amount of tokens received for a given SOL amount
///
/// # Arguments
/// * `amount` - Amount of SOL to spend
///
/// # Returns
/// Amount of tokens that would be received
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,
)
}
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,
)
}
#[test]
fn test_global_account() {
let global: GlobalAccount = get_global();
// Test initial buy price calculation
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;
// Test that returned amount is capped by real_token_reserves
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();
// Test with maximum possible SOL amount
let price: u64 = global.get_initial_buy_price(u64::MAX);
assert!(price > 0);
assert!(price <= global.initial_real_token_reserves);
// Test with large but not maximum SOL amount
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;
// Test with amounts near u64::MAX
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);
}
}