Skip to content
hopper
Get started
Hopper 0.5.0 is on crates.io

A Rust framework for Solana

Your program.
Your rules.
Direct to state.

Borrow account state. Declare the checks around it. Build in Rust with typed handlers, or take direct control through Hopper’s native runtime. Your program runs on Solana.

cargo add hopper-lang@0.5.0 --rename hopper --features proc-macros
One Rust programSource walkthrough
Explore the counter program
counter / lib.rs
#[derive(Clone, Copy)]
#[repr(C)]
#[account(discriminator = 1, version = 1)]
pub struct Counter {
    pub authority: Address,
    pub value: WireU64,
}

Account bytes become a typed view. WireU64 gives the counter a fixed, alignment-safe representation.

Excerpt · requires initialized stateRead the source
111 CU

Raw hello-world fixture

325 CU

Framework counter update

On-chain evidence

Public signatures. Recorded program hashes.

Fixture measurements: 2026-09-30, commit f6333ae. Method and limits

Explore the architecture
why hopper

Keep control as your program grows.

Describe your accounts and actions in Rust. Add the validation your application needs, then use lower-level APIs where measurement shows they help. The execution stays on Solana.

01
borrowed account state

Work directly with account state.

Supported layouts borrow account bytes directly, so updating a field does not require deserializing the whole account. Application code and other data formats can still allocate or copy.

How accounts are read
02
explicit account contracts

Declare what an instruction accepts.

Typed accounts check their owner and layout. Declare signer, writable, authority, and PDA constraints alongside them. Checked borrows and CPI reject incompatible live references. Your handler defines the application rules.

The Hopper model
03
one framework, top to bottom

Start high. Drop low where it counts.

Use #[program] and #[derive(Accounts)] for application handlers. Reach for runtime account views, CPI, and syscalls when you need direct control. The facade exposes both layers; lower-level code takes on the validation it bypasses.

The layers
04
inspectable behavior

Constrain writes. Inspect the result.

Optional write policies constrain tracked mutations and CPI delegation. Manifests describe declared layouts and effects; self-audit tooling checks APIs, unsafe-code coverage, and dated validation evidence. Independent review remains open.

The self-audit
the code

Start with a program you can read.

These excerpts come from the linked Rust sources. Declare state, list accounts, and write a handler. Hopper generates the checks and the dispatch around your Rust code, and lower-level APIs are available when an instruction needs explicit control.

#[derive(Clone, Copy)]#[repr(C)]#[account(discriminator = 1, version = 1)]pub struct Counter {    pub authority: Address,    pub value: WireU64,}
A headered account with a stored authority and an alignment-safe integer. The counter example expects an initialized account; the vault example includes creation.Full source
measured, with the source

Measure the programs your users run.

Start with the whole action: token movement, account checks, refunds, and failure behavior. These dated fixtures show what was measured; your program needs its own budget and tests.

Token escrow | Devnet, September 30, 2026

A real program, run on a real cluster.

Deposit, trade, refund, and cancel against the real SPL Token program: 33 finalized transactions, the refusals the program is supposed to make among them. Every one left exactly the account state it should have, and the deployed program matched the tested one byte for byte.

Classic SPL Token escrow compute units on devnet
On-chain actionCompute units
Create and fund an offer6,868 / 6,213
Take the offer, refund the surplus, close the accounts7,836
Cancel, refund the tokens, close the accounts3,586

The escrow example at commit 847a0b0, with real classic SPL Token transfers. The transactions and checks · The program

Compute units and program sizes for the hello-world and PDA-counter programs
ProgramHopper rawHopper framework
Hello world, compute units111127
Hello world, program size1,456 B1,824 B
PDA counter, create1,5141,471
PDA counter, update1,722325
PDA counter, program size6,608 B8,744 B

The raw counter uses a 10-byte account and full PDA derivation on every update. The framework counter checks the signer, the owner, the layout, and the PDA before the handler runs; it checks the PDA with one hash from the bump stored in the account, and its account carries a 16-byte header (25 bytes).

Hopper hello-world and counter fixtures,2026-09-30, commit f6333ae. Different account layouts and PDA validation paths affect these costs. Reproduce it

