From 40cf50cf3ab0a3773b2ae6619d317e35dfba6d88 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Tue, 19 Nov 2024 14:05:16 +0000 Subject: [PATCH 01/26] add get_xchain_claim_id --- src/utils/exceptions.rs | 12 +++++++++ src/utils/get_xchain_claim_id.rs | 45 ++++++++++++++++++++++++++++++++ src/utils/mod.rs | 1 + 3 files changed, 58 insertions(+) create mode 100644 src/utils/get_xchain_claim_id.rs diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index ca6923eb..23f6769d 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -20,6 +20,8 @@ pub enum XRPLUtilsException { XRPLCoreError(#[from] XRPLCoreException), #[error("XRPL Model error: {0}")] XRPLModelError(#[from] XRPLModelException), + #[error("XRPL XChain Claim ID error: {0}")] + XRPLXChainClaimIdError(#[from] XRPLXChainClaimIdException), #[error("ISO Code error: {0}")] ISOCodeError(#[from] ISOCodeException), #[error("Decimal error: {0}")] @@ -77,6 +79,16 @@ pub enum XRPLNFTIdException { InvalidNFTIdLength { expected: usize, found: usize }, } +#[derive(Debug, Clone, PartialEq, Error)] +#[non_exhaustive] +pub enum XRPLXChainClaimIdException { + #[error("No XChainOwnedClaimID created")] + NoXChainOwnedClaimID, +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLXChainClaimIdException {} + #[derive(Debug, Clone, PartialEq, Error)] #[non_exhaustive] pub enum ISOCodeException { diff --git a/src/utils/get_xchain_claim_id.rs b/src/utils/get_xchain_claim_id.rs new file mode 100644 index 00000000..333793d8 --- /dev/null +++ b/src/utils/get_xchain_claim_id.rs @@ -0,0 +1,45 @@ +use alloc::{borrow::Cow, vec::Vec}; + +use crate::models::{ + ledger::objects::LedgerEntryType, transactions::metadata::TransactionMetadata, +}; + +use super::exceptions::{XRPLUtilsException, XRPLUtilsResult, XRPLXChainClaimIdException}; +use crate::models::transactions::metadata::AffectedNode; + +pub fn get_xchain_claim_id<'a: 'b, 'b>( + meta: &TransactionMetadata<'a>, +) -> XRPLUtilsResult> { + let affected_nodes: Vec<&AffectedNode> = meta + .affected_nodes + .iter() + .filter(|node| { + // node.is_created_node() && node.created_node().ledger_entry_type == "XChainOwnedClaimID" + match node { + AffectedNode::CreatedNode { + ledger_entry_type, .. + } => ledger_entry_type == &LedgerEntryType::XChainOwnedClaimID, + _ => false, + } + }) + .collect(); + + if affected_nodes.is_empty() { + Err(XRPLXChainClaimIdException::NoXChainOwnedClaimID.into()) + } else { + match affected_nodes[0] { + AffectedNode::CreatedNode { new_fields, .. } => { + if let Some(xchain_claim_id) = new_fields.xchain_claim_id.as_ref() { + Ok(xchain_claim_id.clone()) + } else { + Err(XRPLUtilsException::XRPLXChainClaimIdError( + XRPLXChainClaimIdException::NoXChainOwnedClaimID, + )) + } + } + _ => Err(XRPLUtilsException::XRPLXChainClaimIdError( + XRPLXChainClaimIdException::NoXChainOwnedClaimID, + )), + } + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 0251fde8..17447ba5 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,6 +2,7 @@ pub mod exceptions; pub mod get_nftoken_id; +pub mod get_xchain_claim_id; pub mod parse_nftoken_id; pub mod time_conversion; #[cfg(feature = "models")] From 4a5578d8d1503fa00aaca14bb3c4567367561526 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Tue, 19 Nov 2024 14:56:34 +0000 Subject: [PATCH 02/26] add str_conversion --- src/core/binarycodec/types/currency.rs | 7 +++++-- src/utils/exceptions.rs | 8 ++++---- src/utils/mod.rs | 1 + src/utils/str_conversion.rs | 25 +++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/utils/str_conversion.rs diff --git a/src/core/binarycodec/types/currency.rs b/src/core/binarycodec/types/currency.rs index 23fd6927..af3c856a 100644 --- a/src/core/binarycodec/types/currency.rs +++ b/src/core/binarycodec/types/currency.rs @@ -16,6 +16,7 @@ use alloc::vec::Vec; use core::convert::TryFrom; use core::convert::TryInto; use core::fmt::Display; +use exceptions::XRPLUtilsException; use serde::Serializer; use serde::{Deserialize, Serialize}; @@ -28,11 +29,13 @@ pub const NATIVE_CODE: &str = "XRP"; #[serde(try_from = "&str")] pub struct Currency(Hash160); -fn _iso_code_from_hex(value: &[u8]) -> Result, ISOCodeException> { +fn _iso_code_from_hex(value: &[u8]) -> Result, XRPLUtilsException> { let candidate_iso = alloc::str::from_utf8(&value[12..15])?; if candidate_iso == NATIVE_CODE { - Err(ISOCodeException::InvalidXRPBytes) + Err(XRPLUtilsException::ISOCodeError( + ISOCodeException::InvalidXRPBytes, + )) } else if is_iso_code(candidate_iso) { Ok(Some(candidate_iso.to_string())) } else { diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index 23f6769d..02148917 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -34,6 +34,8 @@ pub enum XRPLUtilsException { FromHexError(#[from] hex::FromHexError), #[error("ParseInt error: {0}")] ParseIntError(#[from] core::num::ParseIntError), + #[error("Invalid UTF-8")] + Utf8Error, } #[derive(Debug, Clone, PartialEq, Error)] @@ -100,13 +102,11 @@ pub enum ISOCodeException { InvalidXRPBytes, #[error("Invalid Currency representation")] UnsupportedCurrencyRepresentation, - #[error("Invalid UTF-8")] - Utf8Error, } -impl From for ISOCodeException { +impl From for XRPLUtilsException { fn from(_: core::str::Utf8Error) -> Self { - ISOCodeException::Utf8Error + XRPLUtilsException::Utf8Error } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 17447ba5..bdb98737 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,6 +4,7 @@ pub mod exceptions; pub mod get_nftoken_id; pub mod get_xchain_claim_id; pub mod parse_nftoken_id; +pub mod str_conversion; pub mod time_conversion; #[cfg(feature = "models")] pub(crate) mod transactions; diff --git a/src/utils/str_conversion.rs b/src/utils/str_conversion.rs new file mode 100644 index 00000000..f1f1c291 --- /dev/null +++ b/src/utils/str_conversion.rs @@ -0,0 +1,25 @@ +use alloc::{borrow::Cow, format, string::String}; + +use super::exceptions::XRPLUtilsResult; + +/// Convert a UTF-8-encoded string into hexadecimal encoding. +/// XRPL uses hex strings as inputs in fields like `domain` +/// in the `AccountSet` transaction. +pub fn str_to_hex<'a: 'b, 'b>(value: Cow<'a, str>) -> XRPLUtilsResult> { + let hex_string = value + .as_bytes() + .iter() + .map(|b| format!("{:02x}", b)) + .collect::(); + Ok(Cow::Owned(hex_string)) +} + +/// Convert a hex string into a human-readable string. +/// XRPL uses hex strings as inputs in fields like `domain` +/// in the `AccountSet` transaction. +pub fn hex_to_str<'a: 'b, 'b>(value: Cow<'a, str>) -> XRPLUtilsResult> { + let bytes = hex::decode(value.as_ref())?; + let string = String::from_utf8(bytes).map_err(|e| e.utf8_error())?; + + Ok(Cow::Owned(string)) +} From 893914b0d12f0a1272eacbe24b817779e3491a25 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Tue, 19 Nov 2024 18:11:59 +0000 Subject: [PATCH 03/26] current state of txn parsing --- src/models/transactions/metadata.rs | 23 ++- src/utils/exceptions.rs | 12 ++ src/utils/mod.rs | 1 + src/utils/txn_parser/get_balance_changes.rs | 0 src/utils/txn_parser/get_final_balances.rs | 0 src/utils/txn_parser/get_final_order_book.rs | 0 .../txn_parser/get_order_book_changes.rs | 0 src/utils/txn_parser/mod.rs | 1 + src/utils/txn_parser/utils/balance_parser.rs | 163 ++++++++++++++++++ src/utils/txn_parser/utils/mod.rs | 96 +++++++++++ src/utils/txn_parser/utils/nodes.rs | 76 ++++++++ .../txn_parser/utils/order_book_parser.rs | 35 ++++ src/utils/txn_parser/utils/parser.rs | 63 +++++++ 13 files changed, 462 insertions(+), 8 deletions(-) create mode 100644 src/utils/txn_parser/get_balance_changes.rs create mode 100644 src/utils/txn_parser/get_final_balances.rs create mode 100644 src/utils/txn_parser/get_final_order_book.rs create mode 100644 src/utils/txn_parser/get_order_book_changes.rs create mode 100644 src/utils/txn_parser/mod.rs create mode 100644 src/utils/txn_parser/utils/balance_parser.rs create mode 100644 src/utils/txn_parser/utils/mod.rs create mode 100644 src/utils/txn_parser/utils/nodes.rs create mode 100644 src/utils/txn_parser/utils/order_book_parser.rs create mode 100644 src/utils/txn_parser/utils/parser.rs diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index 16d322f4..959807c7 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -5,7 +5,7 @@ use serde_with::skip_serializing_none; use crate::models::ledger::objects::LedgerEntryType; use crate::models::requests::LedgerIndex; -use crate::models::Amount; +use crate::models::{Amount, IssuedCurrencyAmount}; #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize)] @@ -31,15 +31,15 @@ pub struct Fields<'a> { pub account: Option>, pub balance: Option>, pub book_directory: Option>, - pub expiration: Option, - pub flags: Option, - pub low_limit: Option, Cow<'a, str>>>, - pub high_limit: Option, Cow<'a, str>>>, + pub expiration: Option, + pub flags: Option, + pub low_limit: Option>, + pub high_limit: Option>, pub next_page_min: Option>, #[serde(rename = "NFTokens")] pub nftokens: Option>>, pub previous_page_min: Option>, - pub sequence: Option, + pub sequence: Option, pub taker_gets: Option>, pub taker_pays: Option>, pub xchain_claim_id: Option>, @@ -62,7 +62,7 @@ pub enum AffectedNode<'a> { final_fields: Option>, previous_fields: Option>, previous_txn_id: Option>, - previous_txn_lgr_seq: Option, + previous_txn_lgr_seq: Option, }, #[serde(rename_all = "PascalCase")] DeletedNode { @@ -73,12 +73,19 @@ pub enum AffectedNode<'a> { }, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum NodeType { + CreatedNode, + ModifiedNode, + DeletedNode, +} + #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct TransactionMetadata<'a> { pub affected_nodes: Vec>, - pub transaction_index: i32, + pub transaction_index: u32, pub transaction_result: Amount<'a>, #[serde(rename = "delivered_amount")] pub delivered_amount: Option>, diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index 02148917..f5a2e043 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -22,6 +22,8 @@ pub enum XRPLUtilsException { XRPLModelError(#[from] XRPLModelException), #[error("XRPL XChain Claim ID error: {0}")] XRPLXChainClaimIdError(#[from] XRPLXChainClaimIdException), + #[error("XRPL Txn Parser error: {0}")] + XRPLTxnParserError(#[from] XRPLTxnParserException), #[error("ISO Code error: {0}")] ISOCodeError(#[from] ISOCodeException), #[error("Decimal error: {0}")] @@ -91,6 +93,16 @@ pub enum XRPLXChainClaimIdException { #[cfg(feature = "std")] impl alloc::error::Error for XRPLXChainClaimIdException {} +#[derive(Debug, Clone, PartialEq, Error)] +#[non_exhaustive] +pub enum XRPLTxnParserException { + #[error("Missing field: {0}")] + MissingField(&'static str), +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLTxnParserException {} + #[derive(Debug, Clone, PartialEq, Error)] #[non_exhaustive] pub enum ISOCodeException { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index bdb98737..c13c9021 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -8,6 +8,7 @@ pub mod str_conversion; pub mod time_conversion; #[cfg(feature = "models")] pub(crate) mod transactions; +pub mod txn_parser; pub mod xrpl_conversion; pub use self::time_conversion::*; diff --git a/src/utils/txn_parser/get_balance_changes.rs b/src/utils/txn_parser/get_balance_changes.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/utils/txn_parser/get_final_balances.rs b/src/utils/txn_parser/get_final_balances.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/utils/txn_parser/get_final_order_book.rs b/src/utils/txn_parser/get_final_order_book.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/utils/txn_parser/get_order_book_changes.rs b/src/utils/txn_parser/get_order_book_changes.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/utils/txn_parser/mod.rs b/src/utils/txn_parser/mod.rs new file mode 100644 index 00000000..95adf51c --- /dev/null +++ b/src/utils/txn_parser/mod.rs @@ -0,0 +1 @@ +mod utils; diff --git a/src/utils/txn_parser/utils/balance_parser.rs b/src/utils/txn_parser/utils/balance_parser.rs new file mode 100644 index 00000000..c7f4a9ad --- /dev/null +++ b/src/utils/txn_parser/utils/balance_parser.rs @@ -0,0 +1,163 @@ +use core::str::FromStr; + +use alloc::{ + string::{String, ToString}, + vec::{self, Vec}, +}; +use bigdecimal::{num_bigint::Sign, BigDecimal}; + +use crate::utils::{ + drops_to_xrp, + exceptions::{XRPLTxnParserException, XRPLUtilsResult}, + txn_parser::utils::{negate, Balance}, +}; + +use super::{nodes::NormalizedNode, AccountBalance}; + +fn get_xrp_quantity( + node: NormalizedNode, + value: Option, +) -> XRPLUtilsResult> { + if let Some(value) = value { + let absulte_value = value.clone().abs(); + let xrp_value = if value.sign() != Sign::Minus { + let xrp_quantity = drops_to_xrp(absulte_value.to_string().as_str())?; + let mut negated_value_string = String::from("-"); + negated_value_string.push_str(&xrp_quantity); + + BigDecimal::from_str(&negated_value_string)? + } else { + let xrp_value = drops_to_xrp(absulte_value.to_string().as_str())?; + + BigDecimal::from_str(&xrp_value)? + }; + if let Some(final_fields) = node.final_fields { + if let Some(account) = final_fields.account { + Ok(Some(AccountBalance { + account: account.to_string().into(), + balance: Balance { + currency: "XRP".into(), + value: xrp_value.to_string().into(), + issuer: None, + }, + })) + } else { + Ok(None) + } + } else if let Some(new_fields) = node.new_fields { + if let Some(account) = new_fields.account { + Ok(Some(AccountBalance { + account: account.to_string().into(), + balance: Balance { + currency: "XRP".into(), + value: xrp_value.to_string().into(), + issuer: None, + }, + })) + } else { + Ok(None) + } + } else { + Ok(None) + } + } else { + Ok(None) + } +} + +fn flip_trustline_perspective<'a: 'b, 'b>( + account_balance: &'a AccountBalance<'_>, +) -> XRPLUtilsResult> { + let balance = account_balance.balance.clone(); + let negated_value = negate(&BigDecimal::from_str(balance.value.as_ref())?); + let issuer = balance.issuer.clone(); + + Ok(AccountBalance { + account: issuer.ok_or(XRPLTxnParserException::MissingField("issuer"))?, + balance: Balance { + currency: balance.currency, + value: negated_value.normalized().to_string().into(), + issuer: Some(account_balance.account.clone()), + }, + }) +} +fn get_trustline_quantity( + node: NormalizedNode, + value: Option, +) -> XRPLUtilsResult> { + if value.is_none() { + return Ok(Vec::new()); + } + let fields = if let Some(new_fields) = node.new_fields { + new_fields + } else if let Some(final_fields) = node.final_fields { + final_fields + } else { + return Ok(Vec::new()); + }; + let low_limit = fields.low_limit; + let balance = fields.balance; + let high_limit = fields.high_limit; + if let (Some(low_limit), Some(balance), Some(high_limit)) = (low_limit, balance, high_limit) { + let low_limit_issuer = low_limit.issuer; + let balance_currency = balance; + let high_limit_issuer = high_limit.issuer; + if let (Some(low_limit_issuer), Some(balance_currency), Some(high_limit_issuer)) = + (low_limit_issuer, balance_currency, high_limit_issuer) + { + let result = AccountBalance { + account: low_limit_issuer.to_string().into(), + balance: Balance { + currency: balance_currency.to_string().into(), + issuer: Some(high_limit_issuer.to_string().into()), + value: value.unwrap().normalized().to_string().into(), + }, + }; + return Ok(vec![result.clone(), flip_trustline_perspective(&result)?]); + } + } + Ok(Vec::new()) +} + +fn get_node_balances( + node: NormalizedNode, + value: Option, +) -> XRPLUtilsResult> { + match node.ledger_entry_type.as_str() { + "AccountRoot" => { + if let Some(xrp_quantity) = get_xrp_quantity(node, value)? { + Ok(vec![xrp_quantity]) + } else { + Ok(Vec::new()) + } + } + "RippleState" => get_trustline_quantity(node, value), + _ => Ok(Vec::new()), + } +} + +fn group_balances_by_account(account_balances: Vec) -> Vec { + let grouped_balances = group_by_account(account_balances); + let mut result = Vec::new(); + for (account, account_obj) in grouped_balances { + let balances: Vec = account_obj.into_iter().map(|ab| ab.balance).collect(); + result.push(AccountBalances { + account: account.to_string().into(), + balances, + }); + } + result +} + +fn derive_account_balances( + metadata: TransactionMetadata, + parser: impl Fn(NormalizedNode) -> Option, +) -> XRPLUtilsResult> { + let mut quantities = Vec::new(); + for node in normalize_nodes(metadata) { + if let Some(value) = parser(node.clone()) { + quantities.extend(get_node_balances(node, Some(value))?); + } + } + Ok(group_balances_by_account(quantities)) +} diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs new file mode 100644 index 00000000..f7f30bf9 --- /dev/null +++ b/src/utils/txn_parser/utils/mod.rs @@ -0,0 +1,96 @@ +use core::str::FromStr; + +use alloc::{ + borrow::Cow, + string::{String, ToString}, + vec::Vec, +}; +use bigdecimal::BigDecimal; + +use crate::models::{transactions::offer_create::OfferCreateFlag, Amount, FlagCollection}; + +pub mod balance_parser; +pub mod nodes; +pub mod order_book_parser; +pub mod parser; + +#[derive(Debug, Clone)] +pub struct Balance<'a> { + pub currency: Cow<'a, str>, + pub value: Cow<'a, str>, + pub issuer: Option>, +} + +impl<'a: 'b, 'b> From> for Balance<'b> { + fn from(amount: Amount<'a>) -> Self { + match amount { + Amount::XRPAmount(amount) => Self { + currency: Cow::Borrowed("XRP"), + value: amount.0, + issuer: None, + }, + Amount::IssuedCurrencyAmount(amount) => Self { + currency: amount.currency, + value: amount.value, + issuer: Some(amount.issuer), + }, + } + } +} + +#[derive(Debug, Clone)] +pub struct AccountBalance<'a> { + pub account: Cow<'a, str>, + pub balance: Balance<'a>, +} + +#[derive(Debug, Clone)] +pub struct AccountBalances<'a> { + pub account: Cow<'a, str>, + pub balances: Vec>, +} + +#[derive(Debug, Clone)] +pub enum OfferStatus { + Created, + PartiallyFilled, + Filled, + Cancelled, +} + +#[derive(Debug, Clone)] +pub struct OfferChange<'a> { + pub flags: FlagCollection, + pub taker_gets: Amount<'a>, + pub taker_pays: Amount<'a>, + pub sequence: u32, + pub status: OfferStatus, + pub maker_exchange_rate: Option, + pub expiration_time: Option, +} + +#[derive(Debug, Clone)] +pub struct AccountOfferChange<'a> { + pub maker_account: Cow<'a, str>, + pub offer_change: OfferChange<'a>, +} + +#[derive(Debug, Clone)] +pub struct AccountOfferChanges<'a> { + pub account: Cow<'a, str>, + pub offer_changes: Vec>, +} + +#[derive(Debug, Clone)] +pub struct AccountObjectGroup<'a> { + pub account: Cow<'a, str>, + pub account_balances: Vec<&'a AccountBalance<'a>>, + pub account_offer_changes: Vec<&'a AccountOfferChange<'a>>, +} + +pub fn negate(value: &BigDecimal) -> BigDecimal { + let mut negated_value_string = String::from("-"); + negated_value_string.push_str(&value.to_string()); + + BigDecimal::from_str(&negated_value_string).unwrap() +} diff --git a/src/utils/txn_parser/utils/nodes.rs b/src/utils/txn_parser/utils/nodes.rs new file mode 100644 index 00000000..cfc5848a --- /dev/null +++ b/src/utils/txn_parser/utils/nodes.rs @@ -0,0 +1,76 @@ +use alloc::vec::Vec; + +use crate::models::{ + ledger::objects::LedgerEntryType, + requests::LedgerIndex, + transactions::metadata::{AffectedNode, Fields, NodeType, TransactionMetadata}, +}; + +pub struct NormalizedNode<'a> { + pub node_type: NodeType, + pub ledger_entry_type: LedgerEntryType, + pub ledger_index: LedgerIndex<'a>, + pub new_fields: Option>, + pub final_fields: Option>, + pub previous_fields: Option>, + pub previous_txn_id: Option<&'a str>, + pub previous_txn_lgr_seq: Option, +} + +fn normalize_node<'a: 'b, 'b>(affected_node: &'a AffectedNode<'_>) -> NormalizedNode<'b> { + match affected_node { + AffectedNode::CreatedNode { + ledger_entry_type, + ledger_index, + new_fields, + } => NormalizedNode { + node_type: NodeType::CreatedNode, + ledger_entry_type: ledger_entry_type.clone(), + ledger_index: ledger_index.clone(), + new_fields: Some(new_fields.clone()), + final_fields: None, + previous_fields: None, + previous_txn_id: None, + previous_txn_lgr_seq: None, + }, + AffectedNode::ModifiedNode { + ledger_entry_type, + ledger_index, + final_fields, + previous_fields, + previous_txn_id, + previous_txn_lgr_seq, + } => NormalizedNode { + node_type: NodeType::ModifiedNode, + ledger_entry_type: ledger_entry_type.clone(), + ledger_index: ledger_index.clone(), + new_fields: None, + final_fields: final_fields.clone(), + previous_fields: previous_fields.clone(), + previous_txn_id: previous_txn_id.as_deref(), + previous_txn_lgr_seq: *previous_txn_lgr_seq, + }, + AffectedNode::DeletedNode { + ledger_entry_type, + ledger_index, + final_fields, + previous_fields, + } => NormalizedNode { + node_type: NodeType::DeletedNode, + ledger_entry_type: ledger_entry_type.clone(), + ledger_index: ledger_index.clone(), + new_fields: None, + final_fields: Some(final_fields.clone()), + previous_fields: previous_fields.clone(), + previous_txn_id: None, + previous_txn_lgr_seq: None, + }, + } +} + +pub fn normalize_nodes<'a: 'b, 'b>(meta: &'a TransactionMetadata<'_>) -> Vec> { + meta.affected_nodes + .iter() + .map(|node| normalize_node(node)) + .collect() +} diff --git a/src/utils/txn_parser/utils/order_book_parser.rs b/src/utils/txn_parser/utils/order_book_parser.rs new file mode 100644 index 00000000..77fa2328 --- /dev/null +++ b/src/utils/txn_parser/utils/order_book_parser.rs @@ -0,0 +1,35 @@ +use crate::models::{transactions::metadata::NodeType, Amount}; + +use super::{nodes::NormalizedNode, Balance, OfferStatus}; + +const LSF_SELL: u32 = 0x00020000; + +fn get_offer_status(node: &NormalizedNode<'_>) -> OfferStatus { + match node.node_type { + NodeType::CreatedNode => OfferStatus::Created, + NodeType::ModifiedNode => OfferStatus::PartiallyFilled, + NodeType::DeletedNode => { + if let Some(_) = node.previous_fields { + // a filled offer has previous fields + OfferStatus::Filled + } else { + OfferStatus::Cancelled + } + } + } +} + +fn derive_currency_amount<'a: 'b, 'b>(currency_amount: &'a Amount) -> Balance<'b> { + match currency_amount { + Amount::XRPAmount(amount) => Balance { + currency: "XRP".into(), + value: amount.0.clone(), + issuer: None, + }, + Amount::IssuedCurrencyAmount(amount) => Balance { + currency: amount.currency.clone(), + value: amount.value.clone(), + issuer: Some(amount.issuer.clone()), + }, + } +} diff --git a/src/utils/txn_parser/utils/parser.rs b/src/utils/txn_parser/utils/parser.rs new file mode 100644 index 00000000..fc59593f --- /dev/null +++ b/src/utils/txn_parser/utils/parser.rs @@ -0,0 +1,63 @@ +use core::str::FromStr; + +use alloc::vec::Vec; +use bigdecimal::BigDecimal; + +use crate::utils::exceptions::XRPLUtilsResult; + +use super::{AccountBalance, AccountObjectGroup, AccountOfferChange, Balance}; + +pub fn get_value(balance: &Balance) -> XRPLUtilsResult { + Ok(BigDecimal::from_str(balance.value.as_ref())?) +} + +pub fn group_by_account<'a: 'b, 'b>( + account_balances: Vec<&'a AccountBalance<'_>>, + account_offer_changes: Vec<&'a AccountOfferChange<'_>>, +) -> Vec> { + let mut account_object_groups: Vec> = Vec::new(); + + for balance in account_balances.iter() { + // Find the account object group with the same account. If it doesn't exist, create a new one. + let account_object_group = account_object_groups + .iter_mut() + .find(|group| group.account == balance.account.as_ref()); + if let Some(group) = account_object_group { + group.account_balances.push(balance); + } else { + account_object_groups.push(AccountObjectGroup { + account: balance.account.clone(), + account_balances: Vec::new(), + account_offer_changes: Vec::new(), + }); + account_object_groups + .last_mut() + .unwrap() + .account_balances + .push(balance); + } + } + + for offer_change in account_offer_changes.iter() { + // Find the account object group with the same account. If it doesn't exist, create a new one. + let account_object_group = account_object_groups + .iter_mut() + .find(|group| group.account == offer_change.maker_account.as_ref()); + if let Some(group) = account_object_group { + group.account_offer_changes.push(offer_change); + } else { + account_object_groups.push(AccountObjectGroup { + account: offer_change.maker_account.clone(), + account_balances: Vec::new(), + account_offer_changes: Vec::new(), + }); + account_object_groups + .last_mut() + .unwrap() + .account_offer_changes + .push(offer_change); + } + } + + account_object_groups +} From 5b2b6df86e7b815a921d784b6566ce0fb8a91c01 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 20 Nov 2024 16:33:58 +0000 Subject: [PATCH 04/26] fix core build --- src/utils/exceptions.rs | 5 ++++- src/utils/mod.rs | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index f5a2e043..31c86e4f 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -3,7 +3,9 @@ use alloc::string::String; use thiserror_no_std::Error; -use crate::{core::exceptions::XRPLCoreException, models::XRPLModelException, XRPLSerdeJsonError}; +#[cfg(feature = "models")] +use crate::models::XRPLModelException; +use crate::{core::exceptions::XRPLCoreException, XRPLSerdeJsonError}; pub type XRPLUtilsResult = core::result::Result; @@ -18,6 +20,7 @@ pub enum XRPLUtilsException { XRPLNFTIdError(#[from] XRPLNFTIdException), #[error("XRPL Core error: {0}")] XRPLCoreError(#[from] XRPLCoreException), + #[cfg(feature = "models")] #[error("XRPL Model error: {0}")] XRPLModelError(#[from] XRPLModelException), #[error("XRPL XChain Claim ID error: {0}")] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index c13c9021..ea7ee373 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,8 +1,10 @@ //! Convenience utilities for the XRP Ledger pub mod exceptions; +#[cfg(feature = "models")] pub mod get_nftoken_id; pub mod get_xchain_claim_id; +#[cfg(feature = "models")] pub mod parse_nftoken_id; pub mod str_conversion; pub mod time_conversion; From 3f29ef36be628a19ee3699c03fd889cfe98536c2 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Tue, 19 Nov 2024 14:05:16 +0000 Subject: [PATCH 05/26] add get_xchain_claim_id --- src/utils/exceptions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index 31c86e4f..7efc3031 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -23,10 +23,10 @@ pub enum XRPLUtilsException { #[cfg(feature = "models")] #[error("XRPL Model error: {0}")] XRPLModelError(#[from] XRPLModelException), - #[error("XRPL XChain Claim ID error: {0}")] - XRPLXChainClaimIdError(#[from] XRPLXChainClaimIdException), #[error("XRPL Txn Parser error: {0}")] XRPLTxnParserError(#[from] XRPLTxnParserException), + #[error("XRPL XChain Claim ID error: {0}")] + XRPLXChainClaimIdError(#[from] XRPLXChainClaimIdException), #[error("ISO Code error: {0}")] ISOCodeError(#[from] ISOCodeException), #[error("Decimal error: {0}")] From 926571b73054cf8692dbb82c624f739ad416c167 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Mon, 25 Nov 2024 15:43:00 +0000 Subject: [PATCH 06/26] refactor transaction metadata and balance parsing logic --- src/models/transactions/metadata.rs | 6 +- src/utils/txn_parser/get_balance_changes.rs | 48 ++++++++ src/utils/txn_parser/mod.rs | 4 + src/utils/txn_parser/utils/balance_parser.rs | 115 ++++++------------- src/utils/txn_parser/utils/parser.rs | 13 ++- 5 files changed, 102 insertions(+), 84 deletions(-) diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index 959807c7..2628298f 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -29,17 +29,17 @@ pub struct NFTokenMetadataFields<'a> { #[serde(rename_all = "PascalCase")] pub struct Fields<'a> { pub account: Option>, - pub balance: Option>, + pub balance: Option>, pub book_directory: Option>, pub expiration: Option, - pub flags: Option, + pub flags: u32, pub low_limit: Option>, pub high_limit: Option>, pub next_page_min: Option>, #[serde(rename = "NFTokens")] pub nftokens: Option>>, pub previous_page_min: Option>, - pub sequence: Option, + pub sequence: u32, pub taker_gets: Option>, pub taker_pays: Option>, pub xchain_claim_id: Option>, diff --git a/src/utils/txn_parser/get_balance_changes.rs b/src/utils/txn_parser/get_balance_changes.rs index e69de29b..66a1cd14 100644 --- a/src/utils/txn_parser/get_balance_changes.rs +++ b/src/utils/txn_parser/get_balance_changes.rs @@ -0,0 +1,48 @@ +use alloc::vec::Vec; +use bigdecimal::BigDecimal; + +use crate::{ + models::transactions::metadata::TransactionMetadata, + utils::{ + exceptions::{XRPLUtilsException, XRPLUtilsResult}, + txn_parser::utils::parser::get_value, + }, +}; + +use super::utils::{ + balance_parser::derive_account_balances, nodes::NormalizedNode, AccountBalances, +}; + +pub fn get_balance_changes<'a: 'b, 'b>( + meta: &'a TransactionMetadata<'a>, +) -> XRPLUtilsResult>> { + Ok(derive_account_balances(meta, compute_balance_change)?) +} + +/// Get the balance change from a node. +fn compute_balance_change(node: &NormalizedNode) -> XRPLUtilsResult> { + let new_fields = node.new_fields.as_ref(); + let previous_fields = node.previous_fields.as_ref(); + let final_fields = node.final_fields.as_ref(); + + if let Some(new_fields) = new_fields { + if let Some(balance) = &new_fields.balance { + Ok(Some(get_value(&balance.clone().into())?)) + } else { + Ok(None) + } + } else if let (Some(previous_fields), Some(final_fields)) = (previous_fields, final_fields) { + if let (Some(prev_balance), Some(final_balance)) = + (&previous_fields.balance, &final_fields.balance) + { + let prev_value = get_value(&prev_balance.clone().into())?; + let final_value = get_value(&final_balance.clone().into())?; + + Ok(Some(final_value - prev_value)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} diff --git a/src/utils/txn_parser/mod.rs b/src/utils/txn_parser/mod.rs index 95adf51c..d4fcad89 100644 --- a/src/utils/txn_parser/mod.rs +++ b/src/utils/txn_parser/mod.rs @@ -1 +1,5 @@ +pub mod get_balance_changes; +pub mod get_final_balances; +pub mod get_final_order_book; +pub mod get_order_book_changes; mod utils; diff --git a/src/utils/txn_parser/utils/balance_parser.rs b/src/utils/txn_parser/utils/balance_parser.rs index c7f4a9ad..dd54846f 100644 --- a/src/utils/txn_parser/utils/balance_parser.rs +++ b/src/utils/txn_parser/utils/balance_parser.rs @@ -2,17 +2,23 @@ use core::str::FromStr; use alloc::{ string::{String, ToString}, - vec::{self, Vec}, + vec::Vec, }; use bigdecimal::{num_bigint::Sign, BigDecimal}; -use crate::utils::{ - drops_to_xrp, - exceptions::{XRPLTxnParserException, XRPLUtilsResult}, - txn_parser::utils::{negate, Balance}, +use crate::{ + models::transactions::metadata::TransactionMetadata, + utils::{ + drops_to_xrp, + exceptions::{XRPLTxnParserException, XRPLUtilsResult}, + txn_parser::utils::{negate, nodes::normalize_nodes, Balance}, + }, }; -use super::{nodes::NormalizedNode, AccountBalance}; +use super::{ + nodes::NormalizedNode, parser::group_balances_by_account as group_account_balances_by_account, + AccountBalance, AccountBalances, +}; fn get_xrp_quantity( node: NormalizedNode, @@ -65,9 +71,9 @@ fn get_xrp_quantity( } } -fn flip_trustline_perspective<'a: 'b, 'b>( - account_balance: &'a AccountBalance<'_>, -) -> XRPLUtilsResult> { +fn flip_trustline_perspective<'a>( + account_balance: &'a AccountBalance, +) -> XRPLUtilsResult> { let balance = account_balance.balance.clone(); let negated_value = negate(&BigDecimal::from_str(balance.value.as_ref())?); let issuer = balance.issuer.clone(); @@ -81,83 +87,36 @@ fn flip_trustline_perspective<'a: 'b, 'b>( }, }) } -fn get_trustline_quantity( - node: NormalizedNode, +fn get_trustline_quantity<'a>( + node: &NormalizedNode, value: Option, -) -> XRPLUtilsResult> { - if value.is_none() { - return Ok(Vec::new()); - } - let fields = if let Some(new_fields) = node.new_fields { - new_fields - } else if let Some(final_fields) = node.final_fields { - final_fields - } else { - return Ok(Vec::new()); - }; - let low_limit = fields.low_limit; - let balance = fields.balance; - let high_limit = fields.high_limit; - if let (Some(low_limit), Some(balance), Some(high_limit)) = (low_limit, balance, high_limit) { - let low_limit_issuer = low_limit.issuer; - let balance_currency = balance; - let high_limit_issuer = high_limit.issuer; - if let (Some(low_limit_issuer), Some(balance_currency), Some(high_limit_issuer)) = - (low_limit_issuer, balance_currency, high_limit_issuer) - { - let result = AccountBalance { - account: low_limit_issuer.to_string().into(), - balance: Balance { - currency: balance_currency.to_string().into(), - issuer: Some(high_limit_issuer.to_string().into()), - value: value.unwrap().normalized().to_string().into(), - }, - }; - return Ok(vec![result.clone(), flip_trustline_perspective(&result)?]); - } - } - Ok(Vec::new()) +) -> XRPLUtilsResult>> { + todo!() } -fn get_node_balances( - node: NormalizedNode, +fn get_node_balances<'a>( + node: &NormalizedNode, value: Option, -) -> XRPLUtilsResult> { - match node.ledger_entry_type.as_str() { - "AccountRoot" => { - if let Some(xrp_quantity) = get_xrp_quantity(node, value)? { - Ok(vec![xrp_quantity]) - } else { - Ok(Vec::new()) - } - } - "RippleState" => get_trustline_quantity(node, value), - _ => Ok(Vec::new()), - } +) -> XRPLUtilsResult>> { + todo!() } fn group_balances_by_account(account_balances: Vec) -> Vec { - let grouped_balances = group_by_account(account_balances); - let mut result = Vec::new(); - for (account, account_obj) in grouped_balances { - let balances: Vec = account_obj.into_iter().map(|ab| ab.balance).collect(); - result.push(AccountBalances { - account: account.to_string().into(), - balances, - }); - } - result + let grouped_balances = group_account_balances_by_account(account_balances.as_ref()); + let mut account_balances_grouped: Vec = Vec::new(); } -fn derive_account_balances( - metadata: TransactionMetadata, - parser: impl Fn(NormalizedNode) -> Option, -) -> XRPLUtilsResult> { - let mut quantities = Vec::new(); - for node in normalize_nodes(metadata) { - if let Some(value) = parser(node.clone()) { - quantities.extend(get_node_balances(node, Some(value))?); - } +pub fn derive_account_balances<'a>( + metadata: &TransactionMetadata, + parser_fn: impl Fn(&NormalizedNode) -> XRPLUtilsResult>, +) -> XRPLUtilsResult>> { + let mut account_balances: Vec = Vec::new(); + let nodes = normalize_nodes(metadata); + for node in nodes.into_iter() { + let value = parser_fn(&node)?; + let node_balances = get_node_balances(&node, value)?; + account_balances.extend(node_balances); } - Ok(group_balances_by_account(quantities)) + + Ok(group_balances_by_account(account_balances)) } diff --git a/src/utils/txn_parser/utils/parser.rs b/src/utils/txn_parser/utils/parser.rs index fc59593f..47352489 100644 --- a/src/utils/txn_parser/utils/parser.rs +++ b/src/utils/txn_parser/utils/parser.rs @@ -11,9 +11,8 @@ pub fn get_value(balance: &Balance) -> XRPLUtilsResult { Ok(BigDecimal::from_str(balance.value.as_ref())?) } -pub fn group_by_account<'a: 'b, 'b>( +pub fn group_balances_by_account<'a: 'b, 'b>( account_balances: Vec<&'a AccountBalance<'_>>, - account_offer_changes: Vec<&'a AccountOfferChange<'_>>, ) -> Vec> { let mut account_object_groups: Vec> = Vec::new(); @@ -38,7 +37,15 @@ pub fn group_by_account<'a: 'b, 'b>( } } - for offer_change in account_offer_changes.iter() { + account_object_groups +} + +pub fn group_offers_by_account( + account_offer_changes: Vec, +) -> Vec { + let mut account_object_groups = Vec::new(); + + for offer_change in account_offer_changes.into_iter() { // Find the account object group with the same account. If it doesn't exist, create a new one. let account_object_group = account_object_groups .iter_mut() From a230d8721d8d8cea497665b0a29f716514d084e4 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 4 Jan 2025 23:10:56 +0000 Subject: [PATCH 07/26] add balance change parsing --- src/models/transactions/metadata.rs | 5 +- src/utils/txn_parser/get_balance_changes.rs | 29 ++++- .../txn_parser/test_data/offer_created.json | 93 +++++++++++++ .../txn_parser/test_data/payment_iou.json | 123 ++++++++++++++++++ src/utils/txn_parser/utils/balance_parser.rs | 92 +++++++++++-- src/utils/txn_parser/utils/mod.rs | 14 +- src/utils/txn_parser/utils/parser.rs | 12 +- 7 files changed, 338 insertions(+), 30 deletions(-) create mode 100644 src/utils/txn_parser/test_data/offer_created.json create mode 100644 src/utils/txn_parser/test_data/payment_iou.json diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index 2628298f..fe7ace8d 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -1,4 +1,3 @@ -use alloc::collections::BTreeMap; use alloc::{borrow::Cow, vec::Vec}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -32,14 +31,14 @@ pub struct Fields<'a> { pub balance: Option>, pub book_directory: Option>, pub expiration: Option, - pub flags: u32, + pub flags: Option, pub low_limit: Option>, pub high_limit: Option>, pub next_page_min: Option>, #[serde(rename = "NFTokens")] pub nftokens: Option>>, pub previous_page_min: Option>, - pub sequence: u32, + pub sequence: Option, pub taker_gets: Option>, pub taker_pays: Option>, pub xchain_claim_id: Option>, diff --git a/src/utils/txn_parser/get_balance_changes.rs b/src/utils/txn_parser/get_balance_changes.rs index 66a1cd14..90938bbc 100644 --- a/src/utils/txn_parser/get_balance_changes.rs +++ b/src/utils/txn_parser/get_balance_changes.rs @@ -4,7 +4,7 @@ use bigdecimal::BigDecimal; use crate::{ models::transactions::metadata::TransactionMetadata, utils::{ - exceptions::{XRPLUtilsException, XRPLUtilsResult}, + exceptions::XRPLUtilsResult, txn_parser::utils::parser::get_value, }, }; @@ -46,3 +46,30 @@ fn compute_balance_change(node: &NormalizedNode) -> XRPLUtilsResult = LazyCell::new(|| { + let txn_value: Value = + serde_json::from_str(include_str!("./test_data/payment_iou.json")).unwrap(); + dbg!(&txn_value); + let txn_meta = txn_value["meta"].clone(); + dbg!(&txn_meta); + let txn_meta: TransactionMetadata = serde_json::from_value(txn_meta).unwrap(); + + txn_meta + }); + dbg!(get_balance_changes(&txn).unwrap()); + } +} diff --git a/src/utils/txn_parser/test_data/offer_created.json b/src/utils/txn_parser/test_data/offer_created.json new file mode 100644 index 00000000..75e5eade --- /dev/null +++ b/src/utils/txn_parser/test_data/offer_created.json @@ -0,0 +1,93 @@ +{ + "Account": "rJHbqhp9Sea4f43RoUanrDE1gW9MymTLp9", + "Expiration": 740218424, + "Fee": "25", + "Flags": 2148007936, + "LastLedgerSequence": 72374322, + "Sequence": 71307620, + "SigningPubKey": "020AF1B1419DB3C80FBB3B2621315F78F03806118094140B6FE535ED809561AC23", + "TakerGets": "44930000", + "TakerPays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "value": "14.524821" + }, + "TransactionType": "OfferCreate", + "TxnSignature": "3045022100C1CA7C960C1BD0B2EA9ACCA13BB429C1C5B558AB2B8AFE9FE3815DB075E07806022030152DC46694A3BF8BA1509944F9388723CB30C678E659613D4B3BF8166CCCA5", + "date": 708682430, + "hash": "463E28521ACA7FB5F4081E7E368FA5AEB76FB641FCB3C92CA9E8971A990CE84A", + "inLedger": 72374321, + "ledger_index": 72374321, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Flags": 0, + "Owner": "rJHbqhp9Sea4f43RoUanrDE1gW9MymTLp9", + "RootIndex": "3418F55643869450792F7047DC92DD661D38E68AC827C378D7C12FE8E017DD2B" + }, + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "3418F55643869450792F7047DC92DD661D38E68AC827C378D7C12FE8E017DD2B" + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rJHbqhp9Sea4f43RoUanrDE1gW9MymTLp9", + "Balance": "69932774", + "Flags": 0, + "OwnerCount": 3, + "Sequence": 71307621 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "72D49D4E2ECA5153A90413AA4BCEFBFBE748A2B66A96F5E5611089C095BD666D", + "PreviousFields": { + "Balance": "69932799", + "OwnerCount": 2, + "Sequence": 71307620 + }, + "PreviousTxnID": "70C2D1F863FBF18CA7E9B17D7B35A19BD5ED22C8B703D447A21C746E1F66F311", + "PreviousTxnLgrSeq": 72374313 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "ExchangeRate": "4e0b7c2f29ac3197", + "Flags": 0, + "RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E0B7C2F29AC3197", + "TakerGetsCurrency": "0000000000000000000000000000000000000000", + "TakerGetsIssuer": "0000000000000000000000000000000000000000", + "TakerPaysCurrency": "0000000000000000000000005553440000000000", + "TakerPaysIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1" + }, + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E0B7C2F29AC3197" + } + }, + { + "CreatedNode": { + "LedgerEntryType": "Offer", + "LedgerIndex": "DFA50F8A0710C8483D5DEF31E87BFA5DBC617F16045EC187EB21A07B7A2B23DA", + "NewFields": { + "Account": "rJHbqhp9Sea4f43RoUanrDE1gW9MymTLp9", + "BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E0B7C2F29AC3197", + "Expiration": 740218424, + "Flags": 131072, + "Sequence": 71307620, + "TakerGets": "44930000", + "TakerPays": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "value": "14.524821" + } + } + } + } + ], + "TransactionIndex": 37, + "TransactionResult": "tesSUCCESS" + }, + "validated": true +} \ No newline at end of file diff --git a/src/utils/txn_parser/test_data/payment_iou.json b/src/utils/txn_parser/test_data/payment_iou.json new file mode 100644 index 00000000..0721596b --- /dev/null +++ b/src/utils/txn_parser/test_data/payment_iou.json @@ -0,0 +1,123 @@ +{ + "engine_result": "tesSUCCESS", + "engine_result_code": 0, + "engine_result_message": "The transaction was applied.", + "ledger_hash": "F3F1416BF2E813396AB01FAA38E9F1023AC4D2368D94B0D52B2BC603CEEC01C3", + "ledger_index": 10459371, + "status": "closed", + "type": "transaction", + "validated": true, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.525330905250352" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "1.535330905250352" + } + }, + "PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD", + "PreviousTxnLgrSeq": 10459364 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.02" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0" + }, + "HighNode": "00000000000001E8", + "LowLimit": { + "currency": "USD", + "issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "value": "1000000000" + }, + "LowNode": "0000000000000000" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0.01" + } + }, + "PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD", + "PreviousTxnLgrSeq": 10459364 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Balance": "239555992", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 38 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "E9A39B0BA8703D5FFD05D9EAD01EE6C0E7A15CF33C2C6B7269107BD2BD535818", + "PreviousFields": { + "Balance": "239567992", + "Sequence": 37 + }, + "PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD", + "PreviousTxnLgrSeq": 10459364 + } + } + ], + "TransactionIndex": 2, + "TransactionResult": "tesSUCCESS" + }, + "transaction": { + "Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc", + "Amount": { + "currency": "USD", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "value": "0.01" + }, + "Destination": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K", + "Fee": "12000", + "Flags": 2147483648, + "LastLedgerSequence": 10459379, + "Sequence": 37, + "SigningPubKey": "03F16A52EBDCA6EBF5D99828E1E6918C64D45E6F136476A8F4757512FE553D18F0", + "TransactionType": "Payment", + "TxnSignature": "3044022031D6AB55CDFD17E06DA0BAD6D6B7DC9B5CA8FFF50405F2FCD3ED8D3893B1835E02200524CC1E7D70AE3F00C9F94405C55EE179C363F534905168AE8B5BA01CF568A0", + "date": 471644150, + "hash": "34671C179737CC89E0F8BBAA83C313885ED1733488FC0F3088BAE16A3D9A5B1B" + } +} \ No newline at end of file diff --git a/src/utils/txn_parser/utils/balance_parser.rs b/src/utils/txn_parser/utils/balance_parser.rs index dd54846f..ced14a2c 100644 --- a/src/utils/txn_parser/utils/balance_parser.rs +++ b/src/utils/txn_parser/utils/balance_parser.rs @@ -1,13 +1,16 @@ use core::str::FromStr; use alloc::{ + format, string::{String, ToString}, vec::Vec, }; use bigdecimal::{num_bigint::Sign, BigDecimal}; use crate::{ - models::transactions::metadata::TransactionMetadata, + models::{ + ledger::objects::LedgerEntryType, transactions::metadata::TransactionMetadata, Amount, + }, utils::{ drops_to_xrp, exceptions::{XRPLTxnParserException, XRPLUtilsResult}, @@ -26,7 +29,7 @@ fn get_xrp_quantity( ) -> XRPLUtilsResult> { if let Some(value) = value { let absulte_value = value.clone().abs(); - let xrp_value = if value.sign() != Sign::Minus { + let xrp_value = if value.sign() == Sign::Plus { let xrp_quantity = drops_to_xrp(absulte_value.to_string().as_str())?; let mut negated_value_string = String::from("-"); negated_value_string.push_str(&xrp_quantity); @@ -34,8 +37,10 @@ fn get_xrp_quantity( BigDecimal::from_str(&negated_value_string)? } else { let xrp_value = drops_to_xrp(absulte_value.to_string().as_str())?; + let dec = BigDecimal::from_str(&xrp_value)?; + let negated_xrp_value = negate(&dec); - BigDecimal::from_str(&xrp_value)? + negated_xrp_value }; if let Some(final_fields) = node.final_fields { if let Some(account) = final_fields.account { @@ -72,7 +77,7 @@ fn get_xrp_quantity( } fn flip_trustline_perspective<'a>( - account_balance: &'a AccountBalance, + account_balance: AccountBalance<'a>, ) -> XRPLUtilsResult> { let balance = account_balance.balance.clone(); let negated_value = negate(&BigDecimal::from_str(balance.value.as_ref())?); @@ -88,33 +93,94 @@ fn flip_trustline_perspective<'a>( }) } fn get_trustline_quantity<'a>( - node: &NormalizedNode, + node: NormalizedNode, value: Option, ) -> XRPLUtilsResult>> { - todo!() + if value.is_none() { + return Ok(Vec::new()); + } + + let fields = if let Some(new_fields) = node.new_fields { + new_fields + } else if let Some(final_fields) = node.final_fields { + final_fields + } else { + return Ok(Vec::new()); + }; + + let low_limit = fields.low_limit.as_ref(); + let balance = fields.balance.as_ref(); + let high_limit = fields.high_limit.as_ref(); + + if let (Some(low_limit), Some(balance), Some(high_limit)) = (low_limit, balance, high_limit) { + let low_limit_issuer = low_limit.issuer.as_ref(); + let balance_currency = match balance { + Amount::IssuedCurrencyAmount(ic) => Some(ic.currency.as_ref()), + _ => Some("XRP"), + }; + let high_limit_issuer = high_limit.issuer.as_ref(); + + if let Some(balance_currency) = balance_currency { + let result = AccountBalance { + account: low_limit_issuer.to_string().into(), + balance: Balance { + currency: balance_currency.to_string().into(), + issuer: Some(high_limit_issuer.to_string().into()), + value: format!("{}", value.unwrap().normalized()).into(), + }, + }; + return Ok([result.clone(), flip_trustline_perspective(result)?].into()); + } + } + + Ok(Vec::new()) } -fn get_node_balances<'a>( - node: &NormalizedNode, +fn get_node_balances<'a: 'b, 'b>( + node: NormalizedNode<'a>, value: Option, -) -> XRPLUtilsResult>> { - todo!() +) -> XRPLUtilsResult>> { + if node.ledger_entry_type == LedgerEntryType::AccountRoot { + let xrp_quantity = get_xrp_quantity(node, value)?; + if let Some(xrp_quantity) = xrp_quantity { + Ok([xrp_quantity].into()) + } else { + Ok(Vec::new()) + } + } else if node.ledger_entry_type == LedgerEntryType::RippleState { + let trustline_quantities = get_trustline_quantity(node, value)?; + Ok(trustline_quantities) + } else { + Ok(Vec::new()) + } } fn group_balances_by_account(account_balances: Vec) -> Vec { - let grouped_balances = group_account_balances_by_account(account_balances.as_ref()); + let grouped_balances = group_account_balances_by_account(account_balances); let mut account_balances_grouped: Vec = Vec::new(); + + for group in grouped_balances.into_iter() { + let account = group.account.clone(); + let balances = group + .account_balances + .into_iter() + .map(|balance| balance.balance) + .collect(); + account_balances_grouped.push(AccountBalances { account, balances }); + } + + account_balances_grouped } pub fn derive_account_balances<'a>( - metadata: &TransactionMetadata, + metadata: &'a TransactionMetadata, parser_fn: impl Fn(&NormalizedNode) -> XRPLUtilsResult>, ) -> XRPLUtilsResult>> { let mut account_balances: Vec = Vec::new(); let nodes = normalize_nodes(metadata); for node in nodes.into_iter() { let value = parser_fn(&node)?; - let node_balances = get_node_balances(&node, value)?; + let node_balances = get_node_balances(node, value)?; account_balances.extend(node_balances); } diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs index f7f30bf9..815038bf 100644 --- a/src/utils/txn_parser/utils/mod.rs +++ b/src/utils/txn_parser/utils/mod.rs @@ -2,7 +2,8 @@ use core::str::FromStr; use alloc::{ borrow::Cow, - string::{String, ToString}, + dbg, + string::ToString, vec::Vec, }; use bigdecimal::BigDecimal; @@ -84,13 +85,14 @@ pub struct AccountOfferChanges<'a> { #[derive(Debug, Clone)] pub struct AccountObjectGroup<'a> { pub account: Cow<'a, str>, - pub account_balances: Vec<&'a AccountBalance<'a>>, - pub account_offer_changes: Vec<&'a AccountOfferChange<'a>>, + pub account_balances: Vec>, + pub account_offer_changes: Vec>, } pub fn negate(value: &BigDecimal) -> BigDecimal { - let mut negated_value_string = String::from("-"); - negated_value_string.push_str(&value.to_string()); + let zero = BigDecimal::from_str("0").unwrap(); + let working_value = zero - value; + dbg!(working_value.clone()); - BigDecimal::from_str(&negated_value_string).unwrap() + BigDecimal::from_str(&working_value.to_string()).unwrap() } diff --git a/src/utils/txn_parser/utils/parser.rs b/src/utils/txn_parser/utils/parser.rs index 47352489..efc2f286 100644 --- a/src/utils/txn_parser/utils/parser.rs +++ b/src/utils/txn_parser/utils/parser.rs @@ -11,10 +11,8 @@ pub fn get_value(balance: &Balance) -> XRPLUtilsResult { Ok(BigDecimal::from_str(balance.value.as_ref())?) } -pub fn group_balances_by_account<'a: 'b, 'b>( - account_balances: Vec<&'a AccountBalance<'_>>, -) -> Vec> { - let mut account_object_groups: Vec> = Vec::new(); +pub fn group_balances_by_account(account_balances: Vec) -> Vec { + let mut account_object_groups: Vec = Vec::new(); for balance in account_balances.iter() { // Find the account object group with the same account. If it doesn't exist, create a new one. @@ -22,7 +20,7 @@ pub fn group_balances_by_account<'a: 'b, 'b>( .iter_mut() .find(|group| group.account == balance.account.as_ref()); if let Some(group) = account_object_group { - group.account_balances.push(balance); + group.account_balances.push(balance.clone()); } else { account_object_groups.push(AccountObjectGroup { account: balance.account.clone(), @@ -33,7 +31,7 @@ pub fn group_balances_by_account<'a: 'b, 'b>( .last_mut() .unwrap() .account_balances - .push(balance); + .push(balance.clone()); } } @@ -43,7 +41,7 @@ pub fn group_balances_by_account<'a: 'b, 'b>( pub fn group_offers_by_account( account_offer_changes: Vec, ) -> Vec { - let mut account_object_groups = Vec::new(); + let mut account_object_groups: Vec> = Vec::new(); for offer_change in account_offer_changes.into_iter() { // Find the account object group with the same account. If it doesn't exist, create a new one. From 99a25ae2dce851ad1da4695b598be12d3e57c1b8 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 4 Jan 2025 23:35:55 +0000 Subject: [PATCH 08/26] fix clippy --- src/models/results/account_info.rs | 2 +- src/models/results/mod.rs | 12 ++++++++---- src/utils/str_conversion.rs | 9 +++------ src/utils/txn_parser/get_balance_changes.rs | 8 ++------ src/utils/txn_parser/utils/balance_parser.rs | 8 ++------ src/utils/txn_parser/utils/order_book_parser.rs | 2 +- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/models/results/account_info.rs b/src/models/results/account_info.rs index 8de5edef..bc5fb0e3 100644 --- a/src/models/results/account_info.rs +++ b/src/models/results/account_info.rs @@ -126,7 +126,7 @@ impl<'a> TryFrom> for AccountInfoVersionMap<'a> { fn try_from(result: XRPLResult<'a>) -> XRPLModelResult { match result { - XRPLResult::AccountInfo(account_info) => Ok(account_info), + XRPLResult::AccountInfo(account_info) => Ok(*account_info), res => Err(XRPLResultException::UnexpectedResultType( "AccountInfo".to_string(), res.get_name(), diff --git a/src/models/results/mod.rs b/src/models/results/mod.rs index b9c246ad..c3033643 100644 --- a/src/models/results/mod.rs +++ b/src/models/results/mod.rs @@ -86,7 +86,7 @@ impl XRPLOtherResult { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(untagged)] pub enum XRPLResult<'a> { - AccountInfo(account_info::AccountInfoVersionMap<'a>), + AccountInfo(Box>), // Boxed because AccountInfo is large AccountTx(account_tx::AccountTxVersionMap<'a>), Fee(fee::Fee<'a>), Ledger(ledger::Ledger<'a>), @@ -104,19 +104,23 @@ pub enum XRPLResult<'a> { impl<'a> From> for XRPLResult<'a> { fn from(account_info: account_info::AccountInfo<'a>) -> Self { - XRPLResult::AccountInfo(account_info::AccountInfoVersionMap::Default(account_info)) + XRPLResult::AccountInfo(Box::new(account_info::AccountInfoVersionMap::Default( + account_info, + ))) } } impl<'a> From> for XRPLResult<'a> { fn from(account_info: account_info::AccountInfoV1<'a>) -> Self { - XRPLResult::AccountInfo(account_info::AccountInfoVersionMap::V1(account_info)) + XRPLResult::AccountInfo(Box::new(account_info::AccountInfoVersionMap::V1( + account_info, + ))) } } impl<'a> From> for XRPLResult<'a> { fn from(account_info: account_info::AccountInfoVersionMap<'a>) -> Self { - XRPLResult::AccountInfo(account_info) + XRPLResult::AccountInfo(Box::new(account_info)) } } diff --git a/src/utils/str_conversion.rs b/src/utils/str_conversion.rs index f1f1c291..41d95b4c 100644 --- a/src/utils/str_conversion.rs +++ b/src/utils/str_conversion.rs @@ -1,4 +1,4 @@ -use alloc::{borrow::Cow, format, string::String}; +use alloc::{borrow::Cow, string::String}; use super::exceptions::XRPLUtilsResult; @@ -6,11 +6,8 @@ use super::exceptions::XRPLUtilsResult; /// XRPL uses hex strings as inputs in fields like `domain` /// in the `AccountSet` transaction. pub fn str_to_hex<'a: 'b, 'b>(value: Cow<'a, str>) -> XRPLUtilsResult> { - let hex_string = value - .as_bytes() - .iter() - .map(|b| format!("{:02x}", b)) - .collect::(); + let hex_string = hex::encode(value.as_bytes()); + Ok(Cow::Owned(hex_string)) } diff --git a/src/utils/txn_parser/get_balance_changes.rs b/src/utils/txn_parser/get_balance_changes.rs index 90938bbc..fe76127f 100644 --- a/src/utils/txn_parser/get_balance_changes.rs +++ b/src/utils/txn_parser/get_balance_changes.rs @@ -3,10 +3,7 @@ use bigdecimal::BigDecimal; use crate::{ models::transactions::metadata::TransactionMetadata, - utils::{ - exceptions::XRPLUtilsResult, - txn_parser::utils::parser::get_value, - }, + utils::{exceptions::XRPLUtilsResult, txn_parser::utils::parser::get_value}, }; use super::utils::{ @@ -16,7 +13,7 @@ use super::utils::{ pub fn get_balance_changes<'a: 'b, 'b>( meta: &'a TransactionMetadata<'a>, ) -> XRPLUtilsResult>> { - Ok(derive_account_balances(meta, compute_balance_change)?) + derive_account_balances(meta, compute_balance_change) } /// Get the balance change from a node. @@ -56,7 +53,6 @@ mod test { use super::*; use crate::models::transactions::metadata::TransactionMetadata; - #[test] fn test_parse_balance_changes() { diff --git a/src/utils/txn_parser/utils/balance_parser.rs b/src/utils/txn_parser/utils/balance_parser.rs index ced14a2c..92776cb2 100644 --- a/src/utils/txn_parser/utils/balance_parser.rs +++ b/src/utils/txn_parser/utils/balance_parser.rs @@ -38,9 +38,7 @@ fn get_xrp_quantity( } else { let xrp_value = drops_to_xrp(absulte_value.to_string().as_str())?; let dec = BigDecimal::from_str(&xrp_value)?; - let negated_xrp_value = negate(&dec); - - negated_xrp_value + negate(&dec) }; if let Some(final_fields) = node.final_fields { if let Some(account) = final_fields.account { @@ -76,9 +74,7 @@ fn get_xrp_quantity( } } -fn flip_trustline_perspective<'a>( - account_balance: AccountBalance<'a>, -) -> XRPLUtilsResult> { +fn flip_trustline_perspective(account_balance: AccountBalance) -> XRPLUtilsResult { let balance = account_balance.balance.clone(); let negated_value = negate(&BigDecimal::from_str(balance.value.as_ref())?); let issuer = balance.issuer.clone(); diff --git a/src/utils/txn_parser/utils/order_book_parser.rs b/src/utils/txn_parser/utils/order_book_parser.rs index 77fa2328..3adbeff0 100644 --- a/src/utils/txn_parser/utils/order_book_parser.rs +++ b/src/utils/txn_parser/utils/order_book_parser.rs @@ -9,7 +9,7 @@ fn get_offer_status(node: &NormalizedNode<'_>) -> OfferStatus { NodeType::CreatedNode => OfferStatus::Created, NodeType::ModifiedNode => OfferStatus::PartiallyFilled, NodeType::DeletedNode => { - if let Some(_) = node.previous_fields { + if node.previous_fields.is_some() { // a filled offer has previous fields OfferStatus::Filled } else { From eb24b2fd975e9f1607f6196bc6b655373f5cd357 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 4 Jan 2025 23:39:27 +0000 Subject: [PATCH 09/26] remove dbg! to fix gh workflow --- src/utils/txn_parser/utils/mod.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs index 815038bf..ccb5b6d8 100644 --- a/src/utils/txn_parser/utils/mod.rs +++ b/src/utils/txn_parser/utils/mod.rs @@ -1,11 +1,6 @@ use core::str::FromStr; -use alloc::{ - borrow::Cow, - dbg, - string::ToString, - vec::Vec, -}; +use alloc::{borrow::Cow, string::ToString, vec::Vec}; use bigdecimal::BigDecimal; use crate::models::{transactions::offer_create::OfferCreateFlag, Amount, FlagCollection}; @@ -92,7 +87,6 @@ pub struct AccountObjectGroup<'a> { pub fn negate(value: &BigDecimal) -> BigDecimal { let zero = BigDecimal::from_str("0").unwrap(); let working_value = zero - value; - dbg!(working_value.clone()); BigDecimal::from_str(&working_value.to_string()).unwrap() } From 4373cfda5e2e307d0c51e5be3e5b2b7e63bbabf9 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 4 Jan 2025 23:56:32 +0000 Subject: [PATCH 10/26] fix gh workflow --- src/utils/mod.rs | 2 ++ src/utils/txn_parser/utils/balance_parser.rs | 6 +++--- src/utils/txn_parser/utils/mod.rs | 11 +++++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ea7ee373..403c4869 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,6 +3,7 @@ pub mod exceptions; #[cfg(feature = "models")] pub mod get_nftoken_id; +#[cfg(feature = "models")] pub mod get_xchain_claim_id; #[cfg(feature = "models")] pub mod parse_nftoken_id; @@ -10,6 +11,7 @@ pub mod str_conversion; pub mod time_conversion; #[cfg(feature = "models")] pub(crate) mod transactions; +#[cfg(feature = "models")] pub mod txn_parser; pub mod xrpl_conversion; diff --git a/src/utils/txn_parser/utils/balance_parser.rs b/src/utils/txn_parser/utils/balance_parser.rs index 92776cb2..89ae94cb 100644 --- a/src/utils/txn_parser/utils/balance_parser.rs +++ b/src/utils/txn_parser/utils/balance_parser.rs @@ -38,7 +38,7 @@ fn get_xrp_quantity( } else { let xrp_value = drops_to_xrp(absulte_value.to_string().as_str())?; let dec = BigDecimal::from_str(&xrp_value)?; - negate(&dec) + negate(&dec)? }; if let Some(final_fields) = node.final_fields { if let Some(account) = final_fields.account { @@ -76,7 +76,7 @@ fn get_xrp_quantity( fn flip_trustline_perspective(account_balance: AccountBalance) -> XRPLUtilsResult { let balance = account_balance.balance.clone(); - let negated_value = negate(&BigDecimal::from_str(balance.value.as_ref())?); + let negated_value = negate(&BigDecimal::from_str(balance.value.as_ref())?)?; let issuer = balance.issuer.clone(); Ok(AccountBalance { @@ -122,7 +122,7 @@ fn get_trustline_quantity<'a>( balance: Balance { currency: balance_currency.to_string().into(), issuer: Some(high_limit_issuer.to_string().into()), - value: format!("{}", value.unwrap().normalized()).into(), + value: format!("{}", value.unwrap().normalized()).into(), // safe to unwrap because we checked for None above }, }; return Ok([result.clone(), flip_trustline_perspective(result)?].into()); diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs index ccb5b6d8..7d446caf 100644 --- a/src/utils/txn_parser/utils/mod.rs +++ b/src/utils/txn_parser/utils/mod.rs @@ -3,7 +3,10 @@ use core::str::FromStr; use alloc::{borrow::Cow, string::ToString, vec::Vec}; use bigdecimal::BigDecimal; -use crate::models::{transactions::offer_create::OfferCreateFlag, Amount, FlagCollection}; +use crate::{ + models::{transactions::offer_create::OfferCreateFlag, Amount, FlagCollection}, + utils::exceptions::XRPLUtilsResult, +}; pub mod balance_parser; pub mod nodes; @@ -84,9 +87,9 @@ pub struct AccountObjectGroup<'a> { pub account_offer_changes: Vec>, } -pub fn negate(value: &BigDecimal) -> BigDecimal { - let zero = BigDecimal::from_str("0").unwrap(); +pub fn negate(value: &BigDecimal) -> XRPLUtilsResult { + let zero = BigDecimal::from_str("0")?; let working_value = zero - value; - BigDecimal::from_str(&working_value.to_string()).unwrap() + Ok(BigDecimal::from_str(&working_value.to_string())?) } From ae280c78f1b09db21b5de54ebec496d5051d1ef7 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sun, 5 Jan 2025 00:13:59 +0000 Subject: [PATCH 11/26] add final balance parsing and fix gh workflow --- src/utils/txn_parser/get_balance_changes.rs | 5 +- src/utils/txn_parser/get_final_balances.rs | 55 +++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/utils/txn_parser/get_balance_changes.rs b/src/utils/txn_parser/get_balance_changes.rs index fe76127f..3f3d85c4 100644 --- a/src/utils/txn_parser/get_balance_changes.rs +++ b/src/utils/txn_parser/get_balance_changes.rs @@ -48,7 +48,6 @@ fn compute_balance_change(node: &NormalizedNode) -> XRPLUtilsResult = LazyCell::new(|| { let txn_value: Value = serde_json::from_str(include_str!("./test_data/payment_iou.json")).unwrap(); - dbg!(&txn_value); let txn_meta = txn_value["meta"].clone(); - dbg!(&txn_meta); let txn_meta: TransactionMetadata = serde_json::from_value(txn_meta).unwrap(); txn_meta }); - dbg!(get_balance_changes(&txn).unwrap()); + assert!(get_balance_changes(&txn).is_ok()); } } diff --git a/src/utils/txn_parser/get_final_balances.rs b/src/utils/txn_parser/get_final_balances.rs index e69de29b..f21ac15f 100644 --- a/src/utils/txn_parser/get_final_balances.rs +++ b/src/utils/txn_parser/get_final_balances.rs @@ -0,0 +1,55 @@ +use super::utils::{ + balance_parser::derive_account_balances, nodes::NormalizedNode, parser::get_value, + AccountBalances, +}; +use crate::{ + models::transactions::metadata::TransactionMetadata, utils::exceptions::XRPLUtilsResult, +}; +use alloc::vec::Vec; +use bigdecimal::BigDecimal; + +pub fn get_final_balances<'a: 'b, 'b>( + metadata: &'a TransactionMetadata<'a>, +) -> XRPLUtilsResult>> { + derive_account_balances(metadata, compute_final_balance) +} + +fn compute_final_balance(node: &NormalizedNode) -> XRPLUtilsResult> { + let mut value: BigDecimal = BigDecimal::from(0); + if let Some(new_fields) = &node.new_fields { + if let Some(balance) = &new_fields.balance { + value = get_value(&balance.clone().into())?; + } + } else if let Some(final_fields) = &node.final_fields { + if let Some(balance) = &final_fields.balance { + value = get_value(&balance.clone().into())?; + } + } + if value == BigDecimal::from(0) { + return Ok(None); + } + Ok(Some(value)) +} + +#[cfg(test)] +mod test { + use core::cell::LazyCell; + + use serde_json::Value; + + use super::*; + use crate::models::transactions::metadata::TransactionMetadata; + + #[test] + fn test_parse_final_balances() { + let txn: LazyCell = LazyCell::new(|| { + let txn_value: Value = + serde_json::from_str(include_str!("./test_data/payment_iou.json")).unwrap(); + let txn_meta = txn_value["meta"].clone(); + let txn_meta: TransactionMetadata = serde_json::from_value(txn_meta).unwrap(); + + txn_meta + }); + assert!(get_final_balances(&txn).is_ok()); + } +} From 62599063e426b2d073de4169dc9d835091a86ee3 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 9 Jan 2025 17:09:18 +0000 Subject: [PATCH 12/26] current state of order book parsing --- .../txn_parser/get_order_book_changes.rs | 37 ++++ src/utils/txn_parser/utils/mod.rs | 14 ++ src/utils/txn_parser/utils/nodes.rs | 1 + .../txn_parser/utils/order_book_parser.rs | 183 +++++++++++++++++- 4 files changed, 233 insertions(+), 2 deletions(-) diff --git a/src/utils/txn_parser/get_order_book_changes.rs b/src/utils/txn_parser/get_order_book_changes.rs index e69de29b..bef9a4ee 100644 --- a/src/utils/txn_parser/get_order_book_changes.rs +++ b/src/utils/txn_parser/get_order_book_changes.rs @@ -0,0 +1,37 @@ +use alloc::vec::Vec; + +use crate::{ + models::transactions::metadata::TransactionMetadata, utils::exceptions::XRPLUtilsResult, +}; + +use super::utils::{order_book_parser::compute_order_book_changes, AccountOfferChanges}; + +pub fn get_order_book_changes<'a: 'b, 'b>( + meta: &'a TransactionMetadata<'a>, +) -> XRPLUtilsResult>> { + compute_order_book_changes(meta) +} + +#[cfg(test)] +mod test { + use core::cell::LazyCell; + + use serde_json::Value; + + use super::*; + + use crate::models::transactions::metadata::TransactionMetadata; + + #[test] + fn test_get_order_book_changes() { + let txn: LazyCell = LazyCell::new(|| { + let txn_value: Value = + serde_json::from_str(include_str!("./test_data/offer_created.json")).unwrap(); + let txn_meta = txn_value["meta"].clone(); + let txn_meta: TransactionMetadata = serde_json::from_value(txn_meta).unwrap(); + + txn_meta + }); + assert!(get_order_book_changes(&txn).is_ok()); + } +} diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs index 7d446caf..aff9137d 100644 --- a/src/utils/txn_parser/utils/mod.rs +++ b/src/utils/txn_parser/utils/mod.rs @@ -37,6 +37,20 @@ impl<'a: 'b, 'b> From> for Balance<'b> { } } +impl<'a> Into> for Balance<'a> { + fn into(self) -> Amount<'a> { + if self.currency == "XRP" { + Amount::XRPAmount(self.value.into()) + } else { + Amount::IssuedCurrencyAmount(crate::models::IssuedCurrencyAmount { + currency: self.currency, + value: self.value, + issuer: self.issuer.unwrap_or("".into()), + }) + } + } +} + #[derive(Debug, Clone)] pub struct AccountBalance<'a> { pub account: Cow<'a, str>, diff --git a/src/utils/txn_parser/utils/nodes.rs b/src/utils/txn_parser/utils/nodes.rs index cfc5848a..8c91692b 100644 --- a/src/utils/txn_parser/utils/nodes.rs +++ b/src/utils/txn_parser/utils/nodes.rs @@ -6,6 +6,7 @@ use crate::models::{ transactions::metadata::{AffectedNode, Fields, NodeType, TransactionMetadata}, }; +#[derive(Debug)] pub struct NormalizedNode<'a> { pub node_type: NodeType, pub ledger_entry_type: LedgerEntryType, diff --git a/src/utils/txn_parser/utils/order_book_parser.rs b/src/utils/txn_parser/utils/order_book_parser.rs index 3adbeff0..feb400e6 100644 --- a/src/utils/txn_parser/utils/order_book_parser.rs +++ b/src/utils/txn_parser/utils/order_book_parser.rs @@ -1,9 +1,28 @@ -use crate::models::{transactions::metadata::NodeType, Amount}; +use alloc::{string::ToString, vec::Vec}; +use bigdecimal::BigDecimal; -use super::{nodes::NormalizedNode, Balance, OfferStatus}; +use crate::{ + account, + models::{ + ledger::objects::LedgerEntryType, + transactions::metadata::{NodeType, TransactionMetadata}, + Amount, + }, + utils::{ + exceptions::XRPLUtilsResult, + txn_parser::utils::{nodes::normalize_nodes, OfferChange}, + }, +}; + +use super::{nodes::NormalizedNode, AccountOfferChange, AccountOfferChanges, Balance, OfferStatus}; const LSF_SELL: u32 = 0x00020000; +enum OfferSide { + TakerGets, + TakerPays, +} + fn get_offer_status(node: &NormalizedNode<'_>) -> OfferStatus { match node.node_type { NodeType::CreatedNode => OfferStatus::Created, @@ -19,6 +38,16 @@ fn get_offer_status(node: &NormalizedNode<'_>) -> OfferStatus { } } +fn calculate_delta( + previous_balance: &Balance, + final_balance: &Balance, +) -> XRPLUtilsResult { + let previous_value: BigDecimal = previous_balance.value.parse()?; + let final_value: BigDecimal = final_balance.value.parse()?; + + Ok(final_value - previous_value) +} + fn derive_currency_amount<'a: 'b, 'b>(currency_amount: &'a Amount) -> Balance<'b> { match currency_amount { Amount::XRPAmount(amount) => Balance { @@ -33,3 +62,153 @@ fn derive_currency_amount<'a: 'b, 'b>(currency_amount: &'a Amount) -> Balance<'b }, } } + +fn get_change_amount<'a: 'b, 'b>( + node: &'a NormalizedNode<'a>, + side: OfferSide, +) -> XRPLUtilsResult>> { + if let Some(new_fields) = &node.new_fields { + let amount = match side { + OfferSide::TakerGets => &new_fields.taker_gets, + OfferSide::TakerPays => &new_fields.taker_pays, + }; + if let Some(amount) = amount { + Ok(Some(derive_currency_amount(amount))) + } else { + Ok(None) + } + } else if let Some(final_fields) = &node.final_fields { + let final_fields_amount = match side { + OfferSide::TakerGets => &final_fields.taker_gets, + OfferSide::TakerPays => &final_fields.taker_pays, + }; + let previous_fields_amount = match side { + OfferSide::TakerGets => &node.previous_fields.as_ref().unwrap().taker_gets, + OfferSide::TakerPays => &node.previous_fields.as_ref().unwrap().taker_pays, + }; + if let (Some(final_fields_amount), Some(previous_fields_amount)) = + (final_fields_amount, previous_fields_amount) + { + let final_balance = derive_currency_amount(final_fields_amount); + let previous_balance = derive_currency_amount(previous_fields_amount); + let change = calculate_delta(&previous_balance, &final_balance)?; + Ok(Some(Balance { + currency: final_balance.currency, + value: change.to_string().into(), + issuer: final_balance.issuer, + })) + } else if let (Some(final_fields_amount), None) = + (final_fields_amount, previous_fields_amount) + { + let final_balance = derive_currency_amount(final_fields_amount); + let final_balance_value: BigDecimal = final_balance.value.parse()?; + let value: BigDecimal = 0 - final_balance_value; + Ok(Some(Balance { + currency: final_balance.currency, + value: value.to_string().into(), + issuer: final_balance.issuer, + })) + } else { + Ok(None) + } + } else { + Ok(None) + } +} + +fn get_quality(taker_gets: &Balance, taker_pays: &Balance) -> XRPLUtilsResult { + let taker_gets_value: BigDecimal = taker_gets.value.parse()?; + let taker_pays_value: BigDecimal = taker_pays.value.parse()?; + let quality = taker_pays_value / taker_gets_value; + + Ok(quality.normalized()) +} + +fn get_offer_change<'a: 'b, 'b>( + node: &'a NormalizedNode<'a>, +) -> XRPLUtilsResult>> { + let status = get_offer_status(node); + let taker_gets = get_change_amount(node, OfferSide::TakerGets)?; + let taker_pays = get_change_amount(node, OfferSide::TakerPays)?; + let account = if let Some(new_fields) = &node.new_fields { + new_fields.account.as_ref().map(|account| account) + } else if let Some(final_fields) = &node.final_fields { + final_fields.account.as_ref().map(|account| account) + } else { + None + }; + let sequence = if let Some(new_fields) = &node.new_fields { + new_fields.sequence + } else if let Some(final_fields) = &node.final_fields { + final_fields.sequence + } else { + None + }; + let flags = if let Some(new_fields) = &node.new_fields { + Some(new_fields.flags) + } else if let Some(final_fields) = &node.final_fields { + Some(final_fields.flags) + } else { + None + }; + if taker_gets.is_none() + || taker_pays.is_none() + || account.is_none() + || sequence.is_none() + || flags.is_none() + { + return Ok(None); + } + let taker_gets = taker_gets.unwrap(); + let taker_pays = taker_pays.unwrap(); + let account = account.unwrap(); + let sequence = sequence.unwrap(); + let flags = flags.unwrap(); + + let expiration_time = if let Some(new_fields) = &node.new_fields { + new_fields.expiration + } else if let Some(final_fields) = &node.final_fields { + final_fields.expiration + } else { + None + }; + let quality = get_quality(&taker_gets, &taker_pays)?; + let offer_change = OfferChange { + flags: flags.try_into()?, + taker_gets: taker_gets.into(), + taker_pays: taker_pays.into(), + sequence, + status, + maker_exchange_rate: Some(quality), + expiration_time, + }; + + Ok(Some(AccountOfferChange { + maker_account: account.clone(), + offer_change, + })) +} + +fn group_offer_changes_by_account<'a: 'b, 'b>( + account_offer_changes: Vec>, +) -> Vec> { + todo!() +} + +pub fn compute_order_book_changes<'a: 'b, 'b>( + meta: &'a TransactionMetadata<'a>, +) -> XRPLUtilsResult>> { + let normalized_nodes = normalize_nodes(meta); + let offer_nodes = normalized_nodes + .iter() + .filter(|node| node.ledger_entry_type == LedgerEntryType::Offer) + .collect::>(); + let mut offer_changes = Vec::new(); + for node in offer_nodes { + if let Some(offer_change) = get_offer_change(node)? { + offer_changes.push(offer_change); + } + } + + Ok(group_offer_changes_by_account(offer_changes)) +} From c02f0714f182611855e242c1e98faed24b5e2454 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Wed, 12 Feb 2025 14:16:53 +0000 Subject: [PATCH 13/26] fix multisigning --- .devcontainer/devcontainer.json | 10 +--- src/asynch/clients/json_rpc/mod.rs | 1 - src/core/binarycodec/mod.rs | 4 +- src/models/requests/submit_multisigned.rs | 8 ++- src/models/transactions/account_delete.rs | 2 +- src/models/transactions/account_set.rs | 2 +- src/models/transactions/amm_bid.rs | 2 +- src/models/transactions/amm_create.rs | 2 +- src/models/transactions/amm_delete.rs | 2 +- src/models/transactions/amm_deposit.rs | 2 +- src/models/transactions/amm_vote.rs | 2 +- src/models/transactions/amm_withdraw.rs | 2 +- src/models/transactions/check_cancel.rs | 2 +- src/models/transactions/check_cash.rs | 2 +- src/models/transactions/check_create.rs | 2 +- src/models/transactions/deposit_preauth.rs | 2 +- src/models/transactions/escrow_cancel.rs | 2 +- src/models/transactions/escrow_create.rs | 2 +- src/models/transactions/escrow_finish.rs | 2 +- src/models/transactions/mod.rs | 17 +++--- .../transactions/nftoken_accept_offer.rs | 2 +- src/models/transactions/nftoken_burn.rs | 2 +- .../transactions/nftoken_cancel_offer.rs | 2 +- .../transactions/nftoken_create_offer.rs | 2 +- src/models/transactions/nftoken_mint.rs | 2 +- src/models/transactions/offer_cancel.rs | 2 +- src/models/transactions/offer_create.rs | 2 +- src/models/transactions/payment.rs | 2 +- .../transactions/payment_channel_claim.rs | 2 +- .../transactions/payment_channel_create.rs | 2 +- .../transactions/payment_channel_fund.rs | 2 +- .../pseudo_transactions/enable_amendment.rs | 2 +- .../pseudo_transactions/set_fee.rs | 2 +- .../pseudo_transactions/unl_modify.rs | 2 +- src/models/transactions/set_regular_key.rs | 2 +- src/models/transactions/signer_list_set.rs | 2 +- src/models/transactions/ticket_create.rs | 2 +- src/models/transactions/trust_set.rs | 2 +- .../xchain_account_create_commit.rs | 2 +- .../xchain_add_account_create_attestation.rs | 2 +- .../xchain_add_claim_attestation.rs | 2 +- src/models/transactions/xchain_claim.rs | 2 +- src/models/transactions/xchain_commit.rs | 2 +- .../transactions/xchain_create_bridge.rs | 2 +- .../transactions/xchain_create_claim_id.rs | 2 +- .../transactions/xchain_modify_bridge.rs | 2 +- src/transaction/multisign.rs | 55 +++++++++++++++---- 47 files changed, 105 insertions(+), 72 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9cf3eac7..5f8f265e 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,13 +6,9 @@ "service": "app", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", // Use 'mounts' to make the cargo cache persistent in a Docker Volume. - // "mounts": [ - // { - // "source": "devcontainer-cargo-cache-${devcontainerId}", - // "target": "/usr/local/cargo", - // "type": "volume" - // } - // ] + "mounts": [ + "source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached" + ], // Use 'postCreateCommand' to run commands after the container is created. "postStartCommand": "rustup --version && rustup component add rustfmt && rustup component add clippy", // Configure tool-specific properties. diff --git a/src/asynch/clients/json_rpc/mod.rs b/src/asynch/clients/json_rpc/mod.rs index 9c5ac0ea..be3d8b9b 100644 --- a/src/asynch/clients/json_rpc/mod.rs +++ b/src/asynch/clients/json_rpc/mod.rs @@ -1,4 +1,3 @@ - use alloc::{string::ToString, vec}; use serde::Serialize; use serde_json::{Map, Value}; diff --git a/src/core/binarycodec/mod.rs b/src/core/binarycodec/mod.rs index e85e38c4..820c0557 100644 --- a/src/core/binarycodec/mod.rs +++ b/src/core/binarycodec/mod.rs @@ -23,7 +23,7 @@ use crate::XRPLSerdeJsonError; use super::exceptions::XRPLCoreResult; const TRANSACTION_SIGNATURE_PREFIX: i32 = 0x53545800; -const TRANSACTION_MULTISIG_PREFIX: i32 = 0x534D5400; +const TRANSACTION_MULTISIG_PREFIX: [u8; 4] = (0x534D5400u32).to_be_bytes(); pub fn encode(signed_transaction: &T) -> XRPLCoreResult where @@ -55,7 +55,7 @@ where serialize_json( prepared_transaction, - Some(TRANSACTION_MULTISIG_PREFIX.to_be_bytes().as_ref()), + Some(TRANSACTION_MULTISIG_PREFIX.as_ref()), Some(signing_account_id.as_ref()), true, ) diff --git a/src/models/requests/submit_multisigned.rs b/src/models/requests/submit_multisigned.rs index 913539c9..35cc182f 100644 --- a/src/models/requests/submit_multisigned.rs +++ b/src/models/requests/submit_multisigned.rs @@ -23,6 +23,7 @@ pub struct SubmitMultisigned<'a> { /// The common fields shared by all requests. #[serde(flatten)] pub common_fields: CommonFields<'a>, + pub tx_json: serde_json::Value, /// If true, and the transaction fails locally, do not /// retry or relay the transaction to other servers. pub fail_hard: Option, @@ -41,13 +42,18 @@ impl<'a> Request<'a> for SubmitMultisigned<'a> { } impl<'a> SubmitMultisigned<'a> { - pub fn new(id: Option>, fail_hard: Option) -> Self { + pub fn new( + id: Option>, + tx_json: serde_json::Value, + fail_hard: Option, + ) -> Self { Self { common_fields: CommonFields { command: RequestMethod::SubmitMultisigned, id, }, fail_hard, + tx_json, } } } diff --git a/src/models/transactions/account_delete.rs b/src/models/transactions/account_delete.rs index 21137256..0571435e 100644 --- a/src/models/transactions/account_delete.rs +++ b/src/models/transactions/account_delete.rs @@ -68,7 +68,7 @@ impl<'a> AccountDelete<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, destination: Cow<'a, str>, diff --git a/src/models/transactions/account_set.rs b/src/models/transactions/account_set.rs index 226c0715..8daf729d 100644 --- a/src/models/transactions/account_set.rs +++ b/src/models/transactions/account_set.rs @@ -281,7 +281,7 @@ impl<'a> AccountSet<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, clear_flag: Option, diff --git a/src/models/transactions/amm_bid.rs b/src/models/transactions/amm_bid.rs index f1bd49bb..4d9db250 100644 --- a/src/models/transactions/amm_bid.rs +++ b/src/models/transactions/amm_bid.rs @@ -65,7 +65,7 @@ impl<'a> AMMBid<'_> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, asset: Currency<'a>, diff --git a/src/models/transactions/amm_create.rs b/src/models/transactions/amm_create.rs index dfc5a4fd..5ae56a25 100644 --- a/src/models/transactions/amm_create.rs +++ b/src/models/transactions/amm_create.rs @@ -75,7 +75,7 @@ impl<'a> AMMCreate<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/amm_delete.rs b/src/models/transactions/amm_delete.rs index 2755dabc..ad9e4202 100644 --- a/src/models/transactions/amm_delete.rs +++ b/src/models/transactions/amm_delete.rs @@ -54,7 +54,7 @@ impl<'a> AMMDelete<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, asset: Currency<'a>, diff --git a/src/models/transactions/amm_deposit.rs b/src/models/transactions/amm_deposit.rs index 7c2ae8d4..d092c8bb 100644 --- a/src/models/transactions/amm_deposit.rs +++ b/src/models/transactions/amm_deposit.rs @@ -104,7 +104,7 @@ impl<'a> AMMDeposit<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, asset: Currency<'a>, diff --git a/src/models/transactions/amm_vote.rs b/src/models/transactions/amm_vote.rs index 120a112a..20f61409 100644 --- a/src/models/transactions/amm_vote.rs +++ b/src/models/transactions/amm_vote.rs @@ -70,7 +70,7 @@ impl<'a> AMMVote<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, asset: Currency<'a>, diff --git a/src/models/transactions/amm_withdraw.rs b/src/models/transactions/amm_withdraw.rs index f8ccaf85..d67a6847 100644 --- a/src/models/transactions/amm_withdraw.rs +++ b/src/models/transactions/amm_withdraw.rs @@ -92,7 +92,7 @@ impl<'a> AMMWithdraw<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, asset: Currency<'a>, diff --git a/src/models/transactions/check_cancel.rs b/src/models/transactions/check_cancel.rs index 52520864..2be04727 100644 --- a/src/models/transactions/check_cancel.rs +++ b/src/models/transactions/check_cancel.rs @@ -63,7 +63,7 @@ impl<'a> CheckCancel<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, check_id: Cow<'a, str>, diff --git a/src/models/transactions/check_cash.rs b/src/models/transactions/check_cash.rs index 6083f343..b244f29d 100644 --- a/src/models/transactions/check_cash.rs +++ b/src/models/transactions/check_cash.rs @@ -90,7 +90,7 @@ impl<'a> CheckCash<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, check_id: Cow<'a, str>, diff --git a/src/models/transactions/check_create.rs b/src/models/transactions/check_create.rs index 44b7b6bf..00f8c497 100644 --- a/src/models/transactions/check_create.rs +++ b/src/models/transactions/check_create.rs @@ -74,7 +74,7 @@ impl<'a> CheckCreate<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, destination: Cow<'a, str>, diff --git a/src/models/transactions/deposit_preauth.rs b/src/models/transactions/deposit_preauth.rs index 703d0340..1206b8f8 100644 --- a/src/models/transactions/deposit_preauth.rs +++ b/src/models/transactions/deposit_preauth.rs @@ -81,7 +81,7 @@ impl<'a> DepositPreauth<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, authorize: Option>, diff --git a/src/models/transactions/escrow_cancel.rs b/src/models/transactions/escrow_cancel.rs index 0a4a3b8f..f8b8fdf4 100644 --- a/src/models/transactions/escrow_cancel.rs +++ b/src/models/transactions/escrow_cancel.rs @@ -62,7 +62,7 @@ impl<'a> EscrowCancel<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, owner: Cow<'a, str>, diff --git a/src/models/transactions/escrow_create.rs b/src/models/transactions/escrow_create.rs index 1562191d..8e1f4aff 100644 --- a/src/models/transactions/escrow_create.rs +++ b/src/models/transactions/escrow_create.rs @@ -103,7 +103,7 @@ impl<'a> EscrowCreate<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: XRPAmount<'a>, diff --git a/src/models/transactions/escrow_finish.rs b/src/models/transactions/escrow_finish.rs index 61777cf0..1958a7b9 100644 --- a/src/models/transactions/escrow_finish.rs +++ b/src/models/transactions/escrow_finish.rs @@ -89,7 +89,7 @@ impl<'a> EscrowFinish<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, owner: Cow<'a, str>, diff --git a/src/models/transactions/mod.rs b/src/models/transactions/mod.rs index 598d55f5..b6b8dcea 100644 --- a/src/models/transactions/mod.rs +++ b/src/models/transactions/mod.rs @@ -181,7 +181,7 @@ where /// payment, or a sender on whose behalf this transaction is /// made. Conventionally, a refund should specify the initial /// payment's SourceTag as the refund payment's DestinationTag. - pub signers: Option>>, + pub signers: Option>, /// Hex representation of the public key that corresponds to the /// private key used to sign this transaction. If an empty string, /// indicates a multi-signature is present in the Signers field instead. @@ -214,7 +214,7 @@ where memos: Option>, network_id: Option, sequence: Option, - signers: Option>>, + signers: Option>, signing_pub_key: Option>, source_tag: Option, ticket_sequence: Option, @@ -309,18 +309,19 @@ serde_with_tag! { } } +serde_with_tag! { /// One Signer in a multi-signature. A multi-signed transaction /// can have an array of up to 8 Signers, each contributing a /// signature, in the Signers field. /// /// See Signers Field: /// `` -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone, new)] -#[serde(rename_all = "PascalCase")] -pub struct Signer<'a> { - pub account: Cow<'a, str>, - pub txn_signature: Cow<'a, str>, - pub signing_pub_key: Cow<'a, str>, +#[derive(Debug, PartialEq, Eq, Default, Clone, new)] +pub struct Signer { + pub account: String, + pub txn_signature: String, + pub signing_pub_key: String, +} } /// Standard functions for transactions. diff --git a/src/models/transactions/nftoken_accept_offer.rs b/src/models/transactions/nftoken_accept_offer.rs index fa11af45..dd0b0602 100644 --- a/src/models/transactions/nftoken_accept_offer.rs +++ b/src/models/transactions/nftoken_accept_offer.rs @@ -113,7 +113,7 @@ impl<'a> NFTokenAcceptOffer<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, nftoken_sell_offer: Option>, diff --git a/src/models/transactions/nftoken_burn.rs b/src/models/transactions/nftoken_burn.rs index d38a63e6..44cdd412 100644 --- a/src/models/transactions/nftoken_burn.rs +++ b/src/models/transactions/nftoken_burn.rs @@ -70,7 +70,7 @@ impl<'a> NFTokenBurn<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, nftoken_id: Cow<'a, str>, diff --git a/src/models/transactions/nftoken_cancel_offer.rs b/src/models/transactions/nftoken_cancel_offer.rs index 98a14e2e..ea479b85 100644 --- a/src/models/transactions/nftoken_cancel_offer.rs +++ b/src/models/transactions/nftoken_cancel_offer.rs @@ -89,7 +89,7 @@ impl<'a> NFTokenCancelOffer<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, nftoken_offers: Vec>, diff --git a/src/models/transactions/nftoken_create_offer.rs b/src/models/transactions/nftoken_create_offer.rs index 65270544..48400061 100644 --- a/src/models/transactions/nftoken_create_offer.rs +++ b/src/models/transactions/nftoken_create_offer.rs @@ -168,7 +168,7 @@ impl<'a> NFTokenCreateOffer<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/nftoken_mint.rs b/src/models/transactions/nftoken_mint.rs index ea5e7591..d4d3c18d 100644 --- a/src/models/transactions/nftoken_mint.rs +++ b/src/models/transactions/nftoken_mint.rs @@ -173,7 +173,7 @@ impl<'a> NFTokenMint<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, nftoken_taxon: u32, diff --git a/src/models/transactions/offer_cancel.rs b/src/models/transactions/offer_cancel.rs index f71c03f1..9b8a196d 100644 --- a/src/models/transactions/offer_cancel.rs +++ b/src/models/transactions/offer_cancel.rs @@ -65,7 +65,7 @@ impl<'a> OfferCancel<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, offer_sequence: u32, diff --git a/src/models/transactions/offer_create.rs b/src/models/transactions/offer_create.rs index 78252e8a..ed2fc6e4 100644 --- a/src/models/transactions/offer_create.rs +++ b/src/models/transactions/offer_create.rs @@ -108,7 +108,7 @@ impl<'a> OfferCreate<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, taker_gets: Amount<'a>, diff --git a/src/models/transactions/payment.rs b/src/models/transactions/payment.rs index b8db993c..ac0b60f6 100644 --- a/src/models/transactions/payment.rs +++ b/src/models/transactions/payment.rs @@ -196,7 +196,7 @@ impl<'a> Payment<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/payment_channel_claim.rs b/src/models/transactions/payment_channel_claim.rs index b75f4b08..05dd32fc 100644 --- a/src/models/transactions/payment_channel_claim.rs +++ b/src/models/transactions/payment_channel_claim.rs @@ -117,7 +117,7 @@ impl<'a> PaymentChannelClaim<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, channel: Cow<'a, str>, diff --git a/src/models/transactions/payment_channel_create.rs b/src/models/transactions/payment_channel_create.rs index afa2e072..c39bcdeb 100644 --- a/src/models/transactions/payment_channel_create.rs +++ b/src/models/transactions/payment_channel_create.rs @@ -82,7 +82,7 @@ impl<'a> PaymentChannelCreate<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: XRPAmount<'a>, diff --git a/src/models/transactions/payment_channel_fund.rs b/src/models/transactions/payment_channel_fund.rs index d573c006..dd7d9afc 100644 --- a/src/models/transactions/payment_channel_fund.rs +++ b/src/models/transactions/payment_channel_fund.rs @@ -74,7 +74,7 @@ impl<'a> PaymentChannelFund<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: XRPAmount<'a>, diff --git a/src/models/transactions/pseudo_transactions/enable_amendment.rs b/src/models/transactions/pseudo_transactions/enable_amendment.rs index f439e02c..3e86ff0b 100644 --- a/src/models/transactions/pseudo_transactions/enable_amendment.rs +++ b/src/models/transactions/pseudo_transactions/enable_amendment.rs @@ -79,7 +79,7 @@ impl<'a> EnableAmendment<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amendment: Cow<'a, str>, diff --git a/src/models/transactions/pseudo_transactions/set_fee.rs b/src/models/transactions/pseudo_transactions/set_fee.rs index 6d49deff..a39da69b 100644 --- a/src/models/transactions/pseudo_transactions/set_fee.rs +++ b/src/models/transactions/pseudo_transactions/set_fee.rs @@ -63,7 +63,7 @@ impl<'a> SetFee<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, base_fee: XRPAmount<'a>, diff --git a/src/models/transactions/pseudo_transactions/unl_modify.rs b/src/models/transactions/pseudo_transactions/unl_modify.rs index 0168c6f7..ebcce2e3 100644 --- a/src/models/transactions/pseudo_transactions/unl_modify.rs +++ b/src/models/transactions/pseudo_transactions/unl_modify.rs @@ -72,7 +72,7 @@ impl<'a> UNLModify<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, ledger_sequence: u32, diff --git a/src/models/transactions/set_regular_key.rs b/src/models/transactions/set_regular_key.rs index 1f6aa3c8..c5299971 100644 --- a/src/models/transactions/set_regular_key.rs +++ b/src/models/transactions/set_regular_key.rs @@ -69,7 +69,7 @@ impl<'a> SetRegularKey<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, regular_key: Option>, diff --git a/src/models/transactions/signer_list_set.rs b/src/models/transactions/signer_list_set.rs index eca56e79..099e81f0 100644 --- a/src/models/transactions/signer_list_set.rs +++ b/src/models/transactions/signer_list_set.rs @@ -180,7 +180,7 @@ impl<'a> SignerListSet<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, signer_quorum: u32, diff --git a/src/models/transactions/ticket_create.rs b/src/models/transactions/ticket_create.rs index 3daf3506..8e55af49 100644 --- a/src/models/transactions/ticket_create.rs +++ b/src/models/transactions/ticket_create.rs @@ -63,7 +63,7 @@ impl<'a> TicketCreate<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, ticket_count: u32, diff --git a/src/models/transactions/trust_set.rs b/src/models/transactions/trust_set.rs index 05bf6994..3dc26253 100644 --- a/src/models/transactions/trust_set.rs +++ b/src/models/transactions/trust_set.rs @@ -99,7 +99,7 @@ impl<'a> TrustSet<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, limit_amount: IssuedCurrencyAmount<'a>, diff --git a/src/models/transactions/xchain_account_create_commit.rs b/src/models/transactions/xchain_account_create_commit.rs index bfa57f81..7f86fc4b 100644 --- a/src/models/transactions/xchain_account_create_commit.rs +++ b/src/models/transactions/xchain_account_create_commit.rs @@ -45,7 +45,7 @@ impl<'a> XChainAccountCreateCommit<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/xchain_add_account_create_attestation.rs b/src/models/transactions/xchain_add_account_create_attestation.rs index 28c49fac..89183106 100644 --- a/src/models/transactions/xchain_add_account_create_attestation.rs +++ b/src/models/transactions/xchain_add_account_create_attestation.rs @@ -51,7 +51,7 @@ impl<'a> XChainAddAccountCreateAttestation<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/xchain_add_claim_attestation.rs b/src/models/transactions/xchain_add_claim_attestation.rs index c082baec..29299160 100644 --- a/src/models/transactions/xchain_add_claim_attestation.rs +++ b/src/models/transactions/xchain_add_claim_attestation.rs @@ -50,7 +50,7 @@ impl<'a> XChainAddClaimAttestation<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/xchain_claim.rs b/src/models/transactions/xchain_claim.rs index 1d5a2ee0..9065ff1b 100644 --- a/src/models/transactions/xchain_claim.rs +++ b/src/models/transactions/xchain_claim.rs @@ -53,7 +53,7 @@ impl<'a> XChainClaim<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/xchain_commit.rs b/src/models/transactions/xchain_commit.rs index 9e817b5e..e880406a 100644 --- a/src/models/transactions/xchain_commit.rs +++ b/src/models/transactions/xchain_commit.rs @@ -44,7 +44,7 @@ impl<'a> XChainCommit<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, amount: Amount<'a>, diff --git a/src/models/transactions/xchain_create_bridge.rs b/src/models/transactions/xchain_create_bridge.rs index 013f711f..4ff0126d 100644 --- a/src/models/transactions/xchain_create_bridge.rs +++ b/src/models/transactions/xchain_create_bridge.rs @@ -52,7 +52,7 @@ impl<'a> XChainCreateBridge<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, signature_reward: Amount<'a>, diff --git a/src/models/transactions/xchain_create_claim_id.rs b/src/models/transactions/xchain_create_claim_id.rs index 5dad09f3..375f92dc 100644 --- a/src/models/transactions/xchain_create_claim_id.rs +++ b/src/models/transactions/xchain_create_claim_id.rs @@ -52,7 +52,7 @@ impl<'a> XChainCreateClaimID<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, other_chain_source: Cow<'a, str>, diff --git a/src/models/transactions/xchain_modify_bridge.rs b/src/models/transactions/xchain_modify_bridge.rs index 206d8506..0662d05f 100644 --- a/src/models/transactions/xchain_modify_bridge.rs +++ b/src/models/transactions/xchain_modify_bridge.rs @@ -64,7 +64,7 @@ impl<'a> XChainModifyBridge<'a> { last_ledger_sequence: Option, memos: Option>, sequence: Option, - signers: Option>>, + signers: Option>, source_tag: Option, ticket_sequence: Option, xchain_bridge: XChainBridge<'a>, diff --git a/src/transaction/multisign.rs b/src/transaction/multisign.rs index cfc4bc3d..41c400d5 100644 --- a/src/transaction/multisign.rs +++ b/src/transaction/multisign.rs @@ -29,6 +29,7 @@ where decoded_tx_signers .sort_by_key(|signer| decode_classic_address(signer.account.as_ref()).unwrap()); transaction.get_mut_common_fields().signers = Some(decoded_tx_signers); + transaction.get_mut_common_fields().signing_pub_key = Some("".into()); Ok(()) } @@ -36,18 +37,33 @@ where #[cfg(test)] mod test { use alloc::borrow::Cow; + use alloc::{dbg, vec}; use super::*; - use crate::asynch::transaction::sign; + use crate::asynch::clients::XRPLAsyncClient; + use crate::asynch::transaction::{autofill, sign}; + use crate::asynch::wallet::generate_faucet_wallet; + use crate::clients::json_rpc::JsonRpcClient; + use crate::models::requests::submit_multisigned::SubmitMultisigned; use crate::models::transactions::account_set::AccountSet; + use crate::models::transactions::signer_list_set::SignerEntry; use crate::wallet::Wallet; #[tokio::test] async fn test_multisign() { - let wallet = Wallet::new("sEdT7wHTCLzDG7ueaw4hroSTBvH7Mk5", 0).unwrap(); - let wallet1 = Wallet::create(None).unwrap(); - let wallet2 = Wallet::create(None).unwrap(); - let mut multi_signed_tx = AccountSet::new( + let client = + JsonRpcClient::connect("https://s.altnet.rippletest.net:51234".parse().unwrap()); + let wallet = Wallet::create(None).unwrap(); + let wallet = generate_faucet_wallet(&client, Some(wallet), None, None, None) + .await + .unwrap(); + let first_signer = Wallet::new("sEdTLQkHAWpdS7FDk7EvuS7Mz8aSMRh", 0).unwrap(); + let second_signer = Wallet::new("sEd7DXaHkGQD8mz8xcRLDxfMLqCurif", 0).unwrap(); + let signer_entries = vec![ + SignerEntry::new(first_signer.classic_address.clone(), 1), + SignerEntry::new(second_signer.classic_address.clone(), 1), + ]; + let mut account_set_txn = AccountSet::new( Cow::from(wallet.classic_address.clone()), None, None, @@ -67,13 +83,28 @@ mod test { None, None, ); - let mut tx_1 = multi_signed_tx.clone(); - sign(&mut tx_1, &wallet1, true).unwrap(); - let mut tx_2 = multi_signed_tx.clone(); - sign(&mut tx_2, &wallet2, true).unwrap(); + autofill( + &mut account_set_txn, + &client, + Some(signer_entries.len().try_into().unwrap()), + ) + .await + .unwrap(); + let mut tx_1 = account_set_txn.clone(); + sign(&mut tx_1, &first_signer, true).unwrap(); + let mut tx_2 = account_set_txn.clone(); + sign(&mut tx_2, &second_signer, true).unwrap(); let tx_list = [tx_1.clone(), tx_2.clone()].to_vec(); - - multisign(&mut multi_signed_tx, &tx_list).unwrap(); - assert!(multi_signed_tx.get_common_fields().is_signed()); + dbg!(&account_set_txn, &tx_list); + multisign(&mut account_set_txn, &tx_list).unwrap(); + dbg!(&account_set_txn); + assert!(account_set_txn.get_common_fields().is_signed()); + let res = client + .request( + SubmitMultisigned::new(None, serde_json::to_value(&account_set_txn).unwrap(), None) + .into(), + ) + .await; + dbg!(&res); } } From 0f495aeb58cbc3a34d99c556b4f5e166d03f6cc1 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 13 Feb 2025 17:31:10 +0000 Subject: [PATCH 14/26] fix transactions to use the new constructor and fix the serde tests to always have the SigningPubKey field --- src/models/transactions/account_delete.rs | 16 +++--- src/models/transactions/account_set.rs | 16 +++--- src/models/transactions/amm_bid.rs | 14 ++--- src/models/transactions/amm_create.rs | 14 ++--- src/models/transactions/amm_delete.rs | 14 ++--- src/models/transactions/amm_deposit.rs | 14 ++--- src/models/transactions/amm_vote.rs | 14 ++--- src/models/transactions/amm_withdraw.rs | 14 ++--- src/models/transactions/check_cancel.rs | 16 +++--- src/models/transactions/check_cash.rs | 16 +++--- src/models/transactions/check_create.rs | 16 +++--- src/models/transactions/deposit_preauth.rs | 16 +++--- src/models/transactions/escrow_cancel.rs | 16 +++--- src/models/transactions/escrow_create.rs | 16 +++--- src/models/transactions/escrow_finish.rs | 16 +++--- src/models/transactions/mod.rs | 2 +- .../transactions/nftoken_accept_offer.rs | 16 +++--- src/models/transactions/nftoken_burn.rs | 16 +++--- .../transactions/nftoken_cancel_offer.rs | 16 +++--- .../transactions/nftoken_create_offer.rs | 16 +++--- src/models/transactions/nftoken_mint.rs | 16 +++--- src/models/transactions/offer_cancel.rs | 16 +++--- src/models/transactions/offer_create.rs | 16 +++--- src/models/transactions/payment.rs | 16 +++--- .../transactions/payment_channel_claim.rs | 16 +++--- .../transactions/payment_channel_create.rs | 16 +++--- .../transactions/payment_channel_fund.rs | 16 +++--- .../pseudo_transactions/enable_amendment.rs | 14 ++--- .../pseudo_transactions/set_fee.rs | 14 ++--- .../pseudo_transactions/unl_modify.rs | 14 ++--- src/models/transactions/set_regular_key.rs | 16 +++--- src/models/transactions/signer_list_set.rs | 16 +++--- src/models/transactions/ticket_create.rs | 16 +++--- src/models/transactions/trust_set.rs | 16 +++--- .../xchain_account_create_commit.rs | 14 ++--- .../xchain_add_account_create_attestation.rs | 14 ++--- .../xchain_add_claim_attestation.rs | 14 ++--- src/models/transactions/xchain_claim.rs | 14 ++--- src/models/transactions/xchain_commit.rs | 14 ++--- .../transactions/xchain_create_bridge.rs | 14 ++--- .../transactions/xchain_create_claim_id.rs | 14 ++--- .../transactions/xchain_modify_bridge.rs | 14 ++--- src/transaction/multisign.rs | 57 +++++++++---------- 43 files changed, 340 insertions(+), 341 deletions(-) diff --git a/src/models/transactions/account_delete.rs b/src/models/transactions/account_delete.rs index 0571435e..afc98177 100644 --- a/src/models/transactions/account_delete.rs +++ b/src/models/transactions/account_delete.rs @@ -75,22 +75,22 @@ impl<'a> AccountDelete<'a> { destination_tag: Option, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::AccountDelete, + TransactionType::AccountDelete, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), destination, destination_tag, } @@ -116,7 +116,7 @@ mod test_serde { "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe".into(), Some(13), ); - let default_json_str = r#"{"Account":"rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm","TransactionType":"AccountDelete","Fee":"2000000","Flags":0,"Sequence":2470665,"Destination":"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe","DestinationTag":13}"#; + let default_json_str = r#"{"Account":"rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm","TransactionType":"AccountDelete","Fee":"2000000","Flags":0,"Sequence":2470665,"SigningPubKey":"","Destination":"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe","DestinationTag":13}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/account_set.rs b/src/models/transactions/account_set.rs index 8daf729d..1b95b057 100644 --- a/src/models/transactions/account_set.rs +++ b/src/models/transactions/account_set.rs @@ -294,22 +294,22 @@ impl<'a> AccountSet<'a> { nftoken_minter: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::AccountSet, + TransactionType::AccountSet, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), clear_flag, domain, email_hash, @@ -558,7 +558,7 @@ mod tests { None, None, ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"AccountSet","Fee":"12","Flags":0,"Sequence":5,"Domain":"6578616D706C652E636F6D","MessageKey":"03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB","SetFlag":5}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"AccountSet","Fee":"12","Flags":0,"Sequence":5,"SigningPubKey":"","Domain":"6578616D706C652E636F6D","MessageKey":"03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB","SetFlag":5}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/amm_bid.rs b/src/models/transactions/amm_bid.rs index 4d9db250..520f561f 100644 --- a/src/models/transactions/amm_bid.rs +++ b/src/models/transactions/amm_bid.rs @@ -75,22 +75,22 @@ impl<'a> AMMBid<'_> { auth_accounts: Option>, ) -> AMMBid<'a> { AMMBid { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::AMMBid, + TransactionType::AMMBid, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), asset, asset2, bid_min, diff --git a/src/models/transactions/amm_create.rs b/src/models/transactions/amm_create.rs index 5ae56a25..b0fe8d79 100644 --- a/src/models/transactions/amm_create.rs +++ b/src/models/transactions/amm_create.rs @@ -83,22 +83,22 @@ impl<'a> AMMCreate<'a> { trading_fee: u16, ) -> AMMCreate<'a> { AMMCreate { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::AMMCreate, + TransactionType::AMMCreate, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, amount2, trading_fee, diff --git a/src/models/transactions/amm_delete.rs b/src/models/transactions/amm_delete.rs index ad9e4202..4afd2fc0 100644 --- a/src/models/transactions/amm_delete.rs +++ b/src/models/transactions/amm_delete.rs @@ -61,22 +61,22 @@ impl<'a> AMMDelete<'a> { asset2: Currency<'a>, ) -> AMMDelete<'a> { AMMDelete { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::AMMDelete, + TransactionType::AMMDelete, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), asset, asset2, } diff --git a/src/models/transactions/amm_deposit.rs b/src/models/transactions/amm_deposit.rs index d092c8bb..911379c9 100644 --- a/src/models/transactions/amm_deposit.rs +++ b/src/models/transactions/amm_deposit.rs @@ -115,22 +115,22 @@ impl<'a> AMMDeposit<'a> { lp_token_out: Option>, ) -> AMMDeposit<'a> { AMMDeposit { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::AMMDeposit, + TransactionType::AMMDeposit, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), asset, asset2, amount, diff --git a/src/models/transactions/amm_vote.rs b/src/models/transactions/amm_vote.rs index 20f61409..f37cb749 100644 --- a/src/models/transactions/amm_vote.rs +++ b/src/models/transactions/amm_vote.rs @@ -78,22 +78,22 @@ impl<'a> AMMVote<'a> { trading_fee: Option, ) -> AMMVote<'a> { AMMVote { - common_fields: CommonFields { + common_fields: CommonFields::new( account, + TransactionType::AMMVote, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - transaction_type: TransactionType::AMMVote, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), asset, asset2, trading_fee, diff --git a/src/models/transactions/amm_withdraw.rs b/src/models/transactions/amm_withdraw.rs index d67a6847..c6deaaa6 100644 --- a/src/models/transactions/amm_withdraw.rs +++ b/src/models/transactions/amm_withdraw.rs @@ -103,22 +103,22 @@ impl<'a> AMMWithdraw<'a> { lp_token_in: Option>, ) -> Self { AMMWithdraw { - common_fields: CommonFields { + common_fields: CommonFields::new( account, + TransactionType::AMMWithdraw, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - transaction_type: TransactionType::AMMWithdraw, - }, + None, + ), asset, asset2, amount, diff --git a/src/models/transactions/check_cancel.rs b/src/models/transactions/check_cancel.rs index 2be04727..3f814261 100644 --- a/src/models/transactions/check_cancel.rs +++ b/src/models/transactions/check_cancel.rs @@ -69,22 +69,22 @@ impl<'a> CheckCancel<'a> { check_id: Cow<'a, str>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::CheckCancel, + TransactionType::CheckCancel, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), check_id, } } @@ -108,7 +108,7 @@ mod tests { None, "49647F0D748DC3FE26BDACBC57F251AADEFFF391403EC9BF87C97F67E9977FB0".into(), ); - let default_json_str = r#"{"Account":"rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo","TransactionType":"CheckCancel","Fee":"12","Flags":0,"CheckID":"49647F0D748DC3FE26BDACBC57F251AADEFFF391403EC9BF87C97F67E9977FB0"}"#; + let default_json_str = r#"{"Account":"rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo","TransactionType":"CheckCancel","Fee":"12","Flags":0,"SigningPubKey":"","CheckID":"49647F0D748DC3FE26BDACBC57F251AADEFFF391403EC9BF87C97F67E9977FB0"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/check_cash.rs b/src/models/transactions/check_cash.rs index b244f29d..9b832261 100644 --- a/src/models/transactions/check_cash.rs +++ b/src/models/transactions/check_cash.rs @@ -98,22 +98,22 @@ impl<'a> CheckCash<'a> { deliver_min: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::CheckCash, + TransactionType::CheckCash, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), check_id, amount, deliver_min, @@ -176,7 +176,7 @@ mod tests { Some("100000000".into()), None, ); - let default_json_str = r#"{"Account":"rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy","TransactionType":"CheckCash","Fee":"12","Flags":0,"CheckID":"838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334","Amount":"100000000"}"#; + let default_json_str = r#"{"Account":"rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy","TransactionType":"CheckCash","Fee":"12","Flags":0,"SigningPubKey":"","CheckID":"838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334","Amount":"100000000"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/check_create.rs b/src/models/transactions/check_create.rs index 00f8c497..61e7484c 100644 --- a/src/models/transactions/check_create.rs +++ b/src/models/transactions/check_create.rs @@ -84,22 +84,22 @@ impl<'a> CheckCreate<'a> { invoice_id: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::CheckCreate, + TransactionType::CheckCreate, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), destination, send_max, destination_tag, @@ -131,7 +131,7 @@ mod tests { Some(570113521), Some("6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B".into()), ); - let default_json_str = r#"{"Account":"rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo","TransactionType":"CheckCreate","Fee":"12","Flags":0,"Destination":"rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy","SendMax":"100000000","DestinationTag":1,"Expiration":570113521,"InvoiceID":"6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B"}"#; + let default_json_str = r#"{"Account":"rUn84CUYbNjRoTQ6mSW7BVJPSVJNLb1QLo","TransactionType":"CheckCreate","Fee":"12","Flags":0,"SigningPubKey":"","Destination":"rfkE1aSy9G8Upk4JssnwBxhEv5p4mn2KTy","SendMax":"100000000","DestinationTag":1,"Expiration":570113521,"InvoiceID":"6F1DFD1D0FE8A32E40E1F2C05CF1C15545BAB56B617F9C6C2D63A6B704BEF59B"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/deposit_preauth.rs b/src/models/transactions/deposit_preauth.rs index 1206b8f8..e8bd5993 100644 --- a/src/models/transactions/deposit_preauth.rs +++ b/src/models/transactions/deposit_preauth.rs @@ -88,22 +88,22 @@ impl<'a> DepositPreauth<'a> { unauthorize: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::DepositPreauth, + TransactionType::DepositPreauth, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), authorize, unauthorize, } @@ -164,7 +164,7 @@ mod tests { Some("rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de".into()), None, ); - let default_json_str = r#"{"Account":"rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8","TransactionType":"DepositPreauth","Fee":"10","Flags":0,"Sequence":2,"Authorize":"rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de"}"#; + let default_json_str = r#"{"Account":"rsUiUMpnrgxQp24dJYZDhmV4bE3aBtQyt8","TransactionType":"DepositPreauth","Fee":"10","Flags":0,"Sequence":2,"SigningPubKey":"","Authorize":"rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/escrow_cancel.rs b/src/models/transactions/escrow_cancel.rs index f8b8fdf4..e307ed58 100644 --- a/src/models/transactions/escrow_cancel.rs +++ b/src/models/transactions/escrow_cancel.rs @@ -69,22 +69,22 @@ impl<'a> EscrowCancel<'a> { offer_sequence: u32, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::EscrowCancel, + TransactionType::EscrowCancel, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), owner, offer_sequence, } @@ -110,7 +110,7 @@ mod tests { "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn".into(), 7, ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"EscrowCancel","Flags":0,"Owner":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","OfferSequence":7}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"EscrowCancel","Flags":0,"SigningPubKey":"","Owner":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","OfferSequence":7}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/escrow_create.rs b/src/models/transactions/escrow_create.rs index 8e1f4aff..8ee31493 100644 --- a/src/models/transactions/escrow_create.rs +++ b/src/models/transactions/escrow_create.rs @@ -114,22 +114,22 @@ impl<'a> EscrowCreate<'a> { finish_after: Option, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::EscrowCreate, + TransactionType::EscrowCreate, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, destination, destination_tag, @@ -207,7 +207,7 @@ mod tests { Some(23480), Some(533171558), ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"EscrowCreate","Flags":0,"SourceTag":11747,"Amount":"10000","Destination":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW","DestinationTag":23480,"CancelAfter":533257958,"FinishAfter":533171558,"Condition":"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100"}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"EscrowCreate","Flags":0,"SigningPubKey":"","SourceTag":11747,"Amount":"10000","Destination":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW","DestinationTag":23480,"CancelAfter":533257958,"FinishAfter":533171558,"Condition":"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/escrow_finish.rs b/src/models/transactions/escrow_finish.rs index 1958a7b9..1cb74129 100644 --- a/src/models/transactions/escrow_finish.rs +++ b/src/models/transactions/escrow_finish.rs @@ -98,22 +98,22 @@ impl<'a> EscrowFinish<'a> { fulfillment: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::EscrowFinish, + TransactionType::EscrowFinish, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), owner, offer_sequence, condition, @@ -188,7 +188,7 @@ mod tests { ), Some("A0028000".into()), ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"EscrowFinish","Flags":0,"Owner":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","OfferSequence":7,"Condition":"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100","Fulfillment":"A0028000"}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"EscrowFinish","Flags":0,"SigningPubKey":"","Owner":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","OfferSequence":7,"Condition":"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100","Fulfillment":"A0028000"}"#; // Serialize let default_json_value: Value = serde_json::from_str(default_json_str).unwrap(); // let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/mod.rs b/src/models/transactions/mod.rs index b6b8dcea..22043760 100644 --- a/src/models/transactions/mod.rs +++ b/src/models/transactions/mod.rs @@ -231,7 +231,7 @@ where network_id, sequence, signers, - signing_pub_key, + signing_pub_key: Some(signing_pub_key.unwrap_or("".into())), source_tag, ticket_sequence, txn_signature, diff --git a/src/models/transactions/nftoken_accept_offer.rs b/src/models/transactions/nftoken_accept_offer.rs index dd0b0602..d4c6f991 100644 --- a/src/models/transactions/nftoken_accept_offer.rs +++ b/src/models/transactions/nftoken_accept_offer.rs @@ -121,22 +121,22 @@ impl<'a> NFTokenAcceptOffer<'a> { nftoken_broker_fee: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::NFTokenAcceptOffer, + TransactionType::NFTokenAcceptOffer, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), nftoken_sell_offer, nftoken_buy_offer, nftoken_broker_fee, @@ -246,7 +246,7 @@ mod tests { None, None, ); - let default_json_str = r#"{"Account":"r9spUPhPBfB6kQeF6vPhwmtFwRhBh2JUCG","TransactionType":"NFTokenAcceptOffer","Fee":"12","Flags":0,"LastLedgerSequence":75447550,"Memos":[{"Memo":{"MemoData":"61356534373538372D633134322D346663382D616466362D393666383562356435386437","MemoFormat":null,"MemoType":null}}],"Sequence":68549302,"NFTokenSellOffer":"68CD1F6F906494EA08C9CB5CAFA64DFA90D4E834B7151899B73231DE5A0C3B77"}"#; + let default_json_str = r#"{"Account":"r9spUPhPBfB6kQeF6vPhwmtFwRhBh2JUCG","TransactionType":"NFTokenAcceptOffer","Fee":"12","Flags":0,"LastLedgerSequence":75447550,"Memos":[{"Memo":{"MemoData":"61356534373538372D633134322D346663382D616466362D393666383562356435386437","MemoFormat":null,"MemoType":null}}],"Sequence":68549302,"SigningPubKey":"","NFTokenSellOffer":"68CD1F6F906494EA08C9CB5CAFA64DFA90D4E834B7151899B73231DE5A0C3B77"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/nftoken_burn.rs b/src/models/transactions/nftoken_burn.rs index 44cdd412..33e9a546 100644 --- a/src/models/transactions/nftoken_burn.rs +++ b/src/models/transactions/nftoken_burn.rs @@ -77,22 +77,22 @@ impl<'a> NFTokenBurn<'a> { owner: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::NFTokenBurn, + TransactionType::NFTokenBurn, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), nftoken_id, owner, } @@ -118,7 +118,7 @@ mod tests { "000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65".into(), Some("rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B".into()), ); - let default_json_str = r#"{"Account":"rNCFjv8Ek5oDrNiMJ3pw6eLLFtMjZLJnf2","TransactionType":"NFTokenBurn","Fee":"10","Flags":0,"NFTokenID":"000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65","Owner":"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"}"#; + let default_json_str = r#"{"Account":"rNCFjv8Ek5oDrNiMJ3pw6eLLFtMjZLJnf2","TransactionType":"NFTokenBurn","Fee":"10","Flags":0,"SigningPubKey":"","NFTokenID":"000B013A95F14B0044F78A264E41713C64B5F89242540EE208C3098E00000D65","Owner":"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/nftoken_cancel_offer.rs b/src/models/transactions/nftoken_cancel_offer.rs index ea479b85..e48ecd58 100644 --- a/src/models/transactions/nftoken_cancel_offer.rs +++ b/src/models/transactions/nftoken_cancel_offer.rs @@ -95,22 +95,22 @@ impl<'a> NFTokenCancelOffer<'a> { nftoken_offers: Vec>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::NFTokenCancelOffer, + TransactionType::NFTokenCancelOffer, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), nftoken_offers, } } @@ -171,7 +171,7 @@ mod tests { None, vec!["9C92E061381C1EF37A8CDE0E8FC35188BFC30B1883825042A64309AC09F4C36D".into()], ); - let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"NFTokenCancelOffer","Flags":0,"NFTokenOffers":["9C92E061381C1EF37A8CDE0E8FC35188BFC30B1883825042A64309AC09F4C36D"]}"#; + let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"NFTokenCancelOffer","Flags":0,"SigningPubKey":"","NFTokenOffers":["9C92E061381C1EF37A8CDE0E8FC35188BFC30B1883825042A64309AC09F4C36D"]}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/nftoken_create_offer.rs b/src/models/transactions/nftoken_create_offer.rs index 48400061..7295b7be 100644 --- a/src/models/transactions/nftoken_create_offer.rs +++ b/src/models/transactions/nftoken_create_offer.rs @@ -178,22 +178,22 @@ impl<'a> NFTokenCreateOffer<'a> { owner: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::NFTokenCreateOffer, + TransactionType::NFTokenCreateOffer, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), nftoken_id, amount, owner, @@ -351,7 +351,7 @@ mod tests { None, None, ); - let default_json_str = r#"{"Account":"rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX","TransactionType":"NFTokenCreateOffer","Flags":1,"NFTokenID":"000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007","Amount":"1000000"}"#; + let default_json_str = r#"{"Account":"rs8jBmmfpwgmrSPgwMsh7CvKRmRt1JTVSX","TransactionType":"NFTokenCreateOffer","Flags":1,"SigningPubKey":"","NFTokenID":"000100001E962F495F07A990F4ED55ACCFEEF365DBAA76B6A048C0A200000007","Amount":"1000000"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/nftoken_mint.rs b/src/models/transactions/nftoken_mint.rs index d4d3c18d..d76e2bb4 100644 --- a/src/models/transactions/nftoken_mint.rs +++ b/src/models/transactions/nftoken_mint.rs @@ -182,22 +182,22 @@ impl<'a> NFTokenMint<'a> { uri: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::NFTokenMint, + TransactionType::NFTokenMint, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), nftoken_taxon, issuer, transfer_fee, @@ -321,7 +321,7 @@ mod tests { Some(314), Some("697066733A2F2F62616679626569676479727A74357366703775646D37687537367568377932366E6634646675796C71616266336F636C67747179353566627A6469".into()), ); - let default_json_str = r#"{"Account":"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B","TransactionType":"NFTokenMint","Fee":"10","Flags":8,"Memos":[{"Memo":{"MemoData":"72656E74","MemoFormat":null,"MemoType":"687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E65726963"}}],"NFTokenTaxon":0,"TransferFee":314,"URI":"697066733A2F2F62616679626569676479727A74357366703775646D37687537367568377932366E6634646675796C71616266336F636C67747179353566627A6469"}"#; + let default_json_str = r#"{"Account":"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B","TransactionType":"NFTokenMint","Fee":"10","Flags":8,"Memos":[{"Memo":{"MemoData":"72656E74","MemoFormat":null,"MemoType":"687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E65726963"}}],"SigningPubKey":"","NFTokenTaxon":0,"TransferFee":314,"URI":"697066733A2F2F62616679626569676479727A74357366703775646D37687537367568377932366E6634646675796C71616266336F636C67747179353566627A6469"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/offer_cancel.rs b/src/models/transactions/offer_cancel.rs index 9b8a196d..361fc51d 100644 --- a/src/models/transactions/offer_cancel.rs +++ b/src/models/transactions/offer_cancel.rs @@ -71,22 +71,22 @@ impl<'a> OfferCancel<'a> { offer_sequence: u32, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::OfferCancel, + TransactionType::OfferCancel, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), offer_sequence, } } @@ -110,7 +110,7 @@ mod tests { None, 6, ); - let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"OfferCancel","Fee":"12","Flags":0,"LastLedgerSequence":7108629,"Sequence":7,"OfferSequence":6}"#; + let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"OfferCancel","Fee":"12","Flags":0,"LastLedgerSequence":7108629,"Sequence":7,"SigningPubKey":"","OfferSequence":6}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/offer_create.rs b/src/models/transactions/offer_create.rs index ed2fc6e4..ad6e4995 100644 --- a/src/models/transactions/offer_create.rs +++ b/src/models/transactions/offer_create.rs @@ -117,22 +117,22 @@ impl<'a> OfferCreate<'a> { offer_sequence: Option, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::OfferCreate, + TransactionType::OfferCreate, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), taker_gets, taker_pays, expiration, @@ -230,7 +230,7 @@ mod tests { None, None, ); - let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"OfferCreate","Fee":"12","Flags":0,"LastLedgerSequence":7108682,"Sequence":8,"TakerGets":"6000000","TakerPays":{"currency":"GKO","issuer":"ruazs5h1qEsqpke88pcqnaseXdm6od2xc","value":"2"}}"#; + let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"OfferCreate","Fee":"12","Flags":0,"LastLedgerSequence":7108682,"Sequence":8,"SigningPubKey":"","TakerGets":"6000000","TakerPays":{"currency":"GKO","issuer":"ruazs5h1qEsqpke88pcqnaseXdm6od2xc","value":"2"}}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/payment.rs b/src/models/transactions/payment.rs index ac0b60f6..374880a7 100644 --- a/src/models/transactions/payment.rs +++ b/src/models/transactions/payment.rs @@ -208,22 +208,22 @@ impl<'a> Payment<'a> { send_max: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::Payment, + TransactionType::Payment, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, destination, destination_tag, @@ -406,7 +406,7 @@ mod tests { None, None, ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"Payment","Fee":"12","Flags":131072,"Sequence":2,"Amount":{"currency":"USD","issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","value":"1"},"Destination":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX"}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"Payment","Fee":"12","Flags":131072,"Sequence":2,"SigningPubKey":"","Amount":{"currency":"USD","issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","value":"1"},"Destination":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/payment_channel_claim.rs b/src/models/transactions/payment_channel_claim.rs index 05dd32fc..f430c293 100644 --- a/src/models/transactions/payment_channel_claim.rs +++ b/src/models/transactions/payment_channel_claim.rs @@ -127,22 +127,22 @@ impl<'a> PaymentChannelClaim<'a> { signature: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::PaymentChannelClaim, + TransactionType::PaymentChannelClaim, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), channel, balance, amount, @@ -175,7 +175,7 @@ mod tests { Some("32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A".into()), Some("30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA3CD7B9B".into()), ); - let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"PaymentChannelClaim","Flags":0,"Channel":"C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198","Balance":"1000000","Amount":"1000000","Signature":"30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA3CD7B9B","PublicKey":"32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A"}"#; + let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"PaymentChannelClaim","Flags":0,"SigningPubKey":"","Channel":"C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198","Balance":"1000000","Amount":"1000000","Signature":"30440220718D264EF05CAED7C781FF6DE298DCAC68D002562C9BF3A07C1E721B420C0DAB02203A5A4779EF4D2CCC7BC3EF886676D803A9981B928D3B8ACA483B80ECA3CD7B9B","PublicKey":"32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/payment_channel_create.rs b/src/models/transactions/payment_channel_create.rs index c39bcdeb..b5079734 100644 --- a/src/models/transactions/payment_channel_create.rs +++ b/src/models/transactions/payment_channel_create.rs @@ -93,22 +93,22 @@ impl<'a> PaymentChannelCreate<'a> { destination_tag: Option, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::PaymentChannelCreate, + TransactionType::PaymentChannelCreate, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, destination, settle_delay, @@ -142,7 +142,7 @@ mod tests { Some(533171558), Some(23480), ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"PaymentChannelCreate","Flags":0,"SourceTag":11747,"Amount":"10000","Destination":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW","SettleDelay":86400,"PublicKey":"32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A","CancelAfter":533171558,"DestinationTag":23480}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"PaymentChannelCreate","Flags":0,"SigningPubKey":"","SourceTag":11747,"Amount":"10000","Destination":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW","SettleDelay":86400,"PublicKey":"32D2471DB72B27E3310F355BB33E339BF26F8392D5A93D3BC0FC3B566612DA0F0A","CancelAfter":533171558,"DestinationTag":23480}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/payment_channel_fund.rs b/src/models/transactions/payment_channel_fund.rs index dd7d9afc..ec268787 100644 --- a/src/models/transactions/payment_channel_fund.rs +++ b/src/models/transactions/payment_channel_fund.rs @@ -82,22 +82,22 @@ impl<'a> PaymentChannelFund<'a> { expiration: Option, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::PaymentChannelFund, + TransactionType::PaymentChannelFund, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, channel, expiration, @@ -127,7 +127,7 @@ mod tests { "C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198".into(), Some(543171558), ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"PaymentChannelFund","Flags":0,"Amount":"200000","Channel":"C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198","Expiration":543171558}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"PaymentChannelFund","Flags":0,"SigningPubKey":"","Amount":"200000","Channel":"C1AE6DDDEEC05CF2978C0BAD6FE302948E9533691DC749DCDD3B9E5992CA6198","Expiration":543171558}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/pseudo_transactions/enable_amendment.rs b/src/models/transactions/pseudo_transactions/enable_amendment.rs index 3e86ff0b..8f739fda 100644 --- a/src/models/transactions/pseudo_transactions/enable_amendment.rs +++ b/src/models/transactions/pseudo_transactions/enable_amendment.rs @@ -86,22 +86,22 @@ impl<'a> EnableAmendment<'a> { ledger_sequence: u32, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::EnableAmendment, + TransactionType::EnableAmendment, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amendment, ledger_sequence, } diff --git a/src/models/transactions/pseudo_transactions/set_fee.rs b/src/models/transactions/pseudo_transactions/set_fee.rs index a39da69b..719875ad 100644 --- a/src/models/transactions/pseudo_transactions/set_fee.rs +++ b/src/models/transactions/pseudo_transactions/set_fee.rs @@ -73,22 +73,22 @@ impl<'a> SetFee<'a> { ledger_sequence: u32, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::SetFee, + TransactionType::SetFee, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), base_fee, reference_fee_units, reserve_base, diff --git a/src/models/transactions/pseudo_transactions/unl_modify.rs b/src/models/transactions/pseudo_transactions/unl_modify.rs index ebcce2e3..59c5bc67 100644 --- a/src/models/transactions/pseudo_transactions/unl_modify.rs +++ b/src/models/transactions/pseudo_transactions/unl_modify.rs @@ -80,22 +80,22 @@ impl<'a> UNLModify<'a> { unlmodify_validator: Cow<'a, str>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::UNLModify, + TransactionType::UNLModify, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), ledger_sequence, unlmodify_disabling, unlmodify_validator, diff --git a/src/models/transactions/set_regular_key.rs b/src/models/transactions/set_regular_key.rs index c5299971..fc2d6e45 100644 --- a/src/models/transactions/set_regular_key.rs +++ b/src/models/transactions/set_regular_key.rs @@ -75,22 +75,22 @@ impl<'a> SetRegularKey<'a> { regular_key: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::SetRegularKey, + TransactionType::SetRegularKey, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), regular_key, } } @@ -114,7 +114,7 @@ mod tests { None, Some("rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD".into()), ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"SetRegularKey","Fee":"12","Flags":0,"RegularKey":"rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"SetRegularKey","Fee":"12","Flags":0,"SigningPubKey":"","RegularKey":"rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD"}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/signer_list_set.rs b/src/models/transactions/signer_list_set.rs index 099e81f0..e2780347 100644 --- a/src/models/transactions/signer_list_set.rs +++ b/src/models/transactions/signer_list_set.rs @@ -187,22 +187,22 @@ impl<'a> SignerListSet<'a> { signer_entries: Option>, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::SignerListSet, + TransactionType::SignerListSet, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), signer_quorum, signer_entries, } @@ -397,7 +397,7 @@ mod tests { SignerEntry::new("raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n".to_string(), 1), ]), ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"SignerListSet","Fee":"12","Flags":0,"SignerQuorum":3,"SignerEntries":[{"SignerEntry":{"Account":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW","SignerWeight":2}},{"SignerEntry":{"Account":"rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v","SignerWeight":1}},{"SignerEntry":{"Account":"raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n","SignerWeight":1}}]}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"SignerListSet","Fee":"12","Flags":0,"SigningPubKey":"","SignerQuorum":3,"SignerEntries":[{"SignerEntry":{"Account":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW","SignerWeight":2}},{"SignerEntry":{"Account":"rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v","SignerWeight":1}},{"SignerEntry":{"Account":"raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n","SignerWeight":1}}]}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/ticket_create.rs b/src/models/transactions/ticket_create.rs index 8e55af49..1387c0ab 100644 --- a/src/models/transactions/ticket_create.rs +++ b/src/models/transactions/ticket_create.rs @@ -69,22 +69,22 @@ impl<'a> TicketCreate<'a> { ticket_count: u32, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::TicketCreate, + TransactionType::TicketCreate, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), ticket_count, } } @@ -108,7 +108,7 @@ mod tests { None, 10, ); - let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"TicketCreate","Fee":"10","Flags":0,"Sequence":381,"TicketCount":10}"#; + let default_json_str = r#"{"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","TransactionType":"TicketCreate","Fee":"10","Flags":0,"Sequence":381,"SigningPubKey":"","TicketCount":10}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/trust_set.rs b/src/models/transactions/trust_set.rs index 3dc26253..0aeb0ce1 100644 --- a/src/models/transactions/trust_set.rs +++ b/src/models/transactions/trust_set.rs @@ -107,22 +107,22 @@ impl<'a> TrustSet<'a> { quality_out: Option, ) -> Self { Self { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::TrustSet, + TransactionType::TrustSet, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), limit_amount, quality_in, quality_out, @@ -156,7 +156,7 @@ mod tests { None, None, ); - let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"TrustSet","Fee":"12","Flags":262144,"LastLedgerSequence":8007750,"Sequence":12,"LimitAmount":{"currency":"USD","issuer":"rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc","value":"100"}}"#; + let default_json_str = r#"{"Account":"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX","TransactionType":"TrustSet","Fee":"12","Flags":262144,"LastLedgerSequence":8007750,"Sequence":12,"SigningPubKey":"","LimitAmount":{"currency":"USD","issuer":"rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc","value":"100"}}"#; // Serialize let default_json_value = serde_json::to_value(default_json_str).unwrap(); let serialized_string = serde_json::to_string(&default_txn).unwrap(); diff --git a/src/models/transactions/xchain_account_create_commit.rs b/src/models/transactions/xchain_account_create_commit.rs index 7f86fc4b..9ec934e7 100644 --- a/src/models/transactions/xchain_account_create_commit.rs +++ b/src/models/transactions/xchain_account_create_commit.rs @@ -54,22 +54,22 @@ impl<'a> XChainAccountCreateCommit<'a> { signature_reward: Option>, ) -> XChainAccountCreateCommit<'a> { XChainAccountCreateCommit { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainAccountCreateCommit, + TransactionType::XChainAccountCreateCommit, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, destination, xchain_bridge, diff --git a/src/models/transactions/xchain_add_account_create_attestation.rs b/src/models/transactions/xchain_add_account_create_attestation.rs index 89183106..d3f080fe 100644 --- a/src/models/transactions/xchain_add_account_create_attestation.rs +++ b/src/models/transactions/xchain_add_account_create_attestation.rs @@ -67,22 +67,22 @@ impl<'a> XChainAddAccountCreateAttestation<'a> { xchain_bridge: XChainBridge<'a>, ) -> XChainAddAccountCreateAttestation<'a> { XChainAddAccountCreateAttestation { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainAddAccountCreateAttestation, + TransactionType::XChainAddAccountCreateAttestation, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, attestation_reward_account, attestation_signer_account, diff --git a/src/models/transactions/xchain_add_claim_attestation.rs b/src/models/transactions/xchain_add_claim_attestation.rs index 29299160..674aa5f9 100644 --- a/src/models/transactions/xchain_add_claim_attestation.rs +++ b/src/models/transactions/xchain_add_claim_attestation.rs @@ -65,22 +65,22 @@ impl<'a> XChainAddClaimAttestation<'a> { destination: Option>, ) -> XChainAddClaimAttestation<'a> { XChainAddClaimAttestation { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainAddClaimAttestation, + TransactionType::XChainAddClaimAttestation, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, attestation_reward_account, attestation_signer_account, diff --git a/src/models/transactions/xchain_claim.rs b/src/models/transactions/xchain_claim.rs index 9065ff1b..03a54411 100644 --- a/src/models/transactions/xchain_claim.rs +++ b/src/models/transactions/xchain_claim.rs @@ -63,22 +63,22 @@ impl<'a> XChainClaim<'a> { destination_tag: Option, ) -> XChainClaim<'a> { XChainClaim { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainClaim, + TransactionType::XChainClaim, account_txn_id, fee, + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - flags: FlagCollection::default(), - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, destination, xchain_bridge, diff --git a/src/models/transactions/xchain_commit.rs b/src/models/transactions/xchain_commit.rs index e880406a..507a1c09 100644 --- a/src/models/transactions/xchain_commit.rs +++ b/src/models/transactions/xchain_commit.rs @@ -53,22 +53,22 @@ impl<'a> XChainCommit<'a> { other_chain_destination: Option>, ) -> XChainCommit<'a> { XChainCommit { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainCommit, + TransactionType::XChainCommit, account_txn_id, fee, + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - flags: FlagCollection::default(), - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), amount, other_chain_destination, xchain_bridge, diff --git a/src/models/transactions/xchain_create_bridge.rs b/src/models/transactions/xchain_create_bridge.rs index 4ff0126d..03492275 100644 --- a/src/models/transactions/xchain_create_bridge.rs +++ b/src/models/transactions/xchain_create_bridge.rs @@ -60,22 +60,22 @@ impl<'a> XChainCreateBridge<'a> { min_account_create_amount: Option>, ) -> XChainCreateBridge<'a> { XChainCreateBridge { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainCreateBridge, + TransactionType::XChainCreateBridge, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), signature_reward, xchain_bridge, min_account_create_amount, diff --git a/src/models/transactions/xchain_create_claim_id.rs b/src/models/transactions/xchain_create_claim_id.rs index 375f92dc..bd474729 100644 --- a/src/models/transactions/xchain_create_claim_id.rs +++ b/src/models/transactions/xchain_create_claim_id.rs @@ -60,22 +60,22 @@ impl<'a> XChainCreateClaimID<'a> { xchain_bridge: XChainBridge<'a>, ) -> XChainCreateClaimID<'a> { XChainCreateClaimID { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainCreateClaimID, + TransactionType::XChainCreateClaimID, account_txn_id, fee, - flags: FlagCollection::default(), + Some(FlagCollection::default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), other_chain_source, signature_reward, xchain_bridge, diff --git a/src/models/transactions/xchain_modify_bridge.rs b/src/models/transactions/xchain_modify_bridge.rs index 0662d05f..33be343b 100644 --- a/src/models/transactions/xchain_modify_bridge.rs +++ b/src/models/transactions/xchain_modify_bridge.rs @@ -72,22 +72,22 @@ impl<'a> XChainModifyBridge<'a> { signature_reward: Option>, ) -> XChainModifyBridge<'a> { XChainModifyBridge { - common_fields: CommonFields { + common_fields: CommonFields::new( account, - transaction_type: TransactionType::XChainModifyBridge, + TransactionType::XChainModifyBridge, account_txn_id, fee, - flags: flags.unwrap_or_default(), + Some(flags.unwrap_or_default()), last_ledger_sequence, memos, + None, sequence, signers, + None, source_tag, ticket_sequence, - network_id: None, - signing_pub_key: None, - txn_signature: None, - }, + None, + ), xchain_bridge, min_account_create_amount, signature_reward, diff --git a/src/transaction/multisign.rs b/src/transaction/multisign.rs index 41c400d5..e41fd41c 100644 --- a/src/transaction/multisign.rs +++ b/src/transaction/multisign.rs @@ -37,40 +37,32 @@ where #[cfg(test)] mod test { use alloc::borrow::Cow; - use alloc::{dbg, vec}; use super::*; - use crate::asynch::clients::XRPLAsyncClient; - use crate::asynch::transaction::{autofill, sign}; + use crate::asynch::transaction::sign; use crate::asynch::wallet::generate_faucet_wallet; use crate::clients::json_rpc::JsonRpcClient; - use crate::models::requests::submit_multisigned::SubmitMultisigned; use crate::models::transactions::account_set::AccountSet; - use crate::models::transactions::signer_list_set::SignerEntry; use crate::wallet::Wallet; #[tokio::test] async fn test_multisign() { let client = JsonRpcClient::connect("https://s.altnet.rippletest.net:51234".parse().unwrap()); - let wallet = Wallet::create(None).unwrap(); + let wallet = Wallet::new("sEdSkooMk31MeTjbHVE7vLvgCpEMAdB", 0).unwrap(); let wallet = generate_faucet_wallet(&client, Some(wallet), None, None, None) .await .unwrap(); let first_signer = Wallet::new("sEdTLQkHAWpdS7FDk7EvuS7Mz8aSMRh", 0).unwrap(); let second_signer = Wallet::new("sEd7DXaHkGQD8mz8xcRLDxfMLqCurif", 0).unwrap(); - let signer_entries = vec![ - SignerEntry::new(first_signer.classic_address.clone(), 1), - SignerEntry::new(second_signer.classic_address.clone(), 1), - ]; let mut account_set_txn = AccountSet::new( Cow::from(wallet.classic_address.clone()), None, + Some("40".into()), None, + Some(4814775), None, - None, - None, - None, + Some(4814738), None, None, None, @@ -83,28 +75,35 @@ mod test { None, None, ); - autofill( - &mut account_set_txn, - &client, - Some(signer_entries.len().try_into().unwrap()), - ) - .await - .unwrap(); let mut tx_1 = account_set_txn.clone(); sign(&mut tx_1, &first_signer, true).unwrap(); + let tx_1_expected_signature = "E3BEF86AEFC61E5ED66C95D0C5CE699721A8DAF86B6ED0D1CBAC86C2C03D96A098767B4F163FADBD937A99AC40BD6CED16B2CA98B198C2343D4BA31ECE57530C"; + assert_eq!( + tx_1.get_common_fields().signers.as_ref().unwrap()[0] + .txn_signature + .as_str(), + tx_1_expected_signature + ); let mut tx_2 = account_set_txn.clone(); sign(&mut tx_2, &second_signer, true).unwrap(); + let tx_2_expected_signature = "DB64FC69F34A4881F6087226681E7BDDB212027B3FAFB617E598DCA5BBC8FA1A15A6E37A760B534BA554FBCD8D4A9FDEC8DFED206E3EBC393B875F59C765D304"; + assert_eq!( + tx_2.get_common_fields().signers.as_ref().unwrap()[0] + .txn_signature + .as_str(), + tx_2_expected_signature + ); let tx_list = [tx_1.clone(), tx_2.clone()].to_vec(); - dbg!(&account_set_txn, &tx_list); multisign(&mut account_set_txn, &tx_list).unwrap(); - dbg!(&account_set_txn); assert!(account_set_txn.get_common_fields().is_signed()); - let res = client - .request( - SubmitMultisigned::new(None, serde_json::to_value(&account_set_txn).unwrap(), None) - .into(), - ) - .await; - dbg!(&res); + assert_eq!( + account_set_txn + .get_common_fields() + .signers + .as_ref() + .unwrap() + .len(), + 2 + ); } } From bbfb487f06af51604eb89b04061887bddf50fa3f Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 13 Feb 2025 17:41:05 +0000 Subject: [PATCH 15/26] fix multisign test --- src/transaction/multisign.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/transaction/multisign.rs b/src/transaction/multisign.rs index e41fd41c..9877cf99 100644 --- a/src/transaction/multisign.rs +++ b/src/transaction/multisign.rs @@ -40,19 +40,12 @@ mod test { use super::*; use crate::asynch::transaction::sign; - use crate::asynch::wallet::generate_faucet_wallet; - use crate::clients::json_rpc::JsonRpcClient; use crate::models::transactions::account_set::AccountSet; use crate::wallet::Wallet; #[tokio::test] async fn test_multisign() { - let client = - JsonRpcClient::connect("https://s.altnet.rippletest.net:51234".parse().unwrap()); let wallet = Wallet::new("sEdSkooMk31MeTjbHVE7vLvgCpEMAdB", 0).unwrap(); - let wallet = generate_faucet_wallet(&client, Some(wallet), None, None, None) - .await - .unwrap(); let first_signer = Wallet::new("sEdTLQkHAWpdS7FDk7EvuS7Mz8aSMRh", 0).unwrap(); let second_signer = Wallet::new("sEd7DXaHkGQD8mz8xcRLDxfMLqCurif", 0).unwrap(); let mut account_set_txn = AccountSet::new( From d688fb03ceee4e45bf358253841457b843e5fdb7 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 13 Feb 2025 18:08:24 +0000 Subject: [PATCH 16/26] add changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2881ff3..bed3aa8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Models - Integration Tests - Performance Benchmarks +- Utility functions ## [[Unreleased]] +- add missing NFT request models +- add `parse_nftoken_id` and `get_nftoken_id` utility functions +- complete existing result models and add NFT result models +- add transaction `Metadata` models +- fix serialization issue where null values were tried to be serialized +- fix multisigning bug, because `signing_pub_key` is not set for multisigning but it is required, so it's just an empty string + ## [[v0.4.0]] - add amm support From 392f17b52ceffc783d4c909f7203cbdf8517cb58 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Tue, 19 Nov 2024 14:05:16 +0000 Subject: [PATCH 17/26] add get_xchain_claim_id --- src/utils/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 403c4869..2bc57956 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,6 +3,7 @@ pub mod exceptions; #[cfg(feature = "models")] pub mod get_nftoken_id; +pub mod get_xchain_claim_id; #[cfg(feature = "models")] pub mod get_xchain_claim_id; #[cfg(feature = "models")] From 87c8c467de18774149cdd0eef5a2b636576c5500 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Tue, 19 Nov 2024 18:11:59 +0000 Subject: [PATCH 18/26] current state of txn parsing --- src/models/transactions/metadata.rs | 8 +++++--- src/utils/exceptions.rs | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index a9c18cfe..db7eba7f 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -5,6 +5,7 @@ use serde_with::skip_serializing_none; use crate::models::ledger::objects::LedgerEntryType; use crate::models::requests::LedgerIndex; use crate::models::{Amount, IssuedCurrencyAmount}; +use crate::models::{Amount, IssuedCurrencyAmount}; #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -31,8 +32,7 @@ pub struct Fields<'a> { pub balance: Option>, pub book_directory: Option>, pub expiration: Option, - #[serde(default)] - pub flags: u32, + pub flags: Option, pub low_limit: Option>, pub high_limit: Option>, pub next_page_min: Option>, @@ -63,6 +63,7 @@ pub enum AffectedNode<'a> { previous_fields: Option>, previous_txn_id: Option>, previous_txn_lgr_seq: Option, + previous_txn_lgr_seq: Option, }, #[serde(rename_all = "PascalCase")] DeletedNode { @@ -73,7 +74,7 @@ pub enum AffectedNode<'a> { }, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum NodeType { CreatedNode, ModifiedNode, @@ -86,6 +87,7 @@ pub enum NodeType { pub struct TransactionMetadata<'a> { pub affected_nodes: Vec>, pub transaction_index: u32, + pub transaction_index: u32, pub transaction_result: Amount<'a>, #[serde(rename = "delivered_amount")] pub delivered_amount: Option>, diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index 7efc3031..00d3470d 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -27,6 +27,8 @@ pub enum XRPLUtilsException { XRPLTxnParserError(#[from] XRPLTxnParserException), #[error("XRPL XChain Claim ID error: {0}")] XRPLXChainClaimIdError(#[from] XRPLXChainClaimIdException), + #[error("XRPL Txn Parser error: {0}")] + XRPLTxnParserError(#[from] XRPLTxnParserException), #[error("ISO Code error: {0}")] ISOCodeError(#[from] ISOCodeException), #[error("Decimal error: {0}")] From d2e61977786c048effe69131931c9e2ace20b91d Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Mon, 25 Nov 2024 15:43:00 +0000 Subject: [PATCH 19/26] refactor transaction metadata and balance parsing logic --- src/models/transactions/metadata.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index db7eba7f..c8cf479a 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -32,14 +32,14 @@ pub struct Fields<'a> { pub balance: Option>, pub book_directory: Option>, pub expiration: Option, - pub flags: Option, + pub flags: u32, pub low_limit: Option>, pub high_limit: Option>, pub next_page_min: Option>, #[serde(rename = "NFTokens")] pub nftokens: Option>>, pub previous_page_min: Option>, - pub sequence: Option, + pub sequence: u32, pub taker_gets: Option>, pub taker_pays: Option>, pub xchain_claim_id: Option>, From 5b46652a1fca3e8be87587877d22017bc9ef4303 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 4 Jan 2025 23:10:56 +0000 Subject: [PATCH 20/26] add balance change parsing --- src/models/transactions/metadata.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index c8cf479a..db7eba7f 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -32,14 +32,14 @@ pub struct Fields<'a> { pub balance: Option>, pub book_directory: Option>, pub expiration: Option, - pub flags: u32, + pub flags: Option, pub low_limit: Option>, pub high_limit: Option>, pub next_page_min: Option>, #[serde(rename = "NFTokens")] pub nftokens: Option>>, pub previous_page_min: Option>, - pub sequence: u32, + pub sequence: Option, pub taker_gets: Option>, pub taker_pays: Option>, pub xchain_claim_id: Option>, From ba6e366a1820c008c3265b8fa0fe4ac1e5e7d227 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 15 Feb 2025 16:23:55 +0000 Subject: [PATCH 21/26] resolve errors caused by merging --- src/models/flag_collection.rs | 14 ++++++++++++++ src/utils/exceptions.rs | 2 -- src/utils/mod.rs | 1 - 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/models/flag_collection.rs b/src/models/flag_collection.rs index e5dfb093..c9d53672 100644 --- a/src/models/flag_collection.rs +++ b/src/models/flag_collection.rs @@ -66,6 +66,20 @@ where } } +impl TryFrom> for FlagCollection +where + T: IntoEnumIterator + Serialize, +{ + type Error = XRPLModelException; + + fn try_from(flags: Option) -> XRPLModelResult { + match flags { + Some(flags) => FlagCollection::try_from(flags), + None => Ok(FlagCollection::default()), + } + } +} + impl TryFrom> for u32 where T: IntoEnumIterator + Serialize, diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs index 00d3470d..7efc3031 100644 --- a/src/utils/exceptions.rs +++ b/src/utils/exceptions.rs @@ -27,8 +27,6 @@ pub enum XRPLUtilsException { XRPLTxnParserError(#[from] XRPLTxnParserException), #[error("XRPL XChain Claim ID error: {0}")] XRPLXChainClaimIdError(#[from] XRPLXChainClaimIdException), - #[error("XRPL Txn Parser error: {0}")] - XRPLTxnParserError(#[from] XRPLTxnParserException), #[error("ISO Code error: {0}")] ISOCodeError(#[from] ISOCodeException), #[error("Decimal error: {0}")] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2bc57956..403c4869 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,7 +3,6 @@ pub mod exceptions; #[cfg(feature = "models")] pub mod get_nftoken_id; -pub mod get_xchain_claim_id; #[cfg(feature = "models")] pub mod get_xchain_claim_id; #[cfg(feature = "models")] From b0b075ac9e9343c1c719b33308b020e33cdbc80d Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Sat, 15 Feb 2025 16:24:54 +0000 Subject: [PATCH 22/26] resolve errors caused by merging --- src/models/transactions/metadata.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index db7eba7f..63615e79 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -5,7 +5,6 @@ use serde_with::skip_serializing_none; use crate::models::ledger::objects::LedgerEntryType; use crate::models::requests::LedgerIndex; use crate::models::{Amount, IssuedCurrencyAmount}; -use crate::models::{Amount, IssuedCurrencyAmount}; #[skip_serializing_none] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -63,7 +62,6 @@ pub enum AffectedNode<'a> { previous_fields: Option>, previous_txn_id: Option>, previous_txn_lgr_seq: Option, - previous_txn_lgr_seq: Option, }, #[serde(rename_all = "PascalCase")] DeletedNode { @@ -87,7 +85,6 @@ pub enum NodeType { pub struct TransactionMetadata<'a> { pub affected_nodes: Vec>, pub transaction_index: u32, - pub transaction_index: u32, pub transaction_result: Amount<'a>, #[serde(rename = "delivered_amount")] pub delivered_amount: Option>, From 765be3c808d4d0ceed0a673f1105d9d554fa0295 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 27 Feb 2025 16:34:25 +0000 Subject: [PATCH 23/26] remove order book changes parser --- src/utils/txn_parser/get_final_order_book.rs | 0 .../txn_parser/get_order_book_changes.rs | 37 --- src/utils/txn_parser/mod.rs | 2 - src/utils/txn_parser/utils/mod.rs | 1 - .../txn_parser/utils/order_book_parser.rs | 214 ------------------ 5 files changed, 254 deletions(-) delete mode 100644 src/utils/txn_parser/get_final_order_book.rs delete mode 100644 src/utils/txn_parser/get_order_book_changes.rs delete mode 100644 src/utils/txn_parser/utils/order_book_parser.rs diff --git a/src/utils/txn_parser/get_final_order_book.rs b/src/utils/txn_parser/get_final_order_book.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/utils/txn_parser/get_order_book_changes.rs b/src/utils/txn_parser/get_order_book_changes.rs deleted file mode 100644 index bef9a4ee..00000000 --- a/src/utils/txn_parser/get_order_book_changes.rs +++ /dev/null @@ -1,37 +0,0 @@ -use alloc::vec::Vec; - -use crate::{ - models::transactions::metadata::TransactionMetadata, utils::exceptions::XRPLUtilsResult, -}; - -use super::utils::{order_book_parser::compute_order_book_changes, AccountOfferChanges}; - -pub fn get_order_book_changes<'a: 'b, 'b>( - meta: &'a TransactionMetadata<'a>, -) -> XRPLUtilsResult>> { - compute_order_book_changes(meta) -} - -#[cfg(test)] -mod test { - use core::cell::LazyCell; - - use serde_json::Value; - - use super::*; - - use crate::models::transactions::metadata::TransactionMetadata; - - #[test] - fn test_get_order_book_changes() { - let txn: LazyCell = LazyCell::new(|| { - let txn_value: Value = - serde_json::from_str(include_str!("./test_data/offer_created.json")).unwrap(); - let txn_meta = txn_value["meta"].clone(); - let txn_meta: TransactionMetadata = serde_json::from_value(txn_meta).unwrap(); - - txn_meta - }); - assert!(get_order_book_changes(&txn).is_ok()); - } -} diff --git a/src/utils/txn_parser/mod.rs b/src/utils/txn_parser/mod.rs index d4fcad89..6d4b8769 100644 --- a/src/utils/txn_parser/mod.rs +++ b/src/utils/txn_parser/mod.rs @@ -1,5 +1,3 @@ pub mod get_balance_changes; pub mod get_final_balances; -pub mod get_final_order_book; -pub mod get_order_book_changes; mod utils; diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs index aff9137d..fd5d46a0 100644 --- a/src/utils/txn_parser/utils/mod.rs +++ b/src/utils/txn_parser/utils/mod.rs @@ -10,7 +10,6 @@ use crate::{ pub mod balance_parser; pub mod nodes; -pub mod order_book_parser; pub mod parser; #[derive(Debug, Clone)] diff --git a/src/utils/txn_parser/utils/order_book_parser.rs b/src/utils/txn_parser/utils/order_book_parser.rs deleted file mode 100644 index feb400e6..00000000 --- a/src/utils/txn_parser/utils/order_book_parser.rs +++ /dev/null @@ -1,214 +0,0 @@ -use alloc::{string::ToString, vec::Vec}; -use bigdecimal::BigDecimal; - -use crate::{ - account, - models::{ - ledger::objects::LedgerEntryType, - transactions::metadata::{NodeType, TransactionMetadata}, - Amount, - }, - utils::{ - exceptions::XRPLUtilsResult, - txn_parser::utils::{nodes::normalize_nodes, OfferChange}, - }, -}; - -use super::{nodes::NormalizedNode, AccountOfferChange, AccountOfferChanges, Balance, OfferStatus}; - -const LSF_SELL: u32 = 0x00020000; - -enum OfferSide { - TakerGets, - TakerPays, -} - -fn get_offer_status(node: &NormalizedNode<'_>) -> OfferStatus { - match node.node_type { - NodeType::CreatedNode => OfferStatus::Created, - NodeType::ModifiedNode => OfferStatus::PartiallyFilled, - NodeType::DeletedNode => { - if node.previous_fields.is_some() { - // a filled offer has previous fields - OfferStatus::Filled - } else { - OfferStatus::Cancelled - } - } - } -} - -fn calculate_delta( - previous_balance: &Balance, - final_balance: &Balance, -) -> XRPLUtilsResult { - let previous_value: BigDecimal = previous_balance.value.parse()?; - let final_value: BigDecimal = final_balance.value.parse()?; - - Ok(final_value - previous_value) -} - -fn derive_currency_amount<'a: 'b, 'b>(currency_amount: &'a Amount) -> Balance<'b> { - match currency_amount { - Amount::XRPAmount(amount) => Balance { - currency: "XRP".into(), - value: amount.0.clone(), - issuer: None, - }, - Amount::IssuedCurrencyAmount(amount) => Balance { - currency: amount.currency.clone(), - value: amount.value.clone(), - issuer: Some(amount.issuer.clone()), - }, - } -} - -fn get_change_amount<'a: 'b, 'b>( - node: &'a NormalizedNode<'a>, - side: OfferSide, -) -> XRPLUtilsResult>> { - if let Some(new_fields) = &node.new_fields { - let amount = match side { - OfferSide::TakerGets => &new_fields.taker_gets, - OfferSide::TakerPays => &new_fields.taker_pays, - }; - if let Some(amount) = amount { - Ok(Some(derive_currency_amount(amount))) - } else { - Ok(None) - } - } else if let Some(final_fields) = &node.final_fields { - let final_fields_amount = match side { - OfferSide::TakerGets => &final_fields.taker_gets, - OfferSide::TakerPays => &final_fields.taker_pays, - }; - let previous_fields_amount = match side { - OfferSide::TakerGets => &node.previous_fields.as_ref().unwrap().taker_gets, - OfferSide::TakerPays => &node.previous_fields.as_ref().unwrap().taker_pays, - }; - if let (Some(final_fields_amount), Some(previous_fields_amount)) = - (final_fields_amount, previous_fields_amount) - { - let final_balance = derive_currency_amount(final_fields_amount); - let previous_balance = derive_currency_amount(previous_fields_amount); - let change = calculate_delta(&previous_balance, &final_balance)?; - Ok(Some(Balance { - currency: final_balance.currency, - value: change.to_string().into(), - issuer: final_balance.issuer, - })) - } else if let (Some(final_fields_amount), None) = - (final_fields_amount, previous_fields_amount) - { - let final_balance = derive_currency_amount(final_fields_amount); - let final_balance_value: BigDecimal = final_balance.value.parse()?; - let value: BigDecimal = 0 - final_balance_value; - Ok(Some(Balance { - currency: final_balance.currency, - value: value.to_string().into(), - issuer: final_balance.issuer, - })) - } else { - Ok(None) - } - } else { - Ok(None) - } -} - -fn get_quality(taker_gets: &Balance, taker_pays: &Balance) -> XRPLUtilsResult { - let taker_gets_value: BigDecimal = taker_gets.value.parse()?; - let taker_pays_value: BigDecimal = taker_pays.value.parse()?; - let quality = taker_pays_value / taker_gets_value; - - Ok(quality.normalized()) -} - -fn get_offer_change<'a: 'b, 'b>( - node: &'a NormalizedNode<'a>, -) -> XRPLUtilsResult>> { - let status = get_offer_status(node); - let taker_gets = get_change_amount(node, OfferSide::TakerGets)?; - let taker_pays = get_change_amount(node, OfferSide::TakerPays)?; - let account = if let Some(new_fields) = &node.new_fields { - new_fields.account.as_ref().map(|account| account) - } else if let Some(final_fields) = &node.final_fields { - final_fields.account.as_ref().map(|account| account) - } else { - None - }; - let sequence = if let Some(new_fields) = &node.new_fields { - new_fields.sequence - } else if let Some(final_fields) = &node.final_fields { - final_fields.sequence - } else { - None - }; - let flags = if let Some(new_fields) = &node.new_fields { - Some(new_fields.flags) - } else if let Some(final_fields) = &node.final_fields { - Some(final_fields.flags) - } else { - None - }; - if taker_gets.is_none() - || taker_pays.is_none() - || account.is_none() - || sequence.is_none() - || flags.is_none() - { - return Ok(None); - } - let taker_gets = taker_gets.unwrap(); - let taker_pays = taker_pays.unwrap(); - let account = account.unwrap(); - let sequence = sequence.unwrap(); - let flags = flags.unwrap(); - - let expiration_time = if let Some(new_fields) = &node.new_fields { - new_fields.expiration - } else if let Some(final_fields) = &node.final_fields { - final_fields.expiration - } else { - None - }; - let quality = get_quality(&taker_gets, &taker_pays)?; - let offer_change = OfferChange { - flags: flags.try_into()?, - taker_gets: taker_gets.into(), - taker_pays: taker_pays.into(), - sequence, - status, - maker_exchange_rate: Some(quality), - expiration_time, - }; - - Ok(Some(AccountOfferChange { - maker_account: account.clone(), - offer_change, - })) -} - -fn group_offer_changes_by_account<'a: 'b, 'b>( - account_offer_changes: Vec>, -) -> Vec> { - todo!() -} - -pub fn compute_order_book_changes<'a: 'b, 'b>( - meta: &'a TransactionMetadata<'a>, -) -> XRPLUtilsResult>> { - let normalized_nodes = normalize_nodes(meta); - let offer_nodes = normalized_nodes - .iter() - .filter(|node| node.ledger_entry_type == LedgerEntryType::Offer) - .collect::>(); - let mut offer_changes = Vec::new(); - for node in offer_nodes { - if let Some(offer_change) = get_offer_change(node)? { - offer_changes.push(offer_change); - } - } - - Ok(group_offer_changes_by_account(offer_changes)) -} From 8159f3b9162f722027dd2040c5f91bb76ad974e9 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 27 Feb 2025 16:36:48 +0000 Subject: [PATCH 24/26] fix deref error in account_info --- src/models/results/account_info.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/results/account_info.rs b/src/models/results/account_info.rs index e7942cb2..dffeef76 100644 --- a/src/models/results/account_info.rs +++ b/src/models/results/account_info.rs @@ -171,7 +171,7 @@ impl<'a> TryFrom> for AccountInfoVersionMap<'a> { fn try_from(result: XRPLResult<'a>) -> XRPLModelResult { match result { - XRPLResult::AccountInfo(account_info) => Ok(*account_info), + XRPLResult::AccountInfo(account_info) => Ok(account_info), res => Err(XRPLResultException::UnexpectedResultType( "AccountInfo".to_string(), res.get_name(), From 7b68facbbf8a54463053185b3b1ceb0b909b5ae5 Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 27 Feb 2025 16:51:43 +0000 Subject: [PATCH 25/26] improve tests --- src/utils/txn_parser/get_balance_changes.rs | 48 ++++++++++++++++++++- src/utils/txn_parser/get_final_balances.rs | 48 ++++++++++++++++++++- src/utils/txn_parser/utils/mod.rs | 4 +- 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/src/utils/txn_parser/get_balance_changes.rs b/src/utils/txn_parser/get_balance_changes.rs index 3f3d85c4..18a22bca 100644 --- a/src/utils/txn_parser/get_balance_changes.rs +++ b/src/utils/txn_parser/get_balance_changes.rs @@ -10,6 +10,7 @@ use super::utils::{ balance_parser::derive_account_balances, nodes::NormalizedNode, AccountBalances, }; +/// Parses the balance changes of all accounts affected by a transaction from the transaction metadata. pub fn get_balance_changes<'a: 'b, 'b>( meta: &'a TransactionMetadata<'a>, ) -> XRPLUtilsResult>> { @@ -51,7 +52,9 @@ mod test { use serde_json::Value; use super::*; - use crate::models::transactions::metadata::TransactionMetadata; + use crate::{ + models::transactions::metadata::TransactionMetadata, utils::txn_parser::utils::Balance, + }; #[test] fn test_parse_balance_changes() { @@ -63,6 +66,47 @@ mod test { txn_meta }); - assert!(get_balance_changes(&txn).is_ok()); + let expected_balances = Vec::from([ + AccountBalances { + account: "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc".into(), + balances: Vec::from([ + Balance { + currency: "USD".into(), + value: "-0.01".into(), + issuer: Some("rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q".into()), + }, + Balance { + currency: "XRP".into(), + value: "-0.012".into(), + issuer: None, + }, + ]), + }, + AccountBalances { + account: "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q".into(), + balances: Vec::from([ + Balance { + currency: "USD".into(), + value: "0.01".into(), + issuer: Some("rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc".into()), + }, + Balance { + currency: "USD".into(), + value: "-0.01".into(), + issuer: Some("rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K".into()), + }, + ]), + }, + AccountBalances { + account: "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K".into(), + balances: Vec::from([Balance { + currency: "USD".into(), + value: "0.01".into(), + issuer: Some("rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q".into()), + }]), + }, + ]); + let balance_changes = get_balance_changes(&txn).unwrap(); + assert_eq!(expected_balances, balance_changes); } } diff --git a/src/utils/txn_parser/get_final_balances.rs b/src/utils/txn_parser/get_final_balances.rs index f21ac15f..2a627389 100644 --- a/src/utils/txn_parser/get_final_balances.rs +++ b/src/utils/txn_parser/get_final_balances.rs @@ -8,6 +8,7 @@ use crate::{ use alloc::vec::Vec; use bigdecimal::BigDecimal; +/// Parses the final balances of all accounts affected by a transaction from the transaction metadata. pub fn get_final_balances<'a: 'b, 'b>( metadata: &'a TransactionMetadata<'a>, ) -> XRPLUtilsResult>> { @@ -38,7 +39,9 @@ mod test { use serde_json::Value; use super::*; - use crate::models::transactions::metadata::TransactionMetadata; + use crate::{ + models::transactions::metadata::TransactionMetadata, utils::txn_parser::utils::Balance, + }; #[test] fn test_parse_final_balances() { @@ -50,6 +53,47 @@ mod test { txn_meta }); - assert!(get_final_balances(&txn).is_ok()); + let expected_balances = Vec::from([ + AccountBalances { + account: "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc".into(), + balances: Vec::from([ + Balance { + currency: "USD".into(), + value: "1.525330905250352".into(), + issuer: Some("rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q".into()), + }, + Balance { + currency: "XRP".into(), + value: "-239.555992".into(), + issuer: None, + }, + ]), + }, + AccountBalances { + account: "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q".into(), + balances: Vec::from([ + Balance { + currency: "USD".into(), + value: "-1.525330905250352".into(), + issuer: Some("rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc".into()), + }, + Balance { + currency: "USD".into(), + value: "-0.02".into(), + issuer: Some("rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K".into()), + }, + ]), + }, + AccountBalances { + account: "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K".into(), + balances: Vec::from([Balance { + currency: "USD".into(), + value: "0.02".into(), + issuer: Some("rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q".into()), + }]), + }, + ]); + let final_balances = get_final_balances(&txn).unwrap(); + assert_eq!(final_balances, expected_balances); } } diff --git a/src/utils/txn_parser/utils/mod.rs b/src/utils/txn_parser/utils/mod.rs index fd5d46a0..2f5ed5cc 100644 --- a/src/utils/txn_parser/utils/mod.rs +++ b/src/utils/txn_parser/utils/mod.rs @@ -12,7 +12,7 @@ pub mod balance_parser; pub mod nodes; pub mod parser; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Balance<'a> { pub currency: Cow<'a, str>, pub value: Cow<'a, str>, @@ -56,7 +56,7 @@ pub struct AccountBalance<'a> { pub balance: Balance<'a>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct AccountBalances<'a> { pub account: Cow<'a, str>, pub balances: Vec>, From 110b97152d5025d97e3ae38946b82fc1a225b24c Mon Sep 17 00:00:00 2001 From: LimpidCrypto Date: Thu, 27 Feb 2025 17:21:24 +0000 Subject: [PATCH 26/26] revert metadata changes --- src/models/transactions/metadata.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/models/transactions/metadata.rs b/src/models/transactions/metadata.rs index 63615e79..1e45c85d 100644 --- a/src/models/transactions/metadata.rs +++ b/src/models/transactions/metadata.rs @@ -31,14 +31,16 @@ pub struct Fields<'a> { pub balance: Option>, pub book_directory: Option>, pub expiration: Option, - pub flags: Option, + #[serde(default)] + pub flags: u32, pub low_limit: Option>, pub high_limit: Option>, pub next_page_min: Option>, #[serde(rename = "NFTokens")] pub nftokens: Option>>, pub previous_page_min: Option>, - pub sequence: Option, + #[serde(default)] + pub sequence: u32, pub taker_gets: Option>, pub taker_pays: Option>, pub xchain_claim_id: Option>, @@ -72,7 +74,7 @@ pub enum AffectedNode<'a> { }, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum NodeType { CreatedNode, ModifiedNode,