1use light_compressed_account::{
2 compressed_account::{
3 CompressedAccount as ProgramCompressedAccount, CompressedAccountData,
4 CompressedAccountWithMerkleContext,
5 },
6 instruction_data::compressed_proof::CompressedProof,
7 TreeType,
8};
9use light_ctoken_sdk::compat::{AccountState, TokenData};
10use light_indexed_merkle_tree::array::IndexedElement;
11use light_sdk::instruction::{
12 PackedAccounts, PackedAddressTreeInfo, PackedStateTreeInfo, ValidityProof,
13};
14use num_bigint::BigUint;
15use solana_pubkey::Pubkey;
16use tracing::warn;
17
18use super::{
19 base58::{decode_base58_option_to_pubkey, decode_base58_to_fixed_array},
20 tree_info::QUEUE_TREE_MAPPING,
21 IndexerError,
22};
23
24pub struct ProofOfLeaf {
25 pub leaf: [u8; 32],
26 pub proof: Vec<[u8; 32]>,
27}
28
29pub type Address = [u8; 32];
30pub type Hash = [u8; 32];
31
32#[derive(Debug, Clone, PartialEq)]
33pub struct QueueInfo {
34 pub tree: Pubkey,
35 pub queue: Pubkey,
36 pub queue_type: u8,
37 pub queue_size: u64,
38}
39
40#[derive(Debug, Clone, PartialEq, Default)]
41pub struct QueueInfoResult {
42 pub queues: Vec<QueueInfo>,
43 pub slot: u64,
44}
45
46#[derive(Debug, Clone, PartialEq, Default)]
47pub struct QueueElementsResult {
48 pub output_queue_elements: Option<Vec<MerkleProofWithContext>>,
49 pub output_queue_index: Option<u64>,
50 pub input_queue_elements: Option<Vec<MerkleProofWithContext>>,
51 pub input_queue_index: Option<u64>,
52}
53
54#[derive(Debug, Clone, PartialEq, Default)]
56pub struct OutputQueueDataV2 {
57 pub leaf_indices: Vec<u64>,
58 pub account_hashes: Vec<[u8; 32]>,
59 pub old_leaves: Vec<[u8; 32]>,
60 pub first_queue_index: u64,
61 pub next_index: u64,
63 pub leaves_hash_chains: Vec<[u8; 32]>,
65}
66
67#[derive(Debug, Clone, PartialEq, Default)]
69pub struct InputQueueDataV2 {
70 pub leaf_indices: Vec<u64>,
71 pub account_hashes: Vec<[u8; 32]>,
72 pub current_leaves: Vec<[u8; 32]>,
73 pub tx_hashes: Vec<[u8; 32]>,
74 pub nullifiers: Vec<[u8; 32]>,
76 pub first_queue_index: u64,
77 pub leaves_hash_chains: Vec<[u8; 32]>,
79}
80
81#[derive(Debug, Clone, PartialEq, Default)]
83pub struct StateQueueDataV2 {
84 pub nodes: Vec<u64>,
87 pub node_hashes: Vec<[u8; 32]>,
88 pub initial_root: [u8; 32],
90 pub root_seq: u64,
92 pub output_queue: Option<OutputQueueDataV2>,
94 pub input_queue: Option<InputQueueDataV2>,
96}
97
98#[derive(Debug, Clone, PartialEq, Default)]
100pub struct AddressQueueDataV2 {
101 pub addresses: Vec<[u8; 32]>,
102 pub low_element_values: Vec<[u8; 32]>,
103 pub low_element_next_values: Vec<[u8; 32]>,
104 pub low_element_indices: Vec<u64>,
105 pub low_element_next_indices: Vec<u64>,
106 pub low_element_proofs: Vec<Vec<[u8; 32]>>,
107 pub nodes: Vec<u64>,
108 pub node_hashes: Vec<[u8; 32]>,
109 pub initial_root: [u8; 32],
110 pub first_queue_index: u64,
111}
112
113#[derive(Debug, Clone, PartialEq, Default)]
115pub struct QueueElementsV2Result {
116 pub state_queue: Option<StateQueueDataV2>,
117 pub address_queue: Option<AddressQueueDataV2>,
118}
119
120#[derive(Debug, Clone, PartialEq, Default)]
121pub struct MerkleProofWithContext {
122 pub proof: Vec<[u8; 32]>,
123 pub root: [u8; 32],
124 pub leaf_index: u64,
125 pub leaf: [u8; 32],
126 pub merkle_tree: [u8; 32],
127 pub root_seq: u64,
128 pub tx_hash: Option<[u8; 32]>,
129 pub account_hash: [u8; 32],
130}
131
132#[derive(Debug, Clone, PartialEq, Default)]
133pub struct MerkleProof {
134 pub hash: [u8; 32],
135 pub leaf_index: u64,
136 pub merkle_tree: Pubkey,
137 pub proof: Vec<[u8; 32]>,
138 pub root_seq: u64,
139 pub root: [u8; 32],
140}
141
142#[derive(Debug, Clone, Copy, PartialEq)]
143pub struct AddressWithTree {
144 pub address: Address,
145 pub tree: Pubkey,
146}
147
148#[derive(Clone, Default, Debug, PartialEq)]
149pub struct NewAddressProofWithContext {
150 pub merkle_tree: Pubkey,
151 pub root: [u8; 32],
152 pub root_seq: u64,
153 pub low_address_index: u64,
154 pub low_address_value: [u8; 32],
155 pub low_address_next_index: u64,
156 pub low_address_next_value: [u8; 32],
157 pub low_address_proof: Vec<[u8; 32]>,
158 pub new_low_element: Option<IndexedElement<usize>>,
159 pub new_element: Option<IndexedElement<usize>>,
160 pub new_element_next_value: Option<BigUint>,
161}
162
163#[derive(Debug, Default, Clone, PartialEq)]
164pub struct ValidityProofWithContext {
165 pub proof: ValidityProof,
166 pub accounts: Vec<AccountProofInputs>,
167 pub addresses: Vec<AddressProofInputs>,
168}
169
170impl ValidityProofWithContext {
173 pub fn get_root_indices(&self) -> Vec<Option<u16>> {
174 self.accounts
175 .iter()
176 .map(|account| account.root_index.root_index())
177 .collect()
178 }
179
180 pub fn get_address_root_indices(&self) -> Vec<u16> {
181 self.addresses
182 .iter()
183 .map(|address| address.root_index)
184 .collect()
185 }
186}
187
188#[derive(Clone, Default, Debug, PartialEq)]
189pub struct AccountProofInputs {
190 pub hash: [u8; 32],
191 pub root: [u8; 32],
192 pub root_index: RootIndex,
193 pub leaf_index: u64,
194 pub tree_info: TreeInfo,
195}
196
197#[derive(Clone, Default, Copy, Debug, PartialEq)]
198pub struct RootIndex {
199 proof_by_index: bool,
200 root_index: u16,
201}
202
203impl RootIndex {
204 pub fn new_none() -> Self {
205 Self {
206 proof_by_index: true,
207 root_index: 0,
208 }
209 }
210
211 pub fn new_some(root_index: u16) -> Self {
212 Self {
213 proof_by_index: false,
214 root_index,
215 }
216 }
217
218 pub fn proof_by_index(&self) -> bool {
219 self.proof_by_index
220 }
221
222 pub fn root_index(&self) -> Option<u16> {
223 if !self.proof_by_index {
224 Some(self.root_index)
225 } else {
226 None
227 }
228 }
229}
230
231impl AccountProofInputs {
232 pub fn from_api_model(
233 value: &photon_api::models::AccountProofInputs,
234 ) -> Result<Self, IndexerError> {
235 let root_index = {
236 if value.root_index.prove_by_index {
237 RootIndex::new_none()
238 } else {
239 RootIndex::new_some(value.root_index.root_index)
240 }
241 };
242 Ok(Self {
243 hash: decode_base58_to_fixed_array(&value.hash)?,
244 root: decode_base58_to_fixed_array(&value.root)?,
245 root_index,
246 leaf_index: value.leaf_index,
247 tree_info: TreeInfo::from_api_model(&value.merkle_context)?,
248 })
249 }
250}
251
252#[derive(Clone, Default, Debug, PartialEq)]
253pub struct AddressProofInputs {
254 pub address: [u8; 32],
255 pub root: [u8; 32],
256 pub root_index: u16,
257 pub tree_info: TreeInfo,
258}
259
260impl AddressProofInputs {
261 pub fn from_api_model(
262 value: &photon_api::models::AddressProofInputs,
263 ) -> Result<Self, IndexerError> {
264 Ok(Self {
265 address: decode_base58_to_fixed_array(&value.address)?,
266 root: decode_base58_to_fixed_array(&value.root)?,
267 root_index: value.root_index,
268 tree_info: TreeInfo::from_api_model(&value.merkle_context)?,
269 })
270 }
271}
272
273#[derive(Clone, Default, Debug, PartialEq)]
274pub struct PackedStateTreeInfos {
275 pub packed_tree_infos: Vec<PackedStateTreeInfo>,
276 pub output_tree_index: u8,
277}
278
279#[derive(Clone, Default, Debug, PartialEq)]
280pub struct PackedTreeInfos {
281 pub state_trees: Option<PackedStateTreeInfos>,
282 pub address_trees: Vec<PackedAddressTreeInfo>,
283}
284
285impl ValidityProofWithContext {
286 pub fn pack_tree_infos(&self, packed_accounts: &mut PackedAccounts) -> PackedTreeInfos {
287 let mut packed_tree_infos = Vec::new();
288 let mut address_trees = Vec::new();
289 let mut output_tree_index = None;
290 for account in self.accounts.iter() {
291 let merkle_tree_pubkey_index = packed_accounts.insert_or_get(account.tree_info.tree);
293 let queue_pubkey_index = packed_accounts.insert_or_get(account.tree_info.queue);
294 let tree_info_packed = PackedStateTreeInfo {
295 root_index: account.root_index.root_index,
296 merkle_tree_pubkey_index,
297 queue_pubkey_index,
298 leaf_index: account.leaf_index as u32,
299 prove_by_index: account.root_index.proof_by_index(),
300 };
301 packed_tree_infos.push(tree_info_packed);
302
303 if let Some(next) = account.tree_info.next_tree_info {
306 let index = next.pack_output_tree_index(packed_accounts).unwrap();
309 if output_tree_index.is_none() {
310 output_tree_index = Some(index);
311 }
312 } else {
313 let index = account
316 .tree_info
317 .pack_output_tree_index(packed_accounts)
318 .unwrap();
319 if output_tree_index.is_none() {
320 output_tree_index = Some(index);
321 }
322 }
323 }
324
325 for address in self.addresses.iter() {
326 let address_merkle_tree_pubkey_index =
328 packed_accounts.insert_or_get(address.tree_info.tree);
329 let address_queue_pubkey_index = packed_accounts.insert_or_get(address.tree_info.queue);
330 address_trees.push(PackedAddressTreeInfo {
331 address_merkle_tree_pubkey_index,
332 address_queue_pubkey_index,
333 root_index: address.root_index,
334 });
335 }
336 let packed_tree_infos = if packed_tree_infos.is_empty() {
337 None
338 } else {
339 Some(PackedStateTreeInfos {
340 packed_tree_infos,
341 output_tree_index: output_tree_index.unwrap(),
342 })
343 };
344 PackedTreeInfos {
345 state_trees: packed_tree_infos,
346 address_trees,
347 }
348 }
349
350 pub fn from_api_model(
351 value: photon_api::models::CompressedProofWithContext,
352 num_hashes: usize,
353 ) -> Result<Self, IndexerError> {
354 let proof = ValidityProof::new(Some(CompressedProof {
355 a: value
356 .compressed_proof
357 .a
358 .try_into()
359 .map_err(|_| IndexerError::InvalidResponseData)?,
360 b: value
361 .compressed_proof
362 .b
363 .try_into()
364 .map_err(|_| IndexerError::InvalidResponseData)?,
365 c: value
366 .compressed_proof
367 .c
368 .try_into()
369 .map_err(|_| IndexerError::InvalidResponseData)?,
370 }));
371
372 let accounts = (0..num_hashes)
374 .map(|i| {
375 let tree_pubkey =
376 Pubkey::new_from_array(decode_base58_to_fixed_array(&value.merkle_trees[i])?);
377 let tree_info = super::tree_info::QUEUE_TREE_MAPPING
378 .get(&value.merkle_trees[i])
379 .ok_or(IndexerError::InvalidResponseData)?;
380
381 Ok(AccountProofInputs {
382 hash: decode_base58_to_fixed_array(&value.leaves[i])?,
383 root: decode_base58_to_fixed_array(&value.roots[i])?,
384 root_index: RootIndex::new_some(value.root_indices[i] as u16),
385 leaf_index: value.leaf_indices[i] as u64,
386 tree_info: TreeInfo {
387 tree_type: tree_info.tree_type,
388 tree: tree_pubkey,
389 queue: tree_info.queue,
390 cpi_context: tree_info.cpi_context,
391 next_tree_info: None,
392 },
393 })
394 })
395 .collect::<Result<Vec<_>, IndexerError>>()?;
396
397 let addresses = if value.root_indices.len() > num_hashes {
399 (num_hashes..value.root_indices.len())
400 .map(|i| {
401 let tree_pubkey = Pubkey::new_from_array(decode_base58_to_fixed_array(
402 &value.merkle_trees[i],
403 )?);
404 let tree_info = super::tree_info::QUEUE_TREE_MAPPING
405 .get(&value.merkle_trees[i])
406 .ok_or(IndexerError::InvalidResponseData)?;
407
408 Ok(AddressProofInputs {
409 address: decode_base58_to_fixed_array(&value.leaves[i])?, root: decode_base58_to_fixed_array(&value.roots[i])?,
411 root_index: value.root_indices[i] as u16,
412 tree_info: TreeInfo {
413 tree_type: tree_info.tree_type,
414 tree: tree_pubkey,
415 queue: tree_info.queue,
416 cpi_context: tree_info.cpi_context,
417 next_tree_info: None,
418 },
419 })
420 })
421 .collect::<Result<Vec<_>, IndexerError>>()?
422 } else {
423 Vec::new()
424 };
425
426 Ok(Self {
427 proof,
428 accounts,
429 addresses,
430 })
431 }
432
433 pub fn from_api_model_v2(
434 value: photon_api::models::CompressedProofWithContextV2,
435 ) -> Result<Self, IndexerError> {
436 let proof = if let Some(proof) = value.compressed_proof {
437 ValidityProof::new(Some(CompressedProof {
438 a: proof
439 .a
440 .try_into()
441 .map_err(|_| IndexerError::InvalidResponseData)?,
442 b: proof
443 .b
444 .try_into()
445 .map_err(|_| IndexerError::InvalidResponseData)?,
446 c: proof
447 .c
448 .try_into()
449 .map_err(|_| IndexerError::InvalidResponseData)?,
450 }))
451 } else {
452 ValidityProof::new(None)
453 };
454
455 let accounts = value
456 .accounts
457 .iter()
458 .map(AccountProofInputs::from_api_model)
459 .collect::<Result<Vec<_>, IndexerError>>()?;
460
461 let addresses = value
462 .addresses
463 .iter()
464 .map(AddressProofInputs::from_api_model)
465 .collect::<Result<Vec<_>, IndexerError>>()?;
466
467 Ok(Self {
468 proof,
469 accounts,
470 addresses,
471 })
472 }
473}
474
475#[derive(Clone, Copy, Default, Debug, PartialEq)]
476pub struct NextTreeInfo {
477 pub cpi_context: Option<Pubkey>,
478 pub queue: Pubkey,
479 pub tree: Pubkey,
480 pub tree_type: TreeType,
481}
482
483impl NextTreeInfo {
484 pub fn pack_output_tree_index(
490 &self,
491 packed_accounts: &mut PackedAccounts,
492 ) -> Result<u8, IndexerError> {
493 match self.tree_type {
494 TreeType::StateV1 => Ok(packed_accounts.insert_or_get(self.tree)),
495 TreeType::StateV2 => Ok(packed_accounts.insert_or_get(self.queue)),
496 _ => Err(IndexerError::InvalidPackTreeType),
497 }
498 }
499 pub fn from_api_model(
500 value: &photon_api::models::TreeContextInfo,
501 ) -> Result<Self, IndexerError> {
502 Ok(Self {
503 tree_type: TreeType::from(value.tree_type as u64),
504 tree: Pubkey::new_from_array(decode_base58_to_fixed_array(&value.tree)?),
505 queue: Pubkey::new_from_array(decode_base58_to_fixed_array(&value.queue)?),
506 cpi_context: decode_base58_option_to_pubkey(&value.cpi_context)?,
507 })
508 }
509}
510
511impl TryFrom<&photon_api::models::TreeContextInfo> for NextTreeInfo {
512 type Error = IndexerError;
513
514 fn try_from(value: &photon_api::models::TreeContextInfo) -> Result<Self, Self::Error> {
515 Ok(Self {
516 tree_type: TreeType::from(value.tree_type as u64),
517 tree: Pubkey::new_from_array(decode_base58_to_fixed_array(&value.tree)?),
518 queue: Pubkey::new_from_array(decode_base58_to_fixed_array(&value.queue)?),
519 cpi_context: decode_base58_option_to_pubkey(&value.cpi_context)?,
520 })
521 }
522}
523
524#[derive(Clone, Copy, Default, Debug, PartialEq)]
525pub struct TreeInfo {
526 pub cpi_context: Option<Pubkey>,
527 pub next_tree_info: Option<NextTreeInfo>,
528 pub queue: Pubkey,
529 pub tree: Pubkey,
530 pub tree_type: TreeType,
531}
532
533impl TreeInfo {
534 pub fn pack_output_tree_index(
540 &self,
541 packed_accounts: &mut PackedAccounts,
542 ) -> Result<u8, IndexerError> {
543 match self.tree_type {
544 TreeType::StateV1 => Ok(packed_accounts.insert_or_get(self.tree)),
545 TreeType::StateV2 => Ok(packed_accounts.insert_or_get(self.queue)),
546 _ => Err(IndexerError::InvalidPackTreeType),
547 }
548 }
549
550 pub fn get_output_pubkey(&self) -> Result<Pubkey, IndexerError> {
551 match self.tree_type {
552 TreeType::StateV1 => Ok(self.tree),
553 TreeType::StateV2 => Ok(self.queue),
554 _ => Err(IndexerError::InvalidPackTreeType),
555 }
556 }
557
558 pub fn from_api_model(
559 value: &photon_api::models::MerkleContextV2,
560 ) -> Result<Self, IndexerError> {
561 Ok(Self {
562 tree_type: TreeType::from(value.tree_type as u64),
563 tree: Pubkey::new_from_array(decode_base58_to_fixed_array(&value.tree)?),
564 queue: Pubkey::new_from_array(decode_base58_to_fixed_array(&value.queue)?),
565 cpi_context: decode_base58_option_to_pubkey(&value.cpi_context)?,
566 next_tree_info: value
567 .next_tree_context
568 .as_ref()
569 .map(|tree_info| NextTreeInfo::from_api_model(tree_info.as_ref()))
570 .transpose()?,
571 })
572 }
573
574 pub fn to_light_merkle_context(
575 &self,
576 leaf_index: u32,
577 prove_by_index: bool,
578 ) -> light_compressed_account::compressed_account::MerkleContext {
579 use light_compressed_account::Pubkey;
580 light_compressed_account::compressed_account::MerkleContext {
581 merkle_tree_pubkey: Pubkey::new_from_array(self.tree.to_bytes()),
582 queue_pubkey: Pubkey::new_from_array(self.queue.to_bytes()),
583 leaf_index,
584 tree_type: self.tree_type,
585 prove_by_index,
586 }
587 }
588}
589
590#[derive(Clone, Default, Debug, PartialEq)]
591pub struct CompressedAccount {
592 pub address: Option<[u8; 32]>,
593 pub data: Option<CompressedAccountData>,
594 pub hash: [u8; 32],
595 pub lamports: u64,
596 pub leaf_index: u32,
597 pub owner: Pubkey,
598 pub prove_by_index: bool,
599 pub seq: Option<u64>,
600 pub slot_created: u64,
601 pub tree_info: TreeInfo,
602}
603
604impl TryFrom<CompressedAccountWithMerkleContext> for CompressedAccount {
605 type Error = IndexerError;
606
607 fn try_from(account: CompressedAccountWithMerkleContext) -> Result<Self, Self::Error> {
608 let hash = account
609 .hash()
610 .map_err(|_| IndexerError::InvalidResponseData)?;
611 let tree_info = QUEUE_TREE_MAPPING.get(
613 &Pubkey::new_from_array(account.merkle_context.merkle_tree_pubkey.to_bytes())
614 .to_string(),
615 );
616 let cpi_context = if let Some(tree_info) = tree_info {
617 tree_info.cpi_context
618 } else {
619 warn!("Cpi context not found in queue tree mapping");
620 None
621 };
622 Ok(CompressedAccount {
623 address: account.compressed_account.address,
624 data: account.compressed_account.data,
625 hash,
626 lamports: account.compressed_account.lamports,
627 leaf_index: account.merkle_context.leaf_index,
628 tree_info: TreeInfo {
629 tree: Pubkey::new_from_array(account.merkle_context.merkle_tree_pubkey.to_bytes()),
630 queue: Pubkey::new_from_array(account.merkle_context.queue_pubkey.to_bytes()),
631 tree_type: account.merkle_context.tree_type,
632 cpi_context,
633 next_tree_info: None,
634 },
635 owner: Pubkey::new_from_array(account.compressed_account.owner.to_bytes()),
636 prove_by_index: account.merkle_context.prove_by_index,
637 seq: None,
638 slot_created: u64::MAX,
639 })
640 }
641}
642
643impl From<CompressedAccount> for CompressedAccountWithMerkleContext {
644 fn from(account: CompressedAccount) -> Self {
645 use light_compressed_account::Pubkey;
646 let compressed_account = ProgramCompressedAccount {
647 owner: Pubkey::new_from_array(account.owner.to_bytes()),
648 lamports: account.lamports,
649 address: account.address,
650 data: account.data,
651 };
652
653 let merkle_context = account
654 .tree_info
655 .to_light_merkle_context(account.leaf_index, account.prove_by_index);
656
657 CompressedAccountWithMerkleContext {
658 compressed_account,
659 merkle_context,
660 }
661 }
662}
663
664impl TryFrom<&photon_api::models::AccountV2> for CompressedAccount {
665 type Error = IndexerError;
666
667 fn try_from(account: &photon_api::models::AccountV2) -> Result<Self, Self::Error> {
668 let data = if let Some(data) = &account.data {
669 Ok::<Option<CompressedAccountData>, IndexerError>(Some(CompressedAccountData {
670 discriminator: data.discriminator.to_le_bytes(),
671 data: base64::decode_config(&data.data, base64::STANDARD_NO_PAD)
672 .map_err(|_| IndexerError::InvalidResponseData)?,
673 data_hash: decode_base58_to_fixed_array(&data.data_hash)?,
674 }))
675 } else {
676 Ok::<Option<CompressedAccountData>, IndexerError>(None)
677 }?;
678
679 let owner = Pubkey::new_from_array(decode_base58_to_fixed_array(&account.owner)?);
680 let address = account
681 .address
682 .as_ref()
683 .map(|address| decode_base58_to_fixed_array(address))
684 .transpose()?;
685 let hash = decode_base58_to_fixed_array(&account.hash)?;
686
687 let tree_info = TreeInfo {
688 tree: Pubkey::new_from_array(decode_base58_to_fixed_array(
689 &account.merkle_context.tree,
690 )?),
691 queue: Pubkey::new_from_array(decode_base58_to_fixed_array(
692 &account.merkle_context.queue,
693 )?),
694 tree_type: TreeType::from(account.merkle_context.tree_type as u64),
695 cpi_context: decode_base58_option_to_pubkey(&account.merkle_context.cpi_context)?,
696 next_tree_info: account
697 .merkle_context
698 .next_tree_context
699 .as_ref()
700 .map(|ctx| NextTreeInfo::try_from(ctx.as_ref()))
701 .transpose()?,
702 };
703
704 Ok(CompressedAccount {
705 owner,
706 address,
707 data,
708 hash,
709 lamports: account.lamports,
710 leaf_index: account.leaf_index,
711 seq: account.seq,
712 slot_created: account.slot_created,
713 tree_info,
714 prove_by_index: account.prove_by_index,
715 })
716 }
717}
718
719impl TryFrom<&photon_api::models::Account> for CompressedAccount {
720 type Error = IndexerError;
721
722 fn try_from(account: &photon_api::models::Account) -> Result<Self, Self::Error> {
723 let data = if let Some(data) = &account.data {
724 Ok::<Option<CompressedAccountData>, IndexerError>(Some(CompressedAccountData {
725 discriminator: data.discriminator.to_le_bytes(),
726 data: base64::decode_config(&data.data, base64::STANDARD_NO_PAD)
727 .map_err(|_| IndexerError::InvalidResponseData)?,
728 data_hash: decode_base58_to_fixed_array(&data.data_hash)?,
729 }))
730 } else {
731 Ok::<Option<CompressedAccountData>, IndexerError>(None)
732 }?;
733 let owner = Pubkey::new_from_array(decode_base58_to_fixed_array(&account.owner)?);
734 let address = account
735 .address
736 .as_ref()
737 .map(|address| decode_base58_to_fixed_array(address))
738 .transpose()?;
739 let hash = decode_base58_to_fixed_array(&account.hash)?;
740 let seq = account.seq;
741 let slot_created = account.slot_created;
742 let lamports = account.lamports;
743 let leaf_index = account.leaf_index;
744
745 let tree_info = QUEUE_TREE_MAPPING
746 .get(&account.tree)
747 .ok_or(IndexerError::InvalidResponseData)?;
748
749 let tree_info = TreeInfo {
750 cpi_context: tree_info.cpi_context,
751 queue: tree_info.queue,
752 tree_type: tree_info.tree_type,
753 next_tree_info: None,
754 tree: tree_info.tree,
755 };
756
757 Ok(CompressedAccount {
758 owner,
759 address,
760 data,
761 hash,
762 lamports,
763 leaf_index,
764 seq,
765 slot_created,
766 tree_info,
767 prove_by_index: false,
768 })
769 }
770}
771
772#[derive(Debug, Clone, PartialEq, Default)]
773pub struct AddressQueueIndex {
774 pub address: [u8; 32],
775 pub queue_index: u64,
776}
777
778#[derive(Debug, Clone, PartialEq, Default)]
779pub struct BatchAddressUpdateIndexerResponse {
780 pub batch_start_index: u64,
781 pub addresses: Vec<AddressQueueIndex>,
782 pub non_inclusion_proofs: Vec<NewAddressProofWithContext>,
783 pub subtrees: Vec<[u8; 32]>,
784}
785
786#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
787pub struct StateMerkleTreeAccounts {
788 pub merkle_tree: Pubkey,
789 pub nullifier_queue: Pubkey,
790 pub cpi_context: Pubkey,
791 pub tree_type: TreeType,
792}
793
794#[allow(clippy::from_over_into)]
795impl Into<TreeInfo> for StateMerkleTreeAccounts {
796 fn into(self) -> TreeInfo {
797 TreeInfo {
798 tree: self.merkle_tree,
799 queue: self.nullifier_queue,
800 cpi_context: Some(self.cpi_context),
801 tree_type: self.tree_type,
802 next_tree_info: None,
803 }
804 }
805}
806
807#[derive(Debug, Clone, Copy)]
808pub struct AddressMerkleTreeAccounts {
809 pub merkle_tree: Pubkey,
810 pub queue: Pubkey,
811}
812
813#[derive(Clone, Default, Debug, PartialEq)]
814pub struct CompressedTokenAccount {
815 pub token: TokenData,
817 pub account: CompressedAccount,
819}
820
821impl TryFrom<&photon_api::models::TokenAccount> for CompressedTokenAccount {
822 type Error = IndexerError;
823
824 fn try_from(token_account: &photon_api::models::TokenAccount) -> Result<Self, Self::Error> {
825 let account = CompressedAccount::try_from(token_account.account.as_ref())?;
826
827 let token = TokenData {
828 mint: Pubkey::new_from_array(decode_base58_to_fixed_array(
829 &token_account.token_data.mint,
830 )?),
831 owner: Pubkey::new_from_array(decode_base58_to_fixed_array(
832 &token_account.token_data.owner,
833 )?),
834 amount: token_account.token_data.amount,
835 delegate: token_account
836 .token_data
837 .delegate
838 .as_ref()
839 .map(|d| decode_base58_to_fixed_array(d).map(Pubkey::new_from_array))
840 .transpose()?,
841 state: match token_account.token_data.state {
842 photon_api::models::AccountState::Initialized => AccountState::Initialized,
843 photon_api::models::AccountState::Frozen => AccountState::Frozen,
844 },
845 tlv: token_account
846 .token_data
847 .tlv
848 .as_ref()
849 .map(|tlv| base64::decode_config(tlv, base64::STANDARD_NO_PAD))
850 .transpose()
851 .map_err(|_| IndexerError::InvalidResponseData)?,
852 };
853
854 Ok(CompressedTokenAccount { token, account })
855 }
856}
857
858impl TryFrom<&photon_api::models::TokenAccountV2> for CompressedTokenAccount {
859 type Error = IndexerError;
860
861 fn try_from(token_account: &photon_api::models::TokenAccountV2) -> Result<Self, Self::Error> {
862 let account = CompressedAccount::try_from(token_account.account.as_ref())?;
863
864 let token = TokenData {
865 mint: Pubkey::new_from_array(decode_base58_to_fixed_array(
866 &token_account.token_data.mint,
867 )?),
868 owner: Pubkey::new_from_array(decode_base58_to_fixed_array(
869 &token_account.token_data.owner,
870 )?),
871 amount: token_account.token_data.amount,
872 delegate: token_account
873 .token_data
874 .delegate
875 .as_ref()
876 .map(|d| decode_base58_to_fixed_array(d).map(Pubkey::new_from_array))
877 .transpose()?,
878 state: match token_account.token_data.state {
879 photon_api::models::AccountState::Initialized => AccountState::Initialized,
880 photon_api::models::AccountState::Frozen => AccountState::Frozen,
881 },
882 tlv: token_account
883 .token_data
884 .tlv
885 .as_ref()
886 .map(|tlv| base64::decode_config(tlv, base64::STANDARD_NO_PAD))
887 .transpose()
888 .map_err(|_| IndexerError::InvalidResponseData)?,
889 };
890
891 Ok(CompressedTokenAccount { token, account })
892 }
893}
894
895#[allow(clippy::from_over_into)]
896impl Into<light_ctoken_sdk::compat::TokenDataWithMerkleContext> for CompressedTokenAccount {
897 fn into(self) -> light_ctoken_sdk::compat::TokenDataWithMerkleContext {
898 let compressed_account = CompressedAccountWithMerkleContext::from(self.account);
899
900 light_ctoken_sdk::compat::TokenDataWithMerkleContext {
901 token_data: self.token,
902 compressed_account,
903 }
904 }
905}
906
907#[allow(clippy::from_over_into)]
908impl Into<Vec<light_ctoken_sdk::compat::TokenDataWithMerkleContext>>
909 for super::response::Response<super::response::ItemsWithCursor<CompressedTokenAccount>>
910{
911 fn into(self) -> Vec<light_ctoken_sdk::compat::TokenDataWithMerkleContext> {
912 self.value
913 .items
914 .into_iter()
915 .map(
916 |token_account| light_ctoken_sdk::compat::TokenDataWithMerkleContext {
917 token_data: token_account.token,
918 compressed_account: CompressedAccountWithMerkleContext::from(
919 token_account.account.clone(),
920 ),
921 },
922 )
923 .collect::<Vec<light_ctoken_sdk::compat::TokenDataWithMerkleContext>>()
924 }
925}
926
927impl TryFrom<light_ctoken_sdk::compat::TokenDataWithMerkleContext> for CompressedTokenAccount {
928 type Error = IndexerError;
929
930 fn try_from(
931 token_data_with_context: light_ctoken_sdk::compat::TokenDataWithMerkleContext,
932 ) -> Result<Self, Self::Error> {
933 let account = CompressedAccount::try_from(token_data_with_context.compressed_account)?;
934
935 Ok(CompressedTokenAccount {
936 token: token_data_with_context.token_data,
937 account,
938 })
939 }
940}
941
942#[derive(Clone, Default, Debug, PartialEq)]
943pub struct TokenBalance {
944 pub balance: u64,
945 pub mint: Pubkey,
946}
947
948impl TryFrom<&photon_api::models::TokenBalance> for TokenBalance {
949 type Error = IndexerError;
950
951 fn try_from(token_balance: &photon_api::models::TokenBalance) -> Result<Self, Self::Error> {
952 Ok(TokenBalance {
953 balance: token_balance.balance,
954 mint: Pubkey::new_from_array(decode_base58_to_fixed_array(&token_balance.mint)?),
955 })
956 }
957}
958
959#[derive(Debug, Clone, PartialEq, Default)]
960pub struct SignatureWithMetadata {
961 pub block_time: u64,
962 pub signature: String,
963 pub slot: u64,
964}
965
966impl TryFrom<&photon_api::models::SignatureInfo> for SignatureWithMetadata {
967 type Error = IndexerError;
968
969 fn try_from(sig_info: &photon_api::models::SignatureInfo) -> Result<Self, Self::Error> {
970 Ok(SignatureWithMetadata {
971 block_time: sig_info.block_time,
972 signature: sig_info.signature.clone(),
973 slot: sig_info.slot,
974 })
975 }
976}
977
978#[derive(Clone, Default, Debug, PartialEq)]
979pub struct OwnerBalance {
980 pub balance: u64,
981 pub owner: Pubkey,
982}
983
984impl TryFrom<&photon_api::models::OwnerBalance> for OwnerBalance {
985 type Error = IndexerError;
986
987 fn try_from(owner_balance: &photon_api::models::OwnerBalance) -> Result<Self, Self::Error> {
988 Ok(OwnerBalance {
989 balance: owner_balance.balance,
990 owner: Pubkey::new_from_array(decode_base58_to_fixed_array(&owner_balance.owner)?),
991 })
992 }
993}