Skip to main content

hopper_core/accounts/
program_account.rs

1//! Generic program-owned account wrapper.
2//!
3//! Used for accounts owned by external programs (e.g. SPL Token accounts,
4//! Mint accounts). Validates owner and provides raw typed overlay access.
5
6use hopper_runtime::error::ProgramError;
7use hopper_runtime::{AccountView, Address};
8
9use crate::account::{FixedLayout, Pod, VerifiedAccount};
10use crate::check;
11
12/// A generic program-owned account.
13///
14/// Unlike `HopperAccount<T>` which validates against the executing program,
15/// `ProgramAccount<T>` validates against an arbitrary expected owner.
16/// Used for external program accounts (token accounts, mints, etc.).
17pub struct ProgramAccount<'a, T: Pod + FixedLayout> {
18    view: &'a AccountView,
19    _marker: core::marker::PhantomData<T>,
20}
21
22impl<'a, T: Pod + FixedLayout> ProgramAccount<'a, T> {
23    /// Construct from an AccountView, validating the owner.
24    #[inline]
25    pub fn from_account(
26        account: &'a AccountView,
27        expected_owner: &Address,
28    ) -> Result<Self, ProgramError> {
29        check::check_owner(account, expected_owner)?;
30        let data = account.try_borrow()?;
31        check::check_size(&data, core::mem::size_of::<T>())?;
32        Ok(Self {
33            view: account,
34            _marker: core::marker::PhantomData,
35        })
36    }
37
38    /// Construct without owner validation (caller's responsibility).
39    #[inline]
40    pub fn from_account_unchecked(account: &'a AccountView) -> Self {
41        Self {
42            view: account,
43            _marker: core::marker::PhantomData,
44        }
45    }
46
47    /// Read the typed overlay (immutable).
48    ///
49    /// # Safety
50    ///
51    /// Caller must ensure no conflicting mutable borrows.
52    #[inline]
53    pub fn read(&self) -> Result<VerifiedAccount<'a, T>, ProgramError> {
54        let data = self.view.try_borrow()?;
55        VerifiedAccount::from_ref(data)
56    }
57
58    /// The account's address.
59    #[inline(always)]
60    pub fn address(&self) -> &Address {
61        self.view.address()
62    }
63
64    /// The account's owner.
65    ///
66    /// # Safety
67    ///
68    /// Caller must ensure no conflicting mutable borrows on the account.
69    #[inline(always)]
70    pub unsafe fn owner(&self) -> &Address {
71        // SAFETY: Caller guarantees no conflicting borrows.
72        unsafe { self.view.owner() }
73    }
74
75    /// The underlying AccountView.
76    #[inline(always)]
77    pub fn to_account_view(&self) -> &'a AccountView {
78        self.view
79    }
80}