sui_bridge/
metered_eth_provider.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::metrics::BridgeMetrics;
5use ethers::providers::{Http, HttpClientError, JsonRpcClient, Provider};
6use serde::{Serialize, de::DeserializeOwned};
7use std::fmt::Debug;
8use std::sync::Arc;
9use url::{ParseError, Url};
10
11#[derive(Debug, Clone)]
12pub struct MeteredEthHttpProvider {
13    inner: Http,
14    metrics: Arc<BridgeMetrics>,
15}
16
17#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
18#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
19impl JsonRpcClient for MeteredEthHttpProvider {
20    type Error = HttpClientError;
21
22    async fn request<T: Serialize + Send + Sync + Debug, R: DeserializeOwned + Send>(
23        &self,
24        method: &str,
25        params: T,
26    ) -> Result<R, HttpClientError> {
27        self.metrics
28            .eth_rpc_queries
29            .with_label_values(&[method])
30            .inc();
31        let _guard = self
32            .metrics
33            .eth_rpc_queries_latency
34            .with_label_values(&[method])
35            .start_timer();
36        self.inner.request(method, params).await
37    }
38}
39
40impl MeteredEthHttpProvider {
41    pub fn new(url: impl Into<Url>, metrics: Arc<BridgeMetrics>) -> Self {
42        let client = reqwest::Client::builder()
43            .timeout(std::time::Duration::from_secs(30))
44            .build()
45            .unwrap();
46        let inner = Http::new_with_client(url, client);
47        Self { inner, metrics }
48    }
49}
50
51pub fn new_metered_eth_provider(
52    url: &str,
53    metrics: Arc<BridgeMetrics>,
54) -> Result<Provider<MeteredEthHttpProvider>, ParseError> {
55    let http_provider = MeteredEthHttpProvider::new(Url::parse(url)?, metrics);
56    Ok(Provider::new(http_provider))
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use ethers::providers::Middleware;
63    use prometheus::Registry;
64
65    #[tokio::test]
66    async fn test_metered_eth_provider() {
67        let metrics = Arc::new(BridgeMetrics::new(&Registry::new()));
68        let provider = new_metered_eth_provider("http://localhost:9876", metrics.clone()).unwrap();
69
70        assert_eq!(
71            metrics
72                .eth_rpc_queries
73                .get_metric_with_label_values(&["eth_blockNumber"])
74                .unwrap()
75                .get(),
76            0
77        );
78        assert_eq!(
79            metrics
80                .eth_rpc_queries_latency
81                .get_metric_with_label_values(&["eth_blockNumber"])
82                .unwrap()
83                .get_sample_count(),
84            0
85        );
86
87        provider.get_block_number().await.unwrap_err(); // the rpc cal will fail but we don't care
88
89        assert_eq!(
90            metrics
91                .eth_rpc_queries
92                .get_metric_with_label_values(&["eth_blockNumber"])
93                .unwrap()
94                .get(),
95            1
96        );
97        assert_eq!(
98            metrics
99                .eth_rpc_queries_latency
100                .get_metric_with_label_values(&["eth_blockNumber"])
101                .unwrap()
102                .get_sample_count(),
103            1
104        );
105    }
106}