Skip to content

refactor(solana): single definition for account and program display strings - #438

Open
shahan-khatchadourian-anchorage wants to merge 1 commit into
worktree-434-fmt-preset-treefrom
worktree-435-shared-account-resolution
Open

refactor(solana): single definition for account and program display strings#438
shahan-khatchadourian-anchorage wants to merge 1 commit into
worktree-434-fmt-preset-treefrom
worktree-435-shared-account-resolution

Conversation

@shahan-khatchadourian-anchorage

Copy link
Copy Markdown
Contributor

Summary

Closes #435. Stacked on #436 (worktree-434-fmt-preset-tree).

core::InstructionView owns the placeholder vocabulary for resolving account and program indices to display strings. Three presets hand-roll the same three-arm match instead of using it, and all three diverge on the out-of-bounds arm -- rendering unknown where the canonical form is unresolved(oob:N):

Location Was
presets/jupiter_swap/mod.rs:94 None => "unknown".to_string()
presets/system/mod.rs:123 None => "unknown".to_string()
presets/system/mod.rs:128 None => "unknown".to_string()
presets/unknown_program/mod.rs:67 None => "unknown".to_string()

unknown carries no index, so it is strictly less diagnosable than the canonical form, and a signer comparing two transactions sees different vocabulary for the same condition.

Approach

Extract the two resolution rules in core and build InstructionView on top of them, so there is one definition rather than one-per-caller:

pub fn resolve_program_display(context: &VisualizerContext) -> String
pub fn resolve_account_display(context: &VisualizerContext, position: usize) -> String

Then route each preset through the shape that fits how it reads accounts:

  • jupiter_swap needs every account, so it takes InstructionView::from_context(context).accounts -- the closure disappears entirely.
  • system names two fixed positions, so it calls resolve_account_display(context, 1) / (context, 0).
  • unknown_program drops both of its local copies (resolve_account_str and resolve_program_id_str -- the latter duplicated the program arm the same way) and calls the shared functions.

Behavior

No output changes for in-bounds indices, which is every index a well-formed instruction produces. The None arm is reachable only when a preset names a fixed position and the instruction carries fewer accounts than the layout expects; that case now renders unresolved(oob:N) instead of unknown.

Test plan

  • cargo test -p visualsign-solana -- 270 passed, 0 failed (269 before; +1 new)
  • make -C src lint -- clippy clean with -D warnings
  • New test test_resolve_account_display_matches_instruction_view asserts position-addressed resolution agrees with InstructionView for both resolved and unresolved indices, and covers the out-of-bounds arm that nothing exercised before
  • After this change, grep '=> "unknown"' across the crate leaves only the two swig_wallet enum-name helpers and spl_token (see below)

Scope

Two "unknown" sites are deliberately untouched:

  • presets/swig_wallet/mod.rs:2037,2047 -- program_scope_type_name / numeric_type_name map unrecognized discriminants to a name. Unrelated to account resolution.
  • presets/spl_token/mod.rs -- carries the same stale arm inside its own private InstructionView, which PR fix(solana/spl_token): migrate to shared core::InstructionView, drop data clone #381 removes. This branch does not touch that file, so the two do not conflict and can merge in either order. Once both land, core/mod.rs holds the only copy of the resolution logic in the crate.

Stacking

Based on #436 rather than main: #436 makes the CI fmt gate cover presets/*/, so the preset edits here are format-checked. There is no file overlap between the two branches.

🤖 Generated with Claude Code

…trings

Three presets hand-roll the same three-arm match that
`core::InstructionView` already performs, and each diverges on the
out-of-bounds arm: they render `unknown` where the canonical form is
`unresolved(oob:N)`. The duplicated logic is what let them drift.

Extract `resolve_program_display` and `resolve_account_display` in
`core`, have `InstructionView::from_context` build on them, and route the
three presets through them:

- `jupiter_swap` needs every account, so it takes
  `InstructionView::from_context(context).accounts`.
- `system` names two fixed positions, so it calls
  `resolve_account_display` directly.
- `unknown_program` drops both of its local copies
  (`resolve_account_str`, `resolve_program_id_str`).

`unknown` disappears from account resolution and the placeholder
vocabulary has one definition. No output changes for in-bounds indices,
which is every index a well-formed instruction produces.

Cover the previously-untested out-of-bounds arm, asserting that
position-addressed resolution agrees with `InstructionView`.

Closes #435
@shahan-khatchadourian-anchorage shahan-khatchadourian-anchorage added chain:solana test:proptest Run the Solana property-test suite on this PR test:surfpool Run the Solana surfpool mainnet-fork suite on this PR CI labels Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chain:solana CI test:proptest Run the Solana property-test suite on this PR test:surfpool Run the Solana surfpool mainnet-fork suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant