What is VisualSign parser?
VisualSign Parser is a platform for decoding various blockchains in Rust. It is deployed as a single binary and works with potentially conflicting dependencies from different blockchain ecosystems. The project is split into packages with thevisualsign package serving as a common “core” that chain_parsers use.
Core principles
Minimize dependencies aggressively
Every dependency introduces supply chain risk and increases build times. While this can be mitigated by splitting deployments into different binaries, the fundamental problem remains. Strategies:- Remove dependencies when possible
- Replicate simpler functionalities rather than importing heavy libraries
- For development and test builds, you can be more liberal with
[dev-dependencies]
Pin and validate every dependency
Every dependency is strictly pinned and validated before it is added or bumped. This covers crates, GitHub Actions, container images, downloaded binaries and the tools CI ormake runs. A dependency that is not pinned can change under a reviewed commit. A dependency that is not validated is code nobody looked at.
Pin exactly. A pin names one artifact, and CI and local runs use the same one.
Not allowed:
latestor any floating tag.- Version ranges outside
Cargo.tomlsemver requirements. curl … | shinstallers.- Falling back to whatever tool is on
PATHfor a check CI also runs. A local tool can be used only through an explicit opt-in, as withmake lint-shell’sSHELLCHECK=shellcheck.
- Need. The dependency passes Minimize dependencies aggressively. A bump has a reason: a fix, an advisory or a feature we use.
- Age. The pinned release is at least 7 days old and not yanked. The one exception is the minimum version that fixes a published advisory.
- Provenance. The publisher is the project’s usual owner. For crates,
.cargo_vcs_info.jsonmatches an upstream tag. For images and binaries, the digest or hash comes from the upstream release, not from a mirror. - Advisories.
cargo auditreports no new advisories. A known advisory that remains is named in the PR with its follow-up. - New code. Every new transitive crate is accounted for with
cargo tree -i <crate>, and build scripts and proc-macros are read, because they run at build time. - Build. Every affected workspace builds and tests with
--locked, including ones CI doesn’t cover on that PR (for exampletools/tvc-deploy).
Test thoroughly
Ensure all changes and refactors are tested thoroughly. Usecargo-llvm-cov for test coverage:
target/llvm-cov/html/. Use coverage to identify untested code paths and prioritize adding test fixtures for uncovered instruction types.
Respect blockchain conventions
Our experience with dozens of blockchains has shown there’s no one-size-fits-all approach to encoding. Instead of forcing a particular default everywhere:- Make pragmatic defaults that don’t conflict
- Let blockchains set conventions that make sense for them
- Keep
visualsigncore lean and minimally opinionated
- Ethereum (RLP and hex)
- Solana (Base58 or Base64)
- Future chains with different encodings
Project structure
Monolithic binary with workspace
We deploy a monolithic binary with many differentchain_parsers that implement chain-specific dependencies. While Rust is more forgiving than languages like Go, overlapping dependencies can still cause conflicts.
Dependency guidelines
-
Don’t add blockchain-specific dependencies to the
parsercrate. These belong in thechain_parserscrates for:- Locality of context
- Clear dependency management
-
Evaluate necessity. Is the library truly essential, or can it be replicated and imported as a
dev-dependency? - Integration tests are required. Every chain should have at least one high-level integration test that validates baseline functionality (e.g., decoding a native token transfer).
- Minimize features. Keep dependencies to a minimum and only enable the features you need. Avoid feature bloat.
Code organization
We’ve found that putting everything inlib.rs becomes unwieldy quickly. General guidelines:
- Layer dependencies and context - solve for basic constructs first
- Create clearly named modules - split into files or directories when files exceed ~2000 lines
- General to specific - organize from most general to most specific implementations
Backwards compatibility
Once the toolchain is fully integrated into production wallets, any output changes must be thoroughly examined. Display values, ordering, and formatting should remain stable between versions.Writing good tests
Test coverage within chains is a great model to aim for. Build incrementally:- Start with basic transaction parsing tests
- Add tests for each transaction type
- Include edge cases and error conditions
- Validate against real-world transaction data
Related documentation
- Contributing Overview - PR workflow, code standards, and build instructions
- Project Structure - Architecture and dependency guidelines
- Adding a New Chain - Step-by-step chain integration guide