From c8a15f65999bca2b607e82cb8233eb3962e2b72b Mon Sep 17 00:00:00 2001 From: Kevin Pease Date: Fri, 30 Aug 2024 10:04:16 +0200 Subject: [PATCH 1/4] Paste code from corrupted branch --- stellar_rust_sdk/src/horizon_client.rs | 79 ++++ stellar_rust_sdk/src/lib.rs | 48 ++- .../src/paths/find_payment_paths_request.rs | 193 +++++++++ ...st_strict_receive_payment_paths_request.rs | 252 ++++++++++++ .../list_strict_send_payment_paths_request.rs | 218 ++++++++++ stellar_rust_sdk/src/paths/mod.rs | 377 ++++++++++++++++++ stellar_rust_sdk/src/paths/response.rs | 61 +++ 7 files changed, 1226 insertions(+), 2 deletions(-) create mode 100644 stellar_rust_sdk/src/paths/find_payment_paths_request.rs create mode 100644 stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs create mode 100644 stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs create mode 100644 stellar_rust_sdk/src/paths/mod.rs create mode 100644 stellar_rust_sdk/src/paths/response.rs diff --git a/stellar_rust_sdk/src/horizon_client.rs b/stellar_rust_sdk/src/horizon_client.rs index 7f465d2..43db962 100644 --- a/stellar_rust_sdk/src/horizon_client.rs +++ b/stellar_rust_sdk/src/horizon_client.rs @@ -33,6 +33,7 @@ use crate::{ details_request::{BuyingAsset, DetailsRequest, SellingAsset}, response::DetailsResponse, }, + paths::{prelude::*, DestinationAmount, DestinationAsset, SourceAccount}, payments::prelude::*, trade_aggregations::prelude::*, trades::prelude::*, @@ -1985,6 +1986,84 @@ impl HorizonClient { self.get::(request).await } + /// Retrieves payment paths from the Horizon server. + /// + /// This asynchronous method fetches a list of payment paths from + /// the Horizon server. It requires an [`FindPaymentsPathRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`FindPaymentsPathRequest`] instance, containing the + /// parameters for the paths request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`PathsResponse`], which includes + /// the list of the payment paths obtained from the Horizon server. If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`FindPaymentsPathRequest`] and set any desired + /// filters or parameters. + pub async fn get_find_payment_paths( + &self, + request: &FindPaymentsPathRequest, + ) -> Result { + self.get::(request).await + } + + /// Retrieves a list of strict receive payment paths from the Horizon server. + /// + /// This asynchronous method fetches a list of strict receive payment paths from + /// the Horizon server. It requires an [`ListStrictReceivePaymentPathsRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`ListStrictReceivePaymentPathsRequest`] instance, containing the + /// parameters for the paths request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`PathsResponse`], which includes + /// the list of the strict receive payment paths obtained from the Horizon server. + /// If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`ListStrictReceivePaymentPathsRequest`] and set any desired + /// filters or parameters. + pub async fn get_list_strict_receive_payment_paths( + &self, + request: &ListStrictReceivePaymentPathsRequest< + DestinationAsset, + DestinationAmount, + SourceAccount, + >, + ) -> Result { + self.get::(request).await + } + + /// Retrieves a list of strict send payment paths from the Horizon server. + /// + /// This asynchronous method fetches a list of strict send payment paths from + /// the Horizon server. It requires an [`ListStrictSendPaymentPathsRequest`] to specify the optional query parameters. + /// + /// # Arguments + /// * `request` - A reference to an [`ListStrictSendPaymentPathsRequest`] instance, containing the + /// parameters for the paths request. + /// + /// # Returns + /// + /// On successful execution, returns a `Result` containing an [`PathsResponse`], which includes + /// the list of the strict send payment paths obtained from the Horizon server. + /// If the request fails, it returns an error within `Result`. + /// + /// # Usage + /// To use this method, create an instance of [`ListStrictSendPaymentPathsRequest`] and set any desired + /// filters or parameters. + pub async fn get_list_strict_send_payment_paths( + &self, + request: &ListStrictSendPaymentPathsRequest, + ) -> Result { + self.get::(request).await + } + /// Retrieves a list of all payments from the Horizon server. /// /// This asynchronous method fetches a list of all payments from the Horizon server. diff --git a/stellar_rust_sdk/src/lib.rs b/stellar_rust_sdk/src/lib.rs index f2a65db..4d808cf 100644 --- a/stellar_rust_sdk/src/lib.rs +++ b/stellar_rust_sdk/src/lib.rs @@ -15,7 +15,7 @@ //! stabilization. //! //! #### Supported endpoints: -//! ![93%](https://progress-bar.dev/93/?width=200) +//! ![100%](https://progress-bar.dev/100/?width=200) //! * Accounts //! * Assets //! * Claimable balance @@ -26,13 +26,13 @@ //! * Operations //! * Offers //! * Orderbook +//! * Paths //! * Payments //! * Trades //! * Trade aggregations //! * Transactions //! //! #### Endpoints on the roadmap: -//! * Paths //! //! ## Example Usage @@ -625,6 +625,50 @@ pub mod transactions; /// pub mod trades; +/// Provides `Request` and `Response` structs for retrieving payment paths. +/// +/// This module provides a set of specialized request and response structures designed for +/// interacting with the payment path-related endpoints of the Horizon server. These structures +/// facilitate the construction of requests to query payment paths and the interpretation of +/// the corresponding responses. +/// +/// # Usage +/// +/// This module is intended to be used in conjunction with the [`HorizonClient`](crate::horizon_client::HorizonClient) +/// for making specific payment path-related API calls to the Horizon server. The request +/// structures are designed to be passed to the client's methods, which handle the +/// communication with the server and return the corresponding response structures. +/// +/// # Example +/// +/// To use this module, you can create an instance of a request struct, such as +/// `FindPaymentPathsRequest`, `ListStrictReceivePaymentPathsRequest`, or `ListStrictSendPaymentPathsRequest`, +/// set any desired query parameters, and pass the request to the `HorizonClient`. The client +/// will then execute the request and return the corresponding response struct, like `PathsResponse`. +/// +/// ```rust +/// use stellar_rs::horizon_client::HorizonClient; +/// use stellar_rs::paths::{prelude::*, AssetType}; +/// use stellar_rs::models::Request; +/// +/// # async fn example() -> Result<(), Box> { +/// let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string())?; +/// +/// // Example: Fetching payment paths +/// let request = FindPaymentsPathRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_source_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap(); // Sets the destination account. +/// +/// let paths_response = horizon_client.get_find_payment_paths(&request).await?; +/// +/// // Process the responses... +/// # Ok(()) +/// # } +/// ``` +pub mod paths; + /// Provides `Request` and `Response` structs for retrieving payments. /// /// This module provides a set of specialized request and response structures designed for diff --git a/stellar_rust_sdk/src/paths/find_payment_paths_request.rs b/stellar_rust_sdk/src/paths/find_payment_paths_request.rs new file mode 100644 index 0000000..365c305 --- /dev/null +++ b/stellar_rust_sdk/src/paths/find_payment_paths_request.rs @@ -0,0 +1,193 @@ +use crate::models::Request; +use crate::paths::*; +use crate::BuildQueryParametersExt; + +/// Represents a request to find payment paths on the Stellar Horizon API. +/// +/// This struct is designed to construct a query for discovering viable payment paths from a +/// source account to a destination account, given specific destination assets and amounts. +/// It adheres to the structure and parameters required by the Horizon API for retrieving +/// payment paths. +/// +/// # Usage +/// +/// Create an instance using the `new` method, and then specify the destination asset, amount, +/// and source account using the provided setters. Once the required parameters are set, you +/// can pass this request object to the appropriate method in the Horizon client to fetch +/// the available payment paths. +/// +/// # Example +/// ``` +/// use stellar_rs::paths::prelude::*; +/// use stellar_rs::paths::{AssetType}; +/// +/// let request = FindPaymentsPathRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_source_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap(); // Sets the destination account. +/// ``` +/// +#[derive(Default)] +pub struct FindPaymentsPathRequest< + DAs = NoDestinationAsset, + DAm = NoDestinationAmount, + S = NoSourceAccount, +> { + /// Represents the asset type being sent to the destination account. + pub destination_asset: DAs, + /// Specifies the amount of the destination asset to be received. + pub destination_amount: DAm, + /// Optionally contains the public key of the destination account. + pub destination_account: Option, + /// Identifies the source account from which the payment path originates. + pub source_account: S, +} + +impl FindPaymentsPathRequest { + /// Creates a new `FindPaymentsPathRequest` with default parameters. + pub fn new() -> Self { + FindPaymentsPathRequest { + destination_asset: NoDestinationAsset, + destination_amount: NoDestinationAmount, + destination_account: None, + source_account: NoSourceAccount, + } + } +} + +impl FindPaymentsPathRequest { + /// Sets the destination asset for the payment path request. + /// + /// # Arguments + /// * `destination_asset_type` - The type of asset being sent to the destination account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the destination asset set. + /// + pub fn set_destination_asset( + self, + destination_asset_type: AssetType, + ) -> Result, String> { + Ok(FindPaymentsPathRequest { + destination_asset: DestinationAsset(destination_asset_type), + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: self.source_account, + }) + } + + /// Sets the destination amount for the payment path request. + /// + /// # Arguments + /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the destination amount set. + /// + pub fn set_destination_amount( + self, + destination_amount: String, + ) -> Result, String> { + Ok(FindPaymentsPathRequest { + destination_asset: self.destination_asset, + destination_amount: DestinationAmount(destination_amount), + destination_account: self.destination_account, + source_account: self.source_account, + }) + } + + /// Sets the source account for the payment path request. + /// + /// # Arguments + /// * `source_account` - The Stellar public key of the source account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the source account set + /// + pub fn set_source_account( + self, + source_account: String, + ) -> Result, String> { + Ok(FindPaymentsPathRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: SourceAccount(source_account), + }) + } +} + +impl FindPaymentsPathRequest { + /// Sets the destination account for the payment path request. + /// + /// # Arguments + /// * `destination_account` - The Stellar public key of the destination account. + /// + /// # Returns + /// A new instance of `FindPaymentsPathRequest` with the destination account set. + /// + pub fn set_destination_account( + self, + destination_account: String, + ) -> Result, String> + { + Ok(FindPaymentsPathRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: Some(destination_account), + source_account: self.source_account, + }) + } +} + +impl Request for FindPaymentsPathRequest { + fn get_query_parameters(&self) -> String { + let asset_type_prefix = "destination_asset_type="; + let asset_code_prefix = "&destination_asset_code="; + let asset_issuer_prefix = "&destination_asset_issuer="; + + // Construct parameters for destination asset. + let parameters = match &self.destination_asset { + DestinationAsset(AssetType::Native) => format!("{}=native", asset_type_prefix), + DestinationAsset(AssetType::CreditAlphanum4(asset_data)) + | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.destination_asset { + DestinationAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + DestinationAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + _ => "", // should not be reached + }; + + format!( + "{}{}{}{}{}{}", + asset_type_prefix, + asset_type, + asset_code_prefix, + asset_data.asset_code, + asset_issuer_prefix, + asset_data.issuer_account_id + ) + } + }; + + // Construct and return the query parameters. + vec![ + Some(parameters), + Some(format!("destination_amount={}", self.destination_amount.0)), + self.destination_account + .as_ref() + .map(|d| format!("destination_account={}", d)), + Some(format!("source_account={}", self.source_account.0)), + ] + .build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}{}", + base_url, + super::PATHS_PATH, + self.get_query_parameters() + ) + } +} diff --git a/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs b/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs new file mode 100644 index 0000000..ba3181d --- /dev/null +++ b/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs @@ -0,0 +1,252 @@ +use crate::models::Request; +use crate::paths::*; +use crate::BuildQueryParametersExt; + +/// Represents a request to list strict receive payment paths on the Stellar Horizon API. +/// +/// This struct is designed to construct a query for discovering payment paths that allow +/// a specified destination amount of a specified asset to be received, considering one or more +/// source assets and accounts. It adheres to the structure and parameters required by the Horizon API +/// for retrieving such payment paths. +/// +/// # Usage +/// +/// Create an instance using the `new` method, and then specify the destination asset, amount, +/// source account, and source assets using the provided setters. Once the required parameters are set, +/// you can pass this request object to the appropriate method in the Horizon client to fetch +/// the available strict receive payment paths. +/// +/// # Example +/// ``` +/// use stellar_rs::paths::prelude::*; +/// use stellar_rs::paths::{AssetType, SourceAsset, IssuedOrNative}; +/// +/// let request = ListStrictReceivePaymentPathsRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_source_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap() // Sets the destination account. +/// .set_source_assets(vec![SourceAsset(IssuedOrNative::Native)]).unwrap(); // Sets the source assets. +/// ``` +/// +#[derive(Default, Clone)] +pub struct ListStrictReceivePaymentPathsRequest< + DAs = NoDestinationAsset, + DAm = NoDestinationAmount, + S = NoSourceAccount, +> { + /// Represents the asset type being received by the destination account. + destination_asset: DAs, + /// Specifies the amount of the destination asset to be received. + destination_amount: DAm, + /// Optionally contains the public key of the destination account. + destination_account: Option, + /// Identifies the source account from which the payment path originates. + source_account: S, + /// Optionally contains a list of source assets to consider when finding payment paths. + source_assets: Option>, +} + +impl + ListStrictReceivePaymentPathsRequest +{ + /// Creates a new `ListStrictReceivePaymentPathsRequest` with default parameters. + pub fn new() -> Self { + ListStrictReceivePaymentPathsRequest { + destination_asset: NoDestinationAsset, + destination_amount: NoDestinationAmount, + destination_account: None, + source_account: NoSourceAccount, + source_assets: None, + } + } +} + +impl ListStrictReceivePaymentPathsRequest { + /// Sets the destination asset for the payment path request. + /// + /// # Arguments + /// * `destination_asset_type` - The type of asset being received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination asset set. + /// + pub fn set_destination_asset( + self, + destination_asset_type: AssetType, + ) -> Result, String> { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: DestinationAsset(destination_asset_type), + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: self.source_account, + source_assets: self.source_assets, + }) + } + + pub fn set_destination_amount( + self, + destination_amount: String, + ) -> Result, String> { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: DestinationAmount(destination_amount), + destination_account: self.destination_account, + source_account: self.source_account, + source_assets: self.source_assets, + }) + } + + /// Sets the destination amount for the payment path request. + /// + /// # Arguments + /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination amount set. + /// + pub fn set_source_account( + self, + source_account: String, + ) -> Result, String> { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: SourceAccount(source_account), + source_assets: self.source_assets, + }) + } +} + +impl ListStrictReceivePaymentPathsRequest { + /// Sets the destination account for the payment path request. + /// + /// # Arguments + /// * `destination_account` - The Stellar public key of the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination account set. + /// + pub fn set_destination_account( + self, + destination_account: String, + ) -> Result< + ListStrictReceivePaymentPathsRequest, + String, + > { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: Some(destination_account), + source_account: self.source_account, + source_assets: self.source_assets, + }) + } + + /// Sets the source assets for the payment path request. + /// + /// # Arguments + /// * `source_assets` - A list of source assets to consider when finding payment paths. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the source assets set. + pub fn set_source_assets( + self, + source_assets: Vec, + ) -> Result< + ListStrictReceivePaymentPathsRequest, + String, + > { + Ok(ListStrictReceivePaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_account: self.source_account, + source_assets: Some(source_assets), + }) + } +} + +impl Request + for ListStrictReceivePaymentPathsRequest +{ + fn get_query_parameters(&self) -> String { + let asset_type_prefix = "destination_asset_type="; + let asset_code_prefix = "&destination_asset_code="; + let asset_issuer_prefix = "&destination_asset_issuer="; + + // Construct parameters for destination asset. + let destination_asset_parameters = match &self.destination_asset { + DestinationAsset(AssetType::Native) => format!("{}=native", asset_type_prefix), + DestinationAsset(AssetType::CreditAlphanum4(asset_data)) + | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.destination_asset { + DestinationAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + DestinationAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + _ => "", // should not be reached + }; + + format!( + "{}{}{}{}{}{}", + asset_type_prefix, + asset_type, + asset_issuer_prefix, + asset_data.issuer_account_id, + asset_code_prefix, + asset_data.asset_code, + ) + } + }; + + // Construct source asset parameters, if any. + // If no source asset parameters are set, return an empty vector which will later be ignored. + let source_assets_parameters = + self.source_assets.as_ref().map_or(String::new(), |assets| { + assets + .iter() + .enumerate() + .map(|(i, asset)| { + let prefix = if i == 0 { "source_assets=" } else { "%2C" }; + match asset { + SourceAsset(IssuedOrNative::Native) => format!("{}native", prefix), + SourceAsset(IssuedOrNative::Issued(asset_data)) => { + format!( + "{}{}%3A{}", + prefix, asset_data.asset_code, asset_data.issuer_account_id + ) + } + } + }) + .collect::>() + .join("") + }); + + // Create query parameters vector. + let mut query_parameters = vec![ + Some(destination_asset_parameters), + Some(format!("destination_amount={}", self.destination_amount.0)), + self.destination_account + .as_ref() + .map(|d| format!("destination_account={}", d)), + Some(format!("source_account={}", self.source_account.0)), + ]; + + // Only add source assets parameters if the vector is not empty, to prevent a trailing `&`. + if !source_assets_parameters.is_empty() { + query_parameters.push(Some(source_assets_parameters)); + } + + query_parameters.build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}/{}{}", + base_url, + super::PATHS_PATH, + super::PATHS_STRICT_RECEIVE_PATH, + self.get_query_parameters() + ) + } +} diff --git a/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs b/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs new file mode 100644 index 0000000..c9a2c8d --- /dev/null +++ b/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs @@ -0,0 +1,218 @@ +use crate::models::Request; +use crate::paths::*; +use crate::BuildQueryParametersExt; + +/// Represents a request to list strict send payment paths on the Stellar Horizon API. +/// +/// This struct is designed to construct a query for discovering payment paths that allow +/// a specified destination amount of a specified asset to be sent, considering one or more +/// source assets. It adheres to the structure and parameters required by the Horizon API +/// for retrieving such payment paths. +/// +/// # Usage +/// +/// Create an instance using the `new` method, and then specify the destination asset, amount, +/// destination account, and source assets using the provided setters. Once the required parameters are set, +/// you can pass this request object to the appropriate method in the Horizon client to fetch +/// the available strict send payment paths. +/// +/// # Example +/// ``` +/// use stellar_rs::paths::prelude::*; +/// use stellar_rs::paths::{AssetType, SourceAsset, IssuedOrNative}; +/// +/// let request = ListStrictSendPaymentPathsRequest::new() +/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. +/// .set_destination_amount("100".to_string()).unwrap() // Sets the amount of the destination asset. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap() // Sets the destination account. +/// .set_source_assets(vec![SourceAsset(IssuedOrNative::Native)]).unwrap(); // Sets the source assets. +/// ``` +/// +#[derive(Default, Clone)] +pub struct ListStrictSendPaymentPathsRequest { + /// Represents the asset type being received by the destination account. + destination_asset: DAs, + /// Specifies the amount of the destination asset to be received. + destination_amount: DAm, + /// Optionally contains the public key of the destination account. + destination_account: Option, + /// Optionally contains a list of source assets to consider when finding payment paths. + source_assets: Option>, +} + +impl ListStrictSendPaymentPathsRequest { + /// Creates a new `ListStrictSendPaymentPathsRequest` with default parameters. + pub fn new() -> Self { + ListStrictSendPaymentPathsRequest { + destination_asset: NoDestinationAsset, + destination_amount: NoDestinationAmount, + destination_account: None, + source_assets: None, + } + } +} + +impl ListStrictSendPaymentPathsRequest { + /// Sets the destination asset for the payment path request. + /// + /// # Arguments + /// * `destination_asset_type` - The type of asset being received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination asset set. + /// + pub fn set_destination_asset( + self, + destination_asset_type: AssetType, + ) -> Result, String> { + Ok(ListStrictSendPaymentPathsRequest { + destination_asset: DestinationAsset(destination_asset_type), + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_assets: self.source_assets, + }) + } + + /// Sets the destination amount for the payment path request. + /// + /// # Arguments + /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination amount set. + /// + pub fn set_destination_amount( + self, + destination_amount: String, + ) -> Result, String> { + Ok(ListStrictSendPaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: DestinationAmount(destination_amount), + destination_account: self.destination_account, + source_assets: self.source_assets, + }) + } +} + +impl ListStrictSendPaymentPathsRequest { + /// Sets the destination account for the payment path request. + /// + /// # Arguments + /// * `destination_account` - The Stellar public key of the destination account. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination account set. + /// + pub fn set_destination_account( + self, + destination_account: String, + ) -> Result, String> + { + Ok(ListStrictSendPaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: Some(destination_account), + source_assets: self.source_assets, + }) + } + /// Sets the source assets for the payment path request. + /// + /// # Arguments + /// * `source_assets` - A list of source assets to consider when finding payment paths. + /// + /// # Returns + /// A new instance of `ListStrictSendPaymentPathsRequest` with the source assets set. + /// + pub fn set_source_assets( + self, + source_assets: Vec, + ) -> Result, String> + { + Ok(ListStrictSendPaymentPathsRequest { + destination_asset: self.destination_asset, + destination_amount: self.destination_amount, + destination_account: self.destination_account, + source_assets: Some(source_assets), + }) + } +} + +impl Request for ListStrictSendPaymentPathsRequest { + fn get_query_parameters(&self) -> String { + let asset_type_prefix = "destination_asset_type="; + let asset_code_prefix = "&destination_asset_code="; + let asset_issuer_prefix = "&destination_asset_issuer="; + + // Construct parameters for destination asset. + let destination_asset_parameters = match &self.destination_asset { + DestinationAsset(AssetType::Native) => format!("{}=native", asset_type_prefix), + DestinationAsset(AssetType::CreditAlphanum4(asset_data)) + | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.destination_asset { + DestinationAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + DestinationAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + _ => "", // should not be reached + }; + + format!( + "{}{}{}{}{}{}", + asset_type_prefix, + asset_type, + asset_issuer_prefix, + asset_data.issuer_account_id, + asset_code_prefix, + asset_data.asset_code, + ) + } + }; + + // Construct source asset parameters, if any. + // If no source asset parameters are set, return an empty vector which will later be ignored. + let source_assets_parameters = + self.source_assets.as_ref().map_or(String::new(), |assets| { + assets + .iter() + .enumerate() + .map(|(i, asset)| { + let prefix = if i == 0 { "source_assets=" } else { "%2C" }; + match asset { + SourceAsset(IssuedOrNative::Native) => format!("{}native", prefix), + SourceAsset(IssuedOrNative::Issued(asset_data)) => { + format!( + "{}{}%3A{}", + prefix, asset_data.asset_code, asset_data.issuer_account_id + ) + } + } + }) + .collect::>() + .join("") + }); + + // Create query parameters vector. + let mut query_parameters = vec![ + Some(destination_asset_parameters), + Some(format!("destination_amount={}", self.destination_amount.0)), + self.destination_account + .as_ref() + .map(|d| format!("destination_account={}", d)), + ]; + + // Only add source assets parameters if the vector is not empty, to prevent a trailing `&`. + if !source_assets_parameters.is_empty() { + query_parameters.push(Some(source_assets_parameters)); + } + + query_parameters.build_query_parameters() + } + + fn build_url(&self, base_url: &str) -> String { + format!( + "{}/{}/{}{}", + base_url, + super::PATHS_PATH, + super::PATHS_STRICT_SEND_PATH, + self.get_query_parameters() + ) + } +} diff --git a/stellar_rust_sdk/src/paths/mod.rs b/stellar_rust_sdk/src/paths/mod.rs new file mode 100644 index 0000000..42041b3 --- /dev/null +++ b/stellar_rust_sdk/src/paths/mod.rs @@ -0,0 +1,377 @@ +/// Provides the `FindPaymentPathsRequest`. +/// +/// # Usage +/// This module provides the `FindPaymentPathsRequest` struct, specifically designed for +/// constructing requests to find payment paths based on certain criteria. It is tailored for +/// use with the [`PaymentClient::find_payment_paths`](crate::payment_client::PaymentClient::find_payment_paths) +/// method. +/// +pub mod find_payment_paths_request; + +/// Provides the `ListStrictReceivePaymentPathsRequest`. +/// +/// # Usage +/// This module provides the `ListStrictReceivePaymentPathsRequest` struct, specifically designed for +/// constructing requests to list strict receive payment paths. It is tailored for +/// use with the [`PaymentClient::list_strict_receive_payment_paths`](crate::payment_client::PaymentClient::list_strict_receive_payment_paths) +/// method. +/// +pub mod list_strict_receive_payment_paths_request; + +/// Provides the `ListStrictSendPaymentPathsRequest`. +/// +/// # Usage +/// This module provides the `ListStrictSendPaymentPathsRequest` struct, specifically designed for +/// constructing requests to list strict send payment paths. It is tailored for +/// use with the [`PaymentClient::list_strict_send_payment_paths`](crate::payment_client::PaymentClient::list_strict_send_payment_paths) +/// method. +/// +pub mod list_strict_send_payment_paths_request; + +/// Provides the response structures. +/// +/// This module defines structures representing the responses from the payment path API. +/// The structures are designed to deserialize the JSON response into Rust objects, enabling +/// straightforward access to various details of payment paths. +/// +/// # Usage +/// These structures are equipped with serialization capabilities to handle the JSON data from the +/// payment path server and with getter methods for easy field access. +pub mod response; + +/// The base paths for offer-related endpoints in the Horizon API. +/// +/// # Usage +/// This variable is intended to be used internally by the request-building logic +/// to ensure consistent and accurate path construction for offer-related API calls. +/// +pub(crate) static PATHS_PATH: &str = "paths"; // the base API path +pub(crate) static PATHS_STRICT_RECEIVE_PATH: &str = "strict-receive"; +pub(crate) static PATHS_STRICT_SEND_PATH: &str = "strict-send"; + +/// Represents the destination asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct DestinationAsset(AssetType); + +/// Represents the absence of a destination asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoDestinationAsset; + +/// Represents the destination amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct DestinationAmount(String); + +/// Represents the absence of a destination amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoDestinationAmount; + +/// Represents the source account for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAccount(String); + +/// Represents the absence of a source account for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoSourceAccount; + +/// Represents different types of assets for payment path requests. +#[derive(Default, Clone, Debug)] +pub enum AssetType { + #[default] + Native, + CreditAlphanum4(Asset), + CreditAlphanum12(Asset), +} + +/// Represents an asset containing an asset code and issuer account ID. +#[derive(Clone, Debug)] +pub struct Asset { + pub asset_code: String, + pub issuer_account_id: String, +} + +/// Represents a source asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAsset(pub IssuedOrNative); + +/// Represents whether the source asset is native or issued. +#[derive(Default, Clone, Debug)] +pub enum IssuedOrNative { + #[default] + Native, + Issued(Asset), +} + +/// The `prelude` module of the `paths` module. +/// +/// # Usage +/// This module serves as a convenience for users of the payment path Rust SDK, allowing for easy and +/// ergonomic import of the most commonly used items across various modules. It re-exports +/// key structs and traits from the sibling modules, simplifying access to these components +/// when using the library. +/// +/// By importing the contents of `prelude`, users can conveniently access the primary +/// functionalities of the payment path-related modules without needing to import each item +/// individually. +/// +/// # Contents +/// +/// The `prelude` includes the following re-exports: +/// +/// * From `find_payment_paths_request`: All items (e.g. `FindPaymentPathsRequest`). +/// * From `list_strict_receive_payment_paths_request`: All items (e.g. `ListStrictReceivePaymentPathsRequest`). +/// * From `list_strict_send_payment_paths_request`: All items (e.g. `ListStrictSendPaymentPathsRequest`). +/// * From `response`: All items (e.g. `PaymentPathResponse`, etc.). +/// +pub mod prelude { + pub use super::find_payment_paths_request::*; + pub use super::list_strict_receive_payment_paths_request::*; + pub use super::list_strict_send_payment_paths_request::*; + pub use super::response::*; +} + +#[cfg(test)] +mod tests { + use super::prelude::{ + FindPaymentsPathRequest, ListStrictReceivePaymentPathsRequest, + ListStrictSendPaymentPathsRequest, + }; + use super::{AssetType, IssuedOrNative, SourceAsset}; + use crate::models::Request; + + #[test] + fn test_find_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH}; + const TARGET_PARAMETERS: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_code=USDC&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H"; + + let request = FindPaymentsPathRequest::new() + .set_destination_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source_account( + "GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H".to_string(), + ) + .unwrap() + .set_destination_account( + "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + ) + .unwrap(); + + // Test the construction of the parameters string. + assert_eq!(TARGET_PARAMETERS, request.get_query_parameters()); + + // Test the construction of the url. + let url = "base_url"; + assert_eq!( + format!("{}/{}{}", url, PATHS_PATH, request.get_query_parameters()), + request.build_url(url) + ); + } + + #[test] + fn test_list_strict_receive_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_RECEIVE_PATH}; + + // Create a base request without optional source assets. This request should be valid on its own, and is used to test several combinations of source assets. + let request_base = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source_account( + "GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H".to_string(), + ) + .unwrap() + .set_destination_account( + "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + ) + .unwrap(); + + // Test base request. + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H"; + assert_eq!(expected_parameters, request_base.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_RECEIVE_PATH, + request_base.get_query_parameters() + ), + request_base.build_url(url) + ); + + // Test base request with source assets, the first asset being of the `Native` type. + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H&source_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; + + let test1 = request_base + .clone() + .set_source_assets(vec![ + SourceAsset(IssuedOrNative::Native), + SourceAsset(IssuedOrNative::Native), + SourceAsset(IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + })), + ]) + .unwrap(); + + assert_eq!(expected_parameters, test1.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_RECEIVE_PATH, + test1.get_query_parameters() + ), + test1.build_url(url) + ); + + // Test base request with source assets, the first asset being of the `Issued` type. + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H&source_assets=USDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4%2Cnative%2Cnative"; + + let test1 = request_base + .clone() + .set_source_assets(vec![ + SourceAsset(IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + })), + SourceAsset(IssuedOrNative::Native), + SourceAsset(IssuedOrNative::Native), + ]) + .unwrap(); + + assert_eq!(expected_parameters, test1.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_RECEIVE_PATH, + test1.get_query_parameters() + ), + test1.build_url(url) + ); + } + + #[test] + fn test_list_strict_send_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_SEND_PATH}; + + // Create a base request without optional source assets. This request should be valid on its own, and is used to test several combinations of source assets. + let request_base = ListStrictSendPaymentPathsRequest::new() + .set_destination_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_destination_account( + "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + ) + .unwrap(); + + // Test base request. + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; + assert_eq!(expected_parameters, request_base.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_SEND_PATH, + request_base.get_query_parameters() + ), + request_base.build_url(url) + ); + + // Test base request with source assets, the first asset being of the `Native` type. + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; + + let test1 = request_base + .clone() + .set_source_assets(vec![ + SourceAsset(IssuedOrNative::Native), + SourceAsset(IssuedOrNative::Native), + SourceAsset(IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + })), + ]) + .unwrap(); + + assert_eq!(expected_parameters, test1.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_SEND_PATH, + test1.get_query_parameters() + ), + test1.build_url(url) + ); + + // Test base request with source assets, the first asset being of the `Issued` type. + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_assets=USDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4%2Cnative%2Cnative"; + + let test1 = request_base + .clone() + .set_source_assets(vec![ + SourceAsset(IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + })), + SourceAsset(IssuedOrNative::Native), + SourceAsset(IssuedOrNative::Native), + ]) + .unwrap(); + + assert_eq!(expected_parameters, test1.get_query_parameters()); + + let url = "base_url"; + assert_eq!( + format!( + "{}/{}/{}{}", + url, + PATHS_PATH, + PATHS_STRICT_SEND_PATH, + test1.get_query_parameters() + ), + test1.build_url(url) + ); + } +} diff --git a/stellar_rust_sdk/src/paths/response.rs b/stellar_rust_sdk/src/paths/response.rs new file mode 100644 index 0000000..bdcfebf --- /dev/null +++ b/stellar_rust_sdk/src/paths/response.rs @@ -0,0 +1,61 @@ +use crate::models::prelude::Embedded; +use crate::models::Response; +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; + +/// Represents the response for a payment paths query. +/// +/// This struct defines the overall structure of the response for a query +/// that retrieves payment paths. It includes the embedded results which +/// consist of the details of each payment path. +/// +#[derive(Debug, Clone, Serialize, Deserialize, Getters)] +pub struct PathsResponse { + embedded: Embedded, +} + +/// Represents a single payment path. +/// +/// This struct details a specific payment path including information about +/// the source and destination assets, their amounts, and the sequence of assets +/// that form the path. +/// +#[derive(Debug, Clone, Serialize, Deserialize, Getters)] +pub struct Path { + /// The type of the source asset. + source_asset_type: String, + /// The code of the source asset. Optional. + source_asset_code: Option, + /// The issuer of the source asset. Optional. + source_asset_issuer: Option, + /// The amount of the source asset. + source_amount: String, + /// The type of the destination asset. + destination_asset_type: String, + /// The code of the destination asset. Optional. + destination_asset_code: Option, + /// The issuer of the destination asset. Optional. + destination_asset_issuer: Option, + /// The amount of the destination asset. + destination_amount: String, + /// A vector of assets forming the path. + path: Vec, +} + +/// Represents a single asset used in the payment path. +/// +/// This struct details the information about an asset including its type, +/// code, and issuer. +/// +#[derive(Debug, Clone, Serialize, Deserialize, Getters)] +pub struct Asset { + asset_type: String, + asset_code: Option, + asset_issuer: Option, +} + +impl Response for PathsResponse { + fn from_json(json: String) -> Result { + serde_json::from_str(&json).map_err(|e| e.to_string()) + } +} From 95c12a05ba6f7e397134c4d2cb94a0d21e505ca5 Mon Sep 17 00:00:00 2001 From: Kevin Pease Date: Fri, 30 Aug 2024 10:07:23 +0200 Subject: [PATCH 2/4] Update testnet values --- stellar_rust_sdk/src/offers/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stellar_rust_sdk/src/offers/mod.rs b/stellar_rust_sdk/src/offers/mod.rs index 7e611d1..3da3ccb 100644 --- a/stellar_rust_sdk/src/offers/mod.rs +++ b/stellar_rust_sdk/src/offers/mod.rs @@ -229,12 +229,12 @@ pub mod test { const BUYING_ASSET_CODE: &str = "EURCAllow"; const BUYING_ASSET_ISSUER: &str = "GA6HVGLFUF3BHHGR5CMYXIVZ3RYVUH5EUYAOAY4T3OKI5OQVIWVRK24R"; - const AMOUNT: &str = "922192119415.1975807"; + const AMOUNT: &str = "922192119411.8475807"; const PRICE_R_N: &u32 = &1; const PRICE_R_D: &u32 = &1; const PRICE: &str = "1.0000000"; - const LAST_MODIFIED_LEDGER: &u32 = &923809; - const LAST_MODIFIED_TIME: &str = "2024-08-07T02:21:13Z"; + const LAST_MODIFIED_LEDGER: &u32 = &1280060; + const LAST_MODIFIED_TIME: &str = "2024-08-28T18:20:37Z"; let horizon_client = HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); From 6e4f01b7c497e1f0a9ec86860062b0e226d91e6a Mon Sep 17 00:00:00 2001 From: Kevin Pease Date: Mon, 9 Sep 2024 15:56:05 +0200 Subject: [PATCH 3/4] Refactor requests to match API and extend tests --- stellar_rust_sdk/src/horizon_client.rs | 10 +- .../src/paths/find_payment_paths_request.rs | 22 +- ...st_strict_receive_payment_paths_request.rs | 176 ++++---- .../list_strict_send_payment_paths_request.rs | 247 ++++++----- stellar_rust_sdk/src/paths/mod.rs | 414 +++++++++++------- stellar_rust_sdk/src/paths/response.rs | 1 + 6 files changed, 509 insertions(+), 361 deletions(-) diff --git a/stellar_rust_sdk/src/horizon_client.rs b/stellar_rust_sdk/src/horizon_client.rs index 43db962..cbadfc4 100644 --- a/stellar_rust_sdk/src/horizon_client.rs +++ b/stellar_rust_sdk/src/horizon_client.rs @@ -33,7 +33,7 @@ use crate::{ details_request::{BuyingAsset, DetailsRequest, SellingAsset}, response::DetailsResponse, }, - paths::{prelude::*, DestinationAmount, DestinationAsset, SourceAccount}, + paths::prelude::*, payments::prelude::*, trade_aggregations::prelude::*, trades::prelude::*, @@ -2030,11 +2030,7 @@ impl HorizonClient { /// filters or parameters. pub async fn get_list_strict_receive_payment_paths( &self, - request: &ListStrictReceivePaymentPathsRequest< - DestinationAsset, - DestinationAmount, - SourceAccount, - >, + request: &ListStrictReceivePaymentPathsRequest, ) -> Result { self.get::(request).await } @@ -2059,7 +2055,7 @@ impl HorizonClient { /// filters or parameters. pub async fn get_list_strict_send_payment_paths( &self, - request: &ListStrictSendPaymentPathsRequest, + request: &ListStrictSendPaymentPathsRequest, ) -> Result { self.get::(request).await } diff --git a/stellar_rust_sdk/src/paths/find_payment_paths_request.rs b/stellar_rust_sdk/src/paths/find_payment_paths_request.rs index 365c305..f2f54c6 100644 --- a/stellar_rust_sdk/src/paths/find_payment_paths_request.rs +++ b/stellar_rust_sdk/src/paths/find_payment_paths_request.rs @@ -1,4 +1,4 @@ -use crate::models::Request; +use crate::models::{is_public_key, Request}; use crate::paths::*; use crate::BuildQueryParametersExt; @@ -87,11 +87,11 @@ impl FindPaymentsPathRequest { /// pub fn set_destination_amount( self, - destination_amount: String, + destination_amount: impl Into, ) -> Result, String> { Ok(FindPaymentsPathRequest { destination_asset: self.destination_asset, - destination_amount: DestinationAmount(destination_amount), + destination_amount: DestinationAmount(destination_amount.into()), destination_account: self.destination_account, source_account: self.source_account, }) @@ -107,8 +107,13 @@ impl FindPaymentsPathRequest { /// pub fn set_source_account( self, - source_account: String, + source_account: impl Into, ) -> Result, String> { + let source_account = source_account.into(); + if let Err(e) = is_public_key(&source_account) { + return Err(e.to_string()); + } + Ok(FindPaymentsPathRequest { destination_asset: self.destination_asset, destination_amount: self.destination_amount, @@ -129,9 +134,14 @@ impl FindPaymentsPathRequest /// pub fn set_destination_account( self, - destination_account: String, + destination_account: impl Into, ) -> Result, String> { + let destination_account = destination_account.into(); + if let Err(e) = is_public_key(&destination_account) { + return Err(e.to_string()); + } + Ok(FindPaymentsPathRequest { destination_asset: self.destination_asset, destination_amount: self.destination_amount, @@ -149,7 +159,7 @@ impl Request for FindPaymentsPathRequest format!("{}=native", asset_type_prefix), + DestinationAsset(AssetType::Native) => format!("{}native", asset_type_prefix), DestinationAsset(AssetType::CreditAlphanum4(asset_data)) | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { let asset_type = match self.destination_asset { diff --git a/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs b/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs index ba3181d..85de18f 100644 --- a/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs +++ b/stellar_rust_sdk/src/paths/list_strict_receive_payment_paths_request.rs @@ -1,39 +1,58 @@ -use crate::models::Request; +use crate::models::{is_public_key, Request}; use crate::paths::*; use crate::BuildQueryParametersExt; +/// Represents the absence of either a source account or source asset(s). +#[derive(Default, Clone, Debug)] +pub struct NoSource; + +/// Represents the source which can be either a vector of assets, or an account. +/// Exactly one of these must be set, in order to make a valid request. +#[derive(Clone, Debug)] +pub enum Source { + /// A vector of assets available to the sender. Any returned path must start with an asset in this list. + SourceAssets(Vec), + /// The Stellar address of the sender. Any returned path must start with an asset that the sender holds. + SourceAccount(String), +} + +impl Default for Source { + fn default() -> Self { + Source::SourceAssets(Vec::new()) + } +} + /// Represents a request to list strict receive payment paths on the Stellar Horizon API. /// -/// This struct is designed to construct a query for discovering payment paths that allow +/// This struct is designed to construct a query for listing payment paths that allow /// a specified destination amount of a specified asset to be received, considering one or more /// source assets and accounts. It adheres to the structure and parameters required by the Horizon API /// for retrieving such payment paths. /// /// # Usage /// -/// Create an instance using the `new` method, and then specify the destination asset, amount, -/// source account, and source assets using the provided setters. Once the required parameters are set, -/// you can pass this request object to the appropriate method in the Horizon client to fetch -/// the available strict receive payment paths. +/// Create an instance using the `new` method, and then specify the destination asset, the destination +/// amount, and the source using the provided setters. Once the required parameters are set, optional +/// parameters can be set, and the request object can then be passed to the appropriate method +/// in the Horizon client to fetch the available strict receive payment paths. /// /// # Example /// ``` /// use stellar_rs::paths::prelude::*; -/// use stellar_rs::paths::{AssetType, SourceAsset, IssuedOrNative}; +/// use stellar_rs::paths::{AssetType, IssuedOrNative}; /// /// let request = ListStrictReceivePaymentPathsRequest::new() /// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. /// .set_destination_amount("100.0".to_string()).unwrap() // Sets the amount of the destination asset. -/// .set_source_account("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string()).unwrap() // Sets the source account. -/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap() // Sets the destination account. -/// .set_source_assets(vec![SourceAsset(IssuedOrNative::Native)]).unwrap(); // Sets the source assets. +/// .set_source(Source::SourceAccount("GCDNJUBQSXK57MSKJ4NSXK5DT5CJMMXMWUE7BN6NTJ6JTH23HQVYXG2C".to_string())).unwrap() // Sets the source account. +/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap(); // Sets the destination account. /// ``` /// #[derive(Default, Clone)] pub struct ListStrictReceivePaymentPathsRequest< DAs = NoDestinationAsset, DAm = NoDestinationAmount, - S = NoSourceAccount, + S = Source, > { /// Represents the asset type being received by the destination account. destination_asset: DAs, @@ -41,23 +60,18 @@ pub struct ListStrictReceivePaymentPathsRequest< destination_amount: DAm, /// Optionally contains the public key of the destination account. destination_account: Option, - /// Identifies the source account from which the payment path originates. - source_account: S, - /// Optionally contains a list of source assets to consider when finding payment paths. - source_assets: Option>, + /// Represents the source which can be either a vector of assets, or an account. + source: S, } -impl - ListStrictReceivePaymentPathsRequest -{ +impl ListStrictReceivePaymentPathsRequest { /// Creates a new `ListStrictReceivePaymentPathsRequest` with default parameters. pub fn new() -> Self { ListStrictReceivePaymentPathsRequest { destination_asset: NoDestinationAsset, destination_amount: NoDestinationAmount, destination_account: None, - source_account: NoSourceAccount, - source_assets: None, + source: NoSource, } } } @@ -79,47 +93,65 @@ impl ListStrictReceivePaymentPathsRequest { destination_asset: DestinationAsset(destination_asset_type), destination_amount: self.destination_amount, destination_account: self.destination_account, - source_account: self.source_account, - source_assets: self.source_assets, + source: self.source, }) } + /// Sets the destination amount for the payment path request. + /// + /// # Arguments + /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// + /// # Returns + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination amount set. + /// pub fn set_destination_amount( self, - destination_amount: String, + destination_amount: impl Into, ) -> Result, String> { Ok(ListStrictReceivePaymentPathsRequest { destination_asset: self.destination_asset, - destination_amount: DestinationAmount(destination_amount), + destination_amount: DestinationAmount(destination_amount.into()), destination_account: self.destination_account, - source_account: self.source_account, - source_assets: self.source_assets, + source: self.source, }) } - /// Sets the destination amount for the payment path request. + /// Sets the source for the payment path request. /// /// # Arguments - /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// * `source` - One of the `Source` enum types. /// /// # Returns - /// A new instance of `ListStrictReceivePaymentPathsRequest` with the destination amount set. + /// A new instance of `ListStrictReceivePaymentPathsRequest` with the source set. /// - pub fn set_source_account( + pub fn set_source( self, - source_account: String, - ) -> Result, String> { + source: Source, + ) -> Result, String> { + match &source { + Source::SourceAssets(assets) => { + if assets.is_empty() { + return Err("SourceAssets cannot be empty".to_string()); + } + } + Source::SourceAccount(account) => { + if let Err(e) = is_public_key(&account) { + return Err(e.to_string()); + } + } + } + Ok(ListStrictReceivePaymentPathsRequest { destination_asset: self.destination_asset, destination_amount: self.destination_amount, destination_account: self.destination_account, - source_account: SourceAccount(source_account), - source_assets: self.source_assets, + source: source, }) } } -impl ListStrictReceivePaymentPathsRequest { +impl ListStrictReceivePaymentPathsRequest { /// Sets the destination account for the payment path request. /// /// # Arguments @@ -130,47 +162,24 @@ impl ListStrictReceivePaymentPathsRequest Result< - ListStrictReceivePaymentPathsRequest, - String, - > { - Ok(ListStrictReceivePaymentPathsRequest { - destination_asset: self.destination_asset, - destination_amount: self.destination_amount, - destination_account: Some(destination_account), - source_account: self.source_account, - source_assets: self.source_assets, - }) - } + destination_account: impl Into, + ) -> Result, String> + { + let destination_account = destination_account.into(); + if let Err(e) = is_public_key(&destination_account) { + return Err(e.to_string()); + } - /// Sets the source assets for the payment path request. - /// - /// # Arguments - /// * `source_assets` - A list of source assets to consider when finding payment paths. - /// - /// # Returns - /// A new instance of `ListStrictReceivePaymentPathsRequest` with the source assets set. - pub fn set_source_assets( - self, - source_assets: Vec, - ) -> Result< - ListStrictReceivePaymentPathsRequest, - String, - > { Ok(ListStrictReceivePaymentPathsRequest { destination_asset: self.destination_asset, destination_amount: self.destination_amount, - destination_account: self.destination_account, - source_account: self.source_account, - source_assets: Some(source_assets), + destination_account: Some(destination_account.into()), + source: self.source, }) } } -impl Request - for ListStrictReceivePaymentPathsRequest -{ +impl Request for ListStrictReceivePaymentPathsRequest { fn get_query_parameters(&self) -> String { let asset_type_prefix = "destination_asset_type="; let asset_code_prefix = "&destination_asset_code="; @@ -178,7 +187,7 @@ impl Request // Construct parameters for destination asset. let destination_asset_parameters = match &self.destination_asset { - DestinationAsset(AssetType::Native) => format!("{}=native", asset_type_prefix), + DestinationAsset(AssetType::Native) => format!("{}native", asset_type_prefix), DestinationAsset(AssetType::CreditAlphanum4(asset_data)) | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { let asset_type = match self.destination_asset { @@ -199,18 +208,18 @@ impl Request } }; - // Construct source asset parameters, if any. - // If no source asset parameters are set, return an empty vector which will later be ignored. - let source_assets_parameters = - self.source_assets.as_ref().map_or(String::new(), |assets| { - assets + let source = match &self.source { + Source::SourceAssets(source_assets) => { + // Construct source asset parameters, if any. + // If no source asset parameters are set, return an empty vector which will later be ignored. + source_assets .iter() .enumerate() .map(|(i, asset)| { let prefix = if i == 0 { "source_assets=" } else { "%2C" }; match asset { - SourceAsset(IssuedOrNative::Native) => format!("{}native", prefix), - SourceAsset(IssuedOrNative::Issued(asset_data)) => { + IssuedOrNative::Native => format!("{}native", prefix), + IssuedOrNative::Issued(asset_data) => { format!( "{}{}%3A{}", prefix, asset_data.asset_code, asset_data.issuer_account_id @@ -220,23 +229,22 @@ impl Request }) .collect::>() .join("") - }); + } + Source::SourceAccount(account) => { + format!("source_account={}", account) + } + }; // Create query parameters vector. - let mut query_parameters = vec![ + let query_parameters = vec![ Some(destination_asset_parameters), Some(format!("destination_amount={}", self.destination_amount.0)), self.destination_account .as_ref() .map(|d| format!("destination_account={}", d)), - Some(format!("source_account={}", self.source_account.0)), + Some(source), ]; - // Only add source assets parameters if the vector is not empty, to prevent a trailing `&`. - if !source_assets_parameters.is_empty() { - query_parameters.push(Some(source_assets_parameters)); - } - query_parameters.build_query_parameters() } diff --git a/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs b/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs index c9a2c8d..9c690ef 100644 --- a/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs +++ b/stellar_rust_sdk/src/paths/list_strict_send_payment_paths_request.rs @@ -1,156 +1,176 @@ -use crate::models::Request; +use crate::models::{is_public_key, Request}; use crate::paths::*; use crate::BuildQueryParametersExt; +/// Represents the absence of a source asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoSourceAsset; + +/// Represents the source asset for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAsset(AssetType); + +/// Represents the absence of a source amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoSourceAmount; + +/// Represents the source amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAmount(String); + +/// Represents the absence of a source amount for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct NoDestination; + +/// Represents the destination which can be either a vector of assets, or an account. +/// Exactly one of these must be set, in order to make a valid request. +#[derive(Clone, Debug)] +pub enum Destination { + DestinationAssets(Vec), + DestinationAccount(String), +} + +impl Default for Destination { + fn default() -> Self { + Destination::DestinationAssets(Vec::new()) + } +} + /// Represents a request to list strict send payment paths on the Stellar Horizon API. /// -/// This struct is designed to construct a query for discovering payment paths that allow -/// a specified destination amount of a specified asset to be sent, considering one or more -/// source assets. It adheres to the structure and parameters required by the Horizon API -/// for retrieving such payment paths. +/// This struct is designed to construct a query for listing the paths a payment can take based +/// on the amount of an asset you want the recipient to receive. The destination asset amount +/// stays constant, and the type and amount of an asset sent varies based on offers in the order books. /// /// # Usage /// -/// Create an instance using the `new` method, and then specify the destination asset, amount, -/// destination account, and source assets using the provided setters. Once the required parameters are set, -/// you can pass this request object to the appropriate method in the Horizon client to fetch -/// the available strict send payment paths. +/// Create an instance using the `new` method, and then specify the source asset, source amount, +/// and the destination using the provided setters. Once the required parameters are set, optional +/// parameters can be set, and the request object can then be passed to the appropriate method +/// in the Horizon client to fetch the available strict send payment paths. /// /// # Example /// ``` /// use stellar_rs::paths::prelude::*; -/// use stellar_rs::paths::{AssetType, SourceAsset, IssuedOrNative}; +/// use stellar_rs::paths::{AssetType, IssuedOrNative}; /// /// let request = ListStrictSendPaymentPathsRequest::new() -/// .set_destination_asset(AssetType::Native).unwrap() // Sets the destination asset to native XLM. -/// .set_destination_amount("100".to_string()).unwrap() // Sets the amount of the destination asset. -/// .set_destination_account("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string()).unwrap() // Sets the destination account. -/// .set_source_assets(vec![SourceAsset(IssuedOrNative::Native)]).unwrap(); // Sets the source assets. +/// .set_source_asset(AssetType::Native).unwrap() // Sets the source asset to native XLM. +/// .set_source_amount("100".to_string()).unwrap() // Sets the amount of the source asset. +/// .set_destination(Destination::DestinationAccount("GAZD7JY7RCZN7KJ27SMUGKDPF7GQTYPXLDU7TFTJNSDB3MLO3M22DEIV".to_string())).unwrap(); // Sets an account as destination. /// ``` /// #[derive(Default, Clone)] -pub struct ListStrictSendPaymentPathsRequest { - /// Represents the asset type being received by the destination account. - destination_asset: DAs, - /// Specifies the amount of the destination asset to be received. - destination_amount: DAm, - /// Optionally contains the public key of the destination account. - destination_account: Option, - /// Optionally contains a list of source assets to consider when finding payment paths. - source_assets: Option>, +pub struct ListStrictSendPaymentPathsRequest< + SAs = NoSourceAsset, + SAm = NoSourceAmount, + D = Destination, +> { + /// Represents the asset type being received by the source account. + source_asset: SAs, + /// Specifies the amount of the source asset to be received. + source_amount: SAm, + /// Represents the destination which can be either a vector of assets, or an account. + destination: D, } -impl ListStrictSendPaymentPathsRequest { +impl ListStrictSendPaymentPathsRequest { /// Creates a new `ListStrictSendPaymentPathsRequest` with default parameters. pub fn new() -> Self { ListStrictSendPaymentPathsRequest { - destination_asset: NoDestinationAsset, - destination_amount: NoDestinationAmount, - destination_account: None, - source_assets: None, + source_asset: NoSourceAsset, + source_amount: NoSourceAmount, + destination: NoDestination, } } } -impl ListStrictSendPaymentPathsRequest { - /// Sets the destination asset for the payment path request. +impl ListStrictSendPaymentPathsRequest { + /// Sets the source asset for the payment path request. /// /// # Arguments - /// * `destination_asset_type` - The type of asset being received by the destination account. + /// * `source_asset_type` - The type of asset being received by the source account. /// /// # Returns - /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination asset set. + /// A new instance of `ListStrictSendPaymentPathsRequest` with the source asset set. /// - pub fn set_destination_asset( + pub fn set_source_asset( self, - destination_asset_type: AssetType, - ) -> Result, String> { + source_asset_type: AssetType, + ) -> Result, String> { Ok(ListStrictSendPaymentPathsRequest { - destination_asset: DestinationAsset(destination_asset_type), - destination_amount: self.destination_amount, - destination_account: self.destination_account, - source_assets: self.source_assets, + source_asset: SourceAsset(source_asset_type), + source_amount: self.source_amount, + destination: self.destination, }) } - /// Sets the destination amount for the payment path request. + /// Sets the source amount for the payment path request. /// /// # Arguments - /// * `destination_amount` - The amount of the asset to be received by the destination account. + /// * `source_amount` - The amount of the asset to be received by the source account. /// /// # Returns - /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination amount set. + /// A new instance of `ListStrictSendPaymentPathsRequest` with the source amount set. /// - pub fn set_destination_amount( + pub fn set_source_amount( self, - destination_amount: String, - ) -> Result, String> { + source_amount: impl Into, + ) -> Result, String> { Ok(ListStrictSendPaymentPathsRequest { - destination_asset: self.destination_asset, - destination_amount: DestinationAmount(destination_amount), - destination_account: self.destination_account, - source_assets: self.source_assets, + source_asset: self.source_asset, + source_amount: SourceAmount(source_amount.into()), + destination: self.destination, }) } -} -impl ListStrictSendPaymentPathsRequest { - /// Sets the destination account for the payment path request. - /// - /// # Arguments - /// * `destination_account` - The Stellar public key of the destination account. - /// - /// # Returns - /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination account set. - /// - pub fn set_destination_account( - self, - destination_account: String, - ) -> Result, String> - { - Ok(ListStrictSendPaymentPathsRequest { - destination_asset: self.destination_asset, - destination_amount: self.destination_amount, - destination_account: Some(destination_account), - source_assets: self.source_assets, - }) - } - /// Sets the source assets for the payment path request. + /// Sets the destination for the payment path request. /// /// # Arguments - /// * `source_assets` - A list of source assets to consider when finding payment paths. + /// * `destination` - One of the `Destination` enum types. /// /// # Returns - /// A new instance of `ListStrictSendPaymentPathsRequest` with the source assets set. + /// A new instance of `ListStrictSendPaymentPathsRequest` with the destination set. /// - pub fn set_source_assets( + pub fn set_destination( self, - source_assets: Vec, - ) -> Result, String> - { + destination: Destination, + ) -> Result, String> { + match &destination { + Destination::DestinationAssets(assets) => { + if assets.is_empty() { + return Err("DestinationAssets cannot be empty".to_string()); + } + } + Destination::DestinationAccount(account) => { + if let Err(e) = is_public_key(&account) { + return Err(e.to_string()); + } + } + } + Ok(ListStrictSendPaymentPathsRequest { - destination_asset: self.destination_asset, - destination_amount: self.destination_amount, - destination_account: self.destination_account, - source_assets: Some(source_assets), + source_asset: self.source_asset, + source_amount: self.source_amount, + destination: destination, }) } } -impl Request for ListStrictSendPaymentPathsRequest { +impl Request for ListStrictSendPaymentPathsRequest { fn get_query_parameters(&self) -> String { - let asset_type_prefix = "destination_asset_type="; - let asset_code_prefix = "&destination_asset_code="; - let asset_issuer_prefix = "&destination_asset_issuer="; - - // Construct parameters for destination asset. - let destination_asset_parameters = match &self.destination_asset { - DestinationAsset(AssetType::Native) => format!("{}=native", asset_type_prefix), - DestinationAsset(AssetType::CreditAlphanum4(asset_data)) - | DestinationAsset(AssetType::CreditAlphanum12(asset_data)) => { - let asset_type = match self.destination_asset { - DestinationAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", - DestinationAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", + let asset_type_prefix = "source_asset_type="; + let asset_code_prefix = "&source_asset_code="; + let asset_issuer_prefix = "&source_asset_issuer="; + + // Construct parameters for source asset. + let source_asset_parameters = match &self.source_asset { + SourceAsset(AssetType::Native) => format!("{}native", asset_type_prefix), + SourceAsset(AssetType::CreditAlphanum4(asset_data)) + | SourceAsset(AssetType::CreditAlphanum12(asset_data)) => { + let asset_type = match self.source_asset { + SourceAsset(AssetType::CreditAlphanum4(_)) => "credit_alphanum4", + SourceAsset(AssetType::CreditAlphanum12(_)) => "credit_alphanum12", _ => "", // should not be reached }; @@ -166,18 +186,18 @@ impl Request for ListStrictSendPaymentPathsRequest { + // Construct destination asset parameters, if any. + // If no destination asset parameters are set, return an empty vector which will later be ignored. + destination_assets .iter() .enumerate() .map(|(i, asset)| { - let prefix = if i == 0 { "source_assets=" } else { "%2C" }; + let prefix = if i == 0 { "destination_assets=" } else { "%2C" }; match asset { - SourceAsset(IssuedOrNative::Native) => format!("{}native", prefix), - SourceAsset(IssuedOrNative::Issued(asset_data)) => { + IssuedOrNative::Native => format!("{}native", prefix), + IssuedOrNative::Issued(asset_data) => { format!( "{}{}%3A{}", prefix, asset_data.asset_code, asset_data.issuer_account_id @@ -187,22 +207,19 @@ impl Request for ListStrictSendPaymentPathsRequest>() .join("") - }); + } + Destination::DestinationAccount(account) => { + format!("destination_account={}", account) + } + }; // Create query parameters vector. - let mut query_parameters = vec![ - Some(destination_asset_parameters), - Some(format!("destination_amount={}", self.destination_amount.0)), - self.destination_account - .as_ref() - .map(|d| format!("destination_account={}", d)), + let query_parameters = vec![ + Some(format!("source_amount={}", self.source_amount.0)), + Some(destination), + Some(source_asset_parameters), ]; - // Only add source assets parameters if the vector is not empty, to prevent a trailing `&`. - if !source_assets_parameters.is_empty() { - query_parameters.push(Some(source_assets_parameters)); - } - query_parameters.build_query_parameters() } diff --git a/stellar_rust_sdk/src/paths/mod.rs b/stellar_rust_sdk/src/paths/mod.rs index 42041b3..621cc04 100644 --- a/stellar_rust_sdk/src/paths/mod.rs +++ b/stellar_rust_sdk/src/paths/mod.rs @@ -3,7 +3,7 @@ /// # Usage /// This module provides the `FindPaymentPathsRequest` struct, specifically designed for /// constructing requests to find payment paths based on certain criteria. It is tailored for -/// use with the [`PaymentClient::find_payment_paths`](crate::payment_client::PaymentClient::find_payment_paths) +/// use with the [`HorizonClient::get_find_payment_paths`](crate::horizon_client::HorizonClient::get_find_payment_paths) /// method. /// pub mod find_payment_paths_request; @@ -13,7 +13,7 @@ pub mod find_payment_paths_request; /// # Usage /// This module provides the `ListStrictReceivePaymentPathsRequest` struct, specifically designed for /// constructing requests to list strict receive payment paths. It is tailored for -/// use with the [`PaymentClient::list_strict_receive_payment_paths`](crate::payment_client::PaymentClient::list_strict_receive_payment_paths) +/// use with the [`HorizonClient::get_list_strict_receive_payment_paths`](crate::horizon_client::HorizonClient::get_list_strict_receive_payment_paths) /// method. /// pub mod list_strict_receive_payment_paths_request; @@ -23,7 +23,7 @@ pub mod list_strict_receive_payment_paths_request; /// # Usage /// This module provides the `ListStrictSendPaymentPathsRequest` struct, specifically designed for /// constructing requests to list strict send payment paths. It is tailored for -/// use with the [`PaymentClient::list_strict_send_payment_paths`](crate::payment_client::PaymentClient::list_strict_send_payment_paths) +/// use with the [`HorizonClient::get_list_strict_send_payment_paths`](crate::horizon_client::HorizonClient::get_list_strict_send_payment_paths) /// method. /// pub mod list_strict_send_payment_paths_request; @@ -37,9 +37,10 @@ pub mod list_strict_send_payment_paths_request; /// # Usage /// These structures are equipped with serialization capabilities to handle the JSON data from the /// payment path server and with getter methods for easy field access. +/// pub mod response; -/// The base paths for offer-related endpoints in the Horizon API. +/// The base paths for path-related endpoints in the Horizon API. /// /// # Usage /// This variable is intended to be used internally by the request-building logic @@ -49,31 +50,31 @@ pub(crate) static PATHS_PATH: &str = "paths"; // the base API path pub(crate) static PATHS_STRICT_RECEIVE_PATH: &str = "strict-receive"; pub(crate) static PATHS_STRICT_SEND_PATH: &str = "strict-send"; -/// Represents the destination asset for a payment path request. -#[derive(Default, Clone, Debug)] -pub struct DestinationAsset(AssetType); - /// Represents the absence of a destination asset for a payment path request. #[derive(Default, Clone, Debug)] pub struct NoDestinationAsset; -/// Represents the destination amount for a payment path request. +/// Represents a source asset for a payment path request. #[derive(Default, Clone, Debug)] -pub struct DestinationAmount(String); +pub struct DestinationAsset(AssetType); /// Represents the absence of a destination amount for a payment path request. #[derive(Default, Clone, Debug)] pub struct NoDestinationAmount; -/// Represents the source account for a payment path request. +/// Represents the destination amount for a payment path request. #[derive(Default, Clone, Debug)] -pub struct SourceAccount(String); +pub struct DestinationAmount(String); /// Represents the absence of a source account for a payment path request. #[derive(Default, Clone, Debug)] pub struct NoSourceAccount; -/// Represents different types of assets for payment path requests. +/// Represents the source account for a payment path request. +#[derive(Default, Clone, Debug)] +pub struct SourceAccount(String); + +/// Represents structure of the required asset. #[derive(Default, Clone, Debug)] pub enum AssetType { #[default] @@ -89,11 +90,7 @@ pub struct Asset { pub issuer_account_id: String, } -/// Represents a source asset for a payment path request. -#[derive(Default, Clone, Debug)] -pub struct SourceAsset(pub IssuedOrNative); - -/// Represents whether the source asset is native or issued. +/// Represents structure of an asset used in the vector of optional assets. #[derive(Default, Clone, Debug)] pub enum IssuedOrNative { #[default] @@ -127,23 +124,23 @@ pub mod prelude { pub use super::list_strict_receive_payment_paths_request::*; pub use super::list_strict_send_payment_paths_request::*; pub use super::response::*; + pub use super::{ + DestinationAmount, DestinationAsset, NoDestinationAmount, NoDestinationAsset, + NoSourceAccount, SourceAccount, + }; } #[cfg(test)] mod tests { - use super::prelude::{ - FindPaymentsPathRequest, ListStrictReceivePaymentPathsRequest, - ListStrictSendPaymentPathsRequest, - }; - use super::{AssetType, IssuedOrNative, SourceAsset}; - use crate::models::Request; + use super::prelude::*; + use super::{AssetType, IssuedOrNative}; + use crate::{horizon_client::HorizonClient, models::*}; - #[test] - fn test_find_payment_paths_request() { + #[tokio::test] + async fn test_find_payment_paths_request() { use crate::paths::{Asset, PATHS_PATH}; - const TARGET_PARAMETERS: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_code=USDC&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H"; + // Test creating and sending a request with source assets. Only the response status will be checked, as the request will not yield comparable data. let request = FindPaymentsPathRequest::new() .set_destination_asset(AssetType::CreditAlphanum4(Asset { asset_code: "USDC".to_string(), @@ -162,75 +159,102 @@ mod tests { ) .unwrap(); - // Test the construction of the parameters string. - assert_eq!(TARGET_PARAMETERS, request.get_query_parameters()); + let expected_parameters = + "?destination_asset_type=credit_alphanum4&destination_asset_code=USDC&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H"; + + assert_eq!(expected_parameters, request.get_query_parameters()); - // Test the construction of the url. let url = "base_url"; assert_eq!( format!("{}/{}{}", url, PATHS_PATH, request.get_query_parameters()), request.build_url(url) ); - } - #[test] - fn test_list_strict_receive_payment_paths_request() { - use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_RECEIVE_PATH}; + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); - // Create a base request without optional source assets. This request should be valid on its own, and is used to test several combinations of source assets. - let request_base = ListStrictReceivePaymentPathsRequest::new() - .set_destination_asset(AssetType::CreditAlphanum4(Asset { - asset_code: "USDC".to_string(), - issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" - .to_string(), - })) + let response = horizon_client.get_find_payment_paths(&request).await; + + assert!(response.clone().is_ok()); + + // Test creating and sending a request with source account. + let request = FindPaymentsPathRequest::new() + .set_destination_asset(AssetType::Native) .unwrap() - .set_destination_amount("42".to_string()) + .set_destination_amount("100".to_string()) .unwrap() .set_source_account( - "GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H".to_string(), - ) - .unwrap() - .set_destination_account( - "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + "GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS".to_string(), ) .unwrap(); - // Test base request. let expected_parameters: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H"; - assert_eq!(expected_parameters, request_base.get_query_parameters()); + "?destination_asset_type=native&destination_amount=100&source_account=GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS"; + assert_eq!(request.get_query_parameters(), expected_parameters); let url = "base_url"; assert_eq!( - format!( - "{}/{}/{}{}", - url, - PATHS_PATH, - PATHS_STRICT_RECEIVE_PATH, - request_base.get_query_parameters() - ), - request_base.build_url(url) + format!("{}/{}{}", url, PATHS_PATH, request.get_query_parameters()), + request.build_url(url) ); - // Test base request with source assets, the first asset being of the `Native` type. - let expected_parameters: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H&source_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; - - let test1 = request_base - .clone() - .set_source_assets(vec![ - SourceAsset(IssuedOrNative::Native), - SourceAsset(IssuedOrNative::Native), - SourceAsset(IssuedOrNative::Issued(Asset { + let response = horizon_client.get_find_payment_paths(&request).await; + + const SOURCE_ASSET_TYPE: &str = "native"; + const SOURCE_AMOUNT: &str = "100.0000000"; + const DESTINATION_ASSET_TYPE: &str = "native"; + const DESTINATION_AMOUNT: &str = "100.0000000"; + + assert!(response.clone().is_ok()); + let binding = response.unwrap(); + let response = &binding.embedded().records()[0]; + assert_eq!(response.source_asset_type(), SOURCE_ASSET_TYPE); + assert_eq!(response.source_amount(), SOURCE_AMOUNT); + assert_eq!(response.destination_asset_type(), DESTINATION_ASSET_TYPE); + assert_eq!(response.destination_amount(), DESTINATION_AMOUNT); + + // Test creating a request with an invalid source account ID. + let request = FindPaymentsPathRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source_account("invalid_account_id".to_string()); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + } + + #[tokio::test] + async fn test_list_strict_receive_payment_paths_request() { + use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_RECEIVE_PATH}; + + // Test creating and sending a request with source assets. Only the response status will be checked, as the request will not yield comparable data. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::CreditAlphanum4(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" + .to_string(), + })) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAssets(vec![ + IssuedOrNative::Native, + IssuedOrNative::Native, + IssuedOrNative::Issued(Asset { asset_code: "USDC".to_string(), issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" .to_string(), - })), - ]) + }), + ])) .unwrap(); - assert_eq!(expected_parameters, test1.get_query_parameters()); + let expected_parameters: &str = + "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&source_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; + + assert_eq!(request.get_query_parameters(), expected_parameters); let url = "base_url"; assert_eq!( @@ -239,29 +263,34 @@ mod tests { url, PATHS_PATH, PATHS_STRICT_RECEIVE_PATH, - test1.get_query_parameters() + request.get_query_parameters() ), - test1.build_url(url) + request.build_url(url) ); - // Test base request with source assets, the first asset being of the `Issued` type. - let expected_parameters: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_account=GBAC4BTW6UIJOCCUOZ7QATQPVWX6UQVH3ESQ6NEHBMCXJ3MVP4GMT77H&source_assets=USDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4%2Cnative%2Cnative"; + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); - let test1 = request_base - .clone() - .set_source_assets(vec![ - SourceAsset(IssuedOrNative::Issued(Asset { - asset_code: "USDC".to_string(), - issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" - .to_string(), - })), - SourceAsset(IssuedOrNative::Native), - SourceAsset(IssuedOrNative::Native), - ]) + let response = horizon_client + .get_list_strict_receive_payment_paths(&request) + .await; + + assert!(response.clone().is_ok()); + + // Test creating and sending a request with destination account. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("100".to_string()) + .unwrap() + .set_source(Source::SourceAccount( + "GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS".to_string(), + )) .unwrap(); - assert_eq!(expected_parameters, test1.get_query_parameters()); + let expected_parameters: &str = + "?destination_asset_type=native&destination_amount=100&source_account=GCDE6MVFIOYF7YZCSVA6V7MDCFTNWMIOF5PQU3DWPH27AHNX4ERY6AKS"; + assert_eq!(request.get_query_parameters(), expected_parameters); let url = "base_url"; assert_eq!( @@ -270,35 +299,93 @@ mod tests { url, PATHS_PATH, PATHS_STRICT_RECEIVE_PATH, - test1.get_query_parameters() + request.get_query_parameters() ), - test1.build_url(url) + request.build_url(url) + ); + + let response = horizon_client + .get_list_strict_receive_payment_paths(&request) + .await; + + const SOURCE_ASSET_TYPE: &str = "native"; + const SOURCE_AMOUNT: &str = "100.0000000"; + const DESTINATION_ASSET_TYPE: &str = "native"; + const DESTINATION_AMOUNT: &str = "100.0000000"; + + assert!(response.clone().is_ok()); + let binding = response.unwrap(); + let response = &binding.embedded().records()[0]; + assert_eq!(response.source_asset_type(), SOURCE_ASSET_TYPE); + assert_eq!(response.source_amount(), SOURCE_AMOUNT); + assert_eq!(response.destination_asset_type(), DESTINATION_ASSET_TYPE); + assert_eq!(response.destination_amount(), DESTINATION_AMOUNT); + + // Test creating a request with an empty source assets vector. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAssets(Vec::new())); + assert_eq!(request.err().unwrap(), "SourceAssets cannot be empty"); + + // Test creating a request with an invalid asset source account ID. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAccount("invalid_account_id".to_string())); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); + + // Test creating a request with an invalid source account ID. + let request = ListStrictReceivePaymentPathsRequest::new() + .set_destination_asset(AssetType::Native) + .unwrap() + .set_destination_amount("42".to_string()) + .unwrap() + .set_source(Source::SourceAssets(vec![IssuedOrNative::Native])) + .unwrap() + .set_destination_account("invalid_account_id"); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" ); } - #[test] - fn test_list_strict_send_payment_paths_request() { + #[tokio::test] + async fn test_list_strict_send_payment_paths_request() { use crate::paths::{Asset, PATHS_PATH, PATHS_STRICT_SEND_PATH}; - // Create a base request without optional source assets. This request should be valid on its own, and is used to test several combinations of source assets. - let request_base = ListStrictSendPaymentPathsRequest::new() - .set_destination_asset(AssetType::CreditAlphanum4(Asset { + // Test creating and sending a request with destination assets. Only the response status will be checked, as the request will not yield comparable data. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::CreditAlphanum4(Asset { asset_code: "USDC".to_string(), issuer_account_id: "GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS" .to_string(), })) .unwrap() - .set_destination_amount("42".to_string()) + .set_source_amount("42".to_string()) .unwrap() - .set_destination_account( - "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), - ) + .set_destination(Destination::DestinationAssets(vec![ + IssuedOrNative::Native, + IssuedOrNative::Native, + IssuedOrNative::Issued(Asset { + asset_code: "USDC".to_string(), + issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" + .to_string(), + }), + ])) .unwrap(); - // Test base request. let expected_parameters: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; - assert_eq!(expected_parameters, request_base.get_query_parameters()); + "?source_amount=42&destination_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_asset_type=credit_alphanum4&source_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&source_asset_code=USDC"; + + assert_eq!(request.get_query_parameters(), expected_parameters); let url = "base_url"; assert_eq!( @@ -307,29 +394,35 @@ mod tests { url, PATHS_PATH, PATHS_STRICT_SEND_PATH, - request_base.get_query_parameters() + request.get_query_parameters() ), - request_base.build_url(url) + request.build_url(url) ); - // Test base request with source assets, the first asset being of the `Native` type. - let expected_parameters: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_assets=native%2Cnative%2CUSDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4"; - - let test1 = request_base - .clone() - .set_source_assets(vec![ - SourceAsset(IssuedOrNative::Native), - SourceAsset(IssuedOrNative::Native), - SourceAsset(IssuedOrNative::Issued(Asset { - asset_code: "USDC".to_string(), - issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" - .to_string(), - })), - ]) + let horizon_client = + HorizonClient::new("https://horizon-testnet.stellar.org".to_string()).unwrap(); + + let response = horizon_client + .get_list_strict_send_payment_paths(&request) + .await; + + assert!(response.clone().is_ok()); + + // Test creating and sending a request with destination account. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("100".to_string()) + .unwrap() + .set_destination(Destination::DestinationAccount( + "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4".to_string(), + )) .unwrap(); - assert_eq!(expected_parameters, test1.get_query_parameters()); + let expected_parameters: &str = + "?source_amount=100&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_asset_type=native"; + + assert_eq!(request.get_query_parameters(), expected_parameters); let url = "base_url"; assert_eq!( @@ -338,40 +431,63 @@ mod tests { url, PATHS_PATH, PATHS_STRICT_SEND_PATH, - test1.get_query_parameters() + request.get_query_parameters() ), - test1.build_url(url) + request.build_url(url) ); - // Test base request with source assets, the first asset being of the `Issued` type. - let expected_parameters: &str = - "?destination_asset_type=credit_alphanum4&destination_asset_issuer=GBJJ5OCBXNZWHSJJ4YQ6ECK24MBJSZMLEMINHKGGEWUA5RU2EDMPN6MS&destination_asset_code=USDC&destination_amount=42&destination_account=GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4&source_assets=USDC%3AGBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4%2Cnative%2Cnative"; - - let test1 = request_base - .clone() - .set_source_assets(vec![ - SourceAsset(IssuedOrNative::Issued(Asset { - asset_code: "USDC".to_string(), - issuer_account_id: "GBAKINTNEGR7PO6Z6XW2S5ITT5VARNW6DZ5K4OYSLFNEA2CSMUM2UEF4" - .to_string(), - })), - SourceAsset(IssuedOrNative::Native), - SourceAsset(IssuedOrNative::Native), - ]) - .unwrap(); + let response = horizon_client + .get_list_strict_send_payment_paths(&request) + .await; + + const SOURCE_ASSET_TYPE: &str = "native"; + const SOURCE_AMOUNT: &str = "100.0000000"; + const DESTINATION_ASSET_TYPE: &str = "native"; + const DESTINATION_AMOUNT: &str = "100.0000000"; + + assert!(response.clone().is_ok()); + let binding = response.unwrap(); + let response = &binding.embedded().records()[0]; + assert_eq!(response.source_asset_type(), SOURCE_ASSET_TYPE); + assert_eq!(response.source_amount(), SOURCE_AMOUNT); + assert_eq!(response.destination_asset_type(), DESTINATION_ASSET_TYPE); + assert_eq!(response.destination_amount(), DESTINATION_AMOUNT); + + // Test creating a request with an empty destination assets vector. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAssets(Vec::new())); + assert_eq!(request.err().unwrap(), "DestinationAssets cannot be empty"); - assert_eq!(expected_parameters, test1.get_query_parameters()); + // Test creating a request with an invalid destination asset account ID. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAccount( + "invalid_account_id".to_string(), + )); + assert_eq!( + request.err().unwrap(), + "Public key must be 56 characters long" + ); - let url = "base_url"; + // Test creating a request with an invalid destination account ID. + let request = ListStrictSendPaymentPathsRequest::new() + .set_source_asset(AssetType::Native) + .unwrap() + .set_source_amount("42".to_string()) + .unwrap() + .set_destination(Destination::DestinationAccount( + "invalid_account_id".to_string(), + )); assert_eq!( - format!( - "{}/{}/{}{}", - url, - PATHS_PATH, - PATHS_STRICT_SEND_PATH, - test1.get_query_parameters() - ), - test1.build_url(url) + request.err().unwrap(), + "Public key must be 56 characters long" ); } } diff --git a/stellar_rust_sdk/src/paths/response.rs b/stellar_rust_sdk/src/paths/response.rs index bdcfebf..8ab8d96 100644 --- a/stellar_rust_sdk/src/paths/response.rs +++ b/stellar_rust_sdk/src/paths/response.rs @@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize}; /// #[derive(Debug, Clone, Serialize, Deserialize, Getters)] pub struct PathsResponse { + #[serde(rename = "_embedded")] embedded: Embedded, } From b205fb82d2594c0359ee811bbc83fee80d8b9813 Mon Sep 17 00:00:00 2001 From: Thomas Luijken Date: Tue, 10 Sep 2024 13:14:54 +0200 Subject: [PATCH 4/4] Moved to single test values for all path tests --- stellar_rust_sdk/src/paths/mod.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/stellar_rust_sdk/src/paths/mod.rs b/stellar_rust_sdk/src/paths/mod.rs index 621cc04..93e5939 100644 --- a/stellar_rust_sdk/src/paths/mod.rs +++ b/stellar_rust_sdk/src/paths/mod.rs @@ -136,6 +136,11 @@ mod tests { use super::{AssetType, IssuedOrNative}; use crate::{horizon_client::HorizonClient, models::*}; + const SOURCE_ASSET_TYPE: &str = "native"; + const SOURCE_AMOUNT: &str = "100.0000000"; + const DESTINATION_ASSET_TYPE: &str = "native"; + const DESTINATION_AMOUNT: &str = "100.0000000"; + #[tokio::test] async fn test_find_payment_paths_request() { use crate::paths::{Asset, PATHS_PATH}; @@ -200,10 +205,6 @@ mod tests { let response = horizon_client.get_find_payment_paths(&request).await; - const SOURCE_ASSET_TYPE: &str = "native"; - const SOURCE_AMOUNT: &str = "100.0000000"; - const DESTINATION_ASSET_TYPE: &str = "native"; - const DESTINATION_AMOUNT: &str = "100.0000000"; assert!(response.clone().is_ok()); let binding = response.unwrap(); @@ -308,11 +309,6 @@ mod tests { .get_list_strict_receive_payment_paths(&request) .await; - const SOURCE_ASSET_TYPE: &str = "native"; - const SOURCE_AMOUNT: &str = "100.0000000"; - const DESTINATION_ASSET_TYPE: &str = "native"; - const DESTINATION_AMOUNT: &str = "100.0000000"; - assert!(response.clone().is_ok()); let binding = response.unwrap(); let response = &binding.embedded().records()[0]; @@ -440,11 +436,6 @@ mod tests { .get_list_strict_send_payment_paths(&request) .await; - const SOURCE_ASSET_TYPE: &str = "native"; - const SOURCE_AMOUNT: &str = "100.0000000"; - const DESTINATION_ASSET_TYPE: &str = "native"; - const DESTINATION_AMOUNT: &str = "100.0000000"; - assert!(response.clone().is_ok()); let binding = response.unwrap(); let response = &binding.embedded().records()[0];