raydium-launchlab-sdk 0.1.6

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,
        })
    }
}

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)
}

#[derive(Clone, Debug)]
pub struct PlatformConfig {
    pub epoch: u64,
    pub platform_fee_wallet: Pubkey,
    pub platform_nft_wallet: Pubkey,
    pub platform_scale: u64,
    pub creator_scale: u64,
    pub burn_scale: u64,
    pub fee_rate: u64,
    pub name: [u8; 64],
}

impl Default for PlatformConfig {
    fn default() -> Self {
        Self {
            epoch: 0,
            platform_fee_wallet: Pubkey::default(),
            platform_nft_wallet: Pubkey::default(),
            platform_scale: 0,
            creator_scale: 0,
            burn_scale: 0,
            fee_rate: 0,
            name: [0; 64],
        }
    }
}

impl PlatformConfig {
    pub const DISCRIMINATOR: [u8; 8] = [160, 78, 128, 0, 248, 83, 230, 160];

    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 platform_fee_wallet = read_pubkey(rest, &mut offset)?;
        let platform_nft_wallet = read_pubkey(rest, &mut offset)?;
        let platform_scale = read_u64(rest, &mut offset)?;
        let creator_scale = read_u64(rest, &mut offset)?;
        let burn_scale = read_u64(rest, &mut offset)?;
        let fee_rate = read_u64(rest, &mut offset)?;

        if offset + 64 > rest.len() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::UnexpectedEof,
                "Unexpected end of data",
            ));
        }
        let name: [u8; 64] = rest[offset..offset + 64].try_into().unwrap();

        Ok(Self {
            epoch,
            platform_fee_wallet,
            platform_nft_wallet,
            platform_scale,
            creator_scale,
            burn_scale,
            fee_rate,
            name,
        })
    }
}