Skip to main content

typhoon_accounts/accounts/
interface_account.rs

1use {
2    crate::{
3        discriminator_matches, Discriminator, FromAccountInfo, FromRaw, ReadableAccount,
4        RefFromBytes, System,
5    },
6    core::marker::PhantomData,
7    pinocchio::hint::unlikely,
8    solana_account_view::{AccountView, Ref},
9    solana_program_error::ProgramError,
10    typhoon_errors::{Error, ErrorCode},
11    typhoon_traits::{Owners, ProgramId},
12};
13
14pub struct InterfaceAccount<'a, T>
15where
16    T: Discriminator + RefFromBytes,
17{
18    pub(crate) info: &'a AccountView,
19    pub(crate) _phantom: PhantomData<T>,
20}
21
22impl<'a, T> FromAccountInfo<'a> for InterfaceAccount<'a, T>
23where
24    T: Discriminator + RefFromBytes + Owners,
25{
26    #[inline(always)]
27    fn try_from_info(info: &'a AccountView) -> Result<Self, Error> {
28        // Check data length first - this is the cheapest check and most likely to fail
29        if unlikely(info.data_len() < T::DISCRIMINATOR.len()) {
30            return Err(ProgramError::AccountDataTooSmall.into());
31        }
32
33        // Validate discriminator using optimized comparison for small discriminators
34        if unlikely(!discriminator_matches::<T>(info)) {
35            return Err(ErrorCode::AccountDiscriminatorMismatch.into());
36        }
37
38        // Verify account ownership against multiple allowed owners - checked after discriminator for better branch prediction
39        if unlikely(!T::OWNERS.contains(unsafe { info.owner() })) {
40            return Err(ProgramError::InvalidAccountOwner.into());
41        }
42
43        // Handle special case: zero-lamport system accounts (least common case)
44        if unlikely(info.owned_by(&System::ID)) {
45            // Only perform additional lamports check for system accounts
46            if info.lamports() == 0 {
47                return Err(ProgramError::UninitializedAccount.into());
48            }
49        }
50
51        Ok(InterfaceAccount {
52            info,
53            _phantom: PhantomData,
54        })
55    }
56}
57
58impl<'a, T> From<InterfaceAccount<'a, T>> for &'a AccountView
59where
60    T: Discriminator + RefFromBytes,
61{
62    #[inline(always)]
63    fn from(value: InterfaceAccount<'a, T>) -> Self {
64        value.info
65    }
66}
67
68impl<T> AsRef<AccountView> for InterfaceAccount<'_, T>
69where
70    T: Discriminator + RefFromBytes,
71{
72    #[inline(always)]
73    fn as_ref(&self) -> &AccountView {
74        self.info
75    }
76}
77
78impl<T> ReadableAccount for InterfaceAccount<'_, T>
79where
80    T: RefFromBytes + Discriminator,
81{
82    type DataUnchecked = T;
83    type Data<'a>
84        = Ref<'a, T>
85    where
86        Self: 'a;
87
88    #[inline(always)]
89    fn data<'a>(&'a self) -> Result<Self::Data<'a>, Error> {
90        Ref::filter_map(self.info.try_borrow()?, T::read)
91            .map_err(|_| ProgramError::InvalidAccountData.into())
92    }
93
94    #[inline]
95    fn data_unchecked(&self) -> Result<&Self::DataUnchecked, Error> {
96        let dis_len = T::DISCRIMINATOR.len();
97        let total_len = dis_len + core::mem::size_of::<T>();
98
99        if self.info.data_len() < total_len {
100            return Err(ErrorCode::InvalidDataLength.into());
101        }
102
103        let data_ptr = unsafe { self.info.data_ptr().add(dis_len) };
104
105        if data_ptr.align_offset(core::mem::align_of::<T>()) != 0 {
106            return Err(ErrorCode::InvalidDataAlignment.into());
107        }
108
109        Ok(unsafe { &*(data_ptr as *const T) })
110    }
111}
112
113impl<'a, T> FromRaw<'a> for InterfaceAccount<'a, T>
114where
115    T: RefFromBytes + Discriminator,
116{
117    fn from_raw(info: &'a AccountView) -> Self {
118        Self {
119            info,
120            _phantom: PhantomData,
121        }
122    }
123}