use super::utils::*;
use crate::core::events::*;
use solana_sdk::{pubkey::Pubkey, signature::Signature};
pub mod discriminators {
pub const SWAP_BASE_IN: [u8; 8] = [143, 190, 90, 218, 196, 30, 51, 222];
pub const SWAP_BASE_OUT: [u8; 8] = [55, 217, 98, 86, 163, 74, 180, 173];
pub const CREATE_POOL: [u8; 8] = [233, 146, 209, 142, 207, 104, 64, 188];
pub const DEPOSIT: [u8; 8] = [242, 35, 198, 137, 82, 225, 242, 182];
pub const WITHDRAW: [u8; 8] = [183, 18, 70, 156, 148, 109, 161, 34];
}
pub const PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
pub fn is_raydium_cpmm_log(log: &str) -> bool {
log.contains(&format!("Program {} invoke", PROGRAM_ID))
|| log.contains(&format!("Program {} success", PROGRAM_ID))
|| (log.contains("raydium") && log.contains("cpmm"))
}
#[inline(always)] pub fn parse_log(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
parse_structured_log(log, signature, slot, tx_index, block_time_us, grpc_recv_us)
}
fn parse_structured_log(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
let program_data = extract_program_data(log)?;
if program_data.len() < 8 {
return None;
}
let discriminator: [u8; 8] = program_data[0..8].try_into().ok()?;
let data = &program_data[8..];
match discriminator {
discriminators::SWAP_BASE_IN => {
parse_swap_base_in_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
}
discriminators::SWAP_BASE_OUT => {
parse_swap_base_out_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
}
discriminators::CREATE_POOL => {
parse_create_pool_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
}
discriminators::DEPOSIT => {
parse_deposit_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
}
discriminators::WITHDRAW => {
parse_withdraw_event(data, signature, slot, tx_index, block_time_us, grpc_recv_us)
}
_ => None,
}
}
#[inline(always)]
pub fn parse_swap_base_in_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let _user = read_pubkey(data, offset)?;
offset += 32;
let amount_in = read_u64_le(data, offset)?;
offset += 8;
let _minimum_amount_out = read_u64_le(data, offset)?;
offset += 8;
let amount_out = read_u64_le(data, offset)?;
offset += 8;
let is_base_input = read_bool(data, offset)?;
Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
metadata,
pool_id: pool_state,
input_vault_before: 0,
output_vault_before: 0,
input_amount: amount_in,
output_amount: amount_out,
input_transfer_fee: 0,
output_transfer_fee: 0,
base_input: is_base_input,
}))
}
#[inline(always)]
pub fn parse_swap_base_out_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let _user = read_pubkey(data, offset)?;
offset += 32;
let _maximum_amount_in = read_u64_le(data, offset)?;
offset += 8;
let amount_out = read_u64_le(data, offset)?;
offset += 8;
let amount_in = read_u64_le(data, offset)?;
offset += 8;
let is_base_output = read_bool(data, offset)?;
Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
metadata,
pool_id: pool_state,
input_vault_before: 0,
output_vault_before: 0,
input_amount: amount_in,
output_amount: amount_out,
input_transfer_fee: 0,
output_transfer_fee: 0,
base_input: !is_base_output,
}))
}
#[inline(always)]
pub fn parse_create_pool_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let _token_0_mint = read_pubkey(data, offset)?;
offset += 32;
let _token_1_mint = read_pubkey(data, offset)?;
offset += 32;
let creator = read_pubkey(data, offset)?;
offset += 32;
let initial_amount_0 = read_u64_le(data, offset)?;
offset += 8;
let initial_amount_1 = read_u64_le(data, offset)?;
Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
metadata,
pool: pool_state,
creator,
init_amount0: initial_amount_0,
init_amount1: initial_amount_1,
}))
}
#[inline(always)]
pub fn parse_deposit_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let user = read_pubkey(data, offset)?;
offset += 32;
let lp_token_amount = read_u64_le(data, offset)?;
offset += 8;
let token_0_amount = read_u64_le(data, offset)?;
offset += 8;
let token_1_amount = read_u64_le(data, offset)?;
Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
metadata,
pool: pool_state,
user,
lp_token_amount,
token0_amount: token_0_amount,
token1_amount: token_1_amount,
}))
}
#[inline(always)]
pub fn parse_withdraw_from_data(data: &[u8], metadata: EventMetadata) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let user = read_pubkey(data, offset)?;
offset += 32;
let lp_token_amount = read_u64_le(data, offset)?;
offset += 8;
let token_0_amount = read_u64_le(data, offset)?;
offset += 8;
let token_1_amount = read_u64_le(data, offset)?;
Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
metadata,
pool: pool_state,
user,
lp_token_amount,
token0_amount: token_0_amount,
token1_amount: token_1_amount,
}))
}
fn parse_swap_base_in_event(
data: &[u8],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let user = read_pubkey(data, offset)?;
offset += 32;
let amount_in = read_u64_le(data, offset)?;
offset += 8;
let minimum_amount_out = read_u64_le(data, offset)?;
offset += 8;
let amount_out = read_u64_le(data, offset)?;
offset += 8;
let is_base_input = read_bool(data, offset)?;
let metadata =
create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
metadata,
pool_id: pool_state,
input_vault_before: 0, output_vault_before: 0, input_amount: amount_in,
output_amount: amount_out,
input_transfer_fee: 0, output_transfer_fee: 0, base_input: is_base_input,
}))
}
fn parse_swap_base_out_event(
data: &[u8],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let user = read_pubkey(data, offset)?;
offset += 32;
let maximum_amount_in = read_u64_le(data, offset)?;
offset += 8;
let amount_out = read_u64_le(data, offset)?;
offset += 8;
let amount_in = read_u64_le(data, offset)?;
offset += 8;
let is_base_output = read_bool(data, offset)?;
let metadata =
create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
metadata,
pool_id: pool_state,
input_vault_before: 0, output_vault_before: 0, input_amount: amount_in,
output_amount: amount_out,
input_transfer_fee: 0, output_transfer_fee: 0, base_input: !is_base_output,
}))
}
fn parse_create_pool_event(
data: &[u8],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let token_0_mint = read_pubkey(data, offset)?;
offset += 32;
let token_1_mint = read_pubkey(data, offset)?;
offset += 32;
let creator = read_pubkey(data, offset)?;
offset += 32;
let initial_amount_0 = read_u64_le(data, offset)?;
offset += 8;
let initial_amount_1 = read_u64_le(data, offset)?;
let metadata =
create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
metadata,
pool: pool_state,
creator,
init_amount0: initial_amount_0,
init_amount1: initial_amount_1,
}))
}
fn parse_deposit_event(
data: &[u8],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let user = read_pubkey(data, offset)?;
offset += 32;
let lp_token_amount = read_u64_le(data, offset)?;
offset += 8;
let token_0_amount = read_u64_le(data, offset)?;
offset += 8;
let token_1_amount = read_u64_le(data, offset)?;
let metadata =
create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
metadata,
pool: pool_state,
user,
lp_token_amount,
token0_amount: token_0_amount,
token1_amount: token_1_amount,
}))
}
fn parse_withdraw_event(
data: &[u8],
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
let mut offset = 0;
let pool_state = read_pubkey(data, offset)?;
offset += 32;
let user = read_pubkey(data, offset)?;
offset += 32;
let lp_token_amount = read_u64_le(data, offset)?;
offset += 8;
let token_0_amount = read_u64_le(data, offset)?;
offset += 8;
let token_1_amount = read_u64_le(data, offset)?;
let metadata =
create_metadata_simple(signature, slot, tx_index, block_time_us, pool_state, grpc_recv_us);
Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
metadata,
pool: pool_state,
user,
lp_token_amount,
token0_amount: token_0_amount,
token1_amount: token_1_amount,
}))
}
fn parse_text_log(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
use super::utils::text_parser::*;
if log.contains("swap") || log.contains("Swap") {
if log.contains("base_in") {
return parse_swap_base_in_from_text(
log,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
} else if log.contains("base_out") {
return parse_swap_base_out_from_text(
log,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
} else {
return parse_swap_base_in_from_text(
log,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
}
if log.contains("deposit") || log.contains("Deposit") {
return parse_deposit_from_text(
log,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
if log.contains("withdraw") || log.contains("Withdraw") {
return parse_withdraw_from_text(
log,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
if log.contains("create") && log.contains("pool") {
return parse_create_pool_from_text(
log,
signature,
slot,
tx_index,
block_time_us,
grpc_recv_us,
);
}
None
}
fn parse_swap_base_in_from_text(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
use super::utils::text_parser::*;
let metadata = create_metadata_simple(
signature,
slot,
tx_index,
block_time_us,
Pubkey::default(),
grpc_recv_us,
);
Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
metadata,
pool_id: Pubkey::default(),
input_vault_before: 0,
output_vault_before: 0,
input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
input_transfer_fee: 0,
output_transfer_fee: 0,
base_input: true,
}))
}
fn parse_swap_base_out_from_text(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
use super::utils::text_parser::*;
let metadata = create_metadata_simple(
signature,
slot,
tx_index,
block_time_us,
Pubkey::default(),
grpc_recv_us,
);
Some(DexEvent::RaydiumCpmmSwap(RaydiumCpmmSwapEvent {
metadata,
pool_id: Pubkey::default(),
input_vault_before: 0,
output_vault_before: 0,
input_amount: extract_number_from_text(log, "amount_in").unwrap_or(1_000_000_000),
output_amount: extract_number_from_text(log, "amount_out").unwrap_or(950_000_000),
input_transfer_fee: 0,
output_transfer_fee: 0,
base_input: false,
}))
}
fn parse_create_pool_from_text(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
use super::utils::text_parser::*;
let metadata = create_metadata_simple(
signature,
slot,
tx_index,
block_time_us,
Pubkey::default(),
grpc_recv_us,
);
Some(DexEvent::RaydiumCpmmInitialize(RaydiumCpmmInitializeEvent {
metadata,
pool: Pubkey::default(),
creator: Pubkey::default(),
init_amount0: extract_number_from_text(log, "amount_0").unwrap_or(1_000_000_000),
init_amount1: extract_number_from_text(log, "amount_1").unwrap_or(1_000_000_000),
}))
}
fn parse_deposit_from_text(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
use super::utils::text_parser::*;
let metadata = create_metadata_simple(
signature,
slot,
tx_index,
block_time_us,
Pubkey::default(),
grpc_recv_us,
);
Some(DexEvent::RaydiumCpmmDeposit(RaydiumCpmmDepositEvent {
metadata,
pool: Pubkey::default(),
user: Pubkey::default(),
lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
}))
}
fn parse_withdraw_from_text(
log: &str,
signature: Signature,
slot: u64,
tx_index: u64,
block_time_us: Option<i64>,
grpc_recv_us: i64,
) -> Option<DexEvent> {
use super::utils::text_parser::*;
let metadata = create_metadata_simple(
signature,
slot,
tx_index,
block_time_us,
Pubkey::default(),
grpc_recv_us,
);
Some(DexEvent::RaydiumCpmmWithdraw(RaydiumCpmmWithdrawEvent {
metadata,
pool: Pubkey::default(),
user: Pubkey::default(),
lp_token_amount: extract_number_from_text(log, "lp_token").unwrap_or(1_000_000),
token0_amount: extract_number_from_text(log, "token_0").unwrap_or(1_000_000_000),
token1_amount: extract_number_from_text(log, "token_1").unwrap_or(1_000_000_000),
}))
}