Skip to main content

light_client/indexer/types/
token.rs

1use borsh::BorshDeserialize;
2use light_compressed_account::compressed_account::CompressedAccountWithMerkleContext;
3use light_token::compat::{AccountState, TokenData};
4use light_token_interface::state::ExtensionStruct;
5use solana_pubkey::Pubkey;
6
7use super::{
8    super::{base58::decode_base58_to_fixed_array, IndexerError},
9    account::CompressedAccount,
10};
11
12#[derive(Clone, Default, Debug, PartialEq)]
13pub struct CompressedTokenAccount {
14    /// Token-specific data (mint, owner, amount, delegate, state, tlv)
15    pub token: TokenData,
16    /// General account information (address, hash, lamports, merkle context, etc.)
17    pub account: CompressedAccount,
18}
19
20fn parse_token_data(td: &photon_api::types::TokenData) -> Result<TokenData, IndexerError> {
21    Ok(TokenData {
22        mint: Pubkey::new_from_array(decode_base58_to_fixed_array(&td.mint)?),
23        owner: Pubkey::new_from_array(decode_base58_to_fixed_array(&td.owner)?),
24        amount: *td.amount,
25        delegate: td
26            .delegate
27            .as_ref()
28            .map(|d| decode_base58_to_fixed_array(d).map(Pubkey::new_from_array))
29            .transpose()?,
30        state: match td.state {
31            photon_api::types::AccountState::Initialized => AccountState::Initialized,
32            photon_api::types::AccountState::Frozen => AccountState::Frozen,
33        },
34        tlv: td
35            .tlv
36            .as_ref()
37            .map(|tlv| {
38                let bytes = base64::decode_config(&**tlv, base64::STANDARD_NO_PAD)
39                    .map_err(|e| IndexerError::decode_error("tlv", e))?;
40                Vec::<ExtensionStruct>::deserialize(&mut bytes.as_slice())
41                    .map_err(|e| IndexerError::decode_error("extensions", e))
42            })
43            .transpose()?,
44    })
45}
46
47impl TryFrom<&photon_api::types::TokenAccount> for CompressedTokenAccount {
48    type Error = IndexerError;
49
50    fn try_from(token_account: &photon_api::types::TokenAccount) -> Result<Self, Self::Error> {
51        let account = CompressedAccount::try_from(&token_account.account)?;
52        let token = parse_token_data(&token_account.token_data)?;
53        Ok(CompressedTokenAccount { token, account })
54    }
55}
56
57impl TryFrom<&photon_api::types::TokenAccountV2> for CompressedTokenAccount {
58    type Error = IndexerError;
59
60    fn try_from(token_account: &photon_api::types::TokenAccountV2) -> Result<Self, Self::Error> {
61        let account = CompressedAccount::try_from(&token_account.account)?;
62        let token = parse_token_data(&token_account.token_data)?;
63        Ok(CompressedTokenAccount { token, account })
64    }
65}
66
67#[allow(clippy::from_over_into)]
68impl Into<light_token::compat::TokenDataWithMerkleContext> for CompressedTokenAccount {
69    fn into(self) -> light_token::compat::TokenDataWithMerkleContext {
70        let compressed_account = CompressedAccountWithMerkleContext::from(self.account);
71
72        light_token::compat::TokenDataWithMerkleContext {
73            token_data: self.token,
74            compressed_account,
75        }
76    }
77}
78
79#[allow(clippy::from_over_into)]
80impl Into<Vec<light_token::compat::TokenDataWithMerkleContext>>
81    for super::super::response::Response<
82        super::super::response::ItemsWithCursor<CompressedTokenAccount>,
83    >
84{
85    fn into(self) -> Vec<light_token::compat::TokenDataWithMerkleContext> {
86        self.value
87            .items
88            .into_iter()
89            .map(
90                |token_account| light_token::compat::TokenDataWithMerkleContext {
91                    token_data: token_account.token,
92                    compressed_account: CompressedAccountWithMerkleContext::from(
93                        token_account.account.clone(),
94                    ),
95                },
96            )
97            .collect::<Vec<light_token::compat::TokenDataWithMerkleContext>>()
98    }
99}
100
101impl TryFrom<light_token::compat::TokenDataWithMerkleContext> for CompressedTokenAccount {
102    type Error = IndexerError;
103
104    fn try_from(
105        token_data_with_context: light_token::compat::TokenDataWithMerkleContext,
106    ) -> Result<Self, Self::Error> {
107        let account = CompressedAccount::try_from(token_data_with_context.compressed_account)?;
108
109        Ok(CompressedTokenAccount {
110            token: token_data_with_context.token_data,
111            account,
112        })
113    }
114}
115
116#[derive(Clone, Default, Debug, PartialEq)]
117pub struct TokenBalance {
118    pub balance: u64,
119    pub mint: Pubkey,
120}
121
122impl TryFrom<&photon_api::types::TokenBalance> for TokenBalance {
123    type Error = IndexerError;
124
125    fn try_from(token_balance: &photon_api::types::TokenBalance) -> Result<Self, Self::Error> {
126        Ok(TokenBalance {
127            balance: *token_balance.balance,
128            mint: Pubkey::new_from_array(decode_base58_to_fixed_array(&token_balance.mint)?),
129        })
130    }
131}
132
133#[derive(Debug, Clone, PartialEq, Default)]
134pub struct OwnerBalance {
135    pub balance: u64,
136    pub owner: Pubkey,
137}
138
139impl TryFrom<&photon_api::types::OwnerBalance> for OwnerBalance {
140    type Error = IndexerError;
141
142    fn try_from(owner_balance: &photon_api::types::OwnerBalance) -> Result<Self, Self::Error> {
143        Ok(OwnerBalance {
144            balance: *owner_balance.balance,
145            owner: Pubkey::new_from_array(decode_base58_to_fixed_array(&owner_balance.owner)?),
146        })
147    }
148}