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