raydium-launchlab-sdk 0.1.1

Rust SDK for Raydium LaunchLab program
Documentation
//! Account structures for Raydium LaunchLab

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::pubkey::Pubkey;

use super::types::{AmmCreatorFeeOn, VestingSchedule};

// =============================================================================
// Account Structures
// =============================================================================

/// Global configuration account containing protocol-wide settings
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug)]
pub struct GlobalConfig {
    /// Account update epoch
    pub epoch: u64,
    /// Quote token mint address
    pub quote_mint: Pubkey,
    /// Account that receives protocol fees
    pub protocol_fee_owner: Pubkey,
    /// Account that receives migration fees
    pub migrate_fee_owner: Pubkey,
    /// Wallet for AMM migration control
    pub migrate_to_amm_wallet: Pubkey,
    /// Wallet for CPSwap migration control
    pub migrate_to_cpswap_wallet: Pubkey,
    /// Trading fee rate (denominator is RATE_DENOMINATOR)
    pub trade_fee_rate: u64,
    /// Migration fee amount
    pub migrate_fee: u64,
    /// Curve type for this config
    pub curve_type: u8,
    /// Config index
    pub index: u16,
    /// Padding for future updates
    pub padding: [u8; 64],
}

impl Default for GlobalConfig {
    fn default() -> Self {
        Self {
            epoch: 0,
            quote_mint: Pubkey::default(),
            protocol_fee_owner: Pubkey::default(),
            migrate_fee_owner: Pubkey::default(),
            migrate_to_amm_wallet: Pubkey::default(),
            migrate_to_cpswap_wallet: Pubkey::default(),
            trade_fee_rate: 0,
            migrate_fee: 0,
            curve_type: 0,
            index: 0,
            padding: [0u8; 64],
        }
    }
}

impl GlobalConfig {
    /// Account discriminator
    pub const DISCRIMINATOR: [u8; 8] = [149, 8, 156, 202, 160, 252, 176, 217];
    
    /// Size of the account in bytes (excluding discriminator)
    pub const LEN: usize = 8 + 32 + 32 + 32 + 32 + 32 + 8 + 8 + 1 + 2 + 64;
    
    /// Total size including discriminator
    pub const TOTAL_LEN: usize = 8 + Self::LEN;
    
    /// Deserialize from bytes (including discriminator check)
    pub fn try_from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
        if data.len() < 8 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Data too short",
            ));
        }
        
        let (discriminator, rest) = data.split_at(8);
        if discriminator != Self::DISCRIMINATOR {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Invalid discriminator",
            ));
        }
        
        Self::try_from_slice(rest)
    }
}

/// Platform configuration account containing platform-specific settings
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug)]
pub struct PlatformConfig {
    /// Account update epoch
    pub epoch: u64,
    /// Platform admin address
    pub platform_admin: Pubkey,
    /// Wallet to receive platform fees
    pub platform_fee_wallet: Pubkey,
    /// Wallet to hold platform NFTs
    pub platform_nft_wallet: Pubkey,
    /// CPSwap config for migration
    pub cpswap_config: Pubkey,
    /// Transfer fee extension authority for Token-2022
    pub transfer_fee_extension_authority: Pubkey,
    /// Platform fee rate
    pub fee_rate: u64,
    /// Creator fee rate
    pub creator_fee_rate: u64,
    /// Platform name
    pub name: String,
    /// Platform website
    pub web: String,
    /// Platform image URL
    pub img: String,
    /// NFT symbol for migration
    pub nft_symbol: String,
    /// NFT name for migration
    pub nft_name: String,
    /// NFT URI for migration
    pub nft_uri: String,
    /// Whether NFT metadata is mutable
    pub nft_is_mutable: bool,
    /// Padding for future updates
    pub padding: [u8; 64],
}

impl Default for PlatformConfig {
    fn default() -> Self {
        Self {
            epoch: 0,
            platform_admin: Pubkey::default(),
            platform_fee_wallet: Pubkey::default(),
            platform_nft_wallet: Pubkey::default(),
            cpswap_config: Pubkey::default(),
            transfer_fee_extension_authority: Pubkey::default(),
            fee_rate: 0,
            creator_fee_rate: 0,
            name: String::new(),
            web: String::new(),
            img: String::new(),
            nft_symbol: String::new(),
            nft_name: String::new(),
            nft_uri: String::new(),
            nft_is_mutable: false,
            padding: [0u8; 64],
        }
    }
}

impl PlatformConfig {
    /// Account discriminator
    pub const DISCRIMINATOR: [u8; 8] = [160, 78, 128, 0, 248, 83, 230, 160];
    
    /// Deserialize from bytes (including discriminator check)
    pub fn try_from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
        if data.len() < 8 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Data too short",
            ));
        }
        
        let (discriminator, rest) = data.split_at(8);
        if discriminator != Self::DISCRIMINATOR {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Invalid discriminator",
            ));
        }
        
        Self::try_from_slice(rest)
    }
}

