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