sol_parser_sdk/core/events/
trade_source.rs1use super::{DexEvent, RawInstructionPayload};
3use solana_sdk::pubkey::Pubkey;
4
5#[cfg(test)]
6mod tests {
7 use super::*;
8 use crate::core::common_filler::fill_trade_execution_source;
9 use crate::instr::program_ids::*;
10 use yellowstone_grpc_proto::prelude::{
11 CompiledInstruction, Message, Transaction, TransactionStatusMeta,
12 };
13
14 #[test]
15 fn every_trade_family_preserves_exact_outer_payload_and_direct_calls_stay_native() {
16 let wrapper = Pubkey::new_unique();
17 let loaded = Pubkey::new_unique();
18 for (mut event, native) in [
19 (DexEvent::PumpFunTrade(Default::default()), PUMPFUN_PROGRAM_ID),
20 (DexEvent::PumpFunBuy(Default::default()), PUMPFUN_PROGRAM_ID),
21 (DexEvent::PumpFunSell(Default::default()), PUMPFUN_PROGRAM_ID),
22 (DexEvent::PumpFunBuyExactSolIn(Default::default()), PUMPFUN_PROGRAM_ID),
23 (DexEvent::PumpSwapTrade(Default::default()), PUMPSWAP_PROGRAM_ID),
24 (DexEvent::PumpSwapBuy(Default::default()), PUMPSWAP_PROGRAM_ID),
25 (DexEvent::PumpSwapSell(Default::default()), PUMPSWAP_PROGRAM_ID),
26 (DexEvent::RaydiumLaunchlabTrade(Default::default()), RAYDIUM_LAUNCHLAB_PROGRAM_ID),
27 (DexEvent::RaydiumCpmmSwap(Default::default()), RAYDIUM_CPMM_PROGRAM_ID),
28 (DexEvent::RaydiumClmmSwap(Default::default()), RAYDIUM_CLMM_PROGRAM_ID),
29 (DexEvent::RaydiumAmmV4Swap(Default::default()), RAYDIUM_AMM_V4_PROGRAM_ID),
30 (DexEvent::OrcaWhirlpoolSwap(Default::default()), ORCA_WHIRLPOOL_PROGRAM_ID),
31 (DexEvent::MeteoraPoolsSwap(Default::default()), METEORA_POOLS_PROGRAM_ID),
32 (DexEvent::MeteoraDammV2Swap(Default::default()), METEORA_DAMM_V2_PROGRAM_ID),
33 (DexEvent::MeteoraDlmmSwap(Default::default()), METEORA_DLMM_PROGRAM_ID),
34 (DexEvent::MeteoraDbcSwap(Default::default()), METEORA_DBC_PROGRAM_ID),
35 ] {
36 let transaction = Some(Transaction {
37 message: Some(Message {
38 account_keys: vec![wrapper.to_bytes().to_vec(), native.to_bytes().to_vec()],
39 instructions: vec![
40 CompiledInstruction {
41 program_id_index: 0,
42 accounts: vec![1, 2, 2, 250],
43 data: vec![7, 9],
44 },
45 CompiledInstruction {
46 program_id_index: 1,
47 accounts: vec![],
48 data: vec![8],
49 },
50 ],
51 ..Default::default()
52 }),
53 ..Default::default()
54 });
55 let meta = TransactionStatusMeta {
56 loaded_writable_addresses: vec![loaded.to_bytes().to_vec()],
57 ..Default::default()
58 };
59 let mut direct = event.clone();
60 fill_trade_execution_source(&mut direct, 1, &meta, &transaction);
61 assert!(direct.third_program().is_none());
62 fill_trade_execution_source(&mut event, 0, &meta, &transaction);
63 let raw = event.third_program().unwrap();
64 assert_eq!(raw.program_id, wrapper);
65 assert_eq!(raw.instruction_index, 0);
66 assert_eq!(raw.account_indices, vec![1, 2, 2, 250]);
67 assert_eq!(raw.accounts, vec![Some(native), Some(loaded), Some(loaded), None]);
68 assert_eq!(raw.instruction_data, vec![7, 9]);
69 let json = serde_json::to_value(&event).unwrap();
70 let restored: DexEvent = serde_json::from_value(json).unwrap();
71 assert_eq!(restored.third_program(), Some(raw));
72 }
73 }
74}
75
76impl DexEvent {
77 pub fn third_program(&self) -> Option<&RawInstructionPayload> {
79 match self {
80 Self::PumpFunTrade(t)
81 | Self::PumpFunBuy(t)
82 | Self::PumpFunSell(t)
83 | Self::PumpFunBuyExactSolIn(t) => t.third_program.as_ref(),
84 Self::PumpSwapTrade(t) => t.third_program.as_ref(),
85 Self::PumpSwapBuy(t) => t.third_program.as_ref(),
86 Self::PumpSwapSell(t) => t.third_program.as_ref(),
87 Self::RaydiumLaunchlabTrade(t) => t.third_program.as_ref(),
88 Self::RaydiumCpmmSwap(t) => t.third_program.as_ref(),
89 Self::RaydiumClmmSwap(t) => t.third_program.as_ref(),
90 Self::RaydiumAmmV4Swap(t) => t.third_program.as_ref(),
91 Self::OrcaWhirlpoolSwap(t) => t.third_program.as_ref(),
92 Self::MeteoraPoolsSwap(t) => t.third_program.as_ref(),
93 Self::MeteoraDammV2Swap(t) => t.third_program.as_ref(),
94 Self::MeteoraDlmmSwap(t) => t.third_program.as_ref(),
95 Self::MeteoraDbcSwap(t) => t.third_program.as_ref(),
96 _ => None,
97 }
98 }
99
100 pub(crate) fn trade_source_mut(
101 &mut self,
102 ) -> Option<(&mut Option<RawInstructionPayload>, Pubkey)> {
103 use crate::instr::program_ids::*;
104 match self {
105 Self::PumpFunTrade(t)
106 | Self::PumpFunBuy(t)
107 | Self::PumpFunSell(t)
108 | Self::PumpFunBuyExactSolIn(t) => Some((&mut t.third_program, PUMPFUN_PROGRAM_ID)),
109 Self::PumpSwapTrade(t) => Some((&mut t.third_program, PUMPSWAP_PROGRAM_ID)),
110 Self::PumpSwapBuy(t) => Some((&mut t.third_program, PUMPSWAP_PROGRAM_ID)),
111 Self::PumpSwapSell(t) => Some((&mut t.third_program, PUMPSWAP_PROGRAM_ID)),
112 Self::RaydiumLaunchlabTrade(t) => {
113 Some((&mut t.third_program, RAYDIUM_LAUNCHLAB_PROGRAM_ID))
114 }
115 Self::RaydiumCpmmSwap(t) => Some((&mut t.third_program, RAYDIUM_CPMM_PROGRAM_ID)),
116 Self::RaydiumClmmSwap(t) => Some((&mut t.third_program, RAYDIUM_CLMM_PROGRAM_ID)),
117 Self::RaydiumAmmV4Swap(t) => Some((&mut t.third_program, RAYDIUM_AMM_V4_PROGRAM_ID)),
118 Self::OrcaWhirlpoolSwap(t) => Some((&mut t.third_program, ORCA_WHIRLPOOL_PROGRAM_ID)),
119 Self::MeteoraPoolsSwap(t) => Some((&mut t.third_program, METEORA_POOLS_PROGRAM_ID)),
120 Self::MeteoraDammV2Swap(t) => Some((&mut t.third_program, METEORA_DAMM_V2_PROGRAM_ID)),
121 Self::MeteoraDlmmSwap(t) => Some((&mut t.third_program, METEORA_DLMM_PROGRAM_ID)),
122 Self::MeteoraDbcSwap(t) => Some((&mut t.third_program, METEORA_DBC_PROGRAM_ID)),
123 _ => None,
124 }
125 }
126}