cdk_common/
error.rs

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