#[cfg(test)]
use crate::state::{Account, Mint, Multisig};
use {
crate::{
instruction::MAX_SIGNERS,
state::{AccountState, PackedSizeOf},
},
bytemuck::{Pod, Zeroable},
solana_address::Address,
solana_nullable::MaybeNull,
solana_program_error::ProgramError,
solana_program_option::COption,
solana_program_pack::IsInitialized,
solana_zero_copy::unaligned::{Bool, U64},
};
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
pub struct PodMint {
pub mint_authority: PodCOption<Address>,
pub supply: U64,
pub decimals: u8,
pub is_initialized: Bool,
pub freeze_authority: PodCOption<Address>,
}
impl IsInitialized for PodMint {
fn is_initialized(&self) -> bool {
self.is_initialized.into()
}
}
impl PackedSizeOf for PodMint {
const SIZE_OF: usize = size_of::<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: Address,
pub owner: Address,
pub amount: U64,
pub delegate: PodCOption<Address>,
pub state: u8,
pub is_native: PodCOption<U64>,
pub delegated_amount: U64,
pub close_authority: PodCOption<Address>,
}
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_sdk_ids::system_program::check_id(&self.owner)
|| solana_sdk_ids::incinerator::check_id(&self.owner)
}
}
impl IsInitialized for PodAccount {
fn is_initialized(&self) -> bool {
self.state == AccountState::Initialized as u8 || self.state == AccountState::Frozen as u8
}
}
impl PackedSizeOf for PodAccount {
const SIZE_OF: usize = size_of::<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(U64::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: Bool,
pub signers: [Address; MAX_SIGNERS],
}
impl IsInitialized for PodMultisig {
fn is_initialized(&self) -> bool {
self.is_initialized.into()
}
}
impl PackedSizeOf for PodMultisig {
const SIZE_OF: usize = size_of::<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 option: [u8; 4],
pub 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<Address>> for MaybeNull<Address> {
type Error = ProgramError;
fn try_from(p: PodCOption<Address>) -> Result<Self, Self::Error> {
match p {
PodCOption {
option: PodCOption::<Address>::SOME,
value,
} if value == Address::default() => Err(ProgramError::InvalidArgument),
PodCOption {
option: PodCOption::<Address>::SOME,
value,
} => Ok(Self::from(value)),
PodCOption {
option: PodCOption::<Address>::NONE,
value: _,
} => Ok(Self::default()),
_ => unreachable!(),
}
}
}
#[cfg(test)]
pub(crate) mod test {
use {
super::*,
crate::state::{
test::{
TEST_ACCOUNT, TEST_ACCOUNT_SLICE, TEST_MINT, TEST_MINT_SLICE, TEST_MULTISIG,
TEST_MULTISIG_SLICE,
},
AccountState,
},
};
pub const TEST_POD_MINT: PodMint = PodMint {
mint_authority: PodCOption::some(Address::new_from_array([1; 32])),
supply: U64::from_primitive(42),
decimals: 7,
is_initialized: Bool::from_bool(true),
freeze_authority: PodCOption::some(Address::new_from_array([2; 32])),
};
pub const TEST_POD_ACCOUNT: PodAccount = PodAccount {
mint: Address::new_from_array([1; 32]),
owner: Address::new_from_array([2; 32]),
amount: U64::from_primitive(3),
delegate: PodCOption::some(Address::new_from_array([4; 32])),
state: AccountState::Frozen as u8,
is_native: PodCOption::some(U64::from_primitive(5)),
delegated_amount: U64::from_primitive(6),
close_authority: PodCOption::some(Address::new_from_array([7; 32])),
};
#[test]
fn pod_mint_to_mint_equality() {
let pod_mint = bytemuck::try_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 = bytemuck::try_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 = bytemuck::try_from_bytes::<PodMultisig>(TEST_MULTISIG_SLICE).unwrap();
assert_eq!(*pod_multisig, PodMultisig::from(TEST_MULTISIG));
}
}