sol_parser_sdk/core/events/
trade_source.rs1use super::{DexEvent, RawInstructionPayload, TradeParseStatus};
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 assert_eq!(direct.trade_parse_status(), Some(TradeParseStatus::ExecutionConfirmed));
63 fill_trade_execution_source(&mut event, 0, &meta, &transaction);
64 let raw = event.third_program().unwrap();
65 assert_eq!(raw.program_id, wrapper);
66 assert_eq!(raw.instruction_index, 0);
67 assert_eq!(raw.account_indices, vec![1, 2, 2, 250]);
68 assert_eq!(raw.accounts, vec![Some(native), Some(loaded), Some(loaded), None]);
69 assert_eq!(raw.instruction_data, vec![7, 9]);
70 let json = serde_json::to_value(&event).unwrap();
71 let restored: DexEvent = serde_json::from_value(json).unwrap();
72 assert_eq!(restored.third_program(), Some(raw));
73 assert_eq!(restored.trade_parse_status(), Some(TradeParseStatus::ExecutionConfirmed));
74 }
75 }
76}
77
78impl DexEvent {
79 pub fn third_program(&self) -> Option<&RawInstructionPayload> {
81 match self {
82 Self::PumpFunTrade(t)
83 | Self::PumpFunBuy(t)
84 | Self::PumpFunSell(t)
85 | Self::PumpFunBuyExactSolIn(t) => t.third_program.as_ref(),
86 Self::PumpSwapTrade(t) => t.third_program.as_ref(),
87 Self::PumpSwapBuy(t) => t.third_program.as_ref(),
88 Self::PumpSwapSell(t) => t.third_program.as_ref(),
89 Self::RaydiumLaunchlabTrade(t) => t.third_program.as_ref(),
90 Self::RaydiumCpmmSwap(t) => t.third_program.as_ref(),
91 Self::RaydiumClmmSwap(t) => t.third_program.as_ref(),
92 Self::RaydiumAmmV4Swap(t) => t.third_program.as_ref(),
93 Self::OrcaWhirlpoolSwap(t) => t.third_program.as_ref(),
94 Self::MeteoraPoolsSwap(t) => t.third_program.as_ref(),
95 Self::MeteoraDammV2Swap(t) => t.third_program.as_ref(),
96 Self::MeteoraDlmmSwap(t) => t.third_program.as_ref(),
97 Self::MeteoraDbcSwap(t) => t.third_program.as_ref(),
98 _ => None,
99 }
100 }
101
102 pub fn trade_parse_status(&self) -> Option<TradeParseStatus> {
104 match self {
105 Self::PumpFunTrade(t)
106 | Self::PumpFunBuy(t)
107 | Self::PumpFunSell(t)
108 | Self::PumpFunBuyExactSolIn(t) => Some(t.parse_status),
109 Self::PumpSwapTrade(t) => Some(t.parse_status),
110 Self::PumpSwapBuy(t) => Some(t.parse_status),
111 Self::PumpSwapSell(t) => Some(t.parse_status),
112 Self::RaydiumLaunchlabTrade(t) => Some(t.parse_status),
113 Self::RaydiumCpmmSwap(t) => Some(t.parse_status),
114 Self::RaydiumClmmSwap(t) => Some(t.parse_status),
115 Self::RaydiumAmmV4Swap(t) => Some(t.parse_status),
116 Self::OrcaWhirlpoolSwap(t) => Some(t.parse_status),
117 Self::MeteoraPoolsSwap(t) => Some(t.parse_status),
118 Self::MeteoraDammV2Swap(t) => Some(t.parse_status),
119 Self::MeteoraDlmmSwap(t) => Some(t.parse_status),
120 Self::MeteoraDbcSwap(t) => Some(t.parse_status),
121 _ => None,
122 }
123 }
124
125 pub(crate) fn trade_source_mut(
126 &mut self,
127 ) -> Option<(&mut Option<RawInstructionPayload>, &mut TradeParseStatus, Pubkey)> {
128 use crate::instr::program_ids::*;
129 match self {
130 Self::PumpFunTrade(t)
131 | Self::PumpFunBuy(t)
132 | Self::PumpFunSell(t)
133 | Self::PumpFunBuyExactSolIn(t) => {
134 Some((&mut t.third_program, &mut t.parse_status, PUMPFUN_PROGRAM_ID))
135 }
136 Self::PumpSwapTrade(t) => {
137 Some((&mut t.third_program, &mut t.parse_status, PUMPSWAP_PROGRAM_ID))
138 }
139 Self::PumpSwapBuy(t) => {
140 Some((&mut t.third_program, &mut t.parse_status, PUMPSWAP_PROGRAM_ID))
141 }
142 Self::PumpSwapSell(t) => {
143 Some((&mut t.third_program, &mut t.parse_status, PUMPSWAP_PROGRAM_ID))
144 }
145 Self::RaydiumLaunchlabTrade(t) => {
146 Some((&mut t.third_program, &mut t.parse_status, RAYDIUM_LAUNCHLAB_PROGRAM_ID))
147 }
148 Self::RaydiumCpmmSwap(t) => {
149 Some((&mut t.third_program, &mut t.parse_status, RAYDIUM_CPMM_PROGRAM_ID))
150 }
151 Self::RaydiumClmmSwap(t) => {
152 Some((&mut t.third_program, &mut t.parse_status, RAYDIUM_CLMM_PROGRAM_ID))
153 }
154 Self::RaydiumAmmV4Swap(t) => {
155 Some((&mut t.third_program, &mut t.parse_status, RAYDIUM_AMM_V4_PROGRAM_ID))
156 }
157 Self::OrcaWhirlpoolSwap(t) => {
158 Some((&mut t.third_program, &mut t.parse_status, ORCA_WHIRLPOOL_PROGRAM_ID))
159 }
160 Self::MeteoraPoolsSwap(t) => {
161 Some((&mut t.third_program, &mut t.parse_status, METEORA_POOLS_PROGRAM_ID))
162 }
163 Self::MeteoraDammV2Swap(t) => {
164 Some((&mut t.third_program, &mut t.parse_status, METEORA_DAMM_V2_PROGRAM_ID))
165 }
166 Self::MeteoraDlmmSwap(t) => {
167 Some((&mut t.third_program, &mut t.parse_status, METEORA_DLMM_PROGRAM_ID))
168 }
169 Self::MeteoraDbcSwap(t) => {
170 Some((&mut t.third_program, &mut t.parse_status, METEORA_DBC_PROGRAM_ID))
171 }
172 _ => None,
173 }
174 }
175}