openzeppelin_relayer/services/stellar_dex/
stellar_dex_service.rs1use super::{
8 AssetType, OrderBookService, StellarDexServiceError, StellarDexServiceTrait,
9 StellarQuoteResponse, SwapExecutionResult, SwapTransactionParams,
10};
11use crate::services::{provider::StellarProviderTrait, signer::Signer, signer::StellarSignTrait};
12use async_trait::async_trait;
13use std::collections::HashSet;
14use std::sync::Arc;
15use tracing::debug;
16
17#[derive(Clone)]
22pub enum DexServiceWrapper<P, S>
23where
24 P: StellarProviderTrait + Send + Sync + 'static,
25 S: StellarSignTrait + Signer + Send + Sync + 'static,
26{
27 OrderBook(Arc<OrderBookService<P, S>>),
29 }
32
33impl<P, S> DexServiceWrapper<P, S>
34where
35 P: StellarProviderTrait + Send + Sync + 'static,
36 S: StellarSignTrait + Signer + Send + Sync + 'static,
37{
38 fn can_handle_asset(&self, asset_id: &str) -> bool {
39 match self {
40 DexServiceWrapper::OrderBook(service) => service.can_handle_asset(asset_id),
41 }
43 }
44
45 fn supported_asset_types(&self) -> HashSet<AssetType> {
46 match self {
47 DexServiceWrapper::OrderBook(service) => service.supported_asset_types(),
48 }
50 }
51}
52
53pub struct StellarDexService<P, S>
61where
62 P: StellarProviderTrait + Send + Sync + 'static,
63 S: StellarSignTrait + Signer + Send + Sync + 'static,
64{
65 strategies: Vec<DexServiceWrapper<P, S>>,
67}
68
69impl<P, S> StellarDexService<P, S>
70where
71 P: StellarProviderTrait + Send + Sync + 'static,
72 S: StellarSignTrait + Signer + Send + Sync + 'static,
73{
74 pub fn new(strategies: Vec<DexServiceWrapper<P, S>>) -> Self {
79 Self { strategies }
80 }
81
82 fn find_strategy_for_asset(&self, asset_id: &str) -> Option<&DexServiceWrapper<P, S>> {
90 for strategy in &self.strategies {
91 if strategy.can_handle_asset(asset_id) {
92 debug!(
93 asset_id = %asset_id,
94 "Selected DEX strategy that can handle asset"
95 );
96 return Some(strategy);
97 }
98 }
99 None
100 }
101}
102
103#[async_trait]
104impl<P, S> StellarDexServiceTrait for StellarDexService<P, S>
105where
106 P: StellarProviderTrait + Send + Sync + 'static,
107 S: StellarSignTrait + Signer + Send + Sync + 'static,
108{
109 fn supported_asset_types(&self) -> HashSet<AssetType> {
110 let mut types = HashSet::new();
112 for strategy in &self.strategies {
113 types.extend(strategy.supported_asset_types());
114 }
115 types
116 }
117
118 fn can_handle_asset(&self, asset_id: &str) -> bool {
119 self.find_strategy_for_asset(asset_id).is_some()
121 }
122
123 async fn get_token_to_xlm_quote(
124 &self,
125 asset_id: &str,
126 amount: u64,
127 slippage: f32,
128 asset_decimals: Option<u8>,
129 ) -> Result<StellarQuoteResponse, StellarDexServiceError> {
130 let strategy = self.find_strategy_for_asset(asset_id).ok_or_else(|| {
131 StellarDexServiceError::InvalidAssetIdentifier(format!(
132 "No configured strategy can handle asset: {asset_id}"
133 ))
134 })?;
135
136 match strategy {
137 DexServiceWrapper::OrderBook(svc) => {
138 svc.get_token_to_xlm_quote(asset_id, amount, slippage, asset_decimals)
139 .await
140 } }
145 }
146
147 async fn get_xlm_to_token_quote(
148 &self,
149 asset_id: &str,
150 amount: u64,
151 slippage: f32,
152 asset_decimals: Option<u8>,
153 ) -> Result<StellarQuoteResponse, StellarDexServiceError> {
154 let strategy = self.find_strategy_for_asset(asset_id).ok_or_else(|| {
155 StellarDexServiceError::InvalidAssetIdentifier(format!(
156 "No configured strategy can handle asset: {asset_id}"
157 ))
158 })?;
159
160 match strategy {
161 DexServiceWrapper::OrderBook(svc) => {
162 svc.get_xlm_to_token_quote(asset_id, amount, slippage, asset_decimals)
163 .await
164 } }
169 }
170
171 async fn prepare_swap_transaction(
172 &self,
173 params: SwapTransactionParams,
174 ) -> Result<(String, StellarQuoteResponse), StellarDexServiceError> {
175 let strategy = self
176 .find_strategy_for_asset(¶ms.source_asset)
177 .ok_or_else(|| {
178 StellarDexServiceError::InvalidAssetIdentifier(format!(
179 "No configured strategy can handle asset: {}",
180 params.source_asset
181 ))
182 })?;
183
184 match strategy {
185 DexServiceWrapper::OrderBook(svc) => svc.prepare_swap_transaction(params).await,
186 }
188 }
189
190 async fn execute_swap(
191 &self,
192 params: SwapTransactionParams,
193 ) -> Result<SwapExecutionResult, StellarDexServiceError> {
194 let strategy = self
195 .find_strategy_for_asset(¶ms.source_asset)
196 .ok_or_else(|| {
197 StellarDexServiceError::InvalidAssetIdentifier(format!(
198 "No configured strategy can handle asset: {}",
199 params.source_asset
200 ))
201 })?;
202
203 match strategy {
204 DexServiceWrapper::OrderBook(svc) => svc.execute_swap(params).await,
205 }
207 }
208}