use borsh::{BorshDeserialize, BorshSerialize};
use light_compressed_account::{address::derive_address, Pubkey};
use light_compressible::compression_info::CompressionInfo;
use light_hasher::{sha256::Sha256BE, Hasher};
use light_zero_copy::{ZeroCopy, ZeroCopyMut};
use pinocchio::account_info::AccountInfo;
#[cfg(feature = "solana")]
use solana_msg::msg;
use crate::{
state::ExtensionStruct, AnchorDeserialize, AnchorSerialize, TokenError, LIGHT_TOKEN_PROGRAM_ID,
MINT_ADDRESS_TREE,
};
pub const ACCOUNT_TYPE_MINT: u8 = 1;
#[repr(C)]
#[derive(Debug, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
pub struct Mint {
pub base: BaseMint,
pub metadata: MintMetadata,
pub reserved: [u8; 16],
pub account_type: u8,
pub compression: CompressionInfo,
pub extensions: Option<Vec<ExtensionStruct>>,
}
impl Default for Mint {
fn default() -> Self {
Self {
base: BaseMint::default(),
metadata: MintMetadata::default(),
reserved: [0u8; 16],
account_type: ACCOUNT_TYPE_MINT,
compression: CompressionInfo::default(),
extensions: None,
}
}
}
#[repr(C)]
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct BaseMint {
pub mint_authority: Option<Pubkey>,
pub supply: u64,
pub decimals: u8,
pub is_initialized: bool,
pub freeze_authority: Option<Pubkey>,
}
#[repr(C)]
#[derive(
Debug, Default, PartialEq, Eq, Clone, AnchorDeserialize, AnchorSerialize, ZeroCopyMut, ZeroCopy,
)]
pub struct MintMetadata {
pub version: u8,
pub mint_decompressed: bool,
pub mint: Pubkey,
pub mint_signer: [u8; 32],
pub bump: u8,
}
impl MintMetadata {
pub fn compressed_address(&self) -> [u8; 32] {
derive_address(
self.mint.array_ref(),
&MINT_ADDRESS_TREE,
&LIGHT_TOKEN_PROGRAM_ID,
)
}
}
impl ZMintMetadata<'_> {
pub fn compressed_address(&self) -> [u8; 32] {
derive_address(
self.mint.array_ref(),
&MINT_ADDRESS_TREE,
&LIGHT_TOKEN_PROGRAM_ID,
)
}
}
impl Mint {
pub fn hash(&self) -> Result<[u8; 32], TokenError> {
match self.metadata.version {
3 => Ok(Sha256BE::hash(
self.try_to_vec()
.map_err(|_| TokenError::BorshFailed)?
.as_slice(),
)?),
_ => Err(TokenError::InvalidTokenDataVersion),
}
}
pub fn from_account_info_checked(account_info: &AccountInfo) -> Result<Self, TokenError> {
if !account_info.is_owned_by(&LIGHT_TOKEN_PROGRAM_ID) {
#[cfg(feature = "solana")]
msg!("Mint account has invalid owner");
return Err(TokenError::InvalidMintOwner);
}
let data = account_info
.try_borrow_data()
.map_err(|_| TokenError::MintBorrowFailed)?;
let mint =
Self::try_from_slice(&data).map_err(|_| TokenError::MintDeserializationFailed)?;
if !mint.base.is_initialized {
#[cfg(feature = "solana")]
msg!("Mint account is not initialized");
return Err(TokenError::MintNotInitialized);
}
if !mint.is_mint_account() {
#[cfg(feature = "solana")]
msg!("Mint account is not a Mint account");
return Err(TokenError::MintMismatch);
}
Ok(mint)
}
#[inline(always)]
pub fn is_mint_account(&self) -> bool {
self.account_type == ACCOUNT_TYPE_MINT
}
}