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 disable_cheatcode(e: String) -> Self {
109 let mut error = Error::invalid_request();
110 error.data = Some(json!(format!("Unable to disable cheatcode: {}", e)));
111 Self(error)
112 }
113
114 pub fn enable_cheatcode(e: String) -> Self {
115 let mut error = Error::invalid_request();
116 error.data = Some(json!(format!("Unable to enable cheatcode: {}", e)));
117 Self(error)
118 }
119
120 pub fn set_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 set account {}: {}",
127 pubkey,
128 e.to_string()
129 )));
130 Self(error)
131 }
132 pub fn get_account<T>(pubkey: Pubkey, e: T) -> Self
133 where
134 T: ToString,
135 {
136 let mut error = Error::internal_error();
137 error.data = Some(json!(format!(
138 "Failed to fetch account {} from remote: {}",
139 pubkey,
140 e.to_string()
141 )));
142 Self(error)
143 }
144
145 pub fn get_token_accounts<T>(owner: Pubkey, filter: &TokenAccountsFilter, e: T) -> Self
146 where
147 T: ToString,
148 {
149 let mut error = Error::internal_error();
150 error.data = Some(json!(format!(
151 "Failed to get token accounts by owner {owner} for {}: {}",
152 match filter {
153 TokenAccountsFilter::ProgramId(token_program) => format!("program {token_program}"),
154 TokenAccountsFilter::Mint(mint) => format!("mint {mint}"),
155 },
156 e.to_string()
157 )));
158 Self(error)
159 }
160
161 pub fn get_token_accounts_by_delegate_error<T>(
162 delegate: Pubkey,
163 filter: &TokenAccountsFilter,
164 e: T,
165 ) -> Self
166 where
167 T: ToString,
168 {
169 let mut error = Error::internal_error();
170
171 let filter_description = match filter {
172 TokenAccountsFilter::ProgramId(program_id) => {
173 let program_name = if *program_id == spl_token_interface::ID {
174 "SPL Token program"
175 } else if *program_id == spl_token_2022_interface::ID {
176 "Token 2022 program"
177 } else {
178 "custom token program"
179 };
180 format!("{} ({})", program_id, program_name)
181 }
182 TokenAccountsFilter::Mint(mint) => format!("mint {}", mint),
183 };
184
185 error.data = Some(json!(format!(
186 "Failed to get token accounts by delegate {} for {}: {}",
187 delegate,
188 filter_description,
189 e.to_string()
190 )));
191
192 Self(error)
193 }
194
195 pub fn unsupported_token_program(program_id: Pubkey) -> Self {
196 let mut error = Error::internal_error();
197 error.data = Some(json!(format!(
198 "Unsupported token program: {}. Only SPL Token ({}) and Token 2022 ({}) are currently supported.",
199 program_id,
200 spl_token_interface::ID,
201 spl_token_2022_interface::ID
202 )));
203 Self(error)
204 }
205
206 pub fn get_program_accounts<T>(program_id: 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 fetch program accounts for {program_id}: {}",
213 e.to_string()
214 )));
215 Self(error)
216 }
217
218 pub fn get_token_largest_accounts<T>(mint: Pubkey, 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 get largest token accounts for mint {mint}: {}",
225 e.to_string()
226 )));
227 Self(error)
228 }
229
230 pub fn get_multiple_accounts<T>(e: T) -> Self
231 where
232 T: ToString,
233 {
234 let mut error = Error::internal_error();
235 error.data = Some(json!(format!(
236 "Failed to fetch accounts from remote: {}",
237 e.to_string()
238 )));
239 Self(error)
240 }
241 pub fn get_largest_accounts<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 largest accounts from remote: {}",
248 e.to_string()
249 )));
250 Self(error)
251 }
252
253 pub fn get_signatures_for_address<T>(e: T) -> Self
254 where
255 T: ToString,
256 {
257 let mut error = Error::internal_error();
258 error.data = Some(json!(format!(
259 "Failed to fetch signatures for address from remote: {}",
260 e.to_string()
261 )));
262 Self(error)
263 }
264
265 pub fn invalid_pubkey<D>(pubkey: &str, data: D) -> Self
266 where
267 D: Serialize,
268 {
269 let mut error = Error::invalid_params(format!("Invalid pubkey '{pubkey}'"));
270 error.data = Some(json!(data));
271 Self(error)
272 }
273
274 pub fn invalid_pubkey_at_index<D>(pubkey: &str, index: usize, data: D) -> Self
275 where
276 D: Serialize,
277 {
278 let mut error =
279 Error::invalid_params(format!("Invalid pubkey '{pubkey}' at index {index}"));
280 error.data = Some(json!(data));
281 Self(error)
282 }
283
284 pub fn invalid_signature<D>(signature: &str, data: D) -> Self
285 where
286 D: Serialize,
287 {
288 let mut error = Error::invalid_params(format!("Invalid signature {signature}"));
289 error.data = Some(json!(data));
290 Self(error)
291 }
292
293 pub fn invalid_program_account<P, D>(program_id: P, data: D) -> Self
294 where
295 P: Display,
296 D: Serialize,
297 {
298 let mut error = Error::invalid_params(format!("Invalid program account {program_id}"));
299 error.data = Some(json!(data));
300 Self(error)
301 }
302
303 pub fn invalid_program_data_account<P, D>(program_data_id: P, data: D) -> Self
304 where
305 P: Display,
306 D: Serialize,
307 {
308 let mut error =
309 Error::invalid_params(format!("Invalid program data account {program_data_id}"));
310 error.data = Some(json!(data));
311 Self(error)
312 }
313
314 pub fn expected_program_account<P>(program_id: P) -> Self
315 where
316 P: Display,
317 {
318 let error = Error::invalid_params(format!("Account {program_id} is not a program account"));
319 Self(error)
320 }
321
322 pub fn account_not_found<P>(pubkey: P) -> Self
323 where
324 P: Display,
325 {
326 let error = Error::invalid_params(format!("Account {pubkey} not found"));
327 Self(error)
328 }
329
330 pub fn transaction_not_found<S>(signature: S) -> Self
331 where
332 S: Display,
333 {
334 let error = Error::invalid_params(format!("Transaction {signature} not found"));
335 Self(error)
336 }
337
338 pub fn invalid_account_data<P, D, M>(pubkey: P, data: D, message: Option<M>) -> Self
339 where
340 P: Display,
341 D: Serialize,
342 M: Display,
343 {
344 let base_msg = format!("invalid account data {pubkey}");
345 let full_msg = if let Some(msg) = message {
346 format!("{base_msg}: {msg}")
347 } else {
348 base_msg
349 };
350 let mut error = Error::invalid_params(full_msg);
351 error.data = Some(json!(data));
352 Self(error)
353 }
354
355 pub fn invalid_account_owner<P, M>(pubkey: P, message: Option<M>) -> Self
356 where
357 P: Display,
358 M: Display,
359 {
360 let base_msg = format!("invalid account owner {pubkey}");
361 let full_msg = if let Some(msg) = message {
362 format!("{base_msg}: {msg}")
363 } else {
364 base_msg
365 };
366 let error = Error::invalid_params(full_msg);
367 Self(error)
368 }
369 pub fn invalid_lookup_index<P>(pubkey: P) -> Self
370 where
371 P: Display,
372 {
373 let error =
374 Error::invalid_params(format!("Address lookup {pubkey} contains an invalid index"));
375 Self(error)
376 }
377
378 pub fn invalid_base64_data<D>(typing: &str, data: D) -> Self
379 where
380 D: Display,
381 {
382 let mut error = Error::invalid_params(format!("Invalid base64 {typing}"));
383 error.data = Some(json!(data.to_string()));
384 Self(error)
385 }
386
387 pub fn deserialize_error<D>(typing: &str, data: D) -> Self
388 where
389 D: Display,
390 {
391 let mut error = Error::invalid_params(format!("Failed to deserialize {typing}"));
392 error.data = Some(json!(data.to_string()));
393 Self(error)
394 }
395
396 pub fn rpc_method_not_supported() -> Self {
397 let mut error = Error::internal_error();
398 error.message = "RPC method not supported".to_string();
399 Self(error)
400 }
401
402 pub fn internal<D>(data: D) -> Self
403 where
404 D: Serialize,
405 {
406 let mut error = Error::internal_error();
407 error.data = Some(json!(data));
408 Self(error)
409 }
410
411 pub fn sig_verify_replace_recent_blockhash_collision() -> Self {
412 Self(Error::invalid_params(
413 "sigVerify may not be used with replaceRecentBlockhash",
414 ))
415 }
416
417 pub fn slot_too_old(slot: Slot) -> Self {
418 Self(Error::invalid_params(format!(
419 "Requested {slot} is before the first local slot, and no remote RPC was provided."
420 )))
421 }
422
423 pub fn get_block(e: ClientError, block: Slot) -> Self {
424 let mut error = Error::internal_error();
425 error.data = Some(json!(format!(
426 "Failed to get block {block} from remote: {e}"
427 )));
428 Self(error)
429 }
430
431 pub fn token_mint_not_found(mint: Pubkey) -> Self {
432 let mut error = Error::internal_error();
433 error.message = format!("Token mint {mint} not found");
434 Self(error)
435 }
436
437 pub fn unpack_token_account() -> Self {
438 let mut error = Error::parse_error();
439 error.message = "Failed to unpack token account".to_string();
440 Self(error)
441 }
442
443 pub fn unpack_mint_account() -> Self {
444 let mut error = Error::parse_error();
445 error.message = "Failed to unpack mint account".to_string();
446 Self(error)
447 }
448
449 pub fn invalid_token_account_state(state: &str) -> Self {
450 let error = Error::invalid_params(format!("Invalid token account state {state}"));
451 Self(error)
452 }
453
454 pub fn tag_not_found(tag: &str) -> Self {
455 let mut error = Error::internal_error();
456 error.message = format!("Profile result associated with tag '{tag}' not found in the SVM");
457 Self(error)
458 }
459
460 pub(crate) fn expected_profile_not_found(key: &surfpool_types::UuidOrSignature) -> Self {
461 let mut error = Error::internal_error();
462 error.message = format!("Expected profile not found for key {key}");
463 Self(error)
464 }
465}
466
467impl From<StorageError> for SurfpoolError {
468 fn from(e: StorageError) -> Self {
469 let mut error = Error::internal_error();
470 error.data = Some(json!(format!("Storage error: {}", e.to_string())));
471 SurfpoolError(error)
472 }
473}
474
475impl From<LiteSVMError> for SurfpoolError {
476 fn from(e: LiteSVMError) -> Self {
477 let mut error = Error::internal_error();
478 error.data = Some(json!(format!("LiteSVM error: {}", e.to_string())));
479 SurfpoolError(error)
480 }
481}
482
483impl From<TransactionError> for SurfpoolError {
484 fn from(e: TransactionError) -> Self {
485 let mut error = Error::internal_error();
486 error.data = Some(json!(format!("Transaction error: {}", e.to_string())));
487 SurfpoolError(error)
488 }
489}
490
491#[derive(Debug, Clone)]
499pub enum AirdropError {
500 ZeroAmount,
501 BelowRentExemption { lamports: u64, min_rent: u64 },
502 Other(SurfpoolError),
503}
504
505impl From<SurfpoolError> for AirdropError {
506 fn from(e: SurfpoolError) -> Self {
507 AirdropError::Other(e)
508 }
509}
510
511impl From<StorageError> for AirdropError {
512 fn from(e: StorageError) -> Self {
513 AirdropError::Other(SurfpoolError::from(e))
514 }
515}
516
517impl From<AirdropError> for SurfpoolError {
518 fn from(e: AirdropError) -> Self {
519 match e {
520 AirdropError::ZeroAmount => SurfpoolError(Error::invalid_params(
521 "Airdrop amount must be greater than zero",
522 )),
523 AirdropError::BelowRentExemption { lamports, min_rent } => {
530 SurfpoolError(Error::invalid_params(format!(
531 "Airdrop amount {lamports} is below the rent-exempt minimum of {min_rent} lamports"
532 )))
533 }
534 AirdropError::Other(e) => e,
535 }
536 }
537}
538
539impl From<AirdropError> for Error {
540 fn from(e: AirdropError) -> Self {
541 SurfpoolError::from(e).into()
542 }
543}
544
545impl Display for AirdropError {
546 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
547 match self {
548 AirdropError::ZeroAmount => write!(f, "airdrop amount must be greater than zero"),
549 AirdropError::BelowRentExemption { lamports, min_rent } => write!(
550 f,
551 "airdrop amount {lamports} is below the rent-exempt minimum of {min_rent} lamports"
552 ),
553 AirdropError::Other(e) => Display::fmt(e, f),
554 }
555 }
556}
557
558impl std::error::Error for AirdropError {}