Skip to main content

surfpool_core/
error.rs

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