xrpl_wasm_stdlib/core/ledger_objects/
traits.rs

1use crate::core::ledger_objects::{current_ledger_object, ledger_object};
2use crate::core::types::account_id::AccountID;
3use crate::core::types::amount::Amount;
4use crate::core::types::blob::Blob;
5use crate::core::types::contract_data::{ContractData, XRPL_CONTRACT_DATA_SIZE};
6use crate::core::types::crypto_condition::Condition;
7use crate::core::types::uint::{Hash128, Hash256};
8/// This module provides traits for interacting with XRP Ledger objects.
9///
10/// It defines common interfaces for accessing and manipulating different types of ledger objects,
11/// particularly focusing on Escrow objects. The traits provide methods to get and set various
12/// fields of ledger objects, with separate traits for current ledger objects and general ledger objects.
13use crate::host::error_codes::{
14    match_result_code, match_result_code_with_expected_bytes,
15    match_result_code_with_expected_bytes_optional,
16};
17use crate::host::{Error, get_current_ledger_obj_field, get_ledger_obj_field, update_data};
18use crate::host::{Result, Result::Err, Result::Ok};
19use crate::sfield;
20
21/// Trait providing access to common fields present in all ledger objects.
22///
23/// This trait defines methods to access standard fields that are common across
24/// different types of ledger objects in the XRP Ledger.
25pub trait LedgerObjectCommonFields {
26    /// Returns the slot number (register number) where the ledger object is stored.
27    ///
28    /// This number is used to identify and access the specific ledger object
29    /// when retrieving or modifying its fields.
30    ///
31    /// # Returns
32    ///
33    /// The slot number as an i32 value
34    fn get_slot_num(&self) -> i32;
35
36    /// Retrieves the ledger index (unique identifier) of the ledger object.
37    ///
38    /// # Arguments
39    ///
40    /// * `register_num` - The register number where the ledger object is stored
41    ///
42    /// # Returns
43    ///
44    /// The ledger index as a Hash256 value
45    fn get_ledger_index(&self) -> Result<Hash256> {
46        ledger_object::get_field(self.get_slot_num(), sfield::LedgerIndex)
47    }
48
49    /// Retrieves the flags field of the ledger object.
50    ///
51    /// # Arguments
52    ///
53    /// * `register_num` - The register number where the ledger object is stored
54    ///
55    /// # Returns
56    ///
57    /// The flags as a u32 value
58    fn get_flags(&self) -> Result<u32> {
59        ledger_object::get_field(self.get_slot_num(), sfield::Flags)
60    }
61
62    /// Retrieves the ledger entry type of the object.
63    ///
64    /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
65    ///
66    /// # Returns
67    ///
68    /// The ledger entry type as a u16 value
69    fn get_ledger_entry_type(&self) -> Result<u16> {
70        current_ledger_object::get_field(sfield::LedgerEntryType)
71    }
72}
73
74/// Trait providing access to common fields in the current ledger object.
75///
76/// This trait defines methods to access standard fields that are common across
77/// different types of ledger objects, specifically for the current ledger object
78/// being processed.
79pub trait CurrentLedgerObjectCommonFields {
80    /// Retrieves the ledger index (unique identifier) of the current ledger object.
81    ///
82    /// # Returns
83    ///
84    /// The ledger index as a Hash256 value
85    fn get_ledger_index(&self) -> Result<Hash256> {
86        current_ledger_object::get_field(sfield::LedgerIndex)
87    }
88
89    /// Retrieves the flags field of the current ledger object.
90    ///
91    /// # Returns
92    ///
93    /// The flags as a u32 value
94    fn get_flags(&self) -> Result<u32> {
95        current_ledger_object::get_field(sfield::Flags)
96    }
97
98    /// Retrieves the ledger entry type of the current ledger object.
99    ///
100    /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
101    ///
102    /// # Returns
103    ///
104    /// The ledger entry type as a u16 value
105    fn get_ledger_entry_type(&self) -> Result<u16> {
106        current_ledger_object::get_field(sfield::LedgerEntryType)
107    }
108}
109
110/// Trait providing access to fields specific to Escrow objects in the current ledger.
111///
112/// This trait extends `CurrentLedgerObjectCommonFields` and provides methods to access
113/// fields that are specific to Escrow objects in the current ledger being processed.
114pub trait CurrentEscrowFields: CurrentLedgerObjectCommonFields {
115    /// The address of the owner (sender) of this escrow. This is the account that provided the XRP
116    /// and gets it back if the escrow is canceled.
117    fn get_account(&self) -> Result<AccountID> {
118        current_ledger_object::get_field(sfield::Account)
119    }
120
121    /// The amount currently held in the escrow (could be XRP, IOU, or MPT).
122    fn get_amount(&self) -> Result<Amount> {
123        current_ledger_object::get_field(sfield::Amount)
124    }
125
126    /// The escrow can be canceled if and only if this field is present and the time it specifies
127    /// has passed. Specifically, this is specified as seconds since the Ripple Epoch and it
128    /// "has passed" if it's earlier than the close time of the previous validated ledger.
129    fn get_cancel_after(&self) -> Result<Option<u32>> {
130        current_ledger_object::get_field_optional(sfield::CancelAfter)
131    }
132
133    /// A PREIMAGE-SHA-256 crypto-condition, as hexadecimal. If present, the EscrowFinish
134    /// transaction must contain a fulfillment that satisfies this condition.
135    fn get_condition(&self) -> Result<Option<Condition>> {
136        let mut buffer = [0u8; 32];
137
138        let result_code = unsafe {
139            get_current_ledger_obj_field(sfield::Condition, buffer.as_mut_ptr(), buffer.len())
140        };
141
142        match_result_code_with_expected_bytes_optional(result_code, 32, || Some(buffer.into()))
143    }
144
145    /// The destination address where the XRP is paid if the escrow is successful.
146    fn get_destination(&self) -> Result<AccountID> {
147        current_ledger_object::get_field(sfield::Destination)
148    }
149
150    /// A hint indicating which page of the destination's owner directory links to this object, in
151    /// case the directory consists of multiple pages. Omitted on escrows created before enabling the fix1523 amendment.
152    fn get_destination_node(&self) -> Result<Option<u64>> {
153        current_ledger_object::get_field_optional(sfield::DestinationNode)
154    }
155
156    /// An arbitrary tag to further specify the destination for this escrow, such as a hosted
157    /// recipient at the destination address.
158    fn get_destination_tag(&self) -> Result<Option<u32>> {
159        current_ledger_object::get_field_optional(sfield::DestinationTag)
160    }
161
162    /// The time, in seconds since the Ripple Epoch, after which this escrow can be finished. Any
163    /// EscrowFinish transaction before this time fails. (Specifically, this is compared with the
164    /// close time of the previous validated ledger.)
165    fn get_finish_after(&self) -> Result<Option<u32>> {
166        current_ledger_object::get_field_optional(sfield::FinishAfter)
167    }
168
169    // TODO: Implement this function.
170    // /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
171    // fn get_ledger_entry_type(&self) -> Result<LedgerEntryType> {
172    //     return Ok(LedgerEntryType::Escrow);
173    // }
174
175    /// A hint indicating which page of the sender's owner directory links to this entry, in case
176    /// the directory consists of multiple pages.
177    fn get_owner_node(&self) -> Result<u64> {
178        current_ledger_object::get_field(sfield::OwnerNode)
179    }
180
181    /// The identifying hash of the transaction that most recently modified this entry.
182    fn get_previous_txn_id(&self) -> Result<Hash256> {
183        current_ledger_object::get_field(sfield::PreviousTxnID)
184    }
185
186    /// The index of the ledger that contains the transaction that most recently modified this
187    /// entry.
188    fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
189        current_ledger_object::get_field(sfield::PreviousTxnLgrSeq)
190    }
191
192    /// An arbitrary tag to further specify the source for this escrow, such as a hosted recipient
193    /// at the owner's address.
194    fn get_source_tag(&self) -> Result<Option<u32>> {
195        current_ledger_object::get_field_optional(sfield::SourceTag)
196    }
197
198    /// The WASM code that is executing.
199    fn get_finish_function(&self) -> Result<Option<Blob>> {
200        current_ledger_object::get_field_optional(sfield::FinishFunction)
201    }
202
203    /// Retrieves the contract `data` from the current escrow object.
204    ///
205    /// This function fetches the `data` field from the current ledger object and returns it as a
206    /// ContractData structure. The data is read into a fixed-size buffer of XRPL_CONTRACT_DATA_SIZE.
207    ///
208    /// # Returns
209    ///
210    /// Returns a `Result<ContractData>` where:
211    /// * `Ok(ContractData)` - Contains the retrieved data and its actual length
212    /// * `Err(Error)` - If the retrieval operation failed
213    fn get_data(&self) -> Result<ContractData> {
214        let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
215
216        let result_code =
217            unsafe { get_current_ledger_obj_field(sfield::Data, data.as_mut_ptr(), data.len()) };
218
219        match result_code {
220            code if code >= 0 => Ok(ContractData {
221                data,
222                len: code as usize,
223            }),
224            code => Err(Error::from_code(code)),
225        }
226    }
227
228    /// Updates the contract data in the current escrow object.
229    ///
230    /// # Arguments
231    ///
232    /// * `data` - The contract data to update
233    ///
234    /// # Returns
235    ///
236    /// Returns a `Result<()>` where:
237    /// * `Ok(())` - The data was successfully updated
238    /// * `Err(Error)` - If the update operation failed
239    fn update_current_escrow_data(data: ContractData) -> Result<()> {
240        // TODO: Make sure rippled always deletes any existing data bytes in rippled, and sets the new
241        // length to be `data.len` (e.g., if the developer writes 2 bytes, then that's the new
242        // length and any old bytes are lost).
243        let result_code = unsafe { update_data(data.data.as_ptr(), data.len) };
244        match_result_code_with_expected_bytes(result_code, data.len, || ())
245    }
246}
247
248/// Trait providing access to fields specific to Escrow objects in any ledger.
249///
250/// This trait extends `LedgerObjectCommonFields` and provides methods to access
251/// fields that are specific to Escrow objects in any ledger, not just the current one.
252/// Each method requires a register number to identify which ledger object to access.
253pub trait EscrowFields: LedgerObjectCommonFields {
254    /// The address of the owner (sender) of this escrow. This is the account that provided the XRP
255    /// and gets it back if the escrow is canceled.
256    fn get_account(&self) -> Result<AccountID> {
257        ledger_object::get_field(self.get_slot_num(), sfield::Account)
258    }
259
260    /// The amount of XRP, in drops, currently held in the escrow.
261    fn get_amount(&self) -> Result<Amount> {
262        // Create a buffer large enough for any Amount type
263        const BUFFER_SIZE: usize = 48usize;
264        let mut buffer = [0u8; BUFFER_SIZE];
265
266        let result_code = unsafe {
267            get_ledger_obj_field(
268                self.get_slot_num(),
269                sfield::Amount,
270                buffer.as_mut_ptr(),
271                buffer.len(),
272            )
273        };
274
275        match_result_code(result_code, || Amount::from(buffer))
276    }
277
278    /// The escrow can be canceled if and only if this field is present and the time it specifies
279    /// has passed. Specifically, this is specified as seconds since the Ripple Epoch and it
280    /// "has passed" if it's earlier than the close time of the previous validated ledger.
281    fn get_cancel_after(&self) -> Result<Option<u32>> {
282        ledger_object::get_field_optional(self.get_slot_num(), sfield::CancelAfter)
283    }
284
285    /// A PREIMAGE-SHA-256 crypto-condition, as hexadecimal. If present, the EscrowFinish
286    /// transaction must contain a fulfillment that satisfies this condition.
287    fn get_condition(&self) -> Result<Option<Condition>> {
288        let mut buffer = [0u8; 32];
289
290        let result_code = unsafe {
291            get_ledger_obj_field(
292                self.get_slot_num(),
293                sfield::Condition,
294                buffer.as_mut_ptr(),
295                buffer.len(),
296            )
297        };
298
299        match_result_code_with_expected_bytes_optional(result_code, 32, || Some(buffer.into()))
300    }
301
302    /// The destination address where the XRP is paid if the escrow is successful.
303    fn get_destination(&self) -> Result<AccountID> {
304        ledger_object::get_field(self.get_slot_num(), sfield::Destination)
305    }
306
307    /// A hint indicating which page of the destination's owner directory links to this object, in
308    /// case the directory consists of multiple pages. Omitted on escrows created before enabling the fix1523 amendment.
309    fn get_destination_node(&self) -> Result<Option<Hash256>> {
310        ledger_object::get_field_optional(self.get_slot_num(), sfield::DestinationNode)
311    }
312
313    /// An arbitrary tag to further specify the destination for this escrow, such as a hosted
314    /// recipient at the destination address.
315    fn get_destination_tag(&self) -> Result<Option<u32>> {
316        ledger_object::get_field_optional(self.get_slot_num(), sfield::DestinationTag)
317    }
318
319    /// The time, in seconds since the Ripple Epoch, after which this escrow can be finished. Any
320    /// EscrowFinish transaction before this time fails. (Specifically, this is compared with the
321    /// close time of the previous validated ledger.)
322    fn get_finish_after(&self) -> Result<Option<u32>> {
323        ledger_object::get_field_optional(self.get_slot_num(), sfield::FinishAfter)
324    }
325
326    // TODO: Implement this function.
327    // /// The value 0x0075, mapped to the string Escrow, indicates that this is an Escrow entry.
328    // fn get_ledger_entry_type(&self) -> Result<LedgerEntryType> {
329    //     return Ok(LedgerEntryType::Escrow);
330    // }
331
332    /// A hint indicating which page of the sender's owner directory links to this entry, in case
333    /// the directory consists of multiple pages.
334    fn get_owner_node(&self) -> Result<Hash256> {
335        ledger_object::get_field(self.get_slot_num(), sfield::OwnerNode)
336    }
337
338    /// The identifying hash of the transaction that most recently modified this entry.
339    fn get_previous_txn_id(&self) -> Result<Hash256> {
340        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnID)
341    }
342
343    /// The index of the ledger that contains the transaction that most recently modified this
344    /// entry.
345    fn get_previous_txn_lgr_seq(&self) -> Result<u32> {
346        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnLgrSeq)
347    }
348
349    /// An arbitrary tag to further specify the source for this escrow, such as a hosted recipient
350    /// at the owner's address.
351    fn get_source_tag(&self) -> Result<Option<u32>> {
352        ledger_object::get_field_optional(self.get_slot_num(), sfield::SourceTag)
353    }
354
355    /// The WASM code that is executing.
356    fn get_finish_function(&self) -> Result<Option<Blob>> {
357        ledger_object::get_field_optional(self.get_slot_num(), sfield::FinishFunction)
358    }
359
360    /// Retrieves the contract data from the specified ledger object.
361    ///
362    /// This function fetches the `data` field from the ledger object at the specified register
363    /// and returns it as a ContractData structure. The data is read into a fixed-size buffer
364    /// of XRPL_CONTRACT_DATA_SIZE.
365    ///
366    /// # Arguments
367    ///
368    /// * `register_num` - The register number where the ledger object is stored
369    ///
370    /// # Returns
371    ///
372    /// Returns a `Result<ContractData>` where:
373    /// * `Ok(ContractData)` - Contains the retrieved data and its actual length
374    /// * `Err(Error)` - If the retrieval operation failed
375    fn get_data(&self) -> Result<ContractData> {
376        let mut data: [u8; XRPL_CONTRACT_DATA_SIZE] = [0; XRPL_CONTRACT_DATA_SIZE];
377
378        let result_code = unsafe {
379            get_ledger_obj_field(
380                self.get_slot_num(),
381                sfield::Data,
382                data.as_mut_ptr(),
383                data.len(),
384            )
385        };
386
387        match result_code {
388            code if code >= 0 => Ok(ContractData {
389                data,
390                len: code as usize,
391            }),
392            code => Err(Error::from_code(code)),
393        }
394    }
395}
396
397/// Trait providing access to fields specific to AccountRoot objects in any ledger.
398///
399/// This trait extends `LedgerObjectCommonFields` and provides methods to access
400/// fields that are specific to Escrow objects in any ledger, not just the current one.
401/// Each method requires a register number to identify which ledger object to access.
402pub trait AccountFields: LedgerObjectCommonFields {
403    /// The identifying address of the account.
404    fn get_account(&self) -> Result<AccountID> {
405        ledger_object::get_field(self.get_slot_num(), sfield::Account)
406    }
407
408    /// AccountTxnID field for the account.
409    fn account_txn_id(&self) -> Result<Option<Hash256>> {
410        ledger_object::get_field_optional(self.get_slot_num(), sfield::AccountTxnID)
411    }
412
413    /// The ledger entry ID of the corresponding AMM ledger entry. Set during account creation; cannot be modified.
414    /// If present, indicates that this is a special AMM AccountRoot; always omitted on non-AMM accounts.
415    /// (Added by the AMM amendment)
416    fn amm_id(&self) -> Result<Option<Hash256>> {
417        ledger_object::get_field_optional(self.get_slot_num(), sfield::AMMID)
418    }
419
420    /// The account's current XRP balance in drops.
421    fn balance(&self) -> Result<Option<Amount>> {
422        ledger_object::get_field_optional(self.get_slot_num(), sfield::Balance)
423    }
424
425    /// How many total of this account's issued non-fungible tokens have been burned.
426    /// This number is always equal or less than MintedNFTokens.
427    fn burned_nf_tokens(&self) -> Result<Option<u32>> {
428        ledger_object::get_field_optional(self.get_slot_num(), sfield::BurnedNFTokens)
429    }
430
431    /// A domain associated with this account. In JSON, this is the hexadecimal for the ASCII representation of the
432    /// domain. Cannot be more than 256 bytes in length.
433    fn domain(&self) -> Result<Option<Blob>> {
434        ledger_object::get_field_optional(self.get_slot_num(), sfield::Domain)
435    }
436
437    /// The MD5 hash of an email address. Clients can use this to look up an avatar through services such as Gravatar.
438    fn email_hash(&self) -> Result<Option<Hash128>> {
439        ledger_object::get_field_optional(self.get_slot_num(), sfield::EmailHash)
440    }
441
442    /// The account's Sequence Number at the time it minted its first non-fungible-token.
443    /// (Added by the fixNFTokenRemint amendment)
444    fn first_nf_token_sequence(&self) -> Result<Option<u32>> {
445        ledger_object::get_field_optional(self.get_slot_num(), sfield::FirstNFTokenSequence)
446    }
447
448    /// The value 0x0061, mapped to the string AccountRoot, indicates that this is an AccountRoot object.
449    fn ledger_entry_type(&self) -> Result<u16> {
450        ledger_object::get_field(self.get_slot_num(), sfield::LedgerEntryType)
451    }
452
453    /// A public key that may be used to send encrypted messages to this account. In JSON, uses hexadecimal.
454    /// Must be exactly 33 bytes, with the first byte indicating the key type: 0x02 or 0x03 for secp256k1 keys,
455    /// 0xED for Ed25519 keys.
456    fn message_key(&self) -> Result<Option<Blob>> {
457        ledger_object::get_field_optional(self.get_slot_num(), sfield::MessageKey)
458    }
459
460    /// How many total non-fungible tokens have been minted by and on behalf of this account.
461    /// (Added by the NonFungibleTokensV1_1 amendment)
462    fn minted_nf_tokens(&self) -> Result<Option<u32>> {
463        ledger_object::get_field_optional(self.get_slot_num(), sfield::MintedNFTokens)
464    }
465
466    /// Another account that can mint non-fungible tokens on behalf of this account.
467    /// (Added by the NonFungibleTokensV1_1 amendment)
468    fn nf_token_minter(&self) -> Result<Option<AccountID>> {
469        ledger_object::get_field_optional(self.get_slot_num(), sfield::NFTokenMinter)
470    }
471
472    /// The number of objects this account owns in the ledger, which contributes to its owner reserve.
473    fn owner_count(&self) -> Result<u32> {
474        ledger_object::get_field(self.get_slot_num(), sfield::OwnerCount)
475    }
476
477    /// The identifying hash of the transaction that most recently modified this object.
478    fn previous_txn_id(&self) -> Result<Hash256> {
479        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnID)
480    }
481
482    /// The index of the ledger that contains the transaction that most recently modified this object.
483    fn previous_txn_lgr_seq(&self) -> Result<u32> {
484        ledger_object::get_field(self.get_slot_num(), sfield::PreviousTxnLgrSeq)
485    }
486
487    /// The address of a key pair that can be used to sign transactions for this account instead of the master key.
488    /// Use a SetRegularKey transaction to change this value.
489    fn regular_key(&self) -> Result<Option<AccountID>> {
490        ledger_object::get_field_optional(self.get_slot_num(), sfield::RegularKey)
491    }
492
493    /// The sequence number of the next valid transaction for this account.
494    fn sequence(&self) -> Result<u32> {
495        ledger_object::get_field(self.get_slot_num(), sfield::Sequence)
496    }
497
498    /// How many Tickets this account owns in the ledger. This is updated automatically to ensure that
499    /// the account stays within the hard limit of 250 Tickets at a time. This field is omitted if the account has zero
500    /// Tickets. (Added by the TicketBatch amendment.)
501    fn ticket_count(&self) -> Result<Option<u32>> {
502        ledger_object::get_field_optional(self.get_slot_num(), sfield::TicketCount)
503    }
504
505    /// How many significant digits to use for exchange rates of Offers involving currencies issued by this address.
506    /// Valid values are 3 to 15, inclusive. (Added by the TickSize amendment.)
507    fn tick_size(&self) -> Result<Option<u32>> {
508        ledger_object::get_field_optional(self.get_slot_num(), sfield::TickSize)
509    }
510
511    /// A transfer fee to charge other users for sending currency issued by this account to each other.
512    fn transfer_rate(&self) -> Result<Option<u32>> {
513        ledger_object::get_field_optional(self.get_slot_num(), sfield::TransferRate)
514    }
515
516    /// An arbitrary 256-bit value that users can set.
517    fn wallet_locator(&self) -> Result<Option<Hash256>> {
518        ledger_object::get_field_optional(self.get_slot_num(), sfield::WalletLocator)
519    }
520
521    /// Unused. (The code supports this field but there is no way to set it.)
522    fn wallet_size(&self) -> Result<Option<u32>> {
523        ledger_object::get_field_optional(self.get_slot_num(), sfield::WalletSize)
524    }
525}