Skip to main content

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("Transaction build error: {0}")]
44    TransactionBuildError(String),
45
46    #[error("Assert Rpc Error: {0}")]
47    AssertRpcError(String),
48
49    /// The chosen warp slot is not in the future, so warp is not performed
50    #[error("Warp slot not in the future")]
51    InvalidWarpSlot,
52
53    #[cfg(feature = "program-test")]
54    #[error("LiteSVM Error: {0}")]
55    LiteSvmError(String),
56
57    #[error("Account {0} does not exist")]
58    AccountDoesNotExist(String),
59
60    #[error("Invalid response data.")]
61    InvalidResponseData,
62
63    #[error("Indexer not initialized.")]
64    IndexerNotInitialized,
65
66    #[error("Indexer error: {0}")]
67    IndexerError(#[from] IndexerError),
68
69    #[error(
70        "No state trees available, use rpc.get_latest_active_state_trees() to fetch state trees"
71    )]
72    NoStateTreesAvailable,
73
74    #[error("LightSdkError error: {0}")]
75    LightSdkError(#[from] LightSdkError),
76}
77
78impl From<light_event::error::ParseIndexerEventError> for RpcError {
79    fn from(e: light_event::error::ParseIndexerEventError) -> Self {
80        RpcError::CustomError(format!("ParseIndexerEventError: {}", e))
81    }
82}
83
84impl From<ClientError> for RpcError {
85    fn from(e: ClientError) -> Self {
86        // Check if this is a transaction error - don't mask those as RateLimited
87        if e.kind.get_transaction_error().is_some() {
88            return RpcError::ClientError(e);
89        }
90
91        // Check for HTTP 429 status directly from reqwest error
92        if let ErrorKind::Reqwest(ref reqwest_err) = *e.kind {
93            if let Some(status) = reqwest_err.status() {
94                if status.as_u16() == 429 {
95                    return RpcError::RateLimited;
96                }
97            }
98        }
99
100        RpcError::ClientError(e)
101    }
102}
103
104impl Clone for RpcError {
105    fn clone(&self) -> Self {
106        match self {
107            #[cfg(feature = "program-test")]
108            RpcError::BanksError(_) => RpcError::CustomError("BanksError".to_string()),
109            RpcError::RateLimited => RpcError::RateLimited,
110            RpcError::TransactionError(e) => RpcError::TransactionError(e.clone()),
111            RpcError::ClientError(_) => RpcError::CustomError("ClientError".to_string()),
112            RpcError::IoError(e) => RpcError::IoError(e.kind().into()),
113            RpcError::CustomError(e) => RpcError::CustomError(e.clone()),
114            RpcError::SigningError(e) => RpcError::SigningError(e.clone()),
115            RpcError::TransactionBuildError(e) => RpcError::TransactionBuildError(e.clone()),
116            RpcError::AssertRpcError(e) => RpcError::AssertRpcError(e.clone()),
117            RpcError::InvalidWarpSlot => RpcError::InvalidWarpSlot,
118            RpcError::AccountDoesNotExist(e) => RpcError::AccountDoesNotExist(e.clone()),
119            RpcError::InvalidResponseData => RpcError::InvalidResponseData,
120            RpcError::IndexerNotInitialized => RpcError::IndexerNotInitialized,
121            RpcError::IndexerError(e) => RpcError::IndexerError(e.clone()),
122            RpcError::LightSdkError(e) => RpcError::CustomError(e.to_string()),
123            RpcError::StateTreeLookupTableNotFound => RpcError::StateTreeLookupTableNotFound,
124            RpcError::InvalidStateTreeLookupTable => RpcError::InvalidStateTreeLookupTable,
125            RpcError::NullifyTableNotFound => RpcError::NullifyTableNotFound,
126            RpcError::NoStateTreesAvailable => RpcError::NoStateTreesAvailable,
127            #[cfg(feature = "program-test")]
128            RpcError::LiteSvmError(e) => RpcError::LiteSvmError(e.clone()),
129        }
130    }
131}
132
133#[cfg(feature = "program-test")]
134impl From<litesvm::error::LiteSVMError> for RpcError {
135    fn from(e: litesvm::error::LiteSVMError) -> Self {
136        RpcError::LiteSvmError(e.to_string())
137    }
138}