raydium-launchlab-sdk 0.1.0

Rust SDK for Raydium LaunchLab program
Documentation
use anyhow::Result;
use crate::{
    accounts::PoolState,
    pda,
    TOKEN_PROGRAM_ID,
    calculate_sell_amount,
};
use crate::generated::instructions;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    instruction::{Instruction, AccountMeta},
    system_program,
};
use spl_associated_token_account::get_associated_token_address;
use spl_token::instruction as token_instruction;
use std::str::FromStr;

const WSOL_MINT: &str = "So11111111111111111111111111111111111111112";
const USD1_MINT: &str = "USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB";

pub fn build_sell_instructions(
    client: &RpcClient,
    payer: &Keypair,
    base_mint: &Pubkey,
    total_tokens_to_sell: u64,
    minimum_amount_out: u64,
) -> Result<Vec<Instruction>> {
    let quote_mint_usd1 = Pubkey::from_str(USD1_MINT)?;
    let quote_mint_wsol = Pubkey::from_str(WSOL_MINT)?;

    let (pool_state_usd1, _) = pda::get_pool_state_pda(base_mint, &quote_mint_usd1);
    let (pool_state_wsol, _) = pda::get_pool_state_pda(base_mint, &quote_mint_wsol);

    let (pool_state_address, quote_mint, is_wsol_pool) = 
        if client.get_account_data(&pool_state_usd1).is_ok() {
            (pool_state_usd1, quote_mint_usd1, false)
        } else if client.get_account_data(&pool_state_wsol).is_ok() {
            (pool_state_wsol, quote_mint_wsol, true)
        } else {
            anyhow::bail!("No pool found for this token (tried USD1 and WSOL)");
        };
    
    let pool_state_data = client.get_account_data(&pool_state_address)?;
    let pool_state = PoolState::try_from_bytes(&pool_state_data)?;
    
    let base_uses_token_2022 = (pool_state.token_program_flag & 0x01) != 0;
    let quote_uses_token_2022 = (pool_state.token_program_flag & 0x02) != 0;
    
    let base_token_program = if base_uses_token_2022 {
        Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")?
    } else {
        TOKEN_PROGRAM_ID
    };
    let quote_token_program = if quote_uses_token_2022 {
        Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")?
    } else {
        TOKEN_PROGRAM_ID
    };

    let _estimated_out = calculate_sell_amount(
        total_tokens_to_sell,
        pool_state.virtual_base as u128,
        pool_state.virtual_quote as u128,
        pool_state.real_base as u128,
        pool_state.real_quote as u128,
        100,
    );

    let user_base_token = if base_uses_token_2022 {
        spl_associated_token_account::get_associated_token_address_with_program_id(
            &payer.pubkey(),
            base_mint,
            &base_token_program,
        )
    } else {
        get_associated_token_address(&payer.pubkey(), base_mint)
    };

    let user_quote_token = if quote_uses_token_2022 {
        spl_associated_token_account::get_associated_token_address_with_program_id(
            &payer.pubkey(),
            &quote_mint,
            &quote_token_program,
        )
    } else {
        get_associated_token_address(&payer.pubkey(), &quote_mint)
    };

    let (base_vault, _) = pda::get_pool_vault_pda(&pool_state_address, base_mint);
    let (quote_vault, _) = pda::get_pool_vault_pda(&pool_state_address, &quote_mint);
    let (creator_fee_vault, _) = pda::get_creator_fee_vault_pda(&pool_state.creator, &quote_mint);
    let (platform_fee_vault, _) = pda::get_platform_fee_vault_pda(&pool_state.platform_config, &quote_mint);

    let mut sell_ix = instructions::sell_exact_in(
        &payer.pubkey(),
        &pool_state.global_config,
        &pool_state.platform_config,
        &pool_state_address,
        &user_base_token,
        &user_quote_token,
        &base_vault,
        &quote_vault,
        base_mint,
        &quote_mint,
        &base_token_program,
        total_tokens_to_sell,
        minimum_amount_out,
        0,
    );

    sell_ix.accounts[12] = AccountMeta::new_readonly(quote_token_program, false);
    sell_ix.accounts.push(AccountMeta::new_readonly(system_program::id(), false));
    sell_ix.accounts.push(AccountMeta::new(platform_fee_vault, false));
    sell_ix.accounts.push(AccountMeta::new(creator_fee_vault, false));

    let mut instr = vec![sell_ix];
    
    if is_wsol_pool {
        instr.push(token_instruction::close_account(
            &TOKEN_PROGRAM_ID,
            &user_quote_token,
            &payer.pubkey(),
            &payer.pubkey(),
            &[],
        )?);
    }
    
    Ok(instr)
}