1use actix_web::web::ThinData;
12use serde::{Deserialize, Serialize};
13use std::sync::Arc;
14use utoipa::ToSchema;
15
16#[cfg(test)]
17use mockall::automock;
18
19use crate::{
20 jobs::JobProducerTrait,
21 models::{
22 AppState, DecoratedSignature, DeletePendingTransactionsResponse, EvmNetwork,
23 EvmTransactionDataSignature, JsonRpcRequest, JsonRpcResponse, NetworkRpcRequest,
24 NetworkRpcResult, NetworkTransactionRequest, NetworkType, RelayerError, RelayerRepoModel,
25 RelayerStatus, SignerRepoModel, StellarNetwork, TransactionError, TransactionRepoModel,
26 },
27 services::{get_network_provider, EvmSignerFactory, TransactionCounterService},
28};
29
30use async_trait::async_trait;
31use eyre::Result;
32
33mod evm;
34mod solana;
35mod stellar;
36mod util;
37
38pub use evm::*;
39pub use solana::*;
40pub use stellar::*;
41pub use util::*;
42
43#[async_trait]
47#[allow(dead_code)]
48pub trait Relayer {
49 async fn process_transaction_request(
60 &self,
61 tx_request: NetworkTransactionRequest,
62 ) -> Result<TransactionRepoModel, RelayerError>;
63
64 async fn get_balance(&self) -> Result<BalanceResponse, RelayerError>;
71
72 async fn delete_pending_transactions(
79 &self,
80 ) -> Result<DeletePendingTransactionsResponse, RelayerError>;
81
82 async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError>;
93
94 async fn sign_typed_data(
105 &self,
106 request: SignTypedDataRequest,
107 ) -> Result<SignDataResponse, RelayerError>;
108
109 async fn rpc(
120 &self,
121 request: JsonRpcRequest<NetworkRpcRequest>,
122 ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError>;
123
124 async fn get_status(&self) -> Result<RelayerStatus, RelayerError>;
131
132 async fn initialize_relayer(&self) -> Result<(), RelayerError>;
138
139 async fn validate_min_balance(&self) -> Result<(), RelayerError>;
145}
146
147#[async_trait]
150#[allow(dead_code)]
151#[cfg_attr(test, automock)]
152pub trait SolanaRelayerDexTrait {
153 async fn handle_token_swap_request(
155 &self,
156 relayer_id: String,
157 ) -> Result<Vec<SwapResult>, RelayerError>;
158}
159
160#[async_trait]
163#[allow(dead_code)]
164#[cfg_attr(test, automock)]
165pub trait SolanaRelayerTrait {
166 async fn get_balance(&self) -> Result<BalanceResponse, RelayerError>;
173
174 async fn rpc(
185 &self,
186 request: JsonRpcRequest<NetworkRpcRequest>,
187 ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError>;
188
189 async fn initialize_relayer(&self) -> Result<(), RelayerError>;
195
196 async fn validate_min_balance(&self) -> Result<(), RelayerError>;
202}
203
204pub enum NetworkRelayer<J: JobProducerTrait + 'static> {
205 Evm(DefaultEvmRelayer<J>),
206 Solana(DefaultSolanaRelayer<J>),
207 Stellar(DefaultStellarRelayer<J>),
208}
209
210#[async_trait]
211impl<J: JobProducerTrait + 'static> Relayer for NetworkRelayer<J> {
212 async fn process_transaction_request(
213 &self,
214 tx_request: NetworkTransactionRequest,
215 ) -> Result<TransactionRepoModel, RelayerError> {
216 match self {
217 NetworkRelayer::Evm(relayer) => relayer.process_transaction_request(tx_request).await,
218 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
219 NetworkRelayer::Stellar(relayer) => {
220 relayer.process_transaction_request(tx_request).await
221 }
222 }
223 }
224
225 async fn get_balance(&self) -> Result<BalanceResponse, RelayerError> {
226 match self {
227 NetworkRelayer::Evm(relayer) => relayer.get_balance().await,
228 NetworkRelayer::Solana(relayer) => relayer.get_balance().await,
229 NetworkRelayer::Stellar(relayer) => relayer.get_balance().await,
230 }
231 }
232
233 async fn delete_pending_transactions(
234 &self,
235 ) -> Result<DeletePendingTransactionsResponse, RelayerError> {
236 match self {
237 NetworkRelayer::Evm(relayer) => relayer.delete_pending_transactions().await,
238 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
239 NetworkRelayer::Stellar(relayer) => relayer.delete_pending_transactions().await,
240 }
241 }
242
243 async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError> {
244 match self {
245 NetworkRelayer::Evm(relayer) => relayer.sign_data(request).await,
246 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
247 NetworkRelayer::Stellar(relayer) => relayer.sign_data(request).await,
248 }
249 }
250
251 async fn sign_typed_data(
252 &self,
253 request: SignTypedDataRequest,
254 ) -> Result<SignDataResponse, RelayerError> {
255 match self {
256 NetworkRelayer::Evm(relayer) => relayer.sign_typed_data(request).await,
257 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
258 NetworkRelayer::Stellar(relayer) => relayer.sign_typed_data(request).await,
259 }
260 }
261
262 async fn rpc(
263 &self,
264 request: JsonRpcRequest<NetworkRpcRequest>,
265 ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError> {
266 match self {
267 NetworkRelayer::Evm(relayer) => relayer.rpc(request).await,
268 NetworkRelayer::Solana(relayer) => relayer.rpc(request).await,
269 NetworkRelayer::Stellar(relayer) => relayer.rpc(request).await,
270 }
271 }
272
273 async fn get_status(&self) -> Result<RelayerStatus, RelayerError> {
274 match self {
275 NetworkRelayer::Evm(relayer) => relayer.get_status().await,
276 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
277 NetworkRelayer::Stellar(relayer) => relayer.get_status().await,
278 }
279 }
280
281 async fn validate_min_balance(&self) -> Result<(), RelayerError> {
282 match self {
283 NetworkRelayer::Evm(relayer) => relayer.validate_min_balance().await,
284 NetworkRelayer::Solana(relayer) => relayer.validate_min_balance().await,
285 NetworkRelayer::Stellar(relayer) => relayer.validate_min_balance().await,
286 }
287 }
288
289 async fn initialize_relayer(&self) -> Result<(), RelayerError> {
290 match self {
291 NetworkRelayer::Evm(relayer) => relayer.initialize_relayer().await,
292 NetworkRelayer::Solana(relayer) => relayer.initialize_relayer().await,
293 NetworkRelayer::Stellar(relayer) => relayer.initialize_relayer().await,
294 }
295 }
296}
297
298#[async_trait]
299pub trait RelayerFactoryTrait<J: JobProducerTrait + 'static> {
300 async fn create_relayer(
301 relayer: RelayerRepoModel,
302 signer: SignerRepoModel,
303 state: &ThinData<AppState<J>>,
304 ) -> Result<NetworkRelayer<J>, RelayerError>;
305}
306
307pub struct RelayerFactory;
308
309#[async_trait]
310impl<J: JobProducerTrait + 'static> RelayerFactoryTrait<J> for RelayerFactory {
311 async fn create_relayer(
312 relayer: RelayerRepoModel,
313 signer: SignerRepoModel,
314 state: &ThinData<AppState<J>>,
315 ) -> Result<NetworkRelayer<J>, RelayerError> {
316 match relayer.network_type {
317 NetworkType::Evm => {
318 let network_repo = state
319 .network_repository()
320 .get(NetworkType::Evm, &relayer.network)
321 .await
322 .ok()
323 .flatten()
324 .ok_or_else(|| {
325 RelayerError::NetworkConfiguration(format!(
326 "Network {} not found",
327 relayer.network
328 ))
329 })?;
330
331 let network = EvmNetwork::try_from(network_repo)?;
332
333 let evm_provider = get_network_provider(&network, relayer.custom_rpc_urls.clone())?;
334 let signer_service = EvmSignerFactory::create_evm_signer(signer).await?;
335 let transaction_counter_service = Arc::new(TransactionCounterService::new(
336 relayer.id.clone(),
337 relayer.address.clone(),
338 state.transaction_counter_store(),
339 ));
340 let relayer = DefaultEvmRelayer::new(
341 relayer,
342 signer_service,
343 evm_provider,
344 network,
345 state.relayer_repository(),
346 state.network_repository(),
347 state.transaction_repository(),
348 transaction_counter_service,
349 state.job_producer(),
350 )?;
351
352 Ok(NetworkRelayer::Evm(relayer))
353 }
354 NetworkType::Solana => {
355 let solana_relayer = create_solana_relayer(
356 relayer,
357 signer,
358 state.relayer_repository(),
359 state.network_repository(),
360 state.transaction_repository(),
361 state.job_producer(),
362 )
363 .await?;
364 Ok(NetworkRelayer::Solana(solana_relayer))
365 }
366 NetworkType::Stellar => {
367 let network_repo = state
368 .network_repository()
369 .get(NetworkType::Stellar, &relayer.network)
370 .await
371 .ok()
372 .flatten()
373 .ok_or_else(|| {
374 RelayerError::NetworkConfiguration(format!(
375 "Network {} not found",
376 relayer.network
377 ))
378 })?;
379
380 let network = StellarNetwork::try_from(network_repo)?;
381
382 let stellar_provider =
383 get_network_provider(&network, relayer.custom_rpc_urls.clone())
384 .map_err(|e| RelayerError::NetworkConfiguration(e.to_string()))?;
385
386 let transaction_counter_service = Arc::new(TransactionCounterService::new(
387 relayer.id.clone(),
388 relayer.address.clone(),
389 state.transaction_counter_store(),
390 ));
391
392 let relayer = DefaultStellarRelayer::<J>::new(
393 relayer,
394 stellar_provider,
395 stellar::StellarRelayerDependencies::new(
396 state.relayer_repository(),
397 state.network_repository(),
398 state.transaction_repository(),
399 transaction_counter_service,
400 state.job_producer(),
401 ),
402 )
403 .await?;
404 Ok(NetworkRelayer::Stellar(relayer))
405 }
406 }
407 }
408}
409
410#[derive(Serialize, Deserialize, ToSchema)]
411pub struct SignDataRequest {
412 pub message: String,
413}
414
415#[derive(Serialize, Deserialize, ToSchema)]
416pub struct SignDataResponseEvm {
417 pub r: String,
418 pub s: String,
419 pub v: u8,
420 pub sig: String,
421}
422
423#[derive(Serialize, Deserialize, ToSchema)]
424pub struct SignDataResponseSolana {
425 pub signature: String,
426 pub public_key: String,
427}
428
429#[derive(Serialize, Deserialize, ToSchema)]
430#[serde(untagged)]
431pub enum SignDataResponse {
432 Evm(SignDataResponseEvm),
433 Solana(SignDataResponseSolana),
434}
435
436#[derive(Serialize, Deserialize, ToSchema)]
437pub struct SignTypedDataRequest {
438 pub domain_separator: String,
439 pub hash_struct_message: String,
440}
441
442#[derive(Debug, Serialize, Deserialize)]
443pub struct SignTransactionResponseEvm {
444 pub hash: String,
445 pub signature: EvmTransactionDataSignature,
446 pub raw: Vec<u8>,
447}
448
449#[derive(Debug, Serialize, Deserialize)]
450pub struct SignTransactionResponseStellar {
451 pub signature: DecoratedSignature,
452}
453
454#[derive(Debug, Serialize, Deserialize)]
455pub enum SignTransactionResponse {
456 Evm(SignTransactionResponseEvm),
457 Solana(Vec<u8>),
458 Stellar(SignTransactionResponseStellar),
459}
460
461impl SignTransactionResponse {
462 pub fn into_evm(self) -> Result<SignTransactionResponseEvm, TransactionError> {
463 match self {
464 SignTransactionResponse::Evm(e) => Ok(e),
465 _ => Err(TransactionError::InvalidType(
466 "Expected EVM signature".to_string(),
467 )),
468 }
469 }
470}
471
472#[derive(Debug, Serialize, ToSchema)]
473pub struct BalanceResponse {
474 pub balance: u128,
475 #[schema(example = "wei")]
476 pub unit: String,
477}
478
479#[derive(Serialize, Deserialize, ToSchema)]
480pub struct RelayerUpdateRequest {
481 #[schema(nullable = false)]
482 pub paused: Option<bool>,
483}