Skip to content

Add Exponent Finance Solana visualizer preset#275

Merged
prasanna-anchorage merged 1 commit into
mainfrom
exponent
May 13, 2026
Merged

Add Exponent Finance Solana visualizer preset#275
prasanna-anchorage merged 1 commit into
mainfrom
exponent

Conversation

@kyle-anchorage
Copy link
Copy Markdown
Contributor

@kyle-anchorage kyle-anchorage commented May 1, 2026

Why this PR exists

Solana transactions calling the Exponent Finance core program (ExponentnaRg3CQbW6dqQNZKXp7gtZ9DGMp1cwC4HAS7) currently fall through to the generic unknown_program visualizer, so wallet sign prompts show only the program ID and raw hex instruction data. This adds a dedicated preset that surfaces the instruction name, named accounts, and decoded args.

What changed

  • New preset crate module presets::exponent_finance registered under visualsign-solana.
  • IDL JSON (exponent_finance.json) sourced from the published @exponent-labs/exponent-idl npm package, which is the same data the Exponent SDK and orbmarkets explorer ship.
  • Standard generic-IDL scaffolding: decode_idl_data -> parse_instruction_with_idl -> build_named_accounts -> field builders, mirroring the dflow_aggregator preset.
  • Visualizer kind: Dex ("Exponent Finance"). Config matches all instructions for the program ID via ("*", vec!["*"]).
image

Two adaptations relative to the standard Anchor-style scaffold:

  • Exponent uses Anchor 0.31's #[instruction(discriminator = [N])] attribute, so on-chain instruction discriminators are 1 byte rather than 8. The minimum-data-length check is data.is_empty() and the idl_has_discriminators test only asserts !disc.is_empty(). The underlying solana_parser::find_instruction_by_discriminator already supports variable-length discriminators.
  • The IDL's Number type is a tuple-struct (a single anonymous [u64; 4] field). solana_parser's IdlField schema requires named fields, so that one entry was rewritten in the JSON to {"name": "value", "type": {"array": ["u64", 4]}}. No other types or instructions were modified.

Why this is safe

  • Read-only feature: the parser only reads transaction bytes and produces display fields. No signing, custody, or settlement code is touched.
  • Backward compatible: adds a new preset module and a single registration line in presets/mod.rs. Existing presets are unaffected. Programs that don't match the Exponent program ID continue to dispatch to their existing visualizer.
  • Failure mode is graceful: if a future Exponent instruction has an unrecognized 1-byte discriminator, parse_exponent_finance_instruction returns Err, and the visualizer falls through to build_fallback_fields (which still emits the raw hex). This matches the dflow_aggregator pattern.
  • The Number IDL patch is a structural rename only; the underlying type expression {"array": ["u64", 4]} is preserved, so any instruction that references Number will still decode the same bytes.

Checks run (by agent)

  • cargo fmt -p visualsign-solana
  • cargo clippy -p visualsign-solana --all-targets -- -D warnings -> clean
  • cargo test -p visualsign-solana -> 70 unit tests pass (including the 4 new exponent_finance tests: idl_loads, idl_has_discriminators, unknown_discriminator_returns_error, short_data_returns_error)
  • make -C src test -> all workspace tests pass
  • make -C src lint -> clean across all crates

Manual steps needed (by human)

None - fully automated by CI.

How this is maintainable

  • The preset follows the same shape as dflow_aggregator, so a developer touching either picks up the pattern from one place. The two deviations (1-byte discriminator handling and the Number-type rewrite) are documented inline by the test names and at the top of this PR.
  • build.rs auto-discovers ExponentFinanceVisualizer from the new directory, so adding/removing presets doesn't require touching a registry.
  • The IDL JSON is sourced from the upstream @exponent-labs/exponent-idl package; refreshing the IDL is a copy-paste of a new release plus re-applying the Number-field rewrite (the test test_exponent_finance_idl_loads will fail loudly if the rewrite is missed).
  • Workspace lint policy (unwrap_used = "deny", etc.) is enforced - the preset uses ? and ok_or/ok_or_else for all fallible paths.
  • The 4 sanity tests guard the discriminator/IDL contract: any future change that breaks IDL load, drops a discriminator, or stops rejecting empty/garbage input will surface in CI.

@kyle-anchorage kyle-anchorage marked this pull request as ready for review May 7, 2026 21:17
Copilot AI review requested due to automatic review settings May 7, 2026 21:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a dedicated Solana visualizer preset for the Exponent Finance core program so transactions no longer fall back to the generic unknown_program rendering and instead show decoded instruction names, named accounts, and decoded arguments using the bundled IDL.

Changes:

  • Registered a new presets::exponent_finance module under visualsign-solana.
  • Implemented an IDL-driven instruction parser/visualizer (matching the existing generic-IDL scaffold pattern used by other presets).
  • Added the Exponent Finance IDL JSON (with the documented Number struct field naming adaptation) and a program config mapping for dispatch.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
