From ac6c1603becb986bfda96d584c302a8d842861e1 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Thu, 9 Nov 2023 09:51:43 +0100 Subject: [PATCH 1/6] created files --- .../all_claimable_balances_request.rs | 0 .../all_claimable_balances_response.rs | 0 src/claimable_balances/mod.rs | 11 +++++++++++ .../single_claimable_balance_request.rs | 0 .../single_claimable_balance_response.rs | 0 src/lib.rs | 1 + 6 files changed, 12 insertions(+) create mode 100644 src/claimable_balances/all_claimable_balances_request.rs create mode 100644 src/claimable_balances/all_claimable_balances_response.rs create mode 100644 src/claimable_balances/mod.rs create mode 100644 src/claimable_balances/single_claimable_balance_request.rs create mode 100644 src/claimable_balances/single_claimable_balance_response.rs diff --git a/src/claimable_balances/all_claimable_balances_request.rs b/src/claimable_balances/all_claimable_balances_request.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/claimable_balances/all_claimable_balances_response.rs b/src/claimable_balances/all_claimable_balances_response.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/claimable_balances/mod.rs b/src/claimable_balances/mod.rs new file mode 100644 index 0000000..96b44ab --- /dev/null +++ b/src/claimable_balances/mod.rs @@ -0,0 +1,11 @@ +mod all_claimable_balances_request; +mod all_claimable_balances_response; +mod single_claimable_balance_request; +mod single_claimable_balance_response; + +pub mod prelude { + pub use super::all_claimable_balances_request::*; + pub use super::all_claimable_balances_response::*; + pub use super::single_claimable_balance_request::*; + pub use super::single_claimable_balance_response::*; +} \ No newline at end of file diff --git a/src/claimable_balances/single_claimable_balance_request.rs b/src/claimable_balances/single_claimable_balance_request.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/claimable_balances/single_claimable_balance_response.rs b/src/claimable_balances/single_claimable_balance_response.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index a15f410..e02d3fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ mod assets; mod accounts; mod ledgers; +mod claimable_balances; mod horizon_client; mod models; mod xdr; From d0ca656ec99707ff5214fe6bf87dc5fd604d9f60 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Thu, 9 Nov 2023 10:14:45 +0100 Subject: [PATCH 2/6] claimable assets request --- .../all_claimable_balances_request.rs | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/src/claimable_balances/all_claimable_balances_request.rs b/src/claimable_balances/all_claimable_balances_request.rs index e69de29..670d77d 100644 --- a/src/claimable_balances/all_claimable_balances_request.rs +++ b/src/claimable_balances/all_claimable_balances_request.rs @@ -0,0 +1,183 @@ +use crate::models::*; + +use super::super::Order; +use super::super::AssetType; + +/// AllClaimableBalancesRequest is the request type for the /claimable_balances/all endpoint +/// [More Details] (https://www.stellar.org/developers/horizon/reference/endpoints/claimable_balances-all.html) "All Claimable Balances") +pub struct AllClaimableBalancesRequest { + /// Account ID of the sponsor. Every account in the response will either be sponsored by the + /// given account ID or have a subentry (trustline, offer, or data entry) which is sponsored by + /// the given account ID. + sponsor: Option, + /// Account ID of the signer. Every account in the response will have the given account ID as a + /// signer. + asset: Option, + /// An object that holds both the destination account that can claim the ClaimableBalanceEntry + /// and a ClaimPredicate that must evaluate to true for the claim to succeed. + claimant: Option, + /// Account ID of the signer. Every account in the response will have the given account ID as a + /// signer. + cursor: Option, + /// The maximum number of records returned. The limit can range from 1 to 200 - an upper limit + /// that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it + /// defaults to 10. + limit: Option, + /// A designation of the order in which records should appear. Options include asc (ascending) + /// or desc (descending). If this argument isn’t set, it defaults to asc. + order: Option, +} + +impl Request for AllClaimableBalancesRequest { + /// Creates a new request object + /// # Returns + /// A new request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + fn new() -> Self { + AllClaimableBalancesRequest { + sponsor: None, + asset: None, + claimant: None, + cursor: None, + limit: None, + order: None, + } + } + + /// Gets the relative URL for the request + fn get_path(&self) -> &str { + "/claimable_balances/all" + } + + // Gets the query parameters for the request + fn get_query_parameters(&self) -> String { + let mut query = String::new(); + if let Some(sponsor) = &self.sponsor { + query.push_str(&format!("sponsor={}&", sponsor)); + } + if let Some(asset) = &self.asset { + query.push_str(&format!("asset={}&", asset)); + } + if let Some(claimant) = &self.claimant { + query.push_str(&format!("claimant={}&", claimant)); + } + if let Some(cursor) = &self.cursor { + query.push_str(&format!("cursor={}&", cursor)); + } + if let Some(limit) = &self.limit { + query.push_str(&format!("limit={}&", limit)); + } + if let Some(order) = &self.order { + query.push_str(&format!("order={}&", order)); + } + + query.trim_end_matches('&').to_string() + } + + /// Builds the URL for the request + /// # Arguments + /// * `self` - The request object + /// * `base_url` - The base URL for the Horizon server + /// # Returns + /// The URL for the request + fn build_url(&self, base_url: &str) -> String { + format!( + "{}{}{}", + base_url, + self.get_path(), + self.get_query_parameters() + ) + } + + fn validate(&self) -> Result<(), String> { + if let Some(sponsor) = &self.sponsor { + let is_valid = is_public_key(sponsor); + if is_valid.is_err() { + return Err(is_valid.unwrap_err()); + } + } + + if let Some(claimant) = &self.claimant { + let is_valid = is_public_key(claimant); + if is_valid.is_err() { + return Err(is_valid.unwrap_err()); + } + } + + Ok(()) + } +} + +impl AllClaimableBalancesRequest { + /// Sets the sponsor for the request + /// # Arguments + /// * `self` - The request object + /// * `sponsor` - The sponsor for the request + /// # Returns + /// The request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + pub fn set_sponsor(&mut self, sponsor: String) -> &mut Self { + self.sponsor = Some(sponsor); + self + } + + /// Sets the asset for the request + /// # Arguments + /// * `self` - The request object + /// * `asset` - The asset for the request + /// # Returns + /// The request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + pub fn set_asset(&mut self, asset: AssetType) -> &mut Self { + self.asset = Some(asset); + self + } + + /// Sets the claimant for the request + /// # Arguments + /// * `self` - The request object + /// * `claimant` - The claimant for the request + /// # Returns + /// The request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + pub fn set_claimant(&mut self, claimant: String) -> &mut Self { + self.claimant = Some(claimant); + self + } + + /// Sets the cursor for the request + /// # Arguments + /// * `self` - The request object + /// * `cursor` - The cursor for the request + /// # Returns + /// The request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + pub fn set_cursor(&mut self, cursor: u32) -> &mut Self { + self.cursor = Some(cursor); + self + } + + /// Sets the limit for the request + /// # Arguments + /// * `self` - The request object + /// * `limit` - The limit for the request + /// # Returns + /// The request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + pub fn set_limit(&mut self, limit: u32) -> &mut Self { + self.limit = Some(limit); + self + } + + /// Sets the order for the request + /// # Arguments + /// * `self` - The request object + /// * `order` - The order for the request + /// # Returns + /// The request object + /// [AllClaimableBalancesRequest](struct.AllClaimableBalancesRequest.html) + pub fn set_order(&mut self, order: Order) -> &mut Self { + self.order = Some(order); + self + } +} \ No newline at end of file From 7011b4487f4526fd2a0a63f68c486bd7b5dd2813 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Thu, 9 Nov 2023 10:48:17 +0100 Subject: [PATCH 3/6] all claimable_balancees_response --- .../all_claimable_balances_response.rs | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/src/claimable_balances/all_claimable_balances_response.rs b/src/claimable_balances/all_claimable_balances_response.rs index e69de29..7ddf1dd 100644 --- a/src/claimable_balances/all_claimable_balances_response.rs +++ b/src/claimable_balances/all_claimable_balances_response.rs @@ -0,0 +1,234 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::models::Response; + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct ClaimableBalancesResponse { + _links: Links, + _embedded: EmbeddedRecords, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Links { + #[serde(rename = "self")] + self_link: Href, + next: Href, + prev: Href, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Href { + href: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct EmbeddedRecords { + records: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Record { + _links: RecordLinks, + id: String, + asset: String, + amount: String, + sponsor: String, + last_modified_ledger: u64, + last_modified_time: String, + claimants: Vec, + flags: Flags, + paging_token: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct RecordLinks { + #[serde(rename = "self")] + self_link: Href, + transactions: Href, + operations: Href, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Claimant { + destination: String, + predicate: Predicate, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +enum Predicate { + Unconditional { unconditional: bool }, + Conditional { + or: Vec, + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct ConditionalPredicate { + #[serde(flatten)] + inner: HashMap, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +enum Value { + String(String), + Boolean(bool), +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Flags { + clawback_enabled: bool, +} + +impl Response for ClaimableBalancesResponse { + fn from_json(json: String) -> Result { + serde_json::from_str(&json).map_err(|e| e.to_string()) + } +} + +impl ClaimableBalancesResponse { + pub fn get_links(&self) -> Links { + self._links.clone() + } + + pub fn get_embedded(&self) -> EmbeddedRecords { + self._embedded.clone() + } +} + +impl Links { + pub fn get_self_link(&self) -> Href { + self.self_link.clone() + } + + pub fn get_next(&self) -> Href { + self.next.clone() + } + + pub fn get_prev(&self) -> Href { + self.prev.clone() + } +} + +impl EmbeddedRecords { + pub fn get_records(&self) -> Vec { + self.records.clone() + } + + pub fn get_single_record(&self, index: usize) -> Record { + self.records[index].clone() + } +} + +impl Record { + pub fn get_id(&self) -> String { + self.id.clone() + } + + pub fn get_asset(&self) -> String { + self.asset.clone() + } + + pub fn get_amount(&self) -> String { + self.amount.clone() + } + + pub fn get_sponsor(&self) -> String { + self.sponsor.clone() + } + + pub fn get_last_modified_ledger(&self) -> u64 { + self.last_modified_ledger + } + + pub fn get_last_modified_time(&self) -> String { + self.last_modified_time.clone() + } + + pub fn get_claimants(&self) -> Vec { + self.claimants.clone() + } + + pub fn get_flags(&self) -> Flags { + self.flags.clone() + } + + pub fn get_paging_token(&self) -> String { + self.paging_token.clone() + } + + pub fn get_links(&self) -> RecordLinks { + self._links.clone() + } +} + +impl RecordLinks { + pub fn get_self_link(&self) -> Href { + self.self_link.clone() + } + + pub fn get_transactions(&self) -> Href { + self.transactions.clone() + } + + pub fn get_operations(&self) -> Href { + self.operations.clone() + } +} + +impl Claimant { + pub fn get_destination(&self) -> String { + self.destination.clone() + } + + pub fn get_predicate(&self) -> Predicate { + self.predicate.clone() + } +} + +impl Flags { + pub fn get_clawback_enabled(&self) -> bool { + self.clawback_enabled + } +} + +impl Predicate { + pub fn get_unconditional(&self) -> bool { + match self { + Predicate::Unconditional { unconditional } => unconditional.clone(), + _ => false, + } + } + + pub fn get_conditional(&self) -> Vec { + match self { + Predicate::Conditional { or } => or.clone(), + _ => Vec::new(), + } + } +} + +impl ConditionalPredicate { + pub fn get_inner(&self) -> HashMap { + self.inner.clone() + } +} + +impl Value { + pub fn get_string(&self) -> String { + match self { + Value::String(string) => string.clone(), + _ => String::new(), + } + } + + pub fn get_boolean(&self) -> bool { + match self { + Value::Boolean(boolean) => boolean.clone(), + _ => false, + } + } +} + From 98f31a0d7c193811d77b3f92e868bc296243a2bb Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Fri, 10 Nov 2023 12:59:34 +0100 Subject: [PATCH 4/6] all claimbable assets response rework and tests --- Cargo.toml | 1 + .../all_claimable_balances_request.rs | 4 +- .../all_claimable_balances_response.rs | 328 +++++++++--------- src/horizon_client/horizon_client.rs | 133 ++++++- src/xdr/mod.rs | 1 - 5 files changed, 283 insertions(+), 184 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92d204e..845091a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ serde = { version = "1.0.188", features = ["derive"] } derive-getters = "0.3.0" hex = "0.4.3" base64 = "0.21.4" +chrono = "0.4.31" diff --git a/src/claimable_balances/all_claimable_balances_request.rs b/src/claimable_balances/all_claimable_balances_request.rs index 670d77d..5c782c7 100644 --- a/src/claimable_balances/all_claimable_balances_request.rs +++ b/src/claimable_balances/all_claimable_balances_request.rs @@ -46,7 +46,7 @@ impl Request for AllClaimableBalancesRequest { /// Gets the relative URL for the request fn get_path(&self) -> &str { - "/claimable_balances/all" + "/claimable_balances/" } // Gets the query parameters for the request @@ -82,7 +82,7 @@ impl Request for AllClaimableBalancesRequest { /// The URL for the request fn build_url(&self, base_url: &str) -> String { format!( - "{}{}{}", + "{}{}?{}", base_url, self.get_path(), self.get_query_parameters() diff --git a/src/claimable_balances/all_claimable_balances_response.rs b/src/claimable_balances/all_claimable_balances_response.rs index 7ddf1dd..ac2aa30 100644 --- a/src/claimable_balances/all_claimable_balances_response.rs +++ b/src/claimable_balances/all_claimable_balances_response.rs @@ -1,234 +1,228 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use chrono::DateTime; +use chrono::Utc; +use derive_getters::Getters; +use serde::Deserialize; +use serde::Serialize; use crate::models::Response; -#[derive(Serialize, Deserialize, Debug, Clone)] -struct ClaimableBalancesResponse { - _links: Links, - _embedded: EmbeddedRecords, +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct AllClaimableBalancesResponse { + #[serde(rename = "_links")] + links: Links, + #[serde(rename = "_embedded")] + embedded: Embedded, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct Links { +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Links { #[serde(rename = "self")] - self_link: Href, - next: Href, - prev: Href, + self_field: Self_field, + next: Next, + prev: Prev, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct Href { +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Self_field { href: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct EmbeddedRecords { +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Next { + href: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Prev { + href: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Embedded { records: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct Record { - _links: RecordLinks, +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Record { + #[serde(rename = "_links")] + links: RecordsLinks, id: String, asset: String, amount: String, sponsor: String, - last_modified_ledger: u64, + #[serde(rename = "last_modified_ledger")] + last_modified_ledger: i64, + #[serde(rename = "last_modified_time")] last_modified_time: String, claimants: Vec, flags: Flags, + #[serde(rename = "paging_token")] paging_token: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct RecordLinks { +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct RecordsLinks { #[serde(rename = "self")] - self_link: Href, - transactions: Href, - operations: Href, + self_field: Records_Self_field, + transactions: Transactions, + operations: Operations, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct Claimant { - destination: String, - predicate: Predicate, +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Records_Self_field { + href: String, } -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(untagged)] -enum Predicate { - Unconditional { unconditional: bool }, - Conditional { - or: Vec, - }, +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Transactions { + href: String, + templated: bool, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct ConditionalPredicate { - #[serde(flatten)] - inner: HashMap, +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Operations { + href: String, + templated: bool, } -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(untagged)] -enum Value { - String(String), - Boolean(bool), +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Claimant { + destination: String, + predicate: Predicate, } -#[derive(Serialize, Deserialize, Debug, Clone)] -struct Flags { - clawback_enabled: bool, +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Predicate { + unconditional: Option, + or: Option>, } -impl Response for ClaimableBalancesResponse { - fn from_json(json: String) -> Result { - serde_json::from_str(&json).map_err(|e| e.to_string()) - } +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Or { + and: Option>, + #[serde(rename = "abs_before")] + abs_before: Option, + #[serde(rename = "abs_before_epoch")] + abs_before_epoch: Option, } -impl ClaimableBalancesResponse { - pub fn get_links(&self) -> Links { - self._links.clone() - } - - pub fn get_embedded(&self) -> EmbeddedRecords { - self._embedded.clone() - } +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct And { + not: Option, + #[serde(rename = "abs_before")] + abs_before: Option, + #[serde(rename = "abs_before_epoch")] + abs_before_epoch: Option, } -impl Links { - pub fn get_self_link(&self) -> Href { - self.self_link.clone() - } - - pub fn get_next(&self) -> Href { - self.next.clone() - } - - pub fn get_prev(&self) -> Href { - self.prev.clone() - } +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Not { + #[serde(rename = "abs_before")] + abs_before: String, + #[serde(rename = "abs_before_epoch")] + abs_before_epoch: String, } -impl EmbeddedRecords { - pub fn get_records(&self) -> Vec { - self.records.clone() - } - - pub fn get_single_record(&self, index: usize) -> Record { - self.records[index].clone() - } +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Flags { + #[serde(rename = "clawback_enabled")] + clawback_enabled: bool, } -impl Record { - pub fn get_id(&self) -> String { - self.id.clone() - } - - pub fn get_asset(&self) -> String { - self.asset.clone() - } - - pub fn get_amount(&self) -> String { - self.amount.clone() - } - - pub fn get_sponsor(&self) -> String { - self.sponsor.clone() - } - - pub fn get_last_modified_ledger(&self) -> u64 { - self.last_modified_ledger - } - - pub fn get_last_modified_time(&self) -> String { - self.last_modified_time.clone() - } - - pub fn get_claimants(&self) -> Vec { - self.claimants.clone() - } - - pub fn get_flags(&self) -> Flags { - self.flags.clone() - } - - pub fn get_paging_token(&self) -> String { - self.paging_token.clone() - } +impl Response for AllClaimableBalancesResponse { + fn from_json(json: String) -> Result { + let response = serde_json::from_str(&json).map_err(|e| e.to_string())?; - pub fn get_links(&self) -> RecordLinks { - self._links.clone() + Ok(response) } } -impl RecordLinks { - pub fn get_self_link(&self) -> Href { - self.self_link.clone() - } - - pub fn get_transactions(&self) -> Href { - self.transactions.clone() - } - - pub fn get_operations(&self) -> Href { - self.operations.clone() +impl Predicate { + // This method checks if a claim is valid at a specific datetime. + pub fn is_valid_claim(&self, datetime: DateTime) -> bool { + // If the predicate is marked as unconditional, the claim is always valid. + if let Some(true) = self.unconditional { + true + } + // If there are 'or' conditions, check if any of these conditions validate the claim. + else if let Some(or_conditions) = &self.or { + or_conditions.iter().any(|or| or.is_valid(datetime)) + } + // If there are no conditions, the claim is valid. + else { + true + } } } -impl Claimant { - pub fn get_destination(&self) -> String { - self.destination.clone() - } - pub fn get_predicate(&self) -> Predicate { - self.predicate.clone() +impl Or { + // This method checks if any condition under 'or' validates the claim. + fn is_valid(&self, datetime: DateTime) -> bool { + // If there are 'and' conditions, check if any combination of these conditions is valid. + if let Some(and_conditions) = &self.and { + and_conditions.iter().any(|and| and.is_valid(datetime)) + } + // If there is an 'abs_before' condition, check if the datetime is before this date. + else if let Some(abs_before) = &self.abs_before { + if let Ok(abs_before_date) = DateTime::parse_from_rfc3339(abs_before) { + datetime < abs_before_date + } else { + false + } + } + // If no specific condition is found, the claim is valid. + else { + true + } } } -impl Flags { - pub fn get_clawback_enabled(&self) -> bool { - self.clawback_enabled - } -} +impl And { + // This method checks if all conditions under 'and' are met. + fn is_valid(&self, datetime: DateTime) -> bool { + let mut is_valid = true; -impl Predicate { - pub fn get_unconditional(&self) -> bool { - match self { - Predicate::Unconditional { unconditional } => unconditional.clone(), - _ => false, + // If there is an 'abs_before' condition, check if the datetime is before this date. + if let Some(abs_before) = &self.abs_before { + if let Ok(abs_before_date) = DateTime::parse_from_rfc3339(abs_before) { + is_valid &= datetime < abs_before_date; + } } - } - pub fn get_conditional(&self) -> Vec { - match self { - Predicate::Conditional { or } => or.clone(), - _ => Vec::new(), + // If there is a 'not' condition, it should also validate the datetime. + if let Some(not_condition) = &self.not { + is_valid &= not_condition.is_valid(datetime); } - } -} -impl ConditionalPredicate { - pub fn get_inner(&self) -> HashMap { - self.inner.clone() + is_valid } } -impl Value { - pub fn get_string(&self) -> String { - match self { - Value::String(string) => string.clone(), - _ => String::new(), - } - } - - pub fn get_boolean(&self) -> bool { - match self { - Value::Boolean(boolean) => boolean.clone(), - _ => false, +impl Not { + // This method checks if the datetime does not fall before the specified date, negating the condition. + fn is_valid(&self, datetime: DateTime) -> bool { + if let Ok(not_before_date) = DateTime::parse_from_rfc3339(&self.abs_before) { + datetime >= not_before_date + } else { + false } } } - diff --git a/src/horizon_client/horizon_client.rs b/src/horizon_client/horizon_client.rs index 869b35c..0c3e043 100644 --- a/src/horizon_client/horizon_client.rs +++ b/src/horizon_client/horizon_client.rs @@ -3,6 +3,7 @@ use crate::{ AccountsRequest, AccountsResponse, SingleAccountRequest, SingleAccountsResponse, }, assets::prelude::{AllAssetsRequest, AllAssetsResponse}, + claimable_balances::prelude::{AllClaimableBalancesRequest, AllClaimableBalancesResponse}, ledgers::prelude::{ LedgersRequest, LedgersResponse, SingleLedgerRequest, SingleLedgerResponse, }, @@ -77,6 +78,22 @@ impl HorizonClient { self.get::(request).await } + /// Gets the base URL for the Horizon server + /// # Arguments + /// * `self` - The Horizon client + /// * request - The all claimable balances request + /// # Returns + /// The all claimable balances response + /// # Errors + /// Returns an error if the request fails + /// [GET /claimable_balances/all](https://www.stellar.org/developers/horizon/reference/endpoints/claimable_balances-all.html) + pub async fn get_all_claimable_balances( + &self, + request: &AllClaimableBalancesRequest, + ) -> Result { + self.get::(request).await + } + pub async fn get_all_ledgers( &self, request: &LedgersRequest, @@ -112,7 +129,6 @@ impl HorizonClient { let url = request.build_url(&self.base_url); println!("\n\nURL: {}", url); let response = reqwest::get(&url).await.map_err(|e| e.to_string())?; - println!("Response: {:?}", response); let result: TResponse = handle_response(response).await?; // print!("\n\nResult: {:?}", result); @@ -134,6 +150,7 @@ async fn handle_response( match response.status() { reqwest::StatusCode::OK => { let _response = response.text().await.map_err(|e| e.to_string())?; + // println!("\n\nResponse: {:?}", _response); TResponse::from_json(_response) } _ => { @@ -155,6 +172,7 @@ fn url_validate(url: &str) -> Result<(), String> { #[cfg(test)] mod tests { use base64::encode; + use chrono::{DateTime, TimeZone, Utc}; use crate::{assets::prelude::AllAssetsRequest, ledgers::prelude::SingleLedgerRequest}; @@ -194,7 +212,6 @@ mod tests { assert!(_accounts_response.is_ok()); - // Testing some random values of the response assert_eq!( _accounts_response .clone() @@ -457,7 +474,6 @@ mod tests { assert!(_single_account_response.is_ok()); - // Testing some random values of the response assert_eq!( _single_account_response .clone() @@ -877,7 +893,6 @@ mod tests { assert!(_all_ledgers_response.clone().is_ok()); - // Testing some random values of the response assert_eq!( _all_ledgers_response .clone() @@ -927,8 +942,6 @@ mod tests { .get_paging_token(), "8589934592".to_string() ); - - // Testing some random values of the response } #[tokio::test] @@ -1087,7 +1100,6 @@ mod tests { decoded_xdr_header.scp_value.close_time, stellar_xdr::TimePoint(1686734388) ); - } #[tokio::test] @@ -1142,13 +1154,7 @@ mod tests { "63d98f536ee68d1b27b5b89f23af5311b7569a24faf1403ad0b52b633b07be99" ); - assert_eq!( - _single_ledger_response - .clone() - .unwrap() - .get_sequence(), - 2 - ); + assert_eq!(_single_ledger_response.clone().unwrap().get_sequence(), 2); assert_eq!( _single_ledger_response @@ -1267,4 +1273,103 @@ mod tests { ); } } + + #[tokio::test] + async fn test_get_all_claimable_balances() { + // Initialize horizon client + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + // construct request + let mut all_claimable_balances_request = AllClaimableBalancesRequest::new(); + all_claimable_balances_request.set_limit(2); + + let _all_claimable_balances_response = horizon_client + .get_all_claimable_balances(&all_claimable_balances_request) + .await; + + assert!(_all_claimable_balances_response.clone().is_ok()); + + let binding = _all_claimable_balances_response.clone().unwrap(); + let predicate = binding.embedded().records()[1].claimants()[1].predicate(); + + let now = Utc::now(); + + let jan_first_2022 = Utc::with_ymd_and_hms(&Utc, 2022, 1, 1, 0, 0, 0).unwrap(); + + assert_eq!(predicate.is_valid_claim(now), true); + + assert_eq!(predicate.is_valid_claim(jan_first_2022), false); + + assert_eq!( + _all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .id(), + "000000006520216af66d20d63a58534d6cbdf28ba9f2a9c1e03f8d9a756bb7d988b29bca" + ); + + assert_eq!( + _all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .asset(), + "native" + ); + + assert_eq!( + _all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .amount(), + "12.3300000" + ); + + assert_eq!( + _all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .sponsor(), + "GD7TMSN67PCPZ4SXQHNG4GFO4KEMGTAT6MGWQGKBPOFDY7TP2IYDYFVI" + ); + + assert_eq!( + *_all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .last_modified_ledger(), + 1560 + ); + + assert_eq!( + _all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .last_modified_time(), + "2023-06-14T11:38:24Z" + ); + + assert_eq!( + *_all_claimable_balances_response + .clone() + .unwrap() + .embedded() + .records()[0] + .flags() + .clawback_enabled(), + false + ); + } } diff --git a/src/xdr/mod.rs b/src/xdr/mod.rs index 4ab949d..e2d049e 100644 --- a/src/xdr/mod.rs +++ b/src/xdr/mod.rs @@ -14,7 +14,6 @@ mod tests { let encoded: &[u8] = "AAAAAGPZj1Nu5o0bJ7W4nyOvUxG3Vpok+vFAOtC1K2M7B76ZuZRHr9UdXKbTKiclfOjy72YZFJUkJPVcKT5htvorm1QAAAAAZImGNAAAAAAAAAABAAAAAKgkzRi8nXUGTSmaW1uspDvDqi8yaTgVPYwvm7XLbfAzAAAAQLuRQK8ocAjytwfQelkpwZQa5+jReIO0pbr/9BbUbIffRxQN4Z76J3qDIn5lSJpn0OkYL8ZLPGP0W/S1vlTj5w/fP2GYBKkv20BXGS3EPddI6neK3FK8SYzoBSTAFLgRGXNSJ+05hGEpEjdoewhEaqLJsJbgyYpGLa3aVp8F3SSEAAAAAg3gtrOnZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkBfXhAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=".as_bytes(); let decoded = stellar_xdr::LedgerHeader::from_xdr_base64(encoded).unwrap(); - // println!("Decoded: {:?}", decoded); assert_eq!(decoded.ledger_version, 0); assert_eq!( From 75e4e153a269b4db83e9a83371fe073ffc35f8f5 Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Mon, 13 Nov 2023 15:32:01 +0100 Subject: [PATCH 5/6] claimable balances tests --- .../single_claimable_balance_request.rs | 91 +++++++++ .../single_claimable_balance_response.rs | 187 ++++++++++++++++++ src/horizon_client/horizon_client.rs | 137 ++++++++++++- 3 files changed, 411 insertions(+), 4 deletions(-) diff --git a/src/claimable_balances/single_claimable_balance_request.rs b/src/claimable_balances/single_claimable_balance_request.rs index e69de29..8d6605d 100644 --- a/src/claimable_balances/single_claimable_balance_request.rs +++ b/src/claimable_balances/single_claimable_balance_request.rs @@ -0,0 +1,91 @@ +use chrono::format; + +use crate::models::*; + +use super::super::AssetType; +use super::super::Order; + +/// SingleClaimableBalanceRequest is the struct that implements the type for the /claimable_balances endpoint to get a single claimable balance +/// [More Details](https://laboratory.stellar.org/#explorer?resource=claimable_balances&endpoint=single&network=test "Single Claimable Balance") +#[derive(Debug)] +pub struct SingleClaimableBalanceRequest { + /// Claimable Balance ID + /// [Stellar Documentation](https://developers.stellar.org/api/resources/claimablebalances/single/ "Claimable Balance ID") + claimable_balance_id: Option, +} + +impl Request for SingleClaimableBalanceRequest { + /// Creates a new request object + /// # Returns + /// A new request object + /// [SingleClaimableBalanceRequest](struct.SingleClaimableBalanceRequest.html) + fn new() -> Self { + SingleClaimableBalanceRequest { + claimable_balance_id: None, + } + } + + /// Gets the relative URL for the request + /// # Returns + /// The relative URL for the request + fn get_path(&self) -> &str { + "/claimable_balances/" + } + + /// Gets the query parameters for the request + /// # Returns + /// The query parameters for the request + fn get_query_parameters(&self) -> String { + let mut query = String::new(); + if let Some(claimable_balance_id) = &self.claimable_balance_id { + query.push_str(&format!("{}", claimable_balance_id)); + } + query + } + + /// Builds the URL for the request + /// # Arguments + /// * `self` - The request object + /// * `base_url` - The base URL for the Horizon server + /// # Returns + /// The URL for the request + fn build_url(&self, base_url: &str) -> String { + println!("\n\nBUILD URL: {:?}", format!( + "{}{}{}", + base_url, + self.get_path(), + self.get_query_parameters() + )); + + format!( + "{}{}{}", + base_url, + self.get_path(), + self.get_query_parameters() + ) + } + + /// Returns the type of request + /// # Returns + /// The type of request + /// [RequestType](../enum.RequestType.html) + /// [More Details](https://laboratory.stellar.org/#explorer?resource=claimable_balances&endpoint=single&network=test "Single Claimable Balance") + fn validate(&self) -> Result<(), String> { + + // TODO: Validate claimable_balance_id + + Ok(()) + } +} + +/// Returns the claimable balance ID +/// # Arguments +/// * `self` - The request object +/// # Returns +/// The claimable balance ID +impl SingleClaimableBalanceRequest { + pub fn set_claimable_balance_id(&mut self, claimable_balance_id: String) -> &mut Self { + self.claimable_balance_id = Some(claimable_balance_id); + self + } +} diff --git a/src/claimable_balances/single_claimable_balance_response.rs b/src/claimable_balances/single_claimable_balance_response.rs index e69de29..20c57ad 100644 --- a/src/claimable_balances/single_claimable_balance_response.rs +++ b/src/claimable_balances/single_claimable_balance_response.rs @@ -0,0 +1,187 @@ +use chrono::DateTime; +use chrono::Utc; +use derive_getters::Getters; +use serde::Deserialize; +use serde::Serialize; + +use crate::models::Response; + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct SingleClaimableBalanceResponse { + #[serde(rename = "_links")] + pub links: Links, + pub id: String, + pub asset: String, + pub amount: String, + pub sponsor: String, + #[serde(rename = "last_modified_ledger")] + pub last_modified_ledger: i64, + #[serde(rename = "last_modified_time")] + pub last_modified_time: String, + pub claimants: Vec, + pub flags: Flags, + #[serde(rename = "paging_token")] + pub paging_token: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Links { + #[serde(rename = "self")] + pub self_field: Self_field, + pub transactions: Transactions, + pub operations: Operations, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Self_field { + pub href: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Transactions { + pub href: String, + pub templated: bool, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Operations { + pub href: String, + pub templated: bool, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Claimant { + pub destination: String, + pub predicate: Predicate, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Predicate { + pub unconditional: Option, + pub or: Option>, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Or { + pub and: Option>, + #[serde(rename = "abs_before")] + pub abs_before: Option, + #[serde(rename = "abs_before_epoch")] + pub abs_before_epoch: Option, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct And { + pub not: Option, + #[serde(rename = "abs_before")] + pub abs_before: Option, + #[serde(rename = "abs_before_epoch")] + pub abs_before_epoch: Option, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Not { + #[serde(rename = "abs_before")] + pub abs_before: String, + #[serde(rename = "abs_before_epoch")] + pub abs_before_epoch: String, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, Getters)] +#[serde(rename_all = "camelCase")] +pub struct Flags { + #[serde(rename = "clawback_enabled")] + pub clawback_enabled: bool, +} + +impl Response for SingleClaimableBalanceResponse { + fn from_json(json: String) -> Result { + let response = serde_json::from_str(&json).map_err(|e| e.to_string())?; + + Ok(response) + } +} + +impl Predicate { + // This method checks if a claim is valid at a specific datetime. + pub fn is_valid_claim(&self, datetime: DateTime) -> bool { + // If the predicate is marked as unconditional, the claim is always valid. + if let Some(true) = self.unconditional { + true + } + // If there are 'or' conditions, check if any of these conditions validate the claim. + else if let Some(or_conditions) = &self.or { + or_conditions.iter().any(|or| or.is_valid(datetime)) + } + // If there are no conditions, the claim is valid. + else { + true + } + } +} + + +impl Or { + // This method checks if any condition under 'or' validates the claim. + fn is_valid(&self, datetime: DateTime) -> bool { + // If there are 'and' conditions, check if any combination of these conditions is valid. + if let Some(and_conditions) = &self.and { + and_conditions.iter().any(|and| and.is_valid(datetime)) + } + // If there is an 'abs_before' condition, check if the datetime is before this date. + else if let Some(abs_before) = &self.abs_before { + if let Ok(abs_before_date) = DateTime::parse_from_rfc3339(abs_before) { + datetime < abs_before_date + } else { + false + } + } + // If no specific condition is found, the claim is valid. + else { + true + } + } +} + +impl And { + // This method checks if all conditions under 'and' are met. + fn is_valid(&self, datetime: DateTime) -> bool { + let mut is_valid = true; + + // If there is an 'abs_before' condition, check if the datetime is before this date. + if let Some(abs_before) = &self.abs_before { + if let Ok(abs_before_date) = DateTime::parse_from_rfc3339(abs_before) { + is_valid &= datetime < abs_before_date; + } + } + + // If there is a 'not' condition, it should also validate the datetime. + if let Some(not_condition) = &self.not { + is_valid &= not_condition.is_valid(datetime); + } + + is_valid + } +} + +impl Not { + // This method checks if the datetime does not fall before the specified date, negating the condition. + fn is_valid(&self, datetime: DateTime) -> bool { + if let Ok(not_before_date) = DateTime::parse_from_rfc3339(&self.abs_before) { + datetime >= not_before_date + } else { + false + } + } +} + diff --git a/src/horizon_client/horizon_client.rs b/src/horizon_client/horizon_client.rs index 0c3e043..4416413 100644 --- a/src/horizon_client/horizon_client.rs +++ b/src/horizon_client/horizon_client.rs @@ -3,7 +3,10 @@ use crate::{ AccountsRequest, AccountsResponse, SingleAccountRequest, SingleAccountsResponse, }, assets::prelude::{AllAssetsRequest, AllAssetsResponse}, - claimable_balances::prelude::{AllClaimableBalancesRequest, AllClaimableBalancesResponse}, + claimable_balances::prelude::{ + AllClaimableBalancesRequest, AllClaimableBalancesResponse, SingleClaimableBalanceRequest, + SingleClaimableBalanceResponse, + }, ledgers::prelude::{ LedgersRequest, LedgersResponse, SingleLedgerRequest, SingleLedgerResponse, }, @@ -94,6 +97,22 @@ impl HorizonClient { self.get::(request).await } + /// Gets the base URL for the Horizon server + /// # Arguments + /// * `self` - The Horizon client + /// * request - The single claimable balance request + /// # Returns + /// The single claimable balance response + /// # Errors + /// Returns an error if the request fails + /// [GET /claimable_balances/{claimable_balance_id}](https://www.stellar.org/developers/horizon/reference/endpoints/claimable_balances-single.html) + pub async fn get_single_claimable_balance( + &self, + request: &SingleClaimableBalanceRequest, + ) -> Result { + self.get::(request).await + } + pub async fn get_all_ledgers( &self, request: &LedgersRequest, @@ -127,8 +146,9 @@ impl HorizonClient { // TODO: construct with query parameters let url = request.build_url(&self.base_url); - println!("\n\nURL: {}", url); + // println!("\n\nURL: {}", url); let response = reqwest::get(&url).await.map_err(|e| e.to_string())?; + println!("\n\nREQWEST RESPONSE: {:?}", response); let result: TResponse = handle_response(response).await?; // print!("\n\nResult: {:?}", result); @@ -150,7 +170,7 @@ async fn handle_response( match response.status() { reqwest::StatusCode::OK => { let _response = response.text().await.map_err(|e| e.to_string())?; - // println!("\n\nResponse: {:?}", _response); + // println!("\n\nHANDLE_RESPONSE RESPONSE: {:?}", _response); TResponse::from_json(_response) } _ => { @@ -161,11 +181,13 @@ async fn handle_response( } /// url_validate validates a URL fn url_validate(url: &str) -> Result<(), String> { + println!("URL: {}", url); // check if start with http:// or https:// if !url.starts_with("http://") && !url.starts_with("https://") { return Err(format!("URL must start with http:// or https://: {}", url)); } Url::parse(url).map_err(|e| e.to_string())?; + Ok(()) } @@ -174,7 +196,11 @@ mod tests { use base64::encode; use chrono::{DateTime, TimeZone, Utc}; - use crate::{assets::prelude::AllAssetsRequest, ledgers::prelude::SingleLedgerRequest}; + use crate::{ + assets::prelude::AllAssetsRequest, + claimable_balances::prelude::SingleClaimableBalanceRequest, + ledgers::prelude::SingleLedgerRequest, + }; use super::*; @@ -1372,4 +1398,107 @@ mod tests { false ); } + + #[tokio::test] + async fn test_get_single_claimable_balance() { + // Initialize horizon client + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + // construct request + let mut single_claimable_balance_request = SingleClaimableBalanceRequest::new(); + single_claimable_balance_request.set_claimable_balance_id( + "000000006520216af66d20d63a58534d6cbdf28ba9f2a9c1e03f8d9a756bb7d988b29bca".to_string(), + ); + + let single_claimable_balance_response = horizon_client + .get_single_claimable_balance(&single_claimable_balance_request) + .await; + + assert!(single_claimable_balance_response.is_ok()); + + let binding = single_claimable_balance_response.clone().unwrap(); + + let predicate = binding.claimants()[1].predicate(); + + let now = Utc::now(); + + let jan_first_2022 = Utc::with_ymd_and_hms(&Utc, 2022, 1, 1, 0, 0, 0).unwrap(); + + assert_eq!(predicate.is_valid_claim(now), true); + + assert_eq!(predicate.is_valid_claim(jan_first_2022), false); + + + assert_eq!( + single_claimable_balance_response + .clone() + .unwrap() + .id() + .to_string(), + "000000006520216af66d20d63a58534d6cbdf28ba9f2a9c1e03f8d9a756bb7d988b29bca" + ); + + assert_eq!( + single_claimable_balance_response + .clone() + .unwrap() + .asset() + .to_string(), + "native" + ); + + assert_eq!( + single_claimable_balance_response + .clone() + .unwrap() + .amount() + .to_string(), + "12.3300000" + ); + + assert_eq!( + single_claimable_balance_response + .clone() + .unwrap() + .sponsor() + .to_string(), + "GD7TMSN67PCPZ4SXQHNG4GFO4KEMGTAT6MGWQGKBPOFDY7TP2IYDYFVI" + ); + + assert_eq!( + *single_claimable_balance_response + .clone() + .unwrap() + .last_modified_ledger(), + 1560 + ); + + assert_eq!( + single_claimable_balance_response + .clone() + .unwrap() + .last_modified_time() + .to_string(), + "2023-06-14T11:38:24Z" + ); + + assert_eq!( + *single_claimable_balance_response + .clone() + .unwrap() + .flags() + .clawback_enabled(), + false + ); + + assert_eq!( + single_claimable_balance_response + .clone() + .unwrap() + .paging_token() + .to_string(), + "1560-000000006520216af66d20d63a58534d6cbdf28ba9f2a9c1e03f8d9a756bb7d988b29bca" + ); + } } From 664245a4e025ddd34e2caf375c65a993375edbbc Mon Sep 17 00:00:00 2001 From: LeonardTibben Date: Mon, 13 Nov 2023 15:46:39 +0100 Subject: [PATCH 6/6] code cleanup & comments --- .../single_claimable_balance_request.rs | 14 +------- src/horizon_client/horizon_client.rs | 33 ++++++++++++++----- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/claimable_balances/single_claimable_balance_request.rs b/src/claimable_balances/single_claimable_balance_request.rs index 8d6605d..ac1d098 100644 --- a/src/claimable_balances/single_claimable_balance_request.rs +++ b/src/claimable_balances/single_claimable_balance_request.rs @@ -1,10 +1,5 @@ -use chrono::format; - use crate::models::*; -use super::super::AssetType; -use super::super::Order; - /// SingleClaimableBalanceRequest is the struct that implements the type for the /claimable_balances endpoint to get a single claimable balance /// [More Details](https://laboratory.stellar.org/#explorer?resource=claimable_balances&endpoint=single&network=test "Single Claimable Balance") #[derive(Debug)] @@ -50,13 +45,6 @@ impl Request for SingleClaimableBalanceRequest { /// # Returns /// The URL for the request fn build_url(&self, base_url: &str) -> String { - println!("\n\nBUILD URL: {:?}", format!( - "{}{}{}", - base_url, - self.get_path(), - self.get_query_parameters() - )); - format!( "{}{}{}", base_url, @@ -88,4 +76,4 @@ impl SingleClaimableBalanceRequest { self.claimable_balance_id = Some(claimable_balance_id); self } -} +} \ No newline at end of file diff --git a/src/horizon_client/horizon_client.rs b/src/horizon_client/horizon_client.rs index 4416413..ad2fa35 100644 --- a/src/horizon_client/horizon_client.rs +++ b/src/horizon_client/horizon_client.rs @@ -33,7 +33,7 @@ impl HorizonClient { Ok(Self { base_url }) } - /// Gets the base URL for the Horizon server + /// Gets an account list from the server /// # Arguments /// * `self` - The Horizon client /// * request - The accounts request @@ -49,7 +49,7 @@ impl HorizonClient { self.get::(request).await } - /// Gets the base URL for the Horizon server + /// Gets a single account from the server /// # Arguments /// * `self` - The Horizon client /// * request - The account request @@ -65,7 +65,7 @@ impl HorizonClient { self.get::(request).await } - /// Gets the base URL for the Horizon server + /// Gets all assets from the server /// # Arguments /// * `self` - The Horizon client /// * request - The all assets request @@ -81,7 +81,7 @@ impl HorizonClient { self.get::(request).await } - /// Gets the base URL for the Horizon server + /// Gets all claimable balances from the server /// # Arguments /// * `self` - The Horizon client /// * request - The all claimable balances request @@ -97,7 +97,7 @@ impl HorizonClient { self.get::(request).await } - /// Gets the base URL for the Horizon server + /// Gets a single claimable balance from the server /// # Arguments /// * `self` - The Horizon client /// * request - The single claimable balance request @@ -113,6 +113,15 @@ impl HorizonClient { self.get::(request).await } + /// Gets the all ledger response from the server + /// # Arguments + /// * `self` - The Horizon client + /// * request - The ledgers request + /// # Returns + /// The ledgers response + /// # Errors + /// Returns an error if the request fails + /// [GET /ledgers](https://www.stellar.org/developers/horizon/reference/endpoints/ledgers-all.html) pub async fn get_all_ledgers( &self, request: &LedgersRequest, @@ -120,6 +129,15 @@ impl HorizonClient { self.get::(request).await } + /// Gets a single ledger from the server + /// # Arguments + /// * `self` - The Horizon client + /// * request - The single ledger request + /// # Returns + /// The single ledger response + /// # Errors + /// Returns an error if the request fails + /// [GET /ledgers/{ledger_id}](https://www.stellar.org/developers/horizon/reference/endpoints/ledgers-single.html) pub async fn get_single_ledger( &self, request: &SingleLedgerRequest, @@ -146,7 +164,6 @@ impl HorizonClient { // TODO: construct with query parameters let url = request.build_url(&self.base_url); - // println!("\n\nURL: {}", url); let response = reqwest::get(&url).await.map_err(|e| e.to_string())?; println!("\n\nREQWEST RESPONSE: {:?}", response); let result: TResponse = handle_response(response).await?; @@ -170,7 +187,6 @@ async fn handle_response( match response.status() { reqwest::StatusCode::OK => { let _response = response.text().await.map_err(|e| e.to_string())?; - // println!("\n\nHANDLE_RESPONSE RESPONSE: {:?}", _response); TResponse::from_json(_response) } _ => { @@ -187,7 +203,7 @@ fn url_validate(url: &str) -> Result<(), String> { return Err(format!("URL must start with http:// or https://: {}", url)); } Url::parse(url).map_err(|e| e.to_string())?; - + Ok(()) } @@ -1429,7 +1445,6 @@ mod tests { assert_eq!(predicate.is_valid_claim(jan_first_2022), false); - assert_eq!( single_claimable_balance_response .clone()