xrpl_wasm_stdlib/core/ledger_objects/
mod.rs

1pub mod account_root;
2pub mod current_escrow;
3pub mod escrow;
4pub mod nft;
5pub mod traits;
6
7use crate::core::types::account_id::{ACCOUNT_ID_SIZE, AccountID};
8use crate::core::types::amount::Amount;
9use crate::core::types::blob::Blob;
10use crate::core::types::uint::{HASH128_SIZE, HASH256_SIZE, Hash128, Hash256};
11use crate::host::error_codes::{
12    match_result_code, match_result_code_optional, match_result_code_with_expected_bytes,
13    match_result_code_with_expected_bytes_optional,
14};
15use crate::host::{Result, get_current_ledger_obj_field, get_ledger_obj_field};
16
17/// Trait for types that can be retrieved from ledger object fields.
18///
19/// This trait provides a unified interface for retrieving typed data from XRPL ledger objects,
20/// replacing the previous collection of type-specific functions with a generic, type-safe approach.
21///
22/// ## Supported Types
23///
24/// The following types implement this trait:
25/// - `u16` - 16-bit unsigned integers (2 bytes)
26/// - `u32` - 32-bit unsigned integers (4 bytes)
27/// - `u64` - 64-bit unsigned integers (8 bytes)
28/// - `AccountID` - 20-byte account identifiers
29/// - `Amount` - XRP amounts and token amounts (variable size, up to 48 bytes)
30/// - `Hash128` - 128-bit cryptographic hashes (16 bytes)
31/// - `Hash256` - 256-bit cryptographic hashes (32 bytes)
32/// - `Blob` - Variable-length binary data (up to 1024 bytes)
33///
34/// ## Usage Patterns
35///
36/// ```rust,no_run
37/// use xrpl_wasm_stdlib::core::ledger_objects::{ledger_object, current_ledger_object};
38/// use xrpl_wasm_stdlib::core::types::account_id::AccountID;
39/// use xrpl_wasm_stdlib::sfield;
40/// # fn example() {
41/// # let slot = 0;
42/// // Get a required field from a specific ledger object
43/// let balance: u64 = ledger_object::get_field(slot, sfield::Balance).unwrap();
44/// let account: AccountID = ledger_object::get_field(slot, sfield::Account).unwrap();
45///
46/// // Get an optional field from the current ledger object
47/// let flags: Option<u32> = current_ledger_object::get_field_optional(sfield::Flags).unwrap();
48/// # }
49/// ```
50///
51/// ## Error Handling
52///
53/// - Required field methods return `Result<T>` and error if the field is missing
54/// - Optional field methods return `Result<Option<T>>` and return `None` if the field is missing
55/// - All methods return appropriate errors for buffer size mismatches or other retrieval failures
56///
57/// ## Safety Considerations
58///
59/// - All implementations use appropriately sized buffers for their data types
60/// - Buffer sizes are validated against expected field sizes where applicable
61/// - Unsafe operations are contained within the host function calls
62pub trait FieldGetter: Sized {
63    /// Get a required field from the current ledger object.
64    ///
65    /// # Arguments
66    ///
67    /// * `field_code` - The field code identifying which field to retrieve
68    ///
69    /// # Returns
70    ///
71    /// Returns a `Result<Self>` where:
72    /// * `Ok(Self)` - The field value for the specified field
73    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
74    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self>;
75
76    /// Get an optional field from the current ledger object.
77    ///
78    /// # Arguments
79    ///
80    /// * `field_code` - The field code identifying which field to retrieve
81    ///
82    /// # Returns
83    ///
84    /// Returns a `Result<Option<Self>>` where:
85    /// * `Ok(Some(Self))` - The field value for the specified field
86    /// * `Ok(None)` - If the field is not present
87    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
88    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>>;
89
90    /// Get a required field from a specific ledger object.
91    ///
92    /// # Arguments
93    ///
94    /// * `register_num` - The register number holding the ledger object
95    /// * `field_code` - The field code identifying which field to retrieve
96    ///
97    /// # Returns
98    ///
99    /// Returns a `Result<Self>` where:
100    /// * `Ok(Self)` - The field value for the specified field
101    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
102    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self>;
103
104    /// Get an optional field from a specific ledger object.
105    ///
106    /// # Arguments
107    ///
108    /// * `register_num` - The register number holding the ledger object
109    /// * `field_code` - The field code identifying which field to retrieve
110    ///
111    /// # Returns
112    ///
113    /// Returns a `Result<Option<Self>>` where:
114    /// * `Ok(Some(Self))` - The field value for the specified field
115    /// * `Ok(None)` - If the field is not present in the ledger object
116    /// * `Err(Error)` - If the field retrieval operation failed
117    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>>;
118}
119
120/// Implementation of `FieldGetter` for 16-bit unsigned integers.
121///
122/// This implementation handles 2-byte integer fields in XRPL ledger objects.
123/// Common use cases include ledger entry types and small enumerated values.
124///
125/// # Buffer Management
126///
127/// Uses a 2-byte buffer and validates that exactly 2 bytes are returned
128/// from the host function to ensure data integrity.
129impl FieldGetter for u16 {
130    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
131        let mut value: u16 = 0;
132        let value_ptr: *mut u8 = (&mut value as *mut u16).cast::<u8>();
133        let result_code = unsafe { get_current_ledger_obj_field(field_code, value_ptr, 2) };
134        match_result_code_with_expected_bytes(result_code, 2, || value)
135    }
136
137    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
138        let mut value: u16 = 0;
139        let value_ptr: *mut u8 = (&mut value as *mut u16).cast::<u8>();
140        let result_code = unsafe { get_current_ledger_obj_field(field_code, value_ptr, 2) };
141        match_result_code_with_expected_bytes_optional(result_code, 2, || Some(value))
142    }
143
144    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
145        let mut value: u16 = 0;
146        let value_ptr: *mut u8 = (&mut value as *mut u16).cast::<u8>();
147        let result_code = unsafe { get_ledger_obj_field(register_num, field_code, value_ptr, 2) };
148        match_result_code_with_expected_bytes(result_code, 2, || value)
149    }
150
151    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
152        let mut value: u16 = 0;
153        let value_ptr: *mut u8 = (&mut value as *mut u16).cast::<u8>();
154        let result_code = unsafe { get_ledger_obj_field(register_num, field_code, value_ptr, 2) };
155        match_result_code_with_expected_bytes_optional(result_code, 2, || Some(value))
156    }
157}
158
159/// Implementation of `FieldGetter` for 32-bit unsigned integers.
160///
161/// This implementation handles 4-byte integer fields in XRPL ledger objects.
162/// Common use cases include sequence numbers, flags, timestamps, and various counters.
163///
164/// # Buffer Management
165///
166/// Uses a 4-byte buffer and validates that exactly 4 bytes are returned
167/// from the host function to ensure data integrity.
168impl FieldGetter for u32 {
169    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
170        let mut value: u32 = 0;
171        let value_ptr: *mut u8 = (&mut value as *mut u32).cast::<u8>();
172        let result_code = unsafe { get_current_ledger_obj_field(field_code, value_ptr, 4) };
173        match_result_code_with_expected_bytes(result_code, 4, || value)
174    }
175
176    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
177        let mut value: u32 = 0;
178        let value_ptr: *mut u8 = (&mut value as *mut u32).cast::<u8>();
179        let result_code = unsafe { get_current_ledger_obj_field(field_code, value_ptr, 4) };
180        match_result_code_with_expected_bytes_optional(result_code, 4, || Some(value))
181    }
182
183    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
184        let mut value: u32 = 0;
185        let value_ptr: *mut u8 = (&mut value as *mut u32).cast::<u8>();
186        let result_code = unsafe { get_ledger_obj_field(register_num, field_code, value_ptr, 4) };
187        match_result_code_with_expected_bytes(result_code, 4, || value)
188    }
189
190    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
191        let mut value: u32 = 0;
192        let value_ptr: *mut u8 = (&mut value as *mut u32).cast::<u8>();
193        let result_code = unsafe { get_ledger_obj_field(register_num, field_code, value_ptr, 4) };
194        match_result_code_with_expected_bytes_optional(result_code, 4, || Some(value))
195    }
196}
197
198/// Implementation of `FieldGetter` for 64-bit unsigned integers.
199///
200/// This implementation handles 8-byte integer fields in XRPL ledger objects.
201/// Common use cases include large numeric values, balances represented as integers,
202/// and 64-bit identifiers.
203///
204/// # Buffer Management
205///
206/// Uses an 8-byte buffer and validates that exactly 8 bytes are returned
207/// from the host function to ensure data integrity.
208impl FieldGetter for u64 {
209    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
210        let mut value: u64 = 0;
211        let value_ptr: *mut u8 = (&mut value as *mut u64).cast::<u8>();
212        let result_code = unsafe { get_current_ledger_obj_field(field_code, value_ptr, 8) };
213        match_result_code_with_expected_bytes(result_code, 8, || value)
214    }
215
216    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
217        let mut value: u64 = 0;
218        let value_ptr: *mut u8 = (&mut value as *mut u64).cast::<u8>();
219        let result_code = unsafe { get_current_ledger_obj_field(field_code, value_ptr, 8) };
220        match_result_code_with_expected_bytes_optional(result_code, 8, || Some(value))
221    }
222
223    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
224        let mut value: u64 = 0;
225        let value_ptr: *mut u8 = (&mut value as *mut u64).cast::<u8>();
226        let result_code = unsafe { get_ledger_obj_field(register_num, field_code, value_ptr, 8) };
227        match_result_code_with_expected_bytes(result_code, 8, || value)
228    }
229
230    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
231        let mut value: u64 = 0;
232        let value_ptr: *mut u8 = (&mut value as *mut u64).cast::<u8>();
233        let result_code = unsafe { get_ledger_obj_field(register_num, field_code, value_ptr, 8) };
234        match_result_code_with_expected_bytes_optional(result_code, 8, || Some(value))
235    }
236}
237
238/// Implementation of `FieldGetter` for XRPL account identifiers.
239///
240/// This implementation handles 20-byte account ID fields in XRPL ledger objects.
241/// Account IDs uniquely identify accounts on the XRPL network and are derived
242/// from public keys using cryptographic hashing.
243///
244/// # Buffer Management
245///
246/// Uses a 20-byte buffer (ACCOUNT_ID_SIZE) and validates that exactly 20 bytes
247/// are returned from the host function. The buffer is converted to an AccountID
248/// using the `From<[u8; 20]>` implementation.
249impl FieldGetter for AccountID {
250    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
251        let mut buffer = [0x00; ACCOUNT_ID_SIZE];
252        let result_code = unsafe {
253            get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), ACCOUNT_ID_SIZE)
254        };
255        match_result_code_with_expected_bytes(result_code, ACCOUNT_ID_SIZE, || buffer.into())
256    }
257
258    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
259        let mut buffer = [0x00; ACCOUNT_ID_SIZE];
260        let result_code = unsafe {
261            get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), ACCOUNT_ID_SIZE)
262        };
263        match_result_code_with_expected_bytes_optional(result_code, ACCOUNT_ID_SIZE, || {
264            Some(buffer.into())
265        })
266    }
267
268    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
269        let mut buffer = [0x00; ACCOUNT_ID_SIZE];
270        let result_code = unsafe {
271            get_ledger_obj_field(
272                register_num,
273                field_code,
274                buffer.as_mut_ptr(),
275                ACCOUNT_ID_SIZE,
276            )
277        };
278        match_result_code_with_expected_bytes(result_code, ACCOUNT_ID_SIZE, || buffer.into())
279    }
280
281    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
282        let mut buffer = [0x00; ACCOUNT_ID_SIZE];
283        let result_code = unsafe {
284            get_ledger_obj_field(
285                register_num,
286                field_code,
287                buffer.as_mut_ptr(),
288                ACCOUNT_ID_SIZE,
289            )
290        };
291        match_result_code_with_expected_bytes_optional(result_code, ACCOUNT_ID_SIZE, || {
292            Some(buffer.into())
293        })
294    }
295}
296
297/// Implementation of `FieldGetter` for XRPL amount values.
298///
299/// This implementation handles amount fields in XRPL ledger objects, which can represent
300/// either XRP amounts (8 bytes) or token amounts (up to 48 bytes including currency code
301/// and issuer information).
302///
303/// # Buffer Management
304///
305/// Uses a 48-byte buffer to accommodate the largest possible amount representation.
306/// The Amount type handles the parsing of different amount formats internally.
307/// No strict byte count validation is performed since amounts can vary in size.
308impl FieldGetter for Amount {
309    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
310        const BUFFER_SIZE: usize = 48;
311        let mut buffer = [0u8; BUFFER_SIZE];
312        let result_code =
313            unsafe { get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), BUFFER_SIZE) };
314        match_result_code(result_code, || Amount::from(buffer))
315    }
316
317    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
318        const BUFFER_SIZE: usize = 48;
319        let mut buffer = [0u8; BUFFER_SIZE];
320        let result_code =
321            unsafe { get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), BUFFER_SIZE) };
322        match_result_code_optional(result_code, || Some(Amount::from(buffer)))
323    }
324
325    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
326        const BUFFER_SIZE: usize = 48;
327        let mut buffer = [0u8; BUFFER_SIZE];
328        let result_code = unsafe {
329            get_ledger_obj_field(register_num, field_code, buffer.as_mut_ptr(), BUFFER_SIZE)
330        };
331        match_result_code(result_code, || Amount::from(buffer))
332    }
333
334    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
335        const BUFFER_SIZE: usize = 48;
336        let mut buffer = [0u8; BUFFER_SIZE];
337        let result_code = unsafe {
338            get_ledger_obj_field(register_num, field_code, buffer.as_mut_ptr(), BUFFER_SIZE)
339        };
340        match_result_code_optional(result_code, || Some(Amount::from(buffer)))
341    }
342}
343
344/// Implementation of `FieldGetter` for 128-bit cryptographic hashes.
345///
346/// This implementation handles 16-byte hash fields in XRPL ledger objects.
347/// Hash128 values are commonly used for shorter identifiers and checksums
348/// in XRPL, such as email hashes.
349///
350/// # Buffer Management
351///
352/// Uses a 16-byte buffer (HASH128_SIZE) and validates that exactly 16 bytes
353/// are returned from the host function to ensure data integrity.
354impl FieldGetter for Hash128 {
355    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
356        let mut buffer = [0u8; HASH128_SIZE];
357        let result_code =
358            unsafe { get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), HASH128_SIZE) };
359        match_result_code_with_expected_bytes(result_code, HASH128_SIZE, || Hash128::from(buffer))
360    }
361
362    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
363        let mut buffer = [0u8; HASH128_SIZE];
364        let result_code =
365            unsafe { get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), HASH128_SIZE) };
366        match_result_code_with_expected_bytes_optional(result_code, HASH128_SIZE, || {
367            Some(Hash128::from(buffer))
368        })
369    }
370
371    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
372        let mut buffer = [0u8; HASH128_SIZE];
373        let result_code = unsafe {
374            get_ledger_obj_field(register_num, field_code, buffer.as_mut_ptr(), HASH128_SIZE)
375        };
376        match_result_code_with_expected_bytes(result_code, HASH128_SIZE, || Hash128::from(buffer))
377    }
378
379    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
380        let mut buffer = [0u8; HASH128_SIZE];
381        let result_code = unsafe {
382            get_ledger_obj_field(register_num, field_code, buffer.as_mut_ptr(), HASH128_SIZE)
383        };
384        match_result_code_with_expected_bytes_optional(result_code, HASH128_SIZE, || {
385            Some(Hash128::from(buffer))
386        })
387    }
388}
389
390/// Implementation of `FieldGetter` for 256-bit cryptographic hashes.
391///
392/// This implementation handles 32-byte hash fields in XRPL ledger objects.
393/// Hash256 values are widely used throughout XRPL for transaction IDs,
394/// ledger indexes, object IDs, and various cryptographic operations.
395///
396/// # Buffer Management
397///
398/// Uses a 32-byte buffer (HASH256_SIZE) and validates that exactly 32 bytes
399/// are returned from the host function to ensure data integrity.
400impl FieldGetter for Hash256 {
401    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
402        let mut buffer = [0u8; HASH256_SIZE];
403        let result_code =
404            unsafe { get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), HASH256_SIZE) };
405        match_result_code_with_expected_bytes(result_code, HASH256_SIZE, || Hash256::from(buffer))
406    }
407
408    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
409        let mut buffer = [0u8; HASH256_SIZE];
410        let result_code =
411            unsafe { get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), HASH256_SIZE) };
412        match_result_code_with_expected_bytes_optional(result_code, HASH256_SIZE, || {
413            Some(Hash256::from(buffer))
414        })
415    }
416
417    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
418        let mut buffer = [0u8; HASH256_SIZE];
419        let result_code = unsafe {
420            get_ledger_obj_field(register_num, field_code, buffer.as_mut_ptr(), HASH256_SIZE)
421        };
422        match_result_code_with_expected_bytes(result_code, HASH256_SIZE, || Hash256::from(buffer))
423    }
424
425    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
426        let mut buffer = [0u8; HASH256_SIZE];
427        let result_code = unsafe {
428            get_ledger_obj_field(register_num, field_code, buffer.as_mut_ptr(), HASH256_SIZE)
429        };
430        match_result_code_with_expected_bytes_optional(result_code, HASH256_SIZE, || {
431            Some(Hash256::from(buffer))
432        })
433    }
434}
435
436const BLOB_BUFFER_SIZE: usize = 1024;
437
438/// Implementation of `FieldGetter` for variable-length binary data.
439///
440/// This implementation handles blob fields in XRPL ledger objects, which can contain
441/// arbitrary binary data such as memos, signatures, public keys, and other
442/// variable-length content.
443///
444/// # Buffer Management
445///
446/// Uses a 1024-byte buffer to accommodate most blob field sizes. The actual
447/// length of the data is determined by the return value from the host function
448/// and stored in the Blob's `len` field. No strict byte count validation is
449/// performed since blobs can vary significantly in size.
450impl FieldGetter for Blob {
451    fn get_from_current_ledger_obj(field_code: i32) -> Result<Self> {
452        let mut buffer = [0u8; BLOB_BUFFER_SIZE];
453        let result_code = unsafe {
454            get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), BLOB_BUFFER_SIZE)
455        };
456        match_result_code(result_code, || Blob {
457            data: buffer,
458            len: result_code as usize,
459        })
460    }
461
462    fn get_from_current_ledger_obj_optional(field_code: i32) -> Result<Option<Self>> {
463        let mut buffer = [0u8; BLOB_BUFFER_SIZE];
464        let result_code = unsafe {
465            get_current_ledger_obj_field(field_code, buffer.as_mut_ptr(), BLOB_BUFFER_SIZE)
466        };
467        match_result_code_optional(result_code, || {
468            Some(Blob {
469                data: buffer,
470                len: result_code as usize,
471            })
472        })
473    }
474
475    fn get_from_ledger_obj(register_num: i32, field_code: i32) -> Result<Self> {
476        let mut buffer = [0u8; BLOB_BUFFER_SIZE];
477        let result_code = unsafe {
478            get_ledger_obj_field(
479                register_num,
480                field_code,
481                buffer.as_mut_ptr(),
482                BLOB_BUFFER_SIZE,
483            )
484        };
485        match_result_code(result_code, || Blob {
486            data: buffer,
487            len: result_code as usize,
488        })
489    }
490
491    fn get_from_ledger_obj_optional(register_num: i32, field_code: i32) -> Result<Option<Self>> {
492        let mut buffer = [0u8; BLOB_BUFFER_SIZE];
493        let result_code = unsafe {
494            get_ledger_obj_field(
495                register_num,
496                field_code,
497                buffer.as_mut_ptr(),
498                BLOB_BUFFER_SIZE,
499            )
500        };
501        match_result_code_optional(result_code, || {
502            Some(Blob {
503                data: buffer,
504                len: result_code as usize,
505            })
506        })
507    }
508}
509
510pub mod current_ledger_object {
511    use super::FieldGetter;
512    use crate::host::Result;
513
514    /// Retrieves a field from the current ledger object.
515    ///
516    /// # Arguments
517    ///
518    /// * `field_code` - The field code identifying which field to retrieve
519    ///
520    /// # Returns
521    ///
522    /// Returns a `Result<T>` where:
523    /// * `Ok(T)` - The field value for the specified field
524    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
525    #[inline]
526    pub fn get_field<T: FieldGetter>(field_code: i32) -> Result<T> {
527        T::get_from_current_ledger_obj(field_code)
528    }
529
530    /// Retrieves an optionally present field from the current ledger object.
531    ///
532    /// # Arguments
533    ///
534    /// * `field_code` - The field code identifying which field to retrieve
535    ///
536    /// # Returns
537    ///
538    /// Returns a `Result<Option<T>>` where:
539    /// * `Ok(Some(T))` - The field value for the specified field
540    /// * `Ok(None)` - If the field is not present
541    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
542    #[inline]
543    pub fn get_field_optional<T: FieldGetter>(field_code: i32) -> Result<Option<T>> {
544        T::get_from_current_ledger_obj_optional(field_code)
545    }
546}
547
548pub mod ledger_object {
549    use super::FieldGetter;
550    use crate::host::Result;
551
552    /// Retrieves a field from a specified ledger object.
553    ///
554    /// # Arguments
555    ///
556    /// * `register_num` - The register number holding the ledger object to look for data in
557    /// * `field_code` - The field code identifying which field to retrieve
558    ///
559    /// # Returns
560    ///
561    /// Returns a `Result<T>` where:
562    /// * `Ok(T)` - The field value for the specified field
563    /// * `Err(Error)` - If the field cannot be retrieved or has unexpected size
564    #[inline]
565    pub fn get_field<T: FieldGetter>(register_num: i32, field_code: i32) -> Result<T> {
566        T::get_from_ledger_obj(register_num, field_code)
567    }
568
569    /// Retrieves an optionally present field from a specified ledger object.
570    ///
571    /// # Arguments
572    ///
573    /// * `register_num` - The register number holding the ledger object to look for data in
574    /// * `field_code` - The field code identifying which field to retrieve
575    ///
576    /// # Returns
577    ///
578    /// Returns a `Result<Option<T>>` where:
579    /// * `Ok(Some(T))` - The field value for the specified field
580    /// * `Ok(None)` - If the field is not present in the ledger object
581    /// * `Err(Error)` - If the field retrieval operation failed
582    #[inline]
583    pub fn get_field_optional<T: FieldGetter>(
584        register_num: i32,
585        field_code: i32,
586    ) -> Result<Option<T>> {
587        T::get_from_ledger_obj_optional(register_num, field_code)
588    }
589}
590
591#[cfg(test)]
592mod tests {
593    use super::ledger_object;
594    use crate::sfield;
595
596    #[test]
597    fn test_get_field_compilation() {
598        // This test verifies that the get_field function compiles with the expected types
599        // We can't actually run these functions without a proper host environment,
600        // but we can verify they compile correctly
601
602        let slot = 0;
603
604        // Test the user's requested usage patterns
605        let _balance_call = || -> crate::host::Result<u64> {
606            ledger_object::get_field::<u64>(slot, sfield::Balance)
607        };
608
609        let _sequence_call = || -> crate::host::Result<u32> {
610            ledger_object::get_field::<u32>(slot, sfield::Sequence)
611        };
612
613        // Test with other types to ensure the trait implementations work
614        let _account_call = || -> crate::host::Result<crate::core::types::account_id::AccountID> {
615            ledger_object::get_field(slot, sfield::Account)
616        };
617
618        let _amount_call = || -> crate::host::Result<crate::core::types::amount::Amount> {
619            ledger_object::get_field(slot, sfield::Amount)
620        };
621
622        // Test optional variants
623        let _optional_balance_call = || -> crate::host::Result<Option<u64>> {
624            ledger_object::get_field_optional::<u64>(slot, sfield::Balance)
625        };
626
627        let _optional_sequence_call = || -> crate::host::Result<Option<u32>> {
628            ledger_object::get_field_optional::<u32>(slot, sfield::Sequence)
629        };
630    }
631
632    #[test]
633    fn test_exact_user_pattern() {
634        // Test the exact pattern the user requested
635        let slot = 0;
636
637        // These should compile exactly as the user specified
638        let _balance = ledger_object::get_field::<u64>(slot, sfield::Balance);
639        let _sequence = ledger_object::get_field::<u32>(slot, sfield::Sequence);
640
641        // Also test that Balance should work with Amount type (which is more correct)
642        let _balance_amount =
643            ledger_object::get_field::<crate::core::types::amount::Amount>(slot, sfield::Balance);
644    }
645}