diff --git a/sdk-libs/client/src/indexer/error.rs b/sdk-libs/client/src/indexer/error.rs index 5e83fe2d08..2a5ca31e94 100644 --- a/sdk-libs/client/src/indexer/error.rs +++ b/sdk-libs/client/src/indexer/error.rs @@ -46,7 +46,9 @@ pub enum IndexerError { #[error("Error: `{0}`")] CustomError(String), - #[error("Indexer not initialized.")] + #[error( + "Indexer not initialized. Set photon_url in LightClientConfig to enable indexer API calls." + )] NotInitialized, #[error("Indexer slot has not reached the requested slot.")] IndexerNotSyncedToSlot, @@ -91,7 +93,7 @@ impl From> for IndexerError { IndexerError::ApiError(format!("Serialization error: {}", e)) } photon_api::apis::Error::Io(e) => IndexerError::ApiError(format!("IO error: {}", e)), - _ => IndexerError::ApiError("Unknown API error".to_string()), + _ => IndexerError::ApiError(format!("Unknown API error {}", error)), } } } diff --git a/sdk-libs/client/src/indexer/photon_indexer.rs b/sdk-libs/client/src/indexer/photon_indexer.rs index 0370cf392d..c3129aaba5 100644 --- a/sdk-libs/client/src/indexer/photon_indexer.rs +++ b/sdk-libs/client/src/indexer/photon_indexer.rs @@ -132,9 +132,7 @@ impl PhotonIndexer { result: Option, ) -> Result { if let Some(error) = error { - let error_message = error - .message - .unwrap_or_else(|| "Unknown API error".to_string()); + let error_message = error.message.unwrap_or_default(); return Err(IndexerError::ApiError(format!( "API error in {} (code: {:?}): {}", context, error.code, error_message diff --git a/sdk-libs/client/src/rpc/client.rs b/sdk-libs/client/src/rpc/client.rs index 3102de4a72..494ee9a218 100644 --- a/sdk-libs/client/src/rpc/client.rs +++ b/sdk-libs/client/src/rpc/client.rs @@ -129,6 +129,24 @@ impl LightClient { self.indexer = Some(PhotonIndexer::new(path, api_key)); } + /// Detects the network type based on the RPC URL + fn detect_network(&self) -> RpcUrl { + let url = self.client.url(); + + if url.contains("devnet") { + RpcUrl::Devnet + } else if url.contains("testnet") { + RpcUrl::Testnet + } else if url.contains("localhost") || url.contains("127.0.0.1") { + RpcUrl::Localnet + } else if url.contains("zk-testnet") { + RpcUrl::ZKTestnet + } else { + // Default to mainnet for production URLs and custom URLs + RpcUrl::Custom(url.to_string()) + } + } + async fn retry(&self, operation: F) -> Result where F: Fn() -> Fut, @@ -662,11 +680,48 @@ impl Rpc for LightClient { /// Fetch the latest state tree addresses from the cluster. async fn get_latest_active_state_trees(&mut self) -> Result, RpcError> { - let res = default_state_tree_lookup_tables().0; + let network = self.detect_network(); + + // Return default test values for localnet + if matches!(network, RpcUrl::Localnet) { + use light_compressed_account::TreeType; + use solana_pubkey::pubkey; + + use crate::indexer::TreeInfo; + + #[cfg(feature = "v2")] + let default_trees = vec![TreeInfo { + tree: pubkey!("HLKs5NJ8FXkJg8BrzJt56adFYYuwg5etzDtBbQYTsixu"), + queue: pubkey!("6L7SzhYB3anwEQ9cphpJ1U7Scwj57bx2xueReg7R9cKU"), + cpi_context: Some(pubkey!("7Hp52chxaew8bW1ApR4fck2bh6Y8qA1pu3qwH6N9zaLj")), + next_tree_info: None, + tree_type: TreeType::StateV2, + }]; + + #[cfg(not(feature = "v2"))] + let default_trees = vec![TreeInfo { + tree: pubkey!("smt1NamzXdq4AMqS2fS2F1i5KTYPZRhoHgWx38d8WsT"), + queue: pubkey!("nfq1NvQDJ2GEgnS8zt9prAe8rjjpAW1zFkrvZoBR148"), + cpi_context: Some(pubkey!("cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4")), + next_tree_info: None, + tree_type: TreeType::StateV1, + }]; + + self.state_merkle_trees = default_trees.clone(); + return Ok(default_trees); + } + + let (mainnet_tables, devnet_tables) = default_state_tree_lookup_tables(); + + let lookup_tables = match network { + RpcUrl::Devnet | RpcUrl::Testnet | RpcUrl::ZKTestnet => &devnet_tables, + _ => &mainnet_tables, // Default to mainnet for production and custom URLs + }; + let res = get_light_state_tree_infos( self, - &res[0].state_tree_lookup_table, - &res[0].nullify_table, + &lookup_tables[0].state_tree_lookup_table, + &lookup_tables[0].nullify_table, ) .await?; self.state_merkle_trees = res.clone();