openzeppelin_relayer/domain/relayer/
mod.rs

1//! # Relayer Domain Module
2//!
3//! This module contains the core domain logic for the relayer service.
4//! It handles transaction submission, validation, and monitoring across
5//! different blockchain networks.
6//! ## Architecture
7//!
8//! The relayer domain is organized into network-specific implementations
9//! that share common interfaces for transaction handling and monitoring.
10
11use actix_web::web::ThinData;
12use serde::{Deserialize, Serialize};
13use std::sync::Arc;
14use tracing::instrument;
15use utoipa::ToSchema;
16
17#[cfg(test)]
18use mockall::automock;
19
20use crate::{
21    jobs::JobProducerTrait,
22    models::{
23        transaction::request::{
24            SponsoredTransactionBuildRequest, SponsoredTransactionQuoteRequest,
25        },
26        AppState, DecoratedSignature, DeletePendingTransactionsResponse,
27        EncodedSerializedTransaction, EvmNetwork, EvmTransactionDataSignature, JsonRpcRequest,
28        JsonRpcResponse, NetworkRepoModel, NetworkRpcRequest, NetworkRpcResult,
29        NetworkTransactionRequest, NetworkType, NotificationRepoModel, RelayerError,
30        RelayerRepoModel, RelayerStatus, SignerRepoModel, SponsoredTransactionBuildResponse,
31        SponsoredTransactionQuoteResponse, TransactionError, TransactionRepoModel,
32    },
33    repositories::{
34        ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository,
35        Repository, TransactionCounterTrait, TransactionRepository,
36    },
37    services::{
38        provider::get_network_provider, signer::EvmSignerFactory, TransactionCounterService,
39    },
40};
41
42use async_trait::async_trait;
43use eyre::Result;
44
45mod evm;
46mod solana;
47mod stellar;
48mod util;
49
50pub use evm::*;
51pub use solana::*;
52pub use stellar::*;
53pub use util::*;
54
55// Re-export SwapResult from solana module for use in Stellar
56pub use solana::SwapResult;
57
58/// The `Relayer` trait defines the core functionality required for a relayer
59/// in the system. Implementors of this trait are responsible for handling
60/// transaction requests, managing balances, and interacting with the network.
61#[async_trait]
62#[cfg_attr(test, automock)]
63#[allow(dead_code)]
64pub trait Relayer {
65    /// Processes a transaction request and returns the result.
66    ///
67    /// # Arguments
68    ///
69    /// * `tx_request` - The transaction request to be processed.
70    ///
71    /// # Returns
72    ///
73    /// A `Result` containing a `TransactionRepoModel` on success, or a
74    /// `RelayerError` on failure.
75    async fn process_transaction_request(
76        &self,
77        tx_request: NetworkTransactionRequest,
78    ) -> Result<TransactionRepoModel, RelayerError>;
79
80    /// Retrieves the current balance of the relayer.
81    ///
82    /// # Returns
83    ///
84    /// A `Result` containing a `BalanceResponse` on success, or a
85    /// `RelayerError` on failure.
86    async fn get_balance(&self) -> Result<BalanceResponse, RelayerError>;
87
88    /// Deletes all pending transactions.
89    ///
90    /// # Returns
91    ///
92    /// A `Result` containing a `DeletePendingTransactionsResponse` with details
93    /// about which transactions were cancelled and which failed, or a `RelayerError` on failure.
94    async fn delete_pending_transactions(
95        &self,
96    ) -> Result<DeletePendingTransactionsResponse, RelayerError>;
97
98    /// Signs data using the relayer's credentials.
99    ///
100    /// # Arguments
101    ///
102    /// * `request` - The data to be signed.
103    ///
104    /// # Returns
105    ///
106    /// A `Result` containing a `SignDataResponse` on success, or a
107    /// `RelayerError` on failure.
108    async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError>;
109
110    /// Signs typed data using the relayer's credentials.
111    ///
112    /// # Arguments
113    ///
114    /// * `request` - The typed data to be signed.
115    ///
116    /// # Returns
117    ///
118    /// A `Result` containing a `SignDataResponse` on success, or a
119    /// `RelayerError` on failure.
120    async fn sign_typed_data(
121        &self,
122        request: SignTypedDataRequest,
123    ) -> Result<SignDataResponse, RelayerError>;
124
125    /// Executes a JSON-RPC request.
126    ///
127    /// # Arguments
128    ///
129    /// * `request` - The JSON-RPC request to be executed.
130    ///
131    /// # Returns
132    ///
133    /// A `Result` containing a `JsonRpcResponse` on success, or a
134    /// `RelayerError` on failure.
135    async fn rpc(
136        &self,
137        request: JsonRpcRequest<NetworkRpcRequest>,
138    ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError>;
139
140    /// Retrieves the current status of the relayer.
141    ///
142    /// # Returns
143    ///
144    /// A `Result` containing `RelayerStatus` on success, or a
145    /// `RelayerError` on failure.
146    async fn get_status(&self) -> Result<RelayerStatus, RelayerError>;
147
148    /// Initializes the relayer.
149    ///
150    /// # Returns
151    ///
152    /// A `Result` indicating success, or a `RelayerError` on failure.
153    async fn initialize_relayer(&self) -> Result<(), RelayerError>;
154
155    /// Runs health checks on the relayer without side effects.
156    ///
157    /// This method performs all necessary health checks (RPC validation, balance checks, etc.)
158    /// and returns the results without updating any state or sending notifications.
159    ///
160    /// # Returns
161    ///
162    /// * `Ok(())` - All health checks passed
163    /// * `Err(Vec<HealthCheckFailure>)` - One or more health checks failed with specific reasons
164    async fn check_health(&self) -> Result<(), Vec<crate::models::HealthCheckFailure>>;
165
166    /// Validates that the relayer's balance meets the minimum required.
167    ///
168    /// # Returns
169    ///
170    /// A `Result` indicating success, or a `RelayerError` on failure.
171    async fn validate_min_balance(&self) -> Result<(), RelayerError>;
172
173    /// Signs a transaction using the relayer's credentials.
174    ///
175    /// # Arguments
176    ///
177    /// * `unsigned_xdr` - The unsigned transaction XDR string to be signed.
178    ///
179    /// # Returns
180    ///
181    /// A `Result` containing a `SignTransactionExternalResponse` on success, or a
182    /// `RelayerError` on failure.
183    async fn sign_transaction(
184        &self,
185        request: &SignTransactionRequest,
186    ) -> Result<SignTransactionExternalResponse, RelayerError>;
187
188    /// Handles a targeted health action dispatched via job metadata.
189    ///
190    /// Returns `Ok(true)` if an action was handled, `Ok(false)` if no recognized action.
191    /// Only meaningful for EVM relayers; other network types return `Ok(false)` by default.
192    async fn handle_health_action(
193        &self,
194        _metadata: &std::collections::HashMap<String, String>,
195    ) -> Result<bool, RelayerError> {
196        Ok(false)
197    }
198}
199
200/// Solana Relayer Dex Trait
201/// Subset of methods for Solana relayer
202#[async_trait]
203#[allow(dead_code)]
204#[cfg_attr(test, automock)]
205pub trait SolanaRelayerDexTrait {
206    /// Handles a token swap request.
207    async fn handle_token_swap_request(
208        &self,
209        relayer_id: String,
210    ) -> Result<Vec<SwapResult>, RelayerError>;
211}
212
213/// Subset of methods for Stellar relayer
214#[async_trait]
215#[allow(dead_code)]
216#[cfg_attr(test, automock)]
217pub trait StellarRelayerDexTrait {
218    /// Handles a token swap request.
219    async fn handle_token_swap_request(
220        &self,
221        relayer_id: String,
222    ) -> Result<Vec<SwapResult>, RelayerError>;
223}
224
225/// Gas abstraction trait for relayers that support fee estimation and transaction preparation.
226///
227/// This trait provides a REST-friendly interface for gas abstraction operations,
228/// allowing clients to estimate fees and prepare transactions without using JSON-RPC.
229#[async_trait]
230#[allow(dead_code)]
231#[cfg_attr(test, automock)]
232pub trait GasAbstractionTrait {
233    /// Gets a quote for a gasless transaction.
234    ///
235    /// # Arguments
236    ///
237    /// * `params` - The gasless transaction quote request parameters (network-agnostic).
238    ///
239    /// # Returns
240    ///
241    /// A `Result` containing a fee estimate result on success, or a `RelayerError` on failure.
242    async fn quote_sponsored_transaction(
243        &self,
244        params: SponsoredTransactionQuoteRequest,
245    ) -> Result<SponsoredTransactionQuoteResponse, RelayerError>;
246
247    /// Prepares a transaction with fee payments.
248    ///
249    /// # Arguments
250    ///
251    /// * `params` - The prepare transaction request parameters (network-agnostic).
252    ///
253    /// # Returns
254    ///
255    /// A `Result` containing a prepare transaction result on success, or a `RelayerError` on failure.
256    async fn build_sponsored_transaction(
257        &self,
258        params: SponsoredTransactionBuildRequest,
259    ) -> Result<SponsoredTransactionBuildResponse, RelayerError>;
260}
261
262pub enum NetworkRelayer<
263    J: JobProducerTrait + 'static,
264    T: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
265    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
266    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
267    TCR: TransactionCounterTrait + Send + Sync + 'static,
268> {
269    Evm(Box<DefaultEvmRelayer<J, T, RR, NR, TCR>>),
270    Solana(DefaultSolanaRelayer<J, T, RR, NR>),
271    Stellar(DefaultStellarRelayer<J, T, NR, RR, TCR>),
272}
273
274#[async_trait]
275impl<
276        J: JobProducerTrait + 'static,
277        T: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
278        RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
279        NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
280        TCR: TransactionCounterTrait + Send + Sync + 'static,
281    > Relayer for NetworkRelayer<J, T, RR, NR, TCR>
282{
283    async fn process_transaction_request(
284        &self,
285        tx_request: NetworkTransactionRequest,
286    ) -> Result<TransactionRepoModel, RelayerError> {
287        match self {
288            NetworkRelayer::Evm(relayer) => relayer.process_transaction_request(tx_request).await,
289            NetworkRelayer::Solana(relayer) => {
290                relayer.process_transaction_request(tx_request).await
291            }
292            NetworkRelayer::Stellar(relayer) => {
293                relayer.process_transaction_request(tx_request).await
294            }
295        }
296    }
297
298    async fn get_balance(&self) -> Result<BalanceResponse, RelayerError> {
299        match self {
300            NetworkRelayer::Evm(relayer) => relayer.get_balance().await,
301            NetworkRelayer::Solana(relayer) => relayer.get_balance().await,
302            NetworkRelayer::Stellar(relayer) => relayer.get_balance().await,
303        }
304    }
305
306    async fn delete_pending_transactions(
307        &self,
308    ) -> Result<DeletePendingTransactionsResponse, RelayerError> {
309        match self {
310            NetworkRelayer::Evm(relayer) => relayer.delete_pending_transactions().await,
311            NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
312            NetworkRelayer::Stellar(relayer) => relayer.delete_pending_transactions().await,
313        }
314    }
315
316    async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError> {
317        match self {
318            NetworkRelayer::Evm(relayer) => relayer.sign_data(request).await,
319            NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
320            NetworkRelayer::Stellar(relayer) => relayer.sign_data(request).await,
321        }
322    }
323
324    async fn sign_typed_data(
325        &self,
326        request: SignTypedDataRequest,
327    ) -> Result<SignDataResponse, RelayerError> {
328        match self {
329            NetworkRelayer::Evm(relayer) => relayer.sign_typed_data(request).await,
330            NetworkRelayer::Solana(_) => solana_not_supported_relayer(),
331            NetworkRelayer::Stellar(relayer) => relayer.sign_typed_data(request).await,
332        }
333    }
334
335    async fn rpc(
336        &self,
337        request: JsonRpcRequest<NetworkRpcRequest>,
338    ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError> {
339        match self {
340            NetworkRelayer::Evm(relayer) => relayer.rpc(request).await,
341            NetworkRelayer::Solana(relayer) => relayer.rpc(request).await,
342            NetworkRelayer::Stellar(relayer) => relayer.rpc(request).await,
343        }
344    }
345
346    async fn get_status(&self) -> Result<RelayerStatus, RelayerError> {
347        match self {
348            NetworkRelayer::Evm(relayer) => relayer.get_status().await,
349            NetworkRelayer::Solana(relayer) => relayer.get_status().await,
350            NetworkRelayer::Stellar(relayer) => relayer.get_status().await,
351        }
352    }
353
354    async fn validate_min_balance(&self) -> Result<(), RelayerError> {
355        match self {
356            NetworkRelayer::Evm(relayer) => relayer.validate_min_balance().await,
357            NetworkRelayer::Solana(relayer) => relayer.validate_min_balance().await,
358            NetworkRelayer::Stellar(relayer) => relayer.validate_min_balance().await,
359        }
360    }
361
362    async fn initialize_relayer(&self) -> Result<(), RelayerError> {
363        match self {
364            NetworkRelayer::Evm(relayer) => relayer.initialize_relayer().await,
365            NetworkRelayer::Solana(relayer) => relayer.initialize_relayer().await,
366            NetworkRelayer::Stellar(relayer) => relayer.initialize_relayer().await,
367        }
368    }
369
370    async fn check_health(&self) -> Result<(), Vec<crate::models::HealthCheckFailure>> {
371        match self {
372            NetworkRelayer::Evm(relayer) => relayer.check_health().await,
373            NetworkRelayer::Solana(relayer) => relayer.check_health().await,
374            NetworkRelayer::Stellar(relayer) => relayer.check_health().await,
375        }
376    }
377
378    async fn sign_transaction(
379        &self,
380        request: &SignTransactionRequest,
381    ) -> Result<SignTransactionExternalResponse, RelayerError> {
382        match self {
383            NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
384                "sign_transaction not supported for EVM".to_string(),
385            )),
386            NetworkRelayer::Solana(relayer) => relayer.sign_transaction(request).await,
387            NetworkRelayer::Stellar(relayer) => relayer.sign_transaction(request).await,
388        }
389    }
390
391    async fn handle_health_action(
392        &self,
393        metadata: &std::collections::HashMap<String, String>,
394    ) -> Result<bool, RelayerError> {
395        match self {
396            NetworkRelayer::Evm(relayer) => relayer.handle_health_action(metadata).await,
397            NetworkRelayer::Solana(_) | NetworkRelayer::Stellar(_) => Ok(false),
398        }
399    }
400}
401
402#[async_trait]
403impl<
404        J: JobProducerTrait + 'static,
405        T: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
406        RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
407        NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
408        TCR: TransactionCounterTrait + Send + Sync + 'static,
409    > GasAbstractionTrait for NetworkRelayer<J, T, RR, NR, TCR>
410{
411    async fn quote_sponsored_transaction(
412        &self,
413        params: SponsoredTransactionQuoteRequest,
414    ) -> Result<SponsoredTransactionQuoteResponse, RelayerError> {
415        match params {
416            SponsoredTransactionQuoteRequest::Solana(params) => match self {
417                NetworkRelayer::Solana(relayer) => {
418                    relayer
419                        .quote_sponsored_transaction(SponsoredTransactionQuoteRequest::Solana(
420                            params,
421                        ))
422                        .await
423                }
424                NetworkRelayer::Stellar(_) => Err(RelayerError::ValidationError(
425                    "Solana request type does not match Stellar relayer type".to_string(),
426                )),
427                NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
428                    "Gas abstraction not supported for EVM relayers".to_string(),
429                )),
430            },
431            SponsoredTransactionQuoteRequest::Stellar(params) => match self {
432                NetworkRelayer::Stellar(relayer) => {
433                    relayer
434                        .quote_sponsored_transaction(SponsoredTransactionQuoteRequest::Stellar(
435                            params,
436                        ))
437                        .await
438                }
439                NetworkRelayer::Solana(_) => Err(RelayerError::ValidationError(
440                    "Stellar request type does not match Solana relayer type".to_string(),
441                )),
442                NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
443                    "Gas abstraction not supported for EVM relayers".to_string(),
444                )),
445            },
446        }
447    }
448
449    async fn build_sponsored_transaction(
450        &self,
451        params: SponsoredTransactionBuildRequest,
452    ) -> Result<SponsoredTransactionBuildResponse, RelayerError> {
453        match params {
454            SponsoredTransactionBuildRequest::Solana(params) => match self {
455                NetworkRelayer::Solana(relayer) => {
456                    relayer
457                        .build_sponsored_transaction(SponsoredTransactionBuildRequest::Solana(
458                            params,
459                        ))
460                        .await
461                }
462                NetworkRelayer::Stellar(_) => Err(RelayerError::ValidationError(
463                    "Solana request type does not match Stellar relayer type".to_string(),
464                )),
465                NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
466                    "Gas abstraction not supported for EVM relayers".to_string(),
467                )),
468            },
469            SponsoredTransactionBuildRequest::Stellar(params) => match self {
470                NetworkRelayer::Stellar(relayer) => {
471                    relayer
472                        .build_sponsored_transaction(SponsoredTransactionBuildRequest::Stellar(
473                            params,
474                        ))
475                        .await
476                }
477                NetworkRelayer::Solana(_) => Err(RelayerError::ValidationError(
478                    "Stellar request type does not match Solana relayer type".to_string(),
479                )),
480                NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
481                    "Gas abstraction not supported for EVM relayers".to_string(),
482                )),
483            },
484        }
485    }
486}
487
488impl<
489        J: JobProducerTrait + 'static,
490        T: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
491        RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
492        NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
493        TCR: TransactionCounterTrait + Send + Sync + 'static,
494    > NetworkRelayer<J, T, RR, NR, TCR>
495{
496    /// Handles a token swap request for supported networks (Solana and Stellar).
497    ///
498    /// # Returns
499    ///
500    /// A `Result` containing a `Vec<SwapResult>` on success, or a `RelayerError` on failure.
501    /// Returns `NotSupported` error for EVM networks.
502    pub async fn handle_token_swap_request(
503        &self,
504        relayer_id: String,
505    ) -> Result<Vec<SwapResult>, RelayerError> {
506        match self {
507            NetworkRelayer::Evm(_) => Err(RelayerError::NotSupported(
508                "Token swap not supported for EVM relayers".to_string(),
509            )),
510            NetworkRelayer::Solana(relayer) => relayer.handle_token_swap_request(relayer_id).await,
511            NetworkRelayer::Stellar(relayer) => relayer.handle_token_swap_request(relayer_id).await,
512        }
513    }
514}
515
516#[async_trait]
517pub trait RelayerFactoryTrait<
518    J: JobProducerTrait + Send + Sync + 'static,
519    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
520    TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
521    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
522    NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
523    SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
524    TCR: TransactionCounterTrait + Send + Sync + 'static,
525    PR: PluginRepositoryTrait + Send + Sync + 'static,
526    AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
527>
528{
529    async fn create_relayer(
530        relayer: RelayerRepoModel,
531        signer: SignerRepoModel,
532        state: &ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>>,
533    ) -> Result<NetworkRelayer<J, TR, RR, NR, TCR>, RelayerError>;
534}
535
536pub struct RelayerFactory;
537
538#[async_trait]
539impl<
540        J: JobProducerTrait + 'static,
541        TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
542        RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
543        NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
544        NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
545        SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
546        TCR: TransactionCounterTrait + Send + Sync + 'static,
547        PR: PluginRepositoryTrait + Send + Sync + 'static,
548        AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
549    > RelayerFactoryTrait<J, RR, TR, NR, NFR, SR, TCR, PR, AKR> for RelayerFactory
550{
551    #[instrument(
552        level = "debug",
553        skip(relayer, signer, state),
554        fields(
555            request_id = ?crate::observability::request_id::get_request_id(),
556            relayer_id = %relayer.id,
557            network_type = ?relayer.network_type,
558        )
559    )]
560    async fn create_relayer(
561        relayer: RelayerRepoModel,
562        signer: SignerRepoModel,
563        state: &ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>>,
564    ) -> Result<NetworkRelayer<J, TR, RR, NR, TCR>, RelayerError> {
565        match relayer.network_type {
566            NetworkType::Evm => {
567                let network_repo = state
568                    .network_repository()
569                    .get_by_name(NetworkType::Evm, &relayer.network)
570                    .await
571                    .ok()
572                    .flatten()
573                    .ok_or_else(|| {
574                        RelayerError::NetworkConfiguration(format!(
575                            "Network {} not found",
576                            relayer.network
577                        ))
578                    })?;
579
580                let network = EvmNetwork::try_from(network_repo)?;
581
582                let evm_provider = get_network_provider(&network, relayer.custom_rpc_urls.clone())?;
583                let signer_service = EvmSignerFactory::create_evm_signer(signer.into()).await?;
584                let transaction_counter_service = Arc::new(TransactionCounterService::new(
585                    relayer.id.clone(),
586                    relayer.address.clone(),
587                    state.transaction_counter_store(),
588                ));
589                let relayer = DefaultEvmRelayer::new(
590                    relayer,
591                    signer_service,
592                    evm_provider,
593                    network,
594                    state.relayer_repository(),
595                    state.network_repository(),
596                    state.transaction_repository(),
597                    transaction_counter_service,
598                    state.job_producer(),
599                )?;
600
601                Ok(NetworkRelayer::Evm(Box::new(relayer)))
602            }
603            NetworkType::Solana => {
604                let solana_relayer = create_solana_relayer(
605                    relayer,
606                    signer,
607                    state.relayer_repository(),
608                    state.network_repository(),
609                    state.transaction_repository(),
610                    state.job_producer(),
611                )
612                .await?;
613                Ok(NetworkRelayer::Solana(solana_relayer))
614            }
615            NetworkType::Stellar => {
616                let stellar_relayer = create_stellar_relayer(
617                    relayer,
618                    signer,
619                    state.relayer_repository(),
620                    state.network_repository(),
621                    state.transaction_repository(),
622                    state.job_producer(),
623                    state.transaction_counter_store(),
624                )
625                .await?;
626                Ok(NetworkRelayer::Stellar(stellar_relayer))
627            }
628        }
629    }
630}
631
632#[derive(Serialize, Deserialize, ToSchema)]
633pub struct SignDataRequest {
634    pub message: String,
635}
636
637#[derive(Serialize, Deserialize, ToSchema)]
638pub struct SignDataResponseEvm {
639    pub r: String,
640    pub s: String,
641    pub v: u8,
642    pub sig: String,
643}
644
645#[derive(Serialize, Deserialize, ToSchema)]
646pub struct SignDataResponseSolana {
647    pub signature: String,
648    pub public_key: String,
649}
650
651#[derive(Serialize, Deserialize, ToSchema)]
652#[serde(untagged)]
653pub enum SignDataResponse {
654    Evm(SignDataResponseEvm),
655    Solana(SignDataResponseSolana),
656}
657
658#[derive(Serialize, Deserialize, ToSchema)]
659pub struct SignTypedDataRequest {
660    pub domain_separator: String,
661    pub hash_struct_message: String,
662}
663
664#[derive(Debug, Serialize, Deserialize, ToSchema)]
665pub struct SignTransactionRequestStellar {
666    pub unsigned_xdr: String,
667}
668
669#[derive(Debug, Serialize, Deserialize, ToSchema)]
670pub struct SignTransactionRequestSolana {
671    pub transaction: EncodedSerializedTransaction,
672}
673
674#[derive(Debug, Serialize, Deserialize, ToSchema)]
675#[serde(untagged)]
676pub enum SignTransactionRequest {
677    Stellar(SignTransactionRequestStellar),
678    Evm(Vec<u8>),
679    Solana(SignTransactionRequestSolana),
680}
681
682#[derive(Debug, Serialize, Deserialize, Clone)]
683pub struct SignTransactionResponseEvm {
684    pub hash: String,
685    pub signature: EvmTransactionDataSignature,
686    pub raw: Vec<u8>,
687}
688
689#[derive(Debug, Serialize, Deserialize, Clone)]
690pub struct SignTransactionResponseStellar {
691    pub signature: DecoratedSignature,
692}
693
694#[derive(Debug, Serialize, Deserialize, ToSchema, Clone)]
695pub struct SignTransactionResponseSolana {
696    pub transaction: EncodedSerializedTransaction,
697    pub signature: String,
698}
699
700#[derive(Debug, Serialize, Deserialize)]
701#[serde(rename_all = "camelCase")]
702pub struct SignXdrTransactionResponseStellar {
703    pub signed_xdr: String,
704    pub signature: DecoratedSignature,
705}
706
707#[derive(Debug, Serialize, Deserialize, Clone)]
708pub enum SignTransactionResponse {
709    Evm(SignTransactionResponseEvm),
710    Solana(SignTransactionResponseSolana),
711    Stellar(SignTransactionResponseStellar),
712}
713
714#[derive(Debug, Serialize, Deserialize, ToSchema)]
715#[serde(rename_all = "camelCase")]
716#[schema(as = SignTransactionResponseStellar)]
717pub struct SignTransactionExternalResponseStellar {
718    pub signed_xdr: String,
719    pub signature: String,
720}
721
722#[derive(Debug, Serialize, Deserialize, ToSchema)]
723#[serde(untagged)]
724#[schema(as = SignTransactionResponse)]
725pub enum SignTransactionExternalResponse {
726    Stellar(SignTransactionExternalResponseStellar),
727    Evm(Vec<u8>),
728    Solana(SignTransactionResponseSolana),
729}
730
731impl SignTransactionResponse {
732    pub fn into_evm(self) -> Result<SignTransactionResponseEvm, TransactionError> {
733        match self {
734            SignTransactionResponse::Evm(e) => Ok(e),
735            _ => Err(TransactionError::InvalidType(
736                "Expected EVM signature".to_string(),
737            )),
738        }
739    }
740}
741
742#[derive(Debug, Serialize, ToSchema)]
743pub struct BalanceResponse {
744    pub balance: u128,
745    #[schema(example = "wei")]
746    pub unit: String,
747}