openzeppelin_relayer/domain/relayer/solana/dex/
mod.rs1use std::sync::Arc;
4
5use crate::domain::relayer::RelayerError;
6use crate::models::{RelayerRepoModel, SolanaSwapStrategy};
7use crate::services::{
8 provider::{SolanaProvider, SolanaProviderTrait},
9 signer::{SolanaSignTrait, SolanaSigner},
10 JupiterService, JupiterServiceTrait,
11};
12use async_trait::async_trait;
13#[cfg(test)]
14use mockall::automock;
15use serde::{Deserialize, Serialize};
16#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
18pub struct SwapResult {
19 pub mint: String,
20 pub source_amount: u64,
21 pub destination_amount: u64,
22 pub transaction_signature: String,
23 pub error: Option<String>,
24}
25
26impl Default for SwapResult {
27 fn default() -> Self {
28 Self {
29 mint: "".into(),
30 source_amount: 0,
31 destination_amount: 0,
32 transaction_signature: "".into(),
33 error: None,
34 }
35 }
36}
37
38#[derive(Debug)]
40pub struct SwapParams {
41 pub owner_address: String,
42 pub source_mint: String,
43 pub destination_mint: String,
44 pub amount: u64,
45 pub slippage_percent: f64,
46}
47
48#[async_trait]
50#[cfg_attr(test, automock)]
51pub trait DexStrategy: Send + Sync {
52 async fn execute_swap(&self, params: SwapParams) -> Result<SwapResult, RelayerError>;
54}
55
56pub mod jupiter_swap;
58pub mod jupiter_ultra;
59
60pub enum NetworkDex<P, S, J>
61where
62 P: SolanaProviderTrait + 'static,
63 S: SolanaSignTrait + Send + Sync + 'static,
64 J: JupiterServiceTrait + Send + Sync + 'static,
65{
66 JupiterSwap {
67 dex: jupiter_swap::JupiterSwapDex<P, S, J>,
68 },
69 JupiterUltra {
70 dex: jupiter_ultra::JupiterUltraDex<S, J>,
71 },
72 Noop {
73 dex: NoopDex,
74 },
75}
76
77pub type DefaultNetworkDex = NetworkDex<SolanaProvider, SolanaSigner, JupiterService>;
78
79#[async_trait]
80impl<P, S, J> DexStrategy for NetworkDex<P, S, J>
81where
82 P: SolanaProviderTrait + Send + Sync + 'static,
83 S: SolanaSignTrait + Send + Sync + 'static,
84 J: JupiterServiceTrait + Send + Sync + 'static,
85{
86 async fn execute_swap(&self, params: SwapParams) -> Result<SwapResult, RelayerError> {
87 match self {
88 NetworkDex::JupiterSwap { dex } => dex.execute_swap(params).await,
89 NetworkDex::JupiterUltra { dex } => dex.execute_swap(params).await,
90 NetworkDex::Noop { dex } => dex.execute_swap(params).await,
91 }
92 }
93}
94
95fn resolve_strategy(relayer: &RelayerRepoModel) -> SolanaSwapStrategy {
96 relayer
97 .policies
98 .get_solana_policy()
99 .get_swap_config()
100 .and_then(|cfg| cfg.strategy)
101 .unwrap_or(SolanaSwapStrategy::Noop) }
103
104pub struct NoopDex;
105#[async_trait]
106impl DexStrategy for NoopDex {
107 async fn execute_swap(&self, _params: SwapParams) -> Result<SwapResult, RelayerError> {
108 Ok(SwapResult::default())
109 }
110}
111
112pub fn create_network_dex_generic<P, S, J>(
114 relayer: &RelayerRepoModel,
115 provider: Arc<P>,
116 signer_service: Arc<S>,
117 jupiter_service: Arc<J>,
118) -> Result<NetworkDex<P, S, J>, RelayerError>
119where
120 P: SolanaProviderTrait + Send + Sync + 'static,
121 S: SolanaSignTrait + Send + Sync + 'static,
122 J: JupiterServiceTrait + Send + Sync + 'static,
123{
124 let jupiter_swap_options = relayer
125 .policies
126 .get_solana_policy()
127 .get_swap_config()
128 .and_then(|cfg| cfg.jupiter_swap_options.clone());
129
130 match resolve_strategy(relayer) {
131 SolanaSwapStrategy::JupiterSwap => Ok(NetworkDex::JupiterSwap {
132 dex: jupiter_swap::JupiterSwapDex::<P, S, J>::new(
133 provider,
134 signer_service,
135 jupiter_service,
136 jupiter_swap_options,
137 ),
138 }),
139 SolanaSwapStrategy::JupiterUltra => Ok(NetworkDex::JupiterUltra {
140 dex: jupiter_ultra::JupiterUltraDex::<S, J>::new(signer_service, jupiter_service),
141 }),
142 _ => Ok(NetworkDex::Noop { dex: NoopDex }),
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use secrets::SecretVec;
149
150 use crate::{
151 models::{
152 LocalSignerConfigStorage, RelayerSolanaPolicy, RelayerSolanaSwapConfig,
153 SignerConfigStorage, SignerRepoModel,
154 },
155 services::{provider::MockSolanaProviderTrait, signer::SolanaSignerFactory},
156 };
157
158 use super::*;
159
160 fn create_test_signer_model() -> SignerRepoModel {
161 let seed = vec![1u8; 32];
162 let raw_key = SecretVec::new(32, |v| v.copy_from_slice(&seed));
163 SignerRepoModel {
164 id: "test".to_string(),
165 config: SignerConfigStorage::Local(LocalSignerConfigStorage { raw_key }),
166 }
167 }
168
169 #[test]
170 fn test_create_network_dex_jupiter_swap_explicit() {
171 let mut relayer = RelayerRepoModel::default();
172 let policy = crate::models::RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
173 swap_config: Some(RelayerSolanaSwapConfig {
174 strategy: Some(SolanaSwapStrategy::JupiterSwap),
175 cron_schedule: None,
176 min_balance_threshold: None,
177 jupiter_swap_options: None,
178 }),
179 ..Default::default()
180 });
181
182 relayer.policies = policy;
183
184 let provider = Arc::new(MockSolanaProviderTrait::new());
185
186 let signer_service = Arc::new(
187 SolanaSignerFactory::create_solana_signer(&create_test_signer_model().into()).unwrap(),
188 );
189 let jupiter_service = Arc::new(JupiterService::new_from_network(relayer.network.as_str()));
190
191 let result =
192 create_network_dex_generic(&relayer, provider, signer_service, jupiter_service);
193
194 match result {
195 Ok(NetworkDex::JupiterSwap { .. }) => {}
196 Ok(_) => panic!("Expected JupiterSwap strategy"),
197 Err(e) => panic!("Expected Ok with JupiterSwap, but got error: {:?}", e),
198 }
199 }
200
201 #[test]
202 fn test_create_network_dex_jupiter_ultra_explicit() {
203 let mut relayer = RelayerRepoModel::default();
204 let policy = crate::models::RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
205 swap_config: Some(RelayerSolanaSwapConfig {
206 strategy: Some(SolanaSwapStrategy::JupiterUltra),
207 cron_schedule: None,
208 min_balance_threshold: None,
209 jupiter_swap_options: None,
210 }),
211 ..Default::default()
212 });
213
214 relayer.policies = policy;
215
216 let provider = Arc::new(MockSolanaProviderTrait::new());
217
218 let signer_service = Arc::new(
219 SolanaSignerFactory::create_solana_signer(&create_test_signer_model().into()).unwrap(),
220 );
221 let jupiter_service = Arc::new(JupiterService::new_from_network(relayer.network.as_str()));
222
223 let result =
224 create_network_dex_generic(&relayer, provider, signer_service, jupiter_service);
225
226 match result {
227 Ok(NetworkDex::JupiterUltra { .. }) => {}
228 Ok(_) => panic!("Expected JupiterUltra strategy"),
229 Err(e) => panic!("Expected Ok with JupiterUltra, but got error: {:?}", e),
230 }
231 }
232
233 #[test]
234 fn test_create_network_dex_default_when_no_strategy() {
235 let mut relayer = RelayerRepoModel::default();
236 let policy = crate::models::RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
237 swap_config: Some(RelayerSolanaSwapConfig {
238 strategy: None,
239 cron_schedule: None,
240 min_balance_threshold: None,
241 jupiter_swap_options: None,
242 }),
243 ..Default::default()
244 });
245
246 relayer.policies = policy;
247
248 let provider = Arc::new(MockSolanaProviderTrait::new());
249
250 let signer_service = Arc::new(
251 SolanaSignerFactory::create_solana_signer(&create_test_signer_model().into()).unwrap(),
252 );
253 let jupiter_service = Arc::new(JupiterService::new_from_network(relayer.network.as_str()));
254
255 let result =
256 create_network_dex_generic(&relayer, provider, signer_service, jupiter_service);
257
258 match result {
259 Ok(NetworkDex::Noop { .. }) => {}
260 Ok(_) => panic!("Expected Noop strategy"),
261 Err(e) => panic!("Expected Ok with Noop, but got error: {:?}", e),
262 }
263 }
264
265 #[test]
266 fn test_create_network_dex_default_when_no_swap_config() {
267 let mut relayer = RelayerRepoModel::default();
268 let policy = crate::models::RelayerNetworkPolicy::Solana(RelayerSolanaPolicy {
269 swap_config: None,
270 ..Default::default()
271 });
272
273 relayer.policies = policy;
274
275 let provider = Arc::new(MockSolanaProviderTrait::new());
276
277 let signer_service = Arc::new(
278 SolanaSignerFactory::create_solana_signer(&create_test_signer_model().into()).unwrap(),
279 );
280 let jupiter_service = Arc::new(JupiterService::new_from_network(relayer.network.as_str()));
281
282 let result =
283 create_network_dex_generic(&relayer, provider, signer_service, jupiter_service);
284
285 match result {
286 Ok(NetworkDex::Noop { .. }) => {}
287 Ok(_) => panic!("Expected Noop strategy"),
288 Err(e) => panic!("Expected Ok with Noop, but got error: {:?}", e),
289 }
290 }
291}