Skip to content

feat(solana): add SPL Memo program visualizer preset#360

Draft
kyle-anchorage wants to merge 1 commit into
mainfrom
memo-v2
Draft

feat(solana): add SPL Memo program visualizer preset#360
kyle-anchorage wants to merge 1 commit into
mainfrom
memo-v2

Conversation

@kyle-anchorage

Copy link
Copy Markdown
Contributor

1. Why this PR exists

The SPL Memo program (MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr) is one of the most common instructions in Solana transactions, but the parser had no visualizer for it. A memo instruction fell through to the unknown_program catch-all and rendered as raw hex, so a signer could not read the memo text they were about to sign. The program was already a trusted, canonically-named entry (Memo Program) in builtin_programs.rs, but nothing drove its instruction-body rendering.

No ticket.

2. What changed

  • Added a memo preset (MemoVisualizer) that decodes a memo instruction's data as its UTF-8 text and renders it under VisualizerKind::Payments.
  • Memo is a native, non-Anchor program: its instruction data carries no 8-byte discriminator and no borsh args — the entire data buffer is the memo. So this preset follows the native-program pattern of compute_budget / associated_token_account (direct decode), not the Anchor IDL/discriminator path used by dflow_aggregator and the dApp presets. No JSON IDL file is bundled.
  • Rendering: title Memo; condensed view shows Program: Memo Program plus the memo text; expanded view adds the resolved Program ID and the raw-data hex.
  • Decoding is total: empty data renders (empty memo), non-UTF-8 data renders (non-UTF-8 data; see Raw Data), and the raw bytes stay visible in the Raw Data field in every case.
  • Registered only the v2 program ID (the active one). Memo v1 (Memo1Uhk...) has an identical interface and could be added with a one-line config change.

3. Why this is safe

Backward compatibility. Purely additive. Before, memo instructions rendered via unknown_program; now they get a dedicated, richer rendering. No existing preset, dispatch order, or output changes — build.rs keeps the catch-all unknown_program ordered last, and the new visualizer only claims the memo program ID.

How it was built. Dispatch is driven by SolanaIntegrationConfig: the preset registers the memo program ID, and visualize_with_any selects MemoVisualizer ahead of the catch-all because build.rs orders specific visualizers first. The decode is a single std::str::from_utf8 with explicit fallbacks; there is no unwrap/expect/panic/unsafe (all denied by workspace lints).

Risks mitigated.

  • Malformed or hostile input: invalid UTF-8 and empty data are handled explicitly and never panic; the raw hex is always shown so nothing is hidden from the signer.
  • Spoofing: Memo Program is already a reserved canonical name and the program ID is in the trusted set in builtin_programs.rs, so a caller-supplied IDL cannot impersonate or override it.
  • No existing fixtures render a memo instruction; the memo references in the jupiter/orca/meteora fixtures are account references handled by those programs' own visualizers, so no snapshot output changes.

Checks run (by agent)

  • cargo fmt -p visualsign-solana — clean
  • cargo clippy -p visualsign-solana --all-targets -- -D warnings — clean
  • cargo clippy -p visualsign-solana --features diagnostics --all-targets -- -D warnings — clean
  • cargo test -p visualsign-solana and cargo test -p visualsign-solana --features diagnostics — pass (8 new memo unit tests)
  • make -C src test (full workspace + gRPC integration) — exit 0, 0 failures
  • End-to-end: built a real memo VersionedTransaction and ran it through parser_cli --chain solana --output human; confirmed it dispatches to MemoVisualizer and renders the decoded memo text. The throwaway harness used to mint that transaction was removed before commit.

Manual steps needed (by human)

  • None — fully automated by CI.

4. How this is maintainable

  • The preset is self-describing: a module doc comment explains why Memo uses the native pattern instead of the IDL scaffold, and points at compute_budget / associated_token_account as siblings.
  • Auto-registration: build.rs discovers MemoVisualizer from the directory name, so there is no hand-edited registry — adding or removing a preset is a directory drop-in.
  • Rendering logic is split into a pure render_memo(program_id, data, index) function so it is unit-testable without constructing a VisualizerContext. The 8 tests cover UTF-8/unicode/empty/invalid decode, preview structure, the 1-indexed instruction label, config dispatch, and kind().
  • A developer touching this needs to read only this one module; the field builders and the InstructionVisualizer/SolanaIntegrationConfig contract are identical to every other Solana preset.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant