Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Keep the image tiny — only the Containerfile is needed at build time;
# the docs themselves are bind-mounted at runtime.
*
!Containerfile
11 changes: 10 additions & 1 deletion docs/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,16 @@ npx puppeteer browsers install chrome-headless-shell
cd /tmp && npm install puppeteer
```

The Mintlify dev server must be running on port 3000. Use the screenshot script to capture pages:
The Mintlify dev server must be running on port 3000. The simplest way is the containerized CLI bundled with this directory:

```bash
make -C docs dev # builds the image if needed, serves at :3000
make -C docs broken-links
```

Set `ENGINE=podman` if you don't have Docker. To install the CLI on the host instead, see [Mintlify CLI installation](https://www.mintlify.com/docs/installation) — the npm package is `mint` (run `npm i -g mint`, then `mint dev`).

Use the screenshot script to capture pages once the server is up:

```bash
# Usage: ./scripts/screenshot.js <page-path> [output-file] [scroll-offset]
Expand Down
22 changes: 22 additions & 0 deletions docs/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Containerized Mintlify CLI for local docs validation.
#
# Build: make -C docs build-image
# Serve: make -C docs dev # http://localhost:3000
#
# The container only carries the CLI. The actual docs are bind-mounted from
# this directory at runtime, so edits show up live in the dev server.

FROM node:20-alpine

# `mint` shells out to git for some operations; install it.
RUN apk add --no-cache git

# Install the Mintlify CLI globally. Package name is `mint` (not `mintlify`).
# Bump deliberately by changing the @<version> pin.
RUN npm install -g mint@latest

WORKDIR /docs

EXPOSE 3000

CMD ["mint", "dev", "--port", "3000"]
38 changes: 38 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Docs dev tooling — Mintlify in a container.
#
# `make dev` is the common case: builds the image (if needed) and starts
# the local dev server with this directory bind-mounted, so edits show
# up live.

IMAGE_NAME ?= visualsign-mintlify
PORT ?= 3000
ENGINE ?= docker

.PHONY: build-image dev broken-links shell rss

build-image:
$(ENGINE) build -t $(IMAGE_NAME) -f Containerfile .

dev: build-image
$(ENGINE) run --rm -it \
-p $(PORT):3000 \
-v $(CURDIR):/docs \
$(IMAGE_NAME)

broken-links: build-image
$(ENGINE) run --rm \
-v $(CURDIR):/docs \
$(IMAGE_NAME) mint broken-links

# Open an interactive shell in the container for ad-hoc CLI work.
shell: build-image
$(ENGINE) run --rm -it \
-v $(CURDIR):/docs \
--entrypoint sh \
$(IMAGE_NAME)

# Fetch the auto-generated RSS feeds from a running dev server (port $(PORT)).
# Run `make dev` in another terminal first.
rss:
@echo "Changelog feed:" && curl -sf http://localhost:$(PORT)/changelog/rss.xml | head -40
@echo "Blog feed:" && curl -sf http://localhost:$(PORT)/blog/rss.xml | head -40
10 changes: 10 additions & 0 deletions docs/blog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: "Blog"
description: "Concept exploration, design pieces, and deeper dives from the VisualSign team."
---

Subscribe via RSS at [`/blog/rss.xml`](/blog/rss.xml). Posts are tagged by chain and theme. Each entry below is a stub — click through for the full essay.

<Update label="2026-05-13 — Composable by design: how Solana presets stack" tags={["Solana", "Architecture", "Contributors"]}>
Adding a new protocol to VisualSign rarely touches existing code. The recent Solana preset wave is a useful proof point: 14 protocols shipped without changes to existing decoders or the core parser. This post walks through the three composition primitives — the built-in IDL parser, protocol-specific preset overlays, and layered registries — and why each preset is a pure addition rather than a modification. [Read the full post](/blog/2026-05-13-composable-solana-presets).
</Update>
61 changes: 61 additions & 0 deletions docs/blog/2026-05-13-composable-solana-presets.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: "Composable by design: how Solana presets stack"
description: "Adding a new protocol to VisualSign rarely touches existing code. Here's how the IDL parser, preset overlays, and a build-time registry compose into one signable payload."
---

