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}