Skip to main content

AccountView

Struct AccountView 

Source
pub struct AccountView { /* private fields */ }
Expand description

Zero-copy view over a Solana account.

AccountView is the single canonical type for account access in Hopper programs. It wraps whatever backend is active and exposes a Hopper-owned API surface.

The #[repr(transparent)] layout guarantees that &[backend::AccountView] can be safely reinterpreted as &[AccountView] at the entrypoint boundary with zero conversion cost.

Implementations§

Source§

impl AccountView

Source

pub fn address(&self) -> &Address

The account’s public key.

Source

pub unsafe fn owner(&self) -> &Address

The owning program’s address.

§Safety

The returned reference is invalidated if the account is assigned to a new owner. The caller must ensure no concurrent mutation.

Source

pub fn read_owner(&self) -> Address

Read the owner address as a copy (safe, no aliasing hazard).

Source

pub fn owned_by(&self, program: &Address) -> bool

Whether this account is owned by the given program.

Source

pub fn is_signer(&self) -> bool

Whether this account signed the transaction.

Source

pub fn is_writable(&self) -> bool

Whether this account is writable in the transaction.

Source

pub fn executable(&self) -> bool

Whether this account contains an executable program.

Source

pub fn data_len(&self) -> usize

Current data length in bytes.

Source

pub fn lamports(&self) -> u64

Current lamport balance.

Source

pub fn is_data_empty(&self) -> bool

Whether the account data is empty.

Source

pub fn set_lamports(&self, lamports: u64)

Set the lamport balance.

Source

pub fn try_borrow(&self) -> Result<Ref<'_, [u8]>, ProgramError>

Try to obtain a shared borrow of the account data.

Source

pub fn try_borrow_mut(&self) -> Result<RefMut<'_, [u8]>, ProgramError>

Try to obtain an exclusive (mutable) borrow of the account data.

Source

pub fn segment_ref<'a, T: Pod>( &'a self, borrows: &'a mut SegmentBorrowRegistry, abs_offset: u32, size: u32, ) -> Result<SegRef<'a, T>, ProgramError>

Project a typed segment from this account with segment-level borrow tracking.

The runtime validates the requested byte range, registers a leased read borrow in the provided instruction-scoped registry, and returns a SegRef<T> that releases the lease on drop. This replaces the pre-audit “instruction-sticky” behaviour: the registry entry is now tied to the returned guard’s lifetime, so sequential patterns like let x = segment_ref…; drop(x); let y = segment_ref…; work exactly the way Rust callers expect.

On the native backend (Solana), the inner Ref<T> uses the flat {ptr, state} representation, no dummy slice guard, no intermediate Ref<[u8]>.

The explicit 'a lifetime binds the returned SegRef<'a, T> to the shorter of &self (the account) and &mut borrows (the registry). Either outliving the other would let the guard dangle.

Source

pub fn segment_mut<'a, T: Pod>( &'a self, borrows: &'a mut SegmentBorrowRegistry, abs_offset: u32, size: u32, ) -> Result<SegRefMut<'a, T>, ProgramError>

Project a mutable typed segment. Mirror of [segment_ref]; the returned SegRefMut<T> carries both the account-level exclusive borrow guard and the segment-registry lease, so dropping it is a full release, no lingering entries.

Source

pub fn segment_ref_const<'a, T: Pod>( &'a self, borrows: &'a mut SegmentBorrowRegistry, segment: Segment, ) -> Result<SegRef<'a, T>, ProgramError>

Project a typed segment described by a compile-time [Segment].

This is the “const-driven” access form the Hopper design demands: the offset and size come from a const SEG: Segment = ...; declaration generated by #[hopper::state] or written by hand, so the call collapses to a single ptr + const_offset add on Solana SBF. No runtime string lookup, no dynamic map, no search.

segment.offset is the absolute offset from the start of account data (i.e. past the Hopper header already folded in). Construct it via Segment::new(offset, size) or Segment::body(body_offset, size), the latter adds HopperHeader::SIZE for you.

const BALANCE: Segment = Segment::body(0, 8);
let mut balance = vault.segment_ref_const::<u64>(&mut borrows, BALANCE)?;
Source

pub fn segment_mut_const<'a, T: Pod>( &'a self, borrows: &'a mut SegmentBorrowRegistry, segment: Segment, ) -> Result<SegRefMut<'a, T>, ProgramError>

Mutable const-Segment access. See [segment_ref_const] for the contract, this is the exclusive variant.

Source

pub fn segment_ref_typed<'a, T: Pod, const OFFSET: u32>( &'a self, borrows: &'a mut SegmentBorrowRegistry, _segment: TypedSegment<T, OFFSET>, ) -> Result<SegRef<'a, T>, ProgramError>

Project a typed segment described by a [TypedSegment].

This is the tightest form of segment access Hopper exposes: both the type T and the offset are compile-time constants baked into the [TypedSegment] marker, so the call collapses to a single ptr + literal_offset add with a literal size in the bounds check. The marker argument is a zero-sized token, free to pass around.

