odra_types/
error.rs

1use core::any::Any;
2
3use alloc::{boxed::Box, string::String};
4
5use crate::arithmetic::ArithmeticsError;
6
7const MAX_USER_ERROR: u16 = 32767;
8const USER_ERROR_TOO_HIGH: u16 = 32768;
9const UNWRAP_ERROR: u16 = 1;
10
11const CODE_ADDITION_OVERFLOW: u16 = 100;
12const CODE_SUBTRACTION_OVERFLOW: u16 = 101;
13const CODE_NON_PAYABLE: u16 = 102;
14const CODE_TRANSFER_TO_CONTRACT: u16 = 103;
15const CODE_REENTRANT_CALL: u16 = 104;
16const CODE_CONTRACT_ALREADY_INSTALLED: u16 = 105;
17const CODE_UNKNOWN_CONSTRUCTOR: u16 = 106;
18const CODE_NATIVE_TRANSFER_ERROR: u16 = 107;
19const CODE_INDEX_OUT_OF_BOUNDS: u16 = 108;
20const CODE_ZERO_ADDRESS: u16 = 109;
21const CODE_ADDRESS_CREATION_FAILED: u16 = 110;
22const CODE_SERIALIZATION_FAILED: u16 = 111;
23
24/// General error type in Odra framework
25#[derive(Clone, Debug, PartialEq)]
26pub enum OdraError {
27    /// An error that can occur during smart contract execution
28    ExecutionError(ExecutionError),
29    /// An internal virtual machine error
30    VmError(VmError)
31}
32
33impl From<ArithmeticsError> for ExecutionError {
34    fn from(error: ArithmeticsError) -> Self {
35        match error {
36            ArithmeticsError::AdditionOverflow => Self::addition_overflow(),
37            ArithmeticsError::SubtractingOverflow => Self::subtraction_overflow()
38        }
39    }
40}
41
42impl From<ArithmeticsError> for OdraError {
43    fn from(error: ArithmeticsError) -> Self {
44        Into::<ExecutionError>::into(error).into()
45    }
46}
47
48impl From<Box<dyn Any + Send>> for OdraError {
49    fn from(_: Box<dyn Any + Send>) -> Self {
50        OdraError::VmError(VmError::Panic)
51    }
52}
53
54impl From<casper_types::bytesrepr::Error> for ExecutionError {
55    fn from(value: casper_types::bytesrepr::Error) -> Self {
56        Self::sys(
57            CODE_SERIALIZATION_FAILED,
58            match value {
59                casper_types::bytesrepr::Error::EarlyEndOfStream => "Early end of stream",
60                casper_types::bytesrepr::Error::Formatting => "Formatting",
61                casper_types::bytesrepr::Error::LeftOverBytes => "Leftover bytes",
62                casper_types::bytesrepr::Error::OutOfMemory => "Out of memory",
63                casper_types::bytesrepr::Error::NotRepresentable => "Not representable",
64                casper_types::bytesrepr::Error::ExceededRecursionDepth => {
65                    "Exceeded recursion depth"
66                }
67                _ => "Serialization failed"
68            }
69        )
70    }
71}
72
73/// An error that can occur during smart contract execution
74///
75/// It is represented by an error code and a human-readable message.
76///
77/// Errors codes 0..32767 are available for the user to define custom error
78/// in smart contracts.
79/// 32768 code is a special code representing a violation of the custom error code space.
80///
81/// The rest of codes 32769..[u16::MAX](u16::MAX), are used internally by the framework.
82#[derive(Clone, Debug)]
83pub struct ExecutionError(u16, String);
84
85impl ExecutionError {
86    /// Creates an instance with specified code and message.
87    ///
88    /// If the custom error code space is violated, an error with code 32768 is returned.
89    pub fn new(code: u16, msg: &str) -> Self {
90        if code > MAX_USER_ERROR {
91            Self(
92                USER_ERROR_TOO_HIGH,
93                String::from("User error too high. The code should be in range 0..32767.")
94            )
95        } else {
96            Self(code, String::from(msg))
97        }
98    }
99
100    /// Creates an instance of a system error.
101    fn sys(code: u16, msg: &str) -> Self {
102        ExecutionError(code + USER_ERROR_TOO_HIGH, String::from(msg))
103    }
104
105    /// Return the underlying error code
106    pub fn code(&self) -> u16 {
107        self.0
108    }
109
110    /// Creates a specific type of error, meaning that value unwrapping failed.
111    pub fn unwrap_error() -> Self {
112        Self::sys(UNWRAP_ERROR, "Unwrap error")
113    }
114
115    pub fn non_payable() -> Self {
116        Self::sys(CODE_NON_PAYABLE, "Method does not accept deposit")
117    }
118
119    pub fn can_not_transfer_to_contract() -> Self {
120        Self::sys(
121            CODE_TRANSFER_TO_CONTRACT,
122            "Can't transfer tokens to contract."
123        )
124    }
125
126    pub fn reentrant_call() -> Self {
127        Self::sys(CODE_REENTRANT_CALL, "Reentrant call.")
128    }
129
130    pub fn contract_already_installed() -> Self {
131        Self::sys(
132            CODE_CONTRACT_ALREADY_INSTALLED,
133            "Contract already installed."
134        )
135    }
136
137    pub fn unknown_constructor() -> Self {
138        Self::sys(CODE_UNKNOWN_CONSTRUCTOR, "Unknown constructor.")
139    }
140
141    pub fn native_token_transfer_error() -> Self {
142        Self::sys(CODE_NATIVE_TRANSFER_ERROR, "Native token transfer error.")
143    }
144
145    pub fn addition_overflow() -> Self {
146        Self::sys(CODE_ADDITION_OVERFLOW, "Addition overflow")
147    }
148
149    pub fn subtraction_overflow() -> Self {
150        Self::sys(CODE_SUBTRACTION_OVERFLOW, "Subtraction overflow")
151    }
152
153    pub fn index_out_of_bounds() -> Self {
154        Self::sys(CODE_INDEX_OUT_OF_BOUNDS, "Index out of bounds")
155    }
156
157    pub fn zero_address() -> Self {
158        Self::sys(CODE_ZERO_ADDRESS, "Zero address")
159    }
160
161    pub fn address_creation_failed() -> Self {
162        Self::sys(CODE_ADDRESS_CREATION_FAILED, "Address creation failed")
163    }
164}
165
166impl PartialEq for ExecutionError {
167    fn eq(&self, other: &Self) -> bool {
168        self.0 == other.0
169    }
170}
171
172impl From<ExecutionError> for OdraError {
173    fn from(error: ExecutionError) -> Self {
174        Self::ExecutionError(error)
175    }
176}
177
178/// An internal virtual machine error
179#[derive(Clone, Debug, PartialEq, Eq)]
180pub enum VmError {
181    /// Failed to serialize a value.
182    Serialization,
183    /// Failed to deserialize a value.
184    Deserialization,
185    /// Exceeded the account balance
186    BalanceExceeded,
187    /// Non existing host entrypoint was called.
188    NoSuchMethod(String),
189    /// Accessing a contract with an invalid address.
190    InvalidContractAddress,
191    /// Error calling a host function in a wrong context.
192    InvalidContext,
193    /// Calling a contract with missing entrypoint arguments.
194    MissingArg,
195    /// Non-specified error with a custom message.
196    Other(String),
197    /// Unspecified error.
198    Panic
199}
200
201/// Error that can occur while operating on a collection.
202pub enum CollectionError {
203    // The requested index is bigger than the max collection index.
204    IndexOutOfBounds
205}
206
207impl From<CollectionError> for ExecutionError {
208    fn from(error: CollectionError) -> Self {
209        match error {
210            CollectionError::IndexOutOfBounds => Self::index_out_of_bounds()
211        }
212    }
213}
214
215impl From<CollectionError> for OdraError {
216    fn from(error: CollectionError) -> Self {
217        Into::<ExecutionError>::into(error).into()
218    }
219}
220
221/// Error that can occur while operating on an Address.
222#[derive(Clone, Debug, PartialEq)]
223pub enum AddressError {
224    /// Tried to construct a zero address.
225    ZeroAddress,
226    /// Tried to construct an address and failed.
227    AddressCreationError
228}
229
230impl From<AddressError> for ExecutionError {
231    fn from(error: AddressError) -> Self {
232        match error {
233            AddressError::ZeroAddress => Self::zero_address(),
234            AddressError::AddressCreationError => Self::address_creation_failed()
235        }
236    }
237}
238
239impl From<AddressError> for OdraError {
240    fn from(error: AddressError) -> Self {
241        Into::<ExecutionError>::into(error).into()
242    }
243}