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

Order book #47

Merged
merged 7 commits into from
May 3, 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
53 changes: 53 additions & 0 deletions stellar_rust_sdk/src/horizon_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ use crate::{
response::Operation,
single_operation_request::{OperationId, SingleOperationRequest},
},
order_book::{
details_request::{BuyingAsset, DetailsRequest, SellingAsset},
response::DetailsResponse,
},
};
use reqwest;
use url::Url;
Expand Down Expand Up @@ -943,6 +947,55 @@ impl HorizonClient {
self.get::<OperationResponse>(request).await
}

/// Retrieves a list of order book details from the Horizon server.
///
/// This asynchronous method fetches a list of order book details from the Horizon server.
/// It requires a [`DetailsRequest`] to specify the parameters for the order book details request.
///
/// # Arguments
/// * `request` - A reference to a [`DetailsRequest`] instance, containing the parameters for the order book details request.
///
/// # Returns
///
/// On successful execution, returns a `Result` containing a [`DetailsResponse`], which includes the list of order book details obtained from the Horizon server.
/// If the request fails, it returns an error within `Result`.
///
/// # Usage
/// To use this method, create an instance of [`DetailsRequest`] and set any desired filters or parameters.
///
/// ```
/// # use stellar_rs::order_book::prelude::*;
/// # use stellar_rs::models::Request;
/// # use stellar_rs::horizon_client::HorizonClient;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// # let base_url = "https://horizon-testnet.stellar.org".to_string();
/// # let horizon_client = HorizonClient::new(base_url)
/// # .expect("Failed to create Horizon Client");
/// # let details_request = DetailsRequest::new()
/// # .set_buying_asset(AssetType::Native)
/// # .unwrap()
/// # .set_selling_asset(AssetType::Alphanumeric4(Asset {
/// # asset_code: "USDC".to_string(),
/// # asset_issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"
/// # .to_string(),
/// # }))
/// # .unwrap();
///
/// let response = horizon_client.get_order_book_details(&details_request).await;
///
/// assert!(response.is_ok());
/// # Ok({})
/// # }
/// ```
///
pub async fn get_order_book_details(
&self,
request: &DetailsRequest<SellingAsset, BuyingAsset>,
) -> Result<DetailsResponse, String> {
self.get::<DetailsResponse>(request).await
}

/// Retrieves a list of all operations for a specific liquidity pool from the Horizon server.
///
/// This asynchronous method fetches a list of all operations for a specific liquidity pool from the Horizon server.
Expand Down
41 changes: 41 additions & 0 deletions stellar_rust_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,47 @@ pub mod liquidity_pools;
///
pub mod operations;

/// Provides `Request` and `Response` structs for retrieving order book details.
///
/// The `order_book` module in the Stellar Horizon SDK includes structures and methods that facilitate
/// querying order book data from the Horizon server.
///
/// # Usage
///
/// This module is used to construct requests for order book-related data and to parse the responses
/// received from the Horizon server. It includes request and response structures for querying
/// order book details.
///
/// # Example
///
/// To use this module, you can create an instance of a request struct, such as `DetailsRequest`,
/// 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 `DetailsResponse`.
///
/// ```rust
/// use stellar_rs::horizon_client::HorizonClient;
/// use stellar_rs::order_book::prelude::*;
/// 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 order book details
/// let details_request = DetailsRequest::new()
/// .set_buying_asset(AssetType::Native)?
/// .set_selling_asset(AssetType::Alphanumeric4(Asset {
/// asset_code: "USDC".to_string(),
/// asset_issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5".to_string(),
/// }))?;
/// let details_response = horizon_client.get_order_book_details(&details_request).await?;
///
/// // Process the response...
/// # Ok(())
/// # }
/// ```
///
pub mod order_book;

/// Contains core data structures and traits.
///
/// This module is used by the Stellar Rust SDK to interact with the Horizon API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,6 @@ impl Request for AllLiquidityPoolsRequest {
}

