raydium-launchlab-sdk 0.1.3

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

use solana_program::pubkey::Pubkey;

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

// =============================================================================
// Account Structures with Manual Deserialization
// =============================================================================

/// Pool state account containing all pool information
#[derive(Clone, Debug, Default)]
pub struct PoolState {
    pub epoch: u64,
    pub auth_bump: u8,
    pub status: u8,
    pub base_decimals: u8,
    pub quote_decimals: u8,
    pub migrate_type: u8,
    pub supply: u64,
    pub total_base_sell: u64,
    pub virtual_base: u64,
    pub virtual_quote: u64,
    pub real_base: u64,
    pub real_quote: u64,
    pub total_quote_fund_raising: u64,
    pub quote_protocol_fee: u64,
    pub platform_fee: u64,
    pub migrate_fee: u64,
    pub vesting_schedule: VestingSchedule,
    pub global_config: Pubkey,
    pub platform_config: Pubkey,
    pub base_mint: Pubkey,
    pub quote_mint: Pubkey,
    pub base_vault: Pubkey,
    pub quote_vault: Pubkey,
    pub creator: Pubkey,
    pub token_program_flag: u8,
    pub amm_creator_fee_on: AmmCreatorFeeOn,
}

impl PoolState {
    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::deserialize(rest)
    }
    
    fn deserialize(data: &[u8]) -> Result<Self, std::io::Error> {
        let mut offset = 0;
        
        let epoch = read_u64(data, &mut offset)?;
        let auth_bump = read_u8(data, &mut offset)?;
        let status = read_u8(data, &mut offset)?;
        let base_decimals = read_u8(data, &mut offset)?;
        let quote_decimals = read_u8(data, &mut offset)?;
        let migrate_type = read_u8(data, &mut offset)?;
        let supply = read_u64(data, &mut offset)?;
        let total_base_sell = read_u64(data, &mut offset)?;
        let virtual_base = read_u64(data, &mut offset)?;
        let virtual_quote = read_u64(data, &mut offset)?;
        let real_base = read_u64(data, &mut offset)?;
        let real_quote = read_u64(data, &mut offset)?;
        let total_quote_fund_raising = read_u64(data, &mut offset)?;
        let quote_protocol_fee = read_u64(data, &mut offset)?;
        let platform_fee = read_u64(data, &mut offset)?;
        let migrate_fee = read_u64(data, &mut offset)?;
        
        // VestingSchedule
        let total_locked_amount = read_u64(data, &mut offset)?;
        let cliff_period = read_u64(data, &mut offset)?;
        let unlock_period = read_u64(data, &mut offset)?;
        let start_time = read_u64(data, &mut offset)?;
        let allocated_share_amount = read_u64(data, &mut offset)?;
        let vesting_schedule = VestingSchedule {
            total_locked_amount,
            cliff_period,
            unlock_period,
            start_time,
            allocated_share_amount,
        };
        
        let global_config = read_pubkey(data, &mut offset)?;
        let platform_config = read_pubkey(data, &mut offset)?;
        let base_mint = read_pubkey(data, &mut offset)?;
        let quote_mint = read_pubkey(data, &mut offset)?;
        let base_vault = read_pubkey(data, &mut offset)?;
        let quote_vault = read_pubkey(data, &mut offset)?;
        let creator = read_pubkey(data, &mut offset)?;
        let token_program_flag = read_u8(data, &mut offset)?;
        let amm_creator_fee_on_byte = read_u8(data, &mut offset)?;
        
        Ok(Self {
            epoch,
            auth_bump,
            status,
            base_decimals,
            quote_decimals,
            migrate_type,
            supply,
            total_base_sell,
            virtual_base,
            virtual_quote,
            real_base,
            real_quote,
            total_quote_fund_raising,
            quote_protocol_fee,
            platform_fee,
            migrate_fee,
            vesting_schedule,
            global_config,
            platform_config,
            base_mint,
            quote_mint,
            base_vault,
            quote_vault,
            creator,
            token_program_flag,
            amm_creator_fee_on: AmmCreatorFeeOn::from_u8(amm_creator_fee_on_byte),
        })
    }
    
    pub fn is_funding(&self) -> bool {
        self.status == 0
    }
    
    pub fn is_migrate(&self) -> bool {
        self.status == 1
    }
    
    pub fn is_trading(&self) -> bool {
        self.status == 2
    }
    
    pub fn is_base_token_2022(&self) -> bool {
        self.token_program_flag & 0x01 != 0
    }
    
    pub fn is_quote_token_2022(&self) -> bool {
        self.token_program_flag & 0x02 != 0
    }
}

