Skip to main content

light_compressed_token_sdk/
compat.rs

1//! Solana-compatible token types using `solana_pubkey::Pubkey`.
2//!
3//! This module provides convenience types that use standard Solana pubkeys
4//! instead of byte arrays for easier integration.
5
6use light_compressed_account::compressed_account::CompressedAccountWithMerkleContext;
7use light_sdk::light_hasher::{sha256::Sha256BE, HasherError};
8use solana_program_error::ProgramError;
9use solana_pubkey::Pubkey;
10
11use crate::{AnchorDeserialize, AnchorSerialize};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize, Default)]
14#[repr(u8)]
15pub enum AccountState {
16    #[default]
17    Initialized = 0,
18    Frozen = 1,
19}
20
21impl From<AccountState> for light_token_interface::state::CompressedTokenAccountState {
22    fn from(state: AccountState) -> Self {
23        match state {
24            AccountState::Initialized => {
25                light_token_interface::state::CompressedTokenAccountState::Initialized
26            }
27            AccountState::Frozen => {
28                light_token_interface::state::CompressedTokenAccountState::Frozen
29            }
30        }
31    }
32}
33
34impl TryFrom<u8> for AccountState {
35    type Error = ProgramError;
36
37    fn try_from(value: u8) -> Result<Self, Self::Error> {
38        match value {
39            0 => Ok(AccountState::Initialized),
40            1 => Ok(AccountState::Frozen),
41            _ => Err(ProgramError::InvalidAccountData),
42        }
43    }
44}
45
46/// TokenData using standard Solana pubkeys.
47///
48/// For zero-copy operations, use `TokenData` from `light_token_interface`.
49#[derive(Debug, PartialEq, Eq, AnchorDeserialize, AnchorSerialize, Clone, Default)]
50pub struct TokenData {
51    /// The mint associated with this account
52    pub mint: Pubkey,
53    /// The owner of this account
54    pub owner: Pubkey,
55    /// The amount of tokens this account holds
56    pub amount: u64,
57    /// Optional delegate authorized to transfer tokens
58    pub delegate: Option<Pubkey>,
59    /// The account's state
60    pub state: AccountState,
61    /// TLV extensions for compressed token accounts
62    pub tlv: Option<Vec<light_token_interface::state::ExtensionStruct>>,
63}
64
65impl TokenData {
66    /// TokenDataVersion 3
67    /// CompressedAccount Discriminator `[0,0,0,0,0,0,0,4]`
68    #[inline(always)]
69    pub fn hash_sha_flat(&self) -> Result<[u8; 32], HasherError> {
70        use light_sdk::light_hasher::Hasher;
71        let bytes = self.try_to_vec().map_err(|_| HasherError::BorshError)?;
72        Sha256BE::hash(bytes.as_slice())
73    }
74}
75
76/// TokenData with merkle context for verification
77#[derive(Debug, Clone, PartialEq)]
78pub struct TokenDataWithMerkleContext {
79    pub token_data: TokenData,
80    pub compressed_account: CompressedAccountWithMerkleContext,
81}
82
83impl TokenDataWithMerkleContext {
84    /// Only works for sha flat hash
85    pub fn hash(&self) -> Result<[u8; 32], HasherError> {
86        if let Some(data) = self.compressed_account.compressed_account.data.as_ref() {
87            match data.discriminator {
88                [0, 0, 0, 0, 0, 0, 0, 4] => self.token_data.hash_sha_flat(),
89                _ => Err(HasherError::EmptyInput),
90            }
91        } else {
92            Err(HasherError::EmptyInput)
93        }
94    }
95}
96
97impl From<TokenData> for light_token_interface::state::TokenData {
98    fn from(data: TokenData) -> Self {
99        use light_token_interface::state::CompressedTokenAccountState;
100
101        Self {
102            mint: data.mint.to_bytes().into(),
103            owner: data.owner.to_bytes().into(),
104            amount: data.amount,
105            delegate: data.delegate.map(|d| d.to_bytes().into()),
106            state: match data.state {
107                AccountState::Initialized => CompressedTokenAccountState::Initialized as u8,
108                AccountState::Frozen => CompressedTokenAccountState::Frozen as u8,
109            },
110            tlv: data.tlv,
111        }
112    }
113}
114
115impl From<light_token_interface::state::TokenData> for TokenData {
116    fn from(data: light_token_interface::state::TokenData) -> Self {
117        Self {
118            mint: Pubkey::new_from_array(data.mint.to_bytes()),
119            owner: Pubkey::new_from_array(data.owner.to_bytes()),
120            amount: data.amount,
121            delegate: data.delegate.map(|d| Pubkey::new_from_array(d.to_bytes())),
122            state: AccountState::try_from(data.state).unwrap_or(AccountState::Initialized),
123            tlv: data.tlv,
124        }
125    }
126}
127
128/// Type alias for backward compatibility
129pub type InputTokenDataCompressible =
130    light_token_interface::instructions::transfer2::MultiTokenTransferOutputData;