src/chain_parsers/visualsign-solana/src/presets/mod.rs Registers the new exponent_finance preset module.
src/chain_parsers/visualsign-solana/src/presets/exponent_finance/mod.rs Implements the Exponent Finance IDL-backed visualizer, parsing, field building, and unit tests.
src/chain_parsers/visualsign-solana/src/presets/exponent_finance/config.rs Adds Solana integration config mapping the Exponent program ID to all instructions.
src/chain_parsers/visualsign-solana/src/presets/exponent_finance/exponent_finance.json Bundles the Exponent Finance IDL used for decoding instruction discriminators/args/accounts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@prasanna-anchorage prasanna-anchorage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Rebased onto current main — the diff was reverting #284's pub mod kamino_limit; registration because the PR predates that merge. Resolved the presets/mod.rs conflict by keeping both exponent_finance and kamino_limit in alphabetical order.

Verification:

  • cargo clippy -p visualsign-solana --all-targets -- -D warnings: clean.
  • 4 exponent_finance preset tests pass (test_exponent_finance_idl_loads, test_exponent_finance_idl_has_discriminators, test_unknown_discriminator_returns_error, test_short_data_returns_error).
  • Same preset template as the 11 other Kyle-authored Solana presets already on main.

config.rs uses HashMap per the current convention — #288's sweep will flip it to BTreeMap when that PR lands.

@prasanna-anchorage prasanna-anchorage merged commit 9eed8a9 into main May 13, 2026
7 checks passed
@prasanna-anchorage prasanna-anchorage deleted the exponent branch May 13, 2026 05:00
prasanna-anchorage added a commit that referenced this pull request May 13, 2026
#297)

PR #288 forbade std::collections::HashMap/HashSet via clippy
disallowed-types and converted the existing preset configs to BTreeMap,
but missed the two presets that landed during the same review window:

- exponent_finance (PR #275, merged 2026-05-12 22:00 UTC)
- kamino_limit    (PR #284, merged 2026-05-11 14:50 UTC)

Both still constructed HashMap and passed it where SolanaIntegrationConfigData
now expects BTreeMap, so `cargo build -p visualsign-solana` fails on
post-#288 main.

Verified locally: `cargo build -p visualsign-solana` and
`cargo clippy -p visualsign-solana --all-targets -- -D warnings` are
clean; `cargo fmt --check` passes.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shahan-khatchadourian-anchorage added a commit that referenced this pull request May 13, 2026
Merges origin/main (3bbf4e7) into the PR #255 branch. Resolves three
content conflicts and ports two new presets from main to the wire-data
VisualizerContext API introduced by PR #255.

Conflicts resolved:
- .gitignore: keep both `docs/superpowers/` (PR #255) and `.surfpool/`
  (main, from #294).
- src/chain_parsers/visualsign-solana/src/presets/dflow_aggregator/mod.rs:
  take main's #286 changes (remaining-accounts surfacing, nested-arg
  flattening, `mut expanded_fields` + warn-on-parse-fail) on top of
  PR #255's wire-data API. The raw-data field is now pushed in place via
  `expanded_fields.push(create_raw_data_field(data, ...))` using
  `context.data()` instead of the removed `instruction.data`.
- src/chain_parsers/visualsign-solana/src/presets/unknown_program/mod.rs:
  combine main's #288 determinism fix (locally-built `BTreeMap<String,
  String>` returned alongside the parsed payload, iterated at render time
  instead of `parsed.named_accounts`'s upstream `HashMap`) with PR #255's
  wire-data API (`context.program_id()`, `context.data()`,
  `context.num_accounts()`, `resolve_account_str(context, i)`). Imports
  remain `BTreeMap`; `HashMap` is no longer referenced.

Two new presets ported to the wire-data API (same pattern as the existing
`80b076b` port commit):
- exponent_finance (from main #275): `current_instruction()` /
  `instruction.{program_id,data,accounts}` replaced by
  `context.resolve_program_id()?`, `context.resolve_accounts()?`,
  `context.data()`. Inline `expanded_fields.push(create_raw_data_field(...))`
  pattern; the unused `append_raw_data` helper deleted.
- kamino_limit (from main #284): same API port. Function helpers
  (`build_named_accounts`, `build_parsed_fields`, `build_fallback_fields`,
  `append_raw_data`) retained -- they take `data: &[u8]` and
  `accounts: &[AccountMeta]` and remain useful.

Both new presets' `config.rs` switched from `std::collections::HashMap` to
`std::collections::BTreeMap` to satisfy main's #288 disallowed-types lint
and the `SolanaIntegrationConfigData.programs: BTreeMap<...>` field shape.

Verified:
- cargo fmt clean
- cargo check --workspace --exclude parser_cli clean
- cargo check -p parser_cli clean
- cargo test -p visualsign-solana --lib: 148 passed, 0 failed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

3 participants