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