Prerequisites
Before testing, ensure you have:- The
parser_clibinary built (see installation instructions) - A raw transaction hex from your DApp or protocol
- The expected output you want to verify
Quick testing workflow
1. Parse your transaction
Run the parser CLI with your transaction:2. Verify the output
Check that the visualization:- Shows the correct action (swap, transfer, approval, etc.)
- Displays accurate amounts and addresses
- Uses appropriate labels that users will understand
3. Test the condensed view
Hardware wallets have limited screen space. Verify your visualization works in condensed mode:4. Check JSON output
For programmatic validation, use JSON output:jq:
Common issues
Transaction fails to parse
Cause: Incorrect chain type or malformed hex encoding. Solution: Verify the chain flag matches your transaction and that the hex is properly formatted (with or without0x prefix, depending on chain conventions).
Missing protocol details
Cause: The parser does not recognize your contract or protocol. Solution: You may need to add a protocol-specific preset. See Creating Visualizations for patterns.Output too verbose for hardware wallets
Cause: The condensed view includes too many fields. Solution: Review your visualization’sCondensed section in the PreviewLayout and reduce it to only the essential fields.
Amounts display incorrectly
Cause: Decimal handling or token metadata issues. Solution: Verify that token decimals are correctly applied. Use theamount_v2 field type with proper Amount and Abbreviation values.
Adding test fixtures
When your visualization is working, add a test fixture to ensure it does not regress.1. Save your transaction
Create a fixture file in the appropriate chain directory:my_protocol_swap.input.
2. Add expected output
Create a corresponding expected output file with the same name but.expected extension. This captures the correct JSON output for comparison.
3. Write a test
Add a test that compares the parser output against your expected fixture:4. Run tests
Verify your fixture passes:Property-based testing (Solana)
Solana IDL parsing includes proptest-based fuzz tests that verify crash safety and correctness across randomly generated IDLs and instruction data. These tests live in:src/chain_parsers/visualsign-solana/tests/fuzz_idl_parsing.rs— parser-level fuzz and roundtrip testssrc/chain_parsers/visualsign-solana/tests/pipeline_integration.rs— full-pipeline integration testssrc/chain_parsers/visualsign-solana/tests/semantic_pipeline.rs— deterministic tests with real embedded IDLssrc/chain_parsers/visualsign-solana/tests/common/mod.rs— shared test helpers
Running proptest tests
Running cargo fuzz targets (libFuzzer)
Thefuzz/ directory contains libFuzzer targets that feed arbitrary bytes into the full visualsign-solana stack. These require a nightly toolchain and cargo-fuzz:
| Target | Entry point | What it exercises |
|---|---|---|
fuzz_transaction_string | transaction_string_to_visual_sign | base64/hex decoding, transaction deserialization, IDL dispatch |
fuzz_versioned_transaction | versioned_transaction_to_visual_sign | bincode deserialization, versioned transaction path, address table lookups |
artifacts/. Reproduce it with:
Testing against real IDLs
Thescripts/fuzz_all_idls.sh script runs fuzz tests against all embedded production IDLs in one pass:
Roundtrip tests
A roundtrip test constructs an IDL and matching borsh-encoded instruction bytes, feeds them through the parser, and verifies the output matches expectations. “Roundtrip” refers to the encode-then-decode cycle: you know exactly what went in, so you can assert exactly what comes out. There are two kinds in use:-
Concrete roundtrips (e.g.,
roundtrip_single_u64_arg) — Hand-crafted IDL JSON and hand-crafted byte payloads. These assert that specific parsed values match exactly (e.g.,amount == 42). They serve as specification-by-example: each test documents one type scenario (no args, mixed primitives,Option<T>,Vec<T>, defined structs, multi-instruction dispatch). -
Property-based roundtrips (e.g.,
fuzz_valid_data_always_parses_ok) — Randomly generated IDL shapes paired with machine-generated valid borsh bytes fromarb_valid_instruction_bytes. These assert that parsing succeeds and the instruction name matches, without checking specific field values. They verify the parser’s contract holds across all type combinations, not just the hand-picked examples.
Adding a new test
- Write a strategy that generates the IDL shape you want to test (or use an existing one from
solana_parser_fuzz_core::proptest) - Add a
proptest!test that exercises the parser with generated inputs - Add a concrete roundtrip test for the same scenario to serve as specification-by-example
- Run the tests — if proptest finds a failure, it saves a regression seed to
.proptest-regressions - Commit the
.proptest-regressionsfile so the failing case is reproduced in CI
CI workflows
Tests are triggered by adding labels to a PR:| Label | Workflow | What runs |
|---|---|---|
proptest | proptest.yml | cargo test -p visualsign-solana |
fuzz | fuzz.yml | Both libFuzzer targets for 30 seconds each |
ci | main.yml | Full build, lint, and test suite |
fuzz-failure label is added to the PR. If a proptest fails, the proptest-failure label is added. These labels are removed automatically on a clean run.
End-to-end format chain with test coverage
This traces every data format transformation from wallet input to visual output, annotated with which testing methodology covers each stage.Annotation legend
| Marker | Testing type | What it proves |
|---|---|---|
| FUZZ | cargo-fuzz / libfuzzer | Never panics on arbitrary binary input |
| PROP | proptest (structured random) | Never panics + correctness invariants on structured input |
| CONCRETE | Hand-crafted exact bytes | Exact decoded values match expected |
| SEMANTIC | Real DeFi protocol data | Real-world instruction args decode correctly |
| STRUCTURAL | IDL invariant checks | Production IDLs are well-formed |
| PIPELINE | Full end-to-end pipeline | IdlRegistry -> dispatch -> SignablePayload correct |
Format summary
| Stage | Format | Covered by |
|---|---|---|
| Wire input | base64 or hex encoded string | FUZZ |
| After decode | Vec<u8> raw bytes | FUZZ |
| Transaction struct | bincode-deserialized SolanaTransaction or VersionedTransaction | FUZZ |
| Discriminator matching | First 8 bytes of instruction data (byte comparison) | PROP + CONCRETE |
| Arg encoding | Borsh (little-endian, length-prefixed strings/vecs) | PROP + CONCRETE + SizeGuard |
| IDL definition | Anchor IDL JSON (instructions, discriminators, arg types, defined types) | PROP + STRUCTURAL |
| Parsed args | serde_json::Value (JSON in memory) | CONCRETE + SEMANTIC |
| Final output | SignablePayload -> validated JSON (ASCII-only) | PIPELINE + SEMANTIC |
Validation checklist
Before submitting your visualization:- Parses correctly with
--output human - Condensed view shows critical information only
- Amounts and addresses are accurate
- Labels are clear to non-technical users
- Test fixture added and passing
Related documentation
- Parser CLI Development - Full CLI reference and examples
- Creating Visualizations - Design patterns for visualizations
- Field Types Reference - Available field types and their usage