Skip to main content

hopper/
lib.rs

1//! # Hopper
2//!
3//! Fast zero-copy Solana framework. Start with the familiar account/context
4//! facade, then opt into layout, segment, receipt, policy, migration, and
5//! schema modules only when the program needs them. Unsafe is available when
6//! you need it, and it is spelled `unsafe` so you can find it again.
7//!
8//! The goals, in priority order:
9//!
10//! 1. **Safety by default.** Every account byte you touch has been
11//!    owner-checked, signer-checked, layout-checked, and borrow-checked
12//!    before you see it. Unsafe is an opt-in escape hatch, never a
13//!    default.
14//! 2. **Low-overhead performance.** Account data points directly at
15//!    the runtime input region. No deserialization pass, no heap
16//!    allocation, no hidden format machinery. If it costs compute, it
17//!    is because you asked for it.
18//! 3. **Framework-grade ergonomics.** `#[hopper::account]`,
19//!    `#[hopper::accounts]`, `#[hopper::program]`, typed wrappers such as
20//!    `Account<'info, T>` and `Signer<'info>`, and the facade modules under
21//!    `hopper::{account, cpi, token, system}` keep the beginner surface small.
22//! 4. **Schema that travels.** Every layout, instruction, event, and
23//!    error is emitted as inspectable compile-time metadata. Off-chain
24//!    SDKs, IDLs, client generators, and diff tools consume it without
25//!    parsing source.
26//!
27//! ## Layers
28//!
29//! - `hopper::prelude`: beginner and migration path. Same runtime, clean import
30//!   story.
31//! - `hopper::{account, context, cpi, system, token, associated_token, memo}`:
32//!   everyday program modules.
33//! - `hopper::{layout, segment, receipt, migration, interface, schema, policy}`:
34//!   systems-mode modules for layout evolution, field leasing, manifests, and
35//!   audited escape hatches.
36//! - `hopper::internal`: explicit lower-crate escape hatch.
37//!
38//! ## Crate map
39//!
40//! - `hopper_core`: wire types, account header, segment maps, validation,
41//!   collections, and opt-in advanced subsystems (frame, receipt, policy,
42//!   diff, migration) behind feature gates.
43//! - `hopper_runtime`: the runtime surface a program actually calls.
44//!   Context, Account, Pod, guards, CPI, migrations, log macros,
45//!   entrypoint bridges.
46//! - `hopper_macros`: declarative macros. `hopper_layout!`, `hopper_check!`,
47//!   `hopper_error!`, `hopper_init!`, `hopper_close!`, `hopper_require!`,
48//!   `hopper_manifest!`, `hopper_segment!`, `hopper_validate!`,
49//!   `hopper_virtual!`, `hopper_interface!`, `hopper_assert_compatible!`,
50//!   `hopper_assert_fingerprint!`.
51//! - `hopper_macros_proc`: proc-macro DX layer. `#[hopper::state]`,
52//!   `#[hopper::pod]`, `#[hopper::context]`, `#[hopper::program]`,
53//!   `#[hopper::migrate]`, `#[hopper::args]`, `#[hopper::error]`,
54//!   `#[hopper::event]`, `#[hopper::dynamic]`,
55//!   `#[hopper::dynamic_account]`.
56//! - `hopper_solana`: SPL Token/Mint readers, Token-2022 checks, CPI
57//!   guards, Pyth oracle, TWAP, Ed25519/Merkle crypto, authority rotation.
58//! - `hopper_system`: System Program instruction builders.
59//! - `hopper_token`: SPL Token instruction builders.
60//! - `hopper_token_2022`: Token-2022 instruction builders plus extension
61//!   screening helpers.
62//! - `hopper_associated_token`: ATA derivation and instruction builders.
63//! - `hopper_schema`: layout manifests, fingerprinting, field-level
64//!   diffing, compatibility verdicts, Codama/IDL projections, client
65//!   generation.
66//!
67//! ## Access model
68//!
69//! Four reads, one rule: safety first, unsafe by name.
70//!
71//! ```text
72//! Safe full:    account.load::<T>()        / account.load_mut::<T>()
73//! Safe segment: ctx.segment_ref::<T>(i,o)  / ctx.segment_mut::<T>(i,o)
74//! Explicit raw: unsafe { account.raw_ref() / account.raw_mut() }
75//! Cross-prog:   account.load_cross_program::<T>()
76//! ```
77//!
78//! `load` and `segment_ref` are the defaults. `raw_ref` is the escape
79//! hatch. `load_cross_program` is the Hopper-specific verb for reading
80//! an account owned by another program (foreign-ownership checked at
81//! the type level).
82//!
83//! ## Quick start
84//!
85//! Declare an account layout, bind it in a context, ship a handler.
86//!
87//! ```ignore
88//! use hopper::prelude::*;
89//!
90//! #[account(disc = 1, version = 1)]
91//! #[repr(C)]
92//! #[derive(Clone, Copy)]
93//! pub struct Vault {
94//!     pub authority: Address,
95//!     pub balance: WireU64,
96//!     pub bump: u8,
97//! }
98//! ```
99//!
100//! `Vault` is now a zero-copy account layout with a Hopper header, layout
101//! fingerprint, schema metadata, and typed `Account<'info, Vault>` access. No
102//! Borsh pass, no hidden writeback pass.
103
104#![no_std]
105#![deny(unsafe_op_in_unsafe_fn)]
106
107#[cfg(any(
108    all(feature = "hopper-native-backend", feature = "legacy-pinocchio-compat"),
109    all(feature = "hopper-native-backend", feature = "solana-program-backend"),
110    all(
111        feature = "legacy-pinocchio-compat",
112        feature = "solana-program-backend"
113    ),
114))]
115compile_error!("Enable exactly one Hopper backend: hopper-native-backend, legacy-pinocchio-compat, or solana-program-backend.");
116
117// ── Hopper Lang modules ──────────────────────────────────────────────
118
119#[doc(hidden)]
120pub mod __macro_support;
121pub mod guards;
122pub mod pda;
123pub mod prelude;
124pub mod receipts;
125
126// Re-export crates
127pub use hopper_associated_token;
128pub use hopper_core;
129pub use hopper_runtime;
130pub use hopper_schema;
131pub use hopper_solana;
132pub use hopper_system;
133pub use hopper_token;
134pub use hopper_token_2022;
135
136/// Beginner-facing account surface.
137///
138/// This module is the framework-first import path for account roles and typed
139/// account access. The lower-level crates remain available, but normal program
140/// authors should be able to stay inside `hopper::prelude::*` and these facade
141/// modules.
142pub mod account {
143    pub use hopper_core::abi::{
144        Authority, Mint, Token, TokenAccount, TypedAddress, UntypedAddress,
145    };
146    pub use hopper_core::accounts::{
147        HopperAccount, HopperAccounts, HopperCtx, HopperIx, ProgramAccount, ProgramRef,
148        SegmentedAccount, SignerAccount, ValidateAccount,
149    };
150    pub use hopper_runtime::{
151        Account, AccountView, HopperSigner as Signer, InitAccount, Interface, InterfaceAccount,
152        InterfaceAccountLayout, InterfaceAccountResolve, InterfaceSpec, Program, ProgramId,
153        SystemAccount, SystemId, UncheckedAccount,
154    };
155
156    /// Anchor-style spelling for the System Program marker.
157    pub type System = SystemId;
158}
159
160/// Typed instruction context and account-binding helpers.
161pub mod context {
162    pub use hopper_core::accounts::{hopper_entry, HopperAccounts, HopperCtx, HopperIx};
163    pub use hopper_runtime::Context;
164}
165
166/// Cross-program invocation helpers and generated instruction parts.
167pub mod cpi {
168    pub use hopper_core::cpi::*;
169    pub use hopper_runtime::cpi::{
170        invoke, invoke_signed, invoke_signed_unchecked, invoke_signed_with_bounds,
171        invoke_unchecked, invoke_with_bounds, set_return_data, MAX_CPI_ACCOUNTS, MAX_RETURN_DATA,
172        MAX_STATIC_CPI_ACCOUNTS,
173    };
174    pub use hopper_runtime::{CpiAccount, InstructionAccount, InstructionView, Seed, Signer};
175}
176
177/// System Program helpers.
178#[allow(unused_imports)]
179pub mod system {
180    pub use hopper_system::instructions::*;
181    pub use hopper_system::SYSTEM_PROGRAM_ID;
182    pub use hopper_system::*;
183}
184
185/// SPL Token helpers.
186#[allow(ambiguous_glob_reexports, unused_imports)]
187pub mod token {
188    pub use hopper_runtime::token::*;
189    pub use hopper_solana::interface::{
190        interface_transfer_checked, interface_transfer_checked_signed, InterfaceMint,
191        InterfaceTokenAccount, TokenProgramKind,
192    };
193    pub use hopper_token::instructions::*;
194    pub use hopper_token::TOKEN_PROGRAM_ID;
195    pub use hopper_token::*;
196}
197
198/// SPL Token-2022 helpers.
199#[allow(ambiguous_glob_reexports, unused_imports)]
200pub mod token_2022 {
201    pub use hopper_runtime::token_2022_ext::*;
202    pub use hopper_token_2022::instructions::*;
203    pub use hopper_token_2022::TOKEN_2022_PROGRAM_ID;
204    pub use hopper_token_2022::*;
205}
206
207/// Associated Token Account helpers.
208#[allow(unused_imports)]
209pub mod associated_token {
210    pub use hopper_associated_token::instructions::*;
211    pub use hopper_associated_token::ATA_PROGRAM_ID;
212    pub use hopper_associated_token::*;
213}
214
215/// SPL Memo helper surface.
216pub mod memo {
217    pub use hopper_memo::*;
218}
219
220/// Event and log helpers.
221pub mod events {
222    pub use crate::receipts::{emit_receipt, emit_tagged_receipt, emit_typed_receipt, Receipt};
223    #[cfg(feature = "cpi")]
224    pub use hopper_core::event::emit_event_cpi;
225    pub use hopper_core::event::{emit_event, emit_event_tagged, emit_slices};
226    pub use hopper_runtime::{hopper_emit_cpi, hopper_log, msg};
227}
228
229/// Advanced layout and header surface.
230pub mod layout {
231    pub use hopper_core::abi::{FingerprintTransition, LayoutFingerprint, WireType};
232    pub use hopper_core::account::{
233        check_header, read_discriminator, read_header_flags, read_layout_id, read_version,
234        write_header, AccountHeader, AccountReader, FixedLayout, HEADER_FORMAT, HEADER_LEN,
235    };
236    pub use hopper_core::field_map::{FieldInfo, FieldMap};
237    pub use hopper_runtime::layout::{init_header, HopperHeader, LayoutContract, LayoutInfo};
238    pub use hopper_runtime::{AccountLayout, Pod, WireLayout, ZeroCopy};
239}
240
241/// Advanced segment and field-lease surface.
242pub mod segment {
243    pub use hopper_core::account::segment_role::{
244        SegmentRole, SEG_ROLE_AUDIT, SEG_ROLE_CACHE, SEG_ROLE_CORE, SEG_ROLE_EXTENSION,
245        SEG_ROLE_INDEX, SEG_ROLE_JOURNAL, SEG_ROLE_SHARD,
246    };
247    pub use hopper_core::account::{
248        segment_id, SegmentDescriptor, SegmentEntry, SegmentId, SegmentRegistry,
249        SegmentRegistryMut, SegmentSlice, SegmentSliceMut, SegmentTable, SegmentTableMut,
250        MAX_REGISTRY_SEGMENTS, MAX_SEGMENTS, REGISTRY_HEADER_SIZE, REGISTRY_OFFSET,
251        SEGMENT_DESC_SIZE, SEGMENT_ENTRY_SIZE, SEG_FLAG_DYNAMIC, SEG_FLAG_FROZEN, SEG_FLAG_LOCKED,
252    };
253    pub use hopper_core::segment_map::{assert_segment_field_alignment, SegmentMap, StaticSegment};
254    pub use hopper_runtime::{
255        AccessKind, Ref, RefMut, SegRef, SegRefMut, Segment, SegmentBorrow, SegmentBorrowGuard,
256        SegmentBorrowRegistry, SegmentLease, TypedSegment,
257    };
258}
259
260/// Receipt helpers and receipt wire types.
261pub mod receipt {
262    pub use crate::receipts::{emit_receipt, emit_tagged_receipt, emit_typed_receipt, Receipt};
263    #[cfg(feature = "receipt")]
264    pub use hopper_core::receipt::*;
265}
266
267/// Layout migration helpers.
268pub mod migration {
269    #[cfg(feature = "migrate")]
270    pub use hopper_core::migrate::*;
271    pub use hopper_runtime::{apply_pending_migrations, LayoutMigration, MigrationEdge};
272}
273
274/// Cross-program layout/interface pinning helpers.
275pub mod interface {
276    pub use hopper_runtime::{
277        ForeignLens, ForeignManifest, Interface, InterfaceAccount, InterfaceAccountLayout,
278        InterfaceAccountResolve, InterfaceSpec, TransparentAddress,
279    };
280    pub use hopper_solana::interface::*;
281}
282
283/// Schema, manifest, and IDL projection surface.
284pub mod schema {
285    pub use hopper_schema::*;
286}
287
288/// Policy engine surface for systems-mode programs.
289pub mod policy {
290    #[cfg(feature = "policy")]
291    pub use hopper_core::policy::*;
292    pub use hopper_runtime::{HopperInstructionPolicy, HopperProgramPolicy};
293}
294
295/// Protocol-grade Hopper surface.
296///
297/// Import this when a program deliberately reaches beyond framework mode into
298/// layout contracts, segment registries, receipts, policies, migrations,
299/// manifests, overlays, or cross-program interface pinning.
300#[allow(ambiguous_glob_reexports, unused_imports)]
301pub mod systems {
302    pub use crate::interface::*;
303    pub use crate::layout::*;
304    pub use crate::migration::*;
305    pub use crate::policy::*;
306    pub use crate::receipt::*;
307    pub use crate::schema::*;
308    pub use crate::segment::*;
309    pub use crate::{interface, layout, migration, policy, receipt, schema, segment};
310
311    pub use hopper_core::account::{
312        overlay, overlay_mut, read_dynamic_u16, read_dynamic_u32, read_dynamic_u8, safe_close,
313        safe_close_with_sentinel, safe_realloc, write_dynamic_u16, write_dynamic_u32,
314        write_dynamic_u8, DynamicView, DynamicViewMut, ReallocGuard, VerifiedAccount,
315        VerifiedAccountMut, CLOSE_SENTINEL,
316    };
317    pub use hopper_core::check::{find_and_verify_pda, rent_exempt_min};
318    pub use hopper_core::prelude_advanced::*;
319    pub use hopper_core::prelude_core::*;
320    pub use hopper_runtime::{
321        fast_entrypoint, hopper_entrypoint, hopper_fast_entrypoint, hopper_lazy_entrypoint,
322        lazy_entrypoint, no_allocator, nostd_panic_handler, program_entrypoint, BoundedString,
323        BoundedVec, CpiAccount, HopperString, HopperVec, InstructionAccount, InstructionView, Seed,
324        TailCodec, TailElement,
325    };
326
327    pub use crate::{
328        const_assert_pod, hopper_accounts, hopper_assert_compatible, hopper_assert_fingerprint,
329        hopper_check, hopper_close, hopper_dynamic_fields, hopper_dynamic_tail, hopper_error,
330        hopper_init, hopper_interface, hopper_invariant, hopper_layout, hopper_load,
331        hopper_manifest, hopper_register_discs, hopper_require, hopper_segment, hopper_validate,
332        hopper_verify_pda, hopper_virtual, layout_migrations,
333    };
334
335    #[cfg(feature = "proc-macros")]
336    pub use crate::{args, declare_program, dynamic, migrate, pod, state};
337}
338
339/// First-party DeFi math helpers. Enable with `features = ["finance"]`.
340#[cfg(feature = "finance")]
341pub mod finance {
342    pub use hopper_finance::*;
343}
344
345/// First-party lending helpers. Enable with `features = ["lending"]`.
346#[cfg(feature = "lending")]
347pub mod lending {
348    pub use hopper_lending::*;
349}
350
351/// First-party staking helpers. Enable with `features = ["staking"]`.
352#[cfg(feature = "staking")]
353pub mod staking {
354    pub use hopper_staking::*;
355}
356
357/// First-party vesting helpers. Enable with `features = ["vesting"]`.
358#[cfg(feature = "vesting")]
359pub mod vesting {
360    pub use hopper_vesting::*;
361}
362
363/// First-party distribution helpers. Enable with `features = ["distribute"]`.
364#[cfg(feature = "distribute")]
365pub mod distribute {
366    pub use hopper_distribute::*;
367}
368
369/// First-party multisig helpers. Enable with `features = ["multisig"]`.
370#[cfg(feature = "multisig")]
371pub mod multisig {
372    pub use hopper_multisig::*;
373}
374
375/// Anchor interop helpers. Enable with `features = ["anchor-interop"]`.
376#[cfg(feature = "anchor-interop")]
377pub mod anchor {
378    pub use hopper_anchor::*;
379}
380
381/// Explicit escape hatch for lower layers. Normal programs should not need it.
382#[doc(hidden)]
383pub mod internal {
384    pub use crate::{hopper_core, hopper_runtime, hopper_schema, hopper_solana};
385}
386
387/// Optional Metaplex Token Metadata builders. Behind `--features metaplex`.
388/// Reach for this via `hopper::hopper_metaplex::CreateMetadataAccountV3`,
389/// `CreateMasterEditionV3`, `UpdateMetadataAccountV2`, plus the
390/// `metadata_pda` / `master_edition_pda` PDA helpers.
391#[cfg(feature = "metaplex")]
392pub use hopper_metaplex;
393
394/// Small utilities. Re-exported at the crate root so `use hopper::utils::hint::likely;`
395/// just works.
396pub use hopper_runtime::utils;
397
398// Re-export macros at the crate root
399pub use hopper_core::hopper_dispatch;
400pub use hopper_macros::{
401    const_assert_pod, hopper_accounts, hopper_assert_compatible, hopper_assert_fingerprint,
402    hopper_check, hopper_close, hopper_error, hopper_init, hopper_interface, hopper_invariant,
403    hopper_layout, hopper_manifest, hopper_register_discs, hopper_require, hopper_segment,
404    hopper_validate, hopper_verify_pda, hopper_virtual,
405};
406
407// Audit I4: schema-epoch migration chain composition. `#[macro_export]`
408// macros are always anchored at the defining crate's root, so the
409// user-facing path `hopper::layout_migrations!` requires an explicit
410// re-export here.
411pub use hopper_runtime::layout_migrations;
412
413/// Destructuring sugar for raw-dispatch handlers.
414///
415/// Replaces the common pattern:
416///
417/// ```ignore
418/// let [user, vault, system_program, ..] = accounts else {
419///     return Err(ProgramError::NotEnoughAccountKeys);
420/// };
421/// ```
422///
423/// with the tighter form:
424///
425/// ```ignore
426/// hopper_load!(accounts => [user, vault, system_program]);
427/// ```
428///
429/// The bindings are plain `&AccountView` references (not owned values),
430/// matching the destructuring pattern. A trailing `..` is accepted and
431/// discards any extra accounts. The macro bails with
432/// `ProgramError::NotEnoughAccountKeys` when the slice is too short,
433/// mirroring Hopper's existing idiom.
434///
435/// Use this in the raw-dispatch authoring style (no `#[hopper::context]`).
436/// The proc-macro context already binds accounts by name, so this is only
437/// useful when you are working with `&[AccountView]` directly - typically
438/// inside `fn process_instruction(_, accounts: &[AccountView], _)` before
439/// routing to per-variant handlers.
440///
441/// ## Examples
442///
443/// ```ignore
444/// fn process_deposit(
445///     program_id: &Address,
446///     accounts: &[AccountView],
447///     data: &[u8],
448/// ) -> ProgramResult {
449///     hopper_load!(accounts => [user, vault, system_program]);
450///     user.require_signer()?;
451///     vault.require_writable()?;
452///     // ... rest of handler ...
453///     Ok(())
454/// }
455/// ```
456///
457/// With a trailing rest pattern (accept more accounts, ignore them):
458///
459/// ```ignore
460/// hopper_load!(accounts => [user, vault, ..]);
461/// ```
462///
463/// The trailing `..` is redundant with the default behaviour (the macro
464/// always accepts more accounts than declared) but is supported for
465/// stylistic parity with the native Rust slice pattern.
466#[macro_export]
467macro_rules! hopper_load {
468    ( $slice:expr => [ $($binding:ident),+ $(, ..)? $(,)? ] ) => {
469        let [ $($binding,)+ .. ] = $slice else {
470            return ::core::result::Result::Err(
471                $crate::hopper_runtime::error::ProgramError::NotEnoughAccountKeys,
472            );
473        };
474    };
475}
476
477/// Declare a dynamic-tail payload with bounded dynamic fields.
478///
479/// The generated type implements `TailCodec`, so it can be used directly with
480/// `#[hopper::state(dynamic_tail = MyTail)]`. Fields are encoded in declaration
481/// order. Use `HopperString<N>` for bounded UTF-8 strings and
482/// `HopperVec<T, N>` for bounded element lists. The longer
483/// `BoundedString<N>` and `BoundedVec<T, N>` names remain available when a
484/// protocol wants the storage tradeoff to be explicit in source. For
485/// Quasar-shaped account syntax, prefer `#[hopper::dynamic_account]`; use this
486/// macro when you want an explicit custom `TailCodec` payload.
487///
488/// ```ignore
489/// hopper::hopper_dynamic_tail! {
490///     pub struct MultisigTail {
491///         label: HopperString<32>,
492///         signers: HopperVec<Address, 10>,
493///     }
494/// }
495/// ```
496#[macro_export]
497macro_rules! hopper_dynamic_tail {
498    (
499        $(#[$meta:meta])*
500        $vis:vis struct $name:ident {
501            $( $field:ident : $ty:ty ),* $(,)?
502        }
503    ) => {
504        $(#[$meta])*
505        #[derive(Clone, Copy, Default)]
506        $vis struct $name {
507            $( pub $field: $ty, )*
508        }
509
510        impl $crate::__runtime::TailCodec for $name {
511            const MAX_ENCODED_LEN: usize = 0 $(+ <$ty as $crate::__runtime::TailCodec>::MAX_ENCODED_LEN)*;
512
513            #[inline]
514            fn encode(
515                &self,
516                out: &mut [u8],
517            ) -> ::core::result::Result<usize, $crate::__runtime::ProgramError> {
518                let mut cursor = 0usize;
519                $(
520                    let written = <$ty as $crate::__runtime::TailCodec>::encode(
521                        &self.$field,
522                        &mut out[cursor..],
523                    )?;
524                    cursor = cursor
525                        .checked_add(written)
526                        .ok_or($crate::__runtime::ProgramError::AccountDataTooSmall)?;
527                )*
528                Ok(cursor)
529            }
530
531            #[inline]
532            fn decode(
533                input: &[u8],
534            ) -> ::core::result::Result<(Self, usize), $crate::__runtime::ProgramError> {
535                let mut cursor = 0usize;
536                $(
537                    let ($field, consumed) = <$ty as $crate::__runtime::TailCodec>::decode(
538                        &input[cursor..],
539                    )?;
540                    cursor = cursor
541                        .checked_add(consumed)
542                        .ok_or($crate::__runtime::ProgramError::InvalidAccountData)?;
543                )*
544                Ok((Self { $( $field, )* }, cursor))
545            }
546        }
547    };
548}
549
550#[doc(hidden)]
551#[macro_export]
552macro_rules! __hopper_dynamic_fields_tail {
553    (@emit [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]) => {
554        $crate::hopper_dynamic_tail! {
555            $($meta)*
556            $vis struct $name {
557                $($fields)*
558            }
559        }
560    };
561
562    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]) => {
563        $crate::__hopper_dynamic_fields_tail!(@emit [$($meta)*] [$vis] [$name] [$($fields)*]);
564    };
565    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*] ,) => {
566        $crate::__hopper_dynamic_fields_tail!(@emit [$($meta)*] [$vis] [$name] [$($fields)*]);
567    };
568
569    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]
570        $field:ident : string < $cap:literal >, $($rest:tt)+
571    ) => {
572        $crate::__hopper_dynamic_fields_tail!(@parse [$($meta)*] [$vis] [$name]
573            [$($fields)* $field: $crate::__runtime::HopperString<$cap>,]
574            $($rest)+
575        );
576    };
577    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]
578        $field:ident : string < $cap:literal > $(,)?
579    ) => {
580        $crate::__hopper_dynamic_fields_tail!(@emit [$($meta)*] [$vis] [$name]
581            [$($fields)* $field: $crate::__runtime::HopperString<$cap>,]
582        );
583    };
584
585    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]
586        $field:ident : vec < $ty:ty, $cap:literal >, $($rest:tt)+
587    ) => {
588        $crate::__hopper_dynamic_fields_tail!(@parse [$($meta)*] [$vis] [$name]
589            [$($fields)* $field: $crate::__runtime::HopperVec<$ty, $cap>,]
590            $($rest)+
591        );
592    };
593    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]
594        $field:ident : vec < $ty:ty, $cap:literal > $(,)?
595    ) => {
596        $crate::__hopper_dynamic_fields_tail!(@emit [$($meta)*] [$vis] [$name]
597            [$($fields)* $field: $crate::__runtime::HopperVec<$ty, $cap>,]
598        );
599    };
600
601    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]
602        $field:ident : $ty:ty, $($rest:tt)+
603    ) => {
604        $crate::__hopper_dynamic_fields_tail!(@parse [$($meta)*] [$vis] [$name]
605            [$($fields)* $field: $ty,]
606            $($rest)+
607        );
608    };
609    (@parse [$($meta:tt)*] [$vis:vis] [$name:ident] [$($fields:tt)*]
610        $field:ident : $ty:ty $(,)?
611    ) => {
612        $crate::__hopper_dynamic_fields_tail!(@emit [$($meta)*] [$vis] [$name]
613            [$($fields)* $field: $ty,]
614        );
615    };
616}
617
618/// Explicit sugar for declaring bounded dynamic-tail fields.
619///
620/// This is a small front end over `hopper_dynamic_tail!`. It keeps Hopper's
621/// explicit fixed-body plus encoded-tail model, while letting ported programs
622/// spell custom tail fields as `string<N>` and `vec<T, N>`. For a full account
623/// declaration with inline tail fields, use `#[hopper::dynamic_account]`.
624///
625/// ```ignore
626/// hopper::hopper_dynamic_fields! {
627///     pub struct MultisigTail {
628///         label: string<32>,
629///         signers: vec<Address, 10>,
630///         nonce: u32,
631///     }
632/// }
633/// ```
634#[macro_export]
635macro_rules! hopper_dynamic_fields {
636    (
637        $(#[$meta:meta])*
638        $vis:vis struct $name:ident {
639            $($body:tt)*
640        }
641    ) => {
642        $crate::__hopper_dynamic_fields_tail!(@parse [$(#[$meta])*] [$vis] [$name] [] $($body)*);
643    };
644}
645
646// Ergonomic guard macros (the "winning architecture" design's
647// Jiminy-replacement safety layer). All are `#[macro_export]` from
648// hopper_runtime and are re-exported here so programs see them at
649// the top-level `hopper::*` path without needing to reach through
650// `hopper_runtime::`.
651pub use hopper_runtime::{
652    address, err, error, fast_entrypoint, hopper_emit_cpi, hopper_entrypoint,
653    hopper_fast_entrypoint, hopper_lazy_entrypoint, hopper_log, hopper_unsafe_region,
654    lazy_entrypoint, msg, no_allocator, nostd_panic_handler, program_entrypoint, require,
655    require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq, require_lt,
656    require_lte, require_neq,
657};
658
659/// Declare a bounded multi-layout interface account resolver.
660///
661/// Use this when one account slot may hold one of several Hopper-header layouts
662/// owned by the same interface program set, such as a migration reader that
663/// accepts `VaultV1` or `VaultV2`.
664///
665/// ```ignore
666/// hopper::interface_account_set! {
667///     pub struct AnyVaultAccount: VaultPrograms;
668///     pub enum AnyVault {
669///         V1(VaultV1),
670///         V2(VaultV2),
671///     }
672/// }
673/// ```
674#[macro_export]
675macro_rules! interface_account_set {
676    (
677        $(#[$marker_meta:meta])*
678        $vis:vis struct $marker:ident : $interface:ty;
679        $(#[$resolved_meta:meta])*
680        $enum_vis:vis enum $resolved:ident {
681            $($variant:ident($layout:ty)),+ $(,)?
682        }
683    ) => {
684        $(#[$marker_meta])*
685        #[derive(Clone, Copy, Debug, Default)]
686        $vis struct $marker;
687
688        impl $crate::__runtime::FieldMap for $marker {
689            const FIELDS: &'static [$crate::__runtime::FieldInfo] = &[];
690        }
691
692        impl $crate::__runtime::LayoutContract for $marker {
693            const DISC: u8 = 0;
694            const VERSION: u8 = 0;
695            const LAYOUT_ID: [u8; 8] = [0; 8];
696            const SIZE: usize = $crate::__runtime::HopperHeader::SIZE;
697        }
698
699        impl $crate::__runtime::InterfaceAccountLayout for $marker {
700            type Interface = $interface;
701
702            #[inline]
703            fn validate_interface_account(
704                view: &$crate::__runtime::AccountView,
705            ) -> ::core::result::Result<(), $crate::__runtime::ProgramError> {
706                let data = view.try_borrow()?;
707                let info = $crate::__runtime::LayoutInfo::from_data(&data)
708                    .ok_or($crate::__runtime::ProgramError::AccountDataTooSmall)?;
709                if false $(|| info.matches::<$layout>())+ {
710                    Ok(())
711                } else {
712                    Err($crate::__runtime::ProgramError::InvalidAccountData)
713                }
714            }
715        }
716
717        $(#[$resolved_meta])*
718        $enum_vis enum $resolved<'a> {
719            $($variant($crate::__runtime::Ref<'a, $layout>)),+
720        }
721
722        impl $crate::__runtime::InterfaceAccountResolve for $marker {
723            type Resolved<'a> = $resolved<'a>;
724
725            #[inline]
726            fn resolve<'a>(
727                view: &'a $crate::__runtime::AccountView,
728            ) -> ::core::result::Result<Self::Resolved<'a>, $crate::__runtime::ProgramError> {
729                let info = {
730                    let data = view.try_borrow()?;
731                    $crate::__runtime::LayoutInfo::from_data(&data)
732                        .ok_or($crate::__runtime::ProgramError::AccountDataTooSmall)?
733                };
734                $(
735                    if info.matches::<$layout>() {
736                        return Ok($resolved::$variant(view.load_cross_program::<$layout>()?));
737                    }
738                )+
739                Err($crate::__runtime::ProgramError::InvalidAccountData)
740            }
741        }
742    };
743}
744
745/// Generate the small runtime bridge for a `#[program]` module.
746///
747/// This keeps first-touch programs focused on `#[program]`, `Ctx<T>`, and
748/// `ctx.accounts.*` while still emitting the audited Hopper entrypoint under
749/// the hood.
750///
751/// ```ignore
752/// #[program]
753/// mod counter_program {
754///     // handlers
755/// }
756///
757/// hopper::program_dispatch!(counter_program);
758/// ```
759#[macro_export]
760macro_rules! program_dispatch {
761    ($program_mod:ident) => {
762        #[cfg(target_os = "solana")]
763        $crate::program_entrypoint!(__hopper_process_instruction);
764
765        #[doc(hidden)]
766        fn __hopper_process_instruction(
767            program_id: &$crate::__runtime::Address,
768            accounts: &[$crate::__runtime::AccountView],
769            instruction_data: &[u8],
770        ) -> ::core::result::Result<(), $crate::__runtime::ProgramError> {
771            let mut ctx = $crate::prelude::Context::new(program_id, accounts, instruction_data);
772            $program_mod::process_instruction(&mut ctx)
773        }
774    };
775}
776
777// Optional proc macro re-exports (enabled with `proc-macros` feature)
778#[cfg(feature = "proc-macros")]
779pub use hopper_macros_proc::{
780    account, accounts, args, constant, context, crank, declare_program, dynamic, dynamic_account,
781    error as error_code, event, hopper_args, hopper_constant, hopper_context, hopper_crank,
782    hopper_dynamic, hopper_dynamic_account, hopper_event, hopper_migrate, hopper_pod,
783    hopper_program, hopper_state, migrate, pod, program, state, Accounts, HopperInitSpace,
784};
785
786// Private re-export for generated code to reference runtime types
787#[doc(hidden)]
788pub mod __runtime {
789    pub use hopper_runtime::{
790        apply_pending_migrations, borrow_address_slice, borrow_bounded_str, read_tail,
791        read_tail_len, tail_capacity, tail_payload, write_tail, Account, AccountLayout,
792        AccountView, Address, BoundedString, BoundedVec, Context, FieldInfo, FieldMap,
793        HopperHeader, HopperInstructionPolicy, HopperProgramPolicy, HopperSigner, HopperString,
794        HopperVec, InitAccount, InstructionAccount, InstructionView, Interface, InterfaceAccount,
795        InterfaceAccountLayout, InterfaceAccountResolve, InterfaceSpec, LayoutContract, LayoutInfo,
796        LayoutMigration, MigrationEdge, Pod, Program, ProgramError, ProgramId, Ref, RefMut, SegRef,
797        SegRefMut, SegmentLease, SystemAccount, SystemId, TailCodec, TailElement, UncheckedAccount,
798    };
799
800    // Crank marker type plus dynamic-CPI builder, emitted by
801    // `#[hopper::crank]` and by hand-written programs that need
802    // variable-length CPIs respectively. Exposed through this
803    // doc-hidden module so user code never reaches into
804    // `hopper_runtime::*` directly.
805    pub use hopper_runtime::crank::CrankMarker;
806    pub use hopper_runtime::dyn_cpi::DynCpi;
807
808    // `#[hopper::state]` and `#[hopper::pod]` emit bytemuck-backed
809    // proofs through this path so user code never needs a direct
810    // bytemuck dependency. Gated on the native backend because that's
811    // where the bytemuck re-export lives.
812    #[cfg(feature = "hopper-native-backend")]
813    pub use hopper_runtime::__hopper_native;
814
815    // Audit final-API Step 5 seal. Doc-hidden re-export of the
816    // sealed-trait module so macros can emit
817    // `unsafe impl ::hopper::__runtime::__sealed::HopperZeroCopySealed for Foo {}`
818    // without the user ever naming the private module.
819    pub use hopper_runtime::__sealed;
820
821    // Re-export `hopper_runtime::token` so `#[hopper::context]` can
822    // emit `::hopper::__runtime::token::require_token_mint(...)`
823    // without dragging the user into a direct `hopper_runtime`
824    // dependency. The helpers (require_token_mint / require_mint_*
825    // / require_token_authority) are read-only account-byte
826    // preconditions used to lower Anchor's `token::mint`,
827    // `mint::authority`, etc. constraints to a single inline check.
828    pub use hopper_runtime::token;
829}