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