surfpool_core/
error.rs

1use std::{fmt::Display, future::Future, pin::Pin};
2
3use crossbeam_channel::TrySendError;
4use jsonrpc_core::{Error, Result};
5use serde::Serialize;
6use serde_json::json;
7use solana_client::{client_error::ClientError, rpc_request::TokenAccountsFilter};
8use solana_pubkey::Pubkey;
9use solana_sdk::slot_history::Slot;
10use solana_transaction_status::EncodeError;
11
12pub type SurfpoolResult<T> = std::result::Result<T, SurfpoolError>;
13
14#[derive(Debug, Clone)]
15pub struct SurfpoolError(Error);
16
17impl From<SurfpoolError> for String {
18    fn from(e: SurfpoolError) -> Self {
19        e.0.to_string()
20    }
21}
22
23impl From<SurfpoolError> for Error {
24    fn from(e: SurfpoolError) -> Self {
25        e.0
26    }
27}
28
29impl From<EncodeError> for SurfpoolError {
30    fn from(e: EncodeError) -> Self {
31        let mut error = Error::internal_error();
32        error.data = Some(json!(format!(
33            "Transaction encoding error: {}",
34            e.to_string()
35        )));
36        Self(error)
37    }
38}
39
40impl std::error::Error for SurfpoolError {}
41
42impl std::fmt::Display for SurfpoolError {
43    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
44        let Error {
45            code,
46            message,
47            data,
48        } = &self.0;
49
50        let core = if code.description().eq(message) {
51            code.description()
52        } else {
53            format!("{}: {}", code.description(), message)
54        };
55
56        if let Some(data_value) = data {
57            write!(f, "{}: {}", core, data_value.to_string().as_str())
58        } else {
59            write!(f, "{}", core)
60        }
61    }
62}
63
64impl<T> From<SurfpoolError> for Pin<Box<dyn Future<Output = Result<T>> + Send>> {
65    fn from(e: SurfpoolError) -> Self {
66        Box::pin(async move { Err(e.into()) })
67    }
68}
69
70impl<T> From<TrySendError<T>> for SurfpoolError {
71    fn from(val: TrySendError<T>) -> Self {
72        SurfpoolError::from_try_send_error(val)
73    }
74}
75
76impl From<solana_client::client_error::ClientError> for SurfpoolError {
77    fn from(e: solana_client::client_error::ClientError) -> Self {
78        SurfpoolError::client_error(e)
79    }
80}
81
82impl SurfpoolError {
83    pub fn from_try_send_error<T>(e: TrySendError<T>) -> Self {
84        let mut error = Error::internal_error();
85        error.data = Some(json!(format!(
86            "Failed to send command on channel: {}",
87            e.to_string()
88        )));
89        Self(error)
90    }
91
92    pub fn client_error(e: solana_client::client_error::ClientError) -> Self {
93        let mut error = Error::internal_error();
94        error.data = Some(json!(format!("Solana RPC client error: {}", e.to_string())));
95        Self(error)
96    }
97
98    pub fn no_locker() -> Self {
99        let mut error = Error::internal_error();
100        error.data = Some(json!("Failed to access internal SVM state"));
101        Self(error)
102    }
103
104    pub fn set_account<T>(pubkey: Pubkey, e: T) -> Self
105    where
106        T: ToString,
107    {
108        let mut error = Error::internal_error();
109        error.data = Some(json!(format!(
110            "Failed to set account {}: {}",
111            pubkey,
112            e.to_string()
113        )));
114        Self(error)
115    }
116    pub fn get_account<T>(pubkey: Pubkey, e: T) -> Self
117    where
118        T: ToString,
119    {
120        let mut error = Error::internal_error();
121        error.data = Some(json!(format!(
122            "Failed to fetch account {} from remote: {}",
123            pubkey,
124            e.to_string()
125        )));
126        Self(error)
127    }
128
129    pub fn get_token_accounts<T>(owner: Pubkey, filter: &TokenAccountsFilter, e: T) -> Self
130    where
131        T: ToString,
132    {
133        let mut error = Error::internal_error();
134        error.data = Some(json!(format!(
135            "Failed to get token accounts by owner {owner} for {}: {}",
136            match filter {
137                TokenAccountsFilter::ProgramId(token_program) => format!("program {token_program}"),
138                TokenAccountsFilter::Mint(mint) => format!("mint {mint}"),
139            },
140            e.to_string()
141        )));
142        Self(error)
143    }
144
145    pub fn get_token_accounts_by_delegate_error<T>(
146        delegate: Pubkey,
147        filter: &TokenAccountsFilter,
148        e: T,
149    ) -> Self
150    where
151        T: ToString,
152    {
153        let mut error = Error::internal_error();
154
155        let filter_description = match filter {
156            TokenAccountsFilter::ProgramId(program_id) => {
157                let program_name = if *program_id == spl_token::ID {
158                    "SPL Token program"
159                } else if *program_id == spl_token_2022::ID {
160                    "Token 2022 program"
161                } else {
162                    "custom token program"
163                };
164                format!("{} ({})", program_id, program_name)
165            }
166            TokenAccountsFilter::Mint(mint) => format!("mint {}", mint),
167        };
168
169        error.data = Some(json!(format!(
170            "Failed to get token accounts by delegate {} for {}: {}",
171            delegate,
172            filter_description,
173            e.to_string()
174        )));
175
176        Self(error)
177    }
178
179    pub fn unsupported_token_program(program_id: Pubkey) -> Self {
180        let mut error = Error::internal_error();
181        error.data = Some(json!(format!(
182            "Unsupported token program: {}. Only SPL Token ({}) and Token 2022 ({}) are currently supported.",
183            program_id,
184            spl_token::ID,
185            spl_token_2022::ID
186        )));
187        Self(error)
188    }
189
190    pub fn get_program_accounts<T>(program_id: Pubkey, e: T) -> Self
191    where
192        T: ToString,
193    {
194        let mut error = Error::internal_error();
195        error.data = Some(json!(format!(
196            "Failed to fetch program accounts for {program_id}: {}",
197            e.to_string()
198        )));
199        Self(error)
200    }
201
202    pub fn get_token_largest_accounts<T>(mint: Pubkey, e: T) -> Self
203    where
204        T: ToString,
205    {
206        let mut error = Error::internal_error();
207        error.data = Some(json!(format!(
208            "Failed to get largest token accounts for mint {mint}: {}",
209            e.to_string()
210        )));
211        Self(error)
212    }
213
214    pub fn get_multiple_accounts<T>(e: T) -> Self
215    where
216        T: ToString,
217    {
218        let mut error = Error::internal_error();
219        error.data = Some(json!(format!(
220            "Failed to fetch accounts from remote: {}",
221            e.to_string()
222        )));
223        Self(error)
224    }
225    pub fn get_largest_accounts<T>(e: T) -> Self
226    where
227        T: ToString,
228    {
229        let mut error = Error::internal_error();
230        error.data = Some(json!(format!(
231            "Failed to fetch largest accounts from remote: {}",
232            e.to_string()
233        )));
234        Self(error)
235    }
236
237    pub fn get_signatures_for_address<T>(e: T) -> Self
238    where
239        T: ToString,
240    {
241        let mut error = Error::internal_error();
242        error.data = Some(json!(format!(
243            "Failed to fetch signatures for address from remote: {}",
244            e.to_string()
245        )));
246        Self(error)
247    }
248
249    pub fn invalid_pubkey<D>(pubkey: &str, data: D) -> Self
250    where
251        D: Serialize,
252    {
253        let mut error = Error::invalid_params(format!("Invalid pubkey '{pubkey}'"));
254        error.data = Some(json!(data));
255        Self(error)
256    }
257
258    pub fn invalid_pubkey_at_index<D>(pubkey: &str, index: usize, data: D) -> Self
259    where
260        D: Serialize,
261    {
262        let mut error =
263            Error::invalid_params(format!("Invalid pubkey '{pubkey}' at index {index}"));
264        error.data = Some(json!(data));
265        Self(error)
266    }
267
268    pub fn invalid_signature<D>(signature: &str, data: D) -> Self
269    where
270        D: Serialize,
271    {
272        let mut error = Error::invalid_params(format!("Invalid signature {signature}"));
273        error.data = Some(json!(data));
274        Self(error)
275    }
276
277    pub fn invalid_program_account<P, D>(program_id: P, data: D) -> Self
278    where
279        P: Display,
280        D: Serialize,
281    {
282        let mut error = Error::invalid_params(format!("Invalid program account {program_id}"));
283        error.data = Some(json!(data));
284        Self(error)
285    }
286
287    pub fn expected_program_account<P>(program_id: P) -> Self
288    where
289        P: Display,
290    {
291        let error = Error::invalid_params(format!("Account {program_id} is not a program account"));
292        Self(error)
293    }
294
295    pub fn account_not_found<P>(pubkey: P) -> Self
296    where
297        P: Display,
298    {
299        let error = Error::invalid_params(format!("Account {pubkey} not found"));
300        Self(error)
301    }
302
303    pub fn transaction_not_found<S>(signature: S) -> Self
304    where
305        S: Display,
306    {
307        let error = Error::invalid_params(format!("Transaction {signature} not found"));
308        Self(error)
309    }
310
311    pub fn invalid_account_data<P, D, M>(pubkey: P, data: D, message: Option<M>) -> Self
312    where
313        P: Display,
314        D: Serialize,
315        M: Display,
316    {
317        let base_msg = format!("invalid account data {pubkey}");
318        let full_msg = if let Some(msg) = message {
319            format!("{base_msg}: {msg}")
320        } else {
321            base_msg
322        };
323        let mut error = Error::invalid_params(full_msg);
324        error.data = Some(json!(data));
325        Self(error)
326    }
327
328    pub fn invalid_account_owner<P, M>(pubkey: P, message: Option<M>) -> Self
329    where
330        P: Display,
331        M: Display,
332    {
333        let base_msg = format!("invalid account owner {pubkey}");
334        let full_msg = if let Some(msg) = message {
335            format!("{base_msg}: {msg}")
336        } else {
337            base_msg
338        };
339        let error = Error::invalid_params(full_msg);
340        Self(error)
341    }
342    pub fn invalid_lookup_index<P>(pubkey: P) -> Self
343    where
344        P: Display,
345    {
346        let error =
347            Error::invalid_params(format!("Address lookup {pubkey} contains an invalid index"));
348        Self(error)
349    }
350
351    pub fn invalid_base64_data<D>(typing: &str, data: D) -> Self
352    where
353        D: Display,
354    {
355        let mut error = Error::invalid_params(format!("Invalid base64 {typing}"));
356        error.data = Some(json!(data.to_string()));
357        Self(error)
358    }
359
360    pub fn deserialize_error<D>(typing: &str, data: D) -> Self
361    where
362        D: Display,
363    {
364        let mut error = Error::invalid_params(format!("Failed to deserialize {typing}"));
365        error.data = Some(json!(data.to_string()));
366        Self(error)
367    }
368
369    pub fn internal<D>(data: D) -> Self
370    where
371        D: Serialize,
372    {
373        let mut error = Error::internal_error();
374        error.data = Some(json!(data));
375        Self(error)
376    }
377
378    pub fn sig_verify_replace_recent_blockhash_collision() -> Self {
379        Self(Error::invalid_params(
380            "sigVerify may not be used with replaceRecentBlockhash",
381        ))
382    }
383
384    pub fn slot_too_old(slot: Slot) -> Self {
385        Self(Error::invalid_params(format!(
386            "Requested {slot} is before the first local slot, and no remote RPC was provided."
387        )))
388    }
389
390    pub fn get_block(e: ClientError, block: Slot) -> Self {
391        let mut error = Error::internal_error();
392        error.data = Some(json!(format!(
393            "Failed to get block {block} from remote: {e}"
394        )));
395        Self(error)
396    }
397
398    pub fn token_mint_not_found(mint: Pubkey) -> Self {
399        let mut error = Error::internal_error();
400        error.message = format!("Token mint {mint} not found");
401        Self(error)
402    }
403
404    pub fn unpack_token_account() -> Self {
405        let mut error = Error::parse_error();
406        error.message = "Failed to unpack token account".to_string();
407        Self(error)
408    }
409
410    pub fn unpack_mint_account() -> Self {
411        let mut error = Error::parse_error();
412        error.message = "Failed to unpack mint account".to_string();
413        Self(error)
414    }
415
416    pub fn invalid_token_account_state(state: &str) -> Self {
417        let error = Error::invalid_params(format!("Invalid token account state {state}"));
418        Self(error)
419    }
420
421    pub fn transaction_not_found_in_svm<S>(signature: S) -> Self
422    where
423        S: Display,
424    {
425        let mut error = Error::internal_error();
426        error.message =
427            format!("Transaction with signature '{signature}' was not found in the SVM");
428        Self(error)
429    }
430
431    pub fn tag_not_found(tag: &str) -> Self {
432        let mut error = Error::internal_error();
433        error.message = format!("Profile result associated with tag '{tag}' not found in the SVM");
434        Self(error)
435    }
436}