Skip to content

refactor: unify 0x/0X hex-prefix handling across chains#365

Open
shahan-khatchadourian-anchorage wants to merge 1 commit into
mainfrom
shahankhatch/364-unify-0x-hex-prefix
Open

refactor: unify 0x/0X hex-prefix handling across chains#365
shahan-khatchadourian-anchorage wants to merge 1 commit into
mainfrom
shahankhatch/364-unify-0x-hex-prefix

Conversation

@shahan-khatchadourian-anchorage

Copy link
Copy Markdown
Contributor

Summary

Centralizes 0x/0X hex-prefix acceptance into visualsign::encodings and routes every chain's hex inputs through it, replacing per-crate hand-rolled prefix stripping. Closes #364.

New helpers in visualsign::encodings (the single definition of how the parser accepts a hex prefix):

  • strip_hex_prefix(&str) -> &str — optional 0x/0X, case-insensitive
  • split_hex_prefix(&str) -> Option<&str> — for sites where the prefix is mandatory (returns None to turn into a caller error)
  • decode_hex(&str) -> Result<Vec<u8>, hex::FromHexError> — strip + decode

SupportedEncodings::detect() now treats a leading 0x/0X as hex (the x is stripped before the ASCII-hex-digit test).

Per-chain changes

  • ethereum — ABI signature/public-key decode, canonical pubkey, address validate/normalize, require_hex_prefix, and raw-transaction decode.
  • solana — IDL signature decode, and SolanaTransactionWrapper::from_string hex arm so 0x-prefixed Solana hex transactions decode consistently with the other chains (previously the only chain whose raw-tx path couldn't accept a prefix).
  • sui / tron — raw-transaction decode; drop the now-unused per-crate hex dependency.
  • docs — record the unified hex/0x convention in the root and ethereum CLAUDE.md.

Behavior note

detect() previously classified 0x…-prefixed input as Base64 (the x isn't an ASCII hex digit); it now correctly classifies it as Hex. This makes prefixed hex transactions decode rather than fail.

Test plan

  • cargo fmt — no changes
  • cargo clippy --all-targets -- -D warnings — clean (also enforced by pre-commit across all workspaces)
  • cargo test for visualsign + all four chain crates — pass (648 tests across unit + integration suites)
  • New unit tests in encodings.rs cover prefix presence/absence/case, mandatory-prefix, decode, and the detect() classification.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 11, 2026 19:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR centralizes 0x/0X hex-prefix handling in visualsign::encodings and updates chain parsers (Ethereum/Solana/Sui/Tron) to route hex decoding through the shared helpers, ensuring consistent prefix acceptance and fixing SupportedEncodings::detect() to classify 0x…-prefixed inputs as hex.

Changes:

  • Add shared helpers in visualsign::encodings for optional/required hex prefix handling plus a decode_hex convenience function, with unit tests.
  • Update chain-specific hex decoding paths to use visualsign::encodings::decode_hex (and split_hex_prefix where prefix is required).
  • Promote hex to a non-dev dependency of visualsign and remove now-unneeded per-crate hex dependency usage (e.g., Sui), with corresponding docs updates.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/visualsign/src/encodings.rs Adds shared prefix helpers, updates detect() to account for 0x/0X, and adds unit tests.
src/visualsign/Cargo.toml Promotes hex to a normal dependency (workspace-managed).
src/chain_parsers/visualsign-tron/src/lib.rs Uses visualsign::encodings::decode_hex for hex transaction decoding.
src/chain_parsers/visualsign-sui/src/core/transaction/decoder.rs Uses shared hex decoding to accept optional 0x/0X prefixes.
src/chain_parsers/visualsign-sui/Cargo.toml Drops direct hex dependency (now inherited via visualsign).
src/chain_parsers/visualsign-solana/src/idl/signature.rs Delegates fixed-size hex decode to visualsign::encodings::decode_hex.
src/chain_parsers/visualsign-solana/src/core/visualsign.rs Uses shared hex decode for Solana transaction wrapper hex path.
src/chain_parsers/visualsign-ethereum/src/lib.rs Uses shared hex decode for raw transaction hex decoding.
src/chain_parsers/visualsign-ethereum/src/eth_json.rs Uses split_hex_prefix for mandatory-prefix JSON-RPC hex fields.
src/chain_parsers/visualsign-ethereum/src/cli_plugin.rs Uses shared prefix helpers for address validation/normalization.
src/chain_parsers/visualsign-ethereum/src/abi_metadata.rs Uses shared hex decode for signature/public-key and canonical pubkey decoding.
src/chain_parsers/visualsign-ethereum/CLAUDE.md Documents the unified hex/prefix convention for Ethereum module.
src/Cargo.lock Reflects dependency graph changes (e.g., Sui no longer directly depends on hex).
CLAUDE.md Documents repo-wide unified hex/0x handling guidance.

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

Add `decode_hex`, `strip_hex_prefix`, and `split_hex_prefix` to
`visualsign::encodings` as the single definition of how the parser accepts
a hex prefix, and route every chain's hex inputs through them instead of
hand-rolling prefix stripping per crate.

- visualsign::encodings: add the three helpers; `detect()` now treats a
  leading `0x`/`0X` as hex (the `x` is stripped before the digit test).
- ethereum: ABI signature/public-key decode, address validate/normalize,
  `require_hex_prefix`, and raw-transaction decode.
- solana: IDL signature decode and `SolanaTransactionWrapper::from_string`
  hex arm, so prefixed Solana hex transactions decode consistently with the
  other chains.
- sui/tron: raw-transaction decode; drop the now-unused per-crate `hex` dep.
- docs: record the unified hex/`0x` convention in the CLAUDE.md files.

Closes #364

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shahan-khatchadourian-anchorage shahan-khatchadourian-anchorage force-pushed the shahankhatch/364-unify-0x-hex-prefix branch from 3a90c33 to 9c36529 Compare June 11, 2026 21:12

@prasanna-anchorage prasanna-anchorage left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Clean, well-scoped de-duplication — collapsing the copy-pasted 0x/0X stripping into three tested helpers is the right move, and it incidentally fixes Solana's prefixed-hex path and the 0X gap in Ethereum/Tron from_string. Approving. A few nits inline, plus two non-code notes:

  • PR description nit: it says "sui / tron — drop the now-unused per-crate hex dependency," but tron correctly keeps hex (still uses hex::encode at lib.rs:164/169/424). Only sui dropped it. Code is right; just fix the description.
  • Consistency nit: visualsign-tron/Cargo.toml:17 still pins hex = "0.4.3" while solana was switched to { workspace = true } — worth aligning tron too while touching deps.

/// the test).
pub fn detect(data: &str) -> Self {
if data.chars().all(|c| c.is_ascii_hexdigit()) {
if strip_hex_prefix(data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: this PR makes detect() the sole router (the old Ethereum/Tron code force-routed any 0x prefix to Hex unconditionally), which slightly widens the heuristic's ambiguity: a Base64 string that starts with 0x/0X and whose remainder is all hex digits of even length now classifies as Hex. It's a pre-existing property (an all-hex Base64 string was already mis-detected), negligible for real ~1KB txs, and the consequence is a downstream decode error rather than a silent mis-parse. Worth a one-line note in the doc comment that classification is best-effort, since this is now the single decision point.

SupportedEncodings::Hex => {
hex::decode(data).map_err(|e| TransactionParseError::DecodeError(e.to_string()))?
}
SupportedEncodings::Hex => visualsign::encodings::decode_hex(data)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: the headline behavioral win here is chain-level — a 0x-prefixed Solana tx now decoding where it previously failed — but the new tests live only in encodings.rs at the helper level. Consider one end-to-end assertion (SolanaTransactionWrapper::from_string with a 0x prefix) so a future detect() refactor can't silently re-break this path.

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.

Unify 0x hex-prefix handling across chain parsers

3 participants