casper_types/
api_error.rs

1//! Contains [`ApiError`] and associated helper functions.
2
3use core::{
4    convert::TryFrom,
5    fmt::{self, Debug, Formatter},
6};
7
8use crate::{
9    account::{
10        AddKeyFailure, RemoveKeyFailure, SetThresholdFailure, TryFromIntError,
11        TryFromSliceForAccountHashError, UpdateKeyFailure,
12    },
13    bytesrepr, contracts,
14    system::{auction, handle_payment, mint},
15    CLValueError,
16};
17
18/// All `Error` variants defined in this library other than `Error::User` will convert to a `u32`
19/// value less than or equal to `RESERVED_ERROR_MAX`.
20const RESERVED_ERROR_MAX: u32 = u16::MAX as u32; // 0..=65535
21
22/// Handle Payment errors will have this value added to them when being converted to a `u32`.
23const POS_ERROR_OFFSET: u32 = RESERVED_ERROR_MAX - u8::MAX as u32; // 65280..=65535
24
25/// Mint errors will have this value added to them when being converted to a `u32`.
26const MINT_ERROR_OFFSET: u32 = (POS_ERROR_OFFSET - 1) - u8::MAX as u32; // 65024..=65279
27
28/// Contract header errors will have this value added to them when being converted to a `u32`.
29const HEADER_ERROR_OFFSET: u32 = (MINT_ERROR_OFFSET - 1) - u8::MAX as u32; // 64768..=65023
30
31/// Contract header errors will have this value added to them when being converted to a `u32`.
32const AUCTION_ERROR_OFFSET: u32 = (HEADER_ERROR_OFFSET - 1) - u8::MAX as u32; // 64512..=64767
33
34/// Minimum value of user error's inclusive range.
35const USER_ERROR_MIN: u32 = RESERVED_ERROR_MAX + 1;
36
37/// Maximum value of user error's inclusive range.
38const USER_ERROR_MAX: u32 = 2 * RESERVED_ERROR_MAX + 1;
39
40/// Minimum value of Mint error's inclusive range.
41const MINT_ERROR_MIN: u32 = MINT_ERROR_OFFSET;
42
43/// Maximum value of Mint error's inclusive range.
44const MINT_ERROR_MAX: u32 = POS_ERROR_OFFSET - 1;
45
46/// Minimum value of Handle Payment error's inclusive range.
47const HP_ERROR_MIN: u32 = POS_ERROR_OFFSET;
48
49/// Maximum value of Handle Payment error's inclusive range.
50const HP_ERROR_MAX: u32 = RESERVED_ERROR_MAX;
51
52/// Minimum value of contract header error's inclusive range.
53const HEADER_ERROR_MIN: u32 = HEADER_ERROR_OFFSET;
54
55/// Maximum value of contract header error's inclusive range.
56const HEADER_ERROR_MAX: u32 = HEADER_ERROR_OFFSET + u8::MAX as u32;
57
58/// Minimum value of an auction contract error's inclusive range.
59const AUCTION_ERROR_MIN: u32 = AUCTION_ERROR_OFFSET;
60
61/// Maximum value of an auction contract error's inclusive range.
62const AUCTION_ERROR_MAX: u32 = AUCTION_ERROR_OFFSET + u8::MAX as u32;
63
64/// Errors which can be encountered while running a smart contract.
65///
66/// An `ApiError` can be converted to a `u32` in order to be passed via the execution engine's
67/// `ext_ffi::casper_revert()` function.  This means the information each variant can convey is
68/// limited.
69///
70/// The variants are split into numeric ranges as follows:
71///
72/// | Inclusive range | Variant(s)                                                      |
73/// | ----------------| ----------------------------------------------------------------|
74/// | [1, 64511]      | all except reserved system contract error ranges defined below. |
75/// | [64512, 64767]  | `Auction`                                                       |
76/// | [64768, 65023]  | `ContractHeader`                                                |
77/// | [65024, 65279]  | `Mint`                                                          |
78/// | [65280, 65535]  | `HandlePayment`                                                 |
79/// | [65536, 131071] | `User`                                                          |
80///
81/// Users can specify a C-style enum and implement `From` to ease usage of
82/// `casper_contract::runtime::revert()`, e.g.
83/// ```
84/// use casper_types::ApiError;
85///
86/// #[repr(u16)]
87/// enum FailureCode {
88///     Zero = 0,  // 65,536 as an ApiError::User
89///     One,       // 65,537 as an ApiError::User
90///     Two        // 65,538 as an ApiError::User
91/// }
92///
93/// impl From<FailureCode> for ApiError {
94///     fn from(code: FailureCode) -> Self {
95///         ApiError::User(code as u16)
96///     }
97/// }
98///
99/// assert_eq!(ApiError::User(1), FailureCode::One.into());
100/// assert_eq!(65_536, u32::from(ApiError::from(FailureCode::Zero)));
101/// assert_eq!(65_538, u32::from(ApiError::from(FailureCode::Two)));
102/// ```
103#[derive(Copy, Clone, PartialEq, Eq)]
104#[non_exhaustive]
105pub enum ApiError {
106    /// Optional data was unexpectedly `None`.
107    /// ```
108    /// # use casper_types::ApiError;
109    /// assert_eq!(ApiError::from(1), ApiError::None);
110    /// ```
111    None,
112    /// Specified argument not provided.
113    /// ```
114    /// # use casper_types::ApiError;
115    /// assert_eq!(ApiError::from(2), ApiError::MissingArgument);
116    /// ```
117    MissingArgument,
118    /// Argument not of correct type.
119    /// ```
120    /// # use casper_types::ApiError;
121    /// assert_eq!(ApiError::from(3), ApiError::InvalidArgument);
122    /// ```
123    InvalidArgument,
124    /// Failed to deserialize a value.
125    /// ```
126    /// # use casper_types::ApiError;
127    /// assert_eq!(ApiError::from(4), ApiError::Deserialize);
128    /// ```
129    Deserialize,
130    /// `casper_contract::storage::read()` returned an error.
131    /// ```
132    /// # use casper_types::ApiError;
133    /// assert_eq!(ApiError::from(5), ApiError::Read);
134    /// ```
135    Read,
136    /// The given key returned a `None` value.
137    /// ```
138    /// # use casper_types::ApiError;
139    /// assert_eq!(ApiError::from(6), ApiError::ValueNotFound);
140    /// ```
141    ValueNotFound,
142    /// Failed to find a specified contract.
143    /// ```
144    /// # use casper_types::ApiError;
145    /// assert_eq!(ApiError::from(7), ApiError::ContractNotFound);
146    /// ```
147    ContractNotFound,
148    /// A call to `casper_contract::runtime::get_key()` returned a failure.
149    /// ```
150    /// # use casper_types::ApiError;
151    /// assert_eq!(ApiError::from(8), ApiError::GetKey);
152    /// ```
153    GetKey,
154    /// The [`Key`](crate::Key) variant was not as expected.
155    /// ```
156    /// # use casper_types::ApiError;
157    /// assert_eq!(ApiError::from(9), ApiError::UnexpectedKeyVariant);
158    /// ```
159    UnexpectedKeyVariant,
160    /// Obsolete error variant (we no longer have ContractRef).
161    /// ```
162    /// # use casper_types::ApiError;
163    /// assert_eq!(ApiError::from(10), ApiError::UnexpectedContractRefVariant);
164    /// ```
165    UnexpectedContractRefVariant, // TODO: this variant is not used any longer and can be removed
166    /// Invalid purse name given.
167    /// ```
168    /// # use casper_types::ApiError;
169    /// assert_eq!(ApiError::from(11), ApiError::InvalidPurseName);
170    /// ```
171    InvalidPurseName,
172    /// Invalid purse retrieved.
173    /// ```
174    /// # use casper_types::ApiError;
175    /// assert_eq!(ApiError::from(12), ApiError::InvalidPurse);
176    /// ```
177    InvalidPurse,
178    /// Failed to upgrade contract at [`URef`](crate::URef).
179    /// ```
180    /// # use casper_types::ApiError;
181    /// assert_eq!(ApiError::from(13), ApiError::UpgradeContractAtURef);
182    /// ```
183    UpgradeContractAtURef,
184    /// Failed to transfer motes.
185    /// ```
186    /// # use casper_types::ApiError;
187    /// assert_eq!(ApiError::from(14), ApiError::Transfer);
188    /// ```
189    Transfer,
190    /// The given [`URef`](crate::URef) has no access rights.
191    /// ```
192    /// # use casper_types::ApiError;
193    /// assert_eq!(ApiError::from(15), ApiError::NoAccessRights);
194    /// ```
195    NoAccessRights,
196    /// A given type could not be constructed from a [`CLValue`](crate::CLValue).
197    /// ```
198    /// # use casper_types::ApiError;
199    /// assert_eq!(ApiError::from(16), ApiError::CLTypeMismatch);
200    /// ```
201    CLTypeMismatch,
202    /// Early end of stream while deserializing.
203    /// ```
204    /// # use casper_types::ApiError;
205    /// assert_eq!(ApiError::from(17), ApiError::EarlyEndOfStream);
206    /// ```
207    EarlyEndOfStream,
208    /// Formatting error while deserializing.
209    /// ```
210    /// # use casper_types::ApiError;
211    /// assert_eq!(ApiError::from(18), ApiError::Formatting);
212    /// ```
213    Formatting,
214    /// Not all input bytes were consumed in [`deserialize`](crate::bytesrepr::deserialize).
215    /// ```
216    /// # use casper_types::ApiError;
217    /// assert_eq!(ApiError::from(19), ApiError::LeftOverBytes);
218    /// ```
219    LeftOverBytes,
220    /// Out of memory error.
221    /// ```
222    /// # use casper_types::ApiError;
223    /// assert_eq!(ApiError::from(20), ApiError::OutOfMemory);
224    /// ```
225    OutOfMemory,
226    /// There are already maximum [`AccountHash`](crate::account::AccountHash)s associated with the
227    /// given account.
228    /// ```
229    /// # use casper_types::ApiError;
230    /// assert_eq!(ApiError::from(21), ApiError::MaxKeysLimit);
231    /// ```
232    MaxKeysLimit,
233    /// The given [`AccountHash`](crate::account::AccountHash) is already associated with the given
234    /// account.
235    /// ```
236    /// # use casper_types::ApiError;
237    /// assert_eq!(ApiError::from(22), ApiError::DuplicateKey);
238    /// ```
239    DuplicateKey,
240    /// Caller doesn't have sufficient permissions to perform the given action.
241    /// ```
242    /// # use casper_types::ApiError;
243    /// assert_eq!(ApiError::from(23), ApiError::PermissionDenied);
244    /// ```
245    PermissionDenied,
246    /// The given [`AccountHash`](crate::account::AccountHash) is not associated with the given
247    /// account.
248    /// ```
249    /// # use casper_types::ApiError;
250    /// assert_eq!(ApiError::from(24), ApiError::MissingKey);
251    /// ```
252    MissingKey,
253    /// Removing/updating the given associated [`AccountHash`](crate::account::AccountHash) would
254    /// cause the total [`Weight`](crate::account::Weight) of all remaining `AccountHash`s to
255    /// fall below one of the action thresholds for the given account.
256    /// ```
257    /// # use casper_types::ApiError;
258    /// assert_eq!(ApiError::from(25), ApiError::ThresholdViolation);
259    /// ```
260    ThresholdViolation,
261    /// Setting the key-management threshold to a value lower than the deployment threshold is
262    /// disallowed.
263    /// ```
264    /// # use casper_types::ApiError;
265    /// assert_eq!(ApiError::from(26), ApiError::KeyManagementThreshold);
266    /// ```
267    KeyManagementThreshold,
268    /// Setting the deployment threshold to a value greater than any other threshold is disallowed.
269    /// ```
270    /// # use casper_types::ApiError;
271    /// assert_eq!(ApiError::from(27), ApiError::DeploymentThreshold);
272    /// ```
273    DeploymentThreshold,
274    /// Setting a threshold to a value greater than the total weight of associated keys is
275    /// disallowed.
276    /// ```
277    /// # use casper_types::ApiError;
278    /// assert_eq!(ApiError::from(28), ApiError::InsufficientTotalWeight);
279    /// ```
280    InsufficientTotalWeight,
281    /// The given `u32` doesn't map to a [`SystemContractType`](crate::system::SystemContractType).
282    /// ```
283    /// # use casper_types::ApiError;
284    /// assert_eq!(ApiError::from(29), ApiError::InvalidSystemContract);
285    /// ```
286    InvalidSystemContract,
287    /// Failed to create a new purse.
288    /// ```
289    /// # use casper_types::ApiError;
290    /// assert_eq!(ApiError::from(30), ApiError::PurseNotCreated);
291    /// ```
292    PurseNotCreated,
293    /// An unhandled value, likely representing a bug in the code.
294    /// ```
295    /// # use casper_types::ApiError;
296    /// assert_eq!(ApiError::from(31), ApiError::Unhandled);
297    /// ```
298    Unhandled,
299    /// The provided buffer is too small to complete an operation.
300    /// ```
301    /// # use casper_types::ApiError;
302    /// assert_eq!(ApiError::from(32), ApiError::BufferTooSmall);
303    /// ```
304    BufferTooSmall,
305    /// No data available in the host buffer.
306    /// ```
307    /// # use casper_types::ApiError;
308    /// assert_eq!(ApiError::from(33), ApiError::HostBufferEmpty);
309    /// ```
310    HostBufferEmpty,
311    /// The host buffer has been set to a value and should be consumed first by a read operation.
312    /// ```
313    /// # use casper_types::ApiError;
314    /// assert_eq!(ApiError::from(34), ApiError::HostBufferFull);
315    /// ```
316    HostBufferFull,
317    /// Could not lay out an array in memory
318    /// ```
319    /// # use casper_types::ApiError;
320    /// assert_eq!(ApiError::from(35), ApiError::AllocLayout);
321    /// ```
322    AllocLayout,
323    /// The `dictionary_item_key` length exceeds the maximum length.
324    /// ```
325    /// # use casper_types::ApiError;
326    /// assert_eq!(ApiError::from(36), ApiError::DictionaryItemKeyExceedsLength);
327    /// ```
328    DictionaryItemKeyExceedsLength,
329    /// The `dictionary_item_key` is invalid.
330    /// ```
331    /// # use casper_types::ApiError;
332    /// assert_eq!(ApiError::from(37), ApiError::InvalidDictionaryItemKey);
333    /// ```
334    InvalidDictionaryItemKey,
335    /// Unable to retrieve the requested system contract hash.
336    /// ```
337    /// # use casper_types::ApiError;
338    /// assert_eq!(ApiError::from(38), ApiError::MissingSystemContractHash);
339    /// ```
340    MissingSystemContractHash,
341    /// Exceeded a recursion depth limit.
342    /// ```
343    /// # use casper_types::ApiError;
344    /// assert_eq!(ApiError::from(39), ApiError::ExceededRecursionDepth);
345    /// ```
346    ExceededRecursionDepth,
347    /// Attempt to serialize a value that does not have a serialized representation.
348    /// ```
349    /// # use casper_types::ApiError;
350    /// assert_eq!(ApiError::from(40), ApiError::NonRepresentableSerialization);
351    /// ```
352    NonRepresentableSerialization,
353    /// Error specific to Auction contract. See
354    /// [casper_types::system::auction::Error](crate::system::auction::Error).
355    /// ```
356    /// # use casper_types::ApiError;
357    /// for code in 64512..=64767 {
358    ///     assert!(matches!(ApiError::from(code), ApiError::AuctionError(_auction_error)));
359    /// }
360    /// ```
361    AuctionError(u8),
362    /// Contract header errors. See [casper_types::contracts::Error](crate::contracts::Error).
363    ///
364    /// ```
365    /// # use casper_types::ApiError;
366    /// for code in 64768..=65023 {
367    ///     assert!(matches!(ApiError::from(code), ApiError::ContractHeader(_contract_header_error)));
368    /// }
369    /// ```
370    ContractHeader(u8),
371    /// Error specific to Mint contract. See
372    /// [casper_types::system::mint::Error](crate::system::mint::Error).
373    /// ```
374    /// # use casper_types::ApiError;
375    /// for code in 65024..=65279 {
376    ///     assert!(matches!(ApiError::from(code), ApiError::Mint(_mint_error)));
377    /// }
378    /// ```
379    Mint(u8),
380    /// Error specific to Handle Payment contract. See
381    /// [casper_types::system::handle_payment](crate::system::handle_payment::Error).
382    /// ```
383    /// # use casper_types::ApiError;
384    /// for code in 65280..=65535 {
385    ///     assert!(matches!(ApiError::from(code), ApiError::HandlePayment(_handle_payment_error)));
386    /// }
387    /// ```
388    HandlePayment(u8),
389    /// User-specified error code.  The internal `u16` value is added to `u16::MAX as u32 + 1` when
390    /// an `Error::User` is converted to a `u32`.
391    /// ```
392    /// # use casper_types::ApiError;
393    /// for code in 65536..131071 {
394    ///     assert!(matches!(ApiError::from(code), ApiError::User(_)));
395    /// }
396    /// ```
397    User(u16),
398}
399
400impl From<bytesrepr::Error> for ApiError {
401    fn from(error: bytesrepr::Error) -> Self {
402        match error {
403            bytesrepr::Error::EarlyEndOfStream => ApiError::EarlyEndOfStream,
404            bytesrepr::Error::Formatting => ApiError::Formatting,
405            bytesrepr::Error::LeftOverBytes => ApiError::LeftOverBytes,
406            bytesrepr::Error::OutOfMemory => ApiError::OutOfMemory,
407            bytesrepr::Error::NotRepresentable => ApiError::NonRepresentableSerialization,
408            bytesrepr::Error::ExceededRecursionDepth => ApiError::ExceededRecursionDepth,
409        }
410    }
411}
412
413impl From<AddKeyFailure> for ApiError {
414    fn from(error: AddKeyFailure) -> Self {
415        match error {
416            AddKeyFailure::MaxKeysLimit => ApiError::MaxKeysLimit,
417            AddKeyFailure::DuplicateKey => ApiError::DuplicateKey,
418            AddKeyFailure::PermissionDenied => ApiError::PermissionDenied,
419        }
420    }
421}
422
423impl From<UpdateKeyFailure> for ApiError {
424    fn from(error: UpdateKeyFailure) -> Self {
425        match error {
426            UpdateKeyFailure::MissingKey => ApiError::MissingKey,
427            UpdateKeyFailure::PermissionDenied => ApiError::PermissionDenied,
428            UpdateKeyFailure::ThresholdViolation => ApiError::ThresholdViolation,
429        }
430    }
431}
432
433impl From<RemoveKeyFailure> for ApiError {
434    fn from(error: RemoveKeyFailure) -> Self {
435        match error {
436            RemoveKeyFailure::MissingKey => ApiError::MissingKey,
437            RemoveKeyFailure::PermissionDenied => ApiError::PermissionDenied,
438            RemoveKeyFailure::ThresholdViolation => ApiError::ThresholdViolation,
439        }
440    }
441}
442
443impl From<SetThresholdFailure> for ApiError {
444    fn from(error: SetThresholdFailure) -> Self {
445        match error {
446            SetThresholdFailure::KeyManagementThreshold => ApiError::KeyManagementThreshold,
447            SetThresholdFailure::DeploymentThreshold => ApiError::DeploymentThreshold,
448            SetThresholdFailure::PermissionDeniedError => ApiError::PermissionDenied,
449            SetThresholdFailure::InsufficientTotalWeight => ApiError::InsufficientTotalWeight,
450        }
451    }
452}
453
454impl From<CLValueError> for ApiError {
455    fn from(error: CLValueError) -> Self {
456        match error {
457            CLValueError::Serialization(bytesrepr_error) => bytesrepr_error.into(),
458            CLValueError::Type(_) => ApiError::CLTypeMismatch,
459        }
460    }
461}
462
463impl From<contracts::Error> for ApiError {
464    fn from(error: contracts::Error) -> Self {
465        ApiError::ContractHeader(error as u8)
466    }
467}
468
469impl From<auction::Error> for ApiError {
470    fn from(error: auction::Error) -> Self {
471        ApiError::AuctionError(error as u8)
472    }
473}
474
475// This conversion is not intended to be used by third party crates.
476#[doc(hidden)]
477impl From<TryFromIntError> for ApiError {
478    fn from(_error: TryFromIntError) -> Self {
479        ApiError::Unhandled
480    }
481}
482
483impl From<TryFromSliceForAccountHashError> for ApiError {
484    fn from(_error: TryFromSliceForAccountHashError) -> Self {
485        ApiError::Deserialize
486    }
487}
488
489impl From<mint::Error> for ApiError {
490    fn from(error: mint::Error) -> Self {
491        ApiError::Mint(error as u8)
492    }
493}
494
495impl From<handle_payment::Error> for ApiError {
496    fn from(error: handle_payment::Error) -> Self {
497        ApiError::HandlePayment(error as u8)
498    }
499}
500
501impl From<ApiError> for u32 {
502    fn from(error: ApiError) -> Self {
503        match error {
504            ApiError::None => 1,
505            ApiError::MissingArgument => 2,
506            ApiError::InvalidArgument => 3,
507            ApiError::Deserialize => 4,
508            ApiError::Read => 5,
509            ApiError::ValueNotFound => 6,
510            ApiError::ContractNotFound => 7,
511            ApiError::GetKey => 8,
512            ApiError::UnexpectedKeyVariant => 9,
513            ApiError::UnexpectedContractRefVariant => 10,
514            ApiError::InvalidPurseName => 11,
515            ApiError::InvalidPurse => 12,
516            ApiError::UpgradeContractAtURef => 13,
517            ApiError::Transfer => 14,
518            ApiError::NoAccessRights => 15,
519            ApiError::CLTypeMismatch => 16,
520            ApiError::EarlyEndOfStream => 17,
521            ApiError::Formatting => 18,
522            ApiError::LeftOverBytes => 19,
523            ApiError::OutOfMemory => 20,
524            ApiError::MaxKeysLimit => 21,
525            ApiError::DuplicateKey => 22,
526            ApiError::PermissionDenied => 23,
527            ApiError::MissingKey => 24,
528            ApiError::ThresholdViolation => 25,
529            ApiError::KeyManagementThreshold => 26,
530            ApiError::DeploymentThreshold => 27,
531            ApiError::InsufficientTotalWeight => 28,
532            ApiError::InvalidSystemContract => 29,
533            ApiError::PurseNotCreated => 30,
534            ApiError::Unhandled => 31,
535            ApiError::BufferTooSmall => 32,
536            ApiError::HostBufferEmpty => 33,
537            ApiError::HostBufferFull => 34,
538            ApiError::AllocLayout => 35,
539            ApiError::DictionaryItemKeyExceedsLength => 36,
540            ApiError::InvalidDictionaryItemKey => 37,
541            ApiError::MissingSystemContractHash => 38,
542            ApiError::ExceededRecursionDepth => 39,
543            ApiError::NonRepresentableSerialization => 40,
544            ApiError::AuctionError(value) => AUCTION_ERROR_OFFSET + u32::from(value),
545            ApiError::ContractHeader(value) => HEADER_ERROR_OFFSET + u32::from(value),
546            ApiError::Mint(value) => MINT_ERROR_OFFSET + u32::from(value),
547            ApiError::HandlePayment(value) => POS_ERROR_OFFSET + u32::from(value),
548            ApiError::User(value) => RESERVED_ERROR_MAX + 1 + u32::from(value),
549        }
550    }
551}
552
553impl From<u32> for ApiError {
554    fn from(value: u32) -> ApiError {
555        match value {
556            1 => ApiError::None,
557            2 => ApiError::MissingArgument,
558            3 => ApiError::InvalidArgument,
559            4 => ApiError::Deserialize,
560            5 => ApiError::Read,
561            6 => ApiError::ValueNotFound,
562            7 => ApiError::ContractNotFound,
563            8 => ApiError::GetKey,
564            9 => ApiError::UnexpectedKeyVariant,
565            10 => ApiError::UnexpectedContractRefVariant,
566            11 => ApiError::InvalidPurseName,
567            12 => ApiError::InvalidPurse,
568            13 => ApiError::UpgradeContractAtURef,
569            14 => ApiError::Transfer,
570            15 => ApiError::NoAccessRights,
571            16 => ApiError::CLTypeMismatch,
572            17 => ApiError::EarlyEndOfStream,
573            18 => ApiError::Formatting,
574            19 => ApiError::LeftOverBytes,
575            20 => ApiError::OutOfMemory,
576            21 => ApiError::MaxKeysLimit,
577            22 => ApiError::DuplicateKey,
578            23 => ApiError::PermissionDenied,
579            24 => ApiError::MissingKey,
580            25 => ApiError::ThresholdViolation,
581            26 => ApiError::KeyManagementThreshold,
582            27 => ApiError::DeploymentThreshold,
583            28 => ApiError::InsufficientTotalWeight,
584            29 => ApiError::InvalidSystemContract,
585            30 => ApiError::PurseNotCreated,
586            31 => ApiError::Unhandled,
587            32 => ApiError::BufferTooSmall,
588            33 => ApiError::HostBufferEmpty,
589            34 => ApiError::HostBufferFull,
590            35 => ApiError::AllocLayout,
591            36 => ApiError::DictionaryItemKeyExceedsLength,
592            37 => ApiError::InvalidDictionaryItemKey,
593            38 => ApiError::MissingSystemContractHash,
594            39 => ApiError::ExceededRecursionDepth,
595            40 => ApiError::NonRepresentableSerialization,
596            USER_ERROR_MIN..=USER_ERROR_MAX => ApiError::User(value as u16),
597            HP_ERROR_MIN..=HP_ERROR_MAX => ApiError::HandlePayment(value as u8),
598            MINT_ERROR_MIN..=MINT_ERROR_MAX => ApiError::Mint(value as u8),
599            HEADER_ERROR_MIN..=HEADER_ERROR_MAX => ApiError::ContractHeader(value as u8),
600            AUCTION_ERROR_MIN..=AUCTION_ERROR_MAX => ApiError::AuctionError(value as u8),
601            _ => ApiError::Unhandled,
602        }
603    }
604}
605
606impl Debug for ApiError {
607    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
608        match self {
609            ApiError::None => write!(f, "ApiError::None")?,
610            ApiError::MissingArgument => write!(f, "ApiError::MissingArgument")?,
611            ApiError::InvalidArgument => write!(f, "ApiError::InvalidArgument")?,
612            ApiError::Deserialize => write!(f, "ApiError::Deserialize")?,
613            ApiError::Read => write!(f, "ApiError::Read")?,
614            ApiError::ValueNotFound => write!(f, "ApiError::ValueNotFound")?,
615            ApiError::ContractNotFound => write!(f, "ApiError::ContractNotFound")?,
616            ApiError::GetKey => write!(f, "ApiError::GetKey")?,
617            ApiError::UnexpectedKeyVariant => write!(f, "ApiError::UnexpectedKeyVariant")?,
618            ApiError::UnexpectedContractRefVariant => {
619                write!(f, "ApiError::UnexpectedContractRefVariant")?
620            }
621            ApiError::InvalidPurseName => write!(f, "ApiError::InvalidPurseName")?,
622            ApiError::InvalidPurse => write!(f, "ApiError::InvalidPurse")?,
623            ApiError::UpgradeContractAtURef => write!(f, "ApiError::UpgradeContractAtURef")?,
624            ApiError::Transfer => write!(f, "ApiError::Transfer")?,
625            ApiError::NoAccessRights => write!(f, "ApiError::NoAccessRights")?,
626            ApiError::CLTypeMismatch => write!(f, "ApiError::CLTypeMismatch")?,
627            ApiError::EarlyEndOfStream => write!(f, "ApiError::EarlyEndOfStream")?,
628            ApiError::Formatting => write!(f, "ApiError::Formatting")?,
629            ApiError::LeftOverBytes => write!(f, "ApiError::LeftOverBytes")?,
630            ApiError::OutOfMemory => write!(f, "ApiError::OutOfMemory")?,
631            ApiError::MaxKeysLimit => write!(f, "ApiError::MaxKeysLimit")?,
632            ApiError::DuplicateKey => write!(f, "ApiError::DuplicateKey")?,
633            ApiError::PermissionDenied => write!(f, "ApiError::PermissionDenied")?,
634            ApiError::MissingKey => write!(f, "ApiError::MissingKey")?,
635            ApiError::ThresholdViolation => write!(f, "ApiError::ThresholdViolation")?,
636            ApiError::KeyManagementThreshold => write!(f, "ApiError::KeyManagementThreshold")?,
637            ApiError::DeploymentThreshold => write!(f, "ApiError::DeploymentThreshold")?,
638            ApiError::InsufficientTotalWeight => write!(f, "ApiError::InsufficientTotalWeight")?,
639            ApiError::InvalidSystemContract => write!(f, "ApiError::InvalidSystemContract")?,
640            ApiError::PurseNotCreated => write!(f, "ApiError::PurseNotCreated")?,
641            ApiError::Unhandled => write!(f, "ApiError::Unhandled")?,
642            ApiError::BufferTooSmall => write!(f, "ApiError::BufferTooSmall")?,
643            ApiError::HostBufferEmpty => write!(f, "ApiError::HostBufferEmpty")?,
644            ApiError::HostBufferFull => write!(f, "ApiError::HostBufferFull")?,
645            ApiError::AllocLayout => write!(f, "ApiError::AllocLayout")?,
646            ApiError::DictionaryItemKeyExceedsLength => {
647                write!(f, "ApiError::DictionaryItemKeyTooLarge")?
648            }
649            ApiError::InvalidDictionaryItemKey => write!(f, "ApiError::InvalidDictionaryItemKey")?,
650            ApiError::MissingSystemContractHash => write!(f, "ApiError::MissingContractHash")?,
651            ApiError::NonRepresentableSerialization => {
652                write!(f, "ApiError::NonRepresentableSerialization")?
653            }
654            ApiError::ExceededRecursionDepth => write!(f, "ApiError::ExceededRecursionDepth")?,
655            ApiError::AuctionError(value) => write!(
656                f,
657                "ApiError::AuctionError({:?})",
658                auction::Error::try_from(*value).map_err(|_err| fmt::Error)?
659            )?,
660            ApiError::ContractHeader(value) => write!(
661                f,
662                "ApiError::ContractHeader({:?})",
663                contracts::Error::try_from(*value).map_err(|_err| fmt::Error)?
664            )?,
665            ApiError::Mint(value) => write!(
666                f,
667                "ApiError::Mint({:?})",
668                mint::Error::try_from(*value).map_err(|_err| fmt::Error)?
669            )?,
670            ApiError::HandlePayment(value) => write!(
671                f,
672                "ApiError::HandlePayment({:?})",
673                handle_payment::Error::try_from(*value).map_err(|_err| fmt::Error)?
674            )?,
675            ApiError::User(value) => write!(f, "ApiError::User({})", value)?,
676        }
677        write!(f, " [{}]", u32::from(*self))
678    }
679}
680
681impl fmt::Display for ApiError {
682    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
683        match self {
684            ApiError::User(value) => write!(f, "User error: {}", value),
685            ApiError::ContractHeader(value) => write!(f, "Contract header error: {}", value),
686            ApiError::Mint(value) => write!(f, "Mint error: {}", value),
687            ApiError::HandlePayment(value) => write!(f, "Handle Payment error: {}", value),
688            _ => <Self as Debug>::fmt(self, f),
689        }
690    }
691}
692
693// This function is not intended to be used by third party crates.
694#[doc(hidden)]
695pub fn i32_from<T>(result: Result<(), T>) -> i32
696where
697    ApiError: From<T>,
698{
699    match result {
700        Ok(()) => 0,
701        Err(error) => {
702            let api_error = ApiError::from(error);
703            u32::from(api_error) as i32
704        }
705    }
706}
707
708/// Converts an `i32` to a `Result<(), ApiError>`, where `0` represents `Ok(())`, and all other
709/// inputs are mapped to `Err(ApiError::<variant>)`.  The full list of mappings can be found in the
710/// [docs for `ApiError`](ApiError#mappings).
711pub fn result_from(value: i32) -> Result<(), ApiError> {
712    match value {
713        0 => Ok(()),
714        _ => Err(ApiError::from(value as u32)),
715    }
716}
717
718#[cfg(test)]
719mod tests {
720    use std::{i32, u16, u8};
721
722    use super::*;
723
724    fn round_trip(result: Result<(), ApiError>) {
725        let code = i32_from(result);
726        assert_eq!(result, result_from(code));
727    }
728
729    #[test]
730    fn error_values() {
731        assert_eq!(65_024_u32, u32::from(ApiError::Mint(0))); // MINT_ERROR_OFFSET == 65,024
732        assert_eq!(65_279_u32, u32::from(ApiError::Mint(u8::MAX)));
733        assert_eq!(65_280_u32, u32::from(ApiError::HandlePayment(0))); // POS_ERROR_OFFSET == 65,280
734        assert_eq!(65_535_u32, u32::from(ApiError::HandlePayment(u8::MAX)));
735        assert_eq!(65_536_u32, u32::from(ApiError::User(0))); // u16::MAX + 1
736        assert_eq!(131_071_u32, u32::from(ApiError::User(u16::MAX))); // 2 * u16::MAX + 1
737    }
738
739    #[test]
740    fn error_descriptions_getkey() {
741        assert_eq!("ApiError::GetKey [8]", &format!("{:?}", ApiError::GetKey));
742        assert_eq!("ApiError::GetKey [8]", &format!("{}", ApiError::GetKey));
743    }
744
745    #[test]
746    fn error_descriptions_contract_header() {
747        assert_eq!(
748            "ApiError::ContractHeader(PreviouslyUsedVersion) [64769]",
749            &format!(
750                "{:?}",
751                ApiError::ContractHeader(contracts::Error::PreviouslyUsedVersion as u8)
752            )
753        );
754        assert_eq!(
755            "Contract header error: 0",
756            &format!("{}", ApiError::ContractHeader(0))
757        );
758        assert_eq!(
759            "Contract header error: 255",
760            &format!("{}", ApiError::ContractHeader(u8::MAX))
761        );
762    }
763
764    #[test]
765    fn error_descriptions_mint() {
766        assert_eq!(
767            "ApiError::Mint(InsufficientFunds) [65024]",
768            &format!("{:?}", ApiError::Mint(0))
769        );
770        assert_eq!("Mint error: 0", &format!("{}", ApiError::Mint(0)));
771        assert_eq!("Mint error: 255", &format!("{}", ApiError::Mint(u8::MAX)));
772    }
773
774    #[test]
775    fn error_descriptions_handle_payment() {
776        assert_eq!(
777            "ApiError::HandlePayment(NotBonded) [65280]",
778            &format!(
779                "{:?}",
780                ApiError::HandlePayment(handle_payment::Error::NotBonded as u8)
781            )
782        );
783    }
784    #[test]
785    fn error_descriptions_handle_payment_display() {
786        assert_eq!(
787            "Handle Payment error: 0",
788            &format!(
789                "{}",
790                ApiError::HandlePayment(handle_payment::Error::NotBonded as u8)
791            )
792        );
793    }
794
795    #[test]
796    fn error_descriptions_user_errors() {
797        assert_eq!(
798            "ApiError::User(0) [65536]",
799            &format!("{:?}", ApiError::User(0))
800        );
801
802        assert_eq!("User error: 0", &format!("{}", ApiError::User(0)));
803        assert_eq!(
804            "ApiError::User(65535) [131071]",
805            &format!("{:?}", ApiError::User(u16::MAX))
806        );
807        assert_eq!(
808            "User error: 65535",
809            &format!("{}", ApiError::User(u16::MAX))
810        );
811    }
812
813    #[test]
814    fn error_edge_cases() {
815        assert_eq!(Err(ApiError::Unhandled), result_from(i32::MAX));
816        assert_eq!(
817            Err(ApiError::ContractHeader(255)),
818            result_from(MINT_ERROR_OFFSET as i32 - 1)
819        );
820        assert_eq!(Err(ApiError::Unhandled), result_from(-1));
821        assert_eq!(Err(ApiError::Unhandled), result_from(i32::MIN));
822    }
823
824    #[test]
825    fn error_round_trips() {
826        round_trip(Ok(()));
827        round_trip(Err(ApiError::None));
828        round_trip(Err(ApiError::MissingArgument));
829        round_trip(Err(ApiError::InvalidArgument));
830        round_trip(Err(ApiError::Deserialize));
831        round_trip(Err(ApiError::Read));
832        round_trip(Err(ApiError::ValueNotFound));
833        round_trip(Err(ApiError::ContractNotFound));
834        round_trip(Err(ApiError::GetKey));
835        round_trip(Err(ApiError::UnexpectedKeyVariant));
836        round_trip(Err(ApiError::UnexpectedContractRefVariant));
837        round_trip(Err(ApiError::InvalidPurseName));
838        round_trip(Err(ApiError::InvalidPurse));
839        round_trip(Err(ApiError::UpgradeContractAtURef));
840        round_trip(Err(ApiError::Transfer));
841        round_trip(Err(ApiError::NoAccessRights));
842        round_trip(Err(ApiError::CLTypeMismatch));
843        round_trip(Err(ApiError::EarlyEndOfStream));
844        round_trip(Err(ApiError::Formatting));
845        round_trip(Err(ApiError::LeftOverBytes));
846        round_trip(Err(ApiError::OutOfMemory));
847        round_trip(Err(ApiError::MaxKeysLimit));
848        round_trip(Err(ApiError::DuplicateKey));
849        round_trip(Err(ApiError::PermissionDenied));
850        round_trip(Err(ApiError::MissingKey));
851        round_trip(Err(ApiError::ThresholdViolation));
852        round_trip(Err(ApiError::KeyManagementThreshold));
853        round_trip(Err(ApiError::DeploymentThreshold));
854        round_trip(Err(ApiError::InsufficientTotalWeight));
855        round_trip(Err(ApiError::InvalidSystemContract));
856        round_trip(Err(ApiError::PurseNotCreated));
857        round_trip(Err(ApiError::Unhandled));
858        round_trip(Err(ApiError::BufferTooSmall));
859        round_trip(Err(ApiError::HostBufferEmpty));
860        round_trip(Err(ApiError::HostBufferFull));
861        round_trip(Err(ApiError::AllocLayout));
862        round_trip(Err(ApiError::NonRepresentableSerialization));
863        round_trip(Err(ApiError::ContractHeader(0)));
864        round_trip(Err(ApiError::ContractHeader(u8::MAX)));
865        round_trip(Err(ApiError::Mint(0)));
866        round_trip(Err(ApiError::Mint(u8::MAX)));
867        round_trip(Err(ApiError::HandlePayment(0)));
868        round_trip(Err(ApiError::HandlePayment(u8::MAX)));
869        round_trip(Err(ApiError::User(0)));
870        round_trip(Err(ApiError::User(u16::MAX)));
871        round_trip(Err(ApiError::AuctionError(0)));
872        round_trip(Err(ApiError::AuctionError(u8::MAX)));
873    }
874}