use alloy_network::TransactionBuilder;
use alloy_primitives::{hex, Address};
use alloy_rpc_types::TransactionRequest;
use reqwest::Response;
use serde_json::Value;
use tracing::instrument;
use crate::{
client::parse_error_response, parse_value, AssembleRequest, AssemblyRequest, AssemblyResponse,
ClientConfig, OdosError, OdosHttpClient, Result, RetryConfig, SwapBuilder,
};
use super::TransactionData;
use crate::{QuoteRequest, SingleQuoteResponse};
#[derive(Debug, Clone)]
pub struct OdosClient {
client: OdosHttpClient,
}
impl OdosClient {
pub fn new() -> Result<Self> {
Ok(Self {
client: OdosHttpClient::new()?,
})
}
pub fn with_config(config: ClientConfig) -> Result<Self> {
Ok(Self {
client: OdosHttpClient::with_config(config)?,
})
}
pub fn with_retry_config(retry_config: RetryConfig) -> Result<Self> {
let config = ClientConfig {
retry_config,
..Default::default()
};
Self::with_config(config)
}
pub fn config(&self) -> &ClientConfig {
self.client.config()
}
pub fn swap(&self) -> SwapBuilder<'_> {
SwapBuilder::new(self)
}
#[instrument(skip(self), level = "debug")]
pub async fn quote(&self, quote_request: &QuoteRequest) -> Result<SingleQuoteResponse> {
let response = self
.client
.execute_with_retry(|| {
let mut builder = self
.client
.inner()
.post(self.client.config().endpoint.quote_url())
.header("accept", "application/json")
.json(quote_request);
if let Some(ref api_key) = self.client.config().api_key {
builder = builder.header("X-API-Key", api_key.as_str());
}
builder
})
.await?;
if response.status().is_success() {
let single_quote_response = response.json().await?;
Ok(single_quote_response)
} else {
let status = response.status();
let parsed = parse_error_response(response).await;
Err(OdosError::api_error_with_code(
status,
parsed.message,
parsed.code,
parsed.trace_id,
))
}
}
#[deprecated(since = "0.25.0", note = "Use `quote` instead")]
pub async fn get_swap_quote(
&self,
quote_request: &QuoteRequest,
) -> Result<SingleQuoteResponse> {
self.quote(quote_request).await
}
#[instrument(skip(self), level = "debug")]
pub async fn get_assemble_response(
&self,
assemble_request: AssembleRequest,
) -> Result<Response> {
self.client
.execute_with_retry(|| {
let mut builder = self
.client
.inner()
.post(self.client.config().endpoint.assemble_url())
.header("Content-Type", "application/json")
.json(&assemble_request);
if let Some(ref api_key) = self.client.config().api_key {
builder = builder.header("X-API-Key", api_key.as_str());
}
builder
})
.await
}
#[instrument(skip(self), level = "debug")]
pub async fn assemble_tx_data(
&self,
signer_address: Address,
output_recipient: Address,
path_id: &str,
) -> Result<TransactionData> {
let assemble_request = AssembleRequest {
user_addr: signer_address,
path_id: path_id.to_string(),
simulate: false,
receiver: Some(output_recipient),
};
let response = self.get_assemble_response(assemble_request).await?;
if !response.status().is_success() {
let status = response.status();
let parsed = parse_error_response(response).await;
return Err(OdosError::api_error_with_code(
status,
parsed.message,
parsed.code,
parsed.trace_id,
));
}
let value: Value = response.json().await?;
let AssemblyResponse { transaction, .. } = serde_json::from_value(value)?;
Ok(transaction)
}
#[instrument(skip(self), level = "debug")]
pub async fn assemble(&self, request: &AssemblyRequest) -> Result<TransactionRequest> {
let TransactionData { data, value, .. } = self
.assemble_tx_data(
request.signer_address(),
request.output_recipient(),
request.path_id(),
)
.await?;
Ok(TransactionRequest::default()
.with_input(hex::decode(&data)?)
.with_value(parse_value(&value)?)
.with_to(request.router_address())
.with_from(request.signer_address()))
}
#[deprecated(since = "0.25.0", note = "Use `assemble` instead")]
pub async fn build_base_transaction(
&self,
swap: &AssemblyRequest,
) -> Result<TransactionRequest> {
self.assemble(swap).await
}
}
impl Default for OdosClient {
fn default() -> Self {
Self::new().expect("Failed to create default OdosClient")
}
}
#[deprecated(since = "0.25.0", note = "Use `OdosClient` instead")]
pub type OdosSor = OdosClient;