Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement 'paths' endpoints #107

Merged
merged 4 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions stellar_rust_sdk/src/horizon_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::{
details_request::{BuyingAsset, DetailsRequest, SellingAsset},
response::DetailsResponse,
},
paths::prelude::*,
payments::prelude::*,
trade_aggregations::prelude::*,
trades::prelude::*,
Expand Down Expand Up @@ -1985,6 +1986,80 @@ impl HorizonClient {
self.get::<AllTransactionsResponse>(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<DestinationAsset, DestinationAmount, SourceAccount>,
) -> Result<PathsResponse, String> {
self.get::<PathsResponse>(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, Source>,
) -> Result<PathsResponse, String> {
self.get::<PathsResponse>(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<SourceAsset, SourceAmount, Destination>,
) -> Result<PathsResponse, String> {
self.get::<PathsResponse>(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.
Expand Down
48 changes: 46 additions & 2 deletions stellar_rust_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,13 +26,13 @@
//! * Operations
//! * Offers
//! * Orderbook
//! * Paths
//! * Payments
//! * Trades
//! * Trade aggregations
//! * Transactions
//!
//! #### Endpoints on the roadmap:
//! * Paths

//!
//! ## Example Usage
Expand Down Expand Up @@ -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<dyn std::error::Error>> {
/// 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
Expand Down
6 changes: 3 additions & 3 deletions stellar_rust_sdk/src/offers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
203 changes: 203 additions & 0 deletions stellar_rust_sdk/src/paths/find_payment_paths_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use crate::models::{is_public_key, 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<String>,
/// Identifies the source account from which the payment path originates.
pub source_account: S,
}

impl FindPaymentsPathRequest<NoDestinationAsset, NoDestinationAmount, NoSourceAccount> {
/// 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<DAs, DAm, S> FindPaymentsPathRequest<DAs, DAm, S> {
/// 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<FindPaymentsPathRequest<DestinationAsset, DAm, S>, 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: impl Into<String>,
) -> Result<FindPaymentsPathRequest<DAs, DestinationAmount, S>, String> {
Ok(FindPaymentsPathRequest {
destination_asset: self.destination_asset,
destination_amount: DestinationAmount(destination_amount.into()),
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: impl Into<String>,
) -> Result<FindPaymentsPathRequest<DAs, DAm, SourceAccount>, 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,
destination_account: self.destination_account,
source_account: SourceAccount(source_account),
})
}
}

impl FindPaymentsPathRequest<DestinationAsset, DestinationAmount, SourceAccount> {
/// 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: impl Into<String>,
) -> Result<FindPaymentsPathRequest<DestinationAsset, DestinationAmount, SourceAccount>, 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,
destination_account: Some(destination_account),
source_account: self.source_account,
})
}
}

impl Request for FindPaymentsPathRequest<DestinationAsset, DestinationAmount, SourceAccount> {
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()
)
}
}
Loading