From 84d94ac71680802263c88f2c0425184d2b409de9 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Wed, 13 Mar 2024 15:32:46 +0100 Subject: [PATCH 1/4] fee stats refactor --- src/fee_stats/fee_stats_request.rs | 41 +++++++++++++++++ src/fee_stats/mod.rs | 33 ++++++++++++++ src/fee_stats/response.rs | 73 ++++++++++++++++++++++++++++++ src/horizon_client.rs | 47 +++++++++++++++++++ src/lib.rs | 36 +++++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 src/fee_stats/fee_stats_request.rs create mode 100644 src/fee_stats/mod.rs create mode 100644 src/fee_stats/response.rs diff --git a/src/fee_stats/fee_stats_request.rs b/src/fee_stats/fee_stats_request.rs new file mode 100644 index 0000000..96b3179 --- /dev/null +++ b/src/fee_stats/fee_stats_request.rs @@ -0,0 +1,41 @@ +use crate::models::Request; + +/// Represents a request to fetch fee stats from the Stellar Horizon API. +/// +/// `FeeStatsRequest` is a struct used to construct queries for retrieving information about fee stats +/// from the Horizon server. This request does not include any parameters. +/// +/// # Usage +/// Instances of `FeeStatsRequest` are created and optionally configured using the builder pattern. +/// Once the desired parameters are set, the request can be passed to the Horizon client to fetch +/// fee stats. +/// +/// # Example +/// ```rust +/// use stellar_rs::fee_stats::fee_stats_request::FeeStatsRequest; +/// use stellar_rs::models::*; +/// +/// let request = FeeStatsRequest::new(); +/// +/// // The request can now be used with a Horizon client to fetch fee stats. +/// ``` +/// +#[derive(Default)] +pub struct FeeStatsRequest {} + +impl FeeStatsRequest { + /// Creates a new `FeeStatsRequest` with default parameters. + pub fn new() -> FeeStatsRequest { + FeeStatsRequest::default() + } +} + +impl Request for FeeStatsRequest { + fn get_query_parameters(&self) -> String { + "".to_string() + } + + fn build_url(&self, base_url: &str) -> String { + format!("{}/{}", base_url, super::FEE_STATS_PATH) + } +} \ No newline at end of file diff --git a/src/fee_stats/mod.rs b/src/fee_stats/mod.rs new file mode 100644 index 0000000..adde046 --- /dev/null +++ b/src/fee_stats/mod.rs @@ -0,0 +1,33 @@ +pub mod fee_stats_request; +pub mod response; + +static FEE_STATS_PATH: &str = "fee_stats"; + +pub mod prelude { + pub use super::fee_stats_request::*; + pub use super::response::*; +} + +#[cfg(test)] +mod tests { + use super::prelude::*; + use crate::horizon_client::HorizonClient; + + #[test] + fn dummy_test() { + assert_eq!(super::FEE_STATS_PATH, "fee_stats"); + } + + #[tokio::test] + async fn test_get_fee_stats() { + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + let fee_stats_request = FeeStatsRequest::new(); + let _fee_stats_response = horizon_client.get_fee_stats(&fee_stats_request).await; + + assert!(_fee_stats_response.is_ok()); + + // there is not much use in testing the values of the response, as they are subject to constant change + } +} diff --git a/src/fee_stats/response.rs b/src/fee_stats/response.rs new file mode 100644 index 0000000..c53422d --- /dev/null +++ b/src/fee_stats/response.rs @@ -0,0 +1,73 @@ +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; + +use crate::models::Response; + +/// Represents the response from the Stellar Horizon API when requesting fee stats. +/// +/// This struct encapsulates detailed information about the fee stats, including the last ledger, +/// last ledger base fee, ledger capacity usage, and the fee charged and max fee for transactions. +/// +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct FeeStatsResponse { + /// The last ledger number. + #[serde(rename = "last_ledger")] + pub last_ledger: String, + /// The last ledger base fee. + #[serde(rename = "last_ledger_base_fee")] + pub last_ledger_base_fee: String, + /// The ledger capacity usage. + #[serde(rename = "ledger_capacity_usage")] + pub ledger_capacity_usage: String, + /// The fee charged for transactions. + #[serde(rename = "fee_charged")] + pub fee_charged: Fee, + /// The maximum fee for transactions. + #[serde(rename = "max_fee")] + pub max_fee: Fee, +} + +/// Represents the fee charged and max fee for transactions. +/// +/// This struct encapsulates detailed information about the fee charged and max fee for transactions, +/// including the max, min, mode, and percentile values. +/// +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Fee { + /// The maximum fee for transactions. + pub max: String, + /// The minimum fee for transactions. + pub min: String, + /// The mode fee for transactions. + pub mode: String, + /// The 10th percentile fee for transactions. + pub p10: String, + /// The 20th percentile fee for transactions. + pub p20: String, + /// The 30th percentile fee for transactions. + pub p30: String, + /// The 40th percentile fee for transactions. + pub p40: String, + /// The 50th percentile fee for transactions. + pub p50: String, + /// The 60th percentile fee for transactions. + pub p60: String, + /// The 70th percentile fee for transactions. + pub p70: String, + /// The 80th percentile fee for transactions. + pub p80: String, + /// The 90th percentile fee for transactions. + pub p90: String, + /// The 95th percentile fee for transactions. + pub p95: String, + /// The 99th percentile fee for transactions. + pub p99: String, +} + +impl Response for FeeStatsResponse { + fn from_json(json: String) -> Result { + serde_json::from_str(&json).map_err(|e| e.to_string()) + } +} \ No newline at end of file diff --git a/src/horizon_client.rs b/src/horizon_client.rs index 0d11526..5c54b97 100644 --- a/src/horizon_client.rs +++ b/src/horizon_client.rs @@ -7,6 +7,7 @@ use crate::{ single_claimable_balance_request::{ClaimableBalanceId, SingleClaimableBalanceRequest}, }, effects::prelude::*, + fee_stats::{fee_stats_request::FeeStatsRequest, response::FeeStatsResponse}, ledgers::{ prelude::{Ledger, LedgersRequest, LedgersResponse, SingleLedgerRequest}, single_ledger_request::Sequence, @@ -625,6 +626,52 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves a list of all fee stats from the Horizon server. + /// + /// This asynchronous method fetches a list of all fee stats from the Horizon server. + /// It requires a [`FeeStatsRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to a [`FeeStatsRequest`] instance, containing the + /// parameters for the fee stats request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing a [`FeeStatsResponse`], which includes + /// the list of all fee stats obtained from the Horizon server. If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`FeeStatsRequest`] and set any desired + /// filters or parameters. + /// + /// ``` + /// # use stellar_rs::fee_stats::fee_stats_request::FeeStatsRequest; + /// # use stellar_rs::horizon_client::HorizonClient; + /// # + /// # async fn example() -> Result<(), Box> { + /// # let base_url = "https://horizon-testnet.stellar.org".to_string(); + /// # let horizon_client = HorizonClient::new(base_url) + /// # .expect("Failed to create Horizon Client"); + /// let request = FeeStatsRequest::new(); + /// + /// let response = horizon_client.get_fee_stats(&request).await; + /// + /// // Access the fee stats + /// if let Ok(fee_stats_response) = response { + /// println!("Max Fee: {:?}", fee_stats_response.max_fee()); + /// // Further processing... + /// } + /// # Ok({}) + /// # } + /// ``` + /// + pub async fn get_fee_stats( + &self, + request: &FeeStatsRequest, + ) -> Result { + self.get::(request).await + } + /// Sends a GET request to the Horizon server and retrieves a specified response type. /// /// This internal asynchronous method is designed to handle various GET requests to the diff --git a/src/lib.rs b/src/lib.rs index 22f302f..28cac04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -307,6 +307,42 @@ pub mod ledgers; /// pub mod effects; +/// Provides `Request` and `Response` structs for retrieving fee stats. +/// +/// The `fee_stats` module in the Stellar Horizon SDK includes structures and methods that facilitate +/// querying fee stats data from the Horizon server. +/// +/// # Usage +/// +/// This module is used to construct requests for fee stats-related data and to parse the responses +/// received from the Horizon server. It includes request and response structures for querying +/// fee stats data. +/// +/// # Example +/// +/// To use this module, you can create an instance of a request struct, such as `FeeStatsRequest`, +/// and pass the request to the `HorizonClient`. The client will then execute the request and +/// return the corresponding response struct, like `FeeStatsResponse`. +/// +/// ```rust +/// use stellar_rs::horizon_client::HorizonClient; +/// use stellar_rs::fee_stats::prelude::*; +/// use stellar_rs::models::Request; +/// +/// # async fn example() -> Result<(), Box> { +/// let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string())?; +/// +/// // Example: Fetching fee stats +/// let fee_stats_request = FeeStatsRequest::new(); +/// let fee_stats_response = horizon_client.get_fee_stats(&fee_stats_request).await?; +/// +/// // Process the response... +/// # Ok(()) +/// # } +/// ``` +/// +pub mod fee_stats; + /// Contains core data structures and traits. /// /// This module is used by the Stellar Rust SDK to interact with the Horizon API. From bd3d9f91338c39daf1d25ccef210f88f53567c9b Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Wed, 13 Mar 2024 16:27:48 +0100 Subject: [PATCH 2/4] increased code cov --- src/models/mod.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/models/mod.rs b/src/models/mod.rs index 5167f79..6fe98a2 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -130,11 +130,11 @@ pub fn is_public_key(public_key: &str) -> Result<(), String> { /// Represents an issued asset. Contains both the asset code and the issuer account ID, /// formatted as "asset_code:issuer_account_id". -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct IssuedAsset(String); /// A marker type to represent the native asset (XLM) without additional data. -#[derive(Default, Clone)] +#[derive(Default, Clone, PartialEq, Debug)] pub struct NativeAsset; /// Represents the variants of assets in the Stellar network. @@ -154,7 +154,7 @@ pub struct NativeAsset; /// let issued_asset = native_asset.set_issued("USD", "GDQJUTQYK2MQX2VGDR2FYWLIYAQIEGXTQVTFEMGH2BEWFG4BRUY4CKI7").unwrap(); /// ``` /// -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct Asset { asset: T, } @@ -310,4 +310,47 @@ mod tests { ); } } + + #[test] + fn test_new_native_asset() { + let native_asset = Asset::::new(); + assert_eq!(native_asset.asset, NativeAsset); + } + + #[test] + fn test_set_issued_valid() { + let native_asset = Asset::::new(); + let issued_asset = native_asset + .set_issued( + "USD", + "GAVCBYUQSQA77EOOQMSDDXE6VSWDZRGOZOGMLWGFR6YR4TR243VWBDFO", + ) + .unwrap(); + assert_eq!( + issued_asset.asset.0, + "USD:GAVCBYUQSQA77EOOQMSDDXE6VSWDZRGOZOGMLWGFR6YR4TR243VWBDFO" + ); + } + + #[test] + fn test_set_issued_invalid_asset_code() { + let native_asset = Asset::::new(); + let result = native_asset.set_issued( + "LONG_ASSET_CODE", + "GAVCBYUQSQA77EOOQMSDDXE6VSWDZRGOZOGMLWGFR6YR4TR243VWBDFO", + ); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + "asset_code must be 12 characters or less" + ); + } + + #[test] + fn test_set_issued_invalid_public_key() { + let native_asset = Asset::::new(); + let result = native_asset.set_issued("USD", "INVALID_PUBLIC_KEY"); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), "Public key must be 56 characters long"); + } } From ae1f375d498732146b034cd8e9f323362659b421 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Tue, 19 Mar 2024 14:30:06 +0100 Subject: [PATCH 3/4] check if results are not empty --- src/fee_stats/mod.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/fee_stats/mod.rs b/src/fee_stats/mod.rs index adde046..6c1eb11 100644 --- a/src/fee_stats/mod.rs +++ b/src/fee_stats/mod.rs @@ -24,10 +24,42 @@ mod tests { HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); let fee_stats_request = FeeStatsRequest::new(); - let _fee_stats_response = horizon_client.get_fee_stats(&fee_stats_request).await; + let fee_stats_response = horizon_client.get_fee_stats(&fee_stats_request).await; - assert!(_fee_stats_response.is_ok()); + assert!(fee_stats_response.is_ok()); + let binding = fee_stats_response.unwrap(); + assert_eq!(binding.last_ledger().is_empty(), false); + assert_eq!(binding.last_ledger_base_fee().is_empty(), false); + assert_eq!(binding.ledger_capacity_usage().is_empty(), false); + assert_eq!(binding.fee_charged().max().is_empty(), false); + assert_eq!(binding.fee_charged().min().is_empty(), false); + assert_eq!(binding.fee_charged().mode().is_empty(), false); + assert_eq!(binding.fee_charged().p10().is_empty(), false); + assert_eq!(binding.fee_charged().p20().is_empty(), false); + assert_eq!(binding.fee_charged().p30().is_empty(), false); + assert_eq!(binding.fee_charged().p40().is_empty(), false); + assert_eq!(binding.fee_charged().p50().is_empty(), false); + assert_eq!(binding.fee_charged().p60().is_empty(), false); + assert_eq!(binding.fee_charged().p70().is_empty(), false); + assert_eq!(binding.fee_charged().p80().is_empty(), false); + assert_eq!(binding.fee_charged().p90().is_empty(), false); + assert_eq!(binding.fee_charged().p95().is_empty(), false); + assert_eq!(binding.fee_charged().p99().is_empty(), false); + assert_eq!(binding.max_fee().max().is_empty(), false); + assert_eq!(binding.max_fee().min().is_empty(), false); + assert_eq!(binding.max_fee().mode().is_empty(), false); + assert_eq!(binding.max_fee().p10().is_empty(), false); + assert_eq!(binding.max_fee().p20().is_empty(), false); + assert_eq!(binding.max_fee().p30().is_empty(), false); + assert_eq!(binding.max_fee().p40().is_empty(), false); + assert_eq!(binding.max_fee().p50().is_empty(), false); + assert_eq!(binding.max_fee().p60().is_empty(), false); + assert_eq!(binding.max_fee().p70().is_empty(), false); + assert_eq!(binding.max_fee().p80().is_empty(), false); + assert_eq!(binding.max_fee().p90().is_empty(), false); + assert_eq!(binding.max_fee().p95().is_empty(), false); + assert_eq!(binding.max_fee().p99().is_empty(), false); // there is not much use in testing the values of the response, as they are subject to constant change } } From 3071e151a20e566693048dda3b5c937495ea7deb Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Tue, 19 Mar 2024 14:31:23 +0100 Subject: [PATCH 4/4] clean up --- src/fee_stats/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/fee_stats/mod.rs b/src/fee_stats/mod.rs index 6c1eb11..1bb60f7 100644 --- a/src/fee_stats/mod.rs +++ b/src/fee_stats/mod.rs @@ -13,11 +13,6 @@ mod tests { use super::prelude::*; use crate::horizon_client::HorizonClient; - #[test] - fn dummy_test() { - assert_eq!(super::FEE_STATS_PATH, "fee_stats"); - } - #[tokio::test] async fn test_get_fee_stats() { let horizon_client = @@ -60,6 +55,5 @@ mod tests { assert_eq!(binding.max_fee().p90().is_empty(), false); assert_eq!(binding.max_fee().p95().is_empty(), false); assert_eq!(binding.max_fee().p99().is_empty(), false); - // there is not much use in testing the values of the response, as they are subject to constant change } }