Skip to main content

hopper/
lib.rs

1//! # Hopper
2//!
3//! A zero-copy Solana program framework. One access model, one dispatch
4//! path, one set of safety rules. Unsafe is available when you need it,
5//! and it is spelled `unsafe` so you can find it again.
6//!
7//! The goals, in priority order:
8//!
9//! 1. **Safety by default.** Every account byte you touch has been
10//!    owner-checked, signer-checked, layout-checked, and borrow-checked
11//!    before you see it. Unsafe is an opt-in escape hatch, never a
12//!    default.
13//! 2. **Low-overhead performance.** Account data points directly at
14//!    the runtime input region. No deserialization pass, no heap
15//!    allocation, no hidden format machinery. If it costs compute, it
16//!    is because you asked for it.
17//! 3. **Anchor-grade ergonomics.** `#[hopper::state]`, `#[hopper::context]`,
18//!    `#[hopper::program]`, and the `#[account(...)]` constraint vocabulary
19//!    read the same way an Anchor program reads. Porting is a rename,
20//!    not a rewrite.
21//! 4. **Schema that travels.** Every layout, instruction, event, and
22//!    error is emitted as inspectable compile-time metadata. Off-chain
23//!    SDKs, IDLs, client generators, and diff tools consume it without
24//!    parsing source.
25//!
26//! ## Crate map
27//!
28//! - `hopper_core`: wire types, account header, segment maps, validation,
29//!   collections, and opt-in advanced subsystems (frame, receipt, policy,
30//!   diff, migration) behind feature gates.
31//! - `hopper_runtime`: the runtime surface a program actually calls.
32//!   Context, Account, Pod, guards, CPI, migrations, log macros,
33//!   entrypoint bridges.
34//! - `hopper_macros`: declarative macros. `hopper_layout!`, `hopper_check!`,
35//!   `hopper_error!`, `hopper_init!`, `hopper_close!`, `hopper_require!`,
36//!   `hopper_manifest!`, `hopper_segment!`, `hopper_validate!`,
37//!   `hopper_virtual!`, `hopper_interface!`, `hopper_assert_compatible!`,
38//!   `hopper_assert_fingerprint!`.
39//! - `hopper_macros_proc`: proc-macro DX layer. `#[hopper::state]`,
40//!   `#[hopper::pod]`, `#[hopper::context]`, `#[hopper::program]`,
41//!   `#[hopper::migrate]`, `#[hopper::args]`, `#[hopper::error]`,
42//!   `#[hopper::event]`, `#[hopper::dynamic]`.
43//! - `hopper_solana`: SPL Token/Mint readers, Token-2022 checks, CPI
44//!   guards, Pyth oracle, TWAP, Ed25519/Merkle crypto, authority rotation.
45//! - `hopper_system`: System Program instruction builders.
46//! - `hopper_token`: SPL Token instruction builders.
47//! - `hopper_token_2022`: Token-2022 instruction builders plus extension
48//!   screening helpers.
49//! - `hopper_associated_token`: ATA derivation and instruction builders.
50//! - `hopper_schema`: layout manifests, fingerprinting, field-level
51//!   diffing, compatibility verdicts, Codama/IDL projections, client
52//!   generation.
53//!
54//! ## Access model
55//!
56//! Four reads, one rule: safety first, unsafe by name.
57//!
58//! ```text
59//! Safe full:    account.load::<T>()        / account.load_mut::<T>()
60//! Safe segment: ctx.segment_ref::<T>(i,o)  / ctx.segment_mut::<T>(i,o)
61//! Explicit raw: unsafe { account.raw_ref() / account.raw_mut() }
62//! Cross-prog:   account.load_cross_program::<T>()
63//! ```
64//!
65//! `load` and `segment_ref` are the defaults. `raw_ref` is the escape
66//! hatch. `load_cross_program` is the Hopper-specific verb for reading
67//! an account owned by another program (foreign-ownership checked at
68//! the type level).
69//!
70//! ## Quick start
71//!
72//! Declare a layout, declare a context, ship a handler.
73//!
74//! ```ignore
75//! use hopper::prelude::*;
76//! use hopper::hopper_layout;
77//!
78//! hopper_layout! {
79//!     pub struct Vault, disc = 1, version = 1 {
80//!         authority: [u8; 32] = 32,
81//!         mint:      [u8; 32] = 32,
82//!         balance:   WireU64  = 8,
83//!         bump:      u8       = 1,
84//!     }
85//! }
86//! ```
87//!
88//! That is it. `Vault` is now a zero-copy layout with a 16-byte Hopper
89//! header, a segment map, a schema export for the manifest, and a
90//! `load::<Vault>()` accessor on every `AccountView`. No derives, no
91//! Borsh, no writeback pass.
92
93#![no_std]
94#![deny(unsafe_op_in_unsafe_fn)]
95
96// ── Hopper Lang modules ──────────────────────────────────────────────
97
98#[doc(hidden)]
99pub mod __macro_support;
100pub mod guards;
101pub mod pda;
102pub mod prelude;
103pub mod receipts;
104
105// Re-export crates
106pub use hopper_associated_token;
107pub use hopper_core;
108pub use hopper_runtime;
109pub use hopper_schema;
110pub use hopper_solana;
111pub use hopper_system;
112pub use hopper_token;
113pub use hopper_token_2022;
114
115/// Optional Metaplex Token Metadata builders. Behind `--features metaplex`.
116/// Reach for this via `hopper::hopper_metaplex::CreateMetadataAccountV3`,
117/// `CreateMasterEditionV3`, `UpdateMetadataAccountV2`, plus the
118/// `metadata_pda` / `master_edition_pda` PDA helpers.
119#[cfg(feature = "metaplex")]
120pub use hopper_metaplex;
121
122/// Small utilities. Re-exported at the crate root so `use hopper::utils::hint::likely;`
123/// just works.
124pub use hopper_runtime::utils;
125
126// Re-export macros at the crate root
127pub use hopper_core::hopper_dispatch;
128pub use hopper_macros::{
129    const_assert_pod, hopper_accounts, hopper_assert_compatible, hopper_assert_fingerprint,
130    hopper_check, hopper_close, hopper_error, hopper_init, hopper_interface, hopper_invariant,
131    hopper_layout, hopper_manifest, hopper_register_discs, hopper_require, hopper_segment,
132    hopper_validate, hopper_verify_pda, hopper_virtual,
133};
134
135// Audit I4: schema-epoch migration chain composition. `#[macro_export]`
136// macros are always anchored at the defining crate's root, so the
137// user-facing path `hopper::layout_migrations!` requires an explicit
138// re-export here.
139pub use hopper_runtime::layout_migrations;
140
141/// Destructuring sugar for raw-dispatch handlers.
142///
143/// Replaces the common pattern:
144///
145/// ```ignore
146/// let [user, vault, system_program, ..] = accounts else {
147///     return Err(ProgramError::NotEnoughAccountKeys);
148/// };
149/// ```
150///
151/// with the tighter form:
152///
153/// ```ignore
154/// hopper_load!(accounts => [user, vault, system_program]);
155/// ```
156///
157/// The bindings are plain `&AccountView` references (not owned values),
158/// matching the destructuring pattern. A trailing `..` is accepted and
159/// discards any extra accounts. The macro bails with
160/// `ProgramError::NotEnoughAccountKeys` when the slice is too short,
161/// mirroring Hopper's existing idiom.
162///
163/// Use this in the raw-dispatch authoring style (no `#[hopper::context]`).
164/// The proc-macro context already binds accounts by name, so this is only
165/// useful when you are working with `&[AccountView]` directly - typically
166/// inside `fn process_instruction(_, accounts: &[AccountView], _)` before
167/// routing to per-variant handlers.
168///
169/// ## Examples
170///
171/// ```ignore
172/// fn process_deposit(
173///     program_id: &Address,
174///     accounts: &[AccountView],
175///     data: &[u8],
176/// ) -> ProgramResult {
177///     hopper_load!(accounts => [user, vault, system_program]);
178///     user.require_signer()?;
179///     vault.require_writable()?;
180///     // ... rest of handler ...
181///     Ok(())
182/// }
183/// ```
184///
185/// With a trailing rest pattern (accept more accounts, ignore them):
186///
187/// ```ignore
188/// hopper_load!(accounts => [user, vault, ..]);
189/// ```
190///
191/// The trailing `..` is redundant with the default behaviour (the macro
192/// always accepts more accounts than declared) but is supported for
193/// stylistic parity with the native Rust slice pattern.
194#[macro_export]
195macro_rules! hopper_load {
196    ( $slice:expr => [ $($binding:ident),+ $(, ..)? $(,)? ] ) => {
197        let [ $($binding,)+ .. ] = $slice else {
198            return ::core::result::Result::Err(
199                $crate::hopper_runtime::error::ProgramError::NotEnoughAccountKeys,
200            );
201        };
202    };
203}
204
205// Ergonomic guard macros (the "winning architecture" design's
206// Jiminy-replacement safety layer). All are `#[macro_export]` from
207// hopper_runtime and are re-exported here so programs see them at
208// the top-level `hopper::*` path without needing to reach through
209// `hopper_runtime::`.
210pub use hopper_runtime::{
211    address, err, error, hopper_emit_cpi, hopper_log, hopper_unsafe_region, msg, require,
212    require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq, require_lt,
213    require_lte, require_neq,
214};
215
216// Optional proc macro re-exports (enabled with `proc-macros` feature)
217#[cfg(feature = "proc-macros")]
218pub use hopper_macros_proc::{
219    account, accounts, constant, context, crank, declare_program, hopper_constant, hopper_context,
220    hopper_crank, hopper_migrate, hopper_pod, hopper_program, hopper_state, migrate, pod, program,
221    state, Accounts, HopperInitSpace,
222};
223
224// Private re-export for generated code to reference runtime types
225#[doc(hidden)]
226pub mod __runtime {
227    pub use hopper_runtime::{
228        apply_pending_migrations, read_tail, read_tail_len, tail_payload, write_tail, Account,
229        AccountLayout, AccountView, Address, Context, HopperInstructionPolicy, HopperProgramPolicy,
230        HopperSigner, InitAccount, LayoutMigration, MigrationEdge, Pod, Program, ProgramError,
231        ProgramId, Ref, RefMut, SegRef, SegRefMut, SegmentLease, SystemId, TailCodec,
232    };
233
234    // Crank marker type plus dynamic-CPI builder, emitted by
235    // `#[hopper::crank]` and by hand-written programs that need
236    // variable-length CPIs respectively. Exposed through this
237    // doc-hidden module so user code never reaches into
238    // `hopper_runtime::*` directly.
239    pub use hopper_runtime::crank::CrankMarker;
240    pub use hopper_runtime::dyn_cpi::DynCpi;
241
242    // `#[hopper::state]` and `#[hopper::pod]` emit bytemuck-backed
243    // proofs through this path so user code never needs a direct
244    // bytemuck dependency. Gated on the native backend because that's
245    // where the bytemuck re-export lives.
246    #[cfg(feature = "hopper-native-backend")]
247    pub use hopper_runtime::__hopper_native;
248
249    // Audit final-API Step 5 seal. Doc-hidden re-export of the
250    // sealed-trait module so macros can emit
251    // `unsafe impl ::hopper::__runtime::__sealed::HopperZeroCopySealed for Foo {}`
252    // without the user ever naming the private module.
253    pub use hopper_runtime::__sealed;
254
255    // Re-export `hopper_runtime::token` so `#[hopper::context]` can
256    // emit `::hopper::__runtime::token::require_token_mint(...)`
257    // without dragging the user into a direct `hopper_runtime`
258    // dependency. The helpers (require_token_mint / require_mint_*
259    // / require_token_authority) are read-only account-byte
260    // preconditions used to lower Anchor's `token::mint`,
261    // `mint::authority`, etc. constraints to a single inline check.
262    pub use hopper_runtime::token;
263}