Skip to main content

light_client/indexer/types/
account.rs

1use light_compressed_account::{
2    compressed_account::{
3        CompressedAccount as ProgramCompressedAccount, CompressedAccountData,
4        CompressedAccountWithMerkleContext,
5    },
6    TreeType,
7};
8use solana_pubkey::Pubkey;
9use tracing::warn;
10
11use super::{
12    super::{base58::decode_base58_to_fixed_array, tree_info::QUEUE_TREE_MAPPING, IndexerError},
13    tree::{NextTreeInfo, TreeInfo},
14};
15
16#[derive(Clone, Default, Debug, PartialEq)]
17pub struct CompressedAccount {
18    pub address: Option<[u8; 32]>,
19    pub data: Option<CompressedAccountData>,
20    pub hash: [u8; 32],
21    pub lamports: u64,
22    pub leaf_index: u32,
23    pub owner: Pubkey,
24    pub prove_by_index: bool,
25    pub seq: Option<u64>,
26    pub slot_created: u64,
27    pub tree_info: TreeInfo,
28}
29
30impl TryFrom<CompressedAccountWithMerkleContext> for CompressedAccount {
31    type Error = IndexerError;
32
33    fn try_from(account: CompressedAccountWithMerkleContext) -> Result<Self, Self::Error> {
34        let hash = account
35            .hash()
36            .map_err(|e| IndexerError::decode_error("data", e))?;
37        // Breaks light-program-test
38        let tree_info = QUEUE_TREE_MAPPING.get(
39            &Pubkey::new_from_array(account.merkle_context.merkle_tree_pubkey.to_bytes())
40                .to_string(),
41        );
42        let cpi_context = if let Some(tree_info) = tree_info {
43            tree_info.cpi_context
44        } else {
45            warn!("Cpi context not found in queue tree mapping");
46            None
47        };
48        Ok(CompressedAccount {
49            address: account.compressed_account.address,
50            data: account.compressed_account.data,
51            hash,
52            lamports: account.compressed_account.lamports,
53            leaf_index: account.merkle_context.leaf_index,
54            tree_info: TreeInfo {
55                tree: Pubkey::new_from_array(account.merkle_context.merkle_tree_pubkey.to_bytes()),
56                queue: Pubkey::new_from_array(account.merkle_context.queue_pubkey.to_bytes()),
57                tree_type: account.merkle_context.tree_type,
58                cpi_context,
59                next_tree_info: None,
60            },
61            owner: Pubkey::new_from_array(account.compressed_account.owner.to_bytes()),
62            prove_by_index: account.merkle_context.prove_by_index,
63            seq: None,
64            slot_created: u64::MAX,
65        })
66    }
67}
68
69impl From<CompressedAccount> for CompressedAccountWithMerkleContext {
70    fn from(account: CompressedAccount) -> Self {
71        use light_compressed_account::Pubkey;
72        let compressed_account = ProgramCompressedAccount {
73            owner: Pubkey::new_from_array(account.owner.to_bytes()),
74            lamports: account.lamports,
75            address: account.address,
76            data: account.data,
77        };
78
79        let merkle_context = account
80            .tree_info
81            .to_light_merkle_context(account.leaf_index, account.prove_by_index);
82
83        CompressedAccountWithMerkleContext {
84            compressed_account,
85            merkle_context,
86        }
87    }
88}
89
90impl TryFrom<&photon_api::types::AccountV2> for CompressedAccount {
91    type Error = IndexerError;
92
93    fn try_from(account: &photon_api::types::AccountV2) -> Result<Self, Self::Error> {
94        let data = if let Some(data) = &account.data {
95            Ok::<Option<CompressedAccountData>, IndexerError>(Some(CompressedAccountData {
96                discriminator: (*data.discriminator).to_le_bytes(),
97                data: base64::decode_config(&*data.data, base64::STANDARD_NO_PAD)
98                    .map_err(|e| IndexerError::decode_error("data", e))?,
99                data_hash: decode_base58_to_fixed_array(&data.data_hash)?,
100            }))
101        } else {
102            Ok::<Option<CompressedAccountData>, IndexerError>(None)
103        }?;
104
105        let owner = Pubkey::new_from_array(decode_base58_to_fixed_array(&account.owner)?);
106        let address = account
107            .address
108            .as_ref()
109            .map(|address| decode_base58_to_fixed_array(address))
110            .transpose()?;
111        let hash = decode_base58_to_fixed_array(&account.hash)?;
112
113        let tree_info = TreeInfo {
114            tree: Pubkey::new_from_array(decode_base58_to_fixed_array(
115                &account.merkle_context.tree,
116            )?),
117            queue: Pubkey::new_from_array(decode_base58_to_fixed_array(
118                &account.merkle_context.queue,
119            )?),
120            tree_type: TreeType::from(account.merkle_context.tree_type as u64),
121            cpi_context: super::super::base58::decode_base58_option_to_pubkey(
122                &account.merkle_context.cpi_context,
123            )?,
124            next_tree_info: account
125                .merkle_context
126                .next_tree_context
127                .as_ref()
128                .map(NextTreeInfo::try_from)
129                .transpose()?,
130        };
131
132        Ok(CompressedAccount {
133            owner,
134            address,
135            data,
136            hash,
137            lamports: *account.lamports,
138            leaf_index: *account.leaf_index as u32,
139            seq: account.seq.as_ref().map(|s| **s),
140            slot_created: *account.slot_created,
141            tree_info,
142            prove_by_index: account.prove_by_index,
143        })
144    }
145}
146
147impl TryFrom<&photon_api::types::Account> for CompressedAccount {
148    type Error = IndexerError;
149
150    fn try_from(account: &photon_api::types::Account) -> Result<Self, Self::Error> {
151        let data = if let Some(data) = &account.data {
152            Ok::<Option<CompressedAccountData>, IndexerError>(Some(CompressedAccountData {
153                discriminator: (*data.discriminator).to_le_bytes(),
154                data: base64::decode_config(&*data.data, base64::STANDARD_NO_PAD)
155                    .map_err(|e| IndexerError::decode_error("data", e))?,
156                data_hash: decode_base58_to_fixed_array(&data.data_hash)?,
157            }))
158        } else {
159            Ok::<Option<CompressedAccountData>, IndexerError>(None)
160        }?;
161        let owner = Pubkey::new_from_array(decode_base58_to_fixed_array(&account.owner)?);
162        let address = account
163            .address
164            .as_ref()
165            .map(|address| decode_base58_to_fixed_array(address))
166            .transpose()?;
167        let hash = decode_base58_to_fixed_array(&account.hash)?;
168        let seq = account.seq.as_ref().map(|s| **s);
169        let slot_created = *account.slot_created;
170        let lamports = *account.lamports;
171        let leaf_index = *account.leaf_index as u32;
172
173        let tree_info =
174            QUEUE_TREE_MAPPING
175                .get(&*account.tree)
176                .ok_or(IndexerError::MissingResult {
177                    context: "conversion".into(),
178                    message: "expected value was None".into(),
179                })?;
180
181        let tree_info = TreeInfo {
182            cpi_context: tree_info.cpi_context,
183            queue: tree_info.queue,
184            tree_type: tree_info.tree_type,
185            next_tree_info: None,
186            tree: tree_info.tree,
187        };
188
189        Ok(CompressedAccount {
190            owner,
191            address,
192            data,
193            hash,
194            lamports,
195            leaf_index,
196            seq,
197            slot_created,
198            tree_info,
199            prove_by_index: false,
200        })
201    }
202}