const BALANCE: TypedSegment<WireU64, { HopperHeader::SIZE as u32 }>
    = TypedSegment::new();
let bal = vault.segment_ref_typed(&mut borrows, BALANCE)?;
Source

pub fn segment_mut_typed<'a, T: Pod, const OFFSET: u32>( &'a self, borrows: &'a mut SegmentBorrowRegistry, _segment: TypedSegment<T, OFFSET>, ) -> Result<SegRefMut<'a, T>, ProgramError>

Mutable typed-segment access. See [segment_ref_typed] for the contract, this is the exclusive variant.

Source

pub fn load<T: LayoutContract>(&self) -> Result<Ref<'_, T>, ProgramError>

Load a typed layout after validating the account header.

This is the canonical “validate then project” path:

  1. Check disc, version, and layout_id match T
  2. Verify data length >= T::SIZE
  3. Return zero-copy reference into account data

The returned reference begins at T::TYPE_OFFSET. Body-only layouts project past the Hopper header; header-inclusive layouts project the full account struct from byte 0.

§Example
let vault = account.load::<Vault>()?;
Source

pub fn load_mut<T: LayoutContract>(&self) -> Result<RefMut<'_, T>, ProgramError>

Load a mutable typed layout after validating the account header.

Same as load() but provides a mutable reference for in-place state updates. Changes write directly to account data.

§Example
let mut vault = account.load_mut::<Vault>()?;
vault.balance = vault.balance.checked_add(amount)?;
Source

pub unsafe fn raw_ref<T: Pod>(&self) -> Result<Ref<'_, T>, ProgramError>

Explicit raw typed read of the account buffer.

This bypasses Hopper layout validation and segment tracking, but it still respects the account-level borrow rules enforced by try_borrow().

Source

pub unsafe fn raw_mut<T: Pod>(&self) -> Result<RefMut<'_, T>, ProgramError>

Explicit raw typed write of the account buffer.

This bypasses Hopper layout validation and segment tracking, but it still enforces writability and the account-level exclusive borrow rules.

Source

pub fn load_cross_program<T: LayoutContract>( &self, ) -> Result<Ref<'_, T>, ProgramError>

Load a cross-program layout without ownership checks.

Validates wire format (disc + layout_id + size) but does not check that the account is owned by this program. Use for cross-program reads where the account is owned by another program and you need a typed, zero-copy view of its data.

The layout_id check ensures ABI compatibility: if the other program changes its layout, this will fail rather than silently misinterpret.

§Example
let other_vault = foreign_account.load_cross_program::<OtherVault>()?;
Source

pub fn layout_info(&self) -> Option<LayoutInfo>

Read runtime layout metadata from this account’s header.

Returns None if the account data is too short for a Hopper header. This is useful for runtime inspection, manager tooling, and schema checking when the concrete layout type is not known at compile time.

Source

pub fn fields<T: LayoutContract>() -> &'static [FieldInfo]

Compile-time field metadata for a layout contract.

Source

pub fn field<T: LayoutContract>(name: &str) -> Option<&'static FieldInfo>

Find a compile-time field descriptor by name.

This is a tooling/inspection helper that delegates to FieldMap::field_by_name. It performs a const-driven linear scan over T::FIELDS and is not intended for hot-path use - programs should reach for the const offsets emitted by #[hopper::state] instead.

Source

pub fn extension_range<T: LayoutContract>( &self, ) -> Result<Range<usize>, ProgramError>

Return the extension-region byte range for a layout that declares one.

Callers can apply the returned range to a borrowed data slice when they want to inspect or mutate extension bytes explicitly.

Source

pub fn extension_bytes<T: LayoutContract>( &self, ) -> Result<Ref<'_, [u8]>, ProgramError>

Borrow the extension/tail region declared by a layout contract.

Source

pub fn extension_bytes_mut<T: LayoutContract>( &self, ) -> Result<RefMut<'_, [u8]>, ProgramError>

Mutably borrow the extension/tail region declared by a layout contract.

Source

pub fn init_layout<T: LayoutContract>(&self) -> ProgramResult

Initialize an account with the given layout contract header.

Writes the disc, version, layout_id, and zeroes flags/reserved. Call this when creating a new account before writing field data.

Source

pub fn require_signer(&self) -> ProgramResult

Validate that this account is a signer.

Source

pub fn require_writable(&self) -> ProgramResult

Validate that this account is writable.

Source

pub fn require_owned_by(&self, program: &Address) -> ProgramResult

Validate that this account is owned by the given program.

Source

pub fn require_payer(&self) -> ProgramResult

Validate signer + writable (common “payer” pattern).

Source

pub fn check_signer(&self) -> Result<&Self, ProgramError>

Chainable signer check.

Source

pub fn check_writable(&self) -> Result<&Self, ProgramError>

Chainable writable check.

