pub struct Context<'a> {
pub program_id: &'a Address,
pub instruction_data: &'a [u8],
/* private fields */
}Expand description
Execution context for a Hopper instruction handler.
Wraps the program_id, account slice, and instruction data into a single object with structured access patterns.
§Authored flow
pub fn deposit(ctx: &Context, amount: u64) -> ProgramResult {
let authority = ctx.account(0)?;
let vault = ctx.account(1)?;
authority.require_signer()?;
vault.require_writable()?;
vault.check_disc(1)?;
let mut state = vault.load_mut::<VaultState>()?;
state.balance = state.balance.checked_add(amount).ok_or(ProgramError::ArithmeticOverflow)?;
Ok(())
}Fields§
§program_id: &'a AddressThe program’s own address.
instruction_data: &'a [u8]Raw instruction data (past the discriminator byte, if applicable).
Implementations§
Source§impl<'a> Context<'a>
impl<'a> Context<'a>
Sourcepub fn new(
program_id: &'a Address,
accounts: &'a [AccountView<'a>],
instruction_data: &'a [u8],
) -> Self
pub fn new( program_id: &'a Address, accounts: &'a [AccountView<'a>], instruction_data: &'a [u8], ) -> Self
Create a new context from the entrypoint parameters.
Sourcepub fn set_write_policy(&mut self, policy: &'static WritePolicy)
pub fn set_write_policy(&mut self, policy: &'static WritePolicy)
Install a declared write policy (write-policy enforcement).
From this point on, every Context-mediated write acquire,
segment writes, whole-account load_mut, and the raw escape
hatches raw_mut / as_mut_ptr, must be fully contained in one
of the policy’s declared ranges or it fails with
Custom(0xD000 | account_index) before any byte is written.
Whole-account paths claim [0, data_len), so a policy that
declares only field ranges forces handlers onto the declared
segment accessors.
#[hopper::context(strict_writes)] compiles the context’s
mut / mut(seg, ...) declarations into a static policy
and installs it during bind(); calling this by hand is the raw
equivalent. Direct substrate access on the raw
AccountView (via
account) is outside the governed surface, like
every other documented escape hatch.
Sourcepub fn set_parametric_write_policy(
&mut self,
policy: &'static WritePolicy,
args: &[u32],
) -> ProgramResult
pub fn set_parametric_write_policy( &mut self, policy: &'static WritePolicy, args: &[u32], ) -> ProgramResult
Install a declared write policy and bind the invocation values used by
its ParametricWriteRanges.
Sourcepub fn write_policy(&self) -> Option<&'static WritePolicy>
pub fn write_policy(&self) -> Option<&'static WritePolicy>
The installed write policy, if any.
Return the first byte of a recorded write touch that falls outside the installed invocation-resolved policy.
This is the audit counterpart to the acquire-time gate. It checks the union of static ranges and selected parametric cells because the touch ledger may coalesce adjacent authorized acquires. With no installed policy, or an account index that cannot be represented on the wire, it fails closed by returning the touch’s first byte.
Sourcepub fn program_id(&self) -> &Address
pub fn program_id(&self) -> &Address
Program ID.
Sourcepub fn instruction_data(&self) -> &'a [u8]
pub fn instruction_data(&self) -> &'a [u8]
Raw instruction data.
Sourcepub fn account(&self, index: usize) -> Result<&'a AccountView<'a>, ProgramError>
pub fn account(&self, index: usize) -> Result<&'a AccountView<'a>, ProgramError>
Get an account by index.
Sourcepub fn account_mut(
&self,
index: usize,
) -> Result<&'a AccountView<'a>, ProgramError>
pub fn account_mut( &self, index: usize, ) -> Result<&'a AccountView<'a>, ProgramError>
Get an account by index (mutation-intent variant).
Functionally identical to account() since AccountView uses
interior mutability for data access (overlay_mut, load_mut,
try_borrow_mut). The distinct name signals that the caller
intends to write through the returned reference.
Sourcepub fn num_accounts(&self) -> usize
pub fn num_accounts(&self) -> usize
Get the total number of accounts.
Sourcepub fn accounts(&self) -> &'a [AccountView<'a>]
pub fn accounts(&self) -> &'a [AccountView<'a>]
Get all accounts as a slice.
Sourcepub fn borrows(&self) -> &SegmentBorrowRegistry
pub fn borrows(&self) -> &SegmentBorrowRegistry
Access the instruction-scoped segment borrow registry.
Sourcepub fn borrows_mut(&mut self) -> &mut SegmentBorrowRegistry
pub fn borrows_mut(&mut self) -> &mut SegmentBorrowRegistry
Mutably access the instruction-scoped segment borrow registry.
Sourcepub fn audit_accounts(&self) -> AccountAudit<'a>
pub fn audit_accounts(&self) -> AccountAudit<'a>
Inspect the instruction account slice for duplicate aliases.
Sourcepub fn finish_with_touch_map(&self)
pub fn finish_with_touch_map(&self)
Zero-cost sibling of
finish_with_touch_map, compiled
when the touch-map feature is off.
Keeps the macro-generated opt-in epilogue call compiling on builds
that never enabled the touch-map machinery, and emits nothing.
This is what makes “opt-in present but feature off” produce no
sol_log_data record and pay no compute for it.
Sourcepub fn remaining_accounts(&self, from: usize) -> &'a [AccountView<'a>]
pub fn remaining_accounts(&self, from: usize) -> &'a [AccountView<'a>]
Get the remaining accounts starting at from.
NOTE (binary size): the slicing below goes through get(..), never
self.accounts[from..]. A range index LLVM cannot statically bound
emits slice_end_index_len_fail, which formats its arguments and
links Formatter::pad_integral, do_count_chars and the integer
Display impls, ~3.7 KiB of core::fmt, into every Hopper
program’s .text. These are #[inline(always)] hot-path helpers,
so one panicking index here taxes every program. Keep them get-based.
Sourcepub fn remaining_accounts_strict(&self, from: usize) -> RemainingAccounts<'a>
pub fn remaining_accounts_strict(&self, from: usize) -> RemainingAccounts<'a>
Get remaining accounts in strict duplicate-rejecting mode.
Sourcepub fn remaining_accounts_passthrough(
&self,
from: usize,
) -> RemainingAccounts<'a>
pub fn remaining_accounts_passthrough( &self, from: usize, ) -> RemainingAccounts<'a>
Get remaining accounts in duplicate-preserving passthrough mode.
Sourcepub fn remaining_accounts_typed(&self, from: usize) -> RemainingTyped<'a>
pub fn remaining_accounts_typed(&self, from: usize) -> RemainingTyped<'a>
Get remaining accounts in strict mode and bind a sequential typed parser.
Sourcepub fn remaining_accounts_lazy(&self, from: usize) -> RemainingLazy<'a>
pub fn remaining_accounts_lazy(&self, from: usize) -> RemainingLazy<'a>
Get remaining accounts in strict mode and bind a lazy indexed parser.
Sourcepub fn require_accounts(&self, n: usize) -> ProgramResult
pub fn require_accounts(&self, n: usize) -> ProgramResult
Require at least n accounts are present.
Sourcepub fn require_unique_accounts(&self) -> ProgramResult
pub fn require_unique_accounts(&self) -> ProgramResult
Require all account addresses to be unique.
Sourcepub fn require_unique_writable_accounts(&self) -> ProgramResult
pub fn require_unique_writable_accounts(&self) -> ProgramResult
Require that no duplicated account is writable in this instruction.
Sourcepub fn require_unique_signer_accounts(&self) -> ProgramResult
pub fn require_unique_signer_accounts(&self) -> ProgramResult
Require that no duplicated account is used as a signer role.
Sourcepub fn require_data_len(&self, n: usize) -> ProgramResult
pub fn require_data_len(&self, n: usize) -> ProgramResult
Require at least n bytes of instruction data.
Sourcepub fn load<T: LayoutContract + Pod>(
&self,
index: usize,
) -> Result<Ref<'_, T>, ProgramError>
pub fn load<T: LayoutContract + Pod>( &self, index: usize, ) -> Result<Ref<'_, T>, ProgramError>
Validate-and-load the full typed layout for an account.
This is the indexed shortcut for ctx.account(idx)?.load::<T>().
It’s the canonical “Tier A” access path: the runtime checks the
Hopper header, validates the data length, and projects the typed
view in one inlined call. no extra cost over the spelled-out form.
Sourcepub fn load_mut<T: LayoutContract + Pod>(
&mut self,
index: usize,
) -> Result<RefMut<'_, T>, ProgramError>
pub fn load_mut<T: LayoutContract + Pod>( &mut self, index: usize, ) -> Result<RefMut<'_, T>, ProgramError>
Validate-and-load a mutable typed layout for an account.
Indexed shortcut for ctx.account(idx)?.load_mut::<T>(). The
returned guard holds the account-level exclusive borrow until
it drops.
As a whole-account write borrow, this claims [0, data_len):
under an installed write policy it
requires a whole-account allowance (a plain mut declaration),
and with the touch-map feature it lands in the instruction
touch map as a full-account write record.
Sourcepub fn load_cross_program<T: LayoutContract + Pod>(
&self,
index: usize,
) -> Result<Ref<'_, T>, ProgramError>
pub fn load_cross_program<T: LayoutContract + Pod>( &self, index: usize, ) -> Result<Ref<'_, T>, ProgramError>
Cross-program load: validate ABI fingerprint without ownership check.
Use this when reading an account whose owner is another program but whose layout is published as a Hopper layout contract.
Sourcepub fn segment_ref<'b, T: Pod>(
&'b mut self,
index: usize,
abs_offset: u32,
) -> Result<SegRef<'b, T>, ProgramError>
pub fn segment_ref<'b, T: Pod>( &'b mut self, index: usize, abs_offset: u32, ) -> Result<SegRef<'b, T>, ProgramError>
Register a read borrow for a segment of an account and return a
SegRef<T> that releases both the account-level
byte guard and the segment registry lease on drop.
index is the account index. abs_offset is the absolute byte
offset within the account data (including header bytes).
§Type Safety
T must implement Pod (substrate-level “safe to overlay on
raw bytes” contract: every bit pattern valid, align-1, no
padding, no interior pointers). Segment borrow tracking
prevents conflicting write access to the same byte range for
the guard’s lifetime.
§Canonical path
Three variants exist for different offset sources:
| Variant | Use when |
|---|---|
segment_ref_typed (canonical) | Offset is a compile-time constant (the common case). The const OFFSET: u32 generic becomes an immediate in the pointer arithmetic. |
segment_ref_const | Offset comes from a runtime crate::Segment value (dispatching dynamically between named fields). |
segment_ref (this method) | Offset is fully dynamic (iterating segments in a loop, for example). |
#[hopper::context]-generated accessors default to the canonical
typed path; reach for the others only when the use case
genuinely needs a runtime offset.
Sourcepub fn split_segments_mut<'b, T: Pod, const N: usize>(
&'b mut self,
index: usize,
ranges: [(u32, u32); N],
) -> Result<SegmentsMut<'b, T, N>, ProgramError>
pub fn split_segments_mut<'b, T: Pod, const N: usize>( &'b mut self, index: usize, ranges: [(u32, u32); N], ) -> Result<SegmentsMut<'b, T, N>, ProgramError>
Borrow several disjoint typed sub-ranges of one account mutably at
the same time. See
AccountView::split_segments_mut.
let mut segs = ctx.split_segments_mut::<WireU64, 2>(
vault_idx, [(BALANCE_OFF, 8), (NONCE_OFF, 8)])?;
let [bal, nonce] = segs.all_mut();
bal.set(bal.get() + amount);
nonce.set(nonce.get() + 1);Sourcepub fn segment_mut<'b, T: Pod>(
&'b mut self,
index: usize,
abs_offset: u32,
) -> Result<SegRefMut<'b, T>, ProgramError>
pub fn segment_mut<'b, T: Pod>( &'b mut self, index: usize, abs_offset: u32, ) -> Result<SegRefMut<'b, T>, ProgramError>
Register a write borrow for a segment of an account.
Validates bounds, checks writable, and registers a leased
exclusive borrow, then returns a SegRefMut<T>
that releases on drop.
This primitive permits concurrent mutation of non-overlapping account regions. The lease model also permits sequential same-region borrows within one instruction.
Sourcepub fn tail_seq_mut<'b, T: SeqElement>(
&'b mut self,
index: usize,
body_end: u32,
) -> Result<SeqTailWrite<'b, T>, ProgramError>
pub fn tail_seq_mut<'b, T: SeqElement>( &'b mut self, index: usize, body_end: u32, ) -> Result<SeqTailWrite<'b, T>, ProgramError>
Acquire a growable Seq<T> tail for writing at body_end
(the layout’s TAIL_PREFIX_OFFSET), returning a
SeqTailWrite guard whose
seq_mut yields the O(1)
streaming cursor.
The tail region is [body_end, data_len), the whole account past
the fixed head. Under an installed write policy
this whole region must be granted (a mut(<seq_field>) declaration
compiles to an open-ended tail_from
range), so the fixed head stays protected. Exactly ONE segment
lease is registered, covering the entire tail region, NOT one per
element; so overlap detection and the touch map see a single
tail-region write record regardless of how many elements are
pushed.
Sourcepub fn tail_seq_ref<'b, T: SeqElement>(
&'b mut self,
index: usize,
body_end: u32,
) -> Result<SeqTailRead<'b, T>, ProgramError>
pub fn tail_seq_ref<'b, T: SeqElement>( &'b mut self, index: usize, body_end: u32, ) -> Result<SeqTailRead<'b, T>, ProgramError>
Acquire a Seq<T> tail for reading at body_end, returning a
SeqTailRead guard whose
seq yields the streaming read
cursor. Registers one shared tail-region lease (reads are not
gated by the write policy, but the lease still powers overlap
detection against concurrent writers).
Sourcepub fn segment_ref_const<'b, T: Pod>(
&'b mut self,
index: usize,
segment: Segment,
) -> Result<SegRef<'b, T>, ProgramError>
pub fn segment_ref_const<'b, T: Pod>( &'b mut self, index: usize, segment: Segment, ) -> Result<SegRef<'b, T>, ProgramError>
Const-driven segment read: pass a compile-time crate::Segment and the
account index. Lowers to the same pointer-plus-const-offset shape
as segment_ref but without the caller hand-rolling the offset +
size arguments.
Sourcepub fn segment_mut_const<'b, T: Pod>(
&'b mut self,
index: usize,
segment: Segment,
) -> Result<SegRefMut<'b, T>, ProgramError>
pub fn segment_mut_const<'b, T: Pod>( &'b mut self, index: usize, segment: Segment, ) -> Result<SegRefMut<'b, T>, ProgramError>
Const-driven exclusive segment access. Pair with
#[hopper::state] constants for zero-overhead field writes.
Sourcepub fn segment_ref_typed<'b, T: Pod, const OFFSET: u32>(
&'b mut self,
index: usize,
segment: TypedSegment<T, OFFSET>,
) -> Result<SegRef<'b, T>, ProgramError>
pub fn segment_ref_typed<'b, T: Pod, const OFFSET: u32>( &'b mut self, index: usize, segment: TypedSegment<T, OFFSET>, ) -> Result<SegRef<'b, T>, ProgramError>
Typed-segment read: the type and offset are both compile-time
constants, baked into a crate::TypedSegment zero-sized marker.
Sourcepub fn segment_mut_typed<'b, T: Pod, const OFFSET: u32>(
&'b mut self,
index: usize,
_segment: TypedSegment<T, OFFSET>,
) -> Result<SegRefMut<'b, T>, ProgramError>
pub fn segment_mut_typed<'b, T: Pod, const OFFSET: u32>( &'b mut self, index: usize, _segment: TypedSegment<T, OFFSET>, ) -> Result<SegRefMut<'b, T>, ProgramError>
Typed-segment write. Mirrors Self::segment_ref_typed for the
exclusive path.
Sourcepub unsafe fn raw_ref<T: Pod>(
&self,
index: usize,
) -> Result<Ref<'_, T>, ProgramError>
pub unsafe fn raw_ref<T: Pod>( &self, index: usize, ) -> Result<Ref<'_, T>, ProgramError>
Explicit unsafe whole-account typed read.
§Safety
Caller must uphold the invariants documented for this unsafe API before invoking it.
Sourcepub unsafe fn raw_mut<T: Pod>(
&self,
index: usize,
) -> Result<RefMut<'_, T>, ProgramError>
pub unsafe fn raw_mut<T: Pod>( &self, index: usize, ) -> Result<RefMut<'_, T>, ProgramError>
Explicit unsafe whole-account typed write.
§Safety
Caller must uphold the invariants documented for this unsafe API before invoking it.
Sourcepub unsafe fn raw_unchecked<T: Pod>(
&self,
index: usize,
) -> Result<RefMut<'_, T>, ProgramError>
pub unsafe fn raw_unchecked<T: Pod>( &self, index: usize, ) -> Result<RefMut<'_, T>, ProgramError>
Legacy alias for raw_mut.
Despite the name, this does not bypass borrow tracking: it
delegates to raw_mut, which routes through the checked
segment_mut(0, size_of::<T>()) path (bounds, writable, and
account-level exclusive borrow all enforced). The caller remains
responsible for using a type that matches the account bytes. For a
genuinely untracked pointer, use as_mut_ptr.
§Safety
Caller must uphold the invariants documented for this unsafe API before invoking it.
Sourcepub unsafe fn as_mut_ptr(&self, index: usize) -> Result<*mut u8, ProgramError>
pub unsafe fn as_mut_ptr(&self, index: usize) -> Result<*mut u8, ProgramError>
Canonical raw-pointer escape hatch to an account’s data buffer.
Returns a pointer to the first byte of accounts[index]’s data
region (after the runtime account header, before any Hopper
16-byte layout header). The pointer is valid for reads and
writes for the lifetime of the account view and carries no
borrow-tracking obligations. Dereferencing it is unsafe
because the caller takes over alias-safety responsibility
that the segment registry normally upholds.
This is the explicit power-user primitive the audit asks for:
safe code reaches for segment_ref_typed / segment_mut_typed
/ the generated ctx.<field>_segment_mut(...) accessors; raw
code drops to unsafe { ctx.as_mut_ptr(0)?.add(offset) as *mut T }.
§Safety
The caller must guarantee no aliasing mutable borrow is held
on the same account for the duration of any write through the
returned pointer. The returned pointer must be dereferenced
within the 'info lifetime of the account view; reading past
AccountView::data_len() is undefined behaviour.
Sourcepub fn as_ptr(&self, index: usize) -> Result<*const u8, ProgramError>
pub fn as_ptr(&self, index: usize) -> Result<*const u8, ProgramError>
Immutable sibling of as_mut_ptr. Returns a *const u8.
Shared-borrow checking still runs, so calling this while an
exclusive borrow is live on the same account fails with
AccountBorrowFailed. The return value is safe to obtain; the
caller only needs unsafe to dereference it.
Sourcepub fn read_data<T: ValuePod>(&self, offset: usize) -> Result<T, ProgramError>
pub fn read_data<T: ValuePod>(&self, offset: usize) -> Result<T, ProgramError>
Read instruction data as a typed value (unaligned, little-endian safe).
Reads size_of::<T>() bytes starting at offset via read_unaligned.
Caller must ensure T is a plain-old-data type where all bit patterns
are valid.
Sourcepub fn data_slice(
&self,
offset: usize,
len: usize,
) -> Result<&[u8], ProgramError>
pub fn data_slice( &self, offset: usize, len: usize, ) -> Result<&[u8], ProgramError>
Get a byte slice from instruction data.
Sourcepub fn instruction_tag(&self) -> Result<u8, ProgramError>
pub fn instruction_tag(&self) -> Result<u8, ProgramError>
Read the first byte of instruction data as an instruction tag.
Common pattern for byte-tag dispatch.