//! State transition types use crate::instruction::MAX_SIGNERS; use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; use num_enum::TryFromPrimitive; use solana_program::{ program_error::ProgramError, program_option::COption, program_pack::{IsInitialized, Pack, Sealed}, pubkey::Pubkey, }; /// Mint data. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Mint { /// Optional authority used to mint new tokens. The mint authority may only be provided during /// mint creation. If no mint authority is present then the mint has a fixed supply and no /// further tokens may be minted. pub mint_authority: COption, /// Total supply of tokens. pub supply: u64, /// Number of base 10 digits to the right of the decimal place. pub decimals: u8, /// Is `true` if this structure has been initialized pub is_initialized: bool, /// Optional authority to freeze token accounts. pub freeze_authority: COption, } impl Sealed for Mint {} impl IsInitialized for Mint { fn is_initialized(&self) -> bool { self.is_initialized } } impl Pack for Mint { const LEN: usize = 82; fn unpack_from_slice(src: &[u8]) -> Result { let src = array_ref![src, 0, 82]; let (mint_authority, supply, decimals, is_initialized, freeze_authority) = array_refs![src, 36, 8, 1, 1, 36]; let mint_authority = unpack_coption_key(mint_authority)?; let supply = u64::from_le_bytes(*supply); let decimals = decimals[0]; let is_initialized = match is_initialized { [0] => false, [1] => true, _ => return Err(ProgramError::InvalidAccountData), }; let freeze_authority = unpack_coption_key(freeze_authority)?; Ok(Mint { mint_authority, supply, decimals, is_initialized, freeze_authority, }) } fn pack_into_slice(&self, dst: &mut [u8]) { let dst = array_mut_ref![dst, 0, 82]; let ( mint_authority_dst, supply_dst, decimals_dst, is_initialized_dst, freeze_authority_dst, ) = mut_array_refs![dst, 36, 8, 1, 1, 36]; let &Mint { ref mint_authority, supply, decimals, is_initialized, ref freeze_authority, } = self; pack_coption_key(mint_authority, mint_authority_dst); *supply_dst = supply.to_le_bytes(); decimals_dst[0] = decimals; is_initialized_dst[0] = is_initialized as u8; pack_coption_key(freeze_authority, freeze_authority_dst); } } /// Account data. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Account { /// The mint associated with this account pub mint: Pubkey, /// The owner of this account. pub owner: Pubkey, /// The amount of tokens this account holds. pub amount: u64, /// If `delegate` is `Some` then `delegated_amount` represents /// the amount authorized by the delegate pub delegate: COption, /// The account's state pub state: AccountState, /// If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account /// is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped /// SOL accounts do not drop below this threshold. pub is_native: COption, /// The amount delegated pub delegated_amount: u64, /// Optional authority to close the account. pub close_authority: COption, } impl Account { /// Checks if account is frozen pub fn is_frozen(&self) -> bool { self.state == AccountState::Frozen } /// Checks if account is native pub fn is_native(&self) -> bool { self.is_native.is_some() } } impl Sealed for Account {} impl IsInitialized for Account { fn is_initialized(&self) -> bool { self.state != AccountState::Uninitialized } } impl Pack for Account { const LEN: usize = 165; fn unpack_from_slice(src: &[u8]) -> Result { let src = array_ref![src, 0, 165]; let (mint, owner, amount, delegate, state, is_native, delegated_amount, close_authority) = array_refs![src, 32, 32, 8, 36, 1, 12, 8, 36]; Ok(Account { mint: Pubkey::new_from_array(*mint), owner: Pubkey::new_from_array(*owner), amount: u64::from_le_bytes(*amount), delegate: unpack_coption_key(delegate)?, state: AccountState::try_from_primitive(state[0]) .or(Err(ProgramError::InvalidAccountData))?, is_native: unpack_coption_u64(is_native)?, delegated_amount: u64::from_le_bytes(*delegated_amount), close_authority: unpack_coption_key(close_authority)?, }) } fn pack_into_slice(&self, dst: &mut [u8]) { let dst = array_mut_ref![dst, 0, 165]; let ( mint_dst, owner_dst, amount_dst, delegate_dst, state_dst, is_native_dst, delegated_amount_dst, close_authority_dst, ) = mut_array_refs![dst, 32, 32, 8, 36, 1, 12, 8, 36]; let &Account { ref mint, ref owner, amount, ref delegate, state, ref is_native, delegated_amount, ref close_authority, } = self; mint_dst.copy_from_slice(mint.as_ref()); owner_dst.copy_from_slice(owner.as_ref()); *amount_dst = amount.to_le_bytes(); pack_coption_key(delegate, delegate_dst); state_dst[0] = state as u8; pack_coption_u64(is_native, is_native_dst); *delegated_amount_dst = delegated_amount.to_le_bytes(); pack_coption_key(close_authority, close_authority_dst); } } /// Account state. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)] pub enum AccountState { /// Account is not yet initialized Uninitialized, /// Account is initialized; the account owner and/or delegate may perform permitted operations /// on this account Initialized, /// Account has been frozen by the mint freeze authority. Neither the account owner nor /// the delegate are able to perform operations on this account. Frozen, } impl Default for AccountState { fn default() -> Self { AccountState::Uninitialized } } /// Multisignature data. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Multisig { /// Number of signers required pub m: u8, /// Number of valid signers pub n: u8, /// Is `true` if this structure has been initialized pub is_initialized: bool, /// Signer public keys pub signers: [Pubkey; MAX_SIGNERS], } impl Sealed for Multisig {} impl IsInitialized for Multisig { fn is_initialized(&self) -> bool { self.is_initialized } } impl Pack for Multisig { const LEN: usize = 355; fn unpack_from_slice(src: &[u8]) -> Result { let src = array_ref![src, 0, 355]; #[allow(clippy::ptr_offset_with_cast)] let (m, n, is_initialized, signers_flat) = array_refs![src, 1, 1, 1, 32 * MAX_SIGNERS]; let mut result = Multisig { m: m[0], n: n[0], is_initialized: match is_initialized { [0] => false, [1] => true, _ => return Err(ProgramError::InvalidAccountData), }, signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS], }; for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) { *dst = Pubkey::new(src); } Ok(result) } fn pack_into_slice(&self, dst: &mut [u8]) { let dst = array_mut_ref![dst, 0, 355]; #[allow(clippy::ptr_offset_with_cast)] let (m, n, is_initialized, signers_flat) = mut_array_refs![dst, 1, 1, 1, 32 * MAX_SIGNERS]; *m = [self.m]; *n = [self.n]; *is_initialized = [self.is_initialized as u8]; for (i, src) in self.signers.iter().enumerate() { let dst_array = array_mut_ref![signers_flat, 32 * i, 32]; dst_array.copy_from_slice(src.as_ref()); } } } // Helpers fn pack_coption_key(src: &COption, dst: &mut [u8; 36]) { let (tag, body) = mut_array_refs![dst, 4, 32]; match src { COption::Some(key) => { *tag = [1, 0, 0, 0]; body.copy_from_slice(key.as_ref()); } COption::None => { *tag = [0; 4]; } } } fn unpack_coption_key(src: &[u8; 36]) -> Result, ProgramError> { let (tag, body) = array_refs![src, 4, 32]; match *tag { [0, 0, 0, 0] => Ok(COption::None), [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))), _ => Err(ProgramError::InvalidAccountData), } } fn pack_coption_u64(src: &COption, dst: &mut [u8; 12]) { let (tag, body) = mut_array_refs![dst, 4, 8]; match src { COption::Some(amount) => { *tag = [1, 0, 0, 0]; *body = amount.to_le_bytes(); } COption::None => { *tag = [0; 4]; } } } fn unpack_coption_u64(src: &[u8; 12]) -> Result, ProgramError> { let (tag, body) = array_refs![src, 4, 8]; match *tag { [0, 0, 0, 0] => Ok(COption::None), [1, 0, 0, 0] => Ok(COption::Some(u64::from_le_bytes(*body))), _ => Err(ProgramError::InvalidAccountData), } }