typhoon_accounts/accounts/
interface_account.rs1use {
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 if unlikely(info.data_len() < T::DISCRIMINATOR.len()) {
30 return Err(ProgramError::AccountDataTooSmall.into());
31 }
32
33 if unlikely(!discriminator_matches::<T>(info)) {
35 return Err(ErrorCode::AccountDiscriminatorMismatch.into());
36 }
37
38 if unlikely(!T::OWNERS.contains(unsafe { info.owner() })) {
40 return Err(ProgramError::InvalidAccountOwner.into());
41 }
42
43 if unlikely(info.owned_by(&System::ID)) {
45 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}