light_token_interface/state/compressed_token/
token_data.rs

1use light_compressed_account::Pubkey;
2use light_program_profiler::profile;
3use light_zero_copy::{num_trait::ZeroCopyNumTrait, ZeroCopy, ZeroCopyMut};
4
5use crate::{
6    instructions::extensions::ZExtensionInstructionData,
7    state::extensions::{ExtensionStruct, ZExtensionStructMut},
8    AnchorDeserialize, AnchorSerialize, TokenError,
9};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)]
12#[repr(u8)]
13pub enum CompressedTokenAccountState {
14    //Uninitialized, is always initialized.
15    Initialized = 0,
16    Frozen = 1,
17}
18
19impl TryFrom<u8> for CompressedTokenAccountState {
20    type Error = TokenError;
21
22    fn try_from(value: u8) -> Result<Self, Self::Error> {
23        match value {
24            0 => Ok(CompressedTokenAccountState::Initialized),
25            1 => Ok(CompressedTokenAccountState::Frozen),
26            _ => Err(TokenError::InvalidAccountState),
27        }
28    }
29}
30
31/// TokenData of Compressed Tokens.
32#[derive(
33    Debug, PartialEq, Eq, AnchorSerialize, AnchorDeserialize, Clone, ZeroCopy, ZeroCopyMut,
34)]
35#[repr(C)]
36pub struct TokenData {
37    /// The mint associated with this account
38    pub mint: Pubkey,
39    /// The owner of this account.
40    pub owner: Pubkey,
41    /// The amount of tokens this account holds.
42    pub amount: u64,
43    /// If `delegate` is `Some` then `delegated_amount` represents
44    /// the amount authorized by the delegate
45    pub delegate: Option<Pubkey>,
46    /// The account's state
47    pub state: u8,
48    /// Extensions for the compressed token account
49    pub tlv: Option<Vec<ExtensionStruct>>,
50}
51
52impl TokenData {
53    pub fn state(&self) -> Result<CompressedTokenAccountState, TokenError> {
54        CompressedTokenAccountState::try_from(self.state)
55    }
56}
57
58// Implementation for zero-copy mutable TokenData
59impl<'a> ZTokenDataMut<'a> {
60    /// Set all fields of the TokenData struct at once.
61    /// All data must be allocated before calling this function.
62    #[inline]
63    #[profile]
64    pub fn set(
65        &mut self,
66        mint: Pubkey,
67        owner: Pubkey,
68        amount: impl ZeroCopyNumTrait,
69        delegate: Option<Pubkey>,
70        state: CompressedTokenAccountState,
71        tlv_data: Option<&[ZExtensionInstructionData<'_>]>,
72    ) -> Result<(), TokenError> {
73        self.mint = mint;
74        self.owner = owner;
75        self.amount.set(amount.into());
76        if let Some(z_delegate) = self.delegate.as_deref_mut() {
77            *z_delegate = delegate.ok_or(TokenError::InstructionDataExpectedDelegate)?;
78        }
79        if self.delegate.is_none() && delegate.is_some() {
80            return Err(TokenError::ZeroCopyExpectedDelegate);
81        }
82
83        *self.state = state as u8;
84
85        // Set TLV extension values (space was pre-allocated via new_zero_copy)
86        match (self.tlv.as_mut(), tlv_data) {
87            (Some(tlv_vec), Some(exts)) => {
88                if tlv_vec.len() != 1 || exts.len() != 1 {
89                    return Err(TokenError::TlvExtensionLengthMismatch);
90                }
91                for (tlv_ext, instruction_ext) in tlv_vec.iter_mut().zip(exts.iter()) {
92                    match (tlv_ext, instruction_ext) {
93                        (
94                            ZExtensionStructMut::CompressedOnly(compressed_only),
95                            ZExtensionInstructionData::CompressedOnly(data),
96                        ) => {
97                            compressed_only.delegated_amount = data.delegated_amount;
98                            compressed_only.withheld_transfer_fee = data.withheld_transfer_fee;
99                            compressed_only.is_ata = if data.is_ata() { 1 } else { 0 };
100                        }
101                        _ => return Err(TokenError::UnsupportedTlvExtensionType),
102                    }
103                }
104            }
105            (Some(_), None) | (None, Some(_)) => {
106                return Err(TokenError::TlvExtensionLengthMismatch);
107            }
108            (None, None) => {}
109        }
110
111        Ok(())
112    }
113}