openzeppelin_relayer/services/transaction_counter/
mod.rs1use std::sync::Arc;
7
8use crate::repositories::{TransactionCounterError, TransactionCounterTrait};
9use async_trait::async_trait;
10
11#[cfg(test)]
12use mockall::automock;
13
14#[derive(Clone, Debug)]
15pub struct TransactionCounterService<T> {
16 relayer_id: String,
17 address: String,
18 store: Arc<T>,
19}
20
21impl<T> TransactionCounterService<T> {
22 pub fn new(relayer_id: String, address: String, store: Arc<T>) -> Self {
23 Self {
24 relayer_id,
25 address,
26 store,
27 }
28 }
29}
30
31#[async_trait]
32#[cfg_attr(test, automock)]
33pub trait TransactionCounterServiceTrait: Send + Sync {
34 async fn get(&self) -> Result<Option<u64>, TransactionCounterError>;
35 async fn get_and_increment(&self) -> Result<u64, TransactionCounterError>;
36 async fn decrement(&self) -> Result<u64, TransactionCounterError>;
37 async fn set(&self, value: u64) -> Result<(), TransactionCounterError>;
38
39 async fn sync_floor(&self, floor: u64) -> Result<u64, TransactionCounterError>;
46}
47
48#[async_trait]
49#[allow(dead_code)]
50impl<T> TransactionCounterServiceTrait for TransactionCounterService<T>
51where
52 T: TransactionCounterTrait + Send + Sync,
53{
54 async fn get(&self) -> Result<Option<u64>, TransactionCounterError> {
55 self.store
56 .get(&self.relayer_id, &self.address)
57 .await
58 .map_err(|e| TransactionCounterError::NotFound(e.to_string()))
59 }
60
61 async fn get_and_increment(&self) -> Result<u64, TransactionCounterError> {
62 self.store
63 .get_and_increment(&self.relayer_id, &self.address)
64 .await
65 .map_err(|e| TransactionCounterError::NotFound(e.to_string()))
66 }
67
68 async fn decrement(&self) -> Result<u64, TransactionCounterError> {
69 self.store
70 .decrement(&self.relayer_id, &self.address)
71 .await
72 .map_err(|e| TransactionCounterError::NotFound(e.to_string()))
73 }
74
75 async fn set(&self, value: u64) -> Result<(), TransactionCounterError> {
76 self.store
77 .set(&self.relayer_id, &self.address, value)
78 .await
79 .map_err(|e| TransactionCounterError::NotFound(e.to_string()))
80 }
81
82 async fn sync_floor(&self, floor: u64) -> Result<u64, TransactionCounterError> {
83 self.store
84 .sync_floor(&self.relayer_id, &self.address, floor)
85 .await
86 .map_err(|e| TransactionCounterError::NotFound(e.to_string()))
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::repositories::InMemoryTransactionCounter;
94
95 #[tokio::test]
96 async fn test_transaction_counter() {
97 let store = Arc::new(InMemoryTransactionCounter::default());
98 let service =
99 TransactionCounterService::new("relayer_id".to_string(), "address".to_string(), store);
100
101 assert_eq!(service.get().await.unwrap(), None);
102 assert_eq!(service.get_and_increment().await.unwrap(), 0);
103 assert_eq!(service.get_and_increment().await.unwrap(), 1);
104 assert_eq!(service.decrement().await.unwrap(), 1);
105 assert!(service.set(10).await.is_ok());
106 assert_eq!(service.get().await.unwrap(), Some(10));
107 }
108
109 #[tokio::test]
110 async fn test_sync_floor_does_not_rewind() {
111 let store = Arc::new(InMemoryTransactionCounter::default());
112 let service =
113 TransactionCounterService::new("relayer_id".to_string(), "address".to_string(), store);
114
115 service.set(15).await.unwrap();
117
118 let effective = service.sync_floor(6).await.unwrap();
120 assert_eq!(effective, 15);
121 assert_eq!(service.get().await.unwrap(), Some(15));
122
123 let effective = service.sync_floor(20).await.unwrap();
125 assert_eq!(effective, 20);
126 assert_eq!(service.get().await.unwrap(), Some(20));
127 }
128}