openzeppelin_relayer/domain/relayer/evm/
evm_relayer.rs

1/// This module defines the `EvmRelayer` struct and its associated functionality for
2/// interacting with Ethereum Virtual Machine (EVM) networks. The `EvmRelayer` is responsible
3/// for managing transactions, signing data, and ensuring the relayer's state is synchronized
4/// with the blockchain.
5///
6/// # Components
7///
8/// - `EvmRelayer`: The main struct that encapsulates the relayer's state and operations.
9/// - `RelayerRepoModel`: Represents the relayer's data model.
10/// - `EvmSigner`: Handles signing of data and transactions.
11/// - `EvmProvider`: Provides blockchain interaction capabilities, such as fetching balances
12///   and transaction counts.
13/// - `TransactionCounterService`: Manages the nonce for transactions to ensure they are
14///   processed in the correct order.
15/// - `JobProducer`: Produces jobs for processing transactions and sending notifications.
16///
17/// # Error Handling
18///
19/// The module uses the `RelayerError` enum to handle various errors that can occur during
20/// operations, such as provider errors, insufficient balance, and transaction failures.
21///
22/// # Usage
23///
24/// To use the `EvmRelayer`, create an instance using the `new` method, providing the necessary
25/// components. Then, call the appropriate methods to process transactions, sign data, and
26/// manage the relayer's state.
27use std::sync::Arc;
28
29use crate::{
30    constants::EVM_SMALLEST_UNIT_NAME,
31    domain::{
32        relayer::{Relayer, RelayerError},
33        BalanceResponse, SignDataRequest, SignDataResponse, SignTransactionExternalResponse,
34        SignTransactionRequest, SignTypedDataRequest,
35    },
36    jobs::{JobProducerTrait, TransactionRequest},
37    models::{
38        produce_relayer_disabled_payload, DeletePendingTransactionsResponse, EvmNetwork,
39        JsonRpcRequest, JsonRpcResponse, NetworkRepoModel, NetworkRpcRequest, NetworkRpcResult,
40        NetworkTransactionRequest, NetworkType, RelayerRepoModel, RelayerStatus, RepositoryError,
41        RpcErrorCodes, TransactionRepoModel, TransactionStatus,
42    },
43    repositories::{NetworkRepository, RelayerRepository, Repository, TransactionRepository},
44    services::{
45        DataSignerTrait, EvmProvider, EvmProviderTrait, EvmSigner, TransactionCounterService,
46        TransactionCounterServiceTrait,
47    },
48};
49use async_trait::async_trait;
50use eyre::Result;
51use log::{debug, info, warn};
52
53use super::{
54    create_error_response, create_success_response, map_provider_error, EvmTransactionValidator,
55};
56
57#[allow(dead_code)]
58pub struct EvmRelayer<P, RR, NR, TR, J, S, TCS>
59where
60    P: EvmProviderTrait + Send + Sync,
61    RR: Repository<RelayerRepoModel, String> + RelayerRepository + Send + Sync + 'static,
62    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
63    TR: Repository<TransactionRepoModel, String> + TransactionRepository + Send + Sync + 'static,
64    J: JobProducerTrait + Send + Sync + 'static,
65    S: DataSignerTrait + Send + Sync + 'static,
66{
67    relayer: RelayerRepoModel,
68    signer: S,
69    network: EvmNetwork,
70    provider: P,
71    relayer_repository: Arc<RR>,
72    network_repository: Arc<NR>,
73    transaction_repository: Arc<TR>,
74    job_producer: Arc<J>,
75    transaction_counter_service: Arc<TCS>,
76}
77
78#[allow(clippy::too_many_arguments)]
79impl<P, RR, NR, TR, J, S, TCS> EvmRelayer<P, RR, NR, TR, J, S, TCS>
80where
81    P: EvmProviderTrait + Send + Sync,
82    RR: Repository<RelayerRepoModel, String> + RelayerRepository + Send + Sync + 'static,
83    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
84    TR: Repository<TransactionRepoModel, String> + TransactionRepository + Send + Sync + 'static,
85    J: JobProducerTrait + Send + Sync + 'static,
86    S: DataSignerTrait + Send + Sync + 'static,
87    TCS: TransactionCounterServiceTrait + Send + Sync + 'static,
88{
89    /// Constructs a new `EvmRelayer` instance.
90    ///
91    /// # Arguments
92    ///
93    /// * `relayer` - The relayer's data model.
94    /// * `signer` - The EVM signer for signing data and transactions.
95    /// * `provider` - The EVM provider for blockchain interactions.
96    /// * `network` - The EVM network configuration.
97    /// * `relayer_repository` - The repository for relayer storage.
98    /// * `transaction_repository` - The repository for transaction storage.
99    /// * `transaction_counter_service` - The service for managing transaction nonces.
100    /// * `job_producer` - The job producer for creating transaction jobs.
101    ///
102    /// # Returns
103    ///
104    /// A `Result` containing the new `EvmRelayer` instance or a `RelayerError`
105    pub fn new(
106        relayer: RelayerRepoModel,
107        signer: S,
108        provider: P,
109        network: EvmNetwork,
110        relayer_repository: Arc<RR>,
111        network_repository: Arc<NR>,
112        transaction_repository: Arc<TR>,
113        transaction_counter_service: Arc<TCS>,
114        job_producer: Arc<J>,
115    ) -> Result<Self, RelayerError> {
116        Ok(Self {
117            relayer,
118            signer,
119            network,
120            provider,
121            relayer_repository,
122            network_repository,
123            transaction_repository,
124            transaction_counter_service,
125            job_producer,
126        })
127    }
128
129    /// Synchronizes the nonce with the blockchain.
130    ///
131    /// # Returns
132    ///
133    /// A `Result` indicating success or a `RelayerError` if the operation fails.
134    async fn sync_nonce(&self) -> Result<(), RelayerError> {
135        let on_chain_nonce = self
136            .provider
137            .get_transaction_count(&self.relayer.address)
138            .await
139            .map_err(|e| RelayerError::ProviderError(e.to_string()))?;
140
141        let transaction_counter_nonce = self
142            .transaction_counter_service
143            .get()
144            .await
145            .unwrap_or(Some(0))
146            .unwrap_or(0);
147
148        let nonce = std::cmp::max(on_chain_nonce, transaction_counter_nonce);
149
150        debug!(
151            "Relayer: {} - On-chain nonce: {}, Transaction counter nonce: {}",
152            self.relayer.id, on_chain_nonce, transaction_counter_nonce
153        );
154
155        info!("Setting nonce: {} for relayer: {}", nonce, self.relayer.id);
156
157        self.transaction_counter_service.set(nonce).await?;
158
159        Ok(())
160    }
161
162    /// Validates the RPC connection to the blockchain provider.
163    ///
164    /// # Returns
165    ///
166    /// A `Result` indicating success or a `RelayerError` if the operation fails.
167    async fn validate_rpc(&self) -> Result<(), RelayerError> {
168        self.provider
169            .health_check()
170            .await
171            .map_err(|e| RelayerError::ProviderError(e.to_string()))?;
172
173        Ok(())
174    }
175
176    /// Initiates transaction cancellation via the job queue system.
177    ///
178    /// # Arguments
179    ///
180    /// * `transaction` - The transaction model to cancel.
181    ///
182    /// # Returns
183    ///
184    /// A `Result` indicating success or a `RelayerError` if the job creation fails.
185    async fn cancel_transaction_via_job(
186        &self,
187        transaction: TransactionRepoModel,
188    ) -> Result<(), RelayerError> {
189        use crate::jobs::TransactionSend;
190
191        let cancel_job = TransactionSend::cancel(
192            transaction.id.clone(),
193            transaction.relayer_id.clone(),
194            "Cancelled via delete_pending_transactions".to_string(),
195        );
196
197        self.job_producer
198            .produce_submit_transaction_job(cancel_job, None)
199            .await
200            .map_err(RelayerError::from)?;
201
202        Ok(())
203    }
204}
205
206// Define a concrete type alias for common usage
207pub type DefaultEvmRelayer<J, T, RR, NR, TCR> =
208    EvmRelayer<EvmProvider, RR, NR, T, J, EvmSigner, TransactionCounterService<TCR>>;
209
210#[async_trait]
211impl<P, RR, NR, TR, J, S, TCS> Relayer for EvmRelayer<P, RR, NR, TR, J, S, TCS>
212where
213    P: EvmProviderTrait + Send + Sync,
214    RR: Repository<RelayerRepoModel, String> + RelayerRepository + Send + Sync + 'static,
215    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
216    TR: Repository<TransactionRepoModel, String> + TransactionRepository + Send + Sync + 'static,
217    J: JobProducerTrait + Send + Sync + 'static,
218    S: DataSignerTrait + Send + Sync + 'static,
219    TCS: TransactionCounterServiceTrait + Send + Sync + 'static,
220{
221    /// Processes a transaction request and creates a job for it.
222    ///
223    /// # Arguments
224    ///
225    /// * `network_transaction` - The network transaction request to process.
226    ///
227    /// # Returns
228    ///
229    /// A `Result` containing the `TransactionRepoModel` or a `RelayerError`.
230    async fn process_transaction_request(
231        &self,
232        network_transaction: NetworkTransactionRequest,
233    ) -> Result<TransactionRepoModel, RelayerError> {
234        let network_model = self
235            .network_repository
236            .get_by_name(NetworkType::Evm, &self.relayer.network)
237            .await?
238            .ok_or_else(|| {
239                RelayerError::NetworkConfiguration(format!(
240                    "Network {} not found",
241                    self.relayer.network
242                ))
243            })?;
244        let transaction =
245            TransactionRepoModel::try_from((&network_transaction, &self.relayer, &network_model))?;
246
247        self.transaction_repository
248            .create(transaction.clone())
249            .await
250            .map_err(|e| RepositoryError::TransactionFailure(e.to_string()))?;
251
252        self.job_producer
253            .produce_transaction_request_job(
254                TransactionRequest::new(transaction.id.clone(), transaction.relayer_id.clone()),
255                None,
256            )
257            .await?;
258
259        Ok(transaction)
260    }
261
262    /// Retrieves the balance of the relayer's address.
263    ///
264    /// # Returns
265    ///
266    /// A `Result` containing the `BalanceResponse` or a `RelayerError`.
267    async fn get_balance(&self) -> Result<BalanceResponse, RelayerError> {
268        let balance: u128 = self
269            .provider
270            .get_balance(&self.relayer.address)
271            .await
272            .map_err(|e| RelayerError::ProviderError(e.to_string()))?
273            .try_into()
274            .map_err(|_| {
275                RelayerError::ProviderError("Failed to convert balance to u128".to_string())
276            })?;
277
278        Ok(BalanceResponse {
279            balance,
280            unit: EVM_SMALLEST_UNIT_NAME.to_string(),
281        })
282    }
283
284    /// Gets the status of the relayer.
285    ///
286    /// # Returns
287    ///
288    /// A `Result` containing a boolean indicating the status or a `RelayerError`.
289    async fn get_status(&self) -> Result<RelayerStatus, RelayerError> {
290        let relayer_model = &self.relayer;
291
292        let nonce_u256 = self
293            .provider
294            .get_transaction_count(&relayer_model.address)
295            .await
296            .map_err(|e| RelayerError::ProviderError(format!("Failed to get nonce: {}", e)))?;
297        let nonce_str = nonce_u256.to_string();
298
299        let balance_response = self.get_balance().await?;
300
301        let pending_statuses = [TransactionStatus::Pending, TransactionStatus::Submitted];
302        let pending_transactions = self
303            .transaction_repository
304            .find_by_status(&relayer_model.id, &pending_statuses[..])
305            .await
306            .map_err(RelayerError::from)?;
307        let pending_transactions_count = pending_transactions.len() as u64;
308
309        let confirmed_statuses = [TransactionStatus::Confirmed];
310        let confirmed_transactions = self
311            .transaction_repository
312            .find_by_status(&relayer_model.id, &confirmed_statuses[..])
313            .await
314            .map_err(RelayerError::from)?;
315
316        let last_confirmed_transaction_timestamp = confirmed_transactions
317            .iter()
318            .filter_map(|tx| tx.confirmed_at.as_ref())
319            .max()
320            .cloned();
321
322        Ok(RelayerStatus::Evm {
323            balance: balance_response.balance.to_string(),
324            pending_transactions_count,
325            last_confirmed_transaction_timestamp,
326            system_disabled: relayer_model.system_disabled,
327            paused: relayer_model.paused,
328            nonce: nonce_str,
329        })
330    }
331
332    /// Deletes pending transactions.
333    ///
334    /// # Returns
335    ///
336    /// A `Result` containing a `DeletePendingTransactionsResponse` with details
337    /// about which transactions were cancelled and which failed, or a `RelayerError`.
338    async fn delete_pending_transactions(
339        &self,
340    ) -> Result<DeletePendingTransactionsResponse, RelayerError> {
341        let pending_statuses = [
342            TransactionStatus::Pending,
343            TransactionStatus::Sent,
344            TransactionStatus::Submitted,
345        ];
346
347        // Get all pending transactions
348        let pending_transactions = self
349            .transaction_repository
350            .find_by_status(&self.relayer.id, &pending_statuses[..])
351            .await
352            .map_err(RelayerError::from)?;
353
354        let transaction_count = pending_transactions.len();
355
356        if transaction_count == 0 {
357            info!(
358                "No pending transactions found for relayer: {}",
359                self.relayer.id
360            );
361            return Ok(DeletePendingTransactionsResponse {
362                queued_for_cancellation_transaction_ids: vec![],
363                failed_to_queue_transaction_ids: vec![],
364                total_processed: 0,
365            });
366        }
367
368        info!(
369            "Processing {} pending transactions for relayer: {}",
370            transaction_count, self.relayer.id
371        );
372
373        let mut cancelled_transaction_ids = Vec::new();
374        let mut failed_transaction_ids = Vec::new();
375
376        // Process all pending transactions using the proper cancellation logic via job queue
377        for transaction in pending_transactions {
378            match self.cancel_transaction_via_job(transaction.clone()).await {
379                Ok(_) => {
380                    cancelled_transaction_ids.push(transaction.id.clone());
381                    info!(
382                        "Initiated cancellation for transaction {} with status {:?} for relayer {}",
383                        transaction.id, transaction.status, self.relayer.id
384                    );
385                }
386                Err(e) => {
387                    failed_transaction_ids.push(transaction.id.clone());
388                    warn!(
389                        "Failed to cancel transaction {} for relayer {}: {}",
390                        transaction.id, self.relayer.id, e
391                    );
392                }
393            }
394        }
395
396        let total_processed = cancelled_transaction_ids.len() + failed_transaction_ids.len();
397
398        info!("Completed processing pending transactions for relayer {}: {} queued for cancellation, {} failed to queue",
399              self.relayer.id, cancelled_transaction_ids.len(), failed_transaction_ids.len());
400
401        Ok(DeletePendingTransactionsResponse {
402            queued_for_cancellation_transaction_ids: cancelled_transaction_ids,
403            failed_to_queue_transaction_ids: failed_transaction_ids,
404            total_processed: total_processed as u32,
405        })
406    }
407
408    /// Signs data using the relayer's signer.
409    ///
410    /// # Arguments
411    ///
412    /// * `request` - The request containing the data to sign.
413    ///
414    /// # Returns
415    ///
416    /// A `Result` containing the `SignDataResponse` or a `RelayerError`.
417    async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, RelayerError> {
418        let result = self.signer.sign_data(request).await?;
419
420        Ok(result)
421    }
422
423    /// Signs typed data using the relayer's signer.
424    ///
425    /// # Arguments
426    ///
427    /// * `request` - The request containing the typed data to sign.
428    ///
429    /// # Returns
430    ///
431    /// A `Result` containing the `SignDataResponse` or a `RelayerError`.
432    async fn sign_typed_data(
433        &self,
434        request: SignTypedDataRequest,
435    ) -> Result<SignDataResponse, RelayerError> {
436        let result = self.signer.sign_typed_data(request).await?;
437
438        Ok(result)
439    }
440
441    /// Handles a JSON-RPC request.
442    ///
443    /// # Arguments
444    ///
445    /// * `request` - The JSON-RPC request to handle.
446    ///
447    /// # Returns
448    ///
449    /// A `Result` containing the `JsonRpcResponse` or a `RelayerError`.
450    async fn rpc(
451        &self,
452        request: JsonRpcRequest<NetworkRpcRequest>,
453    ) -> Result<JsonRpcResponse<NetworkRpcResult>, RelayerError> {
454        let evm_request = match request.params {
455            NetworkRpcRequest::Evm(evm_req) => evm_req,
456            _ => {
457                return Ok(create_error_response(
458                    request.id,
459                    RpcErrorCodes::INVALID_PARAMS,
460                    "Invalid params",
461                    "Expected EVM network request",
462                ))
463            }
464        };
465
466        // Parse method and params from the EVM request
467        let (method, params_json) = match evm_request {
468            crate::models::EvmRpcRequest::GenericRpcRequest { method, params } => {
469                (method, serde_json::Value::String(params))
470            }
471            crate::models::EvmRpcRequest::RawRpcRequest { method, params } => (method, params),
472        };
473
474        // Forward the RPC call to the provider
475        match self.provider.raw_request_dyn(&method, params_json).await {
476            Ok(result_value) => Ok(create_success_response(request.id, result_value)),
477            Err(provider_error) => {
478                let (error_code, error_message) = map_provider_error(&provider_error);
479                Ok(create_error_response(
480                    request.id,
481                    error_code,
482                    error_message,
483                    &provider_error.to_string(),
484                ))
485            }
486        }
487    }
488
489    /// Validates that the relayer's balance meets the minimum required balance.
490    ///
491    /// # Returns
492    ///
493    /// A `Result` indicating success or a `RelayerError` if the balance is insufficient.
494    async fn validate_min_balance(&self) -> Result<(), RelayerError> {
495        let policy = self.relayer.policies.get_evm_policy();
496        EvmTransactionValidator::init_balance_validation(
497            &self.relayer.address,
498            &policy,
499            &self.provider,
500        )
501        .await
502        .map_err(|e| RelayerError::InsufficientBalanceError(e.to_string()))?;
503
504        Ok(())
505    }
506
507    /// Initializes the relayer by performing necessary checks and synchronizations.
508    ///
509    /// # Returns
510    ///
511    /// A `Result` indicating success or a `RelayerError` if any initialization step fails.
512    async fn initialize_relayer(&self) -> Result<(), RelayerError> {
513        info!("Initializing relayer: {}", self.relayer.id);
514        let nonce_sync_result = self.sync_nonce().await;
515        let validate_rpc_result = self.validate_rpc().await;
516        let validate_min_balance_result = self.validate_min_balance().await;
517
518        // disable relayer if any check fails
519        if nonce_sync_result.is_err()
520            || validate_rpc_result.is_err()
521            || validate_min_balance_result.is_err()
522        {
523            let reason = vec![
524                nonce_sync_result
525                    .err()
526                    .map(|e| format!("Nonce sync failed: {}", e)),
527                validate_rpc_result
528                    .err()
529                    .map(|e| format!("RPC validation failed: {}", e)),
530                validate_min_balance_result
531                    .err()
532                    .map(|e| format!("Balance check failed: {}", e)),
533            ]
534            .into_iter()
535            .flatten()
536            .collect::<Vec<String>>()
537            .join(", ");
538
539            warn!("Disabling relayer: {} due to: {}", self.relayer.id, reason);
540            let updated_relayer = self
541                .relayer_repository
542                .disable_relayer(self.relayer.id.clone())
543                .await?;
544            if let Some(notification_id) = &self.relayer.notification_id {
545                self.job_producer
546                    .produce_send_notification_job(
547                        produce_relayer_disabled_payload(
548                            notification_id,
549                            &updated_relayer,
550                            &reason,
551                        ),
552                        None,
553                    )
554                    .await?;
555            }
556        }
557        Ok(())
558    }
559
560    async fn sign_transaction(
561        &self,
562        _request: &SignTransactionRequest,
563    ) -> Result<SignTransactionExternalResponse, RelayerError> {
564        Err(RelayerError::NotSupported(
565            "Transaction signing not supported for EVM".to_string(),
566        ))
567    }
568}
569
570#[cfg(test)]
571mod tests {
572    use super::*;
573    use crate::{
574        jobs::MockJobProducerTrait,
575        models::{
576            EvmRpcRequest, EvmRpcResult, JsonRpcId, NetworkRepoModel, NetworkType,
577            RelayerEvmPolicy, RelayerNetworkPolicy, RepositoryError, SignerError,
578            TransactionStatus, U256,
579        },
580        repositories::{MockNetworkRepository, MockRelayerRepository, MockTransactionRepository},
581        services::{MockEvmProviderTrait, MockTransactionCounterServiceTrait, ProviderError},
582    };
583    use mockall::predicate::*;
584    use std::future::ready;
585
586    mockall::mock! {
587        pub DataSigner {}
588
589        #[async_trait]
590        impl DataSignerTrait for DataSigner {
591            async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, SignerError>;
592            async fn sign_typed_data(&self, request: SignTypedDataRequest) -> Result<SignDataResponse, SignerError>;
593        }
594    }
595
596    fn create_test_evm_network() -> EvmNetwork {
597        EvmNetwork {
598            network: "mainnet".to_string(),
599            rpc_urls: vec!["https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY".to_string()],
600            explorer_urls: None,
601            average_blocktime_ms: 12000,
602            is_testnet: false,
603            tags: vec!["mainnet".to_string()],
604            chain_id: 1,
605            required_confirmations: 1,
606            features: vec!["eip1559".to_string()],
607            symbol: "ETH".to_string(),
608        }
609    }
610
611    fn create_test_network_repo_model() -> NetworkRepoModel {
612        use crate::config::{EvmNetworkConfig, NetworkConfigCommon};
613
614        let config = EvmNetworkConfig {
615            common: NetworkConfigCommon {
616                network: "mainnet".to_string(),
617                from: None,
618                rpc_urls: Some(vec![
619                    "https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY".to_string()
620                ]),
621                explorer_urls: None,
622                average_blocktime_ms: Some(12000),
623                is_testnet: Some(false),
624                tags: Some(vec!["mainnet".to_string()]),
625            },
626            chain_id: Some(1),
627            required_confirmations: Some(1),
628            features: Some(vec!["eip1559".to_string()]),
629            symbol: Some("ETH".to_string()),
630        };
631
632        NetworkRepoModel::new_evm(config)
633    }
634
635    fn create_test_relayer() -> RelayerRepoModel {
636        RelayerRepoModel {
637            id: "test-relayer-id".to_string(),
638            name: "Test Relayer".to_string(),
639            network: "mainnet".to_string(), // Changed from "1" to "mainnet"
640            address: "0xSender".to_string(),
641            paused: false,
642            system_disabled: false,
643            signer_id: "test-signer-id".to_string(),
644            notification_id: Some("test-notification-id".to_string()),
645            policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
646                min_balance: Some(100000000000000000u128), // 0.1 ETH
647                whitelist_receivers: Some(vec!["0xRecipient".to_string()]),
648                gas_price_cap: Some(100000000000), // 100 Gwei
649                eip1559_pricing: Some(true),
650                private_transactions: Some(false),
651                gas_limit_estimation: Some(true),
652            }),
653            network_type: NetworkType::Evm,
654            custom_rpc_urls: None,
655        }
656    }
657
658    fn setup_mocks() -> (
659        MockEvmProviderTrait,
660        MockRelayerRepository,
661        MockNetworkRepository,
662        MockTransactionRepository,
663        MockJobProducerTrait,
664        MockDataSigner,
665        MockTransactionCounterServiceTrait,
666    ) {
667        (
668            MockEvmProviderTrait::new(),
669            MockRelayerRepository::new(),
670            MockNetworkRepository::new(),
671            MockTransactionRepository::new(),
672            MockJobProducerTrait::new(),
673            MockDataSigner::new(),
674            MockTransactionCounterServiceTrait::new(),
675        )
676    }
677
678    #[tokio::test]
679    async fn test_get_balance() {
680        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
681            setup_mocks();
682        let relayer_model = create_test_relayer();
683
684        provider
685            .expect_get_balance()
686            .with(eq("0xSender"))
687            .returning(|_| Box::pin(ready(Ok(U256::from(1000000000000000000u64))))); // 1 ETH
688
689        let relayer = EvmRelayer::new(
690            relayer_model,
691            signer,
692            provider,
693            create_test_evm_network(),
694            Arc::new(relayer_repo),
695            Arc::new(network_repo),
696            Arc::new(tx_repo),
697            Arc::new(counter),
698            Arc::new(job_producer),
699        )
700        .unwrap();
701
702        let balance = relayer.get_balance().await.unwrap();
703        assert_eq!(balance.balance, 1000000000000000000u128);
704        assert_eq!(balance.unit, EVM_SMALLEST_UNIT_NAME);
705    }
706
707    #[tokio::test]
708    async fn test_process_transaction_request() {
709        let (
710            provider,
711            relayer_repo,
712            mut network_repo,
713            mut tx_repo,
714            mut job_producer,
715            signer,
716            counter,
717        ) = setup_mocks();
718        let relayer_model = create_test_relayer();
719
720        let network_tx = NetworkTransactionRequest::Evm(crate::models::EvmTransactionRequest {
721            to: Some("0xRecipient".to_string()),
722            value: U256::from(1000000000000000000u64),
723            data: Some("0xData".to_string()),
724            gas_limit: Some(21000),
725            gas_price: Some(20000000000),
726            max_fee_per_gas: None,
727            max_priority_fee_per_gas: None,
728            speed: None,
729            valid_until: None,
730        });
731
732        network_repo
733            .expect_get_by_name()
734            .with(eq(NetworkType::Evm), eq("mainnet"))
735            .returning(|_, _| Ok(Some(create_test_network_repo_model())));
736
737        tx_repo.expect_create().returning(Ok);
738        job_producer
739            .expect_produce_transaction_request_job()
740            .returning(|_, _| Box::pin(ready(Ok(()))));
741
742        let relayer = EvmRelayer::new(
743            relayer_model,
744            signer,
745            provider,
746            create_test_evm_network(),
747            Arc::new(relayer_repo),
748            Arc::new(network_repo),
749            Arc::new(tx_repo),
750            Arc::new(counter),
751            Arc::new(job_producer),
752        )
753        .unwrap();
754
755        let result = relayer.process_transaction_request(network_tx).await;
756        assert!(result.is_ok());
757    }
758
759    #[tokio::test]
760    async fn test_validate_min_balance_sufficient() {
761        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
762            setup_mocks();
763        let relayer_model = create_test_relayer();
764
765        provider
766            .expect_get_balance()
767            .returning(|_| Box::pin(ready(Ok(U256::from(200000000000000000u64))))); // 0.2 ETH > min_balance
768
769        let relayer = EvmRelayer::new(
770            relayer_model,
771            signer,
772            provider,
773            create_test_evm_network(),
774            Arc::new(relayer_repo),
775            Arc::new(network_repo),
776            Arc::new(tx_repo),
777            Arc::new(counter),
778            Arc::new(job_producer),
779        )
780        .unwrap();
781
782        let result = relayer.validate_min_balance().await;
783        assert!(result.is_ok());
784    }
785
786    #[tokio::test]
787    async fn test_validate_min_balance_insufficient() {
788        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
789            setup_mocks();
790        let relayer_model = create_test_relayer();
791
792        provider
793            .expect_get_balance()
794            .returning(|_| Box::pin(ready(Ok(U256::from(50000000000000000u64))))); // 0.05 ETH < min_balance
795
796        let relayer = EvmRelayer::new(
797            relayer_model,
798            signer,
799            provider,
800            create_test_evm_network(),
801            Arc::new(relayer_repo),
802            Arc::new(network_repo),
803            Arc::new(tx_repo),
804            Arc::new(counter),
805            Arc::new(job_producer),
806        )
807        .unwrap();
808
809        let result = relayer.validate_min_balance().await;
810        assert!(matches!(
811            result,
812            Err(RelayerError::InsufficientBalanceError(_))
813        ));
814    }
815
816    #[tokio::test]
817    async fn test_sync_nonce() {
818        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, mut counter) =
819            setup_mocks();
820        let relayer_model = create_test_relayer();
821
822        provider
823            .expect_get_transaction_count()
824            .returning(|_| Box::pin(ready(Ok(42u64))));
825
826        counter
827            .expect_set()
828            .returning(|_nonce| Box::pin(ready(Ok(()))));
829
830        counter
831            .expect_get()
832            .returning(|| Box::pin(ready(Ok(Some(42u64)))));
833
834        let relayer = EvmRelayer::new(
835            relayer_model,
836            signer,
837            provider,
838            create_test_evm_network(),
839            Arc::new(relayer_repo),
840            Arc::new(network_repo),
841            Arc::new(tx_repo),
842            Arc::new(counter),
843            Arc::new(job_producer),
844        )
845        .unwrap();
846
847        let result = relayer.sync_nonce().await;
848        assert!(result.is_ok());
849    }
850
851    #[tokio::test]
852    async fn test_sync_nonce_lower_on_chain_nonce() {
853        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, mut counter) =
854            setup_mocks();
855        let relayer_model = create_test_relayer();
856
857        provider
858            .expect_get_transaction_count()
859            .returning(|_| Box::pin(ready(Ok(40u64))));
860
861        counter
862            .expect_set()
863            .with(eq(42u64))
864            .returning(|_nonce| Box::pin(ready(Ok(()))));
865
866        counter
867            .expect_get()
868            .returning(|| Box::pin(ready(Ok(Some(42u64)))));
869
870        let relayer = EvmRelayer::new(
871            relayer_model,
872            signer,
873            provider,
874            create_test_evm_network(),
875            Arc::new(relayer_repo),
876            Arc::new(network_repo),
877            Arc::new(tx_repo),
878            Arc::new(counter),
879            Arc::new(job_producer),
880        )
881        .unwrap();
882
883        let result = relayer.sync_nonce().await;
884        assert!(result.is_ok());
885    }
886
887    #[tokio::test]
888    async fn test_sync_nonce_lower_transaction_counter_nonce() {
889        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, mut counter) =
890            setup_mocks();
891        let relayer_model = create_test_relayer();
892
893        provider
894            .expect_get_transaction_count()
895            .returning(|_| Box::pin(ready(Ok(42u64))));
896
897        counter
898            .expect_set()
899            .with(eq(42u64))
900            .returning(|_nonce| Box::pin(ready(Ok(()))));
901
902        counter
903            .expect_get()
904            .returning(|| Box::pin(ready(Ok(Some(40u64)))));
905
906        let relayer = EvmRelayer::new(
907            relayer_model,
908            signer,
909            provider,
910            create_test_evm_network(),
911            Arc::new(relayer_repo),
912            Arc::new(network_repo),
913            Arc::new(tx_repo),
914            Arc::new(counter),
915            Arc::new(job_producer),
916        )
917        .unwrap();
918
919        let result = relayer.sync_nonce().await;
920        assert!(result.is_ok());
921    }
922
923    #[tokio::test]
924    async fn test_validate_rpc() {
925        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
926            setup_mocks();
927        let relayer_model = create_test_relayer();
928
929        provider
930            .expect_health_check()
931            .returning(|| Box::pin(ready(Ok(true))));
932
933        let relayer = EvmRelayer::new(
934            relayer_model,
935            signer,
936            provider,
937            create_test_evm_network(),
938            Arc::new(relayer_repo),
939            Arc::new(network_repo),
940            Arc::new(tx_repo),
941            Arc::new(counter),
942            Arc::new(job_producer),
943        )
944        .unwrap();
945
946        let result = relayer.validate_rpc().await;
947        assert!(result.is_ok());
948    }
949
950    #[tokio::test]
951    async fn test_get_status_success() {
952        let (mut provider, relayer_repo, network_repo, mut tx_repo, job_producer, signer, counter) =
953            setup_mocks();
954        let relayer_model = create_test_relayer();
955
956        provider
957            .expect_get_transaction_count()
958            .returning(|_| Box::pin(ready(Ok(10u64))))
959            .once();
960        provider
961            .expect_get_balance()
962            .returning(|_| Box::pin(ready(Ok(U256::from(1000000000000000000u64)))))
963            .once();
964
965        let pending_txs_clone = vec![];
966        tx_repo
967            .expect_find_by_status()
968            .withf(|relayer_id, statuses| {
969                relayer_id == "test-relayer-id"
970                    && statuses == [TransactionStatus::Pending, TransactionStatus::Submitted]
971            })
972            .returning(move |_, _| {
973                Ok(pending_txs_clone.clone()) as Result<Vec<TransactionRepoModel>, RepositoryError>
974            })
975            .once();
976
977        let confirmed_txs_clone = vec![
978            TransactionRepoModel {
979                id: "tx1".to_string(),
980                relayer_id: relayer_model.id.clone(),
981                status: TransactionStatus::Confirmed,
982                confirmed_at: Some("2023-01-01T12:00:00Z".to_string()),
983                ..TransactionRepoModel::default()
984            },
985            TransactionRepoModel {
986                id: "tx2".to_string(),
987                relayer_id: relayer_model.id.clone(),
988                status: TransactionStatus::Confirmed,
989                confirmed_at: Some("2023-01-01T10:00:00Z".to_string()),
990                ..TransactionRepoModel::default()
991            },
992        ];
993        tx_repo
994            .expect_find_by_status()
995            .withf(|relayer_id, statuses| {
996                relayer_id == "test-relayer-id" && statuses == [TransactionStatus::Confirmed]
997            })
998            .returning(move |_, _| {
999                Ok(confirmed_txs_clone.clone())
1000                    as Result<Vec<TransactionRepoModel>, RepositoryError>
1001            })
1002            .once();
1003
1004        let relayer = EvmRelayer::new(
1005            relayer_model.clone(),
1006            signer,
1007            provider,
1008            create_test_evm_network(),
1009            Arc::new(relayer_repo),
1010            Arc::new(network_repo),
1011            Arc::new(tx_repo),
1012            Arc::new(counter),
1013            Arc::new(job_producer),
1014        )
1015        .unwrap();
1016
1017        let status = relayer.get_status().await.unwrap();
1018
1019        match status {
1020            RelayerStatus::Evm {
1021                balance,
1022                pending_transactions_count,
1023                last_confirmed_transaction_timestamp,
1024                system_disabled,
1025                paused,
1026                nonce,
1027            } => {
1028                assert_eq!(balance, "1000000000000000000");
1029                assert_eq!(pending_transactions_count, 0);
1030                assert_eq!(
1031                    last_confirmed_transaction_timestamp,
1032                    Some("2023-01-01T12:00:00Z".to_string())
1033                );
1034                assert_eq!(system_disabled, relayer_model.system_disabled);
1035                assert_eq!(paused, relayer_model.paused);
1036                assert_eq!(nonce, "10");
1037            }
1038            _ => panic!("Expected EVM RelayerStatus"),
1039        }
1040    }
1041
1042    #[tokio::test]
1043    async fn test_get_status_provider_nonce_error() {
1044        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1045            setup_mocks();
1046        let relayer_model = create_test_relayer();
1047
1048        provider.expect_get_transaction_count().returning(|_| {
1049            Box::pin(ready(Err(ProviderError::Other(
1050                "Nonce fetch failed".to_string(),
1051            ))))
1052        });
1053
1054        let relayer = EvmRelayer::new(
1055            relayer_model.clone(),
1056            signer,
1057            provider,
1058            create_test_evm_network(),
1059            Arc::new(relayer_repo),
1060            Arc::new(network_repo),
1061            Arc::new(tx_repo),
1062            Arc::new(counter),
1063            Arc::new(job_producer),
1064        )
1065        .unwrap();
1066
1067        let result = relayer.get_status().await;
1068        assert!(result.is_err());
1069        match result.err().unwrap() {
1070            RelayerError::ProviderError(msg) => assert!(msg.contains("Failed to get nonce")),
1071            _ => panic!("Expected ProviderError for nonce failure"),
1072        }
1073    }
1074
1075    #[tokio::test]
1076    async fn test_get_status_repository_pending_error() {
1077        let (mut provider, relayer_repo, network_repo, mut tx_repo, job_producer, signer, counter) =
1078            setup_mocks();
1079        let relayer_model = create_test_relayer();
1080
1081        provider
1082            .expect_get_transaction_count()
1083            .returning(|_| Box::pin(ready(Ok(10u64))));
1084        provider
1085            .expect_get_balance()
1086            .returning(|_| Box::pin(ready(Ok(U256::from(1000000000000000000u64)))));
1087
1088        tx_repo
1089            .expect_find_by_status()
1090            .withf(|relayer_id, statuses| {
1091                relayer_id == "test-relayer-id"
1092                    && statuses == [TransactionStatus::Pending, TransactionStatus::Submitted]
1093            })
1094            .returning(|_, _| {
1095                Err(RepositoryError::Unknown("DB down".to_string()))
1096                    as Result<Vec<TransactionRepoModel>, RepositoryError>
1097            })
1098            .once();
1099
1100        let relayer = EvmRelayer::new(
1101            relayer_model.clone(),
1102            signer,
1103            provider,
1104            create_test_evm_network(),
1105            Arc::new(relayer_repo),
1106            Arc::new(network_repo),
1107            Arc::new(tx_repo),
1108            Arc::new(counter),
1109            Arc::new(job_producer),
1110        )
1111        .unwrap();
1112
1113        let result = relayer.get_status().await;
1114        assert!(result.is_err());
1115        match result.err().unwrap() {
1116            // Remember our From<RepositoryError> for RelayerError maps to NetworkConfiguration
1117            RelayerError::NetworkConfiguration(msg) => assert!(msg.contains("DB down")),
1118            _ => panic!("Expected NetworkConfiguration error for repo failure"),
1119        }
1120    }
1121
1122    #[tokio::test]
1123    async fn test_get_status_no_confirmed_transactions() {
1124        let (mut provider, relayer_repo, network_repo, mut tx_repo, job_producer, signer, counter) =
1125            setup_mocks();
1126        let relayer_model = create_test_relayer();
1127
1128        provider
1129            .expect_get_transaction_count()
1130            .returning(|_| Box::pin(ready(Ok(10u64))));
1131        provider
1132            .expect_get_balance()
1133            .returning(|_| Box::pin(ready(Ok(U256::from(1000000000000000000u64)))));
1134        provider
1135            .expect_health_check()
1136            .returning(|| Box::pin(ready(Ok(true))));
1137
1138        let pending_txs_empty_clone = vec![];
1139        tx_repo
1140            .expect_find_by_status()
1141            .withf(|relayer_id, statuses| {
1142                relayer_id == "test-relayer-id"
1143                    && statuses == [TransactionStatus::Pending, TransactionStatus::Submitted]
1144            })
1145            .returning(move |_, _| {
1146                Ok(pending_txs_empty_clone.clone())
1147                    as Result<Vec<TransactionRepoModel>, RepositoryError>
1148            })
1149            .once();
1150
1151        let confirmed_txs_empty_clone = vec![];
1152        tx_repo
1153            .expect_find_by_status()
1154            .withf(|relayer_id, statuses| {
1155                relayer_id == "test-relayer-id" && statuses == [TransactionStatus::Confirmed]
1156            })
1157            .returning(move |_, _| {
1158                Ok(confirmed_txs_empty_clone.clone())
1159                    as Result<Vec<TransactionRepoModel>, RepositoryError>
1160            })
1161            .once();
1162
1163        let relayer = EvmRelayer::new(
1164            relayer_model.clone(),
1165            signer,
1166            provider,
1167            create_test_evm_network(),
1168            Arc::new(relayer_repo),
1169            Arc::new(network_repo),
1170            Arc::new(tx_repo),
1171            Arc::new(counter),
1172            Arc::new(job_producer),
1173        )
1174        .unwrap();
1175
1176        let status = relayer.get_status().await.unwrap();
1177        match status {
1178            RelayerStatus::Evm {
1179                balance,
1180                pending_transactions_count,
1181                last_confirmed_transaction_timestamp,
1182                system_disabled,
1183                paused,
1184                nonce,
1185            } => {
1186                assert_eq!(balance, "1000000000000000000");
1187                assert_eq!(pending_transactions_count, 0);
1188                assert_eq!(last_confirmed_transaction_timestamp, None);
1189                assert_eq!(system_disabled, relayer_model.system_disabled);
1190                assert_eq!(paused, relayer_model.paused);
1191                assert_eq!(nonce, "10");
1192            }
1193            _ => panic!("Expected EVM RelayerStatus"),
1194        }
1195    }
1196
1197    #[tokio::test]
1198    async fn test_cancel_transaction_via_job_success() {
1199        let (provider, relayer_repo, network_repo, tx_repo, mut job_producer, signer, counter) =
1200            setup_mocks();
1201        let relayer_model = create_test_relayer();
1202
1203        let test_transaction = TransactionRepoModel {
1204            id: "test-tx-id".to_string(),
1205            relayer_id: relayer_model.id.clone(),
1206            status: TransactionStatus::Pending,
1207            ..TransactionRepoModel::default()
1208        };
1209
1210        job_producer
1211            .expect_produce_submit_transaction_job()
1212            .withf(|job, delay| {
1213                matches!(job.command, crate::jobs::TransactionCommand::Cancel { ref reason }
1214                    if job.transaction_id == "test-tx-id"
1215                    && job.relayer_id == "test-relayer-id"
1216                    && reason == "Cancelled via delete_pending_transactions")
1217                    && delay.is_none()
1218            })
1219            .returning(|_, _| Box::pin(ready(Ok(()))))
1220            .once();
1221
1222        let relayer = EvmRelayer::new(
1223            relayer_model,
1224            signer,
1225            provider,
1226            create_test_evm_network(),
1227            Arc::new(relayer_repo),
1228            Arc::new(network_repo),
1229            Arc::new(tx_repo),
1230            Arc::new(counter),
1231            Arc::new(job_producer),
1232        )
1233        .unwrap();
1234
1235        let result = relayer.cancel_transaction_via_job(test_transaction).await;
1236        assert!(result.is_ok());
1237    }
1238
1239    #[tokio::test]
1240    async fn test_cancel_transaction_via_job_failure() {
1241        let (provider, relayer_repo, network_repo, tx_repo, mut job_producer, signer, counter) =
1242            setup_mocks();
1243        let relayer_model = create_test_relayer();
1244
1245        let test_transaction = TransactionRepoModel {
1246            id: "test-tx-id".to_string(),
1247            relayer_id: relayer_model.id.clone(),
1248            status: TransactionStatus::Pending,
1249            ..TransactionRepoModel::default()
1250        };
1251
1252        job_producer
1253            .expect_produce_submit_transaction_job()
1254            .returning(|_, _| {
1255                Box::pin(ready(Err(crate::jobs::JobProducerError::QueueError(
1256                    "Queue is full".to_string(),
1257                ))))
1258            })
1259            .once();
1260
1261        let relayer = EvmRelayer::new(
1262            relayer_model,
1263            signer,
1264            provider,
1265            create_test_evm_network(),
1266            Arc::new(relayer_repo),
1267            Arc::new(network_repo),
1268            Arc::new(tx_repo),
1269            Arc::new(counter),
1270            Arc::new(job_producer),
1271        )
1272        .unwrap();
1273
1274        let result = relayer.cancel_transaction_via_job(test_transaction).await;
1275        assert!(result.is_err());
1276        match result.err().unwrap() {
1277            RelayerError::QueueError(_) => (),
1278            _ => panic!("Expected QueueError"),
1279        }
1280    }
1281
1282    #[tokio::test]
1283    async fn test_delete_pending_transactions_no_pending() {
1284        let (provider, relayer_repo, network_repo, mut tx_repo, job_producer, signer, counter) =
1285            setup_mocks();
1286        let relayer_model = create_test_relayer();
1287
1288        tx_repo
1289            .expect_find_by_status()
1290            .withf(|relayer_id, statuses| {
1291                relayer_id == "test-relayer-id"
1292                    && statuses
1293                        == [
1294                            TransactionStatus::Pending,
1295                            TransactionStatus::Sent,
1296                            TransactionStatus::Submitted,
1297                        ]
1298            })
1299            .returning(|_, _| Ok(vec![]))
1300            .once();
1301
1302        let relayer = EvmRelayer::new(
1303            relayer_model,
1304            signer,
1305            provider,
1306            create_test_evm_network(),
1307            Arc::new(relayer_repo),
1308            Arc::new(network_repo),
1309            Arc::new(tx_repo),
1310            Arc::new(counter),
1311            Arc::new(job_producer),
1312        )
1313        .unwrap();
1314
1315        let result = relayer.delete_pending_transactions().await.unwrap();
1316        assert_eq!(result.queued_for_cancellation_transaction_ids.len(), 0);
1317        assert_eq!(result.failed_to_queue_transaction_ids.len(), 0);
1318        assert_eq!(result.total_processed, 0);
1319    }
1320
1321    #[tokio::test]
1322    async fn test_delete_pending_transactions_all_successful() {
1323        let (provider, relayer_repo, network_repo, mut tx_repo, mut job_producer, signer, counter) =
1324            setup_mocks();
1325        let relayer_model = create_test_relayer();
1326
1327        let pending_transactions = vec![
1328            TransactionRepoModel {
1329                id: "tx1".to_string(),
1330                relayer_id: relayer_model.id.clone(),
1331                status: TransactionStatus::Pending,
1332                ..TransactionRepoModel::default()
1333            },
1334            TransactionRepoModel {
1335                id: "tx2".to_string(),
1336                relayer_id: relayer_model.id.clone(),
1337                status: TransactionStatus::Sent,
1338                ..TransactionRepoModel::default()
1339            },
1340            TransactionRepoModel {
1341                id: "tx3".to_string(),
1342                relayer_id: relayer_model.id.clone(),
1343                status: TransactionStatus::Submitted,
1344                ..TransactionRepoModel::default()
1345            },
1346        ];
1347
1348        tx_repo
1349            .expect_find_by_status()
1350            .withf(|relayer_id, statuses| {
1351                relayer_id == "test-relayer-id"
1352                    && statuses
1353                        == [
1354                            TransactionStatus::Pending,
1355                            TransactionStatus::Sent,
1356                            TransactionStatus::Submitted,
1357                        ]
1358            })
1359            .returning(move |_, _| Ok(pending_transactions.clone()))
1360            .once();
1361
1362        job_producer
1363            .expect_produce_submit_transaction_job()
1364            .returning(|_, _| Box::pin(ready(Ok(()))))
1365            .times(3);
1366
1367        let relayer = EvmRelayer::new(
1368            relayer_model,
1369            signer,
1370            provider,
1371            create_test_evm_network(),
1372            Arc::new(relayer_repo),
1373            Arc::new(network_repo),
1374            Arc::new(tx_repo),
1375            Arc::new(counter),
1376            Arc::new(job_producer),
1377        )
1378        .unwrap();
1379
1380        let result = relayer.delete_pending_transactions().await.unwrap();
1381        assert_eq!(result.queued_for_cancellation_transaction_ids.len(), 3);
1382        assert_eq!(result.failed_to_queue_transaction_ids.len(), 0);
1383        assert_eq!(result.total_processed, 3);
1384
1385        let expected_ids = vec!["tx1", "tx2", "tx3"];
1386        for id in expected_ids {
1387            assert!(result
1388                .queued_for_cancellation_transaction_ids
1389                .contains(&id.to_string()));
1390        }
1391    }
1392
1393    #[tokio::test]
1394    async fn test_delete_pending_transactions_partial_failures() {
1395        let (provider, relayer_repo, network_repo, mut tx_repo, mut job_producer, signer, counter) =
1396            setup_mocks();
1397        let relayer_model = create_test_relayer();
1398
1399        let pending_transactions = vec![
1400            TransactionRepoModel {
1401                id: "tx1".to_string(),
1402                relayer_id: relayer_model.id.clone(),
1403                status: TransactionStatus::Pending,
1404                ..TransactionRepoModel::default()
1405            },
1406            TransactionRepoModel {
1407                id: "tx2".to_string(),
1408                relayer_id: relayer_model.id.clone(),
1409                status: TransactionStatus::Sent,
1410                ..TransactionRepoModel::default()
1411            },
1412            TransactionRepoModel {
1413                id: "tx3".to_string(),
1414                relayer_id: relayer_model.id.clone(),
1415                status: TransactionStatus::Submitted,
1416                ..TransactionRepoModel::default()
1417            },
1418        ];
1419
1420        tx_repo
1421            .expect_find_by_status()
1422            .withf(|relayer_id, statuses| {
1423                relayer_id == "test-relayer-id"
1424                    && statuses
1425                        == [
1426                            TransactionStatus::Pending,
1427                            TransactionStatus::Sent,
1428                            TransactionStatus::Submitted,
1429                        ]
1430            })
1431            .returning(move |_, _| Ok(pending_transactions.clone()))
1432            .once();
1433
1434        // First job succeeds, second fails, third succeeds
1435        job_producer
1436            .expect_produce_submit_transaction_job()
1437            .returning(|_, _| Box::pin(ready(Ok(()))))
1438            .times(1);
1439        job_producer
1440            .expect_produce_submit_transaction_job()
1441            .returning(|_, _| {
1442                Box::pin(ready(Err(crate::jobs::JobProducerError::QueueError(
1443                    "Queue is full".to_string(),
1444                ))))
1445            })
1446            .times(1);
1447        job_producer
1448            .expect_produce_submit_transaction_job()
1449            .returning(|_, _| Box::pin(ready(Ok(()))))
1450            .times(1);
1451
1452        let relayer = EvmRelayer::new(
1453            relayer_model,
1454            signer,
1455            provider,
1456            create_test_evm_network(),
1457            Arc::new(relayer_repo),
1458            Arc::new(network_repo),
1459            Arc::new(tx_repo),
1460            Arc::new(counter),
1461            Arc::new(job_producer),
1462        )
1463        .unwrap();
1464
1465        let result = relayer.delete_pending_transactions().await.unwrap();
1466        assert_eq!(result.queued_for_cancellation_transaction_ids.len(), 2);
1467        assert_eq!(result.failed_to_queue_transaction_ids.len(), 1);
1468        assert_eq!(result.total_processed, 3);
1469    }
1470
1471    #[tokio::test]
1472    async fn test_delete_pending_transactions_repository_error() {
1473        let (provider, relayer_repo, network_repo, mut tx_repo, job_producer, signer, counter) =
1474            setup_mocks();
1475        let relayer_model = create_test_relayer();
1476
1477        tx_repo
1478            .expect_find_by_status()
1479            .withf(|relayer_id, statuses| {
1480                relayer_id == "test-relayer-id"
1481                    && statuses
1482                        == [
1483                            TransactionStatus::Pending,
1484                            TransactionStatus::Sent,
1485                            TransactionStatus::Submitted,
1486                        ]
1487            })
1488            .returning(|_, _| {
1489                Err(RepositoryError::Unknown(
1490                    "Database connection failed".to_string(),
1491                ))
1492            })
1493            .once();
1494
1495        let relayer = EvmRelayer::new(
1496            relayer_model,
1497            signer,
1498            provider,
1499            create_test_evm_network(),
1500            Arc::new(relayer_repo),
1501            Arc::new(network_repo),
1502            Arc::new(tx_repo),
1503            Arc::new(counter),
1504            Arc::new(job_producer),
1505        )
1506        .unwrap();
1507
1508        let result = relayer.delete_pending_transactions().await;
1509        assert!(result.is_err());
1510        match result.err().unwrap() {
1511            RelayerError::NetworkConfiguration(msg) => {
1512                assert!(msg.contains("Database connection failed"))
1513            }
1514            _ => panic!("Expected NetworkConfiguration error for repository failure"),
1515        }
1516    }
1517
1518    #[tokio::test]
1519    async fn test_delete_pending_transactions_all_failures() {
1520        let (provider, relayer_repo, network_repo, mut tx_repo, mut job_producer, signer, counter) =
1521            setup_mocks();
1522        let relayer_model = create_test_relayer();
1523
1524        let pending_transactions = vec![
1525            TransactionRepoModel {
1526                id: "tx1".to_string(),
1527                relayer_id: relayer_model.id.clone(),
1528                status: TransactionStatus::Pending,
1529                ..TransactionRepoModel::default()
1530            },
1531            TransactionRepoModel {
1532                id: "tx2".to_string(),
1533                relayer_id: relayer_model.id.clone(),
1534                status: TransactionStatus::Sent,
1535                ..TransactionRepoModel::default()
1536            },
1537        ];
1538
1539        tx_repo
1540            .expect_find_by_status()
1541            .withf(|relayer_id, statuses| {
1542                relayer_id == "test-relayer-id"
1543                    && statuses
1544                        == [
1545                            TransactionStatus::Pending,
1546                            TransactionStatus::Sent,
1547                            TransactionStatus::Submitted,
1548                        ]
1549            })
1550            .returning(move |_, _| Ok(pending_transactions.clone()))
1551            .once();
1552
1553        job_producer
1554            .expect_produce_submit_transaction_job()
1555            .returning(|_, _| {
1556                Box::pin(ready(Err(crate::jobs::JobProducerError::QueueError(
1557                    "Queue is full".to_string(),
1558                ))))
1559            })
1560            .times(2);
1561
1562        let relayer = EvmRelayer::new(
1563            relayer_model,
1564            signer,
1565            provider,
1566            create_test_evm_network(),
1567            Arc::new(relayer_repo),
1568            Arc::new(network_repo),
1569            Arc::new(tx_repo),
1570            Arc::new(counter),
1571            Arc::new(job_producer),
1572        )
1573        .unwrap();
1574
1575        let result = relayer.delete_pending_transactions().await.unwrap();
1576        assert_eq!(result.queued_for_cancellation_transaction_ids.len(), 0);
1577        assert_eq!(result.failed_to_queue_transaction_ids.len(), 2);
1578        assert_eq!(result.total_processed, 2);
1579
1580        let expected_failed_ids = vec!["tx1", "tx2"];
1581        for id in expected_failed_ids {
1582            assert!(result
1583                .failed_to_queue_transaction_ids
1584                .contains(&id.to_string()));
1585        }
1586    }
1587
1588    #[tokio::test]
1589    async fn test_rpc_eth_get_balance() {
1590        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1591            setup_mocks();
1592        let relayer_model = create_test_relayer();
1593
1594        provider
1595            .expect_raw_request_dyn()
1596            .withf(|method, params| {
1597                method == "eth_getBalance"
1598                    && params.as_str()
1599                        == Some(r#"["0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest"]"#)
1600            })
1601            .returning(|_, _| Box::pin(async { Ok(serde_json::json!("0xde0b6b3a7640000")) }));
1602
1603        let relayer = EvmRelayer::new(
1604            relayer_model,
1605            signer,
1606            provider,
1607            create_test_evm_network(),
1608            Arc::new(relayer_repo),
1609            Arc::new(network_repo),
1610            Arc::new(tx_repo),
1611            Arc::new(counter),
1612            Arc::new(job_producer),
1613        )
1614        .unwrap();
1615
1616        let request = JsonRpcRequest {
1617            jsonrpc: "2.0".to_string(),
1618            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
1619                method: "eth_getBalance".to_string(),
1620                params: r#"["0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest"]"#.to_string(),
1621            }),
1622            id: Some(JsonRpcId::Number(1)),
1623        };
1624
1625        let response = relayer.rpc(request).await.unwrap();
1626        assert!(response.error.is_none());
1627        assert!(response.result.is_some());
1628
1629        if let Some(NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(result))) = response.result {
1630            assert_eq!(result, serde_json::json!("0xde0b6b3a7640000")); // 1 ETH in hex
1631        }
1632    }
1633
1634    #[tokio::test]
1635    async fn test_rpc_eth_block_number() {
1636        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1637            setup_mocks();
1638        let relayer_model = create_test_relayer();
1639
1640        provider
1641            .expect_raw_request_dyn()
1642            .withf(|method, params| method == "eth_blockNumber" && params.as_str() == Some("[]"))
1643            .returning(|_, _| Box::pin(async { Ok(serde_json::json!("0x3039")) }));
1644
1645        let relayer = EvmRelayer::new(
1646            relayer_model,
1647            signer,
1648            provider,
1649            create_test_evm_network(),
1650            Arc::new(relayer_repo),
1651            Arc::new(network_repo),
1652            Arc::new(tx_repo),
1653            Arc::new(counter),
1654            Arc::new(job_producer),
1655        )
1656        .unwrap();
1657
1658        let request = JsonRpcRequest {
1659            jsonrpc: "2.0".to_string(),
1660            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
1661                method: "eth_blockNumber".to_string(),
1662                params: "[]".to_string(),
1663            }),
1664            id: Some(JsonRpcId::Number(1)),
1665        };
1666
1667        let response = relayer.rpc(request).await.unwrap();
1668        assert!(response.error.is_none());
1669        assert!(response.result.is_some());
1670
1671        if let Some(NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(result))) = response.result {
1672            assert_eq!(result, serde_json::json!("0x3039")); // 12345 in hex
1673        }
1674    }
1675
1676    #[tokio::test]
1677    async fn test_rpc_unsupported_method() {
1678        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1679            setup_mocks();
1680        let relayer_model = create_test_relayer();
1681
1682        provider
1683            .expect_raw_request_dyn()
1684            .withf(|method, _| method == "eth_unsupportedMethod")
1685            .returning(|_, _| {
1686                Box::pin(async {
1687                    Err(ProviderError::Other(
1688                        "Unsupported method: eth_unsupportedMethod".to_string(),
1689                    ))
1690                })
1691            });
1692
1693        let relayer = EvmRelayer::new(
1694            relayer_model,
1695            signer,
1696            provider,
1697            create_test_evm_network(),
1698            Arc::new(relayer_repo),
1699            Arc::new(network_repo),
1700            Arc::new(tx_repo),
1701            Arc::new(counter),
1702            Arc::new(job_producer),
1703        )
1704        .unwrap();
1705
1706        let request = JsonRpcRequest {
1707            jsonrpc: "2.0".to_string(),
1708            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
1709                method: "eth_unsupportedMethod".to_string(),
1710                params: "[]".to_string(),
1711            }),
1712            id: Some(JsonRpcId::Number(1)),
1713        };
1714
1715        let response = relayer.rpc(request).await.unwrap();
1716        assert!(response.result.is_none());
1717        assert!(response.error.is_some());
1718
1719        let error = response.error.unwrap();
1720        assert_eq!(error.code, -32603); // RpcErrorCodes::INTERNAL_ERROR
1721    }
1722
1723    #[tokio::test]
1724    async fn test_rpc_invalid_params() {
1725        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1726            setup_mocks();
1727        let relayer_model = create_test_relayer();
1728
1729        provider
1730            .expect_raw_request_dyn()
1731            .withf(|method, params| method == "eth_getBalance" && params.as_str() == Some("[]"))
1732            .returning(|_, _| {
1733                Box::pin(async {
1734                    Err(ProviderError::Other(
1735                        "Missing address parameter".to_string(),
1736                    ))
1737                })
1738            });
1739
1740        let relayer = EvmRelayer::new(
1741            relayer_model,
1742            signer,
1743            provider,
1744            create_test_evm_network(),
1745            Arc::new(relayer_repo),
1746            Arc::new(network_repo),
1747            Arc::new(tx_repo),
1748            Arc::new(counter),
1749            Arc::new(job_producer),
1750        )
1751        .unwrap();
1752
1753        let request = JsonRpcRequest {
1754            jsonrpc: "2.0".to_string(),
1755            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
1756                method: "eth_getBalance".to_string(),
1757                params: "[]".to_string(), // Missing address parameter
1758            }),
1759            id: Some(JsonRpcId::Number(1)),
1760        };
1761
1762        let response = relayer.rpc(request).await.unwrap();
1763        assert!(response.result.is_none());
1764        assert!(response.error.is_some());
1765
1766        let error = response.error.unwrap();
1767        assert_eq!(error.code, -32603); // RpcErrorCodes::INTERNAL_ERROR
1768    }
1769
1770    #[tokio::test]
1771    async fn test_rpc_non_evm_request() {
1772        let (provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1773            setup_mocks();
1774        let relayer_model = create_test_relayer();
1775
1776        let relayer = EvmRelayer::new(
1777            relayer_model,
1778            signer,
1779            provider,
1780            create_test_evm_network(),
1781            Arc::new(relayer_repo),
1782            Arc::new(network_repo),
1783            Arc::new(tx_repo),
1784            Arc::new(counter),
1785            Arc::new(job_producer),
1786        )
1787        .unwrap();
1788
1789        let request = JsonRpcRequest {
1790            jsonrpc: "2.0".to_string(),
1791            params: NetworkRpcRequest::Solana(crate::models::SolanaRpcRequest::GetSupportedTokens(
1792                crate::models::GetSupportedTokensRequestParams {},
1793            )),
1794            id: Some(JsonRpcId::Number(1)),
1795        };
1796
1797        let response = relayer.rpc(request).await.unwrap();
1798        assert!(response.result.is_none());
1799        assert!(response.error.is_some());
1800
1801        let error = response.error.unwrap();
1802        assert_eq!(error.code, -32602); // RpcErrorCodes::INVALID_PARAMS
1803    }
1804
1805    #[tokio::test]
1806    async fn test_rpc_raw_request_with_array_params() {
1807        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1808            setup_mocks();
1809        let relayer_model = create_test_relayer();
1810
1811        provider
1812            .expect_raw_request_dyn()
1813            .withf(|method, params| {
1814                method == "eth_getTransactionByHash"
1815                    && params.as_array().is_some_and(|arr| {
1816                        arr.len() == 1 && arr[0].as_str() == Some("0x1234567890abcdef")
1817                    })
1818            })
1819            .returning(|_, _| {
1820                Box::pin(async {
1821                    Ok(serde_json::json!({
1822                        "hash": "0x1234567890abcdef",
1823                        "blockNumber": "0x1",
1824                        "gasUsed": "0x5208"
1825                    }))
1826                })
1827            });
1828
1829        let relayer = EvmRelayer::new(
1830            relayer_model,
1831            signer,
1832            provider,
1833            create_test_evm_network(),
1834            Arc::new(relayer_repo),
1835            Arc::new(network_repo),
1836            Arc::new(tx_repo),
1837            Arc::new(counter),
1838            Arc::new(job_producer),
1839        )
1840        .unwrap();
1841
1842        let request = JsonRpcRequest {
1843            jsonrpc: "2.0".to_string(),
1844            params: NetworkRpcRequest::Evm(EvmRpcRequest::RawRpcRequest {
1845                method: "eth_getTransactionByHash".to_string(),
1846                params: serde_json::json!(["0x1234567890abcdef"]),
1847            }),
1848            id: Some(JsonRpcId::Number(42)),
1849        };
1850
1851        let response = relayer.rpc(request).await.unwrap();
1852        assert!(response.error.is_none());
1853        assert!(response.result.is_some());
1854        assert_eq!(response.id, Some(JsonRpcId::Number(42)));
1855
1856        if let Some(NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(result))) = response.result {
1857            assert!(result.get("hash").is_some());
1858            assert!(result.get("blockNumber").is_some());
1859        }
1860    }
1861
1862    #[tokio::test]
1863    async fn test_rpc_raw_request_with_object_params() {
1864        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1865            setup_mocks();
1866        let relayer_model = create_test_relayer();
1867
1868        provider
1869            .expect_raw_request_dyn()
1870            .withf(|method, params| {
1871                method == "eth_call"
1872                    && params
1873                        .as_object()
1874                        .is_some_and(|obj| obj.contains_key("to") && obj.contains_key("data"))
1875            })
1876            .returning(|_, _| {
1877                Box::pin(async {
1878                    Ok(serde_json::json!(
1879                        "0x0000000000000000000000000000000000000000000000000000000000000001"
1880                    ))
1881                })
1882            });
1883
1884        let relayer = EvmRelayer::new(
1885            relayer_model,
1886            signer,
1887            provider,
1888            create_test_evm_network(),
1889            Arc::new(relayer_repo),
1890            Arc::new(network_repo),
1891            Arc::new(tx_repo),
1892            Arc::new(counter),
1893            Arc::new(job_producer),
1894        )
1895        .unwrap();
1896
1897        let request = JsonRpcRequest {
1898            jsonrpc: "2.0".to_string(),
1899            params: NetworkRpcRequest::Evm(EvmRpcRequest::RawRpcRequest {
1900                method: "eth_call".to_string(),
1901                params: serde_json::json!({
1902                    "to": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
1903                    "data": "0x70a08231000000000000000000000000742d35cc6634c0532925a3b844bc454e4438f44e"
1904                }),
1905            }),
1906            id: Some(JsonRpcId::Number(123)),
1907        };
1908
1909        let response = relayer.rpc(request).await.unwrap();
1910        assert!(response.error.is_none());
1911        assert!(response.result.is_some());
1912        assert_eq!(response.id, Some(JsonRpcId::Number(123)));
1913    }
1914
1915    #[tokio::test]
1916    async fn test_rpc_generic_request_with_empty_params() {
1917        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1918            setup_mocks();
1919        let relayer_model = create_test_relayer();
1920
1921        provider
1922            .expect_raw_request_dyn()
1923            .withf(|method, params| method == "net_version" && params.as_str() == Some("[]"))
1924            .returning(|_, _| Box::pin(async { Ok(serde_json::json!("1")) }));
1925
1926        let relayer = EvmRelayer::new(
1927            relayer_model,
1928            signer,
1929            provider,
1930            create_test_evm_network(),
1931            Arc::new(relayer_repo),
1932            Arc::new(network_repo),
1933            Arc::new(tx_repo),
1934            Arc::new(counter),
1935            Arc::new(job_producer),
1936        )
1937        .unwrap();
1938
1939        let request = JsonRpcRequest {
1940            jsonrpc: "2.0".to_string(),
1941            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
1942                method: "net_version".to_string(),
1943                params: "[]".to_string(),
1944            }),
1945            id: Some(JsonRpcId::Number(999)),
1946        };
1947
1948        let response = relayer.rpc(request).await.unwrap();
1949        assert!(response.error.is_none());
1950        assert!(response.result.is_some());
1951        assert_eq!(response.id, Some(JsonRpcId::Number(999)));
1952    }
1953
1954    #[tokio::test]
1955    async fn test_rpc_provider_invalid_address_error() {
1956        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
1957            setup_mocks();
1958        let relayer_model = create_test_relayer();
1959
1960        provider.expect_raw_request_dyn().returning(|_, _| {
1961            Box::pin(async {
1962                Err(ProviderError::InvalidAddress(
1963                    "Invalid address format".to_string(),
1964                ))
1965            })
1966        });
1967
1968        let relayer = EvmRelayer::new(
1969            relayer_model,
1970            signer,
1971            provider,
1972            create_test_evm_network(),
1973            Arc::new(relayer_repo),
1974            Arc::new(network_repo),
1975            Arc::new(tx_repo),
1976            Arc::new(counter),
1977            Arc::new(job_producer),
1978        )
1979        .unwrap();
1980
1981        let request = JsonRpcRequest {
1982            jsonrpc: "2.0".to_string(),
1983            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
1984                method: "eth_getBalance".to_string(),
1985                params: r#"["invalid_address", "latest"]"#.to_string(),
1986            }),
1987            id: Some(JsonRpcId::Number(1)),
1988        };
1989
1990        let response = relayer.rpc(request).await.unwrap();
1991        assert!(response.result.is_none());
1992        assert!(response.error.is_some());
1993
1994        let error = response.error.unwrap();
1995        assert_eq!(error.code, -32602); // RpcErrorCodes::INVALID_PARAMS
1996    }
1997
1998    #[tokio::test]
1999    async fn test_rpc_provider_network_configuration_error() {
2000        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2001            setup_mocks();
2002        let relayer_model = create_test_relayer();
2003
2004        provider.expect_raw_request_dyn().returning(|_, _| {
2005            Box::pin(async {
2006                Err(ProviderError::NetworkConfiguration(
2007                    "Network not reachable".to_string(),
2008                ))
2009            })
2010        });
2011
2012        let relayer = EvmRelayer::new(
2013            relayer_model,
2014            signer,
2015            provider,
2016            create_test_evm_network(),
2017            Arc::new(relayer_repo),
2018            Arc::new(network_repo),
2019            Arc::new(tx_repo),
2020            Arc::new(counter),
2021            Arc::new(job_producer),
2022        )
2023        .unwrap();
2024
2025        let request = JsonRpcRequest {
2026            jsonrpc: "2.0".to_string(),
2027            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
2028                method: "eth_chainId".to_string(),
2029                params: "[]".to_string(),
2030            }),
2031            id: Some(JsonRpcId::Number(2)),
2032        };
2033
2034        let response = relayer.rpc(request).await.unwrap();
2035        assert!(response.result.is_none());
2036        assert!(response.error.is_some());
2037
2038        let error = response.error.unwrap();
2039        assert_eq!(error.code, -33004); // OpenZeppelinErrorCodes::NETWORK_CONFIGURATION
2040    }
2041
2042    #[tokio::test]
2043    async fn test_rpc_provider_timeout_error() {
2044        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2045            setup_mocks();
2046        let relayer_model = create_test_relayer();
2047
2048        provider
2049            .expect_raw_request_dyn()
2050            .returning(|_, _| Box::pin(async { Err(ProviderError::Timeout) }));
2051
2052        let relayer = EvmRelayer::new(
2053            relayer_model,
2054            signer,
2055            provider,
2056            create_test_evm_network(),
2057            Arc::new(relayer_repo),
2058            Arc::new(network_repo),
2059            Arc::new(tx_repo),
2060            Arc::new(counter),
2061            Arc::new(job_producer),
2062        )
2063        .unwrap();
2064
2065        let request = JsonRpcRequest {
2066            jsonrpc: "2.0".to_string(),
2067            params: NetworkRpcRequest::Evm(EvmRpcRequest::RawRpcRequest {
2068                method: "eth_blockNumber".to_string(),
2069                params: serde_json::json!([]),
2070            }),
2071            id: Some(JsonRpcId::Number(3)),
2072        };
2073
2074        let response = relayer.rpc(request).await.unwrap();
2075        assert!(response.result.is_none());
2076        assert!(response.error.is_some());
2077
2078        let error = response.error.unwrap();
2079        assert_eq!(error.code, -33000); // OpenZeppelinErrorCodes::TIMEOUT
2080    }
2081
2082    #[tokio::test]
2083    async fn test_rpc_provider_rate_limited_error() {
2084        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2085            setup_mocks();
2086        let relayer_model = create_test_relayer();
2087
2088        provider
2089            .expect_raw_request_dyn()
2090            .returning(|_, _| Box::pin(async { Err(ProviderError::RateLimited) }));
2091
2092        let relayer = EvmRelayer::new(
2093            relayer_model,
2094            signer,
2095            provider,
2096            create_test_evm_network(),
2097            Arc::new(relayer_repo),
2098            Arc::new(network_repo),
2099            Arc::new(tx_repo),
2100            Arc::new(counter),
2101            Arc::new(job_producer),
2102        )
2103        .unwrap();
2104
2105        let request = JsonRpcRequest {
2106            jsonrpc: "2.0".to_string(),
2107            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
2108                method: "eth_getBalance".to_string(),
2109                params: r#"["0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest"]"#.to_string(),
2110            }),
2111            id: Some(JsonRpcId::Number(4)),
2112        };
2113
2114        let response = relayer.rpc(request).await.unwrap();
2115        assert!(response.result.is_none());
2116        assert!(response.error.is_some());
2117
2118        let error = response.error.unwrap();
2119        assert_eq!(error.code, -33001); // OpenZeppelinErrorCodes::RATE_LIMITED
2120    }
2121
2122    #[tokio::test]
2123    async fn test_rpc_provider_bad_gateway_error() {
2124        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2125            setup_mocks();
2126        let relayer_model = create_test_relayer();
2127
2128        provider
2129            .expect_raw_request_dyn()
2130            .returning(|_, _| Box::pin(async { Err(ProviderError::BadGateway) }));
2131
2132        let relayer = EvmRelayer::new(
2133            relayer_model,
2134            signer,
2135            provider,
2136            create_test_evm_network(),
2137            Arc::new(relayer_repo),
2138            Arc::new(network_repo),
2139            Arc::new(tx_repo),
2140            Arc::new(counter),
2141            Arc::new(job_producer),
2142        )
2143        .unwrap();
2144
2145        let request = JsonRpcRequest {
2146            jsonrpc: "2.0".to_string(),
2147            params: NetworkRpcRequest::Evm(EvmRpcRequest::RawRpcRequest {
2148                method: "eth_gasPrice".to_string(),
2149                params: serde_json::json!([]),
2150            }),
2151            id: Some(JsonRpcId::Number(5)),
2152        };
2153
2154        let response = relayer.rpc(request).await.unwrap();
2155        assert!(response.result.is_none());
2156        assert!(response.error.is_some());
2157
2158        let error = response.error.unwrap();
2159        assert_eq!(error.code, -33002); // OpenZeppelinErrorCodes::BAD_GATEWAY
2160    }
2161
2162    #[tokio::test]
2163    async fn test_rpc_provider_request_error() {
2164        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2165            setup_mocks();
2166        let relayer_model = create_test_relayer();
2167
2168        provider.expect_raw_request_dyn().returning(|_, _| {
2169            Box::pin(async {
2170                Err(ProviderError::RequestError {
2171                    error: "Bad request".to_string(),
2172                    status_code: 400,
2173                })
2174            })
2175        });
2176
2177        let relayer = EvmRelayer::new(
2178            relayer_model,
2179            signer,
2180            provider,
2181            create_test_evm_network(),
2182            Arc::new(relayer_repo),
2183            Arc::new(network_repo),
2184            Arc::new(tx_repo),
2185            Arc::new(counter),
2186            Arc::new(job_producer),
2187        )
2188        .unwrap();
2189
2190        let request = JsonRpcRequest {
2191            jsonrpc: "2.0".to_string(),
2192            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
2193                method: "invalid_method".to_string(),
2194                params: "{}".to_string(),
2195            }),
2196            id: Some(JsonRpcId::Number(6)),
2197        };
2198
2199        let response = relayer.rpc(request).await.unwrap();
2200        assert!(response.result.is_none());
2201        assert!(response.error.is_some());
2202
2203        let error = response.error.unwrap();
2204        assert_eq!(error.code, -33003); // OpenZeppelinErrorCodes::REQUEST_ERROR
2205    }
2206
2207    #[tokio::test]
2208    async fn test_rpc_provider_other_error() {
2209        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2210            setup_mocks();
2211        let relayer_model = create_test_relayer();
2212
2213        provider.expect_raw_request_dyn().returning(|_, _| {
2214            Box::pin(async {
2215                Err(ProviderError::Other(
2216                    "Unexpected error occurred".to_string(),
2217                ))
2218            })
2219        });
2220
2221        let relayer = EvmRelayer::new(
2222            relayer_model,
2223            signer,
2224            provider,
2225            create_test_evm_network(),
2226            Arc::new(relayer_repo),
2227            Arc::new(network_repo),
2228            Arc::new(tx_repo),
2229            Arc::new(counter),
2230            Arc::new(job_producer),
2231        )
2232        .unwrap();
2233
2234        let request = JsonRpcRequest {
2235            jsonrpc: "2.0".to_string(),
2236            params: NetworkRpcRequest::Evm(EvmRpcRequest::RawRpcRequest {
2237                method: "eth_getBalance".to_string(),
2238                params: serde_json::json!(["0x742d35Cc6634C0532925a3b844Bc454e4438f44e", "latest"]),
2239            }),
2240            id: Some(JsonRpcId::Number(7)),
2241        };
2242
2243        let response = relayer.rpc(request).await.unwrap();
2244        assert!(response.result.is_none());
2245        assert!(response.error.is_some());
2246
2247        let error = response.error.unwrap();
2248        assert_eq!(error.code, -32603); // RpcErrorCodes::INTERNAL_ERROR
2249    }
2250
2251    #[tokio::test]
2252    async fn test_rpc_response_preserves_request_id() {
2253        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2254            setup_mocks();
2255        let relayer_model = create_test_relayer();
2256
2257        provider
2258            .expect_raw_request_dyn()
2259            .returning(|_, _| Box::pin(async { Ok(serde_json::json!("0x1")) }));
2260
2261        let relayer = EvmRelayer::new(
2262            relayer_model,
2263            signer,
2264            provider,
2265            create_test_evm_network(),
2266            Arc::new(relayer_repo),
2267            Arc::new(network_repo),
2268            Arc::new(tx_repo),
2269            Arc::new(counter),
2270            Arc::new(job_producer),
2271        )
2272        .unwrap();
2273
2274        let request_id = u64::MAX;
2275        let request = JsonRpcRequest {
2276            jsonrpc: "2.0".to_string(),
2277            params: NetworkRpcRequest::Evm(EvmRpcRequest::GenericRpcRequest {
2278                method: "eth_chainId".to_string(),
2279                params: "[]".to_string(),
2280            }),
2281            id: Some(JsonRpcId::Number(request_id as i64)),
2282        };
2283
2284        let response = relayer.rpc(request).await.unwrap();
2285        assert_eq!(response.id, Some(JsonRpcId::Number(request_id as i64)));
2286        assert_eq!(response.jsonrpc, "2.0");
2287    }
2288
2289    #[tokio::test]
2290    async fn test_rpc_handles_complex_json_response() {
2291        let (mut provider, relayer_repo, network_repo, tx_repo, job_producer, signer, counter) =
2292            setup_mocks();
2293        let relayer_model = create_test_relayer();
2294
2295        let complex_response = serde_json::json!({
2296            "number": "0x1b4",
2297            "hash": "0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae",
2298            "parentHash": "0xe99e022112df268ce40b8b654759b4f39c3cc1b8c86b2f4c7da48ba6d8a6ae8b",
2299            "transactions": [
2300                {
2301                    "hash": "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
2302                    "from": "0xa7d9ddbe1f17865597fbd27ec712455208b6b76d",
2303                    "to": "0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb",
2304                    "value": "0xf3dbb76162000"
2305                }
2306            ],
2307            "gasUsed": "0x5208"
2308        });
2309
2310        provider.expect_raw_request_dyn().returning(move |_, _| {
2311            let response = complex_response.clone();
2312            Box::pin(async move { Ok(response) })
2313        });
2314
2315        let relayer = EvmRelayer::new(
2316            relayer_model,
2317            signer,
2318            provider,
2319            create_test_evm_network(),
2320            Arc::new(relayer_repo),
2321            Arc::new(network_repo),
2322            Arc::new(tx_repo),
2323            Arc::new(counter),
2324            Arc::new(job_producer),
2325        )
2326        .unwrap();
2327
2328        let request = JsonRpcRequest {
2329            jsonrpc: "2.0".to_string(),
2330            params: NetworkRpcRequest::Evm(EvmRpcRequest::RawRpcRequest {
2331                method: "eth_getBlockByNumber".to_string(),
2332                params: serde_json::json!(["0x1b4", true]),
2333            }),
2334            id: Some(JsonRpcId::Number(8)),
2335        };
2336
2337        let response = relayer.rpc(request).await.unwrap();
2338        assert!(response.error.is_none());
2339        assert!(response.result.is_some());
2340
2341        if let Some(NetworkRpcResult::Evm(EvmRpcResult::RawRpcResult(result))) = response.result {
2342            assert!(result.get("transactions").is_some());
2343            assert!(result.get("hash").is_some());
2344            assert!(result.get("gasUsed").is_some());
2345        }
2346    }
2347}