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        services::provider::ProviderConfig,
73    };
74    use std::env;
75
76    fn create_test_network_with_tags(tags: Vec<&str>) -> EvmNetwork {
77        EvmNetwork {
78            network: "test-network".to_string(),
79            rpc_urls: vec![crate::models::RpcConfig::new(
80                "https://rpc.example.com".to_string(),
81            )],
82            explorer_urls: None,
83            average_blocktime_ms: 12000,
84            is_testnet: false,
85            tags: tags.into_iter().map(|s| s.to_string()).collect(),
86            chain_id: 1,
87            required_confirmations: 1,
88            features: vec!["eip1559".to_string()],
89            symbol: "ETH".to_string(),
90            gas_price_cache: None,
91        }
92    }
93
94    fn setup_test_env() {
95        env::set_var("API_KEY", "7EF1CB7C-5003-4696-B384-C72AF8C3E15D");
96        env::set_var("REDIS_URL", "redis://localhost:6379");
97        env::set_var("RPC_TIMEOUT_MS", "5000");
98    }
99
100    #[test]
101    fn test_price_params_handler_for_polygon_zkevm() {
102        setup_test_env();
103        let rpc_configs = vec![RpcConfig::new("http://localhost:8545".to_string())];
104        let provider = EvmProvider::new(ProviderConfig::new(rpc_configs, 30, 3, 60, 60))
105            .expect("Failed to create EvmProvider");
106        let network = create_test_network_with_tags(vec![POLYGON_ZKEVM_TAG]);
107        let handler = PriceParamsHandler::for_network(&network, provider);
108        assert!(handler.is_some());
109        assert!(
110            matches!(handler, Some(PriceParamsHandler::PolygonZKEvm(_))),
111            "Expected PolygonZKEvm handler variant"
112        );
113    }
114
115    #[test]
116    fn test_price_params_handler_for_optimism() {
117        setup_test_env();
118        let rpc_configs = vec![RpcConfig::new("http://localhost:8545".to_string())];
119        let provider = EvmProvider::new(ProviderConfig::new(rpc_configs, 30, 3, 60, 60))
120            .expect("Failed to create EvmProvider");
121        let network = create_test_network_with_tags(vec![OPTIMISM_BASED_TAG]);
122        let handler = PriceParamsHandler::for_network(&network, provider);
123        assert!(handler.is_some());
124        assert!(
125            matches!(handler, Some(PriceParamsHandler::Optimism(_))),
126            "Expected Optimism handler variant"
127        );
128    }
129
130    #[test]
131    fn test_price_params_handler_for_non_l2() {
132        setup_test_env();
133        let rpc_configs = vec![RpcConfig::new("http://localhost:8545".to_string())];
134        let provider = EvmProvider::new(ProviderConfig::new(rpc_configs, 30, 3, 60, 60))
135            .expect("Failed to create EvmProvider");
136        let network = create_test_network_with_tags(vec!["mainnet"]);
137        let handler = PriceParamsHandler::for_network(&network, provider);
138        assert!(handler.is_none());
139    }
140
141    #[tokio::test]
142    async fn test_handle_price_params_with_mock_variant() {
143        setup_test_env();
144        let handler = PriceParamsHandler::Mock(MockPriceHandler::new());
145
146        let tx = EvmTransactionData {
147            from: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string(),
148            to: Some("0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string()),
149            value: U256::from(0u128),
150            data: Some("0x".to_string()),
151            gas_limit: Some(21000),
152            gas_price: Some(1),
153            max_fee_per_gas: None,
154            max_priority_fee_per_gas: None,
155            speed: None,
156            nonce: None,
157            chain_id: 1,
158            hash: None,
159            signature: None,
160            raw: None,
161        };
162
163        let original = PriceParams {
164            gas_price: Some(1),
165            max_fee_per_gas: None,
166            max_priority_fee_per_gas: None,
167            is_min_bumped: None,
168            extra_fee: None,
169            total_cost: U256::from(0),
170        };
171
172        let result = handler.handle_price_params(&tx, original).await.unwrap();
173        assert_eq!(result.extra_fee, Some(U256::from(42u128)));
174        assert_eq!(result.total_cost, U256::from(42u128));
175    }
176}