Skip to main content

sol_parser_sdk/core/events/
trade_source.rs

1//! Uniform provenance access for supported trade/swap event families.
2use 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    /// Original outer router instruction, when exact invocation provenance exists.
78    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}