fn build_url(&self, base_url: &str) -> String {
println!(
"{}/{}?{}",
base_url,
super::LIQUIDITY_POOLS_PATH,
self.get_query_parameters()
);

format!(
"{}/{}?{}",
base_url,
Expand Down
1 change: 0 additions & 1 deletion stellar_rust_sdk/src/operations/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ pub struct OperationLinks {

impl Response for OperationResponse {
fn from_json(json: String) -> Result<Self, String> {
println!("json: {}", json);

let operation_record = serde_json::from_str(&json).map_err(|e| e.to_string())?;

Expand Down
189 changes: 189 additions & 0 deletions stellar_rust_sdk/src/order_book/details_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use crate::{models::Request};

pub struct SellingAsset(AssetType);
pub struct NoSellingAsset;
pub struct BuyingAsset(AssetType);
pub struct NoBuyingAsset;

#[derive(PartialEq, Debug)]
pub struct Asset {
pub asset_code: String,
pub asset_issuer: String,
}

/// Represents the asset type of an asset.
#[derive(PartialEq, Debug)]
pub enum AssetType {
/// A native asset_type type. It holds no Value
Native,
/// An alphanumeric 4 asset_type type. It holds a Asset struct with asset code and asset issuer.
Alphanumeric4(Asset),
/// An alphanumeric 12 asset_type type. It holds a Asset struct with asset code and asset issuer.
Alphanumeric12(Asset),
}

/// Represents the request for the details of an order book.
#[derive(PartialEq, Debug)]
pub struct DetailsRequest<S, B> {
/// The selling asset of the order book.
pub selling_asset: S,
/// The buying asset of the order book.
pub buying_asset: B,
}

/// Represents the selling asset of the order book with no buying asset or selling asset
impl DetailsRequest<NoSellingAsset, NoBuyingAsset> {
pub fn new() -> Self {
DetailsRequest {
selling_asset: NoSellingAsset,
buying_asset: NoBuyingAsset,
}
}

/// Sets the selling asset of the order book.
///
/// # Arguments
/// * `selling_asset` - An [`AssetType`] enum value specifying the selling asset.
///
pub fn set_selling_asset(
self,
selling_asset: AssetType,
) -> Result<DetailsRequest<SellingAsset, NoBuyingAsset>, String> {
Ok(DetailsRequest {
selling_asset: SellingAsset(selling_asset),
buying_asset: NoBuyingAsset,
})
}

/// Sets the buying asset of the order book.
///
/// # Arguments
/// * `buying_asset` - An [`AssetType`] enum value specifying the buying asset.
pub fn set_buying_asset(
self,
buying_asset: AssetType,
) -> Result<DetailsRequest<NoSellingAsset, BuyingAsset>, String> {
Ok(DetailsRequest {
selling_asset: NoSellingAsset,
buying_asset: BuyingAsset(buying_asset),
})
}
}

/// Implements the setting of a selling asset of the order book with a buying asset and no selling asset
impl DetailsRequest<NoSellingAsset, BuyingAsset> {

/// Sets the selling asset of the order book.
///
/// # Arguments
/// * `selling_asset` - An [`AssetType`] enum value specifying the selling asset.
pub fn set_selling_asset(
self,
selling_asset: AssetType,
) -> Result<DetailsRequest<SellingAsset, BuyingAsset>, String> {
Ok(DetailsRequest {
selling_asset: SellingAsset(selling_asset),
buying_asset: self.buying_asset,
})
}
}

/// Implements the setting of a buying asset of the order book with a selling asset and no buying asset
impl DetailsRequest<SellingAsset, NoBuyingAsset> {

/// Sets the buying asset of the order book.
///
/// # Arguments
/// * `buying_asset` - An [`AssetType`] enum value specifying the buying asset.
pub fn set_buying_asset(
self,
buying_asset: AssetType,
) -> Result<DetailsRequest<SellingAsset, BuyingAsset>, String> {
Ok(DetailsRequest {
selling_asset: self.selling_asset,
buying_asset: BuyingAsset(buying_asset),
})
}
}

impl Request for DetailsRequest<SellingAsset, BuyingAsset> {
fn get_query_parameters(&self) -> String {
let mut query: Vec<String> = Vec::new();

match &self.selling_asset.0 {
AssetType::Native => {
query.push(format!("selling_asset_type=native"));
}
AssetType::Alphanumeric4(asset) => {
query.push(format!("selling_asset_type=credit_alphanum4"));
query.push(format!("&selling_asset_code={}", asset.asset_code));
query.push(format!("&selling_asset_issuer={}", asset.asset_issuer));
}
AssetType::Alphanumeric12(asset) => {
query.push(format!("selling_asset_type=credit_alphanum12"));
query.push(format!("&selling_asset_code={}", asset.asset_code));
query.push(format!("&selling_asset_issuer={}", asset.asset_issuer));
}
}

match &self.buying_asset.0 {
AssetType::Native => {
query.push(format!("&buying_asset_type=native"));
}
AssetType::Alphanumeric4(asset) => {
query.push(format!("&buying_asset_type=credit_alphanum4"));
query.push(format!("&buying_asset_code={}", asset.asset_code));
query.push(format!("&buying_asset_issuer={}", asset.asset_issuer));
}
AssetType::Alphanumeric12(asset) => {
query.push(format!("&buying_asset_type=credit_alphanum12"));
query.push(format!("&buying_asset_code={}", asset.asset_code));
query.push(format!("&buying_asset_issuer={}", asset.asset_issuer));
}
}

query.join("")
}

fn build_url(&self, base_url: &str) -> String {
format!(
"{}/{}?{}",
base_url,
super::ORDER_BOOK_PATH,
self.get_query_parameters()
)
}
}

mod tests {
use crate::models::Request;
use crate::order_book::prelude::{Asset, AssetType, DetailsRequest};

#[test]
fn test_details_request() {
let details_request = DetailsRequest::new()
.set_buying_asset(AssetType::Native)
.unwrap()
.set_selling_asset(AssetType::Native)
.unwrap();

assert_eq!(
details_request.get_query_parameters(),
"selling_asset_type=native&buying_asset_type=native"
);

let details_request = DetailsRequest::new()
.set_buying_asset(AssetType::Native)
.unwrap()
.set_selling_asset(AssetType::Alphanumeric4(Asset {
asset_code: "USDC".to_string(),
asset_issuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5".to_string(),
}))
.unwrap();

assert_eq!(
details_request.get_query_parameters(),
"selling_asset_type=credit_alphanum4&selling_asset_code=USDC&selling_asset_issuer=GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5&buying_asset_type=native"
);
}
}
Loading