openzeppelin_relayer/domain/transaction/
common.rs1use chrono::{DateTime, Duration, Utc};
8
9use crate::constants::FINAL_TRANSACTION_STATUSES;
10use crate::models::{TransactionError, TransactionRepoModel, TransactionStatus};
11
12pub fn is_final_state(tx_status: &TransactionStatus) -> bool {
26 FINAL_TRANSACTION_STATUSES.contains(tx_status)
27}
28
29pub fn is_active_nonce_status(tx_status: &TransactionStatus) -> bool {
33 matches!(
34 tx_status,
35 TransactionStatus::Pending
36 | TransactionStatus::Sent
37 | TransactionStatus::Submitted
38 | TransactionStatus::Mined
39 )
40}
41
42pub fn is_pending_transaction(tx_status: &TransactionStatus) -> bool {
43 matches!(
44 tx_status,
45 TransactionStatus::Pending | TransactionStatus::Sent | TransactionStatus::Submitted
46 )
47}
48
49pub fn is_unsubmitted_transaction(tx_status: &TransactionStatus) -> bool {
50 matches!(
51 tx_status,
52 TransactionStatus::Pending | TransactionStatus::Sent
53 )
54}
55
56pub fn get_age_of_sent_at(tx: &TransactionRepoModel) -> Result<Duration, TransactionError> {
58 let now = Utc::now();
59 let sent_at_str = tx.sent_at.as_ref().ok_or_else(|| {
60 TransactionError::UnexpectedError("Transaction sent_at time is missing".to_string())
61 })?;
62 let sent_time = DateTime::parse_from_rfc3339(sent_at_str)
63 .map_err(|_| TransactionError::UnexpectedError("Error parsing sent_at time".to_string()))?
64 .with_timezone(&Utc);
65 Ok(now.signed_duration_since(sent_time))
66}
67
68#[cfg(test)]
69mod tests {
70 use crate::utils::mocks::mockutils::create_mock_transaction;
71
72 use super::*;
73
74 #[test]
75 fn test_is_final_state() {
76 assert!(is_final_state(&TransactionStatus::Confirmed));
78 assert!(is_final_state(&TransactionStatus::Failed));
79 assert!(is_final_state(&TransactionStatus::Expired));
80 assert!(is_final_state(&TransactionStatus::Canceled));
81
82 assert!(!is_final_state(&TransactionStatus::Pending));
84 assert!(!is_final_state(&TransactionStatus::Sent));
85 assert!(!is_final_state(&TransactionStatus::Submitted));
86 assert!(!is_final_state(&TransactionStatus::Mined));
87 }
88
89 #[test]
90 fn test_is_pending_transaction() {
91 assert!(is_pending_transaction(&TransactionStatus::Pending));
93
94 assert!(is_pending_transaction(&TransactionStatus::Sent));
96
97 assert!(is_pending_transaction(&TransactionStatus::Submitted));
99
100 assert!(!is_pending_transaction(&TransactionStatus::Confirmed));
102 assert!(!is_pending_transaction(&TransactionStatus::Failed));
103 assert!(!is_pending_transaction(&TransactionStatus::Canceled));
104 assert!(!is_pending_transaction(&TransactionStatus::Mined));
105 assert!(!is_pending_transaction(&TransactionStatus::Expired));
106 }
107
108 #[test]
109 fn test_is_unsubmitted_transaction() {
110 assert!(is_unsubmitted_transaction(&TransactionStatus::Pending));
112 assert!(is_unsubmitted_transaction(&TransactionStatus::Sent));
113
114 assert!(!is_unsubmitted_transaction(&TransactionStatus::Submitted));
116 assert!(!is_unsubmitted_transaction(&TransactionStatus::Mined));
117 assert!(!is_unsubmitted_transaction(&TransactionStatus::Confirmed));
118 assert!(!is_unsubmitted_transaction(&TransactionStatus::Failed));
119 assert!(!is_unsubmitted_transaction(&TransactionStatus::Canceled));
120 assert!(!is_unsubmitted_transaction(&TransactionStatus::Expired));
121 }
122
123 #[test]
124 fn test_is_active_nonce_status() {
125 assert!(is_active_nonce_status(&TransactionStatus::Pending));
127 assert!(is_active_nonce_status(&TransactionStatus::Sent));
128 assert!(is_active_nonce_status(&TransactionStatus::Submitted));
129 assert!(is_active_nonce_status(&TransactionStatus::Mined));
130
131 assert!(!is_active_nonce_status(&TransactionStatus::Confirmed));
133 assert!(!is_active_nonce_status(&TransactionStatus::Failed));
134 assert!(!is_active_nonce_status(&TransactionStatus::Canceled));
135 assert!(!is_active_nonce_status(&TransactionStatus::Expired));
136 }
137
138 #[test]
139 fn test_get_age_of_sent_at() {
140 let now = Utc::now();
141
142 let sent_at_time = now - Duration::hours(1);
144 let mut tx = create_mock_transaction();
145 tx.sent_at = Some(sent_at_time.to_rfc3339());
146
147 let age_result = get_age_of_sent_at(&tx);
148 assert!(age_result.is_ok());
149 let age = age_result.unwrap();
150 assert!(age.num_minutes() >= 59 && age.num_minutes() <= 61);
152 }
153
154 #[test]
155 fn test_get_age_of_sent_at_missing_sent_at() {
156 let mut tx = create_mock_transaction();
157 tx.sent_at = None; let result = get_age_of_sent_at(&tx);
160 assert!(result.is_err());
161 match result.unwrap_err() {
162 TransactionError::UnexpectedError(msg) => {
163 assert!(msg.contains("sent_at time is missing"));
164 }
165 _ => panic!("Expected UnexpectedError for missing sent_at"),
166 }
167 }
168
169 #[test]
170 fn test_get_age_of_sent_at_invalid_timestamp() {
171 let mut tx = create_mock_transaction();
172 tx.sent_at = Some("invalid-timestamp".to_string()); let result = get_age_of_sent_at(&tx);
175 assert!(result.is_err());
176 match result.unwrap_err() {
177 TransactionError::UnexpectedError(msg) => {
178 assert!(msg.contains("Error parsing sent_at time"));
179 }
180 _ => panic!("Expected UnexpectedError for invalid timestamp"),
181 }
182 }
183}