openzeppelin_relayer/services/gas/
price_params_handler.rs

1//! Network-specific price parameter overrides.
2//!
3//! This module selects and delegates to handlers that can adjust transaction
4//! price parameters for specific EVM networks when required.
5
6#[cfg(test)]
7use crate::services::gas::handlers::MockPriceHandler;
8use crate::{
9    domain::evm::PriceParams,
10    models::{EvmNetwork, EvmTransactionData, TransactionError},
11    services::{
12        gas::handlers::{OptimismPriceHandler, PolygonZKEvmPriceHandler},
13        provider::EvmProvider,
14    },
15};
16#[derive(Clone)]
17pub enum PriceParamsHandler {
18    PolygonZKEvm(PolygonZKEvmPriceHandler<EvmProvider>),
19    Optimism(OptimismPriceHandler<EvmProvider>),
20    #[cfg(test)]
21    Mock(MockPriceHandler),
22}
23
24impl PriceParamsHandler {
25    /// Create a handler for the given network.
26    ///
27    /// Returns None for networks that don't require custom price calculations.
28    pub fn for_network(network: &EvmNetwork, provider: EvmProvider) -> Option<Self> {
29        if network.is_polygon_zkevm() {
30            Some(PriceParamsHandler::PolygonZKEvm(
31                PolygonZKEvmPriceHandler::new(provider),
32            ))
33        } else if network.is_optimism() {
34            Some(PriceParamsHandler::Optimism(OptimismPriceHandler::new(
35                provider,
36            )))
37        } else {
38            None
39        }
40    }
41
42    /// Handle custom price parameters for a transaction.
43    ///
44    /// This method receives the original calculated parameters and modifies them
45    /// according to the specific network's requirements.
46    pub async fn handle_price_params(
47        &self,
48        tx: &EvmTransactionData,
49        original_params: PriceParams,
50    ) -> Result<PriceParams, TransactionError> {
51        match self {
52            PriceParamsHandler::PolygonZKEvm(handler) => {
53                handler.handle_price_params(tx, original_params).await
54            }
55            PriceParamsHandler::Optimism(handler) => {
56                handler.handle_price_params(tx, original_params).await
57            }
58            #[cfg(test)]
59            PriceParamsHandler::Mock(handler) => {
60                handler.handle_price_params(tx, original_params).await
61            }
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::{
70        constants::{OPTIMISM_BASED_TAG, POLYGON_ZKEVM_TAG},
71        models::{RpcConfig, U256},
72    };
73    use std::env;
74
75    fn create_test_network_with_tags(tags: Vec<&str>) -> EvmNetwork {
76        EvmNetwork {
77            network: "test-network".to_string(),
78            rpc_urls: vec!["https://rpc.example.com".to_string()],
79            explorer_urls: None,
80            average_blocktime_ms: 12000,
81            is_testnet: false,
82            tags: tags.into_iter().map(|s| s.to_string()).collect(),
83            chain_id: 1,
84            required_confirmations: 1,
85            features: vec!["eip1559".to_string()],
86            symbol: "ETH".to_string(),
87            gas_price_cache: None,
88        }
89    }
90
91    fn setup_test_env() {
92        env::set_var("API_KEY", "7EF1CB7C-5003-4696-B384-C72AF8C3E15D");
93        env::set_var("REDIS_URL", "redis://localhost:6379");
94        env::set_var("RPC_TIMEOUT_MS", "5000");
95    }
96
97    #[test]
98    fn test_price_params_handler_for_polygon_zkevm() {
99        setup_test_env();
100        let rpc_configs = vec![RpcConfig::new("http://localhost:8545".to_string())];
101        let provider = EvmProvider::new(rpc_configs, 30).expect("Failed to create EvmProvider");
102        let network = create_test_network_with_tags(vec![POLYGON_ZKEVM_TAG]);
103        let handler = PriceParamsHandler::for_network(&network, provider);
104        assert!(handler.is_some());
105        assert!(
106            matches!(handler, Some(PriceParamsHandler::PolygonZKEvm(_))),
107            "Expected PolygonZKEvm handler variant"
108        );
109    }
110
111    #[test]
112    fn test_price_params_handler_for_optimism() {
113        setup_test_env();
114        let rpc_configs = vec![RpcConfig::new("http://localhost:8545".to_string())];
115        let provider = EvmProvider::new(rpc_configs, 30).expect("Failed to create EvmProvider");
116        let network = create_test_network_with_tags(vec![OPTIMISM_BASED_TAG]);
117        let handler = PriceParamsHandler::for_network(&network, provider);
118        assert!(handler.is_some());
119        assert!(
120            matches!(handler, Some(PriceParamsHandler::Optimism(_))),
121            "Expected Optimism handler variant"
122        );
123    }
124
125    #[test]
126    fn test_price_params_handler_for_non_l2() {
127        setup_test_env();
128        let rpc_configs = vec![RpcConfig::new("http://localhost:8545".to_string())];
129        let provider = EvmProvider::new(rpc_configs, 30).expect("Failed to create EvmProvider");
130        let network = create_test_network_with_tags(vec!["mainnet"]);
131        let handler = PriceParamsHandler::for_network(&network, provider);
132        assert!(handler.is_none());
133    }
134
135    #[tokio::test]
136    async fn test_handle_price_params_with_mock_variant() {
137        setup_test_env();
138        let handler = PriceParamsHandler::Mock(MockPriceHandler::new());
139
140        let tx = EvmTransactionData {
141            from: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string(),
142            to: Some("0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string()),
143            value: U256::from(0u128),
144            data: Some("0x".to_string()),
145            gas_limit: Some(21000),
146            gas_price: Some(1),
147            max_fee_per_gas: None,
148            max_priority_fee_per_gas: None,
149            speed: None,
150            nonce: None,
151            chain_id: 1,
152            hash: None,
153            signature: None,
154            raw: None,
155        };
156
157        let original = PriceParams {
158            gas_price: Some(1),
159            max_fee_per_gas: None,
160            max_priority_fee_per_gas: None,
161            is_min_bumped: None,
162            extra_fee: None,
163            total_cost: U256::from(0),
164        };
165
166        let result = handler.handle_price_params(&tx, original).await.unwrap();
167        assert_eq!(result.extra_fee, Some(U256::from(42u128)));
168        assert_eq!(result.total_cost, U256::from(42u128));
169    }
170}