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