In the last few weeks, VisualSign gained decoding for Jupiter (Borrow, Earn, Perpetuals, v6 route_v2), Kamino (Borrow, Vault, Farms, Limit Orders), Meteora (DLMM, DAMM V2), MetaDAO (Futarchy, Conditional Vault), Orca Whirlpool, Onre App, Neutral Trade, DFlow Aggregator, and Exponent Finance. Fourteen protocols. Each PR added a single directory under `src/chain_parsers/visualsign-solana/src/presets/` and a handful of fixtures. None of them changed how any of the other presets decode, and none of them touched the core parser.

That's not luck. It's the consequence of a small, deliberate set of composition primitives.

## The three pieces

VisualSign's Solana decoder is built from three layers you can think about independently:

1. **A built-in IDL parser.** Any Anchor-style program with an IDL gets a typed view of each instruction — discriminator, account list, argument values — produced generically from the IDL alone. This is the `src/idl/` module. It does not know about Jupiter or Kamino.

2. **Protocol-specific preset overlays.** A preset is a small unit that says "for *this* program id, take the typed view and produce these human-readable fields." Presets implement `InstructionVisualizer` and declare which program id they handle via `SolanaIntegrationConfig`. They never parse bytes; they consume the IDL view and call field builders (`create_text_field`, `create_amount_field`, `create_raw_data_field`) to shape output. Each preset lives in its own directory.

3. **A build-time registry.** `build.rs` scans `src/presets/` and emits a generated `available_visualizers()` function — one `Box::new(<Dir>::<Pascal>Visualizer)` per directory. The build comment in the script puts the design intent plainly: "this should allow us to functionally compose instructions at the time of display." There is no central registration site to edit. Adding a directory is the registration.

Field builders sit underneath all three layers. They enforce shape and determinism, so two presets emitting the same logical content produce byte-identical output. That property is what makes the composed payload safe to hash.

## What happens when a transaction lands

A Solana transaction is, in VisualSign's view, a list of instructions. Decoding is a fold:

```text
for each instruction in the transaction:
visualize_with_any(available_visualizers(), context)
-> first preset whose can_handle() returns true
-> preset emits an AnnotatedPayloadField
collect fields -> SignablePayload
```

`available_visualizers()` is the generated slice. `unknown_program` always sorts last, so it catches anything no specific preset claimed. A preset registered for Jupiter's program id has zero knowledge of MetaDAO's preset, and vice versa. The dispatch lookup is the only coupling.

This is why the preset wave required no changes to the core. Each new directory added:

- The IDL JSON (or accepted the built-in IDL path).
- One preset `mod.rs` implementing `InstructionVisualizer`.
- One `config.rs` implementing `SolanaIntegrationConfig` with the program id.
- Fixture tests under `tests/fixtures/`.

Nothing else moved. Reviewers read each preset PR in isolation. CI sees no ripple.

## What composability buys you

If you're **adding a new Solana protocol**, the design pushes you toward a particular shape: one IDL, one visualizer, one config, a handful of fixtures, one new directory. The same shape your reviewer expects, the same shape we expect from ourselves. The `solana-add-idl` skill scaffolds this for you when the protocol exposes an on-chain IDL.

If you're **integrating VisualSign into a wallet**, composability shows up at the data layer too. The Ethereum side uses `LayeredRegistry<T>` to compose wallet-provided overrides on top of compiled-in defaults — wallets can ship their own opinions about token metadata without forking VisualSign. The Solana side currently composes through fixed IDL + preset layers; wallet-side overlays are the natural next step.

