#[cfg(test)]
use crate::state::{Account, Mint, Multisig};
use {
crate::{
instruction::MAX_SIGNERS,
state::{AccountState, PackedSizeOf},
},
bytemuck::{Pod, Zeroable},
solana_program::{
program_error::ProgramError, program_option::COption, program_pack::IsInitialized,
pubkey::Pubkey,
},
spl_pod::{
bytemuck::pod_get_packed_len,
optional_keys::OptionalNonZeroPubkey,
primitives::{PodBool, PodU64},
},
};
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct PodMint {
pub mint_authority: PodCOption<Pubkey>,
pub supply: PodU64,
pub decimals: u8,
pub is_initialized: PodBool,
pub freeze_authority: PodCOption<Pubkey>,
}
impl IsInitialized for PodMint {
fn is_initialized(&self) -> bool {
self.is_initialized.into()
}
}
impl PackedSizeOf for PodMint {
const SIZE_OF: usize = pod_get_packed_len::<Self>();
}
#[cfg(test)]
impl From<Mint> for PodMint {
fn from(mint: Mint) -> Self {
Self {
mint_authority: mint.mint_authority.into(),
supply: mint.supply.into(),
decimals: mint.decimals,
is_initialized: mint.is_initialized.into(),
freeze_authority: mint.freeze_authority.into(),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct PodAccount {
pub mint: Pubkey,
pub owner: Pubkey,
pub amount: PodU64,
pub delegate: PodCOption<Pubkey>,
pub state: u8,
pub is_native: PodCOption<PodU64>,
pub delegated_amount: PodU64,
pub close_authority: PodCOption<Pubkey>,
}
impl PodAccount {
pub fn is_frozen(&self) -> bool {
self.state == AccountState::Frozen as u8
}
pub fn is_native(&self) -> bool {
self.is_native.is_some()
}
pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
solana_program::system_program::check_id(&self.owner)
|| solana_program::incinerator::check_id(&self.owner)
}
}
impl IsInitialized for PodAccount {
fn is_initialized(&self) -> bool {
self.state != 0
}
}
impl PackedSizeOf for PodAccount {
const SIZE_OF: usize = pod_get_packed_len::<Self>();
}
#[cfg(test)]
impl From<Account> for PodAccount {
fn from(account: Account) -> Self {
Self {
mint: account.mint,
owner: account.owner,
amount: account.amount.into(),
delegate: account.delegate.into(),
state: account.state.into(),
is_native: account.is_native.map(PodU64::from_primitive).into(),
delegated_amount: account.delegated_amount.into(),
close_authority: account.close_authority.into(),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct PodMultisig {
pub m: u8,
pub n: u8,
pub is_initialized: PodBool,
pub signers: [Pubkey; MAX_SIGNERS],
}
impl IsInitialized for PodMultisig {
fn is_initialized(&self) -> bool {
self.is_initialized.into()
}
}
impl PackedSizeOf for PodMultisig {
const SIZE_OF: usize = pod_get_packed_len::<Self>();
}
#[cfg(test)]
impl From<Multisig> for PodMultisig {
fn from(multisig: Multisig) -> Self {
Self {
m: multisig.m,
n: multisig.n,
is_initialized: multisig.is_initialized.into(),
signers: multisig.signers,
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct PodCOption<T>
where
T: Pod + Default,
{
pub(crate) option: [u8; 4],
pub(crate) value: T,
}
impl<T> PodCOption<T>
where
T: Pod + Default,
{
pub const NONE: [u8; 4] = [0; 4];
pub const SOME: [u8; 4] = [1, 0, 0, 0];
pub fn none() -> Self {
Self {
option: Self::NONE,
value: T::default(),
}
}
pub const fn some(value: T) -> Self {
Self {
option: Self::SOME,
value,
}
}
pub fn unwrap_or(self, default: T) -> T {
if self.option == Self::NONE {
default
} else {
self.value
}
}
pub fn is_some(&self) -> bool {
self.option == Self::SOME
}
pub fn is_none(&self) -> bool {
self.option == Self::NONE
}
pub fn ok_or<E>(self, error: E) -> Result<T, E> {
match self {
Self {
option: Self::SOME,
value,
} => Ok(value),
_ => Err(error),
}
}
}
impl<T: Pod + Default> From<COption<T>> for PodCOption<T> {
fn from(opt: COption<T>) -> Self {
match opt {
COption::None => Self {
option: Self::NONE,
value: T::default(),
},
COption::Some(v) => Self {
option: Self::SOME,
value: v,
},
}
}
}
impl TryFrom<PodCOption<Pubkey>> for OptionalNonZeroPubkey {
type Error = ProgramError;
fn try_from(p: PodCOption<Pubkey>) -> Result<Self, Self::Error> {
match p {
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value,
} if value == Pubkey::default() => Err(ProgramError::InvalidArgument),
PodCOption {
option: PodCOption::<Pubkey>::SOME,
value,
} => Ok(Self(value)),
PodCOption {
option: PodCOption::<Pubkey>::NONE,
value: _,
} => Ok(Self(Pubkey::default())),
_ => unreachable!(),
}
}
}
#[cfg(test)]
pub mod test {
use {
super::*,
crate::state::{
test::{
TEST_ACCOUNT, TEST_ACCOUNT_SLICE, TEST_MINT, TEST_MINT_SLICE, TEST_MULTISIG,
TEST_MULTISIG_SLICE,
},
AccountState,
},
spl_pod::bytemuck::pod_from_bytes,
};
pub const TEST_POD_MINT: PodMint = PodMint {
mint_authority: PodCOption::some(Pubkey::new_from_array([1; 32])),
supply: PodU64::from_primitive(42),
decimals: 7,
is_initialized: PodBool::from_bool(true),
freeze_authority: PodCOption::some(Pubkey::new_from_array([2; 32])),
};
pub const TEST_POD_ACCOUNT: PodAccount = PodAccount {
mint: Pubkey::new_from_array([1; 32]),
owner: Pubkey::new_from_array([2; 32]),
amount: PodU64::from_primitive(3),
delegate: PodCOption::some(Pubkey::new_from_array([4; 32])),
state: AccountState::Frozen as u8,
is_native: PodCOption::some(PodU64::from_primitive(5)),
delegated_amount: PodU64::from_primitive(6),
close_authority: PodCOption::some(Pubkey::new_from_array([7; 32])),
};
#[test]
fn pod_mint_to_mint_equality() {
let pod_mint = pod_from_bytes::<PodMint>(TEST_MINT_SLICE).unwrap();
assert_eq!(*pod_mint, PodMint::from(TEST_MINT));
assert_eq!(*pod_mint, TEST_POD_MINT);
}
#[test]
fn pod_account_to_account_equality() {
let pod_account = pod_from_bytes::<PodAccount>(TEST_ACCOUNT_SLICE).unwrap();
assert_eq!(*pod_account, PodAccount::from(TEST_ACCOUNT));
assert_eq!(*pod_account, TEST_POD_ACCOUNT);
}
#[test]
fn pod_multisig_to_multisig_equality() {
let pod_multisig = pod_from_bytes::<PodMultisig>(TEST_MULTISIG_SLICE).unwrap();
assert_eq!(*pod_multisig, PodMultisig::from(TEST_MULTISIG));
}
}