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