If you're **building a policy engine or signing pipeline on top of VisualSign**, the chain metadata + content digests change in the HTTP gateway (PR #287) gives you a stable input contract. The gateway forwards `chain_metadata` end-to-end and exposes payload digests on every response. Your policy logic composes onto our parser output without re-parsing or re-decoding.

If you're **writing tests**, surfpool (PR #202) composes real Solana mainnet state into the local harness. Tests run against forked chain data, which catches drift between IDL assumptions and on-chain reality without a live cluster.

## The seams are the product

"Composable by design" is a phrase. The work is in keeping the seams clean as surface area grows. Build-time discovery of presets means there's no list to drift out of sync. Field builders mean two contributors emitting the same content produce identical bytes. A typed IDL view between the raw instruction and the preset means presets only see well-shaped input.

The preset wave is a useful proof point. The more interesting test will be the next chain we add — and watching whether the same primitives carry across.

To start adding a Solana preset, see [Adding a Solana preset](/chains/solana). To see what shipped recently, see the [Changelog](/changelog).
41 changes: 41 additions & 0 deletions docs/changelog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: "Changelog"
description: "Curated release notes for VisualSign — one stream per chain plus a core stream, versioned by release."
---

Pick the stream that matters to you. Each stream has its own RSS feed.

<CardGroup cols={2}>
<Card title="Solana" icon="circle-nodes" href="/changelog/solana">
Solana preset coverage, IDL handling, decoder fixes. RSS at [`/changelog/solana/rss.xml`](/changelog/solana/rss.xml).
</Card>
<Card title="Ethereum" icon="ethereum" href="/changelog/ethereum">
EVM protocol decoders, ABI registry changes, contract additions. RSS at [`/changelog/ethereum/rss.xml`](/changelog/ethereum/rss.xml).
</Card>
<Card title="Sui" icon="droplet" href="/changelog/sui">
Sui Move package decoders, programmable transactions. RSS at [`/changelog/sui/rss.xml`](/changelog/sui/rss.xml).
</Card>
<Card title="Tron" icon="bolt" href="/changelog/tron">
Tron contract decoders, TRC standards. RSS at [`/changelog/tron/rss.xml`](/changelog/tron/rss.xml).
</Card>
<Card title="Core" icon="cube" href="/changelog/core">
Core types, field builders, HTTP/gRPC API, attestation, policy, build and test infrastructure. RSS at [`/changelog/core/rss.xml`](/changelog/core/rss.xml).
</Card>
</CardGroup>

## How releases are tagged

Tags are auto-cut from `main` by the release workflow. The `description` on each `<Update>` block points to the first release that contains the change. If you pull a tag at or above that version, the change is in.

## How entries are authored

