use {
crate::{
discriminator_matches, Discriminator, FromAccountInfo, FromRaw, Owners, ReadableAccount,
RefFromBytes,
},
core::marker::PhantomData,
pinocchio::{
account_info::{AccountInfo, Ref},
hint::unlikely,
program_error::ProgramError,
},
typhoon_errors::{Error, ErrorCode},
};
pub struct InterfaceAccount<'a, T>
where
T: Discriminator + RefFromBytes,
{
pub(crate) info: &'a AccountInfo,
pub(crate) _phantom: PhantomData<T>,
}
impl<'a, T> FromAccountInfo<'a> for InterfaceAccount<'a, T>
where
T: Discriminator + RefFromBytes + Owners,
{
#[inline(always)]
fn try_from_info(info: &'a AccountInfo) -> Result<Self, Error> {
if unlikely(info.data_len() < T::DISCRIMINATOR.len()) {
return Err(ProgramError::AccountDataTooSmall.into());
}
if unlikely(!discriminator_matches::<T>(info)) {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
if unlikely(!T::OWNERS.contains(info.owner())) {
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
}
if unlikely(info.is_owned_by(&pinocchio_system::ID)) {
if *info.try_borrow_lamports()? == 0 {
return Err(ProgramError::UninitializedAccount.into());
}
}
Ok(InterfaceAccount {
info,
_phantom: PhantomData,
})
}
}
impl<'a, T> From<InterfaceAccount<'a, T>> for &'a AccountInfo
where
T: Discriminator + RefFromBytes,
{
#[inline(always)]
fn from(value: InterfaceAccount<'a, T>) -> Self {
value.info
}
}
impl<T> AsRef<AccountInfo> for InterfaceAccount<'_, T>
where
T: Discriminator + RefFromBytes,
{
#[inline(always)]
fn as_ref(&self) -> &AccountInfo {
self.info
}
}
impl<T> ReadableAccount for InterfaceAccount<'_, T>
where
T: RefFromBytes + Discriminator,
{
type DataUnchecked = T;
type Data<'a>
= Ref<'a, T>
where
Self: 'a;
#[inline(always)]
fn data<'a>(&'a self) -> Result<Self::Data<'a>, Error> {
Ref::filter_map(self.info.try_borrow_data()?, T::read)
.map_err(|_| ProgramError::InvalidAccountData.into())
}
#[inline]
fn data_unchecked(&self) -> Result<&Self::DataUnchecked, Error> {
let dis_len = T::DISCRIMINATOR.len();
let total_len = dis_len + core::mem::size_of::<T>();
if self.info.data_len() < total_len {
return Err(ProgramError::InvalidAccountData.into());
}
let data_ptr = unsafe { self.info.data_ptr().add(dis_len) };
if data_ptr.align_offset(core::mem::align_of::<T>()) != 0 {
return Err(ProgramError::InvalidAccountData.into());
}
Ok(unsafe { &*(data_ptr as *const T) })
}
}
impl<'a, T> FromRaw<'a> for InterfaceAccount<'a, T>
where
T: RefFromBytes + Discriminator,
{
fn from_raw(info: &'a AccountInfo) -> Self {
Self {
info,
_phantom: PhantomData,
}
}
}