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, NetworkRepoModel,
24 NetworkRpcRequest, NetworkRpcResult, NetworkTransactionRequest, NetworkType,
25 NotificationRepoModel, RelayerError, RelayerRepoModel, RelayerStatus, SignerRepoModel,
26 StellarNetwork, TransactionError, TransactionRepoModel,
27 },
28 repositories::{
29 NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository,
30 TransactionCounterTrait, TransactionRepository,
31 },
32 services::{
33 get_network_provider, EvmSignerFactory, StellarSignerFactory, TransactionCounterService,
34 },
35};
36
37use async_trait::async_trait;
38use eyre::Result;
39
40mod evm;
41mod solana;
42mod stellar;
43mod util;
44
45pub use evm::*;
46pub use solana::*;
47pub use stellar::*;
48pub use util::*;
49
50#[async_trait]
54#[allow(dead_code)]
55pub trait Relayer {
56 async fn process_transaction_request(
67 &self,
68 tx_request: NetworkTransactionRequest,
69 ) -> Result<TransactionRepoModel, RelayerError>;
70
71 async fn get_balance(&self) -> Result<BalanceResponse, RelayerError>;
78
79 async fn delete_pending_transactions(
86 &self,
87 ) -> Result<DeletePendingTransactionsResponse, RelayerError>;
88
89 async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError>;
100
101 async fn sign_typed_data(
112 &self,
113 request: SignTypedDataRequest,
114 ) -> Result<SignDataResponse, RelayerError>;
115
116 async fn rpc(
127 &self,
128 request: JsonRpcRequest<NetworkRpcRequest>,
129 ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError>;
130
131 async fn get_status(&self) -> Result<RelayerStatus, RelayerError>;
138
139 async fn initialize_relayer(&self) -> Result<(), RelayerError>;
145
146 async fn validate_min_balance(&self) -> Result<(), RelayerError>;
152
153 async fn sign_transaction(
164 &self,
165 request: &SignTransactionRequest,
166 ) -> Result<SignTransactionExternalResponse, RelayerError>;
167}
168
169#[async_trait]
172#[allow(dead_code)]
173#[cfg_attr(test, automock)]
174pub trait SolanaRelayerDexTrait {
175 async fn handle_token_swap_request(
177 &self,
178 relayer_id: String,
179 ) -> Result<Vec<SwapResult>, RelayerError>;
180}
181
182#[async_trait]
185#[allow(dead_code)]
186#[cfg_attr(test, automock)]
187pub trait SolanaRelayerTrait {
188 async fn get_balance(&self) -> Result<BalanceResponse, RelayerError>;
195
196 async fn rpc(
207 &self,
208 request: JsonRpcRequest<NetworkRpcRequest>,
209 ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError>;
210
211 async fn initialize_relayer(&self) -> Result<(), RelayerError>;
217
218 async fn validate_min_balance(&self) -> Result<(), RelayerError>;
224}
225
226pub enum NetworkRelayer<
227 J: JobProducerTrait + 'static,
228 T: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
229 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
230 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
231 TCR: TransactionCounterTrait + Send + Sync + 'static,
232> {
233 Evm(DefaultEvmRelayer<J, T, RR, NR, TCR>),
234 Solana(DefaultSolanaRelayer<J, T, RR, NR>),
235 Stellar(DefaultStellarRelayer<J, T, NR, RR, TCR>),
236}
237
238#[async_trait]
239impl<
240 J: JobProducerTrait + 'static,
241 T: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
242 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
243 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
244 TCR: TransactionCounterTrait + Send + Sync + 'static,
245 > Relayer for NetworkRelayer<J, T, RR, NR, TCR>
246{
247 async fn process_transaction_request(
248 &self,
249 tx_request: NetworkTransactionRequest,
250 ) -> Result<TransactionRepoModel, RelayerError> {
251 match self {
252 NetworkRelayer::Evm(relayer) => relayer.process_transaction_request(tx_request).await,
253 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
254 NetworkRelayer::Stellar(relayer) => {
255 relayer.process_transaction_request(tx_request).await
256 }
257 }
258 }
259
260 async fn get_balance(&self) -> Result<BalanceResponse, RelayerError> {
261 match self {
262 NetworkRelayer::Evm(relayer) => relayer.get_balance().await,
263 NetworkRelayer::Solana(relayer) => relayer.get_balance().await,
264 NetworkRelayer::Stellar(relayer) => relayer.get_balance().await,
265 }
266 }
267
268 async fn delete_pending_transactions(
269 &self,
270 ) -> Result<DeletePendingTransactionsResponse, RelayerError> {
271 match self {
272 NetworkRelayer::Evm(relayer) => relayer.delete_pending_transactions().await,
273 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
274 NetworkRelayer::Stellar(relayer) => relayer.delete_pending_transactions().await,
275 }
276 }
277
278 async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError> {
279 match self {
280 NetworkRelayer::Evm(relayer) => relayer.sign_data(request).await,
281 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
282 NetworkRelayer::Stellar(relayer) => relayer.sign_data(request).await,
283 }
284 }
285
286 async fn sign_typed_data(
287 &self,
288 request: SignTypedDataRequest,
289 ) -> Result<SignDataResponse, RelayerError> {
290 match self {
291 NetworkRelayer::Evm(relayer) => relayer.sign_typed_data(request).await,
292 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
293 NetworkRelayer::Stellar(relayer) => relayer.sign_typed_data(request).await,
294 }
295 }
296
297 async fn rpc(
298 &self,
299 request: JsonRpcRequest<NetworkRpcRequest>,
300 ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError> {
301 match self {
302 NetworkRelayer::Evm(relayer) => relayer.rpc(request).await,
303 NetworkRelayer::Solana(relayer) => relayer.rpc(request).await,
304 NetworkRelayer::Stellar(relayer) => relayer.rpc(request).await,
305 }
306 }
307
308 async fn get_status(&self) -> Result<RelayerStatus, RelayerError> {
309 match self {
310 NetworkRelayer::Evm(relayer) => relayer.get_status().await,
311 NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
312 NetworkRelayer::Stellar(relayer) => relayer.get_status().await,
313 }
314 }
315
316 async fn validate_min_balance(&self) -> Result<(), RelayerError> {
317 match self {
318 NetworkRelayer::Evm(relayer) => relayer.validate_min_balance().await,
319 NetworkRelayer::Solana(relayer) => relayer.validate_min_balance().await,
320 NetworkRelayer::Stellar(relayer) => relayer.validate_min_balance().await,
321 }
322 }
323
324 async fn initialize_relayer(&self) -> Result<(), RelayerError> {
325 match self {
326 NetworkRelayer::Evm(relayer) => relayer.initialize_relayer().await,
327 NetworkRelayer::Solana(relayer) => relayer.initialize_relayer().await,
328 NetworkRelayer::Stellar(relayer) => relayer.initialize_relayer().await,
329 }
330 }
331
332 async fn sign_transaction(
333 &self,
334 request: &SignTransactionRequest,
335 ) -> Result<SignTransactionExternalResponse, RelayerError> {
336 match self {
337 NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
338 "sign_transaction not supported for EVM".to_string(),
339 )),
340 NetworkRelayer::Solana(_) => Err(RelayerError::NotSupported(
341 "sign_transaction not supported for Solana".to_string(),
342 )),
343 NetworkRelayer::Stellar(relayer) => relayer.sign_transaction(request).await,
344 }
345 }
346}
347
348#[async_trait]
349pub trait RelayerFactoryTrait<
350 J: JobProducerTrait + Send + Sync + 'static,
351 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
352 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
353 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
354 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
355 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
356 TCR: TransactionCounterTrait + Send + Sync + 'static,
357 PR: PluginRepositoryTrait + Send + Sync + 'static,
358>
359{
360 async fn create_relayer(
361 relayer: RelayerRepoModel,
362 signer: SignerRepoModel,
363 state: &ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
364 ) -> Result<NetworkRelayer<J, TR, RR, NR, TCR>, RelayerError>;
365}
366
367pub struct RelayerFactory;
368
369#[async_trait]
370impl<
371 J: JobProducerTrait + 'static,
372 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
373 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
374 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
375 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
376 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
377 TCR: TransactionCounterTrait + Send + Sync + 'static,
378 PR: PluginRepositoryTrait + Send + Sync + 'static,
379 > RelayerFactoryTrait<J, RR, TR, NR, NFR, SR, TCR, PR> for RelayerFactory
380{
381 async fn create_relayer(
382 relayer: RelayerRepoModel,
383 signer: SignerRepoModel,
384 state: &ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR>>,
385 ) -> Result<NetworkRelayer<J, TR, RR, NR, TCR>, RelayerError> {
386 match relayer.network_type {
387 NetworkType::Evm => {
388 let network_repo = state
389 .network_repository()
390 .get_by_name(NetworkType::Evm, &relayer.network)
391 .await
392 .ok()
393 .flatten()
394 .ok_or_else(|| {
395 RelayerError::NetworkConfiguration(format!(
396 "Network {} not found",
397 relayer.network
398 ))
399 })?;
400
401 let network = EvmNetwork::try_from(network_repo)?;
402
403 let evm_provider = get_network_provider(&network, relayer.custom_rpc_urls.clone())?;
404 let signer_service = EvmSignerFactory::create_evm_signer(signer.into()).await?;
405 let transaction_counter_service = Arc::new(TransactionCounterService::new(
406 relayer.id.clone(),
407 relayer.address.clone(),
408 state.transaction_counter_store(),
409 ));
410 let relayer = DefaultEvmRelayer::new(
411 relayer,
412 signer_service,
413 evm_provider,
414 network,
415 state.relayer_repository(),
416 state.network_repository(),
417 state.transaction_repository(),
418 transaction_counter_service,
419 state.job_producer(),
420 )?;
421
422 Ok(NetworkRelayer::Evm(relayer))
423 }
424 NetworkType::Solana => {
425 let solana_relayer = create_solana_relayer(
426 relayer,
427 signer,
428 state.relayer_repository(),
429 state.network_repository(),
430 state.transaction_repository(),
431 state.job_producer(),
432 )
433 .await?;
434 Ok(NetworkRelayer::Solana(solana_relayer))
435 }
436 NetworkType::Stellar => {
437 let network_repo = state
438 .network_repository()
439 .get_by_name(NetworkType::Stellar, &relayer.network)
440 .await
441 .ok()
442 .flatten()
443 .ok_or_else(|| {
444 RelayerError::NetworkConfiguration(format!(
445 "Network {} not found",
446 relayer.network
447 ))
448 })?;
449
450 let network = StellarNetwork::try_from(network_repo)?;
451
452 let stellar_provider =
453 get_network_provider(&network, relayer.custom_rpc_urls.clone())
454 .map_err(|e| RelayerError::NetworkConfiguration(e.to_string()))?;
455
456 let signer_service = StellarSignerFactory::create_stellar_signer(&signer.into())?;
457
458 let transaction_counter_service = Arc::new(TransactionCounterService::new(
459 relayer.id.clone(),
460 relayer.address.clone(),
461 state.transaction_counter_store(),
462 ));
463
464 let relayer = DefaultStellarRelayer::<J, TR, NR, RR, TCR>::new(
465 relayer,
466 signer_service,
467 stellar_provider,
468 stellar::StellarRelayerDependencies::new(
469 state.relayer_repository(),
470 state.network_repository(),
471 state.transaction_repository(),
472 transaction_counter_service,
473 state.job_producer(),
474 ),
475 )
476 .await?;
477 Ok(NetworkRelayer::Stellar(relayer))
478 }
479 }
480 }
481}
482
483#[derive(Serialize, Deserialize, ToSchema)]
484pub struct SignDataRequest {
485 pub message: String,
486}
487
488#[derive(Serialize, Deserialize, ToSchema)]
489pub struct SignDataResponseEvm {
490 pub r: String,
491 pub s: String,
492 pub v: u8,
493 pub sig: String,
494}
495
496#[derive(Serialize, Deserialize, ToSchema)]
497pub struct SignDataResponseSolana {
498 pub signature: String,
499 pub public_key: String,
500}
501
502#[derive(Serialize, Deserialize, ToSchema)]
503#[serde(untagged)]
504pub enum SignDataResponse {
505 Evm(SignDataResponseEvm),
506 Solana(SignDataResponseSolana),
507}
508
509#[derive(Serialize, Deserialize, ToSchema)]
510pub struct SignTypedDataRequest {
511 pub domain_separator: String,
512 pub hash_struct_message: String,
513}
514
515#[derive(Debug, Serialize, Deserialize, ToSchema)]
516pub struct SignTransactionRequestStellar {
517 pub unsigned_xdr: String,
518}
519
520#[derive(Debug, Serialize, Deserialize, ToSchema)]
521#[serde(untagged)]
522pub enum SignTransactionRequest {
523 Stellar(SignTransactionRequestStellar),
524 Evm(Vec<u8>),
525 Solana(Vec<u8>),
526}
527
528#[derive(Debug, Serialize, Deserialize)]
529pub struct SignTransactionResponseEvm {
530 pub hash: String,
531 pub signature: EvmTransactionDataSignature,
532 pub raw: Vec<u8>,
533}
534
535#[derive(Debug, Serialize, Deserialize)]
536pub struct SignTransactionResponseStellar {
537 pub signature: DecoratedSignature,
538}
539
540#[derive(Debug, Serialize, Deserialize)]
541#[serde(rename_all = "camelCase")]
542pub struct SignXdrTransactionResponseStellar {
543 pub signed_xdr: String,
544 pub signature: DecoratedSignature,
545}
546
547#[derive(Debug, Serialize, Deserialize)]
548pub enum SignTransactionResponse {
549 Evm(SignTransactionResponseEvm),
550 Solana(Vec<u8>),
551 Stellar(SignTransactionResponseStellar),
552}
553
554#[derive(Debug, Serialize, Deserialize, ToSchema)]
555#[serde(rename_all = "camelCase")]
556#[schema(as = SignTransactionResponseStellar)]
557pub struct SignTransactionExternalResponseStellar {
558 pub signed_xdr: String,
559 pub signature: String,
560}
561
562#[derive(Debug, Serialize, Deserialize, ToSchema)]
563#[serde(untagged)]
564#[schema(as = SignTransactionResponse)]
565pub enum SignTransactionExternalResponse {
566 Stellar(SignTransactionExternalResponseStellar),
567 Evm(Vec<u8>),
568 Solana(Vec<u8>),
569}
570
571impl SignTransactionResponse {
572 pub fn into_evm(self) -> Result<SignTransactionResponseEvm, TransactionError> {
573 match self {
574 SignTransactionResponse::Evm(e) => Ok(e),
575 _ => Err(TransactionError::InvalidType(
576 "Expected EVM signature".to_string(),
577 )),
578 }
579 }
580}
581
582#[derive(Debug, Serialize, ToSchema)]
583pub struct BalanceResponse {
584 pub balance: u128,
585 #[schema(example = "wei")]
586 pub unit: String,
587}