Contributors drop a draft fragment in `docs/changelog/_drafts/` on the PR that ships the change. A maintainer batches drafts into one or more `<Update>` blocks at curation time, splits them across the per-chain and core pages by category, and deletes the consumed drafts. See [`docs/changelog/_drafts/README.md`](https://github.com/anchorageoss/visualsign-parser/blob/main/docs/changelog/_drafts/README.md) for the format.

## Tag vocabulary

All streams use a shared controlled tag vocabulary so RSS subscribers can filter:

- **Audience** — `Wallet API`, `Contributors`, `Security`, `Ecosystem`.
- **Theme** — `Policy`, `Architecture`, `Lints`, `Attestation`, `Performance`.

Chain pages do not use a chain tag (the page already conveys the chain).
62 changes: 62 additions & 0 deletions docs/changelog/_drafts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Changelog drafts

PR authors of user-facing changes drop a fragment here. A maintainer batches drafts into one or more `<Update>` blocks on `../changelog.mdx` when a meaningful release ships, then deletes the consumed drafts.

This directory is intentionally not listed in `docs.json` navigation, so Mintlify does not publish it.

## Fragment format

Name the file `YYYY-MM-DD-<pr-slug>.md`. Include YAML frontmatter and a short prose body.

Example (`2026-05-05-metadao-futarchy.md`):

```markdown
---
category: solana # one of: solana | ethereum | sui | tron | core
label: "Added MetaDAO Futarchy preset"
description: "" # filled in at curation time with the release version (e.g. "v0.646.0")
tags: ["Wallet API"]
---
Solana governance transactions now decode conditional vault and market
interactions. See [Solana presets](/chains/solana).
```

The maintainer reads the frontmatter into the corresponding props on `<Update>`, lifts the body into the block, and writes it to the matching page (`../solana.mdx`, `../ethereum.mdx`, `../sui.mdx`, `../tron.mdx`, or `../core.mdx`). `description` is filled in with the first release tag that contains the change — look up via `git tag --contains <sha> --sort=v:refname | head -1`.

## Category choice

- **`solana`** — anything under `src/chain_parsers/visualsign-solana/`: presets, IDL handling, decoder fixes, fixture coverage.
- **`ethereum`** — anything under `src/chain_parsers/visualsign-ethereum/`: protocol decoders, ABI registry, contract additions.
- **`sui`** — anything under `src/chain_parsers/visualsign-sui/`: Move package decoders, programmable transactions.
- **`tron`** — anything under `src/chain_parsers/visualsign-tron/`: contract decoders, TRC standards.
- **`core`** — everything else: core types in `src/visualsign`, field builders, the parser binaries (`src/parser/{cli,app,grpc-server}`), the HTTP gateway, attestation, policy, codegen, integration tests, build infrastructure.

Don't put a chain tag in the `tags` array for per-chain entries — the page already conveys the chain. Reserve tags for audience and theme.

If a change spans multiple categories (rare — e.g., a core change that requires per-chain updates), drop one fragment per affected category.

## Controlled tag vocabulary

Use tags from the shared vocabulary so subscribers can filter:

- **Audience** — `Wallet API`, `Contributors`, `Security`, `Ecosystem`.
- **Chain** — `Ethereum`, `Solana`, `Sui`, `Tron`.
- **Theme** — `Policy`, `Architecture`, `Lints`, `Attestation`, `Performance`.

A single fragment usually has one audience tag, one chain tag (if applicable), and zero or one theme tag.

## What counts as user-facing

Drop a draft when the change is one of:

- New chain or preset coverage.
- A change to VisualSign payload shape, field names, or determinism guarantees.
- A new or changed CLI, gRPC, or HTTP surface.
- A security-relevant change (TEE, attestation, policy).
- A breaking change to wallet integration.

Skip drafts for internal refactors, test changes, dependency bumps, and CI plumbing.

## Why this workflow

Authoring the entry close to the change keeps facts accurate. Curating at release time keeps the published changelog readable. Maintainers do not have to mine `git log` for context that has already faded.
10 changes: 10 additions & 0 deletions docs/changelog/core.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: "Changelog — Core"
description: "Core types, field builders, HTTP/gRPC API, attestation, policy, and build/test infrastructure. One entry per meaningful release."
---

Subscribe via RSS at [`/changelog/core/rss.xml`](/changelog/core/rss.xml). Filter by tag — `Wallet API`, `Security`, `Architecture`, `Policy`.

<Update label="2026-05-12 — Chain metadata and content digests in HTTP gateway" description="v0.648.0" tags={["Wallet API"]}>
The HTTP gateway now forwards `chain_metadata` end-to-end and exposes content digests on every response. Downstream tools — policy engines, signing pipelines, audit logs — can compose VisualSign output into their own workflows without re-parsing the transaction.
</Update>
8 changes: 8 additions & 0 deletions docs/changelog/ethereum.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "Changelog — Ethereum"
description: "Ethereum protocol decoders, ABI registry changes, EVM-specific contract additions, and decoder fixes. One entry per meaningful release."
---

Subscribe via RSS at [`/changelog/ethereum/rss.xml`](/changelog/ethereum/rss.xml). The feed populates as new entries land.

No entries yet. Recent Ethereum work is tracked in [GitHub commits under `src/chain_parsers/visualsign-ethereum/`](https://github.com/anchorageoss/visualsign-parser/commits/main/src/chain_parsers/visualsign-ethereum); curated entries appear here as part of the next meaningful release.
16 changes: 16 additions & 0 deletions docs/changelog/solana.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
title: "Changelog — Solana"
description: "New Solana preset coverage, IDL handling changes, and Solana-specific decoder fixes. One entry per meaningful release."
---

Subscribe via RSS at [`/changelog/solana/rss.xml`](/changelog/solana/rss.xml). Filter by tag — `Wallet API`, `Contributors`, `Policy`, `Architecture`.

<Update label="2026-05-12 — Solana preset wave" description="v0.646.0 — v0.649.0" tags={["Wallet API", "Contributors"]}>
VisualSign now decodes Jupiter (Borrow, Earn, Perpetuals, v6 route_v2), Kamino (Borrow, Vault, Farms, Limit Orders), Meteora (DLMM, DAMM V2), MetaDAO (Futarchy, Conditional Vault), Orca Whirlpool, Onre App, Neutral Trade, DFlow Aggregator, and Exponent Finance.

None of these additions required changes to existing decoders or to the core parser. Each preset is a self-contained directory under `src/chain_parsers/visualsign-solana/src/presets/`, auto-registered by `build.rs`. See [Composable by design: how Solana presets stack](/blog/2026-05-13-composable-solana-presets) for the architecture, and [Adding a Solana preset](/chains/solana) for the how-to.
</Update>

<Update label="2026-05-12 — Surfpool mainnet-fork integration for IDL fuzz testing" description="v0.647.0" tags={["Contributors"]}>
The built-in Solana IDL parser path is now fuzz-tested against real mainnet state via Surfpool. Composing live chain state into the local test harness catches drift between IDL assumptions and on-chain reality, without requiring a live cluster.
</Update>
8 changes: 8 additions & 0 deletions docs/changelog/sui.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "Changelog — Sui"
description: "Sui Move package decoders, programmable transaction support, and Sui-specific decoder fixes. One entry per meaningful release."
---

Subscribe via RSS at [`/changelog/sui/rss.xml`](/changelog/sui/rss.xml). The feed populates as new entries land.

No entries yet. Recent Sui work is tracked in [GitHub commits under `src/chain_parsers/visualsign-sui/`](https://github.com/anchorageoss/visualsign-parser/commits/main/src/chain_parsers/visualsign-sui); curated entries appear here as part of the next meaningful release.
8 changes: 8 additions & 0 deletions docs/changelog/tron.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "Changelog — Tron"
description: "Tron contract decoders, TRC standards support, and Tron-specific decoder fixes. One entry per meaningful release."
---

Subscribe via RSS at [`/changelog/tron/rss.xml`](/changelog/tron/rss.xml). The feed populates as new entries land.

No entries yet. Recent Tron work is tracked in [GitHub commits under `src/chain_parsers/visualsign-tron/`](https://github.com/anchorageoss/visualsign-parser/commits/main/src/chain_parsers/visualsign-tron); curated entries appear here as part of the next meaningful release.
38 changes: 38 additions & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,44 @@
]
}
]
},
{
"tab": "Changelog",
"groups": [
{
"group": "Overview",
"pages": [
"changelog"
]
},
{
"group": "Chain streams",
"pages": [
"changelog/solana",
"changelog/ethereum",
"changelog/sui",
"changelog/tron"
]
},
{
"group": "Core",
"pages": [
"changelog/core"
]
}
]
},
{
"tab": "Blog",
"groups": [
{
"group": "Blog",
"pages": [
"blog",
"blog/2026-05-13-composable-solana-presets"
]
}
]
}
]
},
Expand Down
Loading