openzeppelin_relayer/services/gas/
network_extra_fee.rs1use async_trait::async_trait;
2
3use crate::{
4 models::{EvmNetwork, EvmTransactionData, TransactionError, U256},
5 services::EvmProviderTrait,
6};
7
8use super::optimism_extra_fee::OptimismExtraFeeService;
9
10#[cfg(test)]
11use mockall::automock;
12
13#[async_trait]
14#[cfg_attr(test, automock)]
15pub trait NetworkExtraFeeCalculatorServiceTrait {
16 async fn get_extra_fee(&self, tx_data: &EvmTransactionData) -> Result<U256, TransactionError>;
25}
26
27pub enum NetworkExtraFeeCalculator<P: EvmProviderTrait> {
29 None,
31 Optimism(OptimismExtraFeeService<P>),
33 #[cfg(test)]
35 Mock(MockNetworkExtraFeeCalculatorServiceTrait),
36}
37
38#[async_trait]
39impl<P: EvmProviderTrait + Send + Sync> NetworkExtraFeeCalculatorServiceTrait
40 for NetworkExtraFeeCalculator<P>
41{
42 async fn get_extra_fee(&self, tx_data: &EvmTransactionData) -> Result<U256, TransactionError> {
43 match self {
44 Self::None => Ok(U256::ZERO),
45 Self::Optimism(service) => service.get_extra_fee(tx_data).await,
46 #[cfg(test)]
47 Self::Mock(mock) => mock.get_extra_fee(tx_data).await,
48 }
49 }
50}
51
52pub fn get_network_extra_fee_calculator_service<P>(
60 network: EvmNetwork,
61 provider: P,
62) -> NetworkExtraFeeCalculator<P>
63where
64 P: EvmProviderTrait + 'static,
65{
66 if network.is_optimism() {
67 NetworkExtraFeeCalculator::Optimism(OptimismExtraFeeService::new(provider))
68 } else {
69 NetworkExtraFeeCalculator::None
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::models::EvmNetwork;
77 use crate::services::MockEvmProviderTrait;
78 use alloy::primitives::Bytes;
79
80 fn create_test_evm_network(name: &str, optimism: bool) -> EvmNetwork {
81 EvmNetwork {
82 network: name.to_string(),
83 rpc_urls: vec!["https://optimism-rpc.com".to_string()],
84 explorer_urls: None,
85 average_blocktime_ms: 12000,
86 is_testnet: false,
87 tags: vec![if optimism { "optimism" } else { name }.to_string()],
88 chain_id: if optimism { 10 } else { 42161 },
89 required_confirmations: 1,
90 features: vec!["eip1559".to_string()],
91 symbol: "ETH".to_string(),
92 }
93 }
94
95 #[test]
96 fn test_get_network_extra_fee_calculator_service_for_optimism() {
97 let provider = MockEvmProviderTrait::new();
98 let network = create_test_evm_network("optimism", true);
99 let service = get_network_extra_fee_calculator_service(network, provider);
100
101 assert!(
102 matches!(service, NetworkExtraFeeCalculator::Optimism(_)),
103 "Should return an Optimism service for Optimism network"
104 );
105 }
106
107 #[test]
108 fn test_get_network_extra_fee_calculator_service_for_non_optimism() {
109 let networks = [
110 create_test_evm_network("mainnet", false),
111 create_test_evm_network("arbitrum", false),
112 create_test_evm_network("polygon", false),
113 ];
114
115 for network in networks {
116 let provider = MockEvmProviderTrait::new();
117 let service = get_network_extra_fee_calculator_service(network, provider);
118
119 assert!(
120 matches!(service, NetworkExtraFeeCalculator::None),
121 "Should return None service for non-Optimism network"
122 );
123 }
124 }
125
126 #[tokio::test]
127 async fn test_integration_with_optimism_extra_fee_service() {
128 let mut mock_provider = MockEvmProviderTrait::new();
129
130 mock_provider
131 .expect_call_contract()
132 .times(6) .returning(|_| {
134 let value_bytes = U256::from(1u64).to_be_bytes::<32>();
135 Box::pin(async move { Ok(Bytes::from(value_bytes.to_vec())) })
136 });
137
138 let network = create_test_evm_network("optimism", true);
139 let service = get_network_extra_fee_calculator_service(network, mock_provider);
140
141 let tx_data = EvmTransactionData {
142 from: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string(),
143 to: Some("0xa24Cea55A6171FbA0935c9e171c4Efe5Ba28DF91".to_string()),
144 gas_price: Some(20000000000),
145 value: U256::from(1000000000),
146 data: Some("0x0123".to_string()),
147 nonce: Some(1),
148 chain_id: 10,
149 gas_limit: Some(21000),
150 hash: None,
151 signature: None,
152 speed: None,
153 max_fee_per_gas: None,
154 max_priority_fee_per_gas: None,
155 raw: None,
156 };
157
158 let extra_fee_result = service.get_extra_fee(&tx_data).await;
159
160 assert!(
161 extra_fee_result.is_ok(),
162 "Should calculate extra fee without errors"
163 );
164
165 let extra_fee = extra_fee_result.unwrap();
166 assert!(
167 extra_fee > U256::ZERO,
168 "Extra fee should be greater than zero"
169 );
170 }
171}