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::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 ({}) is below minimum required balance ({})",
37 balance, min_balance
38 )));
39 }
40
41 Ok(())
42 }
43
44 pub async fn validate_sufficient_relayer_balance(
45 balance_to_use: U256,
46 relayer_address: &str,
47 policy: &RelayerEvmPolicy,
48 provider: &impl EvmProviderTrait,
49 ) -> Result<(), EvmTransactionValidationError> {
50 let balance = provider
51 .get_balance(relayer_address)
52 .await
53 .map_err(|e| EvmTransactionValidationError::ProviderError(e.to_string()))?;
54
55 let min_balance = U256::from(policy.min_balance.unwrap_or(DEFAULT_EVM_MIN_BALANCE));
56
57 let remaining_balance = balance.saturating_sub(balance_to_use);
58
59 if balance < balance_to_use {
61 return Err(EvmTransactionValidationError::InsufficientBalance(format!(
62 "Relayer balance {balance} is insufficient to cover {balance_to_use}"
63 )));
64 }
65
66 if !min_balance.is_zero() && remaining_balance < min_balance {
68 return Err(EvmTransactionValidationError::InsufficientBalance(
69 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))
70 ));
71 }
72
73 Ok(())
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use std::future::ready;
80
81 use super::*;
82 use crate::services::provider::evm::MockEvmProviderTrait;
83 use crate::services::ProviderError;
84 use mockall::predicate::*;
85
86 fn create_test_policy(min_balance: u128) -> RelayerEvmPolicy {
87 RelayerEvmPolicy {
88 min_balance: Some(min_balance),
89 gas_limit_estimation: Some(true),
90 gas_price_cap: None,
91 whitelist_receivers: None,
92 eip1559_pricing: None,
93 private_transactions: Some(false),
94 }
95 }
96
97 #[tokio::test]
98 async fn test_validate_sufficient_balance_routine_check_success() {
99 let mut mock_provider = MockEvmProviderTrait::new();
100 mock_provider
101 .expect_get_balance()
102 .with(eq("0xSender"))
103 .returning(|_| Box::pin(ready(Ok(U256::from(200000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
106 U256::ZERO,
107 "0xSender",
108 &create_test_policy(100000000000000000u128), &mock_provider,
110 )
111 .await;
112
113 assert!(result.is_ok());
114 }
115
116 #[tokio::test]
117 async fn test_validate_sufficient_balance_routine_check_failure() {
118 let mut mock_provider = MockEvmProviderTrait::new();
119 mock_provider
120 .expect_get_balance()
121 .with(eq("0xSender"))
122 .returning(|_| Box::pin(ready(Ok(U256::from(50000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
125 U256::ZERO,
126 "0xSender",
127 &create_test_policy(100000000000000000u128), &mock_provider,
129 )
130 .await;
131
132 assert!(matches!(
133 result,
134 Err(EvmTransactionValidationError::InsufficientBalance(_))
135 ));
136 }
137
138 #[tokio::test]
139 async fn test_validate_sufficient_balance_with_transaction_success() {
140 let mut mock_provider = MockEvmProviderTrait::new();
141 mock_provider
142 .expect_get_balance()
143 .with(eq("0xSender"))
144 .returning(|_| Box::pin(ready(Ok(U256::from(300000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
147 U256::from(100000000000000000u64), "0xSender",
149 &create_test_policy(100000000000000000u128), &mock_provider,
151 )
152 .await;
153
154 assert!(result.is_ok());
155 }
156
157 #[tokio::test]
158 async fn test_validate_sufficient_balance_with_transaction_failure() {
159 let mut mock_provider = MockEvmProviderTrait::new();
160 mock_provider
161 .expect_get_balance()
162 .with(eq("0xSender"))
163 .returning(|_| Box::pin(ready(Ok(U256::from(150000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
166 U256::from(100000000000000000u64), "0xSender",
168 &create_test_policy(100000000000000000u128), &mock_provider,
170 )
171 .await;
172
173 assert!(matches!(
174 result,
175 Err(EvmTransactionValidationError::InsufficientBalance(_))
176 ));
177 }
178
179 #[tokio::test]
180 async fn test_validate_provider_error() {
181 let mut mock_provider = MockEvmProviderTrait::new();
182 mock_provider
183 .expect_get_balance()
184 .with(eq("0xSender"))
185 .returning(|_| {
186 Box::pin(ready(Err(ProviderError::Other(
187 "Provider error".to_string(),
188 ))))
189 });
190
191 let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
192 U256::ZERO,
193 "0xSender",
194 &create_test_policy(100000000000000000u128),
195 &mock_provider,
196 )
197 .await;
198
199 assert!(matches!(
200 result,
201 Err(EvmTransactionValidationError::ProviderError(_))
202 ));
203 }
204
205 #[tokio::test]
206 async fn test_validate_no_min_balance_success() {
207 let mut mock_provider = MockEvmProviderTrait::new();
208 mock_provider
209 .expect_get_balance()
210 .with(eq("0xSender"))
211 .returning(|_| Box::pin(ready(Ok(U256::from(100000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
214 U256::from(50000000000000000u64), "0xSender",
216 &create_test_policy(0), &mock_provider,
218 )
219 .await;
220
221 assert!(result.is_ok());
222 }
223
224 #[tokio::test]
225 async fn test_validate_no_min_balance_failure() {
226 let mut mock_provider = MockEvmProviderTrait::new();
227 mock_provider
228 .expect_get_balance()
229 .with(eq("0xSender"))
230 .returning(|_| Box::pin(ready(Ok(U256::from(100000000000000000u64))))); let result = EvmTransactionValidator::validate_sufficient_relayer_balance(
233 U256::from(150000000000000000u64), "0xSender",
235 &create_test_policy(0), &mock_provider,
237 )
238 .await;
239
240 assert!(matches!(
241 result,
242 Err(EvmTransactionValidationError::InsufficientBalance(_))
243 ));
244 }
245
246 #[tokio::test]
247 async fn test_init_balance_validation_success() {
248 let mut mock_provider = MockEvmProviderTrait::new();
249 mock_provider
250 .expect_get_balance()
251 .with(eq("0xSender"))
252 .returning(|_| Box::pin(ready(Ok(U256::from(200000000000000000u64)))));
253
254 let result = EvmTransactionValidator::init_balance_validation(
255 "0xSender",
256 &create_test_policy(100000000000000000u128),
257 &mock_provider,
258 )
259 .await;
260
261 assert!(result.is_ok());
262 }
263
264 #[tokio::test]
265 async fn test_init_balance_validation_failure() {
266 let mut mock_provider = MockEvmProviderTrait::new();
267 mock_provider
268 .expect_get_balance()
269 .with(eq("0xSender"))
270 .returning(|_| Box::pin(ready(Ok(U256::from(50000000000000000u64)))));
271
272 let result = EvmTransactionValidator::init_balance_validation(
273 "0xSender",
274 &create_test_policy(100000000000000000u128),
275 &mock_provider,
276 )
277 .await;
278
279 assert!(matches!(
280 result,
281 Err(EvmTransactionValidationError::InsufficientBalance(_))
282 ));
283 }
284
285 #[tokio::test]
286 async fn test_init_balance_validation_provider_error() {
287 let mut mock_provider = MockEvmProviderTrait::new();
288 mock_provider
289 .expect_get_balance()
290 .with(eq("0xSender"))
291 .returning(|_| {
292 Box::pin(ready(Err(ProviderError::Other(
293 "Provider error".to_string(),
294 ))))
295 });
296
297 let result = EvmTransactionValidator::init_balance_validation(
298 "0xSender",
299 &create_test_policy(100000000000000000u128),
300 &mock_provider,
301 )
302 .await;
303
304 assert!(matches!(
305 result,
306 Err(EvmTransactionValidationError::ProviderError(_))
307 ));
308 }
309
310 #[tokio::test]
311 async fn test_init_balance_validation_zero_min_balance() {
312 let mut mock_provider = MockEvmProviderTrait::new();
313 mock_provider
314 .expect_get_balance()
315 .with(eq("0xSender"))
316 .returning(|_| Box::pin(ready(Ok(U256::from(0u64)))));
317
318 let result = EvmTransactionValidator::init_balance_validation(
319 "0xSender",
320 &create_test_policy(0), &mock_provider,
322 )
323 .await;
324
325 assert!(result.is_ok());
326 }
327}