1use crate::{
38 constants::{DEFAULT_GAS_LIMIT, DEFAULT_TRANSACTION_SPEED},
39 models::{
40 evm::Speed, EvmNetwork, EvmTransactionData, EvmTransactionDataTrait, RelayerRepoModel,
41 TransactionError, U256,
42 },
43 services::{
44 evm_gas_price::{EvmGasPriceServiceTrait, GasPrices},
45 gas::price_params_handler::PriceParamsHandler,
46 },
47};
48
49#[cfg(test)]
50use mockall::automock;
51
52#[async_trait::async_trait]
53#[cfg_attr(test, automock)]
54pub trait PriceCalculatorTrait: Send + Sync {
55 async fn get_transaction_price_params(
56 &self,
57 tx_data: &EvmTransactionData,
58 relayer: &RelayerRepoModel,
59 ) -> Result<PriceParams, TransactionError>;
60
61 async fn calculate_bumped_gas_price(
62 &self,
63 tx_data: &EvmTransactionData,
64 relayer: &RelayerRepoModel,
65 ) -> Result<PriceParams, TransactionError>;
66}
67
68const PRECISION: u128 = 1_000_000_000; const MINUTE_AND_HALF_MS: u128 = 90000;
70const BASE_FEE_INCREASE_RATE: f64 = 1.125; const MAX_BASE_FEE_MULTIPLIER: u128 = 10 * PRECISION; #[derive(Debug, Clone)]
74pub struct PriceParams {
75 pub gas_price: Option<u128>,
76 pub max_fee_per_gas: Option<u128>,
77 pub max_priority_fee_per_gas: Option<u128>,
78 pub is_min_bumped: Option<bool>,
79 pub extra_fee: Option<U256>,
80 pub total_cost: U256,
81}
82
83impl PriceParams {
84 pub fn calculate_total_cost(&self, is_eip1559: bool, gas_limit: u64, value: U256) -> U256 {
85 match is_eip1559 {
86 true => {
87 U256::from(self.max_fee_per_gas.unwrap_or(0)) * U256::from(gas_limit)
88 + value
89 + self.extra_fee.unwrap_or(U256::ZERO)
90 }
91 false => {
92 U256::from(self.gas_price.unwrap_or(0)) * U256::from(gas_limit)
93 + value
94 + self.extra_fee.unwrap_or(U256::ZERO)
95 }
96 }
97 }
98}
99
100pub fn calculate_min_bump(base_price: u128) -> u128 {
111 const BUMP_NUMERATOR: u128 = 11;
114 const BUMP_DENOMINATOR: u128 = 10;
115
116 let bumped_price = base_price
117 .saturating_mul(BUMP_NUMERATOR)
118 .saturating_div(BUMP_DENOMINATOR);
119
120 std::cmp::max(bumped_price, base_price.saturating_add(1))
122}
123
124pub struct PriceCalculator<G: EvmGasPriceServiceTrait> {
126 gas_price_service: G,
127 price_params_handler: Option<PriceParamsHandler>,
128}
129
130#[async_trait::async_trait]
131impl<G> PriceCalculatorTrait for PriceCalculator<G>
132where
133 G: EvmGasPriceServiceTrait + Send + Sync,
134{
135 async fn get_transaction_price_params(
136 &self,
137 tx_data: &EvmTransactionData,
138 relayer: &RelayerRepoModel,
139 ) -> Result<PriceParams, TransactionError> {
140 PriceCalculator::<G>::get_transaction_price_params(self, tx_data, relayer).await
141 }
142
143 async fn calculate_bumped_gas_price(
144 &self,
145 tx_data: &EvmTransactionData,
146 relayer: &RelayerRepoModel,
147 ) -> Result<PriceParams, TransactionError> {
148 PriceCalculator::<G>::calculate_bumped_gas_price(self, tx_data, relayer).await
149 }
150}
151
152impl<G> PriceCalculator<G>
153where
154 G: EvmGasPriceServiceTrait,
155{
156 pub fn new(gas_price_service: G, price_params_handler: Option<PriceParamsHandler>) -> Self {
157 Self {
158 gas_price_service,
159 price_params_handler,
160 }
161 }
162
163 pub async fn get_transaction_price_params(
179 &self,
180 tx_data: &EvmTransactionData,
181 relayer: &RelayerRepoModel,
182 ) -> Result<PriceParams, TransactionError> {
183 let mut price_final_params = self
184 .fetch_price_params_based_on_tx_type(tx_data, relayer)
185 .await?;
186
187 self.apply_gas_price_cap_and_constraints(&mut price_final_params, relayer)?;
189
190 self.finalize_price_params(relayer, tx_data, price_final_params)
192 .await
193 }
194
195 pub async fn calculate_bumped_gas_price(
209 &self,
210 tx_data: &EvmTransactionData,
211 relayer: &RelayerRepoModel,
212 ) -> Result<PriceParams, TransactionError> {
213 if self.gas_price_service.network().lacks_mempool()
215 || self.gas_price_service.network().is_arbitrum()
216 {
217 let mut price_params = self.get_transaction_price_params(tx_data, relayer).await?;
218
219 price_params.is_min_bumped = Some(true);
221 return Ok(price_params);
222 }
223
224 let network_gas_prices = self.gas_price_service.get_prices_from_json_rpc().await?;
225 let relayer_gas_price_cap = relayer
226 .policies
227 .get_evm_policy()
228 .gas_price_cap
229 .unwrap_or(u128::MAX);
230
231 let bumped_price_params = match (
233 tx_data.max_fee_per_gas,
234 tx_data.max_priority_fee_per_gas,
235 tx_data.gas_price,
236 ) {
237 (Some(max_fee), Some(max_priority_fee), _) => {
238 self.handle_eip1559_bump(
240 &network_gas_prices,
241 relayer_gas_price_cap,
242 tx_data.speed.as_ref(),
243 max_fee,
244 max_priority_fee,
245 )?
246 }
247 (None, None, Some(gas_price)) => {
248 self.handle_legacy_bump(
250 &network_gas_prices,
251 relayer_gas_price_cap,
252 tx_data.speed.as_ref(),
253 gas_price,
254 )?
255 }
256 _ => {
257 return Err(TransactionError::InvalidType(
258 "Transaction missing required gas price parameters".to_string(),
259 ))
260 }
261 };
262
263 self.finalize_price_params(relayer, tx_data, bumped_price_params)
265 .await
266 }
267
268 fn handle_eip1559_bump(
284 &self,
285 network_gas_prices: &GasPrices,
286 gas_price_cap: u128,
287 maybe_speed: Option<&Speed>,
288 max_fee: u128,
289 max_priority_fee: u128,
290 ) -> Result<PriceParams, TransactionError> {
291 let speed = maybe_speed.unwrap_or(&DEFAULT_TRANSACTION_SPEED);
292
293 let min_bump_max_fee = calculate_min_bump(max_fee);
295 let min_bump_max_priority = calculate_min_bump(max_priority_fee);
296
297 let current_market_priority =
299 Self::get_market_price_for_speed(network_gas_prices, true, speed);
300
301 let bumped_priority_fee = if current_market_priority >= min_bump_max_priority {
305 current_market_priority
306 } else {
307 min_bump_max_priority
308 };
309
310 let base_fee_wei = network_gas_prices.base_fee_per_gas;
313 let bumped_max_fee_per_gas = if base_fee_wei >= min_bump_max_fee {
314 base_fee_wei
315 } else {
316 min_bump_max_fee
317 };
318
319 let recommended_max_fee_per_gas = calculate_max_fee_per_gas(
322 base_fee_wei,
323 bumped_priority_fee,
324 self.gas_price_service.network(),
325 );
326
327 let final_max_fee = std::cmp::max(bumped_max_fee_per_gas, recommended_max_fee_per_gas);
329
330 let capped_priority = Self::cap_gas_price(bumped_priority_fee, gas_price_cap);
332 let capped_max_fee = Self::cap_gas_price(final_max_fee, gas_price_cap);
333
334 let is_min_bumped =
336 capped_priority >= min_bump_max_priority && capped_max_fee >= min_bump_max_fee;
337
338 Ok(PriceParams {
340 gas_price: None,
341 max_priority_fee_per_gas: Some(capped_priority),
342 max_fee_per_gas: Some(capped_max_fee),
343 is_min_bumped: Some(is_min_bumped),
344 extra_fee: None,
345 total_cost: U256::ZERO,
346 })
347 }
348
349 fn handle_legacy_bump(
354 &self,
355 network_gas_prices: &GasPrices,
356 gas_price_cap: u128,
357 maybe_speed: Option<&Speed>,
358 gas_price: u128,
359 ) -> Result<PriceParams, TransactionError> {
360 let speed = maybe_speed.unwrap_or(&Speed::Fast);
361
362 let min_bump_gas_price = calculate_min_bump(gas_price);
364
365 let current_market_price =
367 Self::get_market_price_for_speed(network_gas_prices, false, speed);
368
369 let bumped_gas_price = if current_market_price >= min_bump_gas_price {
370 current_market_price
371 } else {
372 min_bump_gas_price
373 };
374
375 let capped_gas_price = Self::cap_gas_price(bumped_gas_price, gas_price_cap);
377
378 let is_min_bumped = capped_gas_price >= min_bump_gas_price;
380
381 Ok(PriceParams {
382 gas_price: Some(capped_gas_price),
383 max_priority_fee_per_gas: None,
384 max_fee_per_gas: None,
385 is_min_bumped: Some(is_min_bumped),
386 extra_fee: None,
387 total_cost: U256::ZERO,
388 })
389 }
390 async fn fetch_price_params_based_on_tx_type(
392 &self,
393 tx_data: &EvmTransactionData,
394 relayer: &RelayerRepoModel,
395 ) -> Result<PriceParams, TransactionError> {
396 if tx_data.is_legacy() {
397 self.fetch_legacy_price_params(tx_data)
398 } else if tx_data.is_eip1559() {
399 self.fetch_eip1559_price_params(tx_data)
400 } else if tx_data.is_speed() {
401 self.fetch_speed_price_params(tx_data, relayer).await
402 } else {
403 Err(TransactionError::NotSupported(
404 "Invalid transaction type".to_string(),
405 ))
406 }
407 }
408
409 fn fetch_legacy_price_params(
417 &self,
418 tx_data: &EvmTransactionData,
419 ) -> Result<PriceParams, TransactionError> {
420 let gas_price = tx_data.gas_price.ok_or(TransactionError::NotSupported(
421 "Gas price is required for legacy transactions".to_string(),
422 ))?;
423 Ok(PriceParams {
424 gas_price: Some(gas_price),
425 max_fee_per_gas: None,
426 max_priority_fee_per_gas: None,
427 is_min_bumped: None,
428 extra_fee: None,
429 total_cost: U256::ZERO,
430 })
431 }
432
433 fn fetch_eip1559_price_params(
434 &self,
435 tx_data: &EvmTransactionData,
436 ) -> Result<PriceParams, TransactionError> {
437 let max_fee = tx_data
438 .max_fee_per_gas
439 .ok_or(TransactionError::NotSupported(
440 "Max fee per gas is required for EIP1559 transactions".to_string(),
441 ))?;
442 let max_priority_fee =
443 tx_data
444 .max_priority_fee_per_gas
445 .ok_or(TransactionError::NotSupported(
446 "Max priority fee per gas is required for EIP1559 transactions".to_string(),
447 ))?;
448 Ok(PriceParams {
449 gas_price: None,
450 max_fee_per_gas: Some(max_fee),
451 max_priority_fee_per_gas: Some(max_priority_fee),
452 is_min_bumped: None,
453 extra_fee: None,
454 total_cost: U256::ZERO,
455 })
456 }
457 async fn fetch_speed_price_params(
462 &self,
463 tx_data: &EvmTransactionData,
464 relayer: &RelayerRepoModel,
465 ) -> Result<PriceParams, TransactionError> {
466 let speed = tx_data
467 .speed
468 .as_ref()
469 .ok_or(TransactionError::NotSupported(
470 "Speed is required".to_string(),
471 ))?;
472 let use_legacy = relayer.policies.get_evm_policy().eip1559_pricing == Some(false)
473 || self.gas_price_service.network().is_legacy();
474
475 if use_legacy {
476 self.fetch_legacy_speed_params(speed).await
477 } else {
478 self.fetch_eip1559_speed_params(speed).await
479 }
480 }
481
482 async fn fetch_eip1559_speed_params(
487 &self,
488 speed: &Speed,
489 ) -> Result<PriceParams, TransactionError> {
490 let prices = self.gas_price_service.get_prices_from_json_rpc().await?;
491 let priority_fee = match speed {
492 Speed::SafeLow => prices.max_priority_fee_per_gas.safe_low,
493 Speed::Average => prices.max_priority_fee_per_gas.average,
494 Speed::Fast => prices.max_priority_fee_per_gas.fast,
495 Speed::Fastest => prices.max_priority_fee_per_gas.fastest,
496 };
497 let max_fee = calculate_max_fee_per_gas(
498 prices.base_fee_per_gas,
499 priority_fee,
500 self.gas_price_service.network(),
501 );
502 Ok(PriceParams {
503 gas_price: None,
504 max_fee_per_gas: Some(max_fee),
505 max_priority_fee_per_gas: Some(priority_fee),
506 is_min_bumped: None,
507 extra_fee: None,
508 total_cost: U256::ZERO,
509 })
510 }
511 async fn fetch_legacy_speed_params(
516 &self,
517 speed: &Speed,
518 ) -> Result<PriceParams, TransactionError> {
519 let prices = self
520 .gas_price_service
521 .get_legacy_prices_from_json_rpc()
522 .await?;
523 let gas_price = match speed {
524 Speed::SafeLow => prices.safe_low,
525 Speed::Average => prices.average,
526 Speed::Fast => prices.fast,
527 Speed::Fastest => prices.fastest,
528 };
529 Ok(PriceParams {
530 gas_price: Some(gas_price),
531 max_fee_per_gas: None,
532 max_priority_fee_per_gas: None,
533 is_min_bumped: None,
534 extra_fee: None,
535 total_cost: U256::ZERO,
536 })
537 }
538
539 fn apply_gas_price_cap_and_constraints(
545 &self,
546 price_params: &mut PriceParams,
547 relayer: &RelayerRepoModel,
548 ) -> Result<(), TransactionError> {
549 let gas_price_cap = relayer
550 .policies
551 .get_evm_policy()
552 .gas_price_cap
553 .unwrap_or(u128::MAX);
554
555 if let (Some(max_fee), Some(max_priority)) = (
556 price_params.max_fee_per_gas,
557 price_params.max_priority_fee_per_gas,
558 ) {
559 let capped_max_fee = Self::cap_gas_price(max_fee, gas_price_cap);
561 price_params.max_fee_per_gas = Some(capped_max_fee);
562
563 price_params.max_priority_fee_per_gas =
565 Some(Self::cap_gas_price(max_priority, capped_max_fee));
566
567 price_params.gas_price = None;
569 } else {
570 price_params.gas_price = Some(Self::cap_gas_price(
572 price_params.gas_price.unwrap_or_default(),
573 gas_price_cap,
574 ));
575
576 price_params.max_fee_per_gas = None;
578 price_params.max_priority_fee_per_gas = None;
579 }
580
581 Ok(())
582 }
583
584 fn cap_gas_price(price: u128, cap: u128) -> u128 {
585 std::cmp::min(price, cap)
586 }
587
588 async fn finalize_price_params(
590 &self,
591 relayer: &RelayerRepoModel,
592 tx_data: &EvmTransactionData,
593 mut price_params: PriceParams,
594 ) -> Result<PriceParams, TransactionError> {
595 let is_eip1559 = tx_data.is_eip1559();
596
597 if let Some(handler) = &self.price_params_handler {
599 price_params = handler.handle_price_params(tx_data, price_params).await?;
600
601 self.apply_gas_price_cap_and_constraints(&mut price_params, relayer)?;
603 }
604
605 if price_params.total_cost == U256::ZERO {
606 price_params.total_cost = price_params.calculate_total_cost(
607 is_eip1559,
608 tx_data.gas_limit.unwrap_or(DEFAULT_GAS_LIMIT),
609 U256::from(tx_data.value),
610 );
611 }
612
613 Ok(price_params)
614 }
615
616 fn get_market_price_for_speed(prices: &GasPrices, is_eip1559: bool, speed: &Speed) -> u128 {
619 if is_eip1559 {
620 match speed {
621 Speed::SafeLow => prices.max_priority_fee_per_gas.safe_low,
622 Speed::Average => prices.max_priority_fee_per_gas.average,
623 Speed::Fast => prices.max_priority_fee_per_gas.fast,
624 Speed::Fastest => prices.max_priority_fee_per_gas.fastest,
625 }
626 } else {
627 match speed {
628 Speed::SafeLow => prices.legacy_prices.safe_low,
629 Speed::Average => prices.legacy_prices.average,
630 Speed::Fast => prices.legacy_prices.fast,
631 Speed::Fastest => prices.legacy_prices.fastest,
632 }
633 }
634 }
635}
636
637fn get_base_fee_multiplier(network: &EvmNetwork) -> u128 {
638 let block_interval_ms = network.average_blocktime().map(|d| d.as_millis()).unwrap();
639
640 let n_blocks = MINUTE_AND_HALF_MS / block_interval_ms;
642
643 let multiplier_f64 = BASE_FEE_INCREASE_RATE.powi(n_blocks as i32);
645
646 let multiplier = (multiplier_f64 * PRECISION as f64) as u128;
648
649 std::cmp::min(multiplier, MAX_BASE_FEE_MULTIPLIER)
651}
652
653fn calculate_max_fee_per_gas(
655 base_fee_wei: u128,
656 max_priority_fee_wei: u128,
657 network: &EvmNetwork,
658) -> u128 {
659 let multiplier = get_base_fee_multiplier(network);
661
662 let multiplied_base_fee = (base_fee_wei * multiplier) / PRECISION;
664
665 multiplied_base_fee + max_priority_fee_wei
667}
668#[cfg(test)]
669mod tests {
670 use super::*;
671 use crate::constants::{ARBITRUM_BASED_TAG, NO_MEMPOOL_TAG};
672 use crate::models::{
673 evm::Speed, EvmNetwork, EvmTransactionData, NetworkType, RelayerEvmPolicy,
674 RelayerNetworkPolicy, RelayerRepoModel, U256,
675 };
676 use crate::services::{
677 evm_gas_price::{EvmGasPriceService, GasPrices, MockEvmGasPriceServiceTrait, SpeedPrices},
678 gas::handlers::test_mock::MockPriceHandler,
679 provider::MockEvmProviderTrait,
680 };
681 use futures::FutureExt;
682
683 fn create_mock_evm_network(name: &str) -> EvmNetwork {
684 let average_blocktime_ms = match name {
685 "optimism" => 2000, _ => 12000, };
688
689 EvmNetwork {
690 network: name.to_string(),
691 rpc_urls: vec!["https://rpc.example.com".to_string()],
692 explorer_urls: None,
693 average_blocktime_ms,
694 is_testnet: true,
695 tags: vec![],
696 chain_id: 1337,
697 required_confirmations: 1,
698 features: vec![],
699 symbol: "ETH".to_string(),
700 gas_price_cache: None,
701 }
702 }
703
704 fn create_mock_no_mempool_network(name: &str) -> EvmNetwork {
705 let average_blocktime_ms = match name {
706 "arbitrum" => 1000, _ => 12000, };
709
710 EvmNetwork {
711 network: name.to_string(),
712 rpc_urls: vec!["https://rpc.example.com".to_string()],
713 explorer_urls: None,
714 average_blocktime_ms,
715 is_testnet: true,
716 tags: vec![NO_MEMPOOL_TAG.to_string()], chain_id: 42161,
718 required_confirmations: 1,
719 features: vec!["eip1559".to_string()], symbol: "ETH".to_string(),
721 gas_price_cache: None,
722 }
723 }
724
725 fn create_mock_relayer() -> RelayerRepoModel {
726 RelayerRepoModel {
727 id: "test-relayer".to_string(),
728 name: "Test Relayer".to_string(),
729 network: "mainnet".to_string(),
730 network_type: NetworkType::Evm,
731 address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string(),
732 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
733 paused: false,
734 notification_id: None,
735 signer_id: "test-signer".to_string(),
736 system_disabled: false,
737 custom_rpc_urls: None,
738 ..Default::default()
739 }
740 }
741
742 #[tokio::test]
743 async fn test_legacy_transaction() {
744 let mut provider = MockEvmProviderTrait::new();
745 provider
746 .expect_get_balance()
747 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
748
749 let relayer = create_mock_relayer();
750 let gas_price_service =
751 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"), None);
752
753 let tx_data = EvmTransactionData {
754 gas_price: Some(20000000000),
755 ..Default::default()
756 };
757
758 let mut provider = MockEvmProviderTrait::new();
759 provider
760 .expect_get_balance()
761 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
762
763 let pc = PriceCalculator::new(gas_price_service, None);
765
766 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
767 assert!(result.is_ok());
768 let params = result.unwrap();
769 assert_eq!(params.gas_price, Some(20000000000));
770 assert!(params.max_fee_per_gas.is_none());
771 assert!(params.max_priority_fee_per_gas.is_none());
772 }
773
774 #[tokio::test]
775 async fn test_eip1559_transaction() {
776 let mut provider = MockEvmProviderTrait::new();
777 provider
778 .expect_get_balance()
779 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
780
781 let relayer = create_mock_relayer();
782 let gas_price_service =
783 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"), None);
784
785 let tx_data = EvmTransactionData {
786 gas_price: None,
787 max_fee_per_gas: Some(30000000000),
788 max_priority_fee_per_gas: Some(2000000000),
789 ..Default::default()
790 };
791
792 let mut provider = MockEvmProviderTrait::new();
793 provider
794 .expect_get_balance()
795 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
796
797 let pc = PriceCalculator::new(gas_price_service, None);
799
800 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
801 assert!(result.is_ok());
802 let params = result.unwrap();
803 assert!(params.gas_price.is_none());
804 assert_eq!(params.max_fee_per_gas, Some(30000000000));
805 assert_eq!(params.max_priority_fee_per_gas, Some(2000000000));
806 }
807
808 #[tokio::test]
809 async fn test_speed_legacy_based_transaction() {
810 let mut provider = MockEvmProviderTrait::new();
811 provider
812 .expect_get_balance()
813 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
814 provider
815 .expect_get_gas_price()
816 .returning(|| async { Ok(20000000000) }.boxed());
817
818 let relayer = create_mock_relayer();
819 let gas_price_service =
820 EvmGasPriceService::new(provider, create_mock_evm_network("celo"), None);
821
822 let tx_data = EvmTransactionData {
823 gas_price: None,
824 speed: Some(Speed::Fast),
825 ..Default::default()
826 };
827
828 let mut provider = MockEvmProviderTrait::new();
829 provider
830 .expect_get_balance()
831 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
832 provider
833 .expect_get_gas_price()
834 .returning(|| async { Ok(20000000000) }.boxed());
835
836 let pc = PriceCalculator::new(gas_price_service, None);
837
838 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
839 assert!(result.is_ok());
840 let params = result.unwrap();
841 assert!(
842 params.gas_price.is_some()
843 || (params.max_fee_per_gas.is_some() && params.max_priority_fee_per_gas.is_some())
844 );
845 }
846
847 #[tokio::test]
848 async fn test_invalid_transaction_type() {
849 let mut provider = MockEvmProviderTrait::new();
850 provider
851 .expect_get_balance()
852 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
853
854 let relayer = create_mock_relayer();
855 let gas_price_service =
856 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"), None);
857
858 let tx_data = EvmTransactionData {
859 gas_price: None,
860 ..Default::default()
861 };
862
863 let mut provider = MockEvmProviderTrait::new();
864 provider
865 .expect_get_balance()
866 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
867
868 let pc = PriceCalculator::new(gas_price_service, None);
869
870 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
871 assert!(result.is_err());
872 assert!(matches!(
873 result.unwrap_err(),
874 TransactionError::NotSupported(_)
875 ));
876 }
877
878 #[tokio::test]
879 async fn test_gas_price_cap() {
880 let mut provider = MockEvmProviderTrait::new();
881 provider
882 .expect_get_balance()
883 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
884
885 let mut relayer = create_mock_relayer();
886 let gas_price_service =
887 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"), None);
888
889 let evm_policy = RelayerEvmPolicy {
891 gas_price_cap: Some(10000000000),
892 eip1559_pricing: Some(true),
893 ..RelayerEvmPolicy::default()
894 };
895 relayer.policies = RelayerNetworkPolicy::Evm(evm_policy);
896
897 let tx_data = EvmTransactionData {
898 gas_price: Some(20000000000), ..Default::default()
900 };
901
902 let mut provider = MockEvmProviderTrait::new();
903 provider
904 .expect_get_balance()
905 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
906
907 let pc = PriceCalculator::new(gas_price_service, None);
908
909 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
910 assert!(result.is_ok());
911 let params = result.unwrap();
912 assert_eq!(params.gas_price, Some(10000000000)); }
914
915 #[test]
916 fn test_get_base_fee_multiplier() {
917 let mainnet = create_mock_evm_network("mainnet");
918 let multiplier = super::get_base_fee_multiplier(&mainnet);
919 assert!(multiplier > 2_200_000_000 && multiplier < 2_400_000_000);
921
922 let optimism = create_mock_evm_network("optimism");
923 let multiplier = super::get_base_fee_multiplier(&optimism);
924 assert_eq!(multiplier, MAX_BASE_FEE_MULTIPLIER);
926 }
927
928 #[test]
929 fn test_get_base_fee_multiplier_overflow_protection() {
930 let mut test_network = create_mock_evm_network("test");
932
933 test_network.average_blocktime_ms = 1;
935 let multiplier = super::get_base_fee_multiplier(&test_network);
936 assert_eq!(multiplier, MAX_BASE_FEE_MULTIPLIER);
938
939 test_network.average_blocktime_ms = 100;
941 let multiplier = super::get_base_fee_multiplier(&test_network);
942 assert_eq!(multiplier, MAX_BASE_FEE_MULTIPLIER);
944
945 test_network.average_blocktime_ms = 1000;
947 let multiplier = super::get_base_fee_multiplier(&test_network);
948 assert_eq!(multiplier, MAX_BASE_FEE_MULTIPLIER);
950
951 test_network.average_blocktime_ms = 5000;
953 let multiplier = super::get_base_fee_multiplier(&test_network);
954 assert!(multiplier > 8_000_000_000 && multiplier < 9_000_000_000);
956 assert!(multiplier < MAX_BASE_FEE_MULTIPLIER);
957
958 test_network.average_blocktime_ms = 10000;
960 let multiplier = super::get_base_fee_multiplier(&test_network);
961 assert!(multiplier > 2_500_000_000 && multiplier < 3_000_000_000);
963 }
964
965 #[test]
966 fn test_calculate_max_fee_per_gas() {
967 let network = create_mock_evm_network("mainnet");
968 let base_fee = 100_000_000_000u128; let priority_fee = 2_000_000_000u128; let max_fee = super::calculate_max_fee_per_gas(base_fee, priority_fee, &network);
972 assert!(max_fee > 225_000_000_000 && max_fee < 235_000_000_000);
975 }
976
977 #[tokio::test]
978 async fn test_handle_eip1559_speed() {
979 let mut mock_gas_price_service = MockEvmGasPriceServiceTrait::new();
980
981 let test_data = [
983 (Speed::SafeLow, 1_000_000_000),
984 (Speed::Average, 2_000_000_000),
985 (Speed::Fast, 3_000_000_000),
986 (Speed::Fastest, 4_000_000_000),
987 ];
988 let mock_prices = GasPrices {
990 legacy_prices: SpeedPrices {
991 safe_low: 10_000_000_000,
992 average: 12_500_000_000,
993 fast: 15_000_000_000,
994 fastest: 20_000_000_000,
995 },
996 max_priority_fee_per_gas: SpeedPrices {
997 safe_low: 1_000_000_000,
998 average: 2_000_000_000,
999 fast: 3_000_000_000,
1000 fastest: 4_000_000_000,
1001 },
1002 base_fee_per_gas: 50_000_000_000,
1003 };
1004
1005 mock_gas_price_service
1007 .expect_get_prices_from_json_rpc()
1008 .returning(move || {
1009 let prices = mock_prices.clone();
1010 Box::pin(async move { Ok(prices) })
1011 });
1012
1013 let network = create_mock_evm_network("mainnet");
1015 mock_gas_price_service
1016 .expect_network()
1017 .return_const(network);
1018
1019 let pc = PriceCalculator::new(mock_gas_price_service, None);
1021
1022 for (speed, expected_priority_fee) in test_data {
1023 let result = pc.fetch_eip1559_speed_params(&speed).await;
1025 assert!(result.is_ok());
1026 let params = result.unwrap();
1027 assert_eq!(params.max_priority_fee_per_gas, Some(expected_priority_fee));
1029
1030 let max_fee = params.max_fee_per_gas.unwrap();
1034 let expected_base_portion = 120_000_000_000; assert!(max_fee < expected_base_portion + expected_priority_fee + 2_000_000_000);
1036 }
1037 }
1038
1039 #[tokio::test]
1040 async fn test_calculate_bumped_gas_price_eip1559_basic() {
1041 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1042 let mock_prices = GasPrices {
1043 legacy_prices: SpeedPrices {
1044 safe_low: 8_000_000_000,
1045 average: 10_000_000_000,
1046 fast: 12_000_000_000,
1047 fastest: 15_000_000_000,
1048 },
1049 max_priority_fee_per_gas: SpeedPrices {
1050 safe_low: 1_000_000_000,
1051 average: 2_000_000_000,
1052 fast: 3_000_000_000,
1053 fastest: 4_000_000_000,
1054 },
1055 base_fee_per_gas: 50_000_000_000,
1056 };
1057 mock_service
1058 .expect_get_prices_from_json_rpc()
1059 .returning(move || {
1060 let prices = mock_prices.clone();
1061 Box::pin(async move { Ok(prices) })
1062 });
1063 mock_service
1064 .expect_network()
1065 .return_const(create_mock_evm_network("mainnet"));
1066
1067 let pc = PriceCalculator::new(mock_service, None);
1068 let mut relayer = create_mock_relayer();
1069 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1071 gas_price_cap: Some(300_000_000_000u128),
1072 ..Default::default()
1073 });
1074
1075 let tx_data = EvmTransactionData {
1076 max_fee_per_gas: Some(100_000_000_000),
1077 max_priority_fee_per_gas: Some(2_000_000_000),
1078 speed: Some(Speed::Fast),
1079 ..Default::default()
1080 };
1081
1082 let bumped = pc
1083 .calculate_bumped_gas_price(&tx_data, &relayer)
1084 .await
1085 .unwrap();
1086 assert!(bumped.max_fee_per_gas.unwrap() >= 110_000_000_000); assert!(bumped.max_priority_fee_per_gas.unwrap() >= 2_200_000_000); }
1089
1090 #[tokio::test]
1091 async fn test_calculate_bumped_gas_price_eip1559_market_lower_than_min_bump() {
1092 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1093 let mock_prices = GasPrices {
1094 legacy_prices: SpeedPrices::default(),
1095 max_priority_fee_per_gas: SpeedPrices {
1096 safe_low: 1_500_000_000, average: 2_500_000_000,
1098 fast: 2_700_000_000,
1099 fastest: 3_000_000_000,
1100 },
1101 base_fee_per_gas: 30_000_000_000,
1102 };
1103 mock_service
1104 .expect_get_prices_from_json_rpc()
1105 .returning(move || {
1106 let prices = mock_prices.clone();
1107 Box::pin(async move { Ok(prices) })
1108 });
1109 mock_service
1110 .expect_network()
1111 .return_const(create_mock_evm_network("mainnet"));
1112
1113 let pc = PriceCalculator::new(mock_service, None);
1114 let relayer = create_mock_relayer();
1115
1116 let tx_data = EvmTransactionData {
1119 max_fee_per_gas: Some(20_000_000_000),
1120 max_priority_fee_per_gas: Some(2_000_000_000),
1121 speed: Some(Speed::SafeLow),
1122 ..Default::default()
1123 };
1124
1125 let bumped = pc
1126 .calculate_bumped_gas_price(&tx_data, &relayer)
1127 .await
1128 .unwrap();
1129 assert!(bumped.max_priority_fee_per_gas.unwrap() >= 2_200_000_000);
1130 assert!(bumped.max_fee_per_gas.unwrap() > 20_000_000_000);
1131 }
1132
1133 #[tokio::test]
1134 async fn test_calculate_bumped_gas_price_legacy_basic() {
1135 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1136 let mock_prices = GasPrices {
1137 legacy_prices: SpeedPrices {
1138 safe_low: 10_000_000_000,
1139 average: 12_000_000_000,
1140 fast: 14_000_000_000,
1141 fastest: 18_000_000_000,
1142 },
1143 max_priority_fee_per_gas: SpeedPrices::default(),
1144 base_fee_per_gas: 0,
1145 };
1146 mock_service
1147 .expect_get_prices_from_json_rpc()
1148 .returning(move || {
1149 let prices = mock_prices.clone();
1150 Box::pin(async move { Ok(prices) })
1151 });
1152 mock_service
1153 .expect_network()
1154 .return_const(create_mock_evm_network("mainnet"));
1155
1156 let pc = PriceCalculator::new(mock_service, None);
1157 let relayer = create_mock_relayer();
1158 let tx_data = EvmTransactionData {
1159 gas_price: Some(10_000_000_000),
1160 speed: Some(Speed::Fast),
1161 ..Default::default()
1162 };
1163
1164 let bumped = pc
1165 .calculate_bumped_gas_price(&tx_data, &relayer)
1166 .await
1167 .unwrap();
1168 assert!(bumped.gas_price.unwrap() >= 11_000_000_000); }
1170
1171 #[tokio::test]
1172 async fn test_calculate_bumped_gas_price_missing_params() {
1173 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1174
1175 mock_service
1177 .expect_get_prices_from_json_rpc()
1178 .times(1)
1179 .returning(|| Box::pin(async { Ok(GasPrices::default()) }));
1180
1181 mock_service
1183 .expect_network()
1184 .return_const(create_mock_evm_network("mainnet"));
1185
1186 let pc = PriceCalculator::new(mock_service, None);
1187 let relayer = create_mock_relayer();
1188 let tx_data = EvmTransactionData {
1190 gas_price: None,
1191 max_fee_per_gas: None,
1192 max_priority_fee_per_gas: None,
1193 ..Default::default()
1194 };
1195
1196 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1197 assert!(result.is_err());
1198 if let Err(TransactionError::InvalidType(msg)) = result {
1199 assert!(msg.contains("missing required gas price parameters"));
1200 } else {
1201 panic!("Expected InvalidType error");
1202 }
1203 }
1204
1205 #[tokio::test]
1206 async fn test_calculate_bumped_gas_price_capped() {
1207 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1208 let mock_prices = GasPrices {
1209 legacy_prices: SpeedPrices::default(),
1210 max_priority_fee_per_gas: SpeedPrices {
1211 safe_low: 4_000_000_000,
1212 average: 5_000_000_000,
1213 fast: 6_000_000_000,
1214 fastest: 8_000_000_000,
1215 },
1216 base_fee_per_gas: 100_000_000_000,
1217 };
1218 mock_service
1219 .expect_get_prices_from_json_rpc()
1220 .returning(move || {
1221 let prices = mock_prices.clone();
1222 Box::pin(async move { Ok(prices) })
1223 });
1224 mock_service
1225 .expect_network()
1226 .return_const(create_mock_evm_network("mainnet"));
1227
1228 let pc = PriceCalculator::new(mock_service, None);
1229 let mut relayer = create_mock_relayer();
1230 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1231 gas_price_cap: Some(105_000_000_000),
1232 ..Default::default()
1233 });
1234
1235 let tx_data = EvmTransactionData {
1236 max_fee_per_gas: Some(90_000_000_000),
1237 max_priority_fee_per_gas: Some(4_000_000_000),
1238 speed: Some(Speed::Fastest),
1239 ..Default::default()
1240 };
1241
1242 let bumped = pc
1244 .calculate_bumped_gas_price(&tx_data, &relayer)
1245 .await
1246 .unwrap();
1247 assert!(bumped.max_fee_per_gas.unwrap() <= 105_000_000_000);
1248 assert!(bumped.max_priority_fee_per_gas.unwrap() <= 105_000_000_000);
1249 }
1250
1251 #[tokio::test]
1252 async fn test_is_min_bumped_flag_eip1559() {
1253 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1254 let mock_prices = GasPrices {
1255 legacy_prices: SpeedPrices::default(),
1256 max_priority_fee_per_gas: SpeedPrices {
1257 safe_low: 1_000_000_000,
1258 average: 2_000_000_000,
1259 fast: 3_000_000_000,
1260 fastest: 4_000_000_000,
1261 },
1262 base_fee_per_gas: 40_000_000_000,
1263 };
1264 mock_service
1265 .expect_get_prices_from_json_rpc()
1266 .returning(move || {
1267 let prices = mock_prices.clone();
1268 Box::pin(async move { Ok(prices) })
1269 });
1270 mock_service
1271 .expect_network()
1272 .return_const(create_mock_evm_network("mainnet"));
1273
1274 let pc = PriceCalculator::new(mock_service, None);
1275 let mut relayer = create_mock_relayer();
1276
1277 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1279 gas_price_cap: Some(200_000_000_000u128),
1280 ..Default::default()
1281 });
1282
1283 let tx_data = EvmTransactionData {
1284 max_fee_per_gas: Some(50_000_000_000),
1285 max_priority_fee_per_gas: Some(2_000_000_000),
1286 speed: Some(Speed::Fast),
1287 ..Default::default()
1288 };
1289
1290 let bumped = pc
1291 .calculate_bumped_gas_price(&tx_data, &relayer)
1292 .await
1293 .unwrap();
1294 assert_eq!(
1295 bumped.is_min_bumped,
1296 Some(true),
1297 "Should be min bumped when prices are high enough"
1298 );
1299
1300 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1302 gas_price_cap: Some(50_000_000_000u128), ..Default::default()
1304 });
1305
1306 let tx_data = EvmTransactionData {
1307 max_fee_per_gas: Some(50_000_000_000),
1308 max_priority_fee_per_gas: Some(2_000_000_000),
1309 speed: Some(Speed::Fast),
1310 ..Default::default()
1311 };
1312
1313 let bumped = pc
1314 .calculate_bumped_gas_price(&tx_data, &relayer)
1315 .await
1316 .unwrap();
1317 assert_eq!(
1319 bumped.is_min_bumped,
1320 Some(false),
1321 "Should not be min bumped when cap is too low"
1322 );
1323 }
1324
1325 #[tokio::test]
1326 async fn test_is_min_bumped_flag_legacy() {
1327 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1328 let mock_prices = GasPrices {
1329 legacy_prices: SpeedPrices {
1330 safe_low: 8_000_000_000,
1331 average: 10_000_000_000,
1332 fast: 12_000_000_000,
1333 fastest: 15_000_000_000,
1334 },
1335 max_priority_fee_per_gas: SpeedPrices::default(),
1336 base_fee_per_gas: 0,
1337 };
1338 mock_service
1339 .expect_get_prices_from_json_rpc()
1340 .returning(move || {
1341 let prices = mock_prices.clone();
1342 Box::pin(async move { Ok(prices) })
1343 });
1344 mock_service
1345 .expect_network()
1346 .return_const(create_mock_evm_network("mainnet"));
1347
1348 let pc = PriceCalculator::new(mock_service, None);
1349 let mut relayer = create_mock_relayer();
1350
1351 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1353 gas_price_cap: Some(100_000_000_000u128),
1354 ..Default::default()
1355 });
1356
1357 let tx_data = EvmTransactionData {
1358 gas_price: Some(10_000_000_000),
1359 speed: Some(Speed::Fast),
1360 ..Default::default()
1361 };
1362
1363 let bumped = pc
1364 .calculate_bumped_gas_price(&tx_data, &relayer)
1365 .await
1366 .unwrap();
1367 assert_eq!(
1368 bumped.is_min_bumped,
1369 Some(true),
1370 "Should be min bumped with sufficient cap"
1371 );
1372
1373 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1375 gas_price_cap: Some(10_000_000_000u128), ..Default::default()
1377 });
1378
1379 let bumped = pc
1380 .calculate_bumped_gas_price(&tx_data, &relayer)
1381 .await
1382 .unwrap();
1383 assert_eq!(
1384 bumped.is_min_bumped,
1385 Some(false),
1386 "Should not be min bumped with insufficient cap"
1387 );
1388 }
1389
1390 #[tokio::test]
1391 async fn test_calculate_bumped_gas_price_with_extra_fee() {
1392 let mut mock_gas_service = MockEvmGasPriceServiceTrait::new();
1394 mock_gas_service
1395 .expect_get_prices_from_json_rpc()
1396 .returning(|| {
1397 Box::pin(async {
1398 Ok(GasPrices {
1399 legacy_prices: SpeedPrices {
1400 safe_low: 10_000_000_000,
1401 average: 12_000_000_000,
1402 fast: 14_000_000_000,
1403 fastest: 18_000_000_000,
1404 },
1405 max_priority_fee_per_gas: SpeedPrices::default(),
1406 base_fee_per_gas: 40_000_000_000,
1407 })
1408 })
1409 });
1410 mock_gas_service
1411 .expect_network()
1412 .return_const(create_mock_evm_network("mainnet"));
1413
1414 let pc = PriceCalculator::new(mock_gas_service, None);
1416
1417 let relayer = create_mock_relayer();
1419 let tx_data = EvmTransactionData {
1420 max_fee_per_gas: Some(50_000_000_000),
1421 max_priority_fee_per_gas: Some(2_000_000_000),
1422 speed: Some(Speed::Fast),
1423 ..Default::default()
1424 };
1425
1426 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1428
1429 assert!(result.is_ok());
1431 let price_params = result.unwrap();
1432 assert_eq!(price_params.extra_fee, None);
1433 }
1434
1435 #[tokio::test]
1436 async fn test_total_cost_recomputed_without_overrider_legacy() {
1437 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1438 let mock_prices = GasPrices {
1439 legacy_prices: SpeedPrices {
1440 safe_low: 10_000_000_000,
1441 average: 12_000_000_000,
1442 fast: 14_000_000_000,
1443 fastest: 18_000_000_000,
1444 },
1445 max_priority_fee_per_gas: SpeedPrices::default(),
1446 base_fee_per_gas: 0,
1447 };
1448 mock_service
1449 .expect_get_prices_from_json_rpc()
1450 .returning(move || {
1451 let prices = mock_prices.clone();
1452 Box::pin(async move { Ok(prices) })
1453 });
1454 mock_service
1455 .expect_network()
1456 .return_const(create_mock_evm_network("mainnet"));
1457
1458 let pc = PriceCalculator::new(mock_service, None);
1459 let relayer = create_mock_relayer();
1460
1461 let gas_limit = 21_000u64;
1462 let tx_data = EvmTransactionData {
1463 gas_price: Some(20_000_000_000),
1464 gas_limit: Some(gas_limit),
1465 value: U256::ZERO,
1466 ..Default::default()
1467 };
1468
1469 let params = pc
1470 .calculate_bumped_gas_price(&tx_data, &relayer)
1471 .await
1472 .unwrap();
1473
1474 let expected = U256::from(params.gas_price.unwrap()) * U256::from(gas_limit);
1476 assert_eq!(params.total_cost, expected);
1477 }
1478
1479 #[tokio::test]
1480 async fn test_total_cost_respected_with_overrider_nonzero_total_legacy() {
1481 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1482 let mock_prices = GasPrices {
1483 legacy_prices: SpeedPrices {
1484 safe_low: 10_000_000_000,
1485 average: 12_000_000_000,
1486 fast: 14_000_000_000,
1487 fastest: 18_000_000_000,
1488 },
1489 max_priority_fee_per_gas: SpeedPrices::default(),
1490 base_fee_per_gas: 0,
1491 };
1492 mock_service
1493 .expect_get_prices_from_json_rpc()
1494 .returning(move || {
1495 let prices = mock_prices.clone();
1496 Box::pin(async move { Ok(prices) })
1497 });
1498 mock_service
1499 .expect_network()
1500 .return_const(create_mock_evm_network("mainnet"));
1501
1502 let handler = Some(PriceParamsHandler::Mock(MockPriceHandler::new()));
1503 let pc = PriceCalculator::new(mock_service, handler);
1504 let relayer = create_mock_relayer();
1505
1506 let tx_data = EvmTransactionData {
1507 gas_price: Some(20_000_000_000),
1508 gas_limit: Some(21_000),
1509 value: U256::ZERO,
1510 ..Default::default()
1511 };
1512
1513 let params = pc
1514 .calculate_bumped_gas_price(&tx_data, &relayer)
1515 .await
1516 .unwrap();
1517
1518 assert_eq!(params.extra_fee, Some(U256::from(42u128)));
1521 assert_eq!(params.total_cost, U256::from(42u128));
1522 }
1523
1524 #[tokio::test]
1525 async fn test_get_transaction_price_params_with_mock_overrider_legacy() {
1526 use crate::services::gas::handlers::test_mock::MockPriceHandler;
1527
1528 let mut mock_gas_service = MockEvmGasPriceServiceTrait::new();
1529 mock_gas_service
1530 .expect_get_legacy_prices_from_json_rpc()
1531 .returning(|| Box::pin(async { Ok(SpeedPrices::default()) }));
1532 mock_gas_service
1533 .expect_network()
1534 .return_const(create_mock_evm_network("mainnet"));
1535
1536 let price_params_handler = Some(PriceParamsHandler::Mock(MockPriceHandler::new()));
1537 let pc = PriceCalculator::new(mock_gas_service, price_params_handler);
1538
1539 let relayer = create_mock_relayer();
1540 let tx_data = EvmTransactionData {
1541 gas_price: Some(20_000_000_000),
1542 ..Default::default()
1543 };
1544
1545 let params = pc
1546 .get_transaction_price_params(&tx_data, &relayer)
1547 .await
1548 .unwrap();
1549 assert_eq!(params.extra_fee, Some(U256::from(42u128)));
1550 assert!(params.total_cost >= U256::from(42u128));
1551 }
1552
1553 #[tokio::test]
1554 async fn test_get_transaction_price_params_with_mock_overrider_eip1559() {
1555 use crate::services::gas::handlers::test_mock::MockPriceHandler;
1556
1557 let mut mock_gas_service = MockEvmGasPriceServiceTrait::new();
1558 let mock_prices = GasPrices {
1559 legacy_prices: SpeedPrices::default(),
1560 max_priority_fee_per_gas: SpeedPrices::default(),
1561 base_fee_per_gas: 50_000_000_000,
1562 };
1563 mock_gas_service
1564 .expect_get_prices_from_json_rpc()
1565 .returning(move || {
1566 let prices = mock_prices.clone();
1567 Box::pin(async move { Ok(prices) })
1568 });
1569 mock_gas_service
1570 .expect_network()
1571 .return_const(create_mock_evm_network("mainnet"));
1572
1573 let price_params_handler = Some(PriceParamsHandler::Mock(MockPriceHandler::new()));
1574 let pc = PriceCalculator::new(mock_gas_service, price_params_handler);
1575
1576 let relayer = create_mock_relayer();
1577 let tx_data = EvmTransactionData {
1578 max_fee_per_gas: Some(30_000_000_000),
1579 max_priority_fee_per_gas: Some(2_000_000_000),
1580 ..Default::default()
1581 };
1582
1583 let params = pc
1584 .get_transaction_price_params(&tx_data, &relayer)
1585 .await
1586 .unwrap();
1587 assert_eq!(params.extra_fee, Some(U256::from(42u128)));
1588 assert!(params.total_cost >= U256::from(42u128));
1589 }
1590
1591 #[tokio::test]
1592 async fn test_get_transaction_price_params_recompute_without_overrider_legacy() {
1593 let mut mock_gas_service = MockEvmGasPriceServiceTrait::new();
1594 mock_gas_service
1595 .expect_get_legacy_prices_from_json_rpc()
1596 .returning(|| Box::pin(async { Ok(SpeedPrices::default()) }));
1597 mock_gas_service
1598 .expect_network()
1599 .return_const(create_mock_evm_network("mainnet"));
1600
1601 let pc = PriceCalculator::new(mock_gas_service, None);
1602 let relayer = create_mock_relayer();
1603
1604 let gas_limit = 21_000u64;
1605 let tx_data = EvmTransactionData {
1606 gas_price: Some(20_000_000_000),
1607 gas_limit: Some(gas_limit),
1608 value: U256::ZERO,
1609 ..Default::default()
1610 };
1611
1612 let params = pc
1613 .get_transaction_price_params(&tx_data, &relayer)
1614 .await
1615 .unwrap();
1616
1617 let expected = U256::from(params.gas_price.unwrap()) * U256::from(gas_limit);
1618 assert_eq!(params.total_cost, expected);
1619 }
1620
1621 #[test]
1622 fn test_calculate_total_cost_eip1559() {
1623 let price_params = PriceParams {
1624 gas_price: None,
1625 max_fee_per_gas: Some(30_000_000_000),
1626 max_priority_fee_per_gas: Some(2_000_000_000),
1627 is_min_bumped: None,
1628 extra_fee: None,
1629 total_cost: U256::ZERO,
1630 };
1631
1632 let gas_limit = 100_000;
1633 let value = U256::from(1_000_000_000_000_000_000u128); let is_eip1559 = true;
1635
1636 let total_cost = price_params.calculate_total_cost(is_eip1559, gas_limit, value);
1637
1638 let expected = U256::from(30_000_000_000u128) * U256::from(gas_limit) + value;
1640 assert_eq!(total_cost, expected);
1641 }
1642
1643 #[test]
1644 fn test_calculate_total_cost_legacy() {
1645 let price_params = PriceParams {
1646 gas_price: Some(20_000_000_000),
1647 max_fee_per_gas: None,
1648 max_priority_fee_per_gas: None,
1649 is_min_bumped: None,
1650 extra_fee: None,
1651 total_cost: U256::ZERO,
1652 };
1653
1654 let gas_limit = 100_000;
1655 let value = U256::from(1_000_000_000_000_000_000u128); let is_eip1559 = false;
1657
1658 let total_cost = price_params.calculate_total_cost(is_eip1559, gas_limit, value);
1659
1660 let expected = U256::from(20_000_000_000u128) * U256::from(gas_limit) + value;
1662 assert_eq!(total_cost, expected);
1663 }
1664
1665 #[test]
1666 fn test_calculate_total_cost_with_extra_fee() {
1667 let price_params = PriceParams {
1668 gas_price: Some(20_000_000_000),
1669 max_fee_per_gas: None,
1670 max_priority_fee_per_gas: None,
1671 is_min_bumped: None,
1672 extra_fee: Some(U256::from(5_000_000_000u128)),
1673 total_cost: U256::ZERO,
1674 };
1675
1676 let gas_limit = 100_000;
1677 let value = U256::from(1_000_000_000_000_000_000u128); let is_eip1559 = false;
1679
1680 let total_cost = price_params.calculate_total_cost(is_eip1559, gas_limit, value);
1681
1682 let expected = U256::from(20_000_000_000u128) * U256::from(gas_limit)
1683 + value
1684 + U256::from(5_000_000_000u128);
1685 assert_eq!(total_cost, expected);
1686 }
1687
1688 #[test]
1689 fn test_calculate_total_cost_zero_values() {
1690 let price_params = PriceParams {
1691 gas_price: Some(0),
1692 max_fee_per_gas: Some(0),
1693 max_priority_fee_per_gas: Some(0),
1694 is_min_bumped: None,
1695 extra_fee: Some(U256::ZERO),
1696 total_cost: U256::ZERO,
1697 };
1698
1699 let gas_limit = 0;
1700 let value = U256::from(0);
1701
1702 let legacy_total_cost = price_params.calculate_total_cost(false, gas_limit, value);
1703 assert_eq!(legacy_total_cost, U256::ZERO);
1704
1705 let eip1559_total_cost = price_params.calculate_total_cost(true, gas_limit, value);
1706 assert_eq!(eip1559_total_cost, U256::ZERO);
1707 }
1708
1709 #[test]
1710 fn test_calculate_total_cost_missing_values() {
1711 let price_params = PriceParams {
1712 gas_price: None,
1713 max_fee_per_gas: None,
1714 max_priority_fee_per_gas: None,
1715 is_min_bumped: None,
1716 extra_fee: None,
1717 total_cost: U256::ZERO,
1718 };
1719
1720 let gas_limit = 100_000;
1721 let value = U256::from(1_000_000_000_000_000_000u128);
1722
1723 let legacy_total = price_params.calculate_total_cost(false, gas_limit, value);
1724 assert_eq!(legacy_total, value);
1725
1726 let eip1559_total = price_params.calculate_total_cost(true, gas_limit, value);
1727 assert_eq!(eip1559_total, value);
1728 }
1729
1730 #[test]
1731 fn test_calculate_min_bump_normal_cases() {
1732 let base_price = 20_000_000_000u128; let expected = 22_000_000_000u128; assert_eq!(calculate_min_bump(base_price), expected);
1735
1736 let base_price = 1_000_000_000u128;
1737 let expected = 1_100_000_000u128; assert_eq!(calculate_min_bump(base_price), expected);
1739
1740 let base_price = 100_000_000_000u128;
1741 let expected = 110_000_000_000u128; assert_eq!(calculate_min_bump(base_price), expected);
1743 }
1744
1745 #[test]
1746 fn test_calculate_min_bump_edge_cases() {
1747 assert_eq!(calculate_min_bump(0), 1);
1749
1750 let result = calculate_min_bump(1);
1752 assert!(result >= 2);
1753
1754 let base_price = 5u128; let result = calculate_min_bump(base_price);
1757 assert!(
1758 result > base_price,
1759 "Result {} should be greater than base_price {}",
1760 result,
1761 base_price
1762 );
1763
1764 let base_price = 9u128;
1765 let result = calculate_min_bump(base_price);
1766 assert_eq!(
1767 result, 10u128,
1768 "9 wei should bump to 10 wei (minimum 1 wei increase)"
1769 );
1770 }
1771
1772 #[test]
1773 fn test_calculate_min_bump_large_values() {
1774 let base_price = u128::MAX / 2;
1776 let result = calculate_min_bump(base_price);
1777 assert!(result > base_price);
1778
1779 let base_price = u128::MAX - 1000;
1781 let result = calculate_min_bump(base_price);
1782 assert!(result >= base_price.saturating_add(1));
1784 }
1785
1786 #[test]
1787 fn test_calculate_min_bump_overflow_protection() {
1788 let base_price = u128::MAX;
1789 let result = calculate_min_bump(base_price);
1790 assert_eq!(result, u128::MAX);
1791
1792 let base_price = (u128::MAX / 11) * 10 + 1;
1793 let result = calculate_min_bump(base_price);
1794 assert!(result >= base_price);
1795 }
1796
1797 #[test]
1798 fn test_calculate_min_bump_minimum_increase_guarantee() {
1799 let test_cases = vec![0, 1, 2, 5, 9, 10, 100, 1000, 10000];
1801
1802 for base_price in test_cases {
1803 let result = calculate_min_bump(base_price);
1804 assert!(
1805 result > base_price,
1806 "calculate_min_bump({}) = {} should be greater than base_price",
1807 base_price,
1808 result
1809 );
1810 assert!(
1811 result >= base_price.saturating_add(1),
1812 "calculate_min_bump({}) = {} should be at least base_price + 1",
1813 base_price,
1814 result
1815 );
1816 }
1817 }
1818
1819 #[tokio::test]
1820 async fn test_calculate_bumped_gas_price_no_mempool_network_eip1559() {
1821 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1822
1823 mock_service
1825 .expect_network()
1826 .return_const(create_mock_no_mempool_network("arbitrum"));
1827
1828 let mock_prices = GasPrices {
1830 legacy_prices: SpeedPrices {
1831 safe_low: 1_000_000_000,
1832 average: 2_000_000_000,
1833 fast: 3_000_000_000,
1834 fastest: 4_000_000_000,
1835 },
1836 max_priority_fee_per_gas: SpeedPrices {
1837 safe_low: 1_000_000_000,
1838 average: 2_000_000_000,
1839 fast: 3_000_000_000,
1840 fastest: 4_000_000_000,
1841 },
1842 base_fee_per_gas: 50_000_000_000,
1843 };
1844
1845 mock_service
1846 .expect_get_prices_from_json_rpc()
1847 .returning(move || {
1848 let prices = mock_prices.clone();
1849 Box::pin(async move { Ok(prices) })
1850 });
1851
1852 mock_service
1854 .expect_get_legacy_prices_from_json_rpc()
1855 .returning(|| {
1856 Box::pin(async {
1857 Ok(SpeedPrices {
1858 safe_low: 1_000_000_000,
1859 average: 2_000_000_000,
1860 fast: 3_000_000_000,
1861 fastest: 4_000_000_000,
1862 })
1863 })
1864 });
1865
1866 let pc = PriceCalculator::new(mock_service, None);
1867 let mut relayer = create_mock_relayer();
1868
1869 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1871 eip1559_pricing: Some(true),
1872 ..Default::default()
1873 });
1874
1875 let tx_data = EvmTransactionData {
1877 speed: Some(Speed::Fast),
1878 gas_limit: Some(21000),
1879 value: U256::from(1_000_000_000_000_000_000u128), ..Default::default()
1881 };
1882
1883 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1884
1885 assert!(result.is_ok());
1886 let price_params = result.unwrap();
1887
1888 assert_eq!(price_params.is_min_bumped, Some(true));
1890
1891 assert!(
1897 price_params.max_priority_fee_per_gas.is_some() || price_params.gas_price.is_some()
1898 );
1899
1900 assert!(price_params.total_cost > U256::ZERO);
1902 }
1903
1904 #[tokio::test]
1905 async fn test_calculate_bumped_gas_price_no_mempool_network_legacy() {
1906 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1907
1908 mock_service
1910 .expect_network()
1911 .return_const(create_mock_no_mempool_network("arbitrum"));
1912
1913 let mock_legacy_prices = SpeedPrices {
1915 safe_low: 10_000_000_000,
1916 average: 12_000_000_000,
1917 fast: 14_000_000_000,
1918 fastest: 18_000_000_000,
1919 };
1920
1921 mock_service
1922 .expect_get_legacy_prices_from_json_rpc()
1923 .returning(move || {
1924 let prices = mock_legacy_prices.clone();
1925 Box::pin(async move { Ok(prices) })
1926 });
1927
1928 let pc = PriceCalculator::new(mock_service, None);
1929 let mut relayer = create_mock_relayer();
1930
1931 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1933 eip1559_pricing: Some(false),
1934 ..Default::default()
1935 });
1936
1937 let tx_data = EvmTransactionData {
1939 speed: Some(Speed::Fast),
1940 gas_limit: Some(21000),
1941 value: U256::from(1_000_000_000_000_000_000u128), ..Default::default()
1943 };
1944
1945 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1946
1947 assert!(result.is_ok());
1948 let price_params = result.unwrap();
1949
1950 assert_eq!(price_params.is_min_bumped, Some(true));
1952
1953 assert!(
1955 price_params.max_priority_fee_per_gas.is_some() || price_params.gas_price.is_some()
1956 );
1957
1958 assert!(price_params.total_cost > U256::ZERO);
1960 }
1961
1962 #[tokio::test]
1963 async fn test_calculate_bumped_gas_price_no_mempool_vs_regular_network() {
1964 let mut mock_service_regular = MockEvmGasPriceServiceTrait::new();
1966
1967 let mock_prices = GasPrices {
1968 legacy_prices: SpeedPrices::default(),
1969 max_priority_fee_per_gas: SpeedPrices {
1970 safe_low: 1_000_000_000,
1971 average: 2_000_000_000,
1972 fast: 3_000_000_000,
1973 fastest: 4_000_000_000,
1974 },
1975 base_fee_per_gas: 50_000_000_000,
1976 };
1977
1978 mock_service_regular
1979 .expect_network()
1980 .return_const(create_mock_evm_network("mainnet"));
1981
1982 let mock_prices_clone = mock_prices.clone();
1983 mock_service_regular
1984 .expect_get_prices_from_json_rpc()
1985 .returning(move || {
1986 let prices = mock_prices_clone.clone();
1987 Box::pin(async move { Ok(prices) })
1988 });
1989
1990 let pc_regular = PriceCalculator::new(mock_service_regular, None);
1991 let relayer = create_mock_relayer();
1992
1993 let tx_data = EvmTransactionData {
1994 speed: Some(Speed::Fast),
1995 ..Default::default()
1996 };
1997
1998 let result_regular = pc_regular
1999 .calculate_bumped_gas_price(&tx_data, &relayer)
2000 .await
2001 .unwrap();
2002
2003 assert!(
2005 result_regular.max_priority_fee_per_gas.is_some() || result_regular.gas_price.is_some()
2006 );
2007
2008 let mut mock_service_no_mempool = MockEvmGasPriceServiceTrait::new();
2010
2011 mock_service_no_mempool
2012 .expect_network()
2013 .return_const(create_mock_no_mempool_network("arbitrum"));
2014
2015 mock_service_no_mempool
2016 .expect_get_prices_from_json_rpc()
2017 .returning(move || {
2018 let prices = mock_prices.clone();
2019 Box::pin(async move { Ok(prices) })
2020 });
2021
2022 let pc_no_mempool = PriceCalculator::new(mock_service_no_mempool, None);
2023
2024 let result_no_mempool = pc_no_mempool
2025 .calculate_bumped_gas_price(&tx_data, &relayer)
2026 .await
2027 .unwrap();
2028
2029 assert_eq!(result_no_mempool.is_min_bumped, Some(true));
2031
2032 assert!(
2034 result_no_mempool.max_priority_fee_per_gas.is_some()
2035 || result_no_mempool.gas_price.is_some()
2036 );
2037
2038 assert_eq!(result_no_mempool.is_min_bumped, Some(true));
2041 }
2042
2043 #[tokio::test]
2044 async fn test_calculate_bumped_gas_price_arbitrum_network() {
2045 let mut mock_service = MockEvmGasPriceServiceTrait::new();
2046
2047 let arbitrum_network = EvmNetwork {
2049 network: "arbitrum-one".to_string(),
2050 rpc_urls: vec!["https://arb1.arbitrum.io/rpc".to_string()],
2051 explorer_urls: None,
2052 average_blocktime_ms: 1000, is_testnet: false,
2054 tags: vec![ARBITRUM_BASED_TAG.to_string()], chain_id: 42161,
2056 required_confirmations: 1,
2057 features: vec!["eip1559".to_string()],
2058 symbol: "ETH".to_string(),
2059 gas_price_cache: None,
2060 };
2061
2062 mock_service.expect_network().return_const(arbitrum_network);
2064
2065 let mock_prices = GasPrices {
2067 legacy_prices: SpeedPrices {
2068 safe_low: 100_000_000, average: 200_000_000,
2070 fast: 300_000_000,
2071 fastest: 400_000_000,
2072 },
2073 max_priority_fee_per_gas: SpeedPrices {
2074 safe_low: 10_000_000, average: 20_000_000,
2076 fast: 30_000_000,
2077 fastest: 40_000_000,
2078 },
2079 base_fee_per_gas: 100_000_000, };
2081
2082 mock_service
2083 .expect_get_prices_from_json_rpc()
2084 .returning(move || {
2085 let prices = mock_prices.clone();
2086 Box::pin(async move { Ok(prices) })
2087 });
2088
2089 let pc = PriceCalculator::new(mock_service, None);
2090 let relayer = create_mock_relayer();
2091
2092 let tx_data = EvmTransactionData {
2094 speed: Some(Speed::Fast),
2095 gas_limit: Some(21000),
2096 value: U256::from(1_000_000_000_000_000_000u128), ..Default::default()
2098 };
2099
2100 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
2101
2102 assert!(result.is_ok());
2103 let price_params = result.unwrap();
2104
2105 assert_eq!(
2107 price_params.is_min_bumped,
2108 Some(true),
2109 "Arbitrum networks should skip bumping and use current market prices"
2110 );
2111
2112 assert!(
2114 price_params.max_priority_fee_per_gas.is_some() || price_params.gas_price.is_some(),
2115 "Should return some form of pricing"
2116 );
2117
2118 assert!(
2120 price_params.total_cost > U256::ZERO,
2121 "Should have non-zero total cost"
2122 );
2123
2124 if let Some(priority_fee) = price_params.max_priority_fee_per_gas {
2127 assert!(
2129 priority_fee <= 50_000_000, "Priority fee should be based on current market, not bumped: {}",
2131 priority_fee
2132 );
2133 }
2134 }
2135}