jupiter_swap_api_client/
lib.rs

1use std::collections::HashMap;
2
3use quote::{InternalQuoteRequest, QuoteRequest, QuoteResponse};
4use reqwest::{Client, Response};
5use serde::de::DeserializeOwned;
6use swap::{SwapInstructionsResponse, SwapInstructionsResponseInternal, SwapRequest, SwapResponse};
7use thiserror::Error;
8
9pub mod quote;
10pub mod route_plan_with_metadata;
11pub mod serde_helpers;
12pub mod swap;
13pub mod transaction_config;
14
15#[derive(Clone)]
16pub struct JupiterSwapApiClient {
17    pub base_path: String,
18}
19
20#[derive(Debug, Error)]
21pub enum ClientError {
22    #[error("Request failed with status {status}: {body}")]
23    RequestFailed {
24        status: reqwest::StatusCode,
25        body: String,
26    },
27    #[error("Failed to deserialize response: {0}")]
28    DeserializationError(#[from] reqwest::Error),
29}
30
31async fn check_is_success(response: Response) -> Result<Response, ClientError> {
32    if !response.status().is_success() {
33        let status = response.status();
34        let body = response.text().await.unwrap_or_default();
35        return Err(ClientError::RequestFailed { status, body });
36    }
37    Ok(response)
38}
39
40async fn check_status_code_and_deserialize<T: DeserializeOwned>(
41    response: Response,
42) -> Result<T, ClientError> {
43    let response = check_is_success(response).await?;
44    response
45        .json::<T>()
46        .await
47        .map_err(ClientError::DeserializationError)
48}
49
50impl JupiterSwapApiClient {
51    pub fn new(base_path: String) -> Self {
52        Self { base_path }
53    }
54
55    pub async fn quote(&self, quote_request: &QuoteRequest) -> Result<QuoteResponse, ClientError> {
56        let url = format!("{}/quote", self.base_path);
57        let extra_args = quote_request.quote_args.clone();
58        let internal_quote_request = InternalQuoteRequest::from(quote_request.clone());
59        let response = Client::new()
60            .get(url)
61            .query(&internal_quote_request)
62            .query(&extra_args)
63            .send()
64            .await?;
65        check_status_code_and_deserialize(response).await
66    }
67
68    pub async fn swap(
69        &self,
70        swap_request: &SwapRequest,
71        extra_args: Option<HashMap<String, String>>,
72    ) -> Result<SwapResponse, ClientError> {
73        let response = Client::new()
74            .post(format!("{}/swap", self.base_path))
75            .query(&extra_args)
76            .json(swap_request)
77            .send()
78            .await?;
79        check_status_code_and_deserialize(response).await
80    }
81
82    pub async fn swap_instructions(
83        &self,
84        swap_request: &SwapRequest,
85    ) -> Result<SwapInstructionsResponse, ClientError> {
86        let response = Client::new()
87            .post(format!("{}/swap-instructions", self.base_path))
88            .json(swap_request)
89            .send()
90            .await?;
91        check_status_code_and_deserialize::<SwapInstructionsResponseInternal>(response)
92            .await
93            .map(Into::into)
94    }
95}