1use crate::{
38 constants::DEFAULT_TRANSACTION_SPEED,
39 models::{
40 evm::Speed, EvmNetwork, EvmTransactionData, EvmTransactionDataTrait, RelayerRepoModel,
41 TransactionError, U256,
42 },
43 services::{
44 gas::{EvmGasPriceServiceTrait, NetworkExtraFeeCalculatorServiceTrait},
45 GasPrices, NetworkExtraFeeCalculator,
46 },
47};
48
49#[cfg(test)]
50use mockall::automock;
51
52#[cfg(test)]
53use crate::services::gas::MockNetworkExtraFeeCalculatorServiceTrait;
54
55#[async_trait::async_trait]
56#[cfg_attr(test, automock)]
57pub trait PriceCalculatorTrait: Send + Sync {
58 async fn get_transaction_price_params(
59 &self,
60 tx_data: &EvmTransactionData,
61 relayer: &RelayerRepoModel,
62 ) -> Result<PriceParams, TransactionError>;
63
64 async fn calculate_bumped_gas_price(
65 &self,
66 tx_data: &EvmTransactionData,
67 relayer: &RelayerRepoModel,
68 ) -> Result<PriceParams, TransactionError>;
69}
70
71type GasPriceCapResult = (Option<u128>, Option<u128>, Option<u128>);
72
73const PRECISION: u128 = 1_000_000_000; const MINUTE_AND_HALF_MS: u128 = 90000;
75const BASE_FEE_INCREASE_FACTOR_PERCENT: u128 = 125; const MAX_BASE_FEE_MULTIPLIER: u128 = 10 * PRECISION; #[derive(Debug, Clone)]
79pub struct PriceParams {
80 pub gas_price: Option<u128>,
81 pub max_fee_per_gas: Option<u128>,
82 pub max_priority_fee_per_gas: Option<u128>,
83 pub is_min_bumped: Option<bool>,
84 pub extra_fee: Option<u128>,
85 pub total_cost: U256,
86}
87
88impl PriceParams {
89 pub fn calculate_total_cost(&self, is_eip1559: bool, gas_limit: u64, value: U256) -> U256 {
90 match is_eip1559 {
91 true => {
92 U256::from(self.max_fee_per_gas.unwrap_or(0)) * U256::from(gas_limit)
93 + value
94 + U256::from(self.extra_fee.unwrap_or(0))
95 }
96 false => {
97 U256::from(self.gas_price.unwrap_or(0)) * U256::from(gas_limit)
98 + value
99 + U256::from(self.extra_fee.unwrap_or(0))
100 }
101 }
102 }
103}
104
105pub fn calculate_min_bump(base_price: u128) -> u128 {
116 const BUMP_NUMERATOR: u128 = 11;
119 const BUMP_DENOMINATOR: u128 = 10;
120
121 let bumped_price = base_price
122 .saturating_mul(BUMP_NUMERATOR)
123 .saturating_div(BUMP_DENOMINATOR);
124
125 std::cmp::max(bumped_price, base_price.saturating_add(1))
127}
128
129pub struct PriceCalculator<G: EvmGasPriceServiceTrait> {
131 gas_price_service: G,
132 network_extra_fee_calculator_service: NetworkExtraFeeCalculator<G::Provider>,
133}
134
135#[async_trait::async_trait]
136impl<G: EvmGasPriceServiceTrait + Send + Sync> PriceCalculatorTrait for PriceCalculator<G> {
137 async fn get_transaction_price_params(
138 &self,
139 tx_data: &EvmTransactionData,
140 relayer: &RelayerRepoModel,
141 ) -> Result<PriceParams, TransactionError> {
142 self.get_transaction_price_params(tx_data, relayer).await
143 }
144
145 async fn calculate_bumped_gas_price(
146 &self,
147 tx_data: &EvmTransactionData,
148 relayer: &RelayerRepoModel,
149 ) -> Result<PriceParams, TransactionError> {
150 self.calculate_bumped_gas_price(tx_data, relayer).await
151 }
152}
153
154impl<G: EvmGasPriceServiceTrait> PriceCalculator<G> {
155 pub fn new(
156 gas_price_service: G,
157 network_extra_fee_calculator_service: NetworkExtraFeeCalculator<G::Provider>,
158 ) -> Self {
159 Self {
160 gas_price_service,
161 network_extra_fee_calculator_service,
162 }
163 }
164
165 pub async fn get_transaction_price_params(
181 &self,
182 tx_data: &EvmTransactionData,
183 relayer: &RelayerRepoModel,
184 ) -> Result<PriceParams, TransactionError> {
185 let price_params = self
186 .fetch_price_params_based_on_tx_type(tx_data, relayer)
187 .await?;
188 let (gas_price_capped, max_fee_per_gas_capped, max_priority_fee_per_gas_capped) = self
189 .apply_gas_price_cap(
190 price_params.gas_price.unwrap_or_default(),
191 price_params.max_fee_per_gas,
192 price_params.max_priority_fee_per_gas,
193 relayer,
194 )?;
195
196 let mut final_params = PriceParams {
197 gas_price: gas_price_capped,
198 max_fee_per_gas: max_fee_per_gas_capped,
199 max_priority_fee_per_gas: max_priority_fee_per_gas_capped,
200 is_min_bumped: None,
201 extra_fee: None,
202 total_cost: U256::ZERO,
203 };
204
205 match &self.network_extra_fee_calculator_service {
206 NetworkExtraFeeCalculator::None => {}
207 _ => {
208 let new_tx = tx_data.clone().with_price_params(final_params.clone());
209 let extra_fee = self
210 .network_extra_fee_calculator_service
211 .get_extra_fee(&new_tx)
212 .await?;
213 final_params.extra_fee = Some(extra_fee.try_into().unwrap_or(0));
214 }
215 }
216
217 final_params.total_cost = final_params.calculate_total_cost(
218 tx_data.is_eip1559(),
219 tx_data.gas_limit,
220 U256::from(tx_data.value),
221 );
222
223 Ok(final_params)
224 }
225
226 pub async fn calculate_bumped_gas_price(
240 &self,
241 tx_data: &EvmTransactionData,
242 relayer: &RelayerRepoModel,
243 ) -> Result<PriceParams, TransactionError> {
244 let network_gas_prices = self.gas_price_service.get_prices_from_json_rpc().await?;
245 let relayer_gas_price_cap = relayer
246 .policies
247 .get_evm_policy()
248 .gas_price_cap
249 .unwrap_or(u128::MAX);
250
251 let bumped_price_params = match (
253 tx_data.max_fee_per_gas,
254 tx_data.max_priority_fee_per_gas,
255 tx_data.gas_price,
256 ) {
257 (Some(max_fee), Some(max_priority_fee), _) => {
258 self.handle_eip1559_bump(
260 &network_gas_prices,
261 relayer_gas_price_cap,
262 tx_data.speed.as_ref(),
263 max_fee,
264 max_priority_fee,
265 )?
266 }
267 (None, None, Some(gas_price)) => {
268 self.handle_legacy_bump(
270 &network_gas_prices,
271 relayer_gas_price_cap,
272 tx_data.speed.as_ref(),
273 gas_price,
274 )?
275 }
276 _ => {
277 return Err(TransactionError::InvalidType(
278 "Transaction missing required gas price parameters".to_string(),
279 ))
280 }
281 };
282
283 let mut final_params = bumped_price_params;
285 let value = tx_data.value;
286 let gas_limit = tx_data.gas_limit;
287 let is_eip1559 = tx_data.is_eip1559();
288
289 match &self.network_extra_fee_calculator_service {
290 NetworkExtraFeeCalculator::None => {}
291 _ => {
292 let new_tx = tx_data.clone().with_price_params(final_params.clone());
293 let extra_fee = self
294 .network_extra_fee_calculator_service
295 .get_extra_fee(&new_tx)
296 .await?;
297 final_params.extra_fee = Some(extra_fee.try_into().unwrap_or(0));
298 }
299 }
300
301 final_params.total_cost =
302 final_params.calculate_total_cost(is_eip1559, gas_limit, U256::from(value));
303
304 Ok(final_params)
305 }
306
307 fn handle_eip1559_bump(
323 &self,
324 network_gas_prices: &GasPrices,
325 gas_price_cap: u128,
326 maybe_speed: Option<&Speed>,
327 max_fee: u128,
328 max_priority_fee: u128,
329 ) -> Result<PriceParams, TransactionError> {
330 let speed = maybe_speed.unwrap_or(&DEFAULT_TRANSACTION_SPEED);
331
332 let min_bump_max_fee = calculate_min_bump(max_fee);
334 let min_bump_max_priority = calculate_min_bump(max_priority_fee);
335
336 let current_market_priority =
338 Self::get_market_price_for_speed(network_gas_prices, true, speed);
339
340 let bumped_priority_fee = if current_market_priority >= min_bump_max_priority {
344 current_market_priority
345 } else {
346 min_bump_max_priority
347 };
348
349 let base_fee_wei = network_gas_prices.base_fee_per_gas;
352 let bumped_max_fee_per_gas = if base_fee_wei >= min_bump_max_fee {
353 base_fee_wei
354 } else {
355 min_bump_max_fee
356 };
357
358 let recommended_max_fee_per_gas = calculate_max_fee_per_gas(
361 base_fee_wei,
362 bumped_priority_fee,
363 self.gas_price_service.network(),
364 );
365
366 let final_max_fee = std::cmp::max(bumped_max_fee_per_gas, recommended_max_fee_per_gas);
368
369 let capped_priority = Self::cap_gas_price(bumped_priority_fee, gas_price_cap);
371 let capped_max_fee = Self::cap_gas_price(final_max_fee, gas_price_cap);
372
373 let is_min_bumped =
375 capped_priority >= min_bump_max_priority && capped_max_fee >= min_bump_max_fee;
376
377 Ok(PriceParams {
379 gas_price: None,
380 max_priority_fee_per_gas: Some(capped_priority),
381 max_fee_per_gas: Some(capped_max_fee),
382 is_min_bumped: Some(is_min_bumped),
383 extra_fee: None,
384 total_cost: U256::ZERO,
385 })
386 }
387
388 fn handle_legacy_bump(
393 &self,
394 network_gas_prices: &GasPrices,
395 gas_price_cap: u128,
396 maybe_speed: Option<&Speed>,
397 gas_price: u128,
398 ) -> Result<PriceParams, TransactionError> {
399 let speed = maybe_speed.unwrap_or(&Speed::Fast);
400
401 let min_bump_gas_price = calculate_min_bump(gas_price);
403
404 let current_market_price =
406 Self::get_market_price_for_speed(network_gas_prices, false, speed);
407
408 let bumped_gas_price = if current_market_price >= min_bump_gas_price {
409 current_market_price
410 } else {
411 min_bump_gas_price
412 };
413
414 let capped_gas_price = Self::cap_gas_price(bumped_gas_price, gas_price_cap);
416
417 let is_min_bumped = capped_gas_price >= min_bump_gas_price;
419
420 Ok(PriceParams {
421 gas_price: Some(capped_gas_price),
422 max_priority_fee_per_gas: None,
423 max_fee_per_gas: None,
424 is_min_bumped: Some(is_min_bumped),
425 extra_fee: None,
426 total_cost: U256::ZERO,
427 })
428 }
429 async fn fetch_price_params_based_on_tx_type(
431 &self,
432 tx_data: &EvmTransactionData,
433 relayer: &RelayerRepoModel,
434 ) -> Result<PriceParams, TransactionError> {
435 if tx_data.is_legacy() {
436 self.fetch_legacy_price_params(tx_data)
437 } else if tx_data.is_eip1559() {
438 self.fetch_eip1559_price_params(tx_data)
439 } else if tx_data.is_speed() {
440 self.fetch_speed_price_params(tx_data, relayer).await
441 } else {
442 Err(TransactionError::NotSupported(
443 "Invalid transaction type".to_string(),
444 ))
445 }
446 }
447
448 fn fetch_legacy_price_params(
456 &self,
457 tx_data: &EvmTransactionData,
458 ) -> Result<PriceParams, TransactionError> {
459 let gas_price = tx_data.gas_price.ok_or(TransactionError::NotSupported(
460 "Gas price is required for legacy transactions".to_string(),
461 ))?;
462 Ok(PriceParams {
463 gas_price: Some(gas_price),
464 max_fee_per_gas: None,
465 max_priority_fee_per_gas: None,
466 is_min_bumped: None,
467 extra_fee: None,
468 total_cost: U256::ZERO,
469 })
470 }
471
472 fn fetch_eip1559_price_params(
473 &self,
474 tx_data: &EvmTransactionData,
475 ) -> Result<PriceParams, TransactionError> {
476 let max_fee = tx_data
477 .max_fee_per_gas
478 .ok_or(TransactionError::NotSupported(
479 "Max fee per gas is required for EIP1559 transactions".to_string(),
480 ))?;
481 let max_priority_fee =
482 tx_data
483 .max_priority_fee_per_gas
484 .ok_or(TransactionError::NotSupported(
485 "Max priority fee per gas is required for EIP1559 transactions".to_string(),
486 ))?;
487 Ok(PriceParams {
488 gas_price: None,
489 max_fee_per_gas: Some(max_fee),
490 max_priority_fee_per_gas: Some(max_priority_fee),
491 is_min_bumped: None,
492 extra_fee: None,
493 total_cost: U256::ZERO,
494 })
495 }
496 async fn fetch_speed_price_params(
501 &self,
502 tx_data: &EvmTransactionData,
503 relayer: &RelayerRepoModel,
504 ) -> Result<PriceParams, TransactionError> {
505 let speed = tx_data
506 .speed
507 .as_ref()
508 .ok_or(TransactionError::NotSupported(
509 "Speed is required".to_string(),
510 ))?;
511 let use_legacy = relayer.policies.get_evm_policy().eip1559_pricing == Some(false)
512 || self.gas_price_service.network().is_legacy();
513
514 if use_legacy {
515 self.fetch_legacy_speed_params(speed).await
516 } else {
517 self.fetch_eip1559_speed_params(speed).await
518 }
519 }
520
521 async fn fetch_eip1559_speed_params(
526 &self,
527 speed: &Speed,
528 ) -> Result<PriceParams, TransactionError> {
529 let prices = self.gas_price_service.get_prices_from_json_rpc().await?;
530 let priority_fee = match speed {
531 Speed::SafeLow => prices.max_priority_fee_per_gas.safe_low,
532 Speed::Average => prices.max_priority_fee_per_gas.average,
533 Speed::Fast => prices.max_priority_fee_per_gas.fast,
534 Speed::Fastest => prices.max_priority_fee_per_gas.fastest,
535 };
536 let max_fee = calculate_max_fee_per_gas(
537 prices.base_fee_per_gas,
538 priority_fee,
539 self.gas_price_service.network(),
540 );
541 Ok(PriceParams {
542 gas_price: None,
543 max_fee_per_gas: Some(max_fee),
544 max_priority_fee_per_gas: Some(priority_fee),
545 is_min_bumped: None,
546 extra_fee: None,
547 total_cost: U256::ZERO,
548 })
549 }
550 async fn fetch_legacy_speed_params(
555 &self,
556 speed: &Speed,
557 ) -> Result<PriceParams, TransactionError> {
558 let prices = self
559 .gas_price_service
560 .get_legacy_prices_from_json_rpc()
561 .await?;
562 let gas_price = match speed {
563 Speed::SafeLow => prices.safe_low,
564 Speed::Average => prices.average,
565 Speed::Fast => prices.fast,
566 Speed::Fastest => prices.fastest,
567 };
568 Ok(PriceParams {
569 gas_price: Some(gas_price),
570 max_fee_per_gas: None,
571 max_priority_fee_per_gas: None,
572 is_min_bumped: None,
573 extra_fee: None,
574 total_cost: U256::ZERO,
575 })
576 }
577
578 fn apply_gas_price_cap(
583 &self,
584 gas_price: u128,
585 max_fee_per_gas: Option<u128>,
586 max_priority_fee_per_gas: Option<u128>,
587 relayer: &RelayerRepoModel,
588 ) -> Result<GasPriceCapResult, TransactionError> {
589 let gas_price_cap = relayer
590 .policies
591 .get_evm_policy()
592 .gas_price_cap
593 .unwrap_or(u128::MAX);
594
595 if let (Some(max_fee), Some(max_priority)) = (max_fee_per_gas, max_priority_fee_per_gas) {
596 let capped_max_fee = Self::cap_gas_price(max_fee, gas_price_cap);
598
599 let capped_max_priority = Self::cap_gas_price(max_priority, capped_max_fee);
601 Ok((None, Some(capped_max_fee), Some(capped_max_priority)))
602 } else {
603 Ok((
605 Some(Self::cap_gas_price(gas_price, gas_price_cap)),
606 None,
607 None,
608 ))
609 }
610 }
611
612 fn cap_gas_price(price: u128, cap: u128) -> u128 {
613 std::cmp::min(price, cap)
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_int = MINUTE_AND_HALF_MS / block_interval_ms;
642
643 let n_blocks_frac = ((MINUTE_AND_HALF_MS % block_interval_ms) * 1000) / block_interval_ms;
645
646 let mut multiplier = PRECISION;
649
650 for _ in 0..n_blocks_int {
652 multiplier = (multiplier
653 * (PRECISION + (PRECISION * BASE_FEE_INCREASE_FACTOR_PERCENT) / 1000))
654 / PRECISION;
655 }
656
657 if n_blocks_frac > 0 {
660 let frac_increase =
661 (n_blocks_frac * BASE_FEE_INCREASE_FACTOR_PERCENT * PRECISION) / (1000 * 1000);
662 multiplier = (multiplier * (PRECISION + frac_increase)) / PRECISION;
663 }
664
665 std::cmp::min(multiplier, MAX_BASE_FEE_MULTIPLIER)
667}
668
669fn calculate_max_fee_per_gas(
671 base_fee_wei: u128,
672 max_priority_fee_wei: u128,
673 network: &EvmNetwork,
674) -> u128 {
675 let multiplier = get_base_fee_multiplier(network);
677
678 let multiplied_base_fee = (base_fee_wei * multiplier) / PRECISION;
680
681 multiplied_base_fee + max_priority_fee_wei
683}
684#[cfg(test)]
685mod tests {
686 use super::*;
687 use crate::models::{
688 evm::Speed, EvmNetwork, EvmTransactionData, NetworkType, RelayerEvmPolicy,
689 RelayerNetworkPolicy, RelayerRepoModel, U256,
690 };
691 use crate::services::{
692 EvmGasPriceService, GasPrices, MockEvmGasPriceServiceTrait, MockEvmProviderTrait,
693 SpeedPrices,
694 };
695 use futures::FutureExt;
696
697 fn create_mock_evm_network(name: &str) -> EvmNetwork {
698 let average_blocktime_ms = match name {
699 "optimism" => 2000, _ => 12000, };
702
703 EvmNetwork {
704 network: name.to_string(),
705 rpc_urls: vec!["https://rpc.example.com".to_string()],
706 explorer_urls: None,
707 average_blocktime_ms,
708 is_testnet: true,
709 tags: vec![],
710 chain_id: 1337,
711 required_confirmations: 1,
712 features: vec![],
713 symbol: "ETH".to_string(),
714 }
715 }
716
717 fn create_mock_relayer() -> RelayerRepoModel {
718 RelayerRepoModel {
719 id: "test-relayer".to_string(),
720 name: "Test Relayer".to_string(),
721 network: "mainnet".to_string(),
722 network_type: NetworkType::Evm,
723 address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".to_string(),
724 policies: RelayerNetworkPolicy::Evm(RelayerEvmPolicy::default()),
725 paused: false,
726 notification_id: None,
727 signer_id: "test-signer".to_string(),
728 system_disabled: false,
729 custom_rpc_urls: None,
730 }
731 }
732
733 #[tokio::test]
734 async fn test_legacy_transaction() {
735 let mut provider = MockEvmProviderTrait::new();
736 provider
737 .expect_get_balance()
738 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
739
740 let relayer = create_mock_relayer();
741 let gas_price_service =
742 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"));
743
744 let tx_data = EvmTransactionData {
745 gas_price: Some(20000000000),
746 ..Default::default()
747 };
748
749 let mut provider = MockEvmProviderTrait::new();
750 provider
751 .expect_get_balance()
752 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
753
754 let pc = PriceCalculator::new(gas_price_service, NetworkExtraFeeCalculator::None);
756
757 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
758 assert!(result.is_ok());
759 let params = result.unwrap();
760 assert_eq!(params.gas_price, Some(20000000000));
761 assert!(params.max_fee_per_gas.is_none());
762 assert!(params.max_priority_fee_per_gas.is_none());
763 }
764
765 #[tokio::test]
766 async fn test_eip1559_transaction() {
767 let mut provider = MockEvmProviderTrait::new();
768 provider
769 .expect_get_balance()
770 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
771
772 let relayer = create_mock_relayer();
773 let gas_price_service =
774 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"));
775
776 let tx_data = EvmTransactionData {
777 gas_price: None,
778 max_fee_per_gas: Some(30000000000),
779 max_priority_fee_per_gas: Some(2000000000),
780 ..Default::default()
781 };
782
783 let mut provider = MockEvmProviderTrait::new();
784 provider
785 .expect_get_balance()
786 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
787
788 let pc = PriceCalculator::new(gas_price_service, NetworkExtraFeeCalculator::None);
790
791 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
792 assert!(result.is_ok());
793 let params = result.unwrap();
794 assert!(params.gas_price.is_none());
795 assert_eq!(params.max_fee_per_gas, Some(30000000000));
796 assert_eq!(params.max_priority_fee_per_gas, Some(2000000000));
797 }
798
799 #[tokio::test]
800 async fn test_speed_legacy_based_transaction() {
801 let mut provider = MockEvmProviderTrait::new();
802 provider
803 .expect_get_balance()
804 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
805 provider
806 .expect_get_gas_price()
807 .returning(|| async { Ok(20000000000) }.boxed());
808
809 let relayer = create_mock_relayer();
810 let gas_price_service = EvmGasPriceService::new(provider, create_mock_evm_network("celo"));
811
812 let tx_data = EvmTransactionData {
813 gas_price: None,
814 speed: Some(Speed::Fast),
815 ..Default::default()
816 };
817
818 let mut provider = MockEvmProviderTrait::new();
819 provider
820 .expect_get_balance()
821 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
822 provider
823 .expect_get_gas_price()
824 .returning(|| async { Ok(20000000000) }.boxed());
825
826 let pc = PriceCalculator::new(gas_price_service, NetworkExtraFeeCalculator::None);
827
828 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
829 assert!(result.is_ok());
830 let params = result.unwrap();
831 assert!(
832 params.gas_price.is_some()
833 || (params.max_fee_per_gas.is_some() && params.max_priority_fee_per_gas.is_some())
834 );
835 }
836
837 #[tokio::test]
838 async fn test_invalid_transaction_type() {
839 let mut provider = MockEvmProviderTrait::new();
840 provider
841 .expect_get_balance()
842 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
843
844 let relayer = create_mock_relayer();
845 let gas_price_service =
846 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"));
847
848 let tx_data = EvmTransactionData {
849 gas_price: None,
850 ..Default::default()
851 };
852
853 let mut provider = MockEvmProviderTrait::new();
854 provider
855 .expect_get_balance()
856 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
857
858 let pc = PriceCalculator::new(gas_price_service, NetworkExtraFeeCalculator::None);
859
860 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
861 assert!(result.is_err());
862 assert!(matches!(
863 result.unwrap_err(),
864 TransactionError::NotSupported(_)
865 ));
866 }
867
868 #[tokio::test]
869 async fn test_gas_price_cap() {
870 let mut provider = MockEvmProviderTrait::new();
871 provider
872 .expect_get_balance()
873 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
874
875 let mut relayer = create_mock_relayer();
876 let gas_price_service =
877 EvmGasPriceService::new(provider, create_mock_evm_network("mainnet"));
878
879 let evm_policy = RelayerEvmPolicy {
881 gas_price_cap: Some(10000000000),
882 eip1559_pricing: Some(true),
883 ..RelayerEvmPolicy::default()
884 };
885 relayer.policies = RelayerNetworkPolicy::Evm(evm_policy);
886
887 let tx_data = EvmTransactionData {
888 gas_price: Some(20000000000), ..Default::default()
890 };
891
892 let mut provider = MockEvmProviderTrait::new();
893 provider
894 .expect_get_balance()
895 .returning(|_| async { Ok(U256::from(1000000000000000000u128)) }.boxed());
896
897 let pc = PriceCalculator::new(gas_price_service, NetworkExtraFeeCalculator::None);
898
899 let result = pc.get_transaction_price_params(&tx_data, &relayer).await;
900 assert!(result.is_ok());
901 let params = result.unwrap();
902 assert_eq!(params.gas_price, Some(10000000000)); }
904
905 #[test]
906 fn test_get_base_fee_multiplier() {
907 let mainnet = create_mock_evm_network("mainnet");
908 let multiplier = super::get_base_fee_multiplier(&mainnet);
909 assert!(multiplier > 2_300_000_000 && multiplier < 2_500_000_000);
911
912 let optimism = create_mock_evm_network("optimism");
913 let multiplier = super::get_base_fee_multiplier(&optimism);
914 assert_eq!(multiplier, MAX_BASE_FEE_MULTIPLIER);
916 }
917
918 #[test]
919 fn test_calculate_max_fee_per_gas() {
920 let network = create_mock_evm_network("mainnet");
921 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);
925 println!("max_fee: {:?}", max_fee);
926 assert!(max_fee > 240_000_000_000 && max_fee < 245_000_000_000);
929 }
930
931 #[tokio::test]
932 async fn test_handle_eip1559_speed() {
933 let mut mock_gas_price_service = MockEvmGasPriceServiceTrait::new();
934
935 let test_data = [
937 (Speed::SafeLow, 1_000_000_000),
938 (Speed::Average, 2_000_000_000),
939 (Speed::Fast, 3_000_000_000),
940 (Speed::Fastest, 4_000_000_000),
941 ];
942 let mock_prices = GasPrices {
944 legacy_prices: SpeedPrices {
945 safe_low: 10_000_000_000,
946 average: 12_500_000_000,
947 fast: 15_000_000_000,
948 fastest: 20_000_000_000,
949 },
950 max_priority_fee_per_gas: SpeedPrices {
951 safe_low: 1_000_000_000,
952 average: 2_000_000_000,
953 fast: 3_000_000_000,
954 fastest: 4_000_000_000,
955 },
956 base_fee_per_gas: 50_000_000_000,
957 };
958
959 mock_gas_price_service
961 .expect_get_prices_from_json_rpc()
962 .returning(move || {
963 let prices = mock_prices.clone();
964 Box::pin(async move { Ok(prices) })
965 });
966
967 let network = create_mock_evm_network("mainnet");
969 mock_gas_price_service
970 .expect_network()
971 .return_const(network);
972
973 let pc = PriceCalculator::new(mock_gas_price_service, NetworkExtraFeeCalculator::None);
975
976 for (speed, expected_priority_fee) in test_data {
977 let result = pc.fetch_eip1559_speed_params(&speed).await;
979 assert!(result.is_ok());
980 let params = result.unwrap();
981 assert_eq!(params.max_priority_fee_per_gas, Some(expected_priority_fee));
983
984 let max_fee = params.max_fee_per_gas.unwrap();
988 let expected_base_portion = 120_000_000_000; assert!(max_fee < expected_base_portion + expected_priority_fee + 2_000_000_000);
990 }
991 }
992
993 #[tokio::test]
994 async fn test_calculate_bumped_gas_price_eip1559_basic() {
995 let mut mock_service = MockEvmGasPriceServiceTrait::new();
996 let mock_prices = GasPrices {
997 legacy_prices: SpeedPrices {
998 safe_low: 8_000_000_000,
999 average: 10_000_000_000,
1000 fast: 12_000_000_000,
1001 fastest: 15_000_000_000,
1002 },
1003 max_priority_fee_per_gas: SpeedPrices {
1004 safe_low: 1_000_000_000,
1005 average: 2_000_000_000,
1006 fast: 3_000_000_000,
1007 fastest: 4_000_000_000,
1008 },
1009 base_fee_per_gas: 50_000_000_000,
1010 };
1011 mock_service
1012 .expect_get_prices_from_json_rpc()
1013 .returning(move || {
1014 let prices = mock_prices.clone();
1015 Box::pin(async move { Ok(prices) })
1016 });
1017 mock_service
1018 .expect_network()
1019 .return_const(create_mock_evm_network("mainnet"));
1020
1021 let pc = PriceCalculator::new(mock_service, NetworkExtraFeeCalculator::None);
1022 let mut relayer = create_mock_relayer();
1023 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1025 gas_price_cap: Some(300_000_000_000u128),
1026 ..Default::default()
1027 });
1028
1029 let tx_data = EvmTransactionData {
1030 max_fee_per_gas: Some(100_000_000_000),
1031 max_priority_fee_per_gas: Some(2_000_000_000),
1032 speed: Some(Speed::Fast),
1033 ..Default::default()
1034 };
1035
1036 let bumped = pc
1037 .calculate_bumped_gas_price(&tx_data, &relayer)
1038 .await
1039 .unwrap();
1040 assert!(bumped.max_fee_per_gas.unwrap() >= 110_000_000_000); assert!(bumped.max_priority_fee_per_gas.unwrap() >= 2_200_000_000); }
1043
1044 #[tokio::test]
1045 async fn test_calculate_bumped_gas_price_eip1559_market_lower_than_min_bump() {
1046 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1047 let mock_prices = GasPrices {
1048 legacy_prices: SpeedPrices::default(),
1049 max_priority_fee_per_gas: SpeedPrices {
1050 safe_low: 1_500_000_000, average: 2_500_000_000,
1052 fast: 2_700_000_000,
1053 fastest: 3_000_000_000,
1054 },
1055 base_fee_per_gas: 30_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, NetworkExtraFeeCalculator::None);
1068 let relayer = create_mock_relayer();
1069
1070 let tx_data = EvmTransactionData {
1073 max_fee_per_gas: Some(20_000_000_000),
1074 max_priority_fee_per_gas: Some(2_000_000_000),
1075 speed: Some(Speed::SafeLow),
1076 ..Default::default()
1077 };
1078
1079 let bumped = pc
1080 .calculate_bumped_gas_price(&tx_data, &relayer)
1081 .await
1082 .unwrap();
1083 assert!(bumped.max_priority_fee_per_gas.unwrap() >= 2_200_000_000);
1084 assert!(bumped.max_fee_per_gas.unwrap() > 20_000_000_000);
1085 }
1086
1087 #[tokio::test]
1088 async fn test_calculate_bumped_gas_price_legacy_basic() {
1089 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1090 let mock_prices = GasPrices {
1091 legacy_prices: SpeedPrices {
1092 safe_low: 10_000_000_000,
1093 average: 12_000_000_000,
1094 fast: 14_000_000_000,
1095 fastest: 18_000_000_000,
1096 },
1097 max_priority_fee_per_gas: SpeedPrices::default(),
1098 base_fee_per_gas: 0,
1099 };
1100 mock_service
1101 .expect_get_prices_from_json_rpc()
1102 .returning(move || {
1103 let prices = mock_prices.clone();
1104 Box::pin(async move { Ok(prices) })
1105 });
1106 mock_service
1107 .expect_network()
1108 .return_const(create_mock_evm_network("mainnet"));
1109
1110 let pc = PriceCalculator::new(mock_service, NetworkExtraFeeCalculator::None);
1111 let relayer = create_mock_relayer();
1112 let tx_data = EvmTransactionData {
1113 gas_price: Some(10_000_000_000),
1114 speed: Some(Speed::Fast),
1115 ..Default::default()
1116 };
1117
1118 let bumped = pc
1119 .calculate_bumped_gas_price(&tx_data, &relayer)
1120 .await
1121 .unwrap();
1122 assert!(bumped.gas_price.unwrap() >= 11_000_000_000); }
1124
1125 #[tokio::test]
1126 async fn test_calculate_bumped_gas_price_missing_params() {
1127 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1128
1129 mock_service
1131 .expect_get_prices_from_json_rpc()
1132 .times(1)
1133 .returning(|| Box::pin(async { Ok(GasPrices::default()) }));
1134
1135 let pc = PriceCalculator::new(mock_service, NetworkExtraFeeCalculator::None);
1136 let relayer = create_mock_relayer();
1137 let tx_data = EvmTransactionData {
1139 gas_price: None,
1140 max_fee_per_gas: None,
1141 max_priority_fee_per_gas: None,
1142 ..Default::default()
1143 };
1144
1145 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1146 assert!(result.is_err());
1147 if let Err(TransactionError::InvalidType(msg)) = result {
1148 assert!(msg.contains("missing required gas price parameters"));
1149 } else {
1150 panic!("Expected InvalidType error");
1151 }
1152 }
1153
1154 #[tokio::test]
1155 async fn test_calculate_bumped_gas_price_capped() {
1156 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1157 let mock_prices = GasPrices {
1158 legacy_prices: SpeedPrices::default(),
1159 max_priority_fee_per_gas: SpeedPrices {
1160 safe_low: 4_000_000_000,
1161 average: 5_000_000_000,
1162 fast: 6_000_000_000,
1163 fastest: 8_000_000_000,
1164 },
1165 base_fee_per_gas: 100_000_000_000,
1166 };
1167 mock_service
1168 .expect_get_prices_from_json_rpc()
1169 .returning(move || {
1170 let prices = mock_prices.clone();
1171 Box::pin(async move { Ok(prices) })
1172 });
1173 mock_service
1174 .expect_network()
1175 .return_const(create_mock_evm_network("mainnet"));
1176
1177 let pc = PriceCalculator::new(mock_service, NetworkExtraFeeCalculator::None);
1178 let mut relayer = create_mock_relayer();
1179 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1180 gas_price_cap: Some(105_000_000_000),
1181 ..Default::default()
1182 });
1183
1184 let tx_data = EvmTransactionData {
1185 max_fee_per_gas: Some(90_000_000_000),
1186 max_priority_fee_per_gas: Some(4_000_000_000),
1187 speed: Some(Speed::Fastest),
1188 ..Default::default()
1189 };
1190
1191 let bumped = pc
1193 .calculate_bumped_gas_price(&tx_data, &relayer)
1194 .await
1195 .unwrap();
1196 assert!(bumped.max_fee_per_gas.unwrap() <= 105_000_000_000);
1197 assert!(bumped.max_priority_fee_per_gas.unwrap() <= 105_000_000_000);
1198 }
1199
1200 #[tokio::test]
1201 async fn test_is_min_bumped_flag_eip1559() {
1202 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1203 let mock_prices = GasPrices {
1204 legacy_prices: SpeedPrices::default(),
1205 max_priority_fee_per_gas: SpeedPrices {
1206 safe_low: 1_000_000_000,
1207 average: 2_000_000_000,
1208 fast: 3_000_000_000,
1209 fastest: 4_000_000_000,
1210 },
1211 base_fee_per_gas: 40_000_000_000,
1212 };
1213 mock_service
1214 .expect_get_prices_from_json_rpc()
1215 .returning(move || {
1216 let prices = mock_prices.clone();
1217 Box::pin(async move { Ok(prices) })
1218 });
1219 mock_service
1220 .expect_network()
1221 .return_const(create_mock_evm_network("mainnet"));
1222
1223 let pc = PriceCalculator::new(mock_service, NetworkExtraFeeCalculator::None);
1224 let mut relayer = create_mock_relayer();
1225
1226 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1228 gas_price_cap: Some(200_000_000_000u128),
1229 ..Default::default()
1230 });
1231
1232 let tx_data = EvmTransactionData {
1233 max_fee_per_gas: Some(50_000_000_000),
1234 max_priority_fee_per_gas: Some(2_000_000_000),
1235 speed: Some(Speed::Fast),
1236 ..Default::default()
1237 };
1238
1239 let bumped = pc
1240 .calculate_bumped_gas_price(&tx_data, &relayer)
1241 .await
1242 .unwrap();
1243 assert_eq!(
1244 bumped.is_min_bumped,
1245 Some(true),
1246 "Should be min bumped when prices are high enough"
1247 );
1248
1249 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1251 gas_price_cap: Some(50_000_000_000u128), ..Default::default()
1253 });
1254
1255 let tx_data = EvmTransactionData {
1256 max_fee_per_gas: Some(50_000_000_000),
1257 max_priority_fee_per_gas: Some(2_000_000_000),
1258 speed: Some(Speed::Fast),
1259 ..Default::default()
1260 };
1261
1262 let bumped = pc
1263 .calculate_bumped_gas_price(&tx_data, &relayer)
1264 .await
1265 .unwrap();
1266 assert_eq!(
1268 bumped.is_min_bumped,
1269 Some(false),
1270 "Should not be min bumped when cap is too low"
1271 );
1272 }
1273
1274 #[tokio::test]
1275 async fn test_is_min_bumped_flag_legacy() {
1276 let mut mock_service = MockEvmGasPriceServiceTrait::new();
1277 let mock_prices = GasPrices {
1278 legacy_prices: SpeedPrices {
1279 safe_low: 8_000_000_000,
1280 average: 10_000_000_000,
1281 fast: 12_000_000_000,
1282 fastest: 15_000_000_000,
1283 },
1284 max_priority_fee_per_gas: SpeedPrices::default(),
1285 base_fee_per_gas: 0,
1286 };
1287 mock_service
1288 .expect_get_prices_from_json_rpc()
1289 .returning(move || {
1290 let prices = mock_prices.clone();
1291 Box::pin(async move { Ok(prices) })
1292 });
1293 mock_service
1294 .expect_network()
1295 .return_const(create_mock_evm_network("mainnet"));
1296
1297 let pc = PriceCalculator::new(mock_service, NetworkExtraFeeCalculator::None);
1298 let mut relayer = create_mock_relayer();
1299
1300 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1302 gas_price_cap: Some(100_000_000_000u128),
1303 ..Default::default()
1304 });
1305
1306 let tx_data = EvmTransactionData {
1307 gas_price: Some(10_000_000_000),
1308 speed: Some(Speed::Fast),
1309 ..Default::default()
1310 };
1311
1312 let bumped = pc
1313 .calculate_bumped_gas_price(&tx_data, &relayer)
1314 .await
1315 .unwrap();
1316 assert_eq!(
1317 bumped.is_min_bumped,
1318 Some(true),
1319 "Should be min bumped with sufficient cap"
1320 );
1321
1322 relayer.policies = RelayerNetworkPolicy::Evm(RelayerEvmPolicy {
1324 gas_price_cap: Some(10_000_000_000u128), ..Default::default()
1326 });
1327
1328 let bumped = pc
1329 .calculate_bumped_gas_price(&tx_data, &relayer)
1330 .await
1331 .unwrap();
1332 assert_eq!(
1333 bumped.is_min_bumped,
1334 Some(false),
1335 "Should not be min bumped with insufficient cap"
1336 );
1337 }
1338
1339 #[tokio::test]
1340 async fn test_calculate_bumped_gas_price_with_extra_fee() {
1341 let mut mock_gas_service = MockEvmGasPriceServiceTrait::new();
1343 mock_gas_service
1344 .expect_get_prices_from_json_rpc()
1345 .returning(|| {
1346 Box::pin(async {
1347 Ok(GasPrices {
1348 legacy_prices: SpeedPrices {
1349 safe_low: 10_000_000_000,
1350 average: 12_000_000_000,
1351 fast: 14_000_000_000,
1352 fastest: 18_000_000_000,
1353 },
1354 max_priority_fee_per_gas: SpeedPrices::default(),
1355 base_fee_per_gas: 40_000_000_000,
1356 })
1357 })
1358 });
1359 mock_gas_service
1360 .expect_network()
1361 .return_const(create_mock_evm_network("mainnet"));
1362
1363 let pc = PriceCalculator::new(mock_gas_service, NetworkExtraFeeCalculator::None);
1365
1366 let relayer = create_mock_relayer();
1368 let tx_data = EvmTransactionData {
1369 max_fee_per_gas: Some(50_000_000_000),
1370 max_priority_fee_per_gas: Some(2_000_000_000),
1371 speed: Some(Speed::Fast),
1372 ..Default::default()
1373 };
1374
1375 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1377
1378 assert!(result.is_ok());
1380 let price_params = result.unwrap();
1381 assert_eq!(price_params.extra_fee, None);
1382 }
1383
1384 #[tokio::test]
1385 async fn test_calculate_bumped_gas_price_with_mock_extra_fee() {
1386 let mut mock_gas_service = MockEvmGasPriceServiceTrait::new();
1388 mock_gas_service
1389 .expect_get_prices_from_json_rpc()
1390 .returning(|| {
1391 Box::pin(async {
1392 Ok(GasPrices {
1393 legacy_prices: SpeedPrices {
1394 safe_low: 10_000_000_000,
1395 average: 12_000_000_000,
1396 fast: 14_000_000_000,
1397 fastest: 18_000_000_000,
1398 },
1399 max_priority_fee_per_gas: SpeedPrices::default(),
1400 base_fee_per_gas: 40_000_000_000,
1401 })
1402 })
1403 });
1404 mock_gas_service
1405 .expect_network()
1406 .return_const(create_mock_evm_network("mainnet"));
1407
1408 let mut mock_extra_fee_service = MockNetworkExtraFeeCalculatorServiceTrait::new();
1410
1411 let expected_extra_fee = U256::from(123456789u64);
1413 mock_extra_fee_service
1414 .expect_get_extra_fee()
1415 .returning(move |_| Box::pin(async move { Ok(expected_extra_fee) }));
1416
1417 let extra_fee_calculator = NetworkExtraFeeCalculator::Mock(mock_extra_fee_service);
1419
1420 let pc = PriceCalculator::new(mock_gas_service, extra_fee_calculator);
1422
1423 let relayer = create_mock_relayer();
1425 let tx_data = EvmTransactionData {
1426 max_fee_per_gas: Some(50_000_000_000),
1427 max_priority_fee_per_gas: Some(2_000_000_000),
1428 speed: Some(Speed::Fast),
1429 ..Default::default()
1430 };
1431
1432 let result = pc.calculate_bumped_gas_price(&tx_data, &relayer).await;
1434
1435 assert!(result.is_ok());
1437 let price_params = result.unwrap();
1438 assert_eq!(
1439 price_params.extra_fee,
1440 Some(expected_extra_fee.try_into().unwrap_or(0))
1441 );
1442 }
1443
1444 #[test]
1445 fn test_calculate_total_cost_eip1559() {
1446 let price_params = PriceParams {
1447 gas_price: None,
1448 max_fee_per_gas: Some(30_000_000_000),
1449 max_priority_fee_per_gas: Some(2_000_000_000),
1450 is_min_bumped: None,
1451 extra_fee: None,
1452 total_cost: U256::ZERO,
1453 };
1454
1455 let gas_limit = 100_000;
1456 let value = U256::from(1_000_000_000_000_000_000u128); let is_eip1559 = true;
1458
1459 let total_cost = price_params.calculate_total_cost(is_eip1559, gas_limit, value);
1460
1461 let expected = U256::from(30_000_000_000u128) * U256::from(gas_limit) + value;
1463 assert_eq!(total_cost, expected);
1464 }
1465
1466 #[test]
1467 fn test_calculate_total_cost_legacy() {
1468 let price_params = PriceParams {
1469 gas_price: Some(20_000_000_000),
1470 max_fee_per_gas: None,
1471 max_priority_fee_per_gas: None,
1472 is_min_bumped: None,
1473 extra_fee: None,
1474 total_cost: U256::ZERO,
1475 };
1476
1477 let gas_limit = 100_000;
1478 let value = U256::from(1_000_000_000_000_000_000u128); let is_eip1559 = false;
1480
1481 let total_cost = price_params.calculate_total_cost(is_eip1559, gas_limit, value);
1482
1483 let expected = U256::from(20_000_000_000u128) * U256::from(gas_limit) + value;
1485 assert_eq!(total_cost, expected);
1486 }
1487
1488 #[test]
1489 fn test_calculate_total_cost_with_extra_fee() {
1490 let price_params = PriceParams {
1491 gas_price: Some(20_000_000_000),
1492 max_fee_per_gas: None,
1493 max_priority_fee_per_gas: None,
1494 is_min_bumped: None,
1495 extra_fee: Some(5_000_000_000),
1496 total_cost: U256::ZERO,
1497 };
1498
1499 let gas_limit = 100_000;
1500 let value = U256::from(1_000_000_000_000_000_000u128); let is_eip1559 = false;
1502
1503 let total_cost = price_params.calculate_total_cost(is_eip1559, gas_limit, value);
1504
1505 let expected = U256::from(20_000_000_000u128) * U256::from(gas_limit)
1506 + value
1507 + U256::from(5_000_000_000u128);
1508 assert_eq!(total_cost, expected);
1509 }
1510
1511 #[test]
1512 fn test_calculate_total_cost_zero_values() {
1513 let price_params = PriceParams {
1514 gas_price: Some(0),
1515 max_fee_per_gas: Some(0),
1516 max_priority_fee_per_gas: Some(0),
1517 is_min_bumped: None,
1518 extra_fee: Some(0),
1519 total_cost: U256::ZERO,
1520 };
1521
1522 let gas_limit = 0;
1523 let value = U256::from(0);
1524
1525 let legacy_total_cost = price_params.calculate_total_cost(false, gas_limit, value);
1526 assert_eq!(legacy_total_cost, U256::ZERO);
1527
1528 let eip1559_total_cost = price_params.calculate_total_cost(true, gas_limit, value);
1529 assert_eq!(eip1559_total_cost, U256::ZERO);
1530 }
1531
1532 #[test]
1533 fn test_calculate_total_cost_missing_values() {
1534 let price_params = PriceParams {
1535 gas_price: None,
1536 max_fee_per_gas: None,
1537 max_priority_fee_per_gas: None,
1538 is_min_bumped: None,
1539 extra_fee: None,
1540 total_cost: U256::ZERO,
1541 };
1542
1543 let gas_limit = 100_000;
1544 let value = U256::from(1_000_000_000_000_000_000u128);
1545
1546 let legacy_total = price_params.calculate_total_cost(false, gas_limit, value);
1547 assert_eq!(legacy_total, value);
1548
1549 let eip1559_total = price_params.calculate_total_cost(true, gas_limit, value);
1550 assert_eq!(eip1559_total, value);
1551 }
1552
1553 #[test]
1554 fn test_calculate_min_bump_normal_cases() {
1555 let base_price = 20_000_000_000u128; let expected = 22_000_000_000u128; assert_eq!(calculate_min_bump(base_price), expected);
1558
1559 let base_price = 1_000_000_000u128;
1560 let expected = 1_100_000_000u128; assert_eq!(calculate_min_bump(base_price), expected);
1562
1563 let base_price = 100_000_000_000u128;
1564 let expected = 110_000_000_000u128; assert_eq!(calculate_min_bump(base_price), expected);
1566 }
1567
1568 #[test]
1569 fn test_calculate_min_bump_edge_cases() {
1570 assert_eq!(calculate_min_bump(0), 1);
1572
1573 let result = calculate_min_bump(1);
1575 assert!(result >= 2);
1576
1577 let base_price = 5u128; let result = calculate_min_bump(base_price);
1580 assert!(
1581 result > base_price,
1582 "Result {} should be greater than base_price {}",
1583 result,
1584 base_price
1585 );
1586
1587 let base_price = 9u128;
1588 let result = calculate_min_bump(base_price);
1589 assert_eq!(
1590 result, 10u128,
1591 "9 wei should bump to 10 wei (minimum 1 wei increase)"
1592 );
1593 }
1594
1595 #[test]
1596 fn test_calculate_min_bump_large_values() {
1597 let base_price = u128::MAX / 2;
1599 let result = calculate_min_bump(base_price);
1600 assert!(result > base_price);
1601
1602 let base_price = u128::MAX - 1000;
1604 let result = calculate_min_bump(base_price);
1605 assert!(result >= base_price.saturating_add(1));
1607 }
1608
1609 #[test]
1610 fn test_calculate_min_bump_overflow_protection() {
1611 let base_price = u128::MAX;
1612 let result = calculate_min_bump(base_price);
1613 assert_eq!(result, u128::MAX);
1614
1615 let base_price = (u128::MAX / 11) * 10 + 1;
1616 let result = calculate_min_bump(base_price);
1617 assert!(result >= base_price);
1618 }
1619
1620 #[test]
1621 fn test_calculate_min_bump_minimum_increase_guarantee() {
1622 let test_cases = vec![0, 1, 2, 5, 9, 10, 100, 1000, 10000];
1624
1625 for base_price in test_cases {
1626 let result = calculate_min_bump(base_price);
1627 assert!(
1628 result > base_price,
1629 "calculate_min_bump({}) = {} should be greater than base_price",
1630 base_price,
1631 result
1632 );
1633 assert!(
1634 result >= base_price.saturating_add(1),
1635 "calculate_min_bump({}) = {} should be at least base_price + 1",
1636 base_price,
1637 result
1638 );
1639 }
1640 }
1641}