Source

pub fn check_owned_by(&self, program: &Address) -> Result<&Self, ProgramError>

Chainable ownership check.

Source

pub fn check_disc(&self, expected: u8) -> Result<&Self, ProgramError>

Chainable discriminator check.

Source

pub fn check_has_data(&self) -> Result<&Self, ProgramError>

Chainable non-empty data check.

Source

pub fn check_executable(&self) -> Result<&Self, ProgramError>

Chainable executable check.

Source

pub fn check_address(&self, expected: &Address) -> Result<&Self, ProgramError>

Chainable address check.

Source

pub fn check_data_len(&self, min_len: usize) -> Result<&Self, ProgramError>

Chainable minimum data length check.

Source

pub fn check_version(&self, expected: u8) -> Result<&Self, ProgramError>

Chainable version check.

Source

pub fn check_layout<T: LayoutContract>(&self) -> Result<&Self, ProgramError>

Chainable full layout contract check (disc + version + layout_id + size).

Source

pub fn disc(&self) -> u8

Read the Hopper account discriminator (first byte of data).

Source

pub fn version(&self) -> u8

Read the Hopper account version (second byte of data).

Source

pub fn layout_id(&self) -> Option<&[u8; 8]>

Read the 8-byte layout_id from the Hopper account header (bytes 4..12).

Source

pub fn require_disc(&self, expected: u8) -> ProgramResult

Verify that this account has the given discriminator.

Source

pub fn flags(&self) -> u8

Pack the account’s boolean flags into a single byte.

Bit layout: bit 0 = signer, bit 1 = writable, bit 2 = executable, bit 3 = has data.

Source

pub fn expect_flags(&self, required: u8) -> ProgramResult

Check that the account’s flags contain all required bits.

Source

pub fn resize(&self, new_len: usize) -> ProgramResult

Resize the account data.

Source

pub unsafe fn assign(&self, new_owner: &Address)

Assign a new owner.

§Safety

The caller must ensure the account is writable and that ownership transfer is authorized.

Source

pub fn close(&self) -> ProgramResult

Close the account: zero lamports and data.

Source

pub fn close_to( &self, destination: &AccountView, program_id: &Address, ) -> ProgramResult

Close the account, transferring remaining lamports to destination.

Idiomatic Solana close pattern: move all lamports to the destination account, then zero this account’s data so the runtime garbage-collects it at the end of the transaction.

§Preconditions (enforced)

Per Solana’s account modification rules (only the owning program can debit lamports or mutate data on a writable account), this method requires:

  • self must be writable, otherwise the runtime will reject the commit anyway, but we fail fast here rather than let the transaction progress through an invalid state.
  • self must be owned by program_id, the program that is executing this instruction. Without this check the safe API would silently encourage patterns that only Solana’s post-instruction verifier catches.
  • destination must be writable, receiving lamports requires write permission on the credit side.

This is the Hopper Safety Audit’s recommended tightening: the pre-audit version mutated lamports and zeroed data without checking either side, relying on the runtime to reject the transaction later. The audit flagged that as “encouraging patterns that will only be rejected later”, the safe API should surface the violation at call time.

Source

pub fn close_to_unchecked(&self, destination: &AccountView) -> ProgramResult

Unchecked variant of [close_to].

Retained for the rare caller that has already verified the preconditions (e.g. inside a validated #[hopper::context] binding). Does not check writable or owner, so only use it when the preconditions are guaranteed by the surrounding code.

Source

pub fn check_borrow(&self) -> Result<(), ProgramError>

Check that the account can be shared-borrowed.

Source

pub fn check_borrow_mut(&self) -> Result<(), ProgramError>

Check that the account can be exclusively borrowed.

Source

pub unsafe fn borrow_unchecked(&self) -> &[u8]

Borrow account data without tracking.

§Safety

The caller must ensure no mutable borrow is active.

Source

pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8]

Mutably borrow account data without tracking.

§Safety

The caller must ensure no other borrows are active.

Source

pub unsafe fn resize_unchecked(&self, new_len: usize)

Resize without bounds checking.

§Safety

The caller must guarantee the new length is within the permitted increase.

Source

pub unsafe fn close_unchecked(&self)

Close without borrow checks.

§Safety

The caller must ensure no active borrows exist.

Trait Implementations§

Source§

impl Clone for AccountView

Source§

fn clone(&self) -> AccountView

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AccountView

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'a> From<&'a AccountView> for CpiAccount<'a>

Available on crate feature hopper-native-backend only.
Source§

fn from(view: &'a AccountView) -> Self

Converts to this type from the input type.
Source§

impl<'a> From<&'a AccountView> for InstructionAccount<'a>

Source§

fn from(view: &'a AccountView) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for AccountView

Source§

fn eq(&self, other: &AccountView) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for AccountView

Source§

impl Send for AccountView

Source§

impl StructuralPartialEq for AccountView

Source§

impl Sync for AccountView

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.