Zero-copy Solana state,
validated before you cast.
Write Solana programs with Anchor ergonomics. Hopper checks owner, role, discriminator, version, and layout fingerprint before account bytes become typed state. No deserialize-then-hope. No unchecked casts hidden in macros. In the first published router-class head-to-head, Hopper beats Quasar on every compute row and lands within 1.8 to 2.4% of hand-written Pinocchio, while carrying receipts, migrations, byte-range write policies, and generated clients none of them have.
The fast path still has types, names, and guardrails.
Check before cast.
Layout validation happens before your handler code runs. Headered accounts prove owner, discriminator, version, and layout ID; compact accounts prove exact size and discriminator. No unchecked casts. No runtime surprises.
One byte when the hot path needs it.
Opt into `[disc][body]` accounts for dense state. Manifests, IDL, registries, and generated TS/Kotlin clients carry the fingerprint while the on-chain account stays 41 bytes when 41 bytes is all it needs.
TLV scanning, zero overhead.
Validate transfer hooks, permanent delegates, confidential transfers and more with direct byte scanning. No detours. No helper methods. Check what you need, skip what you don't.
Native precompile access.
Hash with SHA-256, Keccak, BLAKE3. Verify Ed25519, secp256k1, secp256r1 signatures. Recover Ethereum addresses. All inline, all explicit, all reviewable.
Prove what changed.
Stamp mutations with before/after fingerprints, field changes, invariant tracking, and CPI flags. 72 bytes. Self-describing. Backwards compatible with 64-byte legacy.
CLI is part of the framework.
program_manifest! exports your schema in one line, the same generated consts the runtime enforces, so published equals enforced by construction. hopper compile --emit manifest regenerates it from source, tx send fires instructions, and tx explain decodes what they did. No Node anywhere.
Anchor’s emit_cpi. Three bytes of wire.
One attribute option plus one call mirrors #[event_cpi] + emit_cpi! exactly (same two auto-appended accounts, authenticated sink) at 3 bytes of instruction-data overhead per event vs Anchor’s 16. Live on devnet the named decode cost 3,586 CU, matching Mollusk exactly. Trade-off, stated: Hopper derives the event authority at runtime (~148 CU per attempt); Anchor pins a compile-time constant for ~free and wins that axis.
Every instruction is a migration crank.
Declare migrate(from = V1, with = transform) on a field and accounts upgrade in place as they are touched, typed both sides, one stack copy, no (de)serialization, no dedicated migration instruction. Live on devnet: a real account cranked v1 to v2 for a one-time 29 CU premium (280 vs 251 CU steady-state), both matching Mollusk exactly. Pair with reserved padding and realloc is never needed.
Transactions that name what they wrote.
Opt a context into emit_touch_map and every successful instruction emits byte-range write records hopper tx explain can decode. Live on devnet, a withdraw explains itself as W vault [48..56) -> Vault.balance. Wrapper get_mut writes record too (whole-account, honest but coarse). Per-context opt-in by design: each record costs a sol_log_data (+52 CU measured in the touch-map-enabled smoke crate), and we don’t hide CU in defaults.
Contexts nest. Options compose.
#[composite] embeds one context in another with Anchor’s flattening and nested bumps (an explicit marker, because Hopper refuses to guess). Anchor had nesting first; what it doesn’t have is the rest: strict_writes splices the inner context’s declared write ranges into one compile-time write-set, so published-equals-enforced survives nesting, and event/touch-map slots land after the flattened set.
The authoring model stays Rust-native.
Start with the proc-macro path when you want speed. Drop to declarative macros or raw tiers when you need control. The layout, context, dispatcher, and CPI surface all stay inspectable.
1#[derive(Clone, Copy)]2#[repr(C)]3#[account(discriminator = 1, version = 1)]4pub struct Counter {5 pub authority: Address,6 pub value: WireU64,7}Forward-secret messaging with verifier-bound ferry proofs.
The Styx ferry example uses Hopper accounts for prekeys, ratchet threads, and proof counters while exposing the Solana crypto surface: Keccak domain derivation, Ed25519 precompile checks, sibling instruction inspection, and typed verifier CPI.
Open Styx docEd25519 precompile inspection ties the owner to the current signed prekey
bounded ciphertext events with monotonic counters and sealed-message hashes
513-byte proof v2 with checked domain, fee tier, and base fee inputs
proof bytes are forwarded only to the config-pinned verifier program
#[instruction(5)]
pub fn submit_zk_ferry(
ctx: Ctx<SubmitZkFerry>,
proof: HopperVec<u8, STYX_ZK_PROOF_V2_LEN>,
encrypted_outputs: [u8; 64],
) -> ProgramResult {
ctx.accounts.submit(proof, encrypted_outputs)
}A compromised solver still cannot touch what you did not hand it.
Cicada is a transport-neutral protected-execution intent program. Users publish execution constraints in one shared, column-oriented shard; a solver settles an intent by writing only the exact per-slot settlement cells the runtime granted it. It can never rewrite the user’s limits, the committed route, or a neighbor’s intent, because those columns are never declared writable and the policy is enforced at borrow time, before any byte moves. Anchor rewrites the whole account on exit and Quasar hands back a raw &mut T; neither can refuse a single field write.
See example programsowners, limits, and route policy live in columns a claim or execute handler is never granted mutable access to
cells(slot; ...) grants one intent’s settlement cells; a write to a neighbor’s cell is refused before a byte changes
route CPI is signed by a PDA derived from the user and their source account, not a shared protocol key
settlement needs the observed output delta to clear min_output; any unused input is returned atomically first
#[derive(Accounts)]
#[accounts(strict_writes, emit_touch_map)]
#[instruction(slot: u16)]
pub struct ExecuteIntent<'info> {
pub executor: Signer<'info>,
// The executor may write only these settlement cells, for this
// one slot. Owners, limits, and route policy sit in other columns
// and are never declared writable here.
#[account(
cells(slot;
statuses, claimants, claim_expiries,
settled_inputs, settled_outputs,
settlement_hashes, revisions),
has_one = config,
)]
pub shard: Account<'info, IntentShard>,
// Route CPI signer power is scoped to one user's vault.
#[account(seeds = [
VAULT_AUTHORITY_SEED,
intent_owner.address().as_array(),
source_token.address().as_array(),
], bump)]
pub vault_authority: UncheckedAccount<'info>,
}Same authoring shape. More contract.
Write like Anchor. Get Hopper validation. Add layout identity, field-level borrows, Token-2022 checks, dynamic tails, receipts, manifests, migrations, and generated clients when you need them. Anchor ergonomics plus serious state safety.
Same harness, same seeds, scoped claims.
Current release-facing numbers come from the sibling hopper-bench Mollusk harness, re-measured 2026-07-09 and averaged across 8 deterministic seeds. Quasar's upstream vault exposes only deposit and withdraw, so validation-only rows stay n/a instead of being synthesized.
Withdraw moved 442 → 486 CU versus the previous published table: the +44 CU is the mutation-complete lamport write-gate actually enforcing on the one lamport-moving instruction, a measured safety feature no other column carries (the error-path lowering that bought a 10% .text cut cost +8 here, and the gate-check fast-out recovered it). Every Quasar-comparable row still wins. And the Hopper vault .so now measures smaller than hand-written Pinocchio's on the identical contract (7.46 vs 7.73 KiB, zero writable sections); Quasar's 5.47 KiB still wins the size row.
The large Pinocchio gaps are mostly a PDA-strategy difference: the Pinocchio target uses idiomatic find_program_address (a bump search), while Hopper verifies a stored canonical bump in one hash. The honest claim is that Hopper is fast by default, the cheap path is the one the macros steer you toward.
A real workload, and Hopper wins its class.
Multi-hop swap routing against a shared mock-AMM: measured amount forwarding between hops, dynamic accounts, and a mandatory min-out safety gate every framework must reject. Hopper beats Quasar on every compute row with the smallest binary, and lands within 1.8 to 2.4% of hand-written Pinocchio, while carrying validation, receipts, write policies, and generated clients the others don't have.
These rows were re-earned on 2026-07-09: a routine re-run caught a +52 CU/hop regression, the claim was suspended in public, a per-commit bisect attributed every CU, and the fix landed the rows better than the originals. Hand-written Pinocchio wins raw CU, as it should, it carries no framework. The contract, fixtures, and both snapshots are published in the hopper-bench repo so every delta is checkable.
The lab numbers survive contact with a real cluster.
Every instruction was fired with hopper tx send and decoded back with hopper tx explain, pure Rust on both sides, no JS toolchain. The withdraw names the exact field it wrote, the event decodes by name from inner-instruction metadata, and a real account was cranked v1 → v2 in place by the migrate-at-bind demo for a one-time premium of 29 CU.
The live emit_receipt cost (3,586 CU) and both live migration touches (280 / 251 CU) match their Mollusk lab measurements exactly, two consecutive lab-to-cluster exact matches, the strongest validation the in-process numbers can get. Full signatures and provenance are on the benchmarks page.
Four steps from nothing to devnet.
Add the framework, install the CLI, scaffold a program, and deploy. Hopper gives you native runtime, layout fingerprints, self-describing manifests, and a full validation layer. No detours. No legacy compatibility cruft.
