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