openzeppelin_relayer/domain/transaction/solana/
solana_transaction.rs

1//! Solana transaction implementation
2//!
3//! This module provides the main SolanaRelayerTransaction struct and
4//! implements the Transaction trait for Solana transactions.
5
6use async_trait::async_trait;
7use eyre::Result;
8use log::info;
9use std::sync::Arc;
10
11use crate::{
12    domain::transaction::Transaction,
13    jobs::{JobProducer, JobProducerTrait},
14    models::{NetworkTransactionRequest, RelayerRepoModel, TransactionError, TransactionRepoModel},
15    repositories::{
16        RelayerRepository, RelayerRepositoryStorage, Repository, TransactionRepository,
17        TransactionRepositoryStorage,
18    },
19    services::{SolanaProvider, SolanaProviderTrait},
20};
21
22#[allow(dead_code)]
23pub struct SolanaRelayerTransaction<P, RR, TR, J>
24where
25    P: SolanaProviderTrait,
26    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
27    TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
28    J: JobProducerTrait + Send + Sync + 'static,
29{
30    relayer: RelayerRepoModel,
31    relayer_repository: Arc<RR>,
32    provider: Arc<P>,
33    job_producer: Arc<J>,
34    transaction_repository: Arc<TR>,
35}
36
37pub type DefaultSolanaTransaction = SolanaRelayerTransaction<
38    SolanaProvider,
39    RelayerRepositoryStorage,
40    TransactionRepositoryStorage,
41    JobProducer,
42>;
43
44#[allow(dead_code)]
45impl<P, RR, TR, J> SolanaRelayerTransaction<P, RR, TR, J>
46where
47    P: SolanaProviderTrait,
48    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
49    TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
50    J: JobProducerTrait + Send + Sync + 'static,
51{
52    pub fn new(
53        relayer: RelayerRepoModel,
54        relayer_repository: Arc<RR>,
55        provider: Arc<P>,
56        transaction_repository: Arc<TR>,
57        job_producer: Arc<J>,
58    ) -> Result<Self, TransactionError> {
59        Ok(Self {
60            relayer,
61            relayer_repository,
62            provider,
63            transaction_repository,
64            job_producer,
65        })
66    }
67
68    // Getter methods for status module access
69    pub(super) fn provider(&self) -> &P {
70        &self.provider
71    }
72
73    pub(super) fn transaction_repository(&self) -> &TR {
74        &self.transaction_repository
75    }
76
77    pub(super) fn relayer(&self) -> &RelayerRepoModel {
78        &self.relayer
79    }
80
81    pub(super) fn job_producer(&self) -> &J {
82        &self.job_producer
83    }
84}
85
86#[async_trait]
87impl<P, RR, TR, J> Transaction for SolanaRelayerTransaction<P, RR, TR, J>
88where
89    P: SolanaProviderTrait,
90    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
91    TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
92    J: JobProducerTrait + Send + Sync + 'static,
93{
94    async fn prepare_transaction(
95        &self,
96        tx: TransactionRepoModel,
97    ) -> Result<TransactionRepoModel, TransactionError> {
98        info!("preparing transaction");
99        Ok(tx)
100    }
101
102    async fn submit_transaction(
103        &self,
104        tx: TransactionRepoModel,
105    ) -> Result<TransactionRepoModel, TransactionError> {
106        info!("submitting transaction");
107        Ok(tx)
108    }
109
110    async fn resubmit_transaction(
111        &self,
112        tx: TransactionRepoModel,
113    ) -> Result<TransactionRepoModel, TransactionError> {
114        info!("resubmitting transaction");
115        Ok(tx)
116    }
117
118    /// Main entry point for transaction status handling
119    async fn handle_transaction_status(
120        &self,
121        tx: TransactionRepoModel,
122    ) -> Result<TransactionRepoModel, TransactionError> {
123        self.handle_transaction_status_impl(tx).await
124    }
125
126    async fn cancel_transaction(
127        &self,
128        tx: TransactionRepoModel,
129    ) -> Result<TransactionRepoModel, TransactionError> {
130        Ok(tx)
131    }
132
133    async fn replace_transaction(
134        &self,
135        _old_tx: TransactionRepoModel,
136        _new_tx_request: NetworkTransactionRequest,
137    ) -> Result<TransactionRepoModel, TransactionError> {
138        Ok(_old_tx)
139    }
140
141    async fn sign_transaction(
142        &self,
143        tx: TransactionRepoModel,
144    ) -> Result<TransactionRepoModel, TransactionError> {
145        Ok(tx)
146    }
147
148    async fn validate_transaction(
149        &self,
150        _tx: TransactionRepoModel,
151    ) -> Result<bool, TransactionError> {
152        Ok(true)
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use crate::{
160        jobs::MockJobProducerTrait,
161        repositories::{MockRelayerRepository, MockTransactionRepository},
162        services::MockSolanaProviderTrait,
163        utils::mocks::mockutils::{create_mock_solana_relayer, create_mock_solana_transaction},
164    };
165
166    #[tokio::test]
167    async fn test_solana_transaction_creation() {
168        let relayer = create_mock_solana_relayer("test-solana-relayer".to_string(), false);
169        let relayer_repository = Arc::new(MockRelayerRepository::new());
170        let provider = Arc::new(MockSolanaProviderTrait::new());
171        let transaction_repository = Arc::new(MockTransactionRepository::new());
172        let job_producer = Arc::new(MockJobProducerTrait::new());
173
174        let transaction = SolanaRelayerTransaction::new(
175            relayer,
176            relayer_repository,
177            provider,
178            transaction_repository,
179            job_producer,
180        );
181
182        assert!(transaction.is_ok());
183    }
184
185    #[tokio::test]
186    async fn test_handle_transaction_status_calls_impl() {
187        // Create test data
188        let relayer = create_mock_solana_relayer("test-solana-relayer".to_string(), false);
189        let relayer_repository = Arc::new(MockRelayerRepository::new());
190        let provider = Arc::new(MockSolanaProviderTrait::new());
191        let transaction_repository = Arc::new(MockTransactionRepository::new());
192        let job_producer = Arc::new(MockJobProducerTrait::new());
193
194        // Create test transaction
195        let test_tx = create_mock_solana_transaction();
196
197        // Create transaction handler
198        let transaction_handler = SolanaRelayerTransaction::new(
199            relayer,
200            relayer_repository,
201            provider,
202            transaction_repository,
203            job_producer,
204        )
205        .unwrap();
206
207        // Mock handle_transaction_status_impl to return Ok(test_tx.clone())
208        let result = transaction_handler
209            .handle_transaction_status(test_tx.clone())
210            .await;
211
212        // Verify the result matches what we expect from handle_transaction_status_impl
213        assert!(result.is_err());
214        let error = result.unwrap_err();
215        assert_eq!(
216            error.to_string(),
217            "Transaction validation error: Transaction signature is missing".to_string()
218        );
219    }
220}