From a9dc4dc3e7680593c75345e2ccc7fe72f46dbf45 Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 18:53:58 +0000 Subject: [PATCH 01/10] set fixture --- .../fixtures/jupiter_swap/sample_route.json | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json diff --git a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json new file mode 100644 index 00000000..4f1d8ed1 --- /dev/null +++ b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json @@ -0,0 +1,171 @@ +{ + "description": "Jupiter Route swap - WSOL to USDT swap transaction", + "source": "https://solscan.io/tx/441ttot8CzpgsiRHvAHnNTCBwbSnPuhuy43pCjzZU9BKwBuJeW8f4TMU7FYLeqBst6WJeMEHprdQxr4thxqZSxRs", + "signature": "441ttot8CzpgsiRHvAHnNTCBwbSnPuhuy43pCjzZU9BKwBuJeW8f4TMU7FYLeqBst6WJeMEHprdQxr4thxqZSxRs", + "cluster": "mainnet-beta", + "full_transaction_note": "This transaction has 6 instructions: [0-3] Token account setup, [4] Jupiter Route swap, [5] Close account. We're testing instruction [4].", + "instruction_index": 4, + "instruction_data": "8gKxDaCUhQVtYEpLAEXpKEYshjkYgM8mjv4s27FCdzQSyLQUbmy", + "program_id": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", + "accounts": [ + { + "pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "signer": false, + "writable": false, + "description": "Token program" + }, + { + "pubkey": "B7hSadyLX8YhNT8RDcK8RbnR3KAfX4HbWvV89XmeqitA", + "signer": true, + "writable": false, + "description": "User wallet" + }, + { + "pubkey": "3c5JEJ3un3HZAtWvZ77nhNGxDGqmWM7uZ1cx4bGDsKE8", + "signer": false, + "writable": false, + "description": "User source token account" + }, + { + "pubkey": "FAXnNWMXbadmfMTfWtEu3WDymtRwsxYLGdbKoJbfLKsK", + "signer": false, + "writable": false, + "description": "User destination token account" + }, + { + "pubkey": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", + "signer": false, + "writable": false, + "description": "Jupiter program" + }, + { + "pubkey": "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk", + "signer": false, + "writable": false, + "description": "Jupiter authority" + }, + { + "pubkey": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", + "signer": false, + "writable": false, + "description": "Jupiter program" + }, + { + "pubkey": "D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf", + "signer": false, + "writable": false, + "description": "Event authority" + }, + { + "pubkey": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", + "signer": false, + "writable": false, + "description": "Jupiter program" + }, + { + "pubkey": "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc", + "signer": false, + "writable": false, + "description": "Whirlpool program" + }, + { + "pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "signer": false, + "writable": false, + "description": "Token program" + }, + { + "pubkey": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "signer": false, + "writable": false, + "description": "Token program" + }, + { + "pubkey": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", + "signer": false, + "writable": false, + "description": "Memo program" + }, + { + "pubkey": "B7hSadyLX8YhNT8RDcK8RbnR3KAfX4HbWvV89XmeqitA", + "signer": true, + "writable": false, + "description": "User wallet" + }, + { + "pubkey": "CCifuNxNLBZV9ch94GQ4QgdrmbBdG13xDfReomzqvhMC", + "signer": false, + "writable": false, + "description": "Account 14" + }, + { + "pubkey": "So11111111111111111111111111111111111111112", + "signer": false, + "writable": false, + "description": "Wrapped SOL" + }, + { + "pubkey": "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk", + "signer": false, + "writable": false, + "description": "Jupiter authority" + }, + { + "pubkey": "3c5JEJ3un3HZAtWvZ77nhNGxDGqmWM7uZ1cx4bGDsKE8", + "signer": false, + "writable": false, + "description": "User source token account" + }, + { + "pubkey": "4iBF54K8AY5dbwFZL8JBktqDrRvAnEkBs42aSRDYB14p", + "signer": false, + "writable": false, + "description": "Account 18" + }, + { + "pubkey": "FAXnNWMXbadmfMTfWtEu3WDymtRwsxYLGdbKoJbfLKsK", + "signer": false, + "writable": false, + "description": "User destination token account" + }, + { + "pubkey": "3YYrbiosq79pthXhFV8MwXThXWgb4Wn85fMiqPoo8dq4", + "signer": false, + "writable": false, + "description": "Account 20" + }, + { + "pubkey": "FdV51SZanY1thtYtseN5enYiASp78r7gtEcTo3xxg1eS", + "signer": false, + "writable": false, + "description": "Account 21" + }, + { + "pubkey": "4ThAi4HvBnXpgjBs5BDUQHosHjqMc1G6b9L88Djq1Ue8", + "signer": false, + "writable": false, + "description": "Account 22" + }, + { + "pubkey": "EHmLbEKVdix9brJJ13ey5o3VHo2f8w8Rr5NEwhBtHrrT", + "signer": false, + "writable": false, + "description": "Account 23" + }, + { + "pubkey": "68sWwn3c2exPMnAGe2STD3LFAc97bVBvGaGCHSEctK1", + "signer": false, + "writable": false, + "description": "Account 24" + } + ], + "expected_fields": { + "program_id": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", + "input_amount": "1704961004145737728", + "quoted_output_amount": "54975581388800", + "input_token_address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "output_token_address": "B7hSadyLX8YhNT8RDcK8RbnR3KAfX4HbWvV89XmeqitA", + "slippage": "50", + "raw_data": "e517cb977ae3ad2a010000002f010064000180841e00000000003da9170000000000320000" + } +} From 481169a55311662a746c382ed51787ff3b1dbf6e Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 18:57:23 +0000 Subject: [PATCH 02/10] add tests --- .../src/presets/jupiter_swap/mod.rs | 1 + .../jupiter_swap/tests/fixture_tests.rs | 232 ++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_tests.rs diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs index 69383312..1110b761 100644 --- a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs @@ -759,4 +759,5 @@ mod tests { ); println!("✅ Platform Fee field present in expanded fields"); } + mod fixture_tests; } diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_tests.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_tests.rs new file mode 100644 index 00000000..47df9342 --- /dev/null +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_tests.rs @@ -0,0 +1,232 @@ +// Fixture-based tests for Jupiter Swap instruction parsing +// See /src/chain_parsers/visualsign-solana/TESTING.md for documentation +// +// To add these tests to the existing tests module in mod.rs, add this line at the end +// of the existing `mod tests` block (before the closing brace): +// +// mod fixture_tests; +// +// This file will then be compiled as `tests::fixture_tests` + +use super::*; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; +use std::str::FromStr; +use visualsign::SignablePayloadField; + +#[derive(Debug, serde::Deserialize)] +struct TestFixture { + description: String, + source: String, + signature: String, + cluster: String, + #[serde(default)] + full_transaction_note: Option, + #[allow(dead_code)] + instruction_index: usize, + instruction_data: String, + program_id: String, + accounts: Vec, + expected_fields: serde_json::Map, +} + +#[derive(Debug, serde::Deserialize)] +struct TestAccount { + pubkey: String, + signer: bool, + writable: bool, + #[allow(dead_code)] + description: String, +} + +fn load_fixture(name: &str) -> TestFixture { + let fixture_path = format!( + "{}/tests/fixtures/jupiter_swap/{}.json", + env!("CARGO_MANIFEST_DIR"), + name + ); + let fixture_content = std::fs::read_to_string(&fixture_path) + .unwrap_or_else(|e| panic!("Failed to read fixture {}: {}", fixture_path, e)); + serde_json::from_str(&fixture_content) + .unwrap_or_else(|e| panic!("Failed to parse fixture {}: {}", fixture_path, e)) +} + +fn create_instruction_from_fixture(fixture: &TestFixture) -> Instruction { + let program_id = Pubkey::from_str(&fixture.program_id).unwrap(); + let accounts: Vec = fixture + .accounts + .iter() + .map(|acc| { + let pubkey = Pubkey::from_str(&acc.pubkey).unwrap(); + AccountMeta { + pubkey, + is_signer: acc.signer, + is_writable: acc.writable, + } + }) + .collect(); + + // Instruction data from JSON RPC responses is base58 encoded + let data = bs58::decode(&fixture.instruction_data) + .into_vec() + .expect("Failed to decode base58 instruction data"); + + Instruction { + program_id, + accounts, + data, + } +} + +#[test] +fn test_route_real_transaction() { + use crate::core::VisualizerContext; + use solana_parser::solana::structs::SolanaAccount; + + let fixture: TestFixture = load_fixture("sample_route"); + println!("\n=== Testing Real Transaction ==="); + println!("Description: {}", fixture.description); + println!("Source: {}", fixture.source); + println!("Signature: {}", fixture.signature); + println!("Cluster: {}", fixture.cluster); + if let Some(note) = &fixture.full_transaction_note { + println!("Transaction Context: {}", note); + } + println!(); + + let instruction = create_instruction_from_fixture(&fixture); + let instructions = vec![instruction.clone()]; + + // Create a context - using index 0 since we only loaded the one relevant instruction + // In reality, the fixture.instruction_index would be used with all transaction instructions + let sender = SolanaAccount { + account_key: fixture.accounts.get(0).unwrap().pubkey.clone(), + signer: false, + writable: false, + }; + let context = VisualizerContext::new(&sender, 0, &instructions); + + // Visualize + let visualizer = super::JupiterSwapVisualizer; + let result = visualizer + .visualize_tx_commands(&context) + .expect("Failed to visualize instruction"); + + // Extract the preview layout + if let SignablePayloadField::PreviewLayout { + common, + preview_layout, + } = result.signable_payload_field + { + println!("\n=== Extracted Fields ==="); + println!("Label: {}", common.label); + if let Some(title) = &preview_layout.title { + println!("Title: {}", title.text); + } + + if let Some(expanded) = &preview_layout.expanded { + println!("\nExpanded Fields:"); + for field in &expanded.fields { + match &field.signable_payload_field { + SignablePayloadField::TextV2 { common, text_v2 } => { + println!(" {}: {}", common.label, text_v2.text); + } + SignablePayloadField::Number { common, number } => { + println!(" {}: {}", common.label, number.number); + } + SignablePayloadField::AmountV2 { common, amount_v2 } => { + println!(" {}: {}", common.label, amount_v2.amount); + } + _ => {} + } + } + } + + // Validate against expected fields + println!("\n=== Validation ==="); + for (key, expected_value) in &fixture.expected_fields { + let expected_str = expected_value + .as_str() + .unwrap_or_else(|| panic!("Expected field '{}' is not a string", key)); + + if let Some(expanded) = &preview_layout.expanded { + let found = expanded.fields.iter().any(|field| { + match &field.signable_payload_field { + SignablePayloadField::TextV2 { common, text_v2 } => { + let label_normalized = common.label.to_lowercase().replace(" ", "_"); + let key_normalized = key.to_lowercase(); + let label_matches = label_normalized == key_normalized; + let value_matches = text_v2.text == expected_str; + + if label_matches { + if value_matches { + println!("✓ {}: {} (matches)", key, expected_str); + } else { + println!( + "✗ {}: expected '{}', got '{}'", + key, expected_str, text_v2.text + ); + } + return value_matches; + } + false + } + SignablePayloadField::Number { common, number } => { + let label_normalized = common.label.to_lowercase().replace(" ", "_"); + let key_normalized = key.to_lowercase(); + let label_matches = label_normalized == key_normalized; + let value_matches = number.number == expected_str; + + if label_matches { + if value_matches { + println!("✓ {}: {} (matches)", key, expected_str); + } else { + println!( + "✗ {}: expected '{}', got '{}'", + key, expected_str, number.number + ); + } + return value_matches; + } + false + } + SignablePayloadField::AmountV2 { common, amount_v2 } => { + let label_normalized = common.label.to_lowercase().replace(" ", "_"); + let key_normalized = key.to_lowercase(); + let label_matches = label_normalized == key_normalized; + let value_matches = amount_v2.amount == expected_str; + + if label_matches { + if value_matches { + println!("✓ {}: {} (matches)", key, expected_str); + } else { + println!( + "✗ {}: expected '{}', got '{}'", + key, expected_str, amount_v2.amount + ); + } + return value_matches; + } + false + } + _ => false, + } + }); + + if !found { + println!("✗ {}: field not found in output", key); + } + + assert!( + found, + "Expected field '{}' with value '{}' not found in visualization", + key, expected_str + ); + } + } + } else { + panic!("Expected PreviewLayout field type"); + } +} From 0f56f43036191b27d7a9e177e7274aaf6401ab61 Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 19:41:10 +0000 Subject: [PATCH 03/10] linter --- src/Cargo.lock | 2 ++ src/chain_parsers/visualsign-solana/Cargo.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Cargo.lock b/src/Cargo.lock index 875ef7d1..5e092d84 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -12007,8 +12007,10 @@ dependencies = [ "base64 0.22.1", "bincode", "borsh 1.5.7", + "bs58 0.5.1", "hex", "jupiter-swap-api-client", + "serde", "serde_json", "solana-program", "solana-sdk", diff --git a/src/chain_parsers/visualsign-solana/Cargo.toml b/src/chain_parsers/visualsign-solana/Cargo.toml index 5f023d7d..26001084 100644 --- a/src/chain_parsers/visualsign-solana/Cargo.toml +++ b/src/chain_parsers/visualsign-solana/Cargo.toml @@ -21,6 +21,8 @@ spl-stake-pool = "2.0.2" solana-system-interface = "1.0" [dev-dependencies] +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" jupiter-swap-api-client = "0.2.0" base64 = "0.22.1" +bs58 = "0.5" From ba3fa77d78d556d0a8e78fd9d3d3491033fdb020 Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 19:43:03 +0000 Subject: [PATCH 04/10] rename file --- .../visualsign-solana/src/presets/jupiter_swap/mod.rs | 2 +- .../presets/jupiter_swap/tests/{fixture_tests.rs => tests.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/{fixture_tests.rs => tests.rs} (100%) diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs index 1110b761..40e5a576 100644 --- a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs @@ -759,5 +759,5 @@ mod tests { ); println!("✅ Platform Fee field present in expanded fields"); } - mod fixture_tests; + mod tests; } diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_tests.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs similarity index 100% rename from src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_tests.rs rename to src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs From f162a3d651a62af8aa883cb6015222a00f5b1bdb Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 19:45:27 +0000 Subject: [PATCH 05/10] fix amount in expected fields --- .../tests/fixtures/jupiter_swap/sample_route.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json index 4f1d8ed1..b0ced6e9 100644 --- a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json +++ b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json @@ -161,8 +161,8 @@ ], "expected_fields": { "program_id": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", - "input_amount": "1704961004145737728", - "quoted_output_amount": "54975581388800", + "input_amount": "2000000", + "quoted_output_amount": "1550653", "input_token_address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "output_token_address": "B7hSadyLX8YhNT8RDcK8RbnR3KAfX4HbWvV89XmeqitA", "slippage": "50", From cd0075f21e2ef2e8ae94654c90ec757e8b24515f Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 19:48:57 +0000 Subject: [PATCH 06/10] linter --- .../src/presets/jupiter_swap/tests/tests.rs | 117 +++++++++--------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs index 47df9342..121bf294 100644 --- a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs @@ -152,68 +152,73 @@ fn test_route_real_transaction() { .unwrap_or_else(|| panic!("Expected field '{}' is not a string", key)); if let Some(expanded) = &preview_layout.expanded { - let found = expanded.fields.iter().any(|field| { - match &field.signable_payload_field { - SignablePayloadField::TextV2 { common, text_v2 } => { - let label_normalized = common.label.to_lowercase().replace(" ", "_"); - let key_normalized = key.to_lowercase(); - let label_matches = label_normalized == key_normalized; - let value_matches = text_v2.text == expected_str; - - if label_matches { - if value_matches { - println!("✓ {}: {} (matches)", key, expected_str); - } else { - println!( - "✗ {}: expected '{}', got '{}'", - key, expected_str, text_v2.text - ); + let found = + expanded + .fields + .iter() + .any(|field| match &field.signable_payload_field { + SignablePayloadField::TextV2 { common, text_v2 } => { + let label_normalized = + common.label.to_lowercase().replace(" ", "_"); + let key_normalized = key.to_lowercase(); + let label_matches = label_normalized == key_normalized; + let value_matches = text_v2.text == expected_str; + + if label_matches { + if value_matches { + println!("✓ {}: {} (matches)", key, expected_str); + } else { + println!( + "✗ {}: expected '{}', got '{}'", + key, expected_str, text_v2.text + ); + } + return value_matches; } - return value_matches; + false } - false - } - SignablePayloadField::Number { common, number } => { - let label_normalized = common.label.to_lowercase().replace(" ", "_"); - let key_normalized = key.to_lowercase(); - let label_matches = label_normalized == key_normalized; - let value_matches = number.number == expected_str; - - if label_matches { - if value_matches { - println!("✓ {}: {} (matches)", key, expected_str); - } else { - println!( - "✗ {}: expected '{}', got '{}'", - key, expected_str, number.number - ); + SignablePayloadField::Number { common, number } => { + let label_normalized = + common.label.to_lowercase().replace(" ", "_"); + let key_normalized = key.to_lowercase(); + let label_matches = label_normalized == key_normalized; + let value_matches = number.number == expected_str; + + if label_matches { + if value_matches { + println!("✓ {}: {} (matches)", key, expected_str); + } else { + println!( + "✗ {}: expected '{}', got '{}'", + key, expected_str, number.number + ); + } + return value_matches; } - return value_matches; + false } - false - } - SignablePayloadField::AmountV2 { common, amount_v2 } => { - let label_normalized = common.label.to_lowercase().replace(" ", "_"); - let key_normalized = key.to_lowercase(); - let label_matches = label_normalized == key_normalized; - let value_matches = amount_v2.amount == expected_str; - - if label_matches { - if value_matches { - println!("✓ {}: {} (matches)", key, expected_str); - } else { - println!( - "✗ {}: expected '{}', got '{}'", - key, expected_str, amount_v2.amount - ); + SignablePayloadField::AmountV2 { common, amount_v2 } => { + let label_normalized = + common.label.to_lowercase().replace(" ", "_"); + let key_normalized = key.to_lowercase(); + let label_matches = label_normalized == key_normalized; + let value_matches = amount_v2.amount == expected_str; + + if label_matches { + if value_matches { + println!("✓ {}: {} (matches)", key, expected_str); + } else { + println!( + "✗ {}: expected '{}', got '{}'", + key, expected_str, amount_v2.amount + ); + } + return value_matches; } - return value_matches; + false } - false - } - _ => false, - } - }); + _ => false, + }); if !found { println!("✗ {}: field not found in output", key); From 9c2e1782cb40986a74ea71269cd77b4da1ffee6c Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 19:57:30 +0000 Subject: [PATCH 07/10] revert rename --- .../visualsign-solana/src/presets/jupiter_swap/mod.rs | 2 +- .../presets/jupiter_swap/tests/{tests.rs => fixture_test.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/{tests.rs => fixture_test.rs} (100%) diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs index 40e5a576..188c5e93 100644 --- a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs @@ -759,5 +759,5 @@ mod tests { ); println!("✅ Platform Fee field present in expanded fields"); } - mod tests; + mod fixture_test; } diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_test.rs similarity index 100% rename from src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/tests.rs rename to src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_test.rs From 98c2dd215d7b9ea69295ef1d96b83fc7dbbf2107 Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 20:02:31 +0000 Subject: [PATCH 08/10] more lint --- .../jupiter_swap/tests/fixture_test.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_test.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_test.rs index 121bf294..f91645c0 100644 --- a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_test.rs +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/tests/fixture_test.rs @@ -48,9 +48,9 @@ fn load_fixture(name: &str) -> TestFixture { name ); let fixture_content = std::fs::read_to_string(&fixture_path) - .unwrap_or_else(|e| panic!("Failed to read fixture {}: {}", fixture_path, e)); + .unwrap_or_else(|e| panic!("Failed to read fixture {fixture_path}: {e}")); serde_json::from_str(&fixture_content) - .unwrap_or_else(|e| panic!("Failed to parse fixture {}: {}", fixture_path, e)) + .unwrap_or_else(|e| panic!("Failed to parse fixture {fixture_path}: {e}")) } fn create_instruction_from_fixture(fixture: &TestFixture) -> Instruction { @@ -92,7 +92,7 @@ fn test_route_real_transaction() { println!("Signature: {}", fixture.signature); println!("Cluster: {}", fixture.cluster); if let Some(note) = &fixture.full_transaction_note { - println!("Transaction Context: {}", note); + println!("Transaction Context: {note}"); } println!(); @@ -102,7 +102,7 @@ fn test_route_real_transaction() { // Create a context - using index 0 since we only loaded the one relevant instruction // In reality, the fixture.instruction_index would be used with all transaction instructions let sender = SolanaAccount { - account_key: fixture.accounts.get(0).unwrap().pubkey.clone(), + account_key: fixture.accounts.first().unwrap().pubkey.clone(), signer: false, writable: false, }; @@ -149,7 +149,7 @@ fn test_route_real_transaction() { for (key, expected_value) in &fixture.expected_fields { let expected_str = expected_value .as_str() - .unwrap_or_else(|| panic!("Expected field '{}' is not a string", key)); + .unwrap_or_else(|| panic!("Expected field '{key}' is not a string")); if let Some(expanded) = &preview_layout.expanded { let found = @@ -166,7 +166,7 @@ fn test_route_real_transaction() { if label_matches { if value_matches { - println!("✓ {}: {} (matches)", key, expected_str); + println!("✓ {key}: {expected_str} (matches)"); } else { println!( "✗ {}: expected '{}', got '{}'", @@ -186,7 +186,7 @@ fn test_route_real_transaction() { if label_matches { if value_matches { - println!("✓ {}: {} (matches)", key, expected_str); + println!("✓ {key}: {expected_str} (matches)"); } else { println!( "✗ {}: expected '{}', got '{}'", @@ -206,7 +206,7 @@ fn test_route_real_transaction() { if label_matches { if value_matches { - println!("✓ {}: {} (matches)", key, expected_str); + println!("✓ {key}: {expected_str} (matches)"); } else { println!( "✗ {}: expected '{}', got '{}'", @@ -221,13 +221,12 @@ fn test_route_real_transaction() { }); if !found { - println!("✗ {}: field not found in output", key); + println!("✗ {key}: field not found in output"); } assert!( found, - "Expected field '{}' with value '{}' not found in visualization", - key, expected_str + "Expected field '{key}' with value '{expected_str}' not found in visualization" ); } } From a7573583950ad2ee8b8b977d66c28cb20a1c4939 Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Thu, 6 Nov 2025 23:10:16 +0000 Subject: [PATCH 09/10] fix fixture --- .../fixtures/jupiter_swap/sample_route.json | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json index b0ced6e9..b57b6553 100644 --- a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json +++ b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json @@ -1,5 +1,5 @@ { - "description": "Jupiter Route swap - WSOL to USDT swap transaction", + "description": "Jupiter Route swap - WSOL to USELESS swap transaction", "source": "https://solscan.io/tx/441ttot8CzpgsiRHvAHnNTCBwbSnPuhuy43pCjzZU9BKwBuJeW8f4TMU7FYLeqBst6WJeMEHprdQxr4thxqZSxRs", "signature": "441ttot8CzpgsiRHvAHnNTCBwbSnPuhuy43pCjzZU9BKwBuJeW8f4TMU7FYLeqBst6WJeMEHprdQxr4thxqZSxRs", "cluster": "mainnet-beta", @@ -17,19 +17,19 @@ { "pubkey": "B7hSadyLX8YhNT8RDcK8RbnR3KAfX4HbWvV89XmeqitA", "signer": true, - "writable": false, + "writable": true, "description": "User wallet" }, { "pubkey": "3c5JEJ3un3HZAtWvZ77nhNGxDGqmWM7uZ1cx4bGDsKE8", "signer": false, - "writable": false, + "writable": true, "description": "User source token account" }, { "pubkey": "FAXnNWMXbadmfMTfWtEu3WDymtRwsxYLGdbKoJbfLKsK", "signer": false, - "writable": false, + "writable": true, "description": "User destination token account" }, { @@ -42,7 +42,7 @@ "pubkey": "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk", "signer": false, "writable": false, - "description": "Jupiter authority" + "description": "Output token mint (USELESS) - position 5" }, { "pubkey": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", @@ -95,67 +95,67 @@ { "pubkey": "CCifuNxNLBZV9ch94GQ4QgdrmbBdG13xDfReomzqvhMC", "signer": false, - "writable": false, + "writable": true, "description": "Account 14" }, { "pubkey": "So11111111111111111111111111111111111111112", "signer": false, "writable": false, - "description": "Wrapped SOL" + "description": "Input token mint (WSOL) - position 15" }, { "pubkey": "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk", "signer": false, "writable": false, - "description": "Jupiter authority" + "description": "Output token mint (USELESS) - position 16 (duplicate)" }, { "pubkey": "3c5JEJ3un3HZAtWvZ77nhNGxDGqmWM7uZ1cx4bGDsKE8", "signer": false, - "writable": false, + "writable": true, "description": "User source token account" }, { "pubkey": "4iBF54K8AY5dbwFZL8JBktqDrRvAnEkBs42aSRDYB14p", "signer": false, - "writable": false, + "writable": true, "description": "Account 18" }, { "pubkey": "FAXnNWMXbadmfMTfWtEu3WDymtRwsxYLGdbKoJbfLKsK", "signer": false, - "writable": false, + "writable": true, "description": "User destination token account" }, { "pubkey": "3YYrbiosq79pthXhFV8MwXThXWgb4Wn85fMiqPoo8dq4", "signer": false, - "writable": false, + "writable": true, "description": "Account 20" }, { "pubkey": "FdV51SZanY1thtYtseN5enYiASp78r7gtEcTo3xxg1eS", "signer": false, - "writable": false, + "writable": true, "description": "Account 21" }, { "pubkey": "4ThAi4HvBnXpgjBs5BDUQHosHjqMc1G6b9L88Djq1Ue8", "signer": false, - "writable": false, + "writable": true, "description": "Account 22" }, { "pubkey": "EHmLbEKVdix9brJJ13ey5o3VHo2f8w8Rr5NEwhBtHrrT", "signer": false, - "writable": false, + "writable": true, "description": "Account 23" }, { "pubkey": "68sWwn3c2exPMnAGe2STD3LFAc97bVBvGaGCHSEctK1", "signer": false, - "writable": false, + "writable": true, "description": "Account 24" } ], @@ -163,8 +163,8 @@ "program_id": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", "input_amount": "2000000", "quoted_output_amount": "1550653", - "input_token_address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "output_token_address": "B7hSadyLX8YhNT8RDcK8RbnR3KAfX4HbWvV89XmeqitA", + "input_token_address": "So11111111111111111111111111111111111111112", + "output_token_address": "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk", "slippage": "50", "raw_data": "e517cb977ae3ad2a010000002f010064000180841e00000000003da9170000000000320000" } From dd492ff0f8a31f4cec5423dffed816b217b55eca Mon Sep 17 00:00:00 2001 From: MuhammadHassan Arshad Date: Fri, 7 Nov 2025 18:44:21 +0000 Subject: [PATCH 10/10] remove input_token_address + fix output_token index --- .../visualsign-solana/src/presets/jupiter_swap/mod.rs | 9 ++++----- .../tests/fixtures/jupiter_swap/sample_route.json | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs index 188c5e93..5fa3904d 100644 --- a/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs +++ b/src/chain_parsers/visualsign-solana/src/presets/jupiter_swap/mod.rs @@ -201,7 +201,7 @@ fn parse_route_instruction( JupiterSwapInstruction::parse_amounts_and_slippage_from_data(data)?; let in_token = accounts.first().map(|addr| get_token_info(addr, in_amount)); - let out_token = accounts.get(1).map(|addr| get_token_info(addr, out_amount)); + let out_token = accounts.get(5).map(|addr| get_token_info(addr, out_amount)); Ok(JupiterSwapInstruction::Route { in_token, @@ -219,7 +219,7 @@ fn parse_exact_out_route_instruction( JupiterSwapInstruction::parse_amounts_and_slippage_from_data(data)?; let in_token = accounts.first().map(|addr| get_token_info(addr, in_amount)); - let out_token = accounts.get(1).map(|addr| get_token_info(addr, out_amount)); + let out_token = accounts.get(5).map(|addr| get_token_info(addr, out_amount)); Ok(JupiterSwapInstruction::ExactOutRoute { in_token, @@ -237,7 +237,7 @@ fn parse_shared_accounts_route_instruction( JupiterSwapInstruction::parse_amounts_and_slippage_from_data(data)?; let in_token = accounts.first().map(|addr| get_token_info(addr, in_amount)); - let out_token = accounts.get(1).map(|addr| get_token_info(addr, out_amount)); + let out_token = accounts.get(5).map(|addr| get_token_info(addr, out_amount)); Ok(JupiterSwapInstruction::SharedAccountsRoute { in_token, @@ -349,8 +349,7 @@ fn create_jupiter_swap_expanded_fields( .map_err(|e| VisualSignError::ConversionError(e.to_string()))?, create_text_field("Input Token Name", &token.name) .map_err(|e| VisualSignError::ConversionError(e.to_string()))?, - create_text_field("Input Token Address", &token.address) - .map_err(|e| VisualSignError::ConversionError(e.to_string()))?, + // TODO: Add back Input Token Address ]); } diff --git a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json index b57b6553..bd2d4a24 100644 --- a/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json +++ b/src/chain_parsers/visualsign-solana/tests/fixtures/jupiter_swap/sample_route.json @@ -163,7 +163,6 @@ "program_id": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", "input_amount": "2000000", "quoted_output_amount": "1550653", - "input_token_address": "So11111111111111111111111111111111111111112", "output_token_address": "Dz9mQ9NzkBcCsuGPFJ3r1bS4wgqKMHBPiVuniW8Mbonk", "slippage": "50", "raw_data": "e517cb977ae3ad2a010000002f010064000180841e00000000003da9170000000000320000"