@@ -4,8 +4,20 @@ use std::{path::PathBuf, time::Duration};
44
55use tokio:: sync:: broadcast;
66use yellowstone_vixen:: config:: { BufferConfig , VixenConfig } ;
7+ use yellowstone_vixen_mock:: {
8+ create_mock_transaction_update_with_cache, parse_instructions_from_txn_update,
9+ } ;
710use yellowstone_vixen_yellowstone_grpc_source:: YellowstoneGrpcConfig ;
811
12+ /// Trait for CPI events that can be parsed from instruction data and have token changes
13+ pub trait CpiEventParseable {
14+ fn from_inner_instruction_data ( data : & [ u8 ] ) -> Option < Self >
15+ where
16+ Self : Sized ;
17+ fn source_token_change ( & self ) -> u64 ;
18+ fn destination_token_change ( & self ) -> u64 ;
19+ }
20+
921/// Command line options for integration tests
1022#[ derive( clap:: Parser , Debug ) ]
1123#[ command( version, author, about = "Yellowstone Vixen Integration Tests" ) ]
@@ -179,3 +191,100 @@ where
179191 }
180192 }
181193}
194+
195+ /// Asserts that a CPI event at the specified instruction indices has the expected token changes.
196+ ///
197+ /// # Arguments
198+ /// * `signature` - Transaction signature to fetch and parse
199+ /// * `ix_path` - Path of instruction indices: [top_level, inner, nested_inner, ...]
200+ /// * `expected_source_token_change` - Expected source token change value
201+ /// * `expected_destination_token_change` - Expected destination token change value
202+ ///
203+ /// # Example
204+ /// ```ignore
205+ /// // 2-level: top-level #6 → inner #5
206+ /// assert_okx_dex_v2_cpi_event_token_changes::<SwapCpiEvent2>(sig, &[6, 5], 100, 200).await?;
207+ ///
208+ /// // 3-level: top-level #3 → inner #2 → nested #8
209+ /// assert_okx_dex_v2_cpi_event_token_changes::<SwapCpiEvent2>(sig, &[3, 2, 8], 100, 200).await?;
210+ /// ```
211+ pub async fn assert_okx_dex_v2_cpi_event_token_changes < T : CpiEventParseable > (
212+ signature : & str ,
213+ ix_path : & [ usize ] ,
214+ expected_source_token_change : u64 ,
215+ expected_destination_token_change : u64 ,
216+ ) -> Result < ( ) , Box < dyn std:: error:: Error + Send + Sync > > {
217+ assert ! (
218+ ix_path. len( ) >= 2 ,
219+ "ix_path must have at least 2 indices (top-level and inner)"
220+ ) ;
221+
222+ let txn_update = create_mock_transaction_update_with_cache ( signature)
223+ . await
224+ . expect ( "Failed to fetch transaction" ) ;
225+
226+ let instruction_updates =
227+ parse_instructions_from_txn_update ( & txn_update) . expect ( "Failed to parse instructions" ) ;
228+
229+ let top_level_ix = instruction_updates
230+ . get ( ix_path[ 0 ] )
231+ . ok_or_else ( || format ! ( "Top-level instruction index {} not found" , ix_path[ 0 ] ) ) ?;
232+
233+ // Navigate through the inner instruction path
234+ let mut current_inner = top_level_ix
235+ . inner
236+ . get ( ix_path[ 1 ] )
237+ . ok_or_else ( || format ! ( "Inner instruction index {} not found" , ix_path[ 1 ] ) ) ?;
238+
239+ for ( depth, & idx) in ix_path. iter ( ) . enumerate ( ) . skip ( 2 ) {
240+ current_inner = current_inner. inner . get ( idx) . ok_or_else ( || {
241+ format ! (
242+ "Nested instruction index {} at depth {} not found" ,
243+ idx, depth
244+ )
245+ } ) ?;
246+ }
247+
248+ let event = T :: from_inner_instruction_data ( & current_inner. data )
249+ . ok_or ( "Failed to parse CPI event from instruction data" ) ?;
250+
251+ assert_eq ! (
252+ event. source_token_change( ) ,
253+ expected_source_token_change,
254+ "source_token_change mismatch"
255+ ) ;
256+ assert_eq ! (
257+ event. destination_token_change( ) ,
258+ expected_destination_token_change,
259+ "destination_token_change mismatch"
260+ ) ;
261+
262+ Ok ( ( ) )
263+ }
264+
265+ // Macro to implement CpiEventParseable for multiple types
266+ macro_rules! impl_cpi_event_parseable {
267+ ( $( $ty: ty) ,+ $( , ) ?) => {
268+ $(
269+ impl CpiEventParseable for $ty {
270+ fn from_inner_instruction_data( data: & [ u8 ] ) -> Option <Self > {
271+ Self :: from_inner_instruction_data( data)
272+ }
273+ fn source_token_change( & self ) -> u64 {
274+ self . source_token_change
275+ }
276+ fn destination_token_change( & self ) -> u64 {
277+ self . destination_token_change
278+ }
279+ }
280+ ) +
281+ } ;
282+ }
283+
284+ impl_cpi_event_parseable ! (
285+ yellowstone_vixen_okx_dex_v2_parser:: types:: SwapCpiEvent2 ,
286+ yellowstone_vixen_okx_dex_v2_parser:: types:: SwapWithFeesCpiEvent2 ,
287+ yellowstone_vixen_okx_dex_v2_parser:: types:: SwapWithFeesCpiEventEnhanced ,
288+ yellowstone_vixen_okx_dex_v2_parser:: types:: SwapTobV2CpiEvent2 ,
289+ yellowstone_vixen_okx_dex_v2_parser:: types:: SwapTocV2CpiEvent2 ,
290+ ) ;
0 commit comments