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