Skip to content
hopper
Get started
Operations / tx-tooling

Manifests and tx tooling

Manifest generation, versioned ELF interface binding, and the no-Node hopper tx send / tx explain loop.

This reference covers Hopper 0.5.0, published on crates.io. See the release status for package verification, dated benchmarks, and remaining audit work.

Hopper's tooling loop is pure Rust end to end: the program exports its schema in one line, the CLI generates the manifest from source, and the same CLI sends instructions and decodes confirmed transactions by name, no npx, no node_modules, no JS toolchain anywhere.

program_manifest!: the one-liner

Every Hopper program can export its full schema as a static:

hopper::program_manifest! {
    program = vault_program,
    layouts = [Vault],
    events  = [DepositReceipt],
}

name / version / description default from the crate's CARGO_PKG environment constants. The macro assembles metadata emitted by Hopper's layout, context, handler, and event macros: field types and offsets, instruction arguments and account roles, declared strict_writes ranges, mutation_complete and lamport declarations, receipt expectations, and event fields.

The scoped guarantee is narrower than "the whole program is proven." For macro-declared fields reached through Hopper-tracked typed paths, the same generated layout IDs and write-range constants feed the manifest and runtime gates. Raw, unchecked, FFI, and dependency mutation paths require separate review, and exporting or publishing a declaration does not prove handler behavior.

The counter regression fixture checks that manifest generation preserves its compiled layout identity. In Hopper 0.3.0, hopper::program_manifest! also embeds a versioned SHA-256 commitment to the declared executable interface. hopper verify --release requires the compiled ELF to contain that exact commitment. This binds declarations to an artifact; it does not prove artifact freshness, deployment identity, or behavior.

hopper compile --emit manifest: generate from source

hopper compile --emit manifest --package my-program

Closes the same loop anchor idl build does, without the Node leg: it builds a scratch harness against the target package, prints the package's PROGRAM_MANIFEST, and writes hopper.manifest.json next to the package (--out to override), with a friendly error naming the expected shape when the package exports no manifest static. The generated file immediately drives --emit ts (and the other client generators, see Client generation) unchanged. Both manifest dialects (the older snake_case files and the rendered camelCase output) are accepted by the loader, additively. Old files keep working.

The manifest carries the program's byte-range write-set end to end: writeRanges, strictWrites, mutationComplete, lamportAccounts, and cuEstimate. One honest note on cu_estimate: it is author-supplied and must come from a measured worst-case run of the actual instruction, not from summing the per-primitive tables (see the CU costs page).

hopper tx send: the generic no-Node sender

The instruction sender stock tooling lacks (devs usually shell out to JS scratch scripts):

hopper tx send --program <program-id> \
    --account payer:sw --account <vault>:w \
    --data 015a00000000000000 \
    --keypair ~/.config/solana/id.json --rpc https://api.devnet.solana.com
  • Ordered --account <pubkey|payer>[:s][:w] metas, one per slot; the literal spelling payer resolves to the fee payer.
  • Hex --data, local keypair signing, and refuse-before-send signer coverage: it will not broadcast a transaction whose declared signers it cannot sign.
  • --dry-run previews the exact instruction plan and signer coverage; after confirmation it fetches and prints the measured CU and fee. A send you cannot budget from is half a tool.

It uses the same signed-send stack as hopper publish-idl, the pure-Rust publisher for a validated Solana IDL v0.1.0 projection at the Program Metadata PDA. The current projection preserves Hopper's wire discriminators and marks account bodies with custom hopper-zero-copy-v1 serialization; it does not make them Anchor-Borsh decodable. Publication refuses dynamic account tails, unsupported bounded wire encodings, remaining-account contracts, unresolved or unsafe PDA seeds, unresolved fixed addresses, and ambiguous discriminator prefixes. It does not publish writeRanges or a Grillo effect contract.

One devnet transaction on 2026-07-12 published and read back the then-current payload. That receipt proves the old Program Metadata transport, ownership, compression, and readback path. It does not validate today's Solana IDL v0.1.0 shape or fail-closed projection, and it is not broad production evidence.

hopper tx explain: named decodes from confirmed transactions

hopper tx explain <signature> decodes the artifacts a confirmed transaction published:

  • Touch maps from the log stream, scoped to ranges acquired through Hopper-tracked APIs: W slot 1 (vault) [48..56) -> Vault.balance, see Self-describing transactions.
  • Self-CPI events from inner-instruction metadata: the indexer-grade read path, because inner instructions survive the log truncation that motivates event_cpi in the first place. Authenticity is judged honestly: a marker CPI whose target equals the enclosing program in a successful transaction was sink-authenticated on-chain (only that program's invoke_signed can sign its event authority); a marker-shaped CPI to a foreign program is labeled exactly that.
  • With the program's manifest, events render named and field-decoded: little-endian integers by declared wire type, hex fallback for unknown types, manifest/payload mismatches reported, never padded over.

The loop, run live

On 2026-07-10 every instruction of the devnet smoke program was fired with hopper tx send and decoded back with hopper tx explain: five instructions, live CU from 762 to 3,586, including the named event decode event: DepositReceipt (tag 0x02) { balance: 1500000, deposit_count: 3 } whose live cost matches the Mollusk lab exactly. The full table with signatures is on the Benchmarks page.

Transaction v1 and metadata records

The 0.3.0 CLI also supports hopper tx send --v1. It encodes compute and loaded-data limits in the transaction's config mask, with an optional total-lamport --priority-fee; it rejects those v1-only options on legacy sends. hopper tx explain accepts legacy, v0, and v1 responses. See the CLI reference for the exact limits and defaults.

hopper publish-security validates and publishes a Program Metadata security record. hopper publish-manifest publishes the normalized Hopper manifest under the custom hopper-manifest seed and can read it back. That seed is a Hopper convention, not a standard reserved record. The full manifest retains Hopper declarations omitted by the lossless-only Solana IDL projection. Publishing declarations does not by itself prove that deployed code enforces them. The legacy hopper fetch command still reads a different, application-provisioned PDA.

The 2026-09-21 devnet archive records the security/manifest publication and transaction-v1 checks.