/// Global configuration account
#[derive(Clone, Debug, Default)]
pub struct GlobalConfig {
    pub epoch: u64,
    pub quote_mint: Pubkey,
    pub protocol_fee_owner: Pubkey,
    pub migrate_fee_owner: Pubkey,
    pub migrate_to_amm_wallet: Pubkey,
    pub migrate_to_cpswap_wallet: Pubkey,
    pub trade_fee_rate: u64,
    pub migrate_fee: u64,
    pub curve_type: u8,
    pub index: u16,
}

impl GlobalConfig {
    pub const DISCRIMINATOR: [u8; 8] = [149, 8, 156, 202, 160, 252, 176, 217];
    
    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",
            ));
        }
        
        let mut offset = 0;
        let epoch = read_u64(rest, &mut offset)?;
        let quote_mint = read_pubkey(rest, &mut offset)?;
        let protocol_fee_owner = read_pubkey(rest, &mut offset)?;
        let migrate_fee_owner = read_pubkey(rest, &mut offset)?;
        let migrate_to_amm_wallet = read_pubkey(rest, &mut offset)?;
        let migrate_to_cpswap_wallet = read_pubkey(rest, &mut offset)?;
        let trade_fee_rate = read_u64(rest, &mut offset)?;
        let migrate_fee = read_u64(rest, &mut offset)?;
        let curve_type = read_u8(rest, &mut offset)?;
        let index = read_u16(rest, &mut offset)?;
        
        Ok(Self {
            epoch,
            quote_mint,
            protocol_fee_owner,
            migrate_fee_owner,
            migrate_to_amm_wallet,
            migrate_to_cpswap_wallet,
            trade_fee_rate,
            migrate_fee,
            curve_type,
            index,
        })
    }
}

// =============================================================================
// Helper Functions for Manual Deserialization
// =============================================================================

fn read_u8(data: &[u8], offset: &mut usize) -> Result<u8, std::io::Error> {
    if *offset >= data.len() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::UnexpectedEof,
            "Unexpected end of data",
        ));
    }
    let value = data[*offset];
    *offset += 1;
    Ok(value)
}

fn read_u16(data: &[u8], offset: &mut usize) -> Result<u16, std::io::Error> {
    if *offset + 2 > data.len() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::UnexpectedEof,
            "Unexpected end of data",
        ));
    }
    let value = u16::from_le_bytes([data[*offset], data[*offset + 1]]);
    *offset += 2;
    Ok(value)
}

fn read_u64(data: &[u8], offset: &mut usize) -> Result<u64, std::io::Error> {
    if *offset + 8 > data.len() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::UnexpectedEof,
            "Unexpected end of data",
        ));
    }
    let bytes: [u8; 8] = data[*offset..*offset + 8].try_into().unwrap();
    let value = u64::from_le_bytes(bytes);
    *offset += 8;
    Ok(value)
}

fn read_pubkey(data: &[u8], offset: &mut usize) -> Result<Pubkey, std::io::Error> {
    if *offset + 32 > data.len() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::UnexpectedEof,
            "Unexpected end of data",
        ));
    }
    let bytes: [u8; 32] = data[*offset..*offset + 32].try_into().unwrap();
    let pubkey = Pubkey::new_from_array(bytes);
    *offset += 32;
    Ok(pubkey)
}