/// Pool state account containing all pool information
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug)]
pub struct PoolState {
    /// Account update epoch
    pub epoch: u64,
    /// Bump seed for authority PDA
    pub auth_bump: u8,
    /// Pool status: 0 = Fund, 1 = Migrate, 2 = Trade
    pub status: u8,
    /// Base token decimals
    pub base_decimals: u8,
    /// Quote token decimals
    pub quote_decimals: u8,
    /// Migrate type: 0 = AMM, 1 = CPSwap
    pub migrate_type: u8,
    /// Total supply of base token
    pub supply: u64,
    /// Total amount of base tokens sold
    pub total_base_sell: u64,
    /// Virtual base token amount (curve parameter)
    pub virtual_base: u64,
    /// Virtual quote token amount (curve parameter)
    pub virtual_quote: u64,
    /// Real base token amount in pool
    pub real_base: u64,
    /// Real quote token amount in pool
    pub real_quote: u64,
    /// Total quote fund raising target
    pub total_quote_fund_raising: u64,
    /// Accumulated protocol fees
    pub quote_protocol_fee: u64,
    /// Accumulated platform fees
    pub platform_fee: u64,
    /// Migration fee amount
    pub migrate_fee: u64,
    /// Vesting schedule configuration
    pub vesting_schedule: VestingSchedule,
    /// Global config account
    pub global_config: Pubkey,
    /// Platform config account
    pub platform_config: Pubkey,
    /// Base token mint
    pub base_mint: Pubkey,
    /// Quote token mint
    pub quote_mint: Pubkey,
    /// Base token vault
    pub base_vault: Pubkey,
    /// Quote token vault
    pub quote_vault: Pubkey,
    /// Pool creator address
    pub creator: Pubkey,
    /// Token program flags (bit0: base, bit1: quote; 0 = spl-token, 1 = token-2022)
    pub token_program_flag: u8,
    /// AMM creator fee setting
    pub amm_creator_fee_on: AmmCreatorFeeOn,
    /// Padding for future updates
    pub padding: [u8; 62],
}

impl Default for PoolState {
    fn default() -> Self {
        Self {
            epoch: 0,
            auth_bump: 0,
            status: 0,
            base_decimals: 0,
            quote_decimals: 0,
            migrate_type: 0,
            supply: 0,
            total_base_sell: 0,
            virtual_base: 0,
            virtual_quote: 0,
            real_base: 0,
            real_quote: 0,
            total_quote_fund_raising: 0,
            quote_protocol_fee: 0,
            platform_fee: 0,
            migrate_fee: 0,
            vesting_schedule: VestingSchedule::default(),
            global_config: Pubkey::default(),
            platform_config: Pubkey::default(),
            base_mint: Pubkey::default(),
            quote_mint: Pubkey::default(),
            base_vault: Pubkey::default(),
            quote_vault: Pubkey::default(),
            creator: Pubkey::default(),
            token_program_flag: 0,
            amm_creator_fee_on: AmmCreatorFeeOn::default(),
            padding: [0u8; 62],
        }
    }
}

impl PoolState {
    /// Account discriminator
    pub const DISCRIMINATOR: [u8; 8] = [247, 237, 227, 245, 215, 195, 222, 70];
    
    /// Deserialize from bytes (including discriminator check)
    pub fn try_from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
        if data.len() < 8 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Data too short",
            ));
        }
        
        let (discriminator, rest) = data.split_at(8);
        if discriminator != Self::DISCRIMINATOR {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Invalid discriminator",
            ));
        }
        
        Self::try_from_slice(rest)
    }
    
    /// Check if pool is in funding status
    pub fn is_funding(&self) -> bool {
        self.status == 0
    }
    
    /// Check if pool is waiting for migration
    pub fn is_migrate(&self) -> bool {
        self.status == 1
    }
    
    /// Check if pool migration is complete
    pub fn is_trading(&self) -> bool {
        self.status == 2
    }
    
    /// Check if base token uses Token-2022 program
    pub fn is_base_token_2022(&self) -> bool {
        self.token_program_flag & 0x01 != 0
    }
    
    /// Check if quote token uses Token-2022 program
    pub fn is_quote_token_2022(&self) -> bool {
        self.token_program_flag & 0x02 != 0
    }
    
    /// Calculate current price (quote per base)
    pub fn get_current_price(&self) -> Option<f64> {
        if self.virtual_base == 0 {
            return None;
        }
        Some(self.virtual_quote as f64 / self.virtual_base as f64)
    }
    
    /// Calculate funding progress as percentage
    pub fn get_funding_progress(&self) -> f64 {
        if self.total_quote_fund_raising == 0 {
            return 0.0;
        }
        (self.real_quote as f64 / self.total_quote_fund_raising as f64) * 100.0
    }
}

/// Vesting record account for tracking token vesting
#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Default)]
pub struct VestingRecord {
    /// Account update epoch
    pub epoch: u64,
    /// Associated pool state
    pub pool: Pubkey,
    /// Beneficiary address
    pub beneficiary: Pubkey,
    /// Amount of tokens already claimed
    pub claimed_amount: u64,
    /// Share amount of tokens to be vested
    pub token_share_amount: u64,
    /// Padding for future updates
    pub padding: [u64; 8],
}

impl VestingRecord {
    /// Account discriminator
    pub const DISCRIMINATOR: [u8; 8] = [106, 243, 221, 205, 230, 126, 85, 83];
    
    /// Size of the account in bytes (excluding discriminator)  
    pub const LEN: usize = 8 + 32 + 32 + 8 + 8 + (8 * 8);
    
    /// Total size including discriminator
    pub const TOTAL_LEN: usize = 8 + Self::LEN;
    
    /// Deserialize from bytes (including discriminator check)
    pub fn try_from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
        if data.len() < 8 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Data too short",
            ));
        }
        
        let (discriminator, rest) = data.split_at(8);
        if discriminator != Self::DISCRIMINATOR {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Invalid discriminator",
            ));
        }
        
        Self::try_from_slice(rest)
    }
    
    /// Get remaining claimable amount
    pub fn remaining_amount(&self) -> u64 {
        self.token_share_amount.saturating_sub(self.claimed_amount)
    }
}