Deployed fresh to devnet the same day, the Hopper programs were charged exactly these compute units, every account checked byte for byte. The transactions

bring the program you have

Build at the level your program needs.

Application handlers

Start with typed accounts.

Describe account layouts, declare validation, and write the action your users need. Token and System Program builders carry out transfers; your code defines who may request them.

Account validation#[derive(Accounts)]
Program actions#[program]
Token movementTransferChecked
Build a program
Lower-level code

Control the hot path.

Use account views, explicit borrow checks, CPI, and syscalls directly. Review existing layouts and authority rules when porting a program, then measure its complete workload.

Account memoryAccountView
Signed CPIinvoke_signed
Program addressespda helpers
Write a lower-level handler

Coming from Pinocchio, Pina, Quasar, or Anchor? Compare account layouts, validation boundaries, and integration work in the source comparison. Performance depends on the complete instruction and its checks.

Hopper 0.6 · in development

Borrow the payload.
Keep the checks.

Declare one wire layout. Accept &MyArgs in an ordinary Rust handler, or parse it at a manual entrypoint. Both paths borrow the input and check nested option and enum values.

These excerpts share the same update function in the Solana fixture. Select an entry path, then explore valid and refused inputs in the browser illustration. Account authorization and business rules remain explicit.

Choose the borrowed argument entry path
#[instruction(2)]
pub fn exact(ctx: &mut Context<'_>, args: &Update) -> ProgramResult {
    apply_update(
        ctx.program_id,
        ctx.accounts(),
        ctx.instruction_data(),
        args,
        &[],
    )
}

The dispatcher borrows and validates &Update before entering the handler. apply_update checks accounts and applies the state transition.

Explore the wire contract and fixture
Follow release readinessPrepare your clients for 0.6
Inside the instructionInteractive model

One buffer. Typed access.

Change the input. See where validation stops it.

Instruction data example
Instruction payload12 bytes
00–03 modes04–11 amount
Borrow accepted

A typed reference reads amount = 42 from the same bytes. The absent second option ignores its ff payload.

&Update → original instruction buffer

Checked borrowed arguments
Next release · unreleased

See the Rust

Inside the function lab

Test what actually executes.

Small Solana programs put Hopper’s runtime boundaries under test. Open a check to see what counts as evidence.

The bytes must agree.
17 slices → SHA-256Exact digest bytes

Hash results and integer operations are compared with known vectors and independent arithmetic. A successful transaction alone is not a passing test.

A refusal must hold.
An alias is still borrowedWrite refused · state intact

Invalid lengths, missing authority, held borrows and out-of-bounds lenses must return the expected error. State tests also compare the whole account afterward.

The cluster decides.
Exact deployed ELFReturn bytes + account state

Compiled VM tests, live devnet execution and feature-account observations are distinct evidence. Each test names its build and scope; unsupported calls stay visible.

Explore the test program and its limits

Next release · checked borrowed batches

More elements.
One borrowed buffer.

Give a batch a capacity and a wire type. Hopper checks every element before your handler runs, without copying the batch into a capacity-sized array.

orders: BoundedSlice<'_, Order, 4>

Validation visits the present elements. Borrowing saves the owned container; application checks and Solana transaction limits still apply.

The same capacity and element width reach your clients, even through Rust type aliases. Checked batch encoders are available for Rust, TypeScript, and Python.

Explore the batch contract
2 / 4 capacity
Borrow accepted

All 2 elements pass representation checks. The handler borrows the original bytes and applies its application rules.

Interactive wire-format illustration · not a transaction simulator

get started with the published release

Build with Hopper 0.5.0.

Install the CLI, create a project, and build your first program. You will need Rust and the Solana SBF build tools; the getting-started guide covers setup. Version 0.5.0 was verified on crates.io on 2026-10-01.

01
$ cargo install hopper-cli --version 0.5.0 --locked
install the published CLI
02
$ hopper init my-program --template minimal --yes
create a new program
03
$ cd my-program && hopper build
build with your installed Solana SBF toolchain
Develop against the 0.6.0 source

Clone a reviewed revision, use a path dependency for `hopper-lang`, and install the CLI with `cargo install --path tools/hopper-cli`. Pin the exact Git revision in team and CI environments.