use light_compressed_account::compressed_account::CompressedAccountWithMerkleContext;
use light_sdk::{
instruction::PackedAccounts,
light_hasher::{sha256::Sha256BE, HasherError},
};
pub use light_token_interface::state::TokenData;
use light_token_interface::state::TokenDataVersion;
use solana_account_info::AccountInfo;
use solana_program_error::ProgramError;
use crate::{AnchorDeserialize, AnchorSerialize};
pub trait Pack {
type Packed;
fn pack(&self, remaining_accounts: &mut PackedAccounts) -> Result<Self::Packed, ProgramError>;
}
pub trait Unpack {
type Unpacked;
fn unpack(
&self,
remaining_accounts: &[AccountInfo],
) -> std::result::Result<Self::Unpacked, ProgramError>;
}
pub mod compat {
use solana_pubkey::Pubkey;
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize, Default)]
#[repr(u8)]
pub enum AccountState {
#[default]
Initialized = 0,
Frozen = 1,
}
impl From<AccountState> for light_token_interface::state::CompressedTokenAccountState {
fn from(state: AccountState) -> Self {
match state {
AccountState::Initialized => {
light_token_interface::state::CompressedTokenAccountState::Initialized
}
AccountState::Frozen => {
light_token_interface::state::CompressedTokenAccountState::Frozen
}
}
}
}
impl TryFrom<u8> for AccountState {
type Error = ProgramError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(AccountState::Initialized),
1 => Ok(AccountState::Frozen),
_ => Err(ProgramError::InvalidAccountData),
}
}
}
#[derive(Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize, Clone, Default)]
pub struct TokenData {
pub mint: Pubkey,
pub owner: Pubkey,
pub amount: u64,
pub delegate: Option<Pubkey>,
pub state: AccountState,
pub tlv: Option<Vec<light_token_interface::state::ExtensionStruct>>,
}
impl TokenData {
#[inline(always)]
pub fn hash_sha_flat(&self) -> Result<[u8; 32], HasherError> {
use light_sdk::light_hasher::Hasher;
let bytes = self.try_to_vec().map_err(|_| HasherError::BorshError)?;
Sha256BE::hash(bytes.as_slice())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TokenDataWithMerkleContext {
pub token_data: TokenData,
pub compressed_account: CompressedAccountWithMerkleContext,
}
impl TokenDataWithMerkleContext {
pub fn hash(&self) -> Result<[u8; 32], HasherError> {
if let Some(data) = self.compressed_account.compressed_account.data.as_ref() {
match data.discriminator {
[0, 0, 0, 0, 0, 0, 0, 4] => self.token_data.hash_sha_flat(),
_ => Err(HasherError::EmptyInput),
}
} else {
Err(HasherError::EmptyInput)
}
}
}
impl From<TokenData> for light_token_interface::state::TokenData {
fn from(data: TokenData) -> Self {
use light_token_interface::state::CompressedTokenAccountState;
Self {
mint: data.mint.to_bytes().into(),
owner: data.owner.to_bytes().into(),
amount: data.amount,
delegate: data.delegate.map(|d| d.to_bytes().into()),
state: match data.state {
AccountState::Initialized => CompressedTokenAccountState::Initialized as u8,
AccountState::Frozen => CompressedTokenAccountState::Frozen as u8,
},
tlv: data.tlv,
}
}
}
impl From<light_token_interface::state::TokenData> for TokenData {
fn from(data: light_token_interface::state::TokenData) -> Self {
Self {
mint: Pubkey::new_from_array(data.mint.to_bytes()),
owner: Pubkey::new_from_array(data.owner.to_bytes()),
amount: data.amount,
delegate: data.delegate.map(|d| Pubkey::new_from_array(d.to_bytes())),
state: AccountState::try_from(data.state).unwrap_or(AccountState::Initialized),
tlv: data.tlv,
}
}
}
impl Pack for TokenData {
type Packed = InputTokenDataCompressible;
fn pack(
&self,
remaining_accounts: &mut PackedAccounts,
) -> Result<Self::Packed, ProgramError> {
Ok(InputTokenDataCompressible {
owner: remaining_accounts.insert_or_get(self.owner),
mint: remaining_accounts.insert_or_get_read_only(self.mint),
amount: self.amount,
has_delegate: self.delegate.is_some(),
delegate: if let Some(delegate) = self.delegate {
remaining_accounts.insert_or_get(delegate)
} else {
0
},
version: TokenDataVersion::ShaFlat as u8,
})
}
}
impl Unpack for TokenData {
type Unpacked = Self;
fn unpack(
&self,
_remaining_accounts: &[AccountInfo],
) -> std::result::Result<Self::Unpacked, ProgramError> {
Ok(self.clone())
}
}
impl Unpack for InputTokenDataCompressible {
type Unpacked = TokenData;
fn unpack(
&self,
remaining_accounts: &[AccountInfo],
) -> std::result::Result<Self::Unpacked, ProgramError> {
Ok(TokenData {
owner: *remaining_accounts
.get(self.owner as usize)
.ok_or(ProgramError::InvalidAccountData)?
.key,
amount: self.amount,
delegate: if self.has_delegate {
Some(
*remaining_accounts
.get(self.delegate as usize)
.ok_or(ProgramError::InvalidAccountData)?
.key,
)
} else {
None
},
mint: *remaining_accounts
.get(self.mint as usize)
.ok_or(ProgramError::InvalidAccountData)?
.key,
state: AccountState::Initialized,
tlv: None,
})
}
}
#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
pub struct TokenDataWithVariant<V> {
pub variant: V,
pub token_data: TokenData,
}
#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
pub struct PackedTokenDataWithVariant<V> {
pub variant: V,
pub token_data: InputTokenDataCompressible,
}
#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
pub struct CTokenDataWithVariant<V> {
pub variant: V,
pub token_data: TokenData,
}
impl<V> Pack for CTokenDataWithVariant<V>
where
V: Pack,
V::Packed: AnchorSerialize + Clone + std::fmt::Debug,
{
type Packed = PackedTokenDataWithVariant<V::Packed>;
fn pack(
&self,
remaining_accounts: &mut PackedAccounts,
) -> Result<Self::Packed, ProgramError> {
Ok(PackedTokenDataWithVariant {
variant: self.variant.pack(remaining_accounts)?,
token_data: self.token_data.pack(remaining_accounts)?,
})
}
}
impl<V> Unpack for CTokenDataWithVariant<V>
where
V: Clone,
{
type Unpacked = TokenDataWithVariant<V>;
fn unpack(
&self,
remaining_accounts: &[AccountInfo],
) -> std::result::Result<Self::Unpacked, ProgramError> {
Ok(TokenDataWithVariant {
variant: self.variant.clone(),
token_data: self.token_data.unpack(remaining_accounts)?,
})
}
}
impl<V> Pack for TokenDataWithVariant<V>
where
V: Pack,
V::Packed: AnchorSerialize + Clone + std::fmt::Debug,
{
type Packed = PackedTokenDataWithVariant<V::Packed>;
fn pack(
&self,
remaining_accounts: &mut PackedAccounts,
) -> Result<Self::Packed, ProgramError> {
Ok(PackedTokenDataWithVariant {
variant: self.variant.pack(remaining_accounts)?,
token_data: self.token_data.pack(remaining_accounts)?,
})
}
}
impl<V> Unpack for PackedTokenDataWithVariant<V>
where
V: Unpack,
{
type Unpacked = TokenDataWithVariant<V::Unpacked>;
fn unpack(
&self,
remaining_accounts: &[AccountInfo],
) -> std::result::Result<Self::Unpacked, ProgramError> {
Ok(TokenDataWithVariant {
variant: self.variant.unpack(remaining_accounts)?,
token_data: self.token_data.unpack(remaining_accounts)?,
})
}
}
pub type InputTokenDataCompressible =
light_token_interface::instructions::transfer2::MultiTokenTransferOutputData;
pub type CompressibleTokenDataWithVariant<V> = CTokenDataWithVariant<V>;
pub type PackedCompressibleTokenDataWithVariant<V> = PackedTokenDataWithVariant<V>;
pub type CTokenData<V> = CTokenDataWithVariant<V>;
pub type PackedCTokenData<V> = PackedTokenDataWithVariant<V>;
}