xrpl_wasm_stdlib/host/
mod.rs

1//! Host bindings and utilities exposed to WASM smart contracts.
2//!
3//! This module exposes the low-level host ABI plus typed primitives (Result, Error, helpers).
4//! Most users should prefer the safe, high-level APIs in [`crate::core`], which wrap these bindings.
5//!
6//! ## Float Operations for Fungible Tokens (IOUs)
7//!
8//! The host provides float arithmetic functions for XRPL's fungible token amounts.
9//! These operations use rippled's Number class via FFI to ensure exact consensus compatibility:
10//!
11//! - `float_from_int` / `float_from_uint` - Convert integers to float format
12//! - `float_set` - Create float from exponent and mantissa
13//! - `float_add` / `float_subtract` / `float_multiply` / `float_divide` - Arithmetic
14//! - `float_pow` / `float_root` / `float_log` - Mathematical functions
15//! - `float_compare` - Comparison operations
16//!
17//! All operations support explicit rounding modes (0=ToNearest, 1=TowardsZero, 2=Downward, 3=Upward).
18//!
19//! See the host_bindings documentation for detailed function signatures.
20
21pub mod assert;
22pub mod error_codes;
23pub mod field_helpers;
24pub mod trace;
25
26//////////////////////////////////////
27// Host functions (defined by the host)
28//////////////////////////////////////
29
30#[cfg(not(target_arch = "wasm32"))]
31include!("host_bindings_for_testing.rs");
32
33// host functions defined by the host.
34#[cfg(target_arch = "wasm32")]
35include!("host_bindings.rs");
36
37/// `Result` is a type that represents either a success ([`Ok`]) or failure ([`Err`]) result from the host.
38#[must_use]
39pub enum Result<T> {
40    /// Contains the success value
41    Ok(T),
42    /// Contains the error value
43    Err(Error), // TODO: Test if the WASM size is expanded if we use an enum here instead of i32
44}
45
46impl<T> Result<T> {
47    /// Returns `true` if the result is [`Ok`].
48    #[inline]
49    pub fn is_ok(&self) -> bool {
50        matches!(*self, Result::Ok(_))
51    }
52
53    /// Returns `true` if the result is [`Err`].
54    #[inline]
55    pub fn is_err(&self) -> bool {
56        !self.is_ok()
57    }
58
59    /// Converts from `Result<T>` to `Option<T>`.
60    ///
61    /// Converts `self` into an `Option<T>`, consuming `self`,
62    /// and discarding the error, if any.
63    #[inline]
64    pub fn ok(self) -> Option<T> {
65        match self {
66            Result::Ok(x) => Some(x),
67            Result::Err(_) => None,
68        }
69    }
70
71    /// Converts from `Result<T>` to `Option<Error>`.
72    ///
73    /// Converts `self` into an `Option<Error>`, consuming `self`,
74    /// and discarding the success value, if any.
75    #[inline]
76    pub fn err(self) -> Option<Error> {
77        match self {
78            Result::Ok(_) => None,
79            Result::Err(x) => Some(x),
80        }
81    }
82
83    /// Returns the contained [`Ok`] value, consuming the `self` value.
84    ///
85    /// # Panics
86    ///
87    /// Panics if the value is an [`Err`], with a panic message provided by the
88    /// [`Err`]'s value.
89    #[inline]
90    pub fn unwrap(self) -> T {
91        match self {
92            Result::Ok(t) => t,
93            Result::Err(error) => {
94                let _ = trace::trace_num("error_code=", error.code() as i64);
95                panic!(
96                    "called `Result::unwrap()` on an `Err` with code: {}",
97                    error.code()
98                )
99            }
100        }
101    }
102
103    /// Returns the contained [`Ok`] value or a provided default.
104    #[inline]
105    pub fn unwrap_or(self, default: T) -> T {
106        match self {
107            Result::Ok(t) => t,
108            Result::Err(_) => default,
109        }
110    }
111
112    /// Returns the contained [`Ok`] value or computes it from a closure.
113    #[inline]
114    pub fn unwrap_or_else<F: FnOnce(Error) -> T>(self, op: F) -> T {
115        match self {
116            Result::Ok(t) => t,
117            Result::Err(e) => op(e),
118        }
119    }
120
121    #[inline]
122    pub fn unwrap_or_panic(self) -> T {
123        self.unwrap_or_else(|error| {
124            let _ = trace::trace_num("error_code=", error.code() as i64);
125            core::panic!(
126                "Failed in {}: error_code={}",
127                core::panic::Location::caller(),
128                error.code()
129            );
130        })
131    }
132}
133
134impl From<i64> for Result<u64> {
135    #[inline(always)] // <-- Inline because this function is very small
136    fn from(value: i64) -> Self {
137        match value {
138            res if res >= 0 => Result::Ok(value as _),
139            _ => Result::Err(Error::from_code(value as _)),
140        }
141    }
142}
143
144/// Possible errors returned by XRPL Programmability APIs.
145///
146/// Errors are global across all Programmability APIs.
147#[derive(Clone, Copy, Debug)]
148#[repr(i32)]
149pub enum Error {
150    /// Reserved for internal invariant trips, generally unrelated to inputs.
151    /// These should be reported with an issue.
152    InternalError = error_codes::INTERNAL_ERROR,
153
154    /// The requested serialized field could not be found in the specified object.
155    /// This error is returned when attempting to access a field that doesn't exist
156    /// in the current transaction or ledger object.
157    FieldNotFound = error_codes::FIELD_NOT_FOUND,
158
159    /// The provided buffer is too small to hold the requested data.
160    /// Increase the buffer size and retry the operation.
161    BufferTooSmall = error_codes::BUFFER_TOO_SMALL,
162
163    /// The API was asked to assume the object under analysis is an STArray but it was not.
164    /// This error occurs when trying to perform array operations on non-array objects.
165    NoArray = error_codes::NO_ARRAY,
166
167    /// The specified field is not a leaf field and cannot be accessed directly.
168    /// Leaf fields are primitive types that contain actual data values.
169    NotLeafField = error_codes::NOT_LEAF_FIELD,
170
171    /// The provided locator string is malformed or invalid.
172    /// Locators must follow the proper format for field identification.
173    LocatorMalformed = error_codes::LOCATOR_MALFORMED,
174
175    /// The specified slot number is outside the valid range.
176    /// Slot numbers must be within the allowed bounds for the current context.
177    SlotOutRange = error_codes::SLOT_OUT_RANGE,
178
179    /// No free slots are available for allocation.
180    /// All available slots are currently in use. Consider reusing existing slots.
181    SlotsFull = error_codes::SLOTS_FULL,
182
183    /// The specified slot did not contain any slotted data (i.e., is empty).
184    /// This error occurs when trying to access a slot that hasn't been allocated
185    /// or has been freed.
186    EmptySlot = error_codes::EMPTY_SLOT,
187
188    /// The requested ledger object could not be found.
189    /// This may occur if the object doesn't exist or the keylet is invalid.
190    LedgerObjNotFound = error_codes::LEDGER_OBJ_NOT_FOUND,
191
192    /// An error occurred while decoding serialized data.
193    /// This typically indicates corrupted or invalidly formatted data.
194    InvalidDecoding = error_codes::INVALID_DECODING,
195
196    /// The data field is too large to be processed.
197    /// Consider reducing the size of the data or splitting it into smaller chunks.
198    DataFieldTooLarge = error_codes::DATA_FIELD_TOO_LARGE,
199
200    /// A pointer or buffer length provided as a parameter described memory outside the allowed memory region.
201    /// This error indicates a memory access violation.
202    PointerOutOfBounds = error_codes::POINTER_OUT_OF_BOUNDS,
203
204    /// No memory has been exported by the WebAssembly module.
205    /// The module must export its memory for host functions to access it.
206    NoMemoryExported = error_codes::NO_MEM_EXPORTED,
207
208    /// One or more of the parameters provided to the API are invalid.
209    /// Check the API documentation for valid parameter ranges and formats.
210    InvalidParams = error_codes::INVALID_PARAMS,
211
212    /// The provided account identifier is invalid.
213    /// Account IDs must be valid 20-byte addresses in the proper format.
214    InvalidAccount = error_codes::INVALID_ACCOUNT,
215
216    /// The specified field identifier is invalid or not recognized.
217    /// Field IDs must correspond to valid XRPL serialization fields.
218    InvalidField = error_codes::INVALID_FIELD,
219
220    /// The specified index is outside the valid bounds of the array or collection.
221    /// Ensure the index is within the valid range for the target object.
222    IndexOutOfBounds = error_codes::INDEX_OUT_OF_BOUNDS,
223
224    /// The input provided for floating-point parsing is malformed.
225    /// Floating-point values must be in the correct format for XFL operations.
226    InvalidFloatInput = error_codes::INVALID_FLOAT_INPUT,
227
228    /// An error occurred during floating-point computation.
229    /// This may indicate overflow, underflow, or other arithmetic errors.
230    InvalidFloatComputation = error_codes::INVALID_FLOAT_COMPUTATION,
231}
232
233impl Error {
234    // TODO: Use Trait instead?
235    #[inline(always)] // <-- Inline because this function is very small
236    pub fn from_code(code: i32) -> Self {
237        unsafe { core::mem::transmute(code) }
238    }
239
240    /// Error code
241    #[inline(always)] // <-- Inline because this function is very small
242    pub fn code(self) -> i32 {
243        self as _
244    }
245}
246
247impl From<Error> for i64 {
248    fn from(val: Error) -> Self {
249        val as i64
250    }
251}