light_client/rpc/
errors.rs

1use std::io;
2
3use light_sdk::error::LightSdkError;
4use solana_rpc_client_api::client_error::{Error as ClientError, ErrorKind};
5use solana_transaction_error::TransactionError;
6use thiserror::Error;
7
8use crate::indexer::IndexerError;
9
10#[derive(Error, Debug)]
11pub enum RpcError {
12    #[cfg(feature = "program-test")]
13    #[error("BanksError: {0}")]
14    BanksError(#[from] solana_banks_client::BanksClientError),
15
16    #[error("Rate limited")]
17    RateLimited,
18
19    #[error("State tree lookup table not found")]
20    StateTreeLookupTableNotFound,
21
22    #[error("State tree lookup table must have a multiple of 3 addresses")]
23    InvalidStateTreeLookupTable,
24
25    #[error("Nullify table not found")]
26    NullifyTableNotFound,
27
28    #[error("TransactionError: {0}")]
29    TransactionError(#[from] TransactionError),
30
31    #[error("ClientError: {0}")]
32    ClientError(ClientError),
33
34    #[error("IoError: {0}")]
35    IoError(#[from] io::Error),
36
37    #[error("Error: `{0}`")]
38    CustomError(String),
39
40    #[error("Signing error: {0}")]
41    SigningError(String),
42
43    #[error("Assert Rpc Error: {0}")]
44    AssertRpcError(String),
45
46    /// The chosen warp slot is not in the future, so warp is not performed
47    #[error("Warp slot not in the future")]
48    InvalidWarpSlot,
49
50    #[cfg(feature = "program-test")]
51    #[error("LiteSVM Error: {0}")]
52    LiteSvmError(String),
53
54    #[error("Account {0} does not exist")]
55    AccountDoesNotExist(String),
56
57    #[error("Invalid response data.")]
58    InvalidResponseData,
59
60    #[error("Indexer not initialized.")]
61    IndexerNotInitialized,
62
63    #[error("Indexer error: {0}")]
64    IndexerError(#[from] IndexerError),
65
66    #[error(
67        "No state trees available, use rpc.get_latest_active_state_trees() to fetch state trees"
68    )]
69    NoStateTreesAvailable,
70
71    #[error("LightSdkError error: {0}")]
72    LightSdkError(#[from] LightSdkError),
73}
74
75impl From<light_event::error::ParseIndexerEventError> for RpcError {
76    fn from(e: light_event::error::ParseIndexerEventError) -> Self {
77        RpcError::CustomError(format!("ParseIndexerEventError: {}", e))
78    }
79}
80
81impl From<ClientError> for RpcError {
82    fn from(e: ClientError) -> Self {
83        // Check if this is a transaction error - don't mask those as RateLimited
84        if e.kind.get_transaction_error().is_some() {
85            return RpcError::ClientError(e);
86        }
87
88        // Check for HTTP 429 status directly from reqwest error
89        if let ErrorKind::Reqwest(ref reqwest_err) = e.kind {
90            if let Some(status) = reqwest_err.status() {
91                if status.as_u16() == 429 {
92                    return RpcError::RateLimited;
93                }
94            }
95        }
96
97        RpcError::ClientError(e)
98    }
99}
100
101impl Clone for RpcError {
102    fn clone(&self) -> Self {
103        match self {
104            #[cfg(feature = "program-test")]
105            RpcError::BanksError(_) => RpcError::CustomError("BanksError".to_string()),
106            RpcError::RateLimited => RpcError::RateLimited,
107            RpcError::TransactionError(e) => RpcError::TransactionError(e.clone()),
108            RpcError::ClientError(_) => RpcError::CustomError("ClientError".to_string()),
109            RpcError::IoError(e) => RpcError::IoError(e.kind().into()),
110            RpcError::CustomError(e) => RpcError::CustomError(e.clone()),
111            RpcError::SigningError(e) => RpcError::SigningError(e.clone()),
112            RpcError::AssertRpcError(e) => RpcError::AssertRpcError(e.clone()),
113            RpcError::InvalidWarpSlot => RpcError::InvalidWarpSlot,
114            RpcError::AccountDoesNotExist(e) => RpcError::AccountDoesNotExist(e.clone()),
115            RpcError::InvalidResponseData => RpcError::InvalidResponseData,
116            RpcError::IndexerNotInitialized => RpcError::IndexerNotInitialized,
117            RpcError::IndexerError(e) => RpcError::IndexerError(e.clone()),
118            RpcError::LightSdkError(e) => RpcError::CustomError(e.to_string()),
119            RpcError::StateTreeLookupTableNotFound => RpcError::StateTreeLookupTableNotFound,
120            RpcError::InvalidStateTreeLookupTable => RpcError::InvalidStateTreeLookupTable,
121            RpcError::NullifyTableNotFound => RpcError::NullifyTableNotFound,
122            RpcError::NoStateTreesAvailable => RpcError::NoStateTreesAvailable,
123            #[cfg(feature = "program-test")]
124            RpcError::LiteSvmError(e) => RpcError::LiteSvmError(e.clone()),
125        }
126    }
127}
128
129#[cfg(feature = "program-test")]
130impl From<litesvm::error::LiteSVMError> for RpcError {
131    fn from(e: litesvm::error::LiteSVMError) -> Self {
132        RpcError::LiteSvmError(e.to_string())
133    }
134}