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, UpdateKeyFailure,
11    },
12    addressable_entity::{self, MessageTopicError, TryFromSliceForAccountHashError},
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    /// Unsupported contract discovery variant.
161    /// ```
162    /// # use casper_types::ApiError;
163    /// assert_eq!(ApiError::from(10), ApiError::UnexpectedContractRefVariant);
164    /// ```
165    UnexpectedContractRefVariant,
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`](addressable_entity::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::SystemEntityType).
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
363    /// [casper_types::contracts::Error](crate::addressable_entity::Error).
364    ///
365    /// ```
366    /// # use casper_types::ApiError;
367    /// for code in 64768..=65023 {
368    ///     assert!(matches!(ApiError::from(code), ApiError::ContractHeader(_contract_header_error)));
369    /// }
370    /// ```
371    ContractHeader(u8),
372    /// Error specific to Mint contract. See
373    /// [casper_types::system::mint::Error](crate::system::mint::Error).
374    /// ```
375    /// # use casper_types::ApiError;
376    /// for code in 65024..=65279 {
377    ///     assert!(matches!(ApiError::from(code), ApiError::Mint(_mint_error)));
378    /// }
379    /// ```
380    Mint(u8),
381    /// Error specific to Handle Payment contract. See
382    /// [casper_types::system::handle_payment](crate::system::handle_payment::Error).
383    /// ```
384    /// # use casper_types::ApiError;
385    /// for code in 65280..=65535 {
386    ///     assert!(matches!(ApiError::from(code), ApiError::HandlePayment(_handle_payment_error)));
387    /// }
388    /// ```
389    HandlePayment(u8),
390    /// User-specified error code.  The internal `u16` value is added to `u16::MAX as u32 + 1` when
391    /// an `Error::User` is converted to a `u32`.
392    /// ```
393    /// # use casper_types::ApiError;
394    /// for code in 65536..131071 {
395    ///     assert!(matches!(ApiError::from(code), ApiError::User(_)));
396    /// }
397    /// ```
398    User(u16),
399    /// The message topic is already registered.
400    /// ```
401    /// # use casper_types::ApiError;
402    /// assert_eq!(ApiError::from(41), ApiError::MessageTopicAlreadyRegistered);
403    /// ```
404    MessageTopicAlreadyRegistered,
405    /// The maximum number of allowed message topics was exceeded.
406    /// ```
407    /// # use casper_types::ApiError;
408    /// assert_eq!(ApiError::from(42), ApiError::MaxTopicsNumberExceeded);
409    /// ```
410    MaxTopicsNumberExceeded,
411    /// The maximum size for the topic name was exceeded.
412    /// ```
413    /// # use casper_types::ApiError;
414    /// assert_eq!(ApiError::from(43), ApiError::MaxTopicNameSizeExceeded);
415    /// ```
416    MaxTopicNameSizeExceeded,
417    /// The message topic is not registered.
418    /// ```
419    /// # use casper_types::ApiError;
420    /// assert_eq!(ApiError::from(44), ApiError::MessageTopicNotRegistered);
421    /// ```
422    MessageTopicNotRegistered,
423    /// The message topic is full and cannot accept new messages.
424    /// ```
425    /// # use casper_types::ApiError;
426    /// assert_eq!(ApiError::from(45), ApiError::MessageTopicFull);
427    /// ```
428    MessageTopicFull,
429    /// The message topic is full and cannot accept new messages.
430    /// ```
431    /// # use casper_types::ApiError;
432    /// assert_eq!(ApiError::from(46), ApiError::MessageTooLarge);
433    /// ```
434    MessageTooLarge,
435    /// The maximum number of messages emitted per block was exceeded when trying to emit a
436    /// message.
437    /// ```
438    /// # use casper_types::ApiError;
439    /// assert_eq!(ApiError::from(47), ApiError::MaxMessagesPerBlockExceeded);
440    /// ```
441    MaxMessagesPerBlockExceeded,
442    /// Attempt to call FFI function `casper_add_contract_version()` from a transaction not defined
443    /// as an installer/upgrader.
444    /// ```
445    /// # use casper_types::ApiError;
446    /// assert_eq!(ApiError::from(48), ApiError::NotAllowedToAddContractVersion);
447    /// ```
448    NotAllowedToAddContractVersion,
449    /// Invalid delegation amount limits.
450    /// ```
451    /// # use casper_types::ApiError;
452    /// assert_eq!(ApiError::from(49), ApiError::InvalidDelegationAmountLimits);
453    /// ```
454    InvalidDelegationAmountLimits,
455    /// Invalid action for caller information.
456    /// ```
457    /// # use casper_types::ApiError;
458    /// assert_eq!(ApiError::from(50), ApiError::InvalidCallerInfoRequest);
459    /// ```
460    InvalidCallerInfoRequest,
461}
462
463impl From<bytesrepr::Error> for ApiError {
464    fn from(error: bytesrepr::Error) -> Self {
465        match error {
466            bytesrepr::Error::EarlyEndOfStream => ApiError::EarlyEndOfStream,
467            bytesrepr::Error::Formatting => ApiError::Formatting,
468            bytesrepr::Error::LeftOverBytes => ApiError::LeftOverBytes,
469            bytesrepr::Error::OutOfMemory => ApiError::OutOfMemory,
470            bytesrepr::Error::NotRepresentable => ApiError::NonRepresentableSerialization,
471            bytesrepr::Error::ExceededRecursionDepth => ApiError::ExceededRecursionDepth,
472        }
473    }
474}
475
476impl From<AddKeyFailure> for ApiError {
477    fn from(error: AddKeyFailure) -> Self {
478        match error {
479            AddKeyFailure::MaxKeysLimit => ApiError::MaxKeysLimit,
480            AddKeyFailure::DuplicateKey => ApiError::DuplicateKey,
481            AddKeyFailure::PermissionDenied => ApiError::PermissionDenied,
482        }
483    }
484}
485
486impl From<UpdateKeyFailure> for ApiError {
487    fn from(error: UpdateKeyFailure) -> Self {
488        match error {
489            UpdateKeyFailure::MissingKey => ApiError::MissingKey,
490            UpdateKeyFailure::PermissionDenied => ApiError::PermissionDenied,
491            UpdateKeyFailure::ThresholdViolation => ApiError::ThresholdViolation,
492        }
493    }
494}
495
496impl From<RemoveKeyFailure> for ApiError {
497    fn from(error: RemoveKeyFailure) -> Self {
498        match error {
499            RemoveKeyFailure::MissingKey => ApiError::MissingKey,
500            RemoveKeyFailure::PermissionDenied => ApiError::PermissionDenied,
501            RemoveKeyFailure::ThresholdViolation => ApiError::ThresholdViolation,
502        }
503    }
504}
505
506impl From<SetThresholdFailure> for ApiError {
507    fn from(error: SetThresholdFailure) -> Self {
508        match error {
509            SetThresholdFailure::KeyManagementThreshold => ApiError::KeyManagementThreshold,
510            SetThresholdFailure::DeploymentThreshold => ApiError::DeploymentThreshold,
511            SetThresholdFailure::PermissionDeniedError => ApiError::PermissionDenied,
512            SetThresholdFailure::InsufficientTotalWeight => ApiError::InsufficientTotalWeight,
513        }
514    }
515}
516
517impl From<CLValueError> for ApiError {
518    fn from(error: CLValueError) -> Self {
519        match error {
520            CLValueError::Serialization(bytesrepr_error) => bytesrepr_error.into(),
521            CLValueError::Type(_) => ApiError::CLTypeMismatch,
522        }
523    }
524}
525
526impl From<addressable_entity::Error> for ApiError {
527    fn from(error: addressable_entity::Error) -> Self {
528        ApiError::ContractHeader(error as u8)
529    }
530}
531
532impl From<contracts::Error> for ApiError {
533    fn from(error: contracts::Error) -> Self {
534        ApiError::ContractHeader(error as u8)
535    }
536}
537
538impl From<auction::Error> for ApiError {
539    fn from(error: auction::Error) -> Self {
540        ApiError::AuctionError(error as u8)
541    }
542}
543
544// This conversion is not intended to be used by third party crates.
545#[doc(hidden)]
546impl From<TryFromIntError> for ApiError {
547    fn from(_error: TryFromIntError) -> Self {
548        ApiError::Unhandled
549    }
550}
551
552impl From<TryFromSliceForAccountHashError> for ApiError {
553    fn from(_error: TryFromSliceForAccountHashError) -> Self {
554        ApiError::Deserialize
555    }
556}
557
558impl From<mint::Error> for ApiError {
559    fn from(error: mint::Error) -> Self {
560        ApiError::Mint(error as u8)
561    }
562}
563
564impl From<handle_payment::Error> for ApiError {
565    fn from(error: handle_payment::Error) -> Self {
566        ApiError::HandlePayment(error as u8)
567    }
568}
569
570impl From<MessageTopicError> for ApiError {
571    fn from(error: MessageTopicError) -> Self {
572        match error {
573            MessageTopicError::DuplicateTopic => ApiError::MessageTopicAlreadyRegistered,
574            MessageTopicError::MaxTopicsExceeded => ApiError::MaxTopicsNumberExceeded,
575            MessageTopicError::TopicNameSizeExceeded => ApiError::MaxTopicNameSizeExceeded,
576        }
577    }
578}
579
580impl From<ApiError> for u32 {
581    fn from(error: ApiError) -> Self {
582        match error {
583            ApiError::None => 1,
584            ApiError::MissingArgument => 2,
585            ApiError::InvalidArgument => 3,
586            ApiError::Deserialize => 4,
587            ApiError::Read => 5,
588            ApiError::ValueNotFound => 6,
589            ApiError::ContractNotFound => 7,
590            ApiError::GetKey => 8,
591            ApiError::UnexpectedKeyVariant => 9,
592            ApiError::UnexpectedContractRefVariant => 10,
593            ApiError::InvalidPurseName => 11,
594            ApiError::InvalidPurse => 12,
595            ApiError::UpgradeContractAtURef => 13,
596            ApiError::Transfer => 14,
597            ApiError::NoAccessRights => 15,
598            ApiError::CLTypeMismatch => 16,
599            ApiError::EarlyEndOfStream => 17,
600            ApiError::Formatting => 18,
601            ApiError::LeftOverBytes => 19,
602            ApiError::OutOfMemory => 20,
603            ApiError::MaxKeysLimit => 21,
604            ApiError::DuplicateKey => 22,
605            ApiError::PermissionDenied => 23,
606            ApiError::MissingKey => 24,
607            ApiError::ThresholdViolation => 25,
608            ApiError::KeyManagementThreshold => 26,
609            ApiError::DeploymentThreshold => 27,
610            ApiError::InsufficientTotalWeight => 28,
611            ApiError::InvalidSystemContract => 29,
612            ApiError::PurseNotCreated => 30,
613            ApiError::Unhandled => 31,
614            ApiError::BufferTooSmall => 32,
615            ApiError::HostBufferEmpty => 33,
616            ApiError::HostBufferFull => 34,
617            ApiError::AllocLayout => 35,
618            ApiError::DictionaryItemKeyExceedsLength => 36,
619            ApiError::InvalidDictionaryItemKey => 37,
620            ApiError::MissingSystemContractHash => 38,
621            ApiError::ExceededRecursionDepth => 39,
622            ApiError::NonRepresentableSerialization => 40,
623            ApiError::MessageTopicAlreadyRegistered => 41,
624            ApiError::MaxTopicsNumberExceeded => 42,
625            ApiError::MaxTopicNameSizeExceeded => 43,
626            ApiError::MessageTopicNotRegistered => 44,
627            ApiError::MessageTopicFull => 45,
628            ApiError::MessageTooLarge => 46,
629            ApiError::MaxMessagesPerBlockExceeded => 47,
630            ApiError::NotAllowedToAddContractVersion => 48,
631            ApiError::InvalidDelegationAmountLimits => 49,
632            ApiError::InvalidCallerInfoRequest => 50,
633            ApiError::AuctionError(value) => AUCTION_ERROR_OFFSET + u32::from(value),
634            ApiError::ContractHeader(value) => HEADER_ERROR_OFFSET + u32::from(value),
635            ApiError::Mint(value) => MINT_ERROR_OFFSET + u32::from(value),
636            ApiError::HandlePayment(value) => POS_ERROR_OFFSET + u32::from(value),
637            ApiError::User(value) => RESERVED_ERROR_MAX + 1 + u32::from(value),
638        }
639    }
640}
641
642impl From<u32> for ApiError {
643    fn from(value: u32) -> ApiError {
644        match value {
645            1 => ApiError::None,
646            2 => ApiError::MissingArgument,
647            3 => ApiError::InvalidArgument,
648            4 => ApiError::Deserialize,
649            5 => ApiError::Read,
650            6 => ApiError::ValueNotFound,
651            7 => ApiError::ContractNotFound,
652            8 => ApiError::GetKey,
653            9 => ApiError::UnexpectedKeyVariant,
654            10 => ApiError::UnexpectedContractRefVariant,
655            11 => ApiError::InvalidPurseName,
656            12 => ApiError::InvalidPurse,
657            13 => ApiError::UpgradeContractAtURef,
658            14 => ApiError::Transfer,
659            15 => ApiError::NoAccessRights,
660            16 => ApiError::CLTypeMismatch,
661            17 => ApiError::EarlyEndOfStream,
662            18 => ApiError::Formatting,
663            19 => ApiError::LeftOverBytes,
664            20 => ApiError::OutOfMemory,
665            21 => ApiError::MaxKeysLimit,
666            22 => ApiError::DuplicateKey,
667            23 => ApiError::PermissionDenied,
668            24 => ApiError::MissingKey,
669            25 => ApiError::ThresholdViolation,
670            26 => ApiError::KeyManagementThreshold,
671            27 => ApiError::DeploymentThreshold,
672            28 => ApiError::InsufficientTotalWeight,
673            29 => ApiError::InvalidSystemContract,
674            30 => ApiError::PurseNotCreated,
675            31 => ApiError::Unhandled,
676            32 => ApiError::BufferTooSmall,
677            33 => ApiError::HostBufferEmpty,
678            34 => ApiError::HostBufferFull,
679            35 => ApiError::AllocLayout,
680            36 => ApiError::DictionaryItemKeyExceedsLength,
681            37 => ApiError::InvalidDictionaryItemKey,
682            38 => ApiError::MissingSystemContractHash,
683            39 => ApiError::ExceededRecursionDepth,
684            40 => ApiError::NonRepresentableSerialization,
685            41 => ApiError::MessageTopicAlreadyRegistered,
686            42 => ApiError::MaxTopicsNumberExceeded,
687            43 => ApiError::MaxTopicNameSizeExceeded,
688            44 => ApiError::MessageTopicNotRegistered,
689            45 => ApiError::MessageTopicFull,
690            46 => ApiError::MessageTooLarge,
691            47 => ApiError::MaxMessagesPerBlockExceeded,
692            48 => ApiError::NotAllowedToAddContractVersion,
693            49 => ApiError::InvalidDelegationAmountLimits,
694            50 => ApiError::InvalidCallerInfoRequest,
695            USER_ERROR_MIN..=USER_ERROR_MAX => ApiError::User(value as u16),
696            HP_ERROR_MIN..=HP_ERROR_MAX => ApiError::HandlePayment(value as u8),
697            MINT_ERROR_MIN..=MINT_ERROR_MAX => ApiError::Mint(value as u8),
698            HEADER_ERROR_MIN..=HEADER_ERROR_MAX => ApiError::ContractHeader(value as u8),
699            AUCTION_ERROR_MIN..=AUCTION_ERROR_MAX => ApiError::AuctionError(value as u8),
700            _ => ApiError::Unhandled,
701        }
702    }
703}
704
705impl Debug for ApiError {
706    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
707        match self {
708            ApiError::None => write!(f, "ApiError::None")?,
709            ApiError::MissingArgument => write!(f, "ApiError::MissingArgument")?,
710            ApiError::InvalidArgument => write!(f, "ApiError::InvalidArgument")?,
711            ApiError::Deserialize => write!(f, "ApiError::Deserialize")?,
712            ApiError::Read => write!(f, "ApiError::Read")?,
713            ApiError::ValueNotFound => write!(f, "ApiError::ValueNotFound")?,
714            ApiError::ContractNotFound => write!(f, "ApiError::ContractNotFound")?,
715            ApiError::GetKey => write!(f, "ApiError::GetKey")?,
716            ApiError::UnexpectedKeyVariant => write!(f, "ApiError::UnexpectedKeyVariant")?,
717            ApiError::UnexpectedContractRefVariant => {
718                write!(f, "ApiError::UnexpectedContractRefVariant")?
719            }
720            ApiError::InvalidPurseName => write!(f, "ApiError::InvalidPurseName")?,
721            ApiError::InvalidPurse => write!(f, "ApiError::InvalidPurse")?,
722            ApiError::UpgradeContractAtURef => write!(f, "ApiError::UpgradeContractAtURef")?,
723            ApiError::Transfer => write!(f, "ApiError::Transfer")?,
724            ApiError::NoAccessRights => write!(f, "ApiError::NoAccessRights")?,
725            ApiError::CLTypeMismatch => write!(f, "ApiError::CLTypeMismatch")?,
726            ApiError::EarlyEndOfStream => write!(f, "ApiError::EarlyEndOfStream")?,
727            ApiError::Formatting => write!(f, "ApiError::Formatting")?,
728            ApiError::LeftOverBytes => write!(f, "ApiError::LeftOverBytes")?,
729            ApiError::OutOfMemory => write!(f, "ApiError::OutOfMemory")?,
730            ApiError::MaxKeysLimit => write!(f, "ApiError::MaxKeysLimit")?,
731            ApiError::DuplicateKey => write!(f, "ApiError::DuplicateKey")?,
732            ApiError::PermissionDenied => write!(f, "ApiError::PermissionDenied")?,
733            ApiError::MissingKey => write!(f, "ApiError::MissingKey")?,
734            ApiError::ThresholdViolation => write!(f, "ApiError::ThresholdViolation")?,
735            ApiError::KeyManagementThreshold => write!(f, "ApiError::KeyManagementThreshold")?,
736            ApiError::DeploymentThreshold => write!(f, "ApiError::DeploymentThreshold")?,
737            ApiError::InsufficientTotalWeight => write!(f, "ApiError::InsufficientTotalWeight")?,
738            ApiError::InvalidSystemContract => write!(f, "ApiError::InvalidSystemContract")?,
739            ApiError::PurseNotCreated => write!(f, "ApiError::PurseNotCreated")?,
740            ApiError::Unhandled => write!(f, "ApiError::Unhandled")?,
741            ApiError::BufferTooSmall => write!(f, "ApiError::BufferTooSmall")?,
742            ApiError::HostBufferEmpty => write!(f, "ApiError::HostBufferEmpty")?,
743            ApiError::HostBufferFull => write!(f, "ApiError::HostBufferFull")?,
744            ApiError::AllocLayout => write!(f, "ApiError::AllocLayout")?,
745            ApiError::DictionaryItemKeyExceedsLength => {
746                write!(f, "ApiError::DictionaryItemKeyTooLarge")?
747            }
748            ApiError::InvalidDictionaryItemKey => write!(f, "ApiError::InvalidDictionaryItemKey")?,
749            ApiError::MissingSystemContractHash => write!(f, "ApiError::MissingContractHash")?,
750            ApiError::NonRepresentableSerialization => {
751                write!(f, "ApiError::NonRepresentableSerialization")?
752            }
753            ApiError::MessageTopicAlreadyRegistered => {
754                write!(f, "ApiError::MessageTopicAlreadyRegistered")?
755            }
756            ApiError::MaxTopicsNumberExceeded => write!(f, "ApiError::MaxTopicsNumberExceeded")?,
757            ApiError::MaxTopicNameSizeExceeded => write!(f, "ApiError::MaxTopicNameSizeExceeded")?,
758            ApiError::MessageTopicNotRegistered => {
759                write!(f, "ApiError::MessageTopicNotRegistered")?
760            }
761            ApiError::MessageTopicFull => write!(f, "ApiError::MessageTopicFull")?,
762            ApiError::MessageTooLarge => write!(f, "ApiError::MessageTooLarge")?,
763            ApiError::MaxMessagesPerBlockExceeded => {
764                write!(f, "ApiError::MaxMessagesPerBlockExceeded")?
765            }
766            ApiError::NotAllowedToAddContractVersion => {
767                write!(f, "ApiError::NotAllowedToAddContractVersion")?
768            }
769            ApiError::InvalidDelegationAmountLimits => {
770                write!(f, "ApiError::InvalidDelegationAmountLimits")?
771            }
772            ApiError::InvalidCallerInfoRequest => write!(f, "ApiError::InvalidCallerInfoRequest")?,
773            ApiError::ExceededRecursionDepth => write!(f, "ApiError::ExceededRecursionDepth")?,
774            ApiError::AuctionError(value) => write!(
775                f,
776                "ApiError::AuctionError({:?})",
777                auction::Error::try_from(*value).map_err(|_err| fmt::Error)?
778            )?,
779            ApiError::ContractHeader(value) => write!(
780                f,
781                "ApiError::ContractHeader({:?})",
782                addressable_entity::Error::try_from(*value).map_err(|_err| fmt::Error)?
783            )?,
784            ApiError::Mint(value) => write!(
785                f,
786                "ApiError::Mint({:?})",
787                mint::Error::try_from(*value).map_err(|_err| fmt::Error)?
788            )?,
789            ApiError::HandlePayment(value) => write!(
790                f,
791                "ApiError::HandlePayment({:?})",
792                handle_payment::Error::try_from(*value).map_err(|_err| fmt::Error)?
793            )?,
794            ApiError::User(value) => write!(f, "ApiError::User({})", value)?,
795        }
796        write!(f, " [{}]", u32::from(*self))
797    }
798}
799
800impl fmt::Display for ApiError {
801    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
802        match self {
803            ApiError::User(value) => write!(f, "User error: {}", value),
804            ApiError::ContractHeader(value) => write!(f, "Contract header error: {}", value),
805            ApiError::Mint(value) => write!(f, "Mint error: {}", value),
806            ApiError::HandlePayment(value) => write!(f, "Handle Payment error: {}", value),
807            _ => <Self as Debug>::fmt(self, f),
808        }
809    }
810}
811
812// This function is not intended to be used by third party crates.
813#[doc(hidden)]
814pub fn i32_from<T>(result: Result<(), T>) -> i32
815where
816    ApiError: From<T>,
817{
818    match result {
819        Ok(()) => 0,
820        Err(error) => {
821            let api_error = ApiError::from(error);
822            u32::from(api_error) as i32
823        }
824    }
825}
826
827/// Converts an `i32` to a `Result<(), ApiError>`, where `0` represents `Ok(())`, and all other
828/// inputs are mapped to `Err(ApiError::<variant>)`.  The full list of mappings can be found in the
829/// [docs for `ApiError`](ApiError#mappings).
830pub fn result_from(value: i32) -> Result<(), ApiError> {
831    match value {
832        0 => Ok(()),
833        _ => Err(ApiError::from(value as u32)),
834    }
835}
836
837#[cfg(test)]
838mod tests {
839    use super::*;
840
841    fn round_trip(result: Result<(), ApiError>) {
842        let code = i32_from(result);
843        assert_eq!(result, result_from(code));
844    }
845
846    #[test]
847    fn error_values() {
848        assert_eq!(65_024_u32, u32::from(ApiError::Mint(0))); // MINT_ERROR_OFFSET == 65,024
849        assert_eq!(65_279_u32, u32::from(ApiError::Mint(u8::MAX)));
850        assert_eq!(65_280_u32, u32::from(ApiError::HandlePayment(0))); // POS_ERROR_OFFSET == 65,280
851        assert_eq!(65_535_u32, u32::from(ApiError::HandlePayment(u8::MAX)));
852        assert_eq!(65_536_u32, u32::from(ApiError::User(0))); // u16::MAX + 1
853        assert_eq!(131_071_u32, u32::from(ApiError::User(u16::MAX))); // 2 * u16::MAX + 1
854    }
855
856    #[test]
857    fn error_descriptions_getkey() {
858        assert_eq!("ApiError::GetKey [8]", &format!("{:?}", ApiError::GetKey));
859        assert_eq!("ApiError::GetKey [8]", &format!("{}", ApiError::GetKey));
860    }
861
862    #[test]
863    fn error_descriptions_contract_header() {
864        assert_eq!(
865            "ApiError::ContractHeader(PreviouslyUsedVersion) [64769]",
866            &format!(
867                "{:?}",
868                ApiError::ContractHeader(addressable_entity::Error::PreviouslyUsedVersion as u8)
869            )
870        );
871        assert_eq!(
872            "Contract header error: 0",
873            &format!("{}", ApiError::ContractHeader(0))
874        );
875        assert_eq!(
876            "Contract header error: 255",
877            &format!("{}", ApiError::ContractHeader(u8::MAX))
878        );
879    }
880
881    #[test]
882    fn error_descriptions_mint() {
883        assert_eq!(
884            "ApiError::Mint(InsufficientFunds) [65024]",
885            &format!("{:?}", ApiError::Mint(0))
886        );
887        assert_eq!("Mint error: 0", &format!("{}", ApiError::Mint(0)));
888        assert_eq!("Mint error: 255", &format!("{}", ApiError::Mint(u8::MAX)));
889    }
890
891    #[test]
892    fn error_descriptions_handle_payment() {
893        assert_eq!(
894            "ApiError::HandlePayment(NotBonded) [65280]",
895            &format!(
896                "{:?}",
897                ApiError::HandlePayment(handle_payment::Error::NotBonded as u8)
898            )
899        );
900    }
901
902    #[test]
903    fn error_descriptions_handle_payment_display() {
904        assert_eq!(
905            "Handle Payment error: 0",
906            &format!(
907                "{}",
908                ApiError::HandlePayment(handle_payment::Error::NotBonded as u8)
909            )
910        );
911    }
912
913    #[test]
914    fn error_descriptions_user_errors() {
915        assert_eq!(
916            "ApiError::User(0) [65536]",
917            &format!("{:?}", ApiError::User(0))
918        );
919
920        assert_eq!("User error: 0", &format!("{}", ApiError::User(0)));
921        assert_eq!(
922            "ApiError::User(65535) [131071]",
923            &format!("{:?}", ApiError::User(u16::MAX))
924        );
925        assert_eq!(
926            "User error: 65535",
927            &format!("{}", ApiError::User(u16::MAX))
928        );
929    }
930
931    #[test]
932    fn error_edge_cases() {
933        assert_eq!(Err(ApiError::Unhandled), result_from(i32::MAX));
934        assert_eq!(
935            Err(ApiError::ContractHeader(255)),
936            result_from(MINT_ERROR_OFFSET as i32 - 1)
937        );
938        assert_eq!(Err(ApiError::Unhandled), result_from(-1));
939        assert_eq!(Err(ApiError::Unhandled), result_from(i32::MIN));
940    }
941
942    #[test]
943    fn error_round_trips() {
944        round_trip(Ok(()));
945        round_trip(Err(ApiError::None));
946        round_trip(Err(ApiError::MissingArgument));
947        round_trip(Err(ApiError::InvalidArgument));
948        round_trip(Err(ApiError::Deserialize));
949        round_trip(Err(ApiError::Read));
950        round_trip(Err(ApiError::ValueNotFound));
951        round_trip(Err(ApiError::ContractNotFound));
952        round_trip(Err(ApiError::GetKey));
953        round_trip(Err(ApiError::UnexpectedKeyVariant));
954        round_trip(Err(ApiError::UnexpectedContractRefVariant));
955        round_trip(Err(ApiError::InvalidPurseName));
956        round_trip(Err(ApiError::InvalidPurse));
957        round_trip(Err(ApiError::UpgradeContractAtURef));
958        round_trip(Err(ApiError::Transfer));
959        round_trip(Err(ApiError::NoAccessRights));
960        round_trip(Err(ApiError::CLTypeMismatch));
961        round_trip(Err(ApiError::EarlyEndOfStream));
962        round_trip(Err(ApiError::Formatting));
963        round_trip(Err(ApiError::LeftOverBytes));
964        round_trip(Err(ApiError::OutOfMemory));
965        round_trip(Err(ApiError::MaxKeysLimit));
966        round_trip(Err(ApiError::DuplicateKey));
967        round_trip(Err(ApiError::PermissionDenied));
968        round_trip(Err(ApiError::MissingKey));
969        round_trip(Err(ApiError::ThresholdViolation));
970        round_trip(Err(ApiError::KeyManagementThreshold));
971        round_trip(Err(ApiError::DeploymentThreshold));
972        round_trip(Err(ApiError::InsufficientTotalWeight));
973        round_trip(Err(ApiError::InvalidSystemContract));
974        round_trip(Err(ApiError::PurseNotCreated));
975        round_trip(Err(ApiError::Unhandled));
976        round_trip(Err(ApiError::BufferTooSmall));
977        round_trip(Err(ApiError::HostBufferEmpty));
978        round_trip(Err(ApiError::HostBufferFull));
979        round_trip(Err(ApiError::AllocLayout));
980        round_trip(Err(ApiError::NonRepresentableSerialization));
981        round_trip(Err(ApiError::ContractHeader(0)));
982        round_trip(Err(ApiError::ContractHeader(u8::MAX)));
983        round_trip(Err(ApiError::Mint(0)));
984        round_trip(Err(ApiError::Mint(u8::MAX)));
985        round_trip(Err(ApiError::HandlePayment(0)));
986        round_trip(Err(ApiError::HandlePayment(u8::MAX)));
987        round_trip(Err(ApiError::User(0)));
988        round_trip(Err(ApiError::User(u16::MAX)));
989        round_trip(Err(ApiError::AuctionError(0)));
990        round_trip(Err(ApiError::AuctionError(u8::MAX)));
991        round_trip(Err(ApiError::MessageTopicAlreadyRegistered));
992        round_trip(Err(ApiError::MaxTopicsNumberExceeded));
993        round_trip(Err(ApiError::MaxTopicNameSizeExceeded));
994        round_trip(Err(ApiError::MessageTopicNotRegistered));
995        round_trip(Err(ApiError::MessageTopicFull));
996        round_trip(Err(ApiError::MessageTooLarge));
997        round_trip(Err(ApiError::NotAllowedToAddContractVersion));
998        round_trip(Err(ApiError::InvalidDelegationAmountLimits));
999    }
1000}