Skip to content

Commit 05c116a

Browse files
committed
feat: integration test against given cpi event
1 parent 1995862 commit 05c116a

3 files changed

Lines changed: 190 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
target/
22
crates/mock/fixtures
3+
tests/fixtures
34
*.env*
45
Vixen.toml
56
Vixen.test.toml

tests/common/mod.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,20 @@ use std::{path::PathBuf, time::Duration};
44

55
use tokio::sync::broadcast;
66
use yellowstone_vixen::config::{BufferConfig, VixenConfig};
7+
use yellowstone_vixen_mock::{
8+
create_mock_transaction_update_with_cache, parse_instructions_from_txn_update,
9+
};
710
use 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+
);

tests/integration_test.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,3 +509,83 @@ async fn test_raydium_launchpad_specific_signatures(
509509
let parser = RaydiumLaunchpadInstructionParser;
510510
test_specific_signatures("Raydium Launchpad", &parser, signatures).await
511511
}
512+
513+
/// Test OKX DEX v2 CPI event parsing from a specific transaction
514+
/// test all ix in [README.md](../crates/kryptogo-vixen-okx-dex-v2-parser/README.md)
515+
///
516+
/// NOTE:
517+
/// Use helius explorer (Orb) to see the logs for the transaction and find out inner instruction index.
518+
/// Solscan or Orb instruction page don't show inner instruction nested level
519+
/// cross reference instruction page with Solscan or Orb to find out which CPI event is the one we want to test.
520+
/// https://orbmarkets.io/tx/3Rrgt5ABbfUNoqerVQNCjfQYwafnSm3VNgmtB31aZ4y11Rc4FSHjdMzrXSkyquNnFVp8NAjrU1fAk6ero1cbw59q?tab=logs
521+
///
522+
/// For example i want to test last inner instruction (CPI event, Invoking OKX DEX Router Program) of top-level instruction (Invoking OKX DEX Router Program)
523+
/// top-level ix index to 6 and inner ix index to 6.
524+
/// The index on instruction page is 1-indexed, all index must minus 1.
525+
///
526+
/// You need to manually fill in the ix path and expected token changes and type of CPI event.
527+
/// You should add non existent type in impl_cpi_event_parseable
528+
#[tokio::test]
529+
async fn test_okx_dex_v2_cpi_event_parsing() {
530+
use yellowstone_vixen_okx_dex_v2_parser::types::{
531+
SwapCpiEvent2, SwapTocV2CpiEvent2, SwapWithFeesCpiEvent2, SwapWithFeesCpiEventEnhanced,
532+
};
533+
534+
common::assert_okx_dex_v2_cpi_event_token_changes::<SwapCpiEvent2>(
535+
"4XfXNQABC7igdCgtux9dXDb6Dj8VzxBQb5JzgpNdy3ajKdnMbRfiZbywfbuoQTvQ3XCHdBvPBSCCqzDKaenHETVY",
536+
&[3, 3], // top-level #3 → inner #3
537+
2000500000,
538+
295045121,
539+
)
540+
.await
541+
.expect("Swap CPI event parsing test failed");
542+
543+
// ProxySwap no real tx yet
544+
545+
common::assert_okx_dex_v2_cpi_event_token_changes::<SwapWithFeesCpiEvent2>(
546+
"3Rrgt5ABbfUNoqerVQNCjfQYwafnSm3VNgmtB31aZ4y11Rc4FSHjdMzrXSkyquNnFVp8NAjrU1fAk6ero1cbw59q",
547+
&[6, 6], // top-level #6 → inner #6
548+
10000000,
549+
14918710783,
550+
)
551+
.await
552+
.expect("SwapTob CPI event parsing test failed");
553+
554+
common::assert_okx_dex_v2_cpi_event_token_changes::<SwapWithFeesCpiEventEnhanced>(
555+
"2wpzTEZzyWgC9ZTHMmppcdVwKDdCE1owBby1cFPNKB2S6XWW4sc4w3mxgDq4N1Z5bhzAGhLQqk6qMDCrVEi5RVhc",
556+
&[6, 10], // top-level #6 → inner #10
557+
1000000,
558+
5699503,
559+
)
560+
.await
561+
.expect("SwapTobEnhanced CPI event parsing test failed");
562+
563+
// SwapTobV2 no real tx yet
564+
565+
common::assert_okx_dex_v2_cpi_event_token_changes::<SwapWithFeesCpiEvent2>(
566+
"5H5SLPoNyvKjSQfUfiu3PxMKiqfejMh6wuge2TmteRJc6jGxW77XzbiQsvcd9y5zGrfkQ8E7cATepgTHkTu19shp",
567+
&[3, 2, 9],
568+
4675790000,
569+
115187775,
570+
)
571+
.await
572+
.expect("SwapTobWithReceiver CPI event parsing test failed");
573+
574+
common::assert_okx_dex_v2_cpi_event_token_changes::<SwapWithFeesCpiEvent2>(
575+
"X41pjVYMdoZd15v1AnHpqV9sGspTEBfzhJ6uk95X2tdthxnQCiGDz5iLfdkhhPfV6cNX14Jpqivq5wmonDudDMi",
576+
&[4, 8],
577+
1191877137296814,
578+
7968827164,
579+
)
580+
.await
581+
.expect("SwapToc CPI event parsing test failed");
582+
583+
common::assert_okx_dex_v2_cpi_event_token_changes::<SwapTocV2CpiEvent2>(
584+
"37DzX3osK9x5jKsCZnZHtkLopf3xmEekHDubpUBd9dVxPy9yCF9TWzvy5rLNSFnM9FyqnE9LeYyGDRvs4hdXmajc",
585+
&[7, 8],
586+
1986400000,
587+
224645346850,
588+
)
589+
.await
590+
.expect("SwapTocV2 CPI event parsing test failed");
591+
}

0 commit comments

Comments
 (0)