use crate::core::events::*;
use crate::instr::program_ids::RAYDIUM_CLMM_PROGRAM_ID;
use solana_sdk::pubkey::Pubkey;
pub type AccountGetter<'a> = dyn Fn(usize) -> Pubkey + 'a;
const CLMM_MEMO_PROGRAM: Pubkey =
solana_sdk::pubkey!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
#[inline]
pub fn tick_array_bitmap_extension_pda(pool_state: &Pubkey) -> Pubkey {
Pubkey::find_program_address(
&[b"pool_tick_array_bitmap_extension", pool_state.as_ref()],
&RAYDIUM_CLMM_PROGRAM_ID,
)
.0
}
#[inline]
pub fn split_clmm_remaining_accounts(
pool_state: &Pubkey,
remaining: &[Pubkey],
) -> (Option<Pubkey>, Vec<Pubkey>) {
if remaining.is_empty() {
return (None, Vec::new());
}
let bitmap_pda = tick_array_bitmap_extension_pda(pool_state);
let mut bitmap = None;
let mut ticks = Vec::with_capacity(remaining.len());
for &key in remaining {
if key == bitmap_pda {
bitmap = Some(key);
} else {
ticks.push(key);
}
}
(bitmap, ticks)
}
pub fn fill_clmm_swap_accounts(e: &mut RaydiumClmmSwapEvent, get: &AccountGetter<'_>) {
if e.pool_state == Pubkey::default() {
e.pool_state = get(2);
}
if e.sender == Pubkey::default() {
e.sender = get(0);
}
if e.token_account_0 == Pubkey::default() {
e.token_account_0 = get(3);
}
if e.token_account_1 == Pubkey::default() {
e.token_account_1 = get(4);
}
if e.amm_config == Pubkey::default() {
e.amm_config = get(1);
}
if e.input_vault == Pubkey::default() {
e.input_vault = get(5);
}
if e.output_vault == Pubkey::default() {
e.output_vault = get(6);
}
if e.observation_state == Pubkey::default() {
e.observation_state = get(7);
}
let is_swap_v2 = get(10) == CLMM_MEMO_PROGRAM;
let remaining_start = if is_swap_v2 {
if e.input_mint == Pubkey::default() {
e.input_mint = get(11);
}
if e.output_mint == Pubkey::default() {
e.output_mint = get(12);
}
13usize
} else {
9usize
};
if e.tick_arrays.is_empty() && e.tick_array_bitmap_extension.is_none() {
let mut remaining = Vec::new();
let mut idx = remaining_start;
while idx < remaining_start + 16 {
let key = get(idx);
if key == Pubkey::default() {
break;
}
remaining.push(key);
idx += 1;
}
if e.pool_state != Pubkey::default() {
let (bitmap, ticks) = split_clmm_remaining_accounts(&e.pool_state, &remaining);
e.tick_array_bitmap_extension = bitmap;
e.tick_arrays = ticks;
} else {
e.tick_arrays = remaining;
}
}
}
pub fn fill_clmm_create_pool_accounts(e: &mut RaydiumClmmCreatePoolEvent, get: &AccountGetter<'_>) {
if e.creator == Pubkey::default() {
e.creator = get(0);
}
if e.pool == Pubkey::default() {
e.pool = get(2);
}
if e.token_0_mint == Pubkey::default() {
e.token_0_mint = get(3);
}
if e.token_1_mint == Pubkey::default() {
e.token_1_mint = get(4);
}
if e.token_vault_0 == Pubkey::default() {
e.token_vault_0 = get(5);
}
if e.token_vault_1 == Pubkey::default() {
e.token_vault_1 = get(6);
}
}
pub fn fill_clmm_open_position_accounts(
e: &mut RaydiumClmmOpenPositionEvent,
get: &AccountGetter<'_>,
) {
if e.user == Pubkey::default() {
e.user = get(1);
}
if e.position_nft_mint == Pubkey::default() {
e.position_nft_mint = get(2);
}
if e.pool == Pubkey::default() {
e.pool = get(5);
}
}
pub fn fill_clmm_close_position_accounts(
e: &mut RaydiumClmmClosePositionEvent,
get: &AccountGetter<'_>,
) {
if e.user == Pubkey::default() {
e.user = get(0);
}
if e.position_nft_mint == Pubkey::default() {
e.position_nft_mint = get(1);
}
}
pub fn fill_clmm_increase_liquidity_accounts(
e: &mut RaydiumClmmIncreaseLiquidityEvent,
get: &AccountGetter<'_>,
) {
if e.user == Pubkey::default() {
e.user = get(0);
}
if e.position_nft_mint == Pubkey::default() {
e.position_nft_mint = get(1);
}
if e.pool == Pubkey::default() {
e.pool = get(2);
}
}
pub fn fill_clmm_decrease_liquidity_accounts(
e: &mut RaydiumClmmDecreaseLiquidityEvent,
get: &AccountGetter<'_>,
) {
if e.user == Pubkey::default() {
e.user = get(0);
}
if e.position_nft_mint == Pubkey::default() {
e.position_nft_mint = get(1);
}
if e.pool == Pubkey::default() {
e.pool = get(3);
}
}
pub fn fill_cpmm_swap_accounts(e: &mut RaydiumCpmmSwapEvent, get: &AccountGetter<'_>) {
if e.pool_id == Pubkey::default() {
e.pool_id = get(3);
}
if e.amm_config == Pubkey::default() {
e.amm_config = get(2);
}
if e.input_vault == Pubkey::default() {
e.input_vault = get(6);
}
if e.output_vault == Pubkey::default() {
e.output_vault = get(7);
}
if e.input_token_program == Pubkey::default() {
e.input_token_program = get(8);
}
if e.output_token_program == Pubkey::default() {
e.output_token_program = get(9);
}
if e.input_token_mint == Pubkey::default() {
e.input_token_mint = get(10);
}
if e.output_token_mint == Pubkey::default() {
e.output_token_mint = get(11);
}
if e.observation_state == Pubkey::default() {
e.observation_state = get(12);
}
}
pub fn fill_cpmm_deposit_accounts(e: &mut RaydiumCpmmDepositEvent, get: &AccountGetter<'_>) {
if e.user == Pubkey::default() {
e.user = get(0); }
}
pub fn fill_cpmm_withdraw_accounts(e: &mut RaydiumCpmmWithdrawEvent, get: &AccountGetter<'_>) {
if e.user == Pubkey::default() {
e.user = get(0); }
}
pub fn fill_cpmm_initialize_accounts(e: &mut RaydiumCpmmInitializeEvent, get: &AccountGetter<'_>) {
if e.creator == Pubkey::default() {
e.creator = get(0);
}
if e.pool == Pubkey::default() {
e.pool = get(3);
}
}
pub fn fill_amm_v4_swap_accounts(e: &mut RaydiumAmmV4SwapEvent, get: &AccountGetter<'_>) {
if e.amm == Pubkey::default() {
e.amm = get(1);
}
}
pub fn fill_amm_v4_deposit_accounts(e: &mut RaydiumAmmV4DepositEvent, get: &AccountGetter<'_>) {
if e.token_program == Pubkey::default() {
e.token_program = get(0);
}
if e.amm_authority == Pubkey::default() {
e.amm_authority = get(2);
}
}
pub fn fill_amm_v4_withdraw_accounts(e: &mut RaydiumAmmV4WithdrawEvent, get: &AccountGetter<'_>) {
if e.token_program == Pubkey::default() {
e.token_program = get(0);
}
if e.amm_authority == Pubkey::default() {
e.amm_authority = get(2);
}
if e.amm_open_orders == Pubkey::default() {
e.amm_open_orders = get(3);
}
}
#[cfg(test)]
mod clmm_remaining_tests {
use super::*;
#[test]
fn bitmap_pda_matches_idl_seeds() {
let pool = Pubkey::new_unique();
let (expected, _) = Pubkey::find_program_address(
&[b"pool_tick_array_bitmap_extension", pool.as_ref()],
&RAYDIUM_CLMM_PROGRAM_ID,
);
assert_eq!(tick_array_bitmap_extension_pda(&pool), expected);
}
#[test]
fn split_pulls_bitmap_from_any_remaining_slot() {
let pool = Pubkey::new_unique();
let bitmap = tick_array_bitmap_extension_pda(&pool);
let t0 = Pubkey::new_unique();
let t1 = Pubkey::new_unique();
let (ext, ticks) = split_clmm_remaining_accounts(&pool, &[t0, bitmap, t1]);
assert_eq!(ext, Some(bitmap));
assert_eq!(ticks, vec![t0, t1]);
}
#[test]
fn fill_swap_v2_separates_bitmap_and_ticks() {
let memo = CLMM_MEMO_PROGRAM;
let pool = Pubkey::new_unique();
let bitmap = tick_array_bitmap_extension_pda(&pool);
let t0 = Pubkey::new_unique();
let t1 = Pubkey::new_unique();
let mut accounts: Vec<_> = (0..16).map(|_| Pubkey::new_unique()).collect();
accounts[2] = pool;
accounts[10] = memo;
accounts[13] = bitmap;
accounts[14] = t0;
accounts[15] = t1;
let mut e = RaydiumClmmSwapEvent::default();
fill_clmm_swap_accounts(&mut e, &|i| accounts.get(i).copied().unwrap_or_default());
assert_eq!(e.tick_array_bitmap_extension, Some(bitmap));
assert_eq!(e.tick_arrays, vec![t0, t1]);
assert_eq!(e.input_mint, accounts[11]);
assert_eq!(e.output_mint, accounts[12]);
}
#[test]
fn fill_swap_v1_starts_remaining_at_tick_array_slot() {
let pool = Pubkey::new_unique();
let t0 = Pubkey::new_unique();
let t1 = Pubkey::new_unique();
let mut accounts: Vec<_> = (0..11).map(|_| Pubkey::new_unique()).collect();
accounts[2] = pool;
accounts[9] = t0;
accounts[10] = t1;
let mut e = RaydiumClmmSwapEvent::default();
fill_clmm_swap_accounts(&mut e, &|i| accounts.get(i).copied().unwrap_or_default());
assert!(e.tick_array_bitmap_extension.is_none());
assert_eq!(e.tick_arrays, vec![t0, t1]);
assert_eq!(e.input_mint, Pubkey::default());
}
}