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}