use {
crate::{
error::FarmError,
id::zero,
instruction::raydium::{
RaydiumAddLiquidity, RaydiumRemoveLiquidity, RaydiumStake, RaydiumSwap, RaydiumUnstake,
},
math,
pack::check_data_len,
program::account,
},
arrayref::{array_ref, array_refs},
solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
instruction::{AccountMeta, Instruction},
msg,
program::{invoke, invoke_signed},
program_error::ProgramError,
pubkey::Pubkey,
},
};
pub mod raydium_v2 {
solana_program::declare_id!("RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr");
}
pub mod raydium_v3 {
solana_program::declare_id!("27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv");
}
pub mod raydium_v4 {
solana_program::declare_id!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
}
pub mod raydium_stake {
solana_program::declare_id!("EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q");
}
pub mod raydium_stake_v4 {
solana_program::declare_id!("CBuCnLe26faBpcBP2fktp4rp8abpcAnTWft6ZrP5Q4T");
}
pub mod raydium_stake_v5 {
solana_program::declare_id!("9KEPoZmtHUrBbhWN1v1KWLMkkvwY6WLtAVUCPRtRjP4z");
}
pub const RAYDIUM_FEE: f64 = 0.0025;
pub const RAYDIUM_FEE_NUMERATOR: u64 = 25;
pub const RAYDIUM_FEE_DENOMINATOR: u64 = 10000;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RaydiumUserStakeInfo {
pub state: u64,
pub farm_id: Pubkey,
pub stake_owner: Pubkey,
pub deposit_balance: u64,
pub reward_debt: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RaydiumUserStakeInfoV4 {
pub state: u64,
pub farm_id: Pubkey,
pub stake_owner: Pubkey,
pub deposit_balance: u64,
pub reward_debt: u64,
pub reward_debt_b: u64,
}
impl RaydiumUserStakeInfo {
pub const LEN: usize = 88;
pub fn get_size(&self) -> usize {
RaydiumUserStakeInfo::LEN
}
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
check_data_len(input, RaydiumUserStakeInfo::LEN)?;
let input = array_ref![input, 0, RaydiumUserStakeInfo::LEN];
#[allow(clippy::ptr_offset_with_cast)]
let (state, farm_id, stake_owner, deposit_balance, reward_debt) =
array_refs![input, 8, 32, 32, 8, 8];
Ok(Self {
state: u64::from_le_bytes(*state),
farm_id: Pubkey::new_from_array(*farm_id),
stake_owner: Pubkey::new_from_array(*stake_owner),
deposit_balance: u64::from_le_bytes(*deposit_balance),
reward_debt: u64::from_le_bytes(*reward_debt),
})
}
}
impl RaydiumUserStakeInfoV4 {
pub const LEN: usize = 96;
pub fn get_size(&self) -> usize {
RaydiumUserStakeInfoV4::LEN
}
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
check_data_len(input, RaydiumUserStakeInfoV4::LEN)?;
let input = array_ref![input, 0, RaydiumUserStakeInfoV4::LEN];
#[allow(clippy::ptr_offset_with_cast)]
let (state, farm_id, stake_owner, deposit_balance, reward_debt, reward_debt_b) =
array_refs![input, 8, 32, 32, 8, 8, 8];
Ok(Self {
state: u64::from_le_bytes(*state),
farm_id: Pubkey::new_from_array(*farm_id),
stake_owner: Pubkey::new_from_array(*stake_owner),
deposit_balance: u64::from_le_bytes(*deposit_balance),
reward_debt: u64::from_le_bytes(*reward_debt),
reward_debt_b: u64::from_le_bytes(*reward_debt_b),
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AmmInfoV4 {
pub status: u64,
pub nonce: u64,
pub order_num: u64,
pub depth: u64,
pub coin_decimals: u64,
pub pc_decimals: u64,
pub state: u64,
pub reset_flag: u64,
pub min_size: u64,
pub vol_max_cut_ratio: u64,
pub amount_wave: u64,
pub coin_lot_size: u64,
pub pc_lot_size: u64,
pub min_price_multiplier: u64,
pub max_price_multiplier: u64,
pub sys_decimal_value: u64,
pub min_separate_numerator: u64,
pub min_separate_denominator: u64,
pub trade_fee_numerator: u64,
pub trade_fee_denominator: u64,
pub pnl_numerator: u64,
pub pnl_denominator: u64,
pub swap_fee_numerator: u64,
pub swap_fee_denominator: u64,
pub need_take_pnl_coin: u64,
pub need_take_pnl_pc: u64,
pub total_pnl_pc: u64,
pub total_pnl_coin: u64,
pub pool_total_deposit_pc: u128,
pub pool_total_deposit_coin: u128,
pub swap_coin_in_amount: u128,
pub swap_pc_out_amount: u128,
pub swap_coin_to_pc_fee: u64,
pub swap_pc_in_amount: u128,
pub swap_coin_out_amount: u128,
pub swap_pc_to_coin_fee: u64,
pub token_coin: Pubkey,
pub token_pc: Pubkey,
pub coin_mint: Pubkey,
pub pc_mint: Pubkey,
pub lp_mint: Pubkey,
pub open_orders: Pubkey,
pub market: Pubkey,
pub serum_dex: Pubkey,
pub target_orders: Pubkey,
pub withdraw_queue: Pubkey,
pub token_temp_lp: Pubkey,
pub amm_owner: Pubkey,
pub pnl_owner: Pubkey,
}
impl AmmInfoV4 {
pub const LEN: usize = 752;
pub fn get_size(&self) -> usize {
AmmInfoV4::LEN
}
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
check_data_len(input, AmmInfoV4::LEN)?;
let input = array_ref![input, 0, AmmInfoV4::LEN];
#[allow(clippy::ptr_offset_with_cast)]
let (
status,
nonce,
order_num,
depth,
coin_decimals,
pc_decimals,
state,
reset_flag,
min_size,
vol_max_cut_ratio,
amount_wave,
coin_lot_size,
pc_lot_size,
min_price_multiplier,
max_price_multiplier,
sys_decimal_value,
min_separate_numerator,
min_separate_denominator,
trade_fee_numerator,
trade_fee_denominator,
pnl_numerator,
pnl_denominator,
swap_fee_numerator,
swap_fee_denominator,
need_take_pnl_coin,
need_take_pnl_pc,
total_pnl_pc,
total_pnl_coin,
pool_total_deposit_pc,
pool_total_deposit_coin,
swap_coin_in_amount,
swap_pc_out_amount,
swap_coin_to_pc_fee,
swap_pc_in_amount,
swap_coin_out_amount,
swap_pc_to_coin_fee,
token_coin,
token_pc,
coin_mint,
pc_mint,
lp_mint,
open_orders,
market,
serum_dex,
target_orders,
withdraw_queue,
token_temp_lp,
amm_owner,
pnl_owner,
) = array_refs![
input, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 16, 16, 16, 16, 8, 16, 16, 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32
];
Ok(Self {
status: u64::from_le_bytes(*status),
nonce: u64::from_le_bytes(*nonce),
order_num: u64::from_le_bytes(*order_num),
depth: u64::from_le_bytes(*depth),
coin_decimals: u64::from_le_bytes(*coin_decimals),
pc_decimals: u64::from_le_bytes(*pc_decimals),
state: u64::from_le_bytes(*state),
reset_flag: u64::from_le_bytes(*reset_flag),
min_size: u64::from_le_bytes(*min_size),
vol_max_cut_ratio: u64::from_le_bytes(*vol_max_cut_ratio),
amount_wave: u64::from_le_bytes(*amount_wave),
coin_lot_size: u64::from_le_bytes(*coin_lot_size),
pc_lot_size: u64::from_le_bytes(*pc_lot_size),
min_price_multiplier: u64::from_le_bytes(*min_price_multiplier),
max_price_multiplier: u64::from_le_bytes(*max_price_multiplier),
sys_decimal_value: u64::from_le_bytes(*sys_decimal_value),
min_separate_numerator: u64::from_le_bytes(*min_separate_numerator),
min_separate_denominator: u64::from_le_bytes(*min_separate_denominator),
trade_fee_numerator: u64::from_le_bytes(*trade_fee_numerator),
trade_fee_denominator: u64::from_le_bytes(*trade_fee_denominator),
pnl_numerator: u64::from_le_bytes(*pnl_numerator),
pnl_denominator: u64::from_le_bytes(*pnl_denominator),
swap_fee_numerator: u64::from_le_bytes(*swap_fee_numerator),
swap_fee_denominator: u64::from_le_bytes(*swap_fee_denominator),
need_take_pnl_coin: u64::from_le_bytes(*need_take_pnl_coin),
need_take_pnl_pc: u64::from_le_bytes(*need_take_pnl_pc),
total_pnl_pc: u64::from_le_bytes(*total_pnl_pc),
total_pnl_coin: u64::from_le_bytes(*total_pnl_coin),
pool_total_deposit_pc: u128::from_le_bytes(*pool_total_deposit_pc),
pool_total_deposit_coin: u128::from_le_bytes(*pool_total_deposit_coin),
swap_coin_in_amount: u128::from_le_bytes(*swap_coin_in_amount),
swap_pc_out_amount: u128::from_le_bytes(*swap_pc_out_amount),
swap_coin_to_pc_fee: u64::from_le_bytes(*swap_coin_to_pc_fee),
swap_pc_in_amount: u128::from_le_bytes(*swap_pc_in_amount),
swap_coin_out_amount: u128::from_le_bytes(*swap_coin_out_amount),
swap_pc_to_coin_fee: u64::from_le_bytes(*swap_pc_to_coin_fee),
token_coin: Pubkey::new_from_array(*token_coin),
token_pc: Pubkey::new_from_array(*token_pc),
coin_mint: Pubkey::new_from_array(*coin_mint),
pc_mint: Pubkey::new_from_array(*pc_mint),
lp_mint: Pubkey::new_from_array(*lp_mint),
open_orders: Pubkey::new_from_array(*open_orders),
market: Pubkey::new_from_array(*market),
serum_dex: Pubkey::new_from_array(*serum_dex),
target_orders: Pubkey::new_from_array(*target_orders),
withdraw_queue: Pubkey::new_from_array(*withdraw_queue),
token_temp_lp: Pubkey::new_from_array(*token_temp_lp),
amm_owner: Pubkey::new_from_array(*amm_owner),
pnl_owner: Pubkey::new_from_array(*pnl_owner),
})
}
}
pub fn check_pool_program_id(program_id: &Pubkey) -> bool {
program_id == &raydium_v2::id()
|| program_id == &raydium_v3::id()
|| program_id == &raydium_v4::id()
}
pub fn check_stake_program_id(program_id: &Pubkey) -> bool {
program_id == &raydium_stake::id()
|| program_id == &raydium_stake_v4::id()
|| program_id == &raydium_stake_v5::id()
}
pub fn get_stake_account_balance(stake_account: &AccountInfo) -> Result<u64, ProgramError> {
let data = stake_account.try_borrow_data()?;
if data.len() == RaydiumUserStakeInfoV4::LEN {
Ok(RaydiumUserStakeInfoV4::unpack(&data)?.deposit_balance)
} else if data.len() == RaydiumUserStakeInfo::LEN {
Ok(RaydiumUserStakeInfo::unpack(&data)?.deposit_balance)
} else {
Err(ProgramError::InvalidAccountData)
}
}
pub fn get_pool_token_balances<'a, 'b>(
pool_coin_token_account: &'a AccountInfo<'b>,
pool_pc_token_account: &'a AccountInfo<'b>,
amm_open_orders: &'a AccountInfo<'b>,
amm_id: &'a AccountInfo<'b>,
) -> Result<(u64, u64), ProgramError> {
let mut token_a_balance = account::get_token_balance(pool_coin_token_account)?;
let mut token_b_balance = account::get_token_balance(pool_pc_token_account)?;
if amm_open_orders.data_len() == 3228 {
let open_orders_data = amm_open_orders.try_borrow_data()?;
let base_token_total = array_ref![open_orders_data, 85, 8];
let quote_token_total = array_ref![open_orders_data, 101, 8];
token_a_balance =
math::checked_add(token_a_balance, u64::from_le_bytes(*base_token_total))?;
token_b_balance =
math::checked_add(token_b_balance, u64::from_le_bytes(*quote_token_total))?;
}
let (pnl_coin_offset, pnl_pc_offset) = if amm_id.data_len() == 624 {
(136, 144)
} else if amm_id.data_len() == 680 {
(144, 152)
} else if amm_id.data_len() == 752 {
(192, 200)
} else {
(0, 0)
};
if pnl_coin_offset > 0 {
let amm_id_data = amm_id.try_borrow_data()?;
let need_take_pnl_coin = u64::from_le_bytes(*array_ref![amm_id_data, pnl_coin_offset, 8]);
let need_take_pnl_pc = u64::from_le_bytes(*array_ref![amm_id_data, pnl_pc_offset, 8]);
token_a_balance = if let Some(res) = token_a_balance.checked_sub(need_take_pnl_coin) {
res
} else {
0
};
token_b_balance = if let Some(res) = token_b_balance.checked_sub(need_take_pnl_pc) {
res
} else {
0
};
}
Ok((token_a_balance, token_b_balance))
}
pub fn get_pool_deposit_amounts<'a, 'b>(
pool_coin_token_account: &'a AccountInfo<'b>,
pool_pc_token_account: &'a AccountInfo<'b>,
lp_token_mint: &'a AccountInfo<'b>,
amm_open_orders: &'a AccountInfo<'b>,
amm_id: &'a AccountInfo<'b>,
max_coin_token_amount: u64,
max_pc_token_amount: u64,
) -> Result<(u64, u64, u64), ProgramError> {
if max_coin_token_amount == 0 && max_pc_token_amount == 0 {
msg!("Error: At least one of token amounts must be non-zero");
return Err(ProgramError::InvalidArgument);
}
let mut coin_token_amount = max_coin_token_amount;
let mut pc_token_amount = max_pc_token_amount;
let (coin_balance, pc_balance) = get_pool_token_balances(
pool_coin_token_account,
pool_pc_token_account,
amm_open_orders,
amm_id,
)?;
if coin_balance == 0 || pc_balance == 0 {
if max_coin_token_amount == 0 || max_pc_token_amount == 0 {
msg!("Error: Both amounts must be specified for the initial deposit to an empty pool");
return Err(ProgramError::InvalidArgument);
} else {
return Ok((1, max_coin_token_amount, max_pc_token_amount));
}
}
if max_coin_token_amount == 0 {
let estimated_coin_amount = math::checked_as_u64(math::checked_div(
math::checked_mul(coin_balance as u128, max_pc_token_amount as u128)?,
pc_balance as u128,
)?)?;
coin_token_amount = if estimated_coin_amount > 1 {
estimated_coin_amount - 1
} else {
0
};
} else if max_pc_token_amount == 0 {
pc_token_amount = math::checked_add(
math::checked_as_u64(math::checked_div(
math::checked_mul(pc_balance as u128, max_coin_token_amount as u128)?,
coin_balance as u128,
)?)?,
1,
)?;
}
let min_lp_tokens_out = estimate_lp_tokens_amount(
lp_token_mint,
coin_token_amount,
pc_token_amount,
coin_balance,
pc_balance,
)?;
Ok((min_lp_tokens_out, coin_token_amount, pc_token_amount))
}
pub fn get_pool_withdrawal_amounts<'a, 'b>(
pool_coin_token_account: &'a AccountInfo<'b>,
pool_pc_token_account: &'a AccountInfo<'b>,
amm_open_orders: &'a AccountInfo<'b>,
amm_id: &'a AccountInfo<'b>,
lp_token_mint: &'a AccountInfo<'b>,
lp_token_amount: u64,
) -> Result<(u64, u64), ProgramError> {
if lp_token_amount == 0 {
msg!("Error: LP token amount must be non-zero");
return Err(ProgramError::InvalidArgument);
}
let (coin_balance, pc_balance) = get_pool_token_balances(
pool_coin_token_account,
pool_pc_token_account,
amm_open_orders,
amm_id,
)?;
if coin_balance == 0 && pc_balance == 0 {
return Ok((0, 0));
}
let lp_token_supply = account::get_token_supply(lp_token_mint)?;
if lp_token_supply == 0 {
return Ok((0, 0));
}
Ok((
math::checked_as_u64(math::checked_div(
math::checked_mul(coin_balance as u128, lp_token_amount as u128)?,
lp_token_supply as u128,
)?)?,
math::checked_as_u64(math::checked_div(
math::checked_mul(pc_balance as u128, lp_token_amount as u128)?,
lp_token_supply as u128,
)?)?,
))
}
pub fn get_pool_swap_amounts<'a, 'b>(
pool_coin_token_account: &'a AccountInfo<'b>,
pool_pc_token_account: &'a AccountInfo<'b>,
amm_open_orders: &'a AccountInfo<'b>,
amm_id: &'a AccountInfo<'b>,
coin_token_amount_in: u64,
pc_token_amount_in: u64,
) -> Result<(u64, u64), ProgramError> {
if (coin_token_amount_in == 0 && pc_token_amount_in == 0)
|| (coin_token_amount_in > 0 && pc_token_amount_in > 0)
{
msg!("Error: One and only one of token amounts must be non-zero");
return Err(ProgramError::InvalidArgument);
}
let (coin_balance, pc_balance) = get_pool_token_balances(
pool_coin_token_account,
pool_pc_token_account,
amm_open_orders,
amm_id,
)?;
if coin_balance == 0 || pc_balance == 0 {
msg!("Error: Can't swap in an empty pool");
return Err(FarmError::EmptyPool.into());
}
if coin_token_amount_in == 0 {
let amount_in_no_fee = math::get_no_fee_amount(
pc_token_amount_in,
RAYDIUM_FEE_NUMERATOR,
RAYDIUM_FEE_DENOMINATOR,
)? as u128;
let estimated_coin_amount = math::checked_as_u64(math::checked_div(
math::checked_mul(coin_balance as u128, amount_in_no_fee)?,
math::checked_add(pc_balance as u128, amount_in_no_fee)?,
)?)?;
Ok((
pc_token_amount_in,
math::get_no_fee_amount(estimated_coin_amount, 3, 100)?,
))
} else {
let amount_in_no_fee = math::get_no_fee_amount(
coin_token_amount_in,
RAYDIUM_FEE_NUMERATOR,
RAYDIUM_FEE_DENOMINATOR,
)? as u128;
let estimated_pc_amount = math::checked_as_u64(math::checked_div(
math::checked_mul(pc_balance as u128, amount_in_no_fee)?,
math::checked_add(coin_balance as u128, amount_in_no_fee)?,
)?)?;
Ok((
coin_token_amount_in,
math::get_no_fee_amount(estimated_pc_amount, 3, 100)?,
))
}
}
pub fn estimate_lp_tokens_amount(
lp_token_mint: &AccountInfo,
token_a_deposit: u64,
token_b_deposit: u64,
pool_coin_balance: u64,
pool_pc_balance: u64,
) -> Result<u64, ProgramError> {
if pool_coin_balance != 0 && pool_pc_balance != 0 {
Ok(std::cmp::min(
math::checked_as_u64(math::checked_div(
math::checked_mul(
token_a_deposit as u128,
account::get_token_supply(lp_token_mint)? as u128,
)?,
pool_coin_balance as u128,
)?)?,
math::checked_as_u64(math::checked_div(
math::checked_mul(
token_b_deposit as u128,
account::get_token_supply(lp_token_mint)? as u128,
)?,
pool_pc_balance as u128,
)?)?,
))
} else if pool_coin_balance != 0 {
math::checked_as_u64(math::checked_div(
math::checked_mul(
token_a_deposit as u128,
account::get_token_supply(lp_token_mint)? as u128,
)?,
pool_coin_balance as u128,
)?)
} else if pool_pc_balance != 0 {
math::checked_as_u64(math::checked_div(
math::checked_mul(
token_b_deposit as u128,
account::get_token_supply(lp_token_mint)? as u128,
)?,
pool_pc_balance as u128,
)?)
} else {
Ok(0)
}
}
pub fn add_liquidity(
accounts: &[AccountInfo],
max_coin_token_amount: u64,
max_pc_token_amount: u64,
) -> ProgramResult {
if let [user_account, user_token_a_account, user_token_b_account, user_lp_token_account, pool_program_id, pool_coin_token_account, pool_pc_token_account, lp_token_mint, spl_token_id, amm_id, amm_authority, amm_open_orders, amm_target, serum_market] =
accounts
{
if !check_pool_program_id(pool_program_id.key) {
return Err(ProgramError::IncorrectProgramId);
}
let raydium_accounts = vec![
AccountMeta::new_readonly(*spl_token_id.key, false),
AccountMeta::new(*amm_id.key, false),
AccountMeta::new_readonly(*amm_authority.key, false),
AccountMeta::new_readonly(*amm_open_orders.key, false),
AccountMeta::new(*amm_target.key, false),
AccountMeta::new(*lp_token_mint.key, false),
AccountMeta::new(*pool_coin_token_account.key, false),
AccountMeta::new(*pool_pc_token_account.key, false),
AccountMeta::new_readonly(*serum_market.key, false),
AccountMeta::new(*user_token_a_account.key, false),
AccountMeta::new(*user_token_b_account.key, false),
AccountMeta::new(*user_lp_token_account.key, false),
AccountMeta::new_readonly(*user_account.key, true),
];
let instruction = Instruction {
program_id: *pool_program_id.key,
accounts: raydium_accounts,
data: RaydiumAddLiquidity {
instruction: 3,
max_coin_token_amount,
max_pc_token_amount,
base_side: 0,
}
.to_vec()?,
};
invoke(&instruction, accounts)
} else {
Err(ProgramError::NotEnoughAccountKeys)
}
}
pub fn add_liquidity_with_seeds(
accounts: &[AccountInfo],
seeds: &[&[&[u8]]],
max_coin_token_amount: u64,
max_pc_token_amount: u64,
) -> ProgramResult {
if let [authority_account, token_a_custody_account, token_b_custody_account, lp_token_custody_account, pool_program_id, pool_coin_token_account, pool_pc_token_account, lp_token_mint, spl_token_id, amm_id, amm_authority, amm_open_orders, amm_target, serum_market] =
accounts
{
if !check_pool_program_id(pool_program_id.key) {
return Err(ProgramError::IncorrectProgramId);
}
let raydium_accounts = vec![
AccountMeta::new_readonly(*spl_token_id.key, false),
AccountMeta::new(*amm_id.key, false),
AccountMeta::new_readonly(*amm_authority.key, false),
AccountMeta::new_readonly(*amm_open_orders.key, false),
AccountMeta::new(*amm_target.key, false),
AccountMeta::new(*lp_token_mint.key, false),
AccountMeta::new(*pool_coin_token_account.key, false),
AccountMeta::new(*pool_pc_token_account.key, false),
AccountMeta::new_readonly(*serum_market.key, false),
AccountMeta::new(*token_a_custody_account.key, false),
AccountMeta::new(*token_b_custody_account.key, false),
AccountMeta::new(*lp_token_custody_account.key, false),
AccountMeta::new_readonly(*authority_account.key, true),
];
let instruction = Instruction {
program_id: *pool_program_id.key,
accounts: raydium_accounts,
data: RaydiumAddLiquidity {
instruction: 3,
max_coin_token_amount,
max_pc_token_amount,
base_side: 0,
}
.to_vec()?,
};
invoke_signed(&instruction, accounts, seeds)
} else {
Err(ProgramError::NotEnoughAccountKeys)
}
}
pub fn remove_liquidity_with_seeds(
accounts: &[AccountInfo],
seeds: &[&[&[u8]]],
amount: u64,
) -> ProgramResult {
if let [authority_account, token_a_custody_account, token_b_custody_account, lp_token_custody_account, pool_program_id, pool_withdraw_queue, pool_temp_lp_token_account, pool_coin_token_account, pool_pc_token_account, lp_token_mint, spl_token_id, amm_id, amm_authority, amm_open_orders, amm_target, serum_market, serum_program_id, serum_bids, serum_asks, serum_event_queue, serum_coin_vault_account, serum_pc_vault_account, serum_vault_signer] =
accounts
{
if !check_pool_program_id(pool_program_id.key) {
return Err(ProgramError::IncorrectProgramId);
}
let raydium_accounts = vec![
AccountMeta::new_readonly(*spl_token_id.key, false),
AccountMeta::new(*amm_id.key, false),
AccountMeta::new_readonly(*amm_authority.key, false),
AccountMeta::new(*amm_open_orders.key, false),
AccountMeta::new(*amm_target.key, false),
AccountMeta::new(*lp_token_mint.key, false),
AccountMeta::new(*pool_coin_token_account.key, false),
AccountMeta::new(*pool_pc_token_account.key, false),
AccountMeta::new(*pool_withdraw_queue.key, false),
AccountMeta::new(*pool_temp_lp_token_account.key, false),
AccountMeta::new_readonly(*serum_program_id.key, false),
AccountMeta::new(*serum_market.key, false),
AccountMeta::new(*serum_coin_vault_account.key, false),
AccountMeta::new(*serum_pc_vault_account.key, false),
AccountMeta::new_readonly(*serum_vault_signer.key, false),
AccountMeta::new(*lp_token_custody_account.key, false),
AccountMeta::new(*token_a_custody_account.key, false),
AccountMeta::new(*token_b_custody_account.key, false),
AccountMeta::new_readonly(*authority_account.key, true),
AccountMeta::new(*serum_event_queue.key, false),
AccountMeta::new(*serum_bids.key, false),
AccountMeta::new(*serum_asks.key, false),
];
let instruction = Instruction {
program_id: *pool_program_id.key,
accounts: raydium_accounts,
data: RaydiumRemoveLiquidity {
instruction: 4,
amount,
}
.to_vec()?,
};
invoke_signed(&instruction, accounts, seeds)
} else {
Err(ProgramError::NotEnoughAccountKeys)
}
}
pub fn stake_with_seeds(
accounts: &[AccountInfo],
seeds: &[&[&[u8]]],
amount: u64,
) -> ProgramResult {
if let [authority_account, stake_info_account, lp_token_custody_account, token_a_custody_account, token_b_custody_account, pool_program_id, farm_lp_token_account, farm_first_reward_token_account, farm_second_reward_token_account, clock_id, spl_token_id, farm_id, farm_authority] =
accounts
{
if !check_stake_program_id(pool_program_id.key) {
return Err(ProgramError::IncorrectProgramId);
}
let mut raydium_accounts = vec![
AccountMeta::new(*farm_id.key, false),
AccountMeta::new_readonly(*farm_authority.key, false),
AccountMeta::new(*stake_info_account.key, false),
AccountMeta::new_readonly(*authority_account.key, true),
AccountMeta::new(*lp_token_custody_account.key, false),
AccountMeta::new(*farm_lp_token_account.key, false),
AccountMeta::new(*token_a_custody_account.key, false),
AccountMeta::new(*farm_first_reward_token_account.key, false),
AccountMeta::new_readonly(*clock_id.key, false),
AccountMeta::new_readonly(*spl_token_id.key, false),
];
if *farm_second_reward_token_account.key != zero::id() {
raydium_accounts.push(AccountMeta::new(*token_b_custody_account.key, false));
raydium_accounts.push(AccountMeta::new(
*farm_second_reward_token_account.key,
false,
));
}
let instruction = Instruction {
program_id: *pool_program_id.key,
accounts: raydium_accounts,
data: RaydiumStake {
instruction: 1,
amount,
}
.to_vec()?,
};
invoke_signed(&instruction, accounts, seeds)
} else {
Err(ProgramError::NotEnoughAccountKeys)
}
}
pub fn swap_with_seeds(
accounts: &[AccountInfo],
seeds: &[&[&[u8]]],
amount_in: u64,
min_amount_out: u64,
) -> ProgramResult {
if let [authority_account, token_a_custody_account, token_b_custody_account, pool_program_id, pool_coin_token_account, pool_pc_token_account, spl_token_id, amm_id, amm_authority, amm_open_orders, amm_target, serum_market, serum_program_id, serum_bids, serum_asks, serum_event_queue, serum_coin_vault_account, serum_pc_vault_account, serum_vault_signer] =
accounts
{
if !check_pool_program_id(pool_program_id.key) {
return Err(ProgramError::IncorrectProgramId);
}
let raydium_accounts = vec![
AccountMeta::new_readonly(*spl_token_id.key, false),
AccountMeta::new(*amm_id.key, false),
AccountMeta::new_readonly(*amm_authority.key, false),
AccountMeta::new(*amm_open_orders.key, false),
AccountMeta::new(*amm_target.key, false),
AccountMeta::new(*pool_coin_token_account.key, false),
AccountMeta::new(*pool_pc_token_account.key, false),
AccountMeta::new_readonly(*serum_program_id.key, false),
AccountMeta::new(*serum_market.key, false),
AccountMeta::new(*serum_bids.key, false),
AccountMeta::new(*serum_asks.key, false),
AccountMeta::new(*serum_event_queue.key, false),
AccountMeta::new(*serum_coin_vault_account.key, false),
AccountMeta::new(*serum_pc_vault_account.key, false),
AccountMeta::new_readonly(*serum_vault_signer.key, false),
AccountMeta::new(*token_a_custody_account.key, false),
AccountMeta::new(*token_b_custody_account.key, false),
AccountMeta::new_readonly(*authority_account.key, true),
];
let instruction = Instruction {
program_id: *pool_program_id.key,
accounts: raydium_accounts,
data: RaydiumSwap {
instruction: 9,
amount_in,
min_amount_out,
}
.to_vec()?,
};
invoke_signed(&instruction, accounts, seeds)
} else {
Err(ProgramError::NotEnoughAccountKeys)
}
}
pub fn unstake_with_seeds(
accounts: &[AccountInfo],
seeds: &[&[&[u8]]],
amount: u64,
) -> ProgramResult {
if let [authority_account, stake_info_account, lp_token_custody_account, token_a_custody_account, token_b_custody_account, pool_program_id, farm_lp_token_account, farm_first_reward_token_account, farm_second_reward_token_account, clock_id, spl_token_id, farm_id, farm_authority] =
accounts
{
if !check_stake_program_id(pool_program_id.key) {
return Err(ProgramError::IncorrectProgramId);
}
let mut raydium_accounts = vec![
AccountMeta::new(*farm_id.key, false),
AccountMeta::new_readonly(*farm_authority.key, false),
AccountMeta::new(*stake_info_account.key, false),
AccountMeta::new_readonly(*authority_account.key, true),
AccountMeta::new(*lp_token_custody_account.key, false),
AccountMeta::new(*farm_lp_token_account.key, false),
AccountMeta::new(*token_a_custody_account.key, false),
AccountMeta::new(*farm_first_reward_token_account.key, false),
AccountMeta::new_readonly(*clock_id.key, false),
AccountMeta::new_readonly(*spl_token_id.key, false),
];
if *farm_second_reward_token_account.key != zero::id() {
raydium_accounts.push(AccountMeta::new(*token_b_custody_account.key, false));
raydium_accounts.push(AccountMeta::new(
*farm_second_reward_token_account.key,
false,
));
}
let instruction = Instruction {
program_id: *pool_program_id.key,
accounts: raydium_accounts,
data: RaydiumUnstake {
instruction: 2,
amount,
}
.to_vec()?,
};
invoke_signed(&instruction, accounts, seeds)
} else {
Err(ProgramError::NotEnoughAccountKeys)
}
}