openzeppelin_relayer/jobs/handlers/
transaction_submission_handler.rs

1//! Transaction submission handler for processing submission jobs.
2//!
3//! Handles the submission of prepared transactions to networks:
4//! - Submits transactions to appropriate networks
5//! - Handles different submission commands (Submit, Cancel, Resubmit)
6//! - Updates transaction status after submission
7//! - Enqueues status monitoring jobs
8use actix_web::web::ThinData;
9use apalis::prelude::{Attempt, Data, *};
10use eyre::Result;
11use log::info;
12
13use crate::{
14    constants::WORKER_DEFAULT_MAXIMUM_RETRIES,
15    domain::{get_relayer_transaction, get_transaction_by_id, Transaction},
16    jobs::{handle_result, Job, TransactionCommand, TransactionSend},
17    models::DefaultAppState,
18};
19
20pub async fn transaction_submission_handler(
21    job: Job<TransactionSend>,
22    state: Data<ThinData<DefaultAppState>>,
23    attempt: Attempt,
24) -> Result<(), Error> {
25    info!("handling transaction submission: {:?}", job.data);
26
27    let result = handle_request(job.data, state).await;
28
29    handle_result(
30        result,
31        attempt,
32        "Transaction Sender",
33        WORKER_DEFAULT_MAXIMUM_RETRIES,
34    )
35}
36
37async fn handle_request(
38    status_request: TransactionSend,
39    state: Data<ThinData<DefaultAppState>>,
40) -> Result<()> {
41    let relayer_transaction =
42        get_relayer_transaction(status_request.relayer_id.clone(), &state).await?;
43
44    let transaction = get_transaction_by_id(status_request.transaction_id, &state).await?;
45
46    match status_request.command {
47        TransactionCommand::Submit => {
48            relayer_transaction.submit_transaction(transaction).await?;
49        }
50        TransactionCommand::Cancel { reason } => {
51            info!("Cancelling transaction: {:?}", reason);
52            relayer_transaction.submit_transaction(transaction).await?;
53        }
54        TransactionCommand::Resubmit => {
55            info!("Resubmitting transaction with updated parameters");
56            relayer_transaction
57                .resubmit_transaction(transaction)
58                .await?;
59        }
60        TransactionCommand::Resend => {
61            info!("Resending transaction");
62            relayer_transaction.submit_transaction(transaction).await?;
63        }
64    };
65
66    info!("Transaction handled successfully");
67
68    Ok(())
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use std::collections::HashMap;
75
76    #[tokio::test]
77    async fn test_submission_handler_job_validation() {
78        // Create a job with Submit command
79        let submit_job = TransactionSend::submit("tx123", "relayer-1");
80        let job = Job::new(crate::jobs::JobType::TransactionSend, submit_job);
81
82        // Validate the job data
83        match job.data.command {
84            TransactionCommand::Submit => {}
85            _ => panic!("Expected Submit command"),
86        }
87        assert_eq!(job.data.transaction_id, "tx123");
88        assert_eq!(job.data.relayer_id, "relayer-1");
89        assert!(job.data.metadata.is_none());
90
91        // Create a job with Cancel command
92        let cancel_job = TransactionSend::cancel("tx123", "relayer-1", "user requested");
93        let job = Job::new(crate::jobs::JobType::TransactionSend, cancel_job);
94
95        // Validate the job data
96        match job.data.command {
97            TransactionCommand::Cancel { reason } => {
98                assert_eq!(reason, "user requested");
99            }
100            _ => panic!("Expected Cancel command"),
101        }
102    }
103
104    #[tokio::test]
105    async fn test_submission_job_with_metadata() {
106        // Create a job with metadata
107        let mut metadata = HashMap::new();
108        metadata.insert("gas_price".to_string(), "20000000000".to_string());
109
110        let submit_job =
111            TransactionSend::submit("tx123", "relayer-1").with_metadata(metadata.clone());
112
113        // Validate the metadata
114        assert!(submit_job.metadata.is_some());
115        let job_metadata = submit_job.metadata.unwrap();
116        assert_eq!(job_metadata.get("gas_price").unwrap(), "20000000000");
117    }
118
119    // Note: As with the transaction_request_handler tests, full testing of the
120    // handler functionality would require dependency injection or integration tests.
121}