1use std::{
2 fmt::{Debug, Display, Formatter},
3 time::Duration,
4};
5
6use async_trait::async_trait;
7use borsh::BorshDeserialize;
8use bs58;
9use light_compressed_account::{
10 indexer_event::{
11 event::{BatchPublicTransactionEvent, PublicTransactionEvent},
12 parse::event_from_light_transaction,
13 },
14 TreeType,
15};
16use solana_account::Account;
17use solana_clock::Slot;
18use solana_commitment_config::CommitmentConfig;
19use solana_hash::Hash;
20use solana_instruction::Instruction;
21use solana_keypair::Keypair;
22use solana_pubkey::{pubkey, Pubkey};
23use solana_rpc_client::rpc_client::RpcClient;
24use solana_rpc_client_api::config::{RpcSendTransactionConfig, RpcTransactionConfig};
25use solana_signature::Signature;
26use solana_transaction::Transaction;
27use solana_transaction_status_client_types::{
28 option_serializer::OptionSerializer, TransactionStatus, UiInstruction, UiTransactionEncoding,
29};
30use tokio::time::{sleep, Instant};
31use tracing::warn;
32
33use super::LightClientConfig;
34use crate::{
35 indexer::{photon_indexer::PhotonIndexer, Indexer, TreeInfo},
36 rpc::{
37 errors::RpcError,
38 get_light_state_tree_infos::{
39 default_state_tree_lookup_tables, get_light_state_tree_infos,
40 },
41 merkle_tree::MerkleTreeExt,
42 Rpc,
43 },
44};
45
46pub enum RpcUrl {
47 Testnet,
48 Devnet,
49 Localnet,
50 ZKTestnet,
51 Custom(String),
52}
53
54impl Display for RpcUrl {
55 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56 let str = match self {
57 RpcUrl::Testnet => "https://api.testnet.solana.com".to_string(),
58 RpcUrl::Devnet => "https://api.devnet.solana.com".to_string(),
59 RpcUrl::Localnet => "http://localhost:8899".to_string(),
60 RpcUrl::ZKTestnet => "https://zk-testnet.helius.dev:8899".to_string(),
61 RpcUrl::Custom(url) => url.clone(),
62 };
63 write!(f, "{}", str)
64 }
65}
66
67#[derive(Clone, Debug, Copy)]
68pub struct RetryConfig {
69 pub max_retries: u32,
70 pub retry_delay: Duration,
71 pub timeout: Duration,
74}
75
76impl Default for RetryConfig {
77 fn default() -> Self {
78 RetryConfig {
79 max_retries: 10,
80 retry_delay: Duration::from_secs(1),
81 timeout: Duration::from_secs(60),
82 }
83 }
84}
85
86#[allow(dead_code)]
87pub struct LightClient {
88 pub client: RpcClient,
89 pub payer: Keypair,
90 pub retry_config: RetryConfig,
91 pub indexer: Option<PhotonIndexer>,
92 pub state_merkle_trees: Vec<TreeInfo>,
93}
94
95impl Debug for LightClient {
96 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
97 write!(f, "LightClient {{ client: {:?} }}", self.client.url())
98 }
99}
100
101impl LightClient {
102 pub async fn new_with_retry(
103 config: LightClientConfig,
104 retry_config: Option<RetryConfig>,
105 ) -> Result<Self, RpcError> {
106 let payer = Keypair::new();
107 let commitment_config = config
108 .commitment_config
109 .unwrap_or(CommitmentConfig::confirmed());
110 let client = RpcClient::new_with_commitment(config.url.to_string(), commitment_config);
111 let retry_config = retry_config.unwrap_or_default();
112
113 let indexer = config
114 .photon_url
115 .map(|path| PhotonIndexer::new(path, config.api_key));
116
117 let mut new = Self {
118 client,
119 payer,
120 retry_config,
121 indexer,
122 state_merkle_trees: Vec::new(),
123 };
124 if config.fetch_active_tree {
125 new.get_latest_active_state_trees().await?;
126 }
127 Ok(new)
128 }
129
130 pub fn add_indexer(&mut self, path: String, api_key: Option<String>) {
131 self.indexer = Some(PhotonIndexer::new(path, api_key));
132 }
133
134 fn detect_network(&self) -> RpcUrl {
136 let url = self.client.url();
137
138 if url.contains("devnet") {
139 RpcUrl::Devnet
140 } else if url.contains("testnet") {
141 RpcUrl::Testnet
142 } else if url.contains("localhost") || url.contains("127.0.0.1") {
143 RpcUrl::Localnet
144 } else if url.contains("zk-testnet") {
145 RpcUrl::ZKTestnet
146 } else {
147 RpcUrl::Custom(url.to_string())
149 }
150 }
151
152 async fn retry<F, Fut, T>(&self, operation: F) -> Result<T, RpcError>
153 where
154 F: Fn() -> Fut,
155 Fut: std::future::Future<Output = Result<T, RpcError>>,
156 {
157 let mut attempts = 0;
158 let start_time = Instant::now();
159 loop {
160 match operation().await {
161 Ok(result) => return Ok(result),
162 Err(e) => {
163 let retry = self.should_retry(&e);
164 if retry {
165 attempts += 1;
166 if attempts >= self.retry_config.max_retries
167 || start_time.elapsed() >= self.retry_config.timeout
168 {
169 return Err(e);
170 }
171 warn!(
172 "Operation failed, retrying in {:?} (attempt {}/{}): {:?}",
173 self.retry_config.retry_delay,
174 attempts,
175 self.retry_config.max_retries,
176 e
177 );
178 sleep(self.retry_config.retry_delay).await;
179 } else {
180 return Err(e);
181 }
182 }
183 }
184 }
185 }
186
187 async fn _create_and_send_transaction_with_batched_event(
188 &mut self,
189 instructions: &[Instruction],
190 payer: &Pubkey,
191 signers: &[&Keypair],
192 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
193 let latest_blockhash = self.client.get_latest_blockhash()?;
194
195 let mut instructions_vec = vec![
196 solana_compute_budget_interface::ComputeBudgetInstruction::set_compute_unit_limit(
197 1_000_000,
198 ),
199 ];
200 instructions_vec.extend_from_slice(instructions);
201
202 let transaction = Transaction::new_signed_with_payer(
203 instructions_vec.as_slice(),
204 Some(payer),
205 signers,
206 latest_blockhash,
207 );
208
209 let (signature, slot) = self
210 .process_transaction_with_context(transaction.clone())
211 .await?;
212
213 let mut vec = Vec::new();
214 let mut vec_accounts = Vec::new();
215 let mut program_ids = Vec::new();
216 instructions_vec.iter().for_each(|x| {
217 program_ids.push(light_compressed_account::Pubkey::new_from_array(
218 x.program_id.to_bytes(),
219 ));
220 vec.push(x.data.clone());
221 vec_accounts.push(
222 x.accounts
223 .iter()
224 .map(|x| light_compressed_account::Pubkey::new_from_array(x.pubkey.to_bytes()))
225 .collect(),
226 );
227 });
228 {
229 let rpc_transaction_config = RpcTransactionConfig {
230 encoding: Some(UiTransactionEncoding::Base64),
231 commitment: Some(self.client.commitment()),
232 ..Default::default()
233 };
234 let transaction = self
235 .client
236 .get_transaction_with_config(&signature, rpc_transaction_config)
237 .map_err(|e| RpcError::CustomError(e.to_string()))?;
238 let decoded_transaction = transaction
239 .transaction
240 .transaction
241 .decode()
242 .clone()
243 .unwrap();
244 let account_keys = decoded_transaction.message.static_account_keys();
245 let meta = transaction.transaction.meta.as_ref().ok_or_else(|| {
246 RpcError::CustomError("Transaction missing metadata information".to_string())
247 })?;
248 if meta.status.is_err() {
249 return Err(RpcError::CustomError(
250 "Transaction status indicates an error".to_string(),
251 ));
252 }
253
254 let inner_instructions = match &meta.inner_instructions {
255 OptionSerializer::Some(i) => i,
256 OptionSerializer::None => {
257 return Err(RpcError::CustomError(
258 "No inner instructions found".to_string(),
259 ));
260 }
261 OptionSerializer::Skip => {
262 return Err(RpcError::CustomError(
263 "No inner instructions found".to_string(),
264 ));
265 }
266 };
267
268 for ix in inner_instructions.iter() {
269 for ui_instruction in ix.instructions.iter() {
270 match ui_instruction {
271 UiInstruction::Compiled(ui_compiled_instruction) => {
272 let accounts = &ui_compiled_instruction.accounts;
273 let data = bs58::decode(&ui_compiled_instruction.data)
274 .into_vec()
275 .map_err(|_| {
276 RpcError::CustomError(
277 "Failed to decode instruction data".to_string(),
278 )
279 })?;
280 vec.push(data);
281 program_ids.push(light_compressed_account::Pubkey::new_from_array(
282 account_keys[ui_compiled_instruction.program_id_index as usize]
283 .to_bytes(),
284 ));
285 vec_accounts.push(
286 accounts
287 .iter()
288 .map(|x| {
289 light_compressed_account::Pubkey::new_from_array(
290 account_keys[(*x) as usize].to_bytes(),
291 )
292 })
293 .collect(),
294 );
295 }
296 UiInstruction::Parsed(_) => {
297 println!("Parsed instructions are not implemented yet");
298 }
299 }
300 }
301 }
302 }
303 let parsed_event =
304 event_from_light_transaction(program_ids.as_slice(), vec.as_slice(), vec_accounts)
305 .unwrap();
306 let event = parsed_event.map(|e| (e, signature, slot));
307 Ok(event)
308 }
309
310 async fn _create_and_send_transaction_with_event<T>(
311 &mut self,
312 instructions: &[Instruction],
313 payer: &Pubkey,
314 signers: &[&Keypair],
315 ) -> Result<Option<(T, Signature, u64)>, RpcError>
316 where
317 T: BorshDeserialize + Send + Debug,
318 {
319 let latest_blockhash = self.client.get_latest_blockhash()?;
320
321 let mut instructions_vec = vec![
322 solana_compute_budget_interface::ComputeBudgetInstruction::set_compute_unit_limit(
323 1_000_000,
324 ),
325 ];
326 instructions_vec.extend_from_slice(instructions);
327
328 let transaction = Transaction::new_signed_with_payer(
329 instructions_vec.as_slice(),
330 Some(payer),
331 signers,
332 latest_blockhash,
333 );
334
335 let (signature, slot) = self
336 .process_transaction_with_context(transaction.clone())
337 .await?;
338
339 let mut parsed_event = None;
340 for instruction in &transaction.message.instructions {
341 let ix_data = instruction.data.clone();
342 match T::deserialize(&mut &instruction.data[..]) {
343 Ok(e) => {
344 parsed_event = Some(e);
345 break;
346 }
347 Err(e) => {
348 warn!(
349 "Failed to parse event: {:?}, type: {:?}, ix data: {:?}",
350 e,
351 std::any::type_name::<T>(),
352 ix_data
353 );
354 }
355 }
356 }
357
358 if parsed_event.is_none() {
359 parsed_event = self.parse_inner_instructions::<T>(signature).ok();
360 }
361
362 let result = parsed_event.map(|e| (e, signature, slot));
363 Ok(result)
364 }
365}
366
367impl LightClient {
368 #[allow(clippy::result_large_err)]
369 fn parse_inner_instructions<T: BorshDeserialize>(
370 &self,
371 signature: Signature,
372 ) -> Result<T, RpcError> {
373 let rpc_transaction_config = RpcTransactionConfig {
374 encoding: Some(UiTransactionEncoding::Base64),
375 commitment: Some(self.client.commitment()),
376 ..Default::default()
377 };
378 let transaction = self
379 .client
380 .get_transaction_with_config(&signature, rpc_transaction_config)
381 .map_err(|e| RpcError::CustomError(e.to_string()))?;
382 let meta = transaction.transaction.meta.as_ref().ok_or_else(|| {
383 RpcError::CustomError("Transaction missing metadata information".to_string())
384 })?;
385 if meta.status.is_err() {
386 return Err(RpcError::CustomError(
387 "Transaction status indicates an error".to_string(),
388 ));
389 }
390
391 let inner_instructions = match &meta.inner_instructions {
392 OptionSerializer::Some(i) => i,
393 OptionSerializer::None => {
394 return Err(RpcError::CustomError(
395 "No inner instructions found".to_string(),
396 ));
397 }
398 OptionSerializer::Skip => {
399 return Err(RpcError::CustomError(
400 "No inner instructions found".to_string(),
401 ));
402 }
403 };
404
405 for ix in inner_instructions.iter() {
406 for ui_instruction in ix.instructions.iter() {
407 match ui_instruction {
408 UiInstruction::Compiled(ui_compiled_instruction) => {
409 let data = bs58::decode(&ui_compiled_instruction.data)
410 .into_vec()
411 .map_err(|_| {
412 RpcError::CustomError(
413 "Failed to decode instruction data".to_string(),
414 )
415 })?;
416
417 match T::try_from_slice(data.as_slice()) {
418 Ok(parsed_data) => return Ok(parsed_data),
419 Err(e) => {
420 warn!("Failed to parse inner instruction: {:?}", e);
421 }
422 }
423 }
424 UiInstruction::Parsed(_) => {
425 println!("Parsed instructions are not implemented yet");
426 }
427 }
428 }
429 }
430 Err(RpcError::CustomError(
431 "Failed to find any parseable inner instructions".to_string(),
432 ))
433 }
434}
435
436#[async_trait]
437impl Rpc for LightClient {
438 async fn new(config: LightClientConfig) -> Result<Self, RpcError>
439 where
440 Self: Sized,
441 {
442 Self::new_with_retry(config, None).await
443 }
444
445 fn get_payer(&self) -> &Keypair {
446 &self.payer
447 }
448
449 fn get_url(&self) -> String {
450 self.client.url()
451 }
452
453 async fn health(&self) -> Result<(), RpcError> {
454 self.retry(|| async { self.client.get_health().map_err(RpcError::from) })
455 .await
456 }
457
458 async fn get_program_accounts(
459 &self,
460 program_id: &Pubkey,
461 ) -> Result<Vec<(Pubkey, Account)>, RpcError> {
462 self.retry(|| async {
463 self.client
464 .get_program_accounts(program_id)
465 .map_err(RpcError::from)
466 })
467 .await
468 }
469
470 async fn process_transaction(
471 &mut self,
472 transaction: Transaction,
473 ) -> Result<Signature, RpcError> {
474 self.retry(|| async {
475 self.client
476 .send_and_confirm_transaction(&transaction)
477 .map_err(RpcError::from)
478 })
479 .await
480 }
481
482 async fn process_transaction_with_context(
483 &mut self,
484 transaction: Transaction,
485 ) -> Result<(Signature, Slot), RpcError> {
486 self.retry(|| async {
487 let signature = self.client.send_and_confirm_transaction(&transaction)?;
488 let sig_info = self.client.get_signature_statuses(&[signature])?;
489 let slot = sig_info
490 .value
491 .first()
492 .and_then(|s| s.as_ref())
493 .map(|s| s.slot)
494 .ok_or_else(|| RpcError::CustomError("Failed to get slot".into()))?;
495 Ok((signature, slot))
496 })
497 .await
498 }
499
500 async fn confirm_transaction(&self, signature: Signature) -> Result<bool, RpcError> {
501 self.retry(|| async {
502 self.client
503 .confirm_transaction(&signature)
504 .map_err(RpcError::from)
505 })
506 .await
507 }
508
509 async fn get_account(&self, address: Pubkey) -> Result<Option<Account>, RpcError> {
510 self.retry(|| async {
511 self.client
512 .get_account_with_commitment(&address, self.client.commitment())
513 .map(|response| response.value)
514 .map_err(RpcError::from)
515 })
516 .await
517 }
518
519 async fn get_minimum_balance_for_rent_exemption(
520 &self,
521 data_len: usize,
522 ) -> Result<u64, RpcError> {
523 self.retry(|| async {
524 self.client
525 .get_minimum_balance_for_rent_exemption(data_len)
526 .map_err(RpcError::from)
527 })
528 .await
529 }
530
531 async fn airdrop_lamports(
532 &mut self,
533 to: &Pubkey,
534 lamports: u64,
535 ) -> Result<Signature, RpcError> {
536 self.retry(|| async {
537 let signature = self
538 .client
539 .request_airdrop(to, lamports)
540 .map_err(RpcError::ClientError)?;
541 self.retry(|| async {
542 if self
543 .client
544 .confirm_transaction_with_commitment(&signature, self.client.commitment())?
545 .value
546 {
547 Ok(())
548 } else {
549 Err(RpcError::CustomError("Airdrop not confirmed".into()))
550 }
551 })
552 .await?;
553
554 Ok(signature)
555 })
556 .await
557 }
558
559 async fn get_balance(&self, pubkey: &Pubkey) -> Result<u64, RpcError> {
560 self.retry(|| async { self.client.get_balance(pubkey).map_err(RpcError::from) })
561 .await
562 }
563
564 async fn get_latest_blockhash(&mut self) -> Result<(Hash, u64), RpcError> {
565 self.retry(|| async {
566 self.client
567 .get_latest_blockhash_with_commitment(CommitmentConfig::confirmed())
570 .map_err(RpcError::from)
571 })
572 .await
573 }
574
575 async fn get_slot(&self) -> Result<u64, RpcError> {
576 self.retry(|| async { self.client.get_slot().map_err(RpcError::from) })
577 .await
578 }
579
580 async fn send_transaction(&self, transaction: &Transaction) -> Result<Signature, RpcError> {
581 self.retry(|| async {
582 self.client
583 .send_transaction_with_config(
584 transaction,
585 RpcSendTransactionConfig {
586 skip_preflight: true,
587 max_retries: Some(self.retry_config.max_retries as usize),
588 ..Default::default()
589 },
590 )
591 .map_err(RpcError::from)
592 })
593 .await
594 }
595
596 async fn send_transaction_with_config(
597 &self,
598 transaction: &Transaction,
599 config: RpcSendTransactionConfig,
600 ) -> Result<Signature, RpcError> {
601 self.retry(|| async {
602 self.client
603 .send_transaction_with_config(transaction, config)
604 .map_err(RpcError::from)
605 })
606 .await
607 }
608
609 async fn get_transaction_slot(&self, signature: &Signature) -> Result<u64, RpcError> {
610 self.retry(|| async {
611 Ok(self
612 .client
613 .get_transaction_with_config(
614 signature,
615 RpcTransactionConfig {
616 encoding: Some(UiTransactionEncoding::Base64),
617 commitment: Some(self.client.commitment()),
618 ..Default::default()
619 },
620 )
621 .map_err(RpcError::from)?
622 .slot)
623 })
624 .await
625 }
626
627 async fn get_signature_statuses(
628 &self,
629 signatures: &[Signature],
630 ) -> Result<Vec<Option<TransactionStatus>>, RpcError> {
631 self.client
632 .get_signature_statuses(signatures)
633 .map(|response| response.value)
634 .map_err(RpcError::from)
635 }
636
637 async fn create_and_send_transaction_with_event<T>(
638 &mut self,
639 instructions: &[Instruction],
640 payer: &Pubkey,
641 signers: &[&Keypair],
642 ) -> Result<Option<(T, Signature, u64)>, RpcError>
643 where
644 T: BorshDeserialize + Send + Debug,
645 {
646 self._create_and_send_transaction_with_event::<T>(instructions, payer, signers)
647 .await
648 }
649
650 async fn create_and_send_transaction_with_public_event(
651 &mut self,
652 instructions: &[Instruction],
653 payer: &Pubkey,
654 signers: &[&Keypair],
655 ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError> {
656 let parsed_event = self
657 ._create_and_send_transaction_with_batched_event(instructions, payer, signers)
658 .await?;
659
660 let event = parsed_event.map(|(e, signature, slot)| (e[0].event.clone(), signature, slot));
661 Ok(event)
662 }
663
664 async fn create_and_send_transaction_with_batched_event(
665 &mut self,
666 instructions: &[Instruction],
667 payer: &Pubkey,
668 signers: &[&Keypair],
669 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
670 self._create_and_send_transaction_with_batched_event(instructions, payer, signers)
671 .await
672 }
673
674 fn indexer(&self) -> Result<&impl Indexer, RpcError> {
675 self.indexer.as_ref().ok_or(RpcError::IndexerNotInitialized)
676 }
677
678 fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError> {
679 self.indexer.as_mut().ok_or(RpcError::IndexerNotInitialized)
680 }
681
682 async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError> {
684 let network = self.detect_network();
685
686 if matches!(network, RpcUrl::Localnet) {
688 use light_compressed_account::TreeType;
689 use solana_pubkey::pubkey;
690
691 use crate::indexer::TreeInfo;
692
693 #[cfg(feature = "v2")]
694 let default_trees = vec![
695 TreeInfo {
696 tree: pubkey!("bmt1LryLZUMmF7ZtqESaw7wifBXLfXHQYoE4GAmrahU"),
697 queue: pubkey!("oq1na8gojfdUhsfCpyjNt6h4JaDWtHf1yQj4koBWfto"),
698 cpi_context: Some(pubkey!("cpi15BoVPKgEPw5o8wc2T816GE7b378nMXnhH3Xbq4y")),
699 next_tree_info: None,
700 tree_type: TreeType::StateV2,
701 },
702 TreeInfo {
703 tree: pubkey!("bmt2UxoBxB9xWev4BkLvkGdapsz6sZGkzViPNph7VFi"),
704 queue: pubkey!("oq2UkeMsJLfXt2QHzim242SUi3nvjJs8Pn7Eac9H9vg"),
705 cpi_context: Some(pubkey!("cpi2yGapXUR3As5SjnHBAVvmApNiLsbeZpF3euWnW6B")),
706 next_tree_info: None,
707 tree_type: TreeType::StateV2,
708 },
709 TreeInfo {
710 tree: pubkey!("bmt3ccLd4bqSVZVeCJnH1F6C8jNygAhaDfxDwePyyGb"),
711 queue: pubkey!("oq3AxjekBWgo64gpauB6QtuZNesuv19xrhaC1ZM1THQ"),
712 cpi_context: Some(pubkey!("cpi3mbwMpSX8FAGMZVP85AwxqCaQMfEk9Em1v8QK9Rf")),
713 next_tree_info: None,
714 tree_type: TreeType::StateV2,
715 },
716 TreeInfo {
717 tree: pubkey!("bmt4d3p1a4YQgk9PeZv5s4DBUmbF5NxqYpk9HGjQsd8"),
718 queue: pubkey!("oq4ypwvVGzCUMoiKKHWh4S1SgZJ9vCvKpcz6RT6A8dq"),
719 cpi_context: Some(pubkey!("cpi4yyPDc4bCgHAnsenunGA8Y77j3XEDyjgfyCKgcoc")),
720 next_tree_info: None,
721 tree_type: TreeType::StateV2,
722 },
723 TreeInfo {
724 tree: pubkey!("bmt5yU97jC88YXTuSukYHa8Z5Bi2ZDUtmzfkDTA2mG2"),
725 queue: pubkey!("oq5oh5ZR3yGomuQgFduNDzjtGvVWfDRGLuDVjv9a96P"),
726 cpi_context: Some(pubkey!("cpi5ZTjdgYpZ1Xr7B1cMLLUE81oTtJbNNAyKary2nV6")),
727 next_tree_info: None,
728 tree_type: TreeType::StateV2,
729 },
730 ];
731
732 #[cfg(not(feature = "v2"))]
733 let default_trees = vec![TreeInfo {
734 tree: pubkey!("smt1NamzXdq4AMqS2fS2F1i5KTYPZRhoHgWx38d8WsT"),
735 queue: pubkey!("nfq1NvQDJ2GEgnS8zt9prAe8rjjpAW1zFkrvZoBR148"),
736 cpi_context: Some(pubkey!("cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4")),
737 next_tree_info: None,
738 tree_type: TreeType::StateV1,
739 }];
740
741 self.state_merkle_trees = default_trees.clone();
742 return Ok(default_trees);
743 }
744
745 let (mainnet_tables, devnet_tables) = default_state_tree_lookup_tables();
746
747 let lookup_tables = match network {
748 RpcUrl::Devnet | RpcUrl::Testnet | RpcUrl::ZKTestnet => &devnet_tables,
749 _ => &mainnet_tables, };
751
752 let res = get_light_state_tree_infos(
753 self,
754 &lookup_tables[0].state_tree_lookup_table,
755 &lookup_tables[0].nullify_table,
756 )
757 .await?;
758 self.state_merkle_trees = res.clone();
759 Ok(res)
760 }
761
762 fn get_state_tree_infos(&self) -> Vec<TreeInfo> {
764 self.state_merkle_trees.to_vec()
765 }
766
767 fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError> {
770 let mut rng = rand::thread_rng();
771 select_state_tree_info(&mut rng, &self.state_merkle_trees)
772 }
773
774 fn get_address_tree_v1(&self) -> TreeInfo {
775 TreeInfo {
776 tree: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"),
777 queue: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"),
778 cpi_context: None,
779 next_tree_info: None,
780 tree_type: TreeType::AddressV1,
781 }
782 }
783
784 fn get_address_tree_v2(&self) -> TreeInfo {
785 TreeInfo {
786 tree: pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx"),
787 queue: pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx"),
788 cpi_context: None,
789 next_tree_info: None,
790 tree_type: TreeType::AddressV2,
791 }
792 }
793}
794
795impl MerkleTreeExt for LightClient {}
796
797pub fn select_state_tree_info<R: rand::Rng>(
820 rng: &mut R,
821 state_trees: &[TreeInfo],
822) -> Result<TreeInfo, RpcError> {
823 if state_trees.is_empty() {
824 return Err(RpcError::NoStateTreesAvailable);
825 }
826
827 Ok(state_trees[rng.gen_range(0..state_trees.len())])
828}