cdk_common/
error.rs

1//! Errors
2
3use std::array::TryFromSliceError;
4use std::fmt;
5
6use cashu::{CurrencyUnit, PaymentMethod};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use serde_json::Value;
9use thiserror::Error;
10
11use crate::nuts::Id;
12use crate::util::hex;
13#[cfg(feature = "wallet")]
14use crate::wallet::WalletKey;
15use crate::Amount;
16
17/// CDK Error
18#[derive(Debug, Error)]
19pub enum Error {
20    /// Mint does not have a key for amount
21    #[error("No Key for Amount")]
22    AmountKey,
23    /// Keyset is not known
24    #[error("Keyset id not known: `{0}`")]
25    KeysetUnknown(Id),
26    /// Unsupported unit
27    #[error("Unit unsupported")]
28    UnsupportedUnit,
29    /// Payment failed
30    #[error("Payment failed")]
31    PaymentFailed,
32    /// Payment pending
33    #[error("Payment pending")]
34    PaymentPending,
35    /// Invoice already paid
36    #[error("Request already paid")]
37    RequestAlreadyPaid,
38    /// Invalid payment request
39    #[error("Invalid payment request")]
40    InvalidPaymentRequest,
41    /// Bolt11 invoice does not have amount
42    #[error("Invoice Amount undefined")]
43    InvoiceAmountUndefined,
44    /// Split Values must be less then or equal to amount
45    #[error("Split Values must be less then or equal to amount")]
46    SplitValuesGreater,
47    /// Amount overflow
48    #[error("Amount Overflow")]
49    AmountOverflow,
50    /// Witness missing or invalid
51    #[error("Signature missing or invalid")]
52    SignatureMissingOrInvalid,
53    /// Amountless Invoice Not supported
54    #[error("Amount Less Invoice is not allowed")]
55    AmountLessNotAllowed,
56    /// Multi-Part Internal Melt Quotes are not supported
57    #[error("Multi-Part Internal Melt Quotes are not supported")]
58    InternalMultiPartMeltQuote,
59    /// Multi-Part Payment not supported for unit and method
60    #[error("Multi-Part payment is not supported for unit `{0}` and method `{1}`")]
61    MppUnitMethodNotSupported(CurrencyUnit, PaymentMethod),
62    /// Clear Auth Required
63    #[error("Clear Auth Required")]
64    ClearAuthRequired,
65    /// Blind Auth Required
66    #[error("Blind Auth Required")]
67    BlindAuthRequired,
68    /// Clear Auth Failed
69    #[error("Clear Auth Failed")]
70    ClearAuthFailed,
71    /// Blind Auth Failed
72    #[error("Blind Auth Failed")]
73    BlindAuthFailed,
74    /// Auth settings undefined
75    #[error("Auth settings undefined")]
76    AuthSettingsUndefined,
77    /// Mint time outside of tolerance
78    #[error("Mint time outside of tolerance")]
79    MintTimeExceedsTolerance,
80    /// Insufficient blind auth tokens
81    #[error("Insufficient blind auth tokens, must reauth")]
82    InsufficientBlindAuthTokens,
83    /// Auth localstore undefined
84    #[error("Auth localstore undefined")]
85    AuthLocalstoreUndefined,
86    /// Wallet cat not set
87    #[error("Wallet cat not set")]
88    CatNotSet,
89    /// Could not get mint info
90    #[error("Could not get mint info")]
91    CouldNotGetMintInfo,
92    /// Multi-Part Payment not supported for unit and method
93    #[error("Amountless invoices are not supported for unit `{0}` and method `{1}`")]
94    AmountlessInvoiceNotSupported(CurrencyUnit, PaymentMethod),
95    /// Duplicate Payment id
96    #[error("Payment id seen for mint")]
97    DuplicatePaymentId,
98    /// Pubkey required
99    #[error("Pubkey required")]
100    PubkeyRequired,
101    /// Invalid payment method
102    #[error("Invalid payment method")]
103    InvalidPaymentMethod,
104    /// Amount undefined
105    #[error("Amount undefined")]
106    AmountUndefined,
107    /// Unsupported payment method
108    #[error("Payment method unsupported")]
109    UnsupportedPaymentMethod,
110    /// Could not parse bolt12
111    #[error("Could not parse bolt12")]
112    Bolt12parse,
113
114    /// BIP353 address parsing error
115    #[error("Failed to parse BIP353 address: {0}")]
116    Bip353Parse(String),
117
118    /// Operation timeout
119    #[error("Operation timeout")]
120    Timeout,
121
122    /// BIP353 address resolution error
123    #[error("Failed to resolve BIP353 address: {0}")]
124    Bip353Resolve(String),
125    /// BIP353 no Lightning offer found
126    #[error("No Lightning offer found in BIP353 payment instructions")]
127    Bip353NoLightningOffer,
128
129    /// Internal Error - Send error
130    #[error("Internal send error: {0}")]
131    SendError(String),
132
133    /// Internal Error - Recv error
134    #[error("Internal receive error: {0}")]
135    RecvError(String),
136
137    // Mint Errors
138    /// Minting is disabled
139    #[error("Minting is disabled")]
140    MintingDisabled,
141    /// Quote is not known
142    #[error("Unknown quote")]
143    UnknownQuote,
144    /// Quote is expired
145    #[error("Expired quote: Expired: `{0}`, Time: `{1}`")]
146    ExpiredQuote(u64, u64),
147    /// Amount is outside of allowed range
148    #[error("Amount must be between `{0}` and `{1}` is `{2}`")]
149    AmountOutofLimitRange(Amount, Amount, Amount),
150    /// Quote is not paiud
151    #[error("Quote not paid")]
152    UnpaidQuote,
153    /// Quote is pending
154    #[error("Quote pending")]
155    PendingQuote,
156    /// ecash already issued for quote
157    #[error("Quote already issued")]
158    IssuedQuote,
159    /// Quote has already been paid
160    #[error("Quote is already paid")]
161    PaidQuote,
162    /// Payment state is unknown
163    #[error("Payment state is unknown")]
164    UnknownPaymentState,
165    /// Melting is disabled
166    #[error("Minting is disabled")]
167    MeltingDisabled,
168    /// Unknown Keyset
169    #[error("Unknown Keyset")]
170    UnknownKeySet,
171    /// BlindedMessage is already signed
172    #[error("Blinded Message is already signed")]
173    BlindedMessageAlreadySigned,
174    /// Inactive Keyset
175    #[error("Inactive Keyset")]
176    InactiveKeyset,
177    /// Transaction unbalanced
178    #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
179    TransactionUnbalanced(u64, u64, u64),
180    /// Duplicate proofs provided
181    #[error("Duplicate Inputs")]
182    DuplicateInputs,
183    /// Duplicate output
184    #[error("Duplicate outputs")]
185    DuplicateOutputs,
186    /// Multiple units provided
187    #[error("Cannot have multiple units")]
188    MultipleUnits,
189    /// Unit mismatch
190    #[error("Input unit must match output")]
191    UnitMismatch,
192    /// Sig all cannot be used in melt
193    #[error("Sig all cannot be used in melt")]
194    SigAllUsedInMelt,
195    /// Token is already spent
196    #[error("Token Already Spent")]
197    TokenAlreadySpent,
198    /// Token is already pending
199    #[error("Token Pending")]
200    TokenPending,
201    /// Internal Error
202    #[error("Internal Error")]
203    Internal,
204    /// Oidc config not set
205    #[error("Oidc client not set")]
206    OidcNotSet,
207
208    // Wallet Errors
209    /// P2PK spending conditions not met
210    #[error("P2PK condition not met `{0}`")]
211    P2PKConditionsNotMet(String),
212    /// Spending Locktime not provided
213    #[error("Spending condition locktime not provided")]
214    LocktimeNotProvided,
215    /// Invalid Spending Conditions
216    #[error("Invalid spending conditions: `{0}`")]
217    InvalidSpendConditions(String),
218    /// Incorrect Wallet
219    #[error("Incorrect wallet: `{0}`")]
220    IncorrectWallet(String),
221    /// Unknown Wallet
222    #[error("Unknown wallet: `{0}`")]
223    #[cfg(feature = "wallet")]
224    UnknownWallet(WalletKey),
225    /// Max Fee Ecxeded
226    #[error("Max fee exceeded")]
227    MaxFeeExceeded,
228    /// Url path segments could not be joined
229    #[error("Url path segments could not be joined")]
230    UrlPathSegments,
231    ///  Unknown error response
232    #[error("Unknown error response: `{0}`")]
233    UnknownErrorResponse(String),
234    /// Invalid DLEQ proof
235    #[error("Could not verify DLEQ proof")]
236    CouldNotVerifyDleq,
237    /// Dleq Proof not provided for signature
238    #[error("Dleq proof not provided for signature")]
239    DleqProofNotProvided,
240    /// Incorrect Mint
241    /// Token does not match wallet mint
242    #[error("Token does not match wallet mint")]
243    IncorrectMint,
244    /// Receive can only be used with tokens from single mint
245    #[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
246    MultiMintTokenNotSupported,
247    /// Preimage not provided
248    #[error("Preimage not provided")]
249    PreimageNotProvided,
250    /// Insufficient Funds
251    #[error("Insufficient funds")]
252    InsufficientFunds,
253    /// Unexpected proof state
254    #[error("Unexpected proof state")]
255    UnexpectedProofState,
256    /// No active keyset
257    #[error("No active keyset")]
258    NoActiveKeyset,
259    /// Incorrect quote amount
260    #[error("Incorrect quote amount")]
261    IncorrectQuoteAmount,
262    /// Invoice Description not supported
263    #[error("Invoice Description not supported")]
264    InvoiceDescriptionUnsupported,
265    /// Invalid transaction direction
266    #[error("Invalid transaction direction")]
267    InvalidTransactionDirection,
268    /// Invalid transaction id
269    #[error("Invalid transaction id")]
270    InvalidTransactionId,
271    /// Transaction not found
272    #[error("Transaction not found")]
273    TransactionNotFound,
274    /// Custom Error
275    #[error("`{0}`")]
276    Custom(String),
277
278    // External Error conversions
279    /// Parse invoice error
280    #[error(transparent)]
281    Invoice(#[from] lightning_invoice::ParseOrSemanticError),
282    /// Bip32 error
283    #[error(transparent)]
284    Bip32(#[from] bitcoin::bip32::Error),
285    /// Parse int error
286    #[error(transparent)]
287    ParseInt(#[from] std::num::ParseIntError),
288    /// Parse 9rl Error
289    #[error(transparent)]
290    UrlParseError(#[from] url::ParseError),
291    /// Utf8 parse error
292    #[error(transparent)]
293    Utf8ParseError(#[from] std::string::FromUtf8Error),
294    /// Serde Json error
295    #[error(transparent)]
296    SerdeJsonError(#[from] serde_json::Error),
297    /// Base64 error
298    #[error(transparent)]
299    Base64Error(#[from] bitcoin::base64::DecodeError),
300    /// From hex error
301    #[error(transparent)]
302    HexError(#[from] hex::Error),
303    /// Http transport error
304    #[error("Http transport error {0:?}: {1}")]
305    HttpError(Option<u16>, String),
306    #[cfg(feature = "wallet")]
307    // Crate error conversions
308    /// Cashu Url Error
309    #[error(transparent)]
310    CashuUrl(#[from] crate::mint_url::Error),
311    /// Secret error
312    #[error(transparent)]
313    Secret(#[from] crate::secret::Error),
314    /// Amount Error
315    #[error(transparent)]
316    AmountError(#[from] crate::amount::Error),
317    /// DHKE Error
318    #[error(transparent)]
319    DHKE(#[from] crate::dhke::Error),
320    /// NUT00 Error
321    #[error(transparent)]
322    NUT00(#[from] crate::nuts::nut00::Error),
323    /// Nut01 error
324    #[error(transparent)]
325    NUT01(#[from] crate::nuts::nut01::Error),
326    /// NUT02 error
327    #[error(transparent)]
328    NUT02(#[from] crate::nuts::nut02::Error),
329    /// NUT03 error
330    #[error(transparent)]
331    NUT03(#[from] crate::nuts::nut03::Error),
332    /// NUT04 error
333    #[error(transparent)]
334    NUT04(#[from] crate::nuts::nut04::Error),
335    /// NUT05 error
336    #[error(transparent)]
337    NUT05(#[from] crate::nuts::nut05::Error),
338    /// NUT11 Error
339    #[error(transparent)]
340    NUT11(#[from] crate::nuts::nut11::Error),
341    /// NUT12 Error
342    #[error(transparent)]
343    NUT12(#[from] crate::nuts::nut12::Error),
344    /// NUT13 Error
345    #[error(transparent)]
346    #[cfg(feature = "wallet")]
347    NUT13(#[from] crate::nuts::nut13::Error),
348    /// NUT14 Error
349    #[error(transparent)]
350    NUT14(#[from] crate::nuts::nut14::Error),
351    /// NUT18 Error
352    #[error(transparent)]
353    NUT18(#[from] crate::nuts::nut18::Error),
354    /// NUT20 Error
355    #[error(transparent)]
356    NUT20(#[from] crate::nuts::nut20::Error),
357    /// NUT21 Error
358    #[error(transparent)]
359    NUT21(#[from] crate::nuts::nut21::Error),
360    /// NUT22 Error
361    #[error(transparent)]
362    NUT22(#[from] crate::nuts::nut22::Error),
363    /// NUT23 Error
364    #[error(transparent)]
365    NUT23(#[from] crate::nuts::nut23::Error),
366    /// From slice error
367    #[error(transparent)]
368    TryFromSliceError(#[from] TryFromSliceError),
369    /// Database Error
370    #[error(transparent)]
371    Database(crate::database::Error),
372    /// Payment Error
373    #[error(transparent)]
374    #[cfg(feature = "mint")]
375    Payment(#[from] crate::payment::Error),
376}
377
378/// CDK Error Response
379///
380/// See NUT definition in [00](https://github.com/cashubtc/nuts/blob/main/00.md)
381#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
382#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
383pub struct ErrorResponse {
384    /// Error Code
385    pub code: ErrorCode,
386    /// Human readable Text
387    pub error: Option<String>,
388    /// Longer human readable description
389    pub detail: Option<String>,
390}
391
392impl fmt::Display for ErrorResponse {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        write!(
395            f,
396            "code: {}, error: {}, detail: {}",
397            self.code,
398            self.error.clone().unwrap_or_default(),
399            self.detail.clone().unwrap_or_default()
400        )
401    }
402}
403
404impl ErrorResponse {
405    /// Create new [`ErrorResponse`]
406    pub fn new(code: ErrorCode, error: Option<String>, detail: Option<String>) -> Self {
407        Self {
408            code,
409            error,
410            detail,
411        }
412    }
413
414    /// Error response from json
415    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
416        let value: Value = serde_json::from_str(json)?;
417
418        Self::from_value(value)
419    }
420
421    /// Error response from json Value
422    pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
423        match serde_json::from_value::<ErrorResponse>(value.clone()) {
424            Ok(res) => Ok(res),
425            Err(_) => Ok(Self {
426                code: ErrorCode::Unknown(999),
427                error: Some(value.to_string()),
428                detail: None,
429            }),
430        }
431    }
432}
433
434impl From<Error> for ErrorResponse {
435    fn from(err: Error) -> ErrorResponse {
436        match err {
437            Error::TokenAlreadySpent => ErrorResponse {
438                code: ErrorCode::TokenAlreadySpent,
439                error: Some(err.to_string()),
440                detail: None,
441            },
442            Error::UnsupportedUnit => ErrorResponse {
443                code: ErrorCode::UnsupportedUnit,
444                error: Some(err.to_string()),
445                detail: None,
446            },
447            Error::PaymentFailed => ErrorResponse {
448                code: ErrorCode::LightningError,
449                error: Some(err.to_string()),
450                detail: None,
451            },
452            Error::RequestAlreadyPaid => ErrorResponse {
453                code: ErrorCode::InvoiceAlreadyPaid,
454                error: Some("Invoice already paid.".to_string()),
455                detail: None,
456            },
457            Error::TransactionUnbalanced(inputs_total, outputs_total, fee_expected) => {
458                ErrorResponse {
459                    code: ErrorCode::TransactionUnbalanced,
460                    error: Some(format!(
461                        "Inputs: {inputs_total}, Outputs: {outputs_total}, expected_fee: {fee_expected}",
462                    )),
463                    detail: Some("Transaction inputs should equal outputs less fee".to_string()),
464                }
465            }
466            Error::MintingDisabled => ErrorResponse {
467                code: ErrorCode::MintingDisabled,
468                error: Some(err.to_string()),
469                detail: None,
470            },
471            Error::BlindedMessageAlreadySigned => ErrorResponse {
472                code: ErrorCode::BlindedMessageAlreadySigned,
473                error: Some(err.to_string()),
474                detail: None,
475            },
476            Error::InsufficientFunds => ErrorResponse {
477                code: ErrorCode::TransactionUnbalanced,
478                error: Some(err.to_string()),
479                detail: None,
480            },
481            Error::AmountOutofLimitRange(_min, _max, _amount) => ErrorResponse {
482                code: ErrorCode::AmountOutofLimitRange,
483                error: Some(err.to_string()),
484                detail: None,
485            },
486            Error::ExpiredQuote(_, _) => ErrorResponse {
487                code: ErrorCode::QuoteExpired,
488                error: Some(err.to_string()),
489                detail: None,
490            },
491            Error::PendingQuote => ErrorResponse {
492                code: ErrorCode::QuotePending,
493                error: Some(err.to_string()),
494                detail: None,
495            },
496            Error::TokenPending => ErrorResponse {
497                code: ErrorCode::TokenPending,
498                error: Some(err.to_string()),
499                detail: None,
500            },
501            Error::ClearAuthRequired => ErrorResponse {
502                code: ErrorCode::ClearAuthRequired,
503                error: None,
504                detail: None,
505            },
506            Error::ClearAuthFailed => ErrorResponse {
507                code: ErrorCode::ClearAuthFailed,
508                error: None,
509                detail: None,
510            },
511            Error::BlindAuthRequired => ErrorResponse {
512                code: ErrorCode::BlindAuthRequired,
513                error: None,
514                detail: None,
515            },
516            Error::BlindAuthFailed => ErrorResponse {
517                code: ErrorCode::BlindAuthFailed,
518                error: None,
519                detail: None,
520            },
521            Error::NUT20(err) => ErrorResponse {
522                code: ErrorCode::WitnessMissingOrInvalid,
523                error: Some(err.to_string()),
524                detail: None,
525            },
526            Error::DuplicateInputs => ErrorResponse {
527                code: ErrorCode::DuplicateInputs,
528                error: Some(err.to_string()),
529                detail: None,
530            },
531            Error::DuplicateOutputs => ErrorResponse {
532                code: ErrorCode::DuplicateOutputs,
533                error: Some(err.to_string()),
534                detail: None,
535            },
536            Error::MultipleUnits => ErrorResponse {
537                code: ErrorCode::MultipleUnits,
538                error: Some(err.to_string()),
539                detail: None,
540            },
541            Error::UnitMismatch => ErrorResponse {
542                code: ErrorCode::UnitMismatch,
543                error: Some(err.to_string()),
544                detail: None,
545            },
546            Error::UnpaidQuote => ErrorResponse {
547                code: ErrorCode::QuoteNotPaid,
548                error: Some(err.to_string()),
549                detail: None
550            },
551            _ => ErrorResponse {
552                code: ErrorCode::Unknown(9999),
553                error: Some(err.to_string()),
554                detail: None,
555            },
556        }
557    }
558}
559
560#[cfg(feature = "mint")]
561impl From<crate::database::Error> for Error {
562    fn from(db_error: crate::database::Error) -> Self {
563        match db_error {
564            crate::database::Error::InvalidStateTransition(state) => match state {
565                crate::state::Error::Pending => Self::TokenPending,
566                crate::state::Error::AlreadySpent => Self::TokenAlreadySpent,
567                state => Self::Database(crate::database::Error::InvalidStateTransition(state)),
568            },
569            db_error => Self::Database(db_error),
570        }
571    }
572}
573
574#[cfg(not(feature = "mint"))]
575impl From<crate::database::Error> for Error {
576    fn from(db_error: crate::database::Error) -> Self {
577        Self::Database(db_error)
578    }
579}
580
581impl From<ErrorResponse> for Error {
582    fn from(err: ErrorResponse) -> Error {
583        match err.code {
584            ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
585            ErrorCode::QuoteNotPaid => Self::UnpaidQuote,
586            ErrorCode::QuotePending => Self::PendingQuote,
587            ErrorCode::QuoteExpired => Self::ExpiredQuote(0, 0),
588            ErrorCode::KeysetNotFound => Self::UnknownKeySet,
589            ErrorCode::KeysetInactive => Self::InactiveKeyset,
590            ErrorCode::BlindedMessageAlreadySigned => Self::BlindedMessageAlreadySigned,
591            ErrorCode::UnsupportedUnit => Self::UnsupportedUnit,
592            ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced(0, 0, 0),
593            ErrorCode::MintingDisabled => Self::MintingDisabled,
594            ErrorCode::InvoiceAlreadyPaid => Self::RequestAlreadyPaid,
595            ErrorCode::TokenNotVerified => Self::DHKE(crate::dhke::Error::TokenNotVerified),
596            ErrorCode::LightningError => Self::PaymentFailed,
597            ErrorCode::AmountOutofLimitRange => {
598                Self::AmountOutofLimitRange(Amount::default(), Amount::default(), Amount::default())
599            }
600            ErrorCode::TokenPending => Self::TokenPending,
601            ErrorCode::WitnessMissingOrInvalid => Self::SignatureMissingOrInvalid,
602            ErrorCode::DuplicateInputs => Self::DuplicateInputs,
603            ErrorCode::DuplicateOutputs => Self::DuplicateOutputs,
604            ErrorCode::MultipleUnits => Self::MultipleUnits,
605            ErrorCode::UnitMismatch => Self::UnitMismatch,
606            ErrorCode::ClearAuthRequired => Self::ClearAuthRequired,
607            ErrorCode::BlindAuthRequired => Self::BlindAuthRequired,
608            _ => Self::UnknownErrorResponse(err.to_string()),
609        }
610    }
611}
612
613/// Possible Error Codes
614#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
615#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
616pub enum ErrorCode {
617    /// Token is already spent
618    TokenAlreadySpent,
619    /// Token Pending
620    TokenPending,
621    /// Quote is not paid
622    QuoteNotPaid,
623    /// Quote is not expired
624    QuoteExpired,
625    /// Quote Pending
626    QuotePending,
627    /// Keyset is not found
628    KeysetNotFound,
629    /// Keyset inactive
630    KeysetInactive,
631    /// Blinded Message Already signed
632    BlindedMessageAlreadySigned,
633    /// Unsupported unit
634    UnsupportedUnit,
635    /// Token already issed for quote
636    TokensAlreadyIssued,
637    /// Minting Disabled
638    MintingDisabled,
639    /// Invoice Already Paid
640    InvoiceAlreadyPaid,
641    /// Token Not Verified
642    TokenNotVerified,
643    /// Lightning Error
644    LightningError,
645    /// Unbalanced Error
646    TransactionUnbalanced,
647    /// Amount outside of allowed range
648    AmountOutofLimitRange,
649    /// Witness missing or invalid
650    WitnessMissingOrInvalid,
651    /// Duplicate Inputs
652    DuplicateInputs,
653    /// Duplicate Outputs
654    DuplicateOutputs,
655    /// Multiple Units
656    MultipleUnits,
657    /// Input unit does not match output
658    UnitMismatch,
659    /// Clear Auth Required
660    ClearAuthRequired,
661    /// Clear Auth Failed
662    ClearAuthFailed,
663    /// Blind Auth Required
664    BlindAuthRequired,
665    /// Blind Auth Failed
666    BlindAuthFailed,
667    /// Unknown error code
668    Unknown(u16),
669}
670
671impl ErrorCode {
672    /// Error code from u16
673    pub fn from_code(code: u16) -> Self {
674        match code {
675            10002 => Self::BlindedMessageAlreadySigned,
676            10003 => Self::TokenNotVerified,
677            11001 => Self::TokenAlreadySpent,
678            11002 => Self::TransactionUnbalanced,
679            11005 => Self::UnsupportedUnit,
680            11006 => Self::AmountOutofLimitRange,
681            11007 => Self::DuplicateInputs,
682            11008 => Self::DuplicateOutputs,
683            11009 => Self::MultipleUnits,
684            11010 => Self::UnitMismatch,
685            11012 => Self::TokenPending,
686            12001 => Self::KeysetNotFound,
687            12002 => Self::KeysetInactive,
688            20000 => Self::LightningError,
689            20001 => Self::QuoteNotPaid,
690            20002 => Self::TokensAlreadyIssued,
691            20003 => Self::MintingDisabled,
692            20005 => Self::QuotePending,
693            20006 => Self::InvoiceAlreadyPaid,
694            20007 => Self::QuoteExpired,
695            20008 => Self::WitnessMissingOrInvalid,
696            30001 => Self::ClearAuthRequired,
697            30002 => Self::ClearAuthFailed,
698            31001 => Self::BlindAuthRequired,
699            31002 => Self::BlindAuthFailed,
700            _ => Self::Unknown(code),
701        }
702    }
703
704    /// Error code to u16
705    pub fn to_code(&self) -> u16 {
706        match self {
707            Self::BlindedMessageAlreadySigned => 10002,
708            Self::TokenNotVerified => 10003,
709            Self::TokenAlreadySpent => 11001,
710            Self::TransactionUnbalanced => 11002,
711            Self::UnsupportedUnit => 11005,
712            Self::AmountOutofLimitRange => 11006,
713            Self::DuplicateInputs => 11007,
714            Self::DuplicateOutputs => 11008,
715            Self::MultipleUnits => 11009,
716            Self::UnitMismatch => 11010,
717            Self::TokenPending => 11012,
718            Self::KeysetNotFound => 12001,
719            Self::KeysetInactive => 12002,
720            Self::LightningError => 20000,
721            Self::QuoteNotPaid => 20001,
722            Self::TokensAlreadyIssued => 20002,
723            Self::MintingDisabled => 20003,
724            Self::QuotePending => 20005,
725            Self::InvoiceAlreadyPaid => 20006,
726            Self::QuoteExpired => 20007,
727            Self::WitnessMissingOrInvalid => 20008,
728            Self::ClearAuthRequired => 30001,
729            Self::ClearAuthFailed => 30002,
730            Self::BlindAuthRequired => 31001,
731            Self::BlindAuthFailed => 31002,
732            Self::Unknown(code) => *code,
733        }
734    }
735}
736
737impl Serialize for ErrorCode {
738    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
739    where
740        S: Serializer,
741    {
742        serializer.serialize_u16(self.to_code())
743    }
744}
745
746impl<'de> Deserialize<'de> for ErrorCode {
747    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
748    where
749        D: Deserializer<'de>,
750    {
751        let code = u16::deserialize(deserializer)?;
752
753        Ok(ErrorCode::from_code(code))
754    }
755}
756
757impl fmt::Display for ErrorCode {
758    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
759        write!(f, "{}", self.to_code())
760    }
761}