openzeppelin_relayer/domain/relayer/evm/
validations.rs1use thiserror::Error;
2
3use crate::{
4 models::{RelayerEvmPolicy, U256},
5 services::EvmProviderTrait,
6};
7
8#[derive(Debug, Error)]
9pub enum EvmTransactionValidationError {
10 #[error("Provider error: {0}")]
11 ProviderError(String),
12 #[error("Validation error: {0}")]
13 ValidationError(String),
14 #[error("Insufficient balance: {0}")]
15 InsufficientBalance(String),
16}
17
18pub struct EvmTransactionValidator {}
19
20impl EvmTransactionValidator {
21 pub async fn init_balance_validation(
22 relayer_address: &str,
23 policy: &RelayerEvmPolicy,
24 provider: &impl EvmProviderTrait,
25 ) -> Result<(), EvmTransactionValidationError> {
26 let balance = provider
27 .get_balance(relayer_address)
28 .await
29 .map_err(|e| EvmTransactionValidationError::ProviderError(e.to_string()))?;
30
31 let min_balance = U256::from(policy.min_balance);
32
33 if balance < min_balance {
34 return Err(EvmTransactionValidationError::InsufficientBalance(format!(
35 "Relayer balance {balance} is less than the minimum enforced balance of {}",
36 policy.min_balance
37 )));
38 }
39
40 Ok(())
41 }
42
43 pub async fn validate_sufficient_relayer_balance(
44 balance_to_use: U256,
45 relayer_address: &str,
46 policy: &RelayerEvmPolicy,
47 provider: &impl EvmProviderTrait,
48 ) -> Result<(), EvmTransactionValidationError> {
49 let balance = provider
50 .get_balance(relayer_address)
51 .await
52 .map_err(|e| EvmTransactionValidationError::ProviderError(e.to_string()))?;
53
54 let min_balance = U256::from(policy.min_balance);
55 let remaining_balance = balance.saturating_sub(balance_to_use);
56
57 if balance < balance_to_use {
59 return Err(EvmTransactionValidationError::InsufficientBalance(format!(
60 "Relayer balance {balance} is insufficient to cover {balance_to_use}"
61 )));
62 }
63
64 if !min_balance.is_zero() && remaining_balance < min_balance {
66 return Err(EvmTransactionValidationError::InsufficientBalance(
67 format!("Relayer balance {balance} is insufficient to cover {balance_to_use}, with an enforced minimum balance of {}", policy.min_balance)
68 ));
69 }
70
71 Ok(())
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use std::future::ready;
78
79 use super::*;
80 use crate::services::provider::evm::MockEvmProviderTrait;
81 use crate::services::ProviderError;
82 use mockall::predicate::*;
83
84 fn create_test_policy(min_balance: u128) -> RelayerEvmPolicy {
85 RelayerEvmPolicy {
86 gas_price_cap: None,
87 whitelist_receivers: None,
88 eip1559_pricing: None,
89 private_transactions: false,
90 min_balance,
91 }
92 }
93
94 #[tokio::test]
95 async fn test_validate_sufficient_balance_routine_check_success() {
96 let mut mock_provider = MockEvmProviderTrait::new();
97 mock_provider
98 .expect_get_balance()
99 .with(eq("0xSender"))
100 .returning(|_| Box::pin(ready(Ok(U256::from(200000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
103 U256::ZERO,
104 "0xSender",
105 &create_test_policy(100000000000000000u128), &mock_provider,
107 )
108 .await;
109
110 assert!(result.is_ok());
111 }
112
113 #[tokio::test]
114 async fn test_validate_sufficient_balance_routine_check_failure() {
115 let mut mock_provider = MockEvmProviderTrait::new();
116 mock_provider
117 .expect_get_balance()
118 .with(eq("0xSender"))
119 .returning(|_| Box::pin(ready(Ok(U256::from(50000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
122 U256::ZERO,
123 "0xSender",
124 &create_test_policy(100000000000000000u128), &mock_provider,
126 )
127 .await;
128
129 assert!(matches!(
130 result,
131 Err(EvmTransactionValidationError::InsufficientBalance(_))
132 ));
133 }
134
135 #[tokio::test]
136 async fn test_validate_sufficient_balance_with_transaction_success() {
137 let mut mock_provider = MockEvmProviderTrait::new();
138 mock_provider
139 .expect_get_balance()
140 .with(eq("0xSender"))
141 .returning(|_| Box::pin(ready(Ok(U256::from(300000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
144 U256::from(100000000000000000u64), "0xSender",
146 &create_test_policy(100000000000000000u128), &mock_provider,
148 )
149 .await;
150
151 assert!(result.is_ok());
152 }
153
154 #[tokio::test]
155 async fn test_validate_sufficient_balance_with_transaction_failure() {
156 let mut mock_provider = MockEvmProviderTrait::new();
157 mock_provider
158 .expect_get_balance()
159 .with(eq("0xSender"))
160 .returning(|_| Box::pin(ready(Ok(U256::from(150000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
163 U256::from(100000000000000000u64), "0xSender",
165 &create_test_policy(100000000000000000u128), &mock_provider,
167 )
168 .await;
169
170 assert!(matches!(
171 result,
172 Err(EvmTransactionValidationError::InsufficientBalance(_))
173 ));
174 }
175
176 #[tokio::test]
177 async fn test_validate_provider_error() {
178 let mut mock_provider = MockEvmProviderTrait::new();
179 mock_provider
180 .expect_get_balance()
181 .with(eq("0xSender"))
182 .returning(|_| {
183 Box::pin(ready(Err(ProviderError::Other(
184 "Provider error".to_string(),
185 ))))
186 });
187
188 let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
189 U256::ZERO,
190 "0xSender",
191 &create_test_policy(100000000000000000u128),
192 &mock_provider,
193 )
194 .await;
195
196 assert!(matches!(
197 result,
198 Err(EvmTransactionValidationError::ProviderError(_))
199 ));
200 }
201
202 #[tokio::test]
203 async fn test_validate_no_min_balance_success() {
204 let mut mock_provider = MockEvmProviderTrait::new();
205 mock_provider
206 .expect_get_balance()
207 .with(eq("0xSender"))
208 .returning(|_| Box::pin(ready(Ok(U256::from(100000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
211 U256::from(50000000000000000u64), "0xSender",
213 &create_test_policy(0), &mock_provider,
215 )
216 .await;
217
218 assert!(result.is_ok());
219 }
220
221 #[tokio::test]
222 async fn test_validate_no_min_balance_failure() {
223 let mut mock_provider = MockEvmProviderTrait::new();
224 mock_provider
225 .expect_get_balance()
226 .with(eq("0xSender"))
227 .returning(|_| Box::pin(ready(Ok(U256::from(100000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
230 U256::from(150000000000000000u64), "0xSender",
232 &create_test_policy(0), &mock_provider,
234 )
235 .await;
236
237 assert!(matches!(
238 result,
239 Err(EvmTransactionValidationError::InsufficientBalance(_))
240 ));
241 }
242
243 #[tokio::test]
244 async fn test_init_balance_validation_success() {
245 let mut mock_provider = MockEvmProviderTrait::new();
246 mock_provider
247 .expect_get_balance()
248 .with(eq("0xSender"))
249 .returning(|_| Box::pin(ready(Ok(U256::from(200000000000000000u64)))));
250
251 let result = EvmTransactionValidator::init_balance_validation(
252 "0xSender",
253 &create_test_policy(100000000000000000u128),
254 &mock_provider,
255 )
256 .await;
257
258 assert!(result.is_ok());
259 }
260
261 #[tokio::test]
262 async fn test_init_balance_validation_failure() {
263 let mut mock_provider = MockEvmProviderTrait::new();
264 mock_provider
265 .expect_get_balance()
266 .with(eq("0xSender"))
267 .returning(|_| Box::pin(ready(Ok(U256::from(50000000000000000u64)))));
268
269 let result = EvmTransactionValidator::init_balance_validation(
270 "0xSender",
271 &create_test_policy(100000000000000000u128),
272 &mock_provider,
273 )
274 .await;
275
276 assert!(matches!(
277 result,
278 Err(EvmTransactionValidationError::InsufficientBalance(_))
279 ));
280 }
281
282 #[tokio::test]
283 async fn test_init_balance_validation_provider_error() {
284 let mut mock_provider = MockEvmProviderTrait::new();
285 mock_provider
286 .expect_get_balance()
287 .with(eq("0xSender"))
288 .returning(|_| {
289 Box::pin(ready(Err(ProviderError::Other(
290 "Provider error".to_string(),
291 ))))
292 });
293
294 let result = EvmTransactionValidator::init_balance_validation(
295 "0xSender",
296 &create_test_policy(100000000000000000u128),
297 &mock_provider,
298 )
299 .await;
300
301 assert!(matches!(
302 result,
303 Err(EvmTransactionValidationError::ProviderError(_))
304 ));
305 }
306
307 #[tokio::test]
308 async fn test_init_balance_validation_zero_min_balance() {
309 let mut mock_provider = MockEvmProviderTrait::new();
310 mock_provider
311 .expect_get_balance()
312 .with(eq("0xSender"))
313 .returning(|_| Box::pin(ready(Ok(U256::from(0u64)))));
314
315 let result = EvmTransactionValidator::init_balance_validation(
316 "0xSender",
317 &create_test_policy(0), &mock_provider,
319 )
320 .await;
321
322 assert!(result.is_ok());
323 }
324}