Skip to main content

ethers_abirpc/
providers.rs

1use {
2    crate::{
3        chain::{Chain, RetryClientConfig},
4        error::Error,
5    },
6    async_trait::async_trait,
7    ethers::{
8        providers::{
9            Http, HttpRateLimitRetryPolicy, Ipc, Middleware, Provider, RetryClient,
10            RetryClientBuilder, Ws,
11        },
12        types::U256,
13    },
14    std::{path::Path, time::Duration},
15    url::Url,
16};
17
18pub type HttpTransport = Http;
19
20pub type HttpProvider = Provider<HttpTransport>;
21
22pub type WsTransport = Ws;
23
24pub type WsProvider = Provider<WsTransport>;
25
26pub type IpcTransport = Ipc;
27
28pub type IpcProvider = Provider<IpcTransport>;
29
30pub type RetryTransport = RetryClient<Http>;
31
32pub type RetryProvider = Provider<RetryTransport>;
33
34pub type MockProvider = Provider<ethers::providers::MockProvider>;
35
36#[async_trait]
37pub trait AbiProviderTrait<M>
38where
39    M: Middleware,
40{
41    async fn provider(&self) -> Result<M, Error>;
42}
43
44pub struct AbiProvider {
45    pub url: Option<String>,
46    pub chain: Option<Chain>,
47}
48
49impl AbiProvider {
50    pub fn _new(url: Option<String>, chain: Option<Chain>) -> Self {
51        Self { url, chain }
52    }
53
54    pub fn new(url: String, chain: Chain) -> Self {
55        Self {
56            url: Some(url),
57            chain: Some(chain),
58        }
59    }
60
61    pub fn mock() -> Self {
62        Self {
63            url: None,
64            chain: None,
65        }
66    }
67}
68
69macro_rules! assert_chain_id {
70    ($chain: expr, $provider: expr) => {
71        if let Some(chain) = $chain {
72            if chain.assert_chain_id() {
73                let provider_chain_id = $provider.get_chainid().await?;
74                if U256::from(chain.id()) != provider_chain_id {
75                    let e = format!(
76                        "Configured chain_id ({}) does not match chain ({})",
77                        U256::from(chain.id()),
78                        provider_chain_id
79                    );
80                    return Err(Error::ChainIdError(e));
81                }
82            }
83        }
84    };
85}
86
87#[async_trait]
88impl AbiProviderTrait<WsProvider> for AbiProvider {
89    async fn provider(&self) -> Result<WsProvider, Error> {
90        match &self.url {
91            Some(url) => {
92                let url = Url::parse(url)?;
93                let provider = Provider::<WsTransport>::connect(url).await?;
94                assert_chain_id!(self.chain, provider);
95                Ok(provider)
96            }
97            None => Err(Error::Error(String::from("Provider url is None"))),
98        }
99    }
100}
101
102#[async_trait]
103impl AbiProviderTrait<IpcProvider> for AbiProvider {
104    async fn provider(&self) -> Result<IpcProvider, Error> {
105        match &self.url {
106            Some(url) => {
107                let provider = Provider::<IpcTransport>::connect_ipc(Path::new(&url)).await?;
108                assert_chain_id!(self.chain, provider);
109                Ok(provider)
110            }
111            None => Err(Error::Error(String::from("Provider url is None"))),
112        }
113    }
114}
115
116#[async_trait]
117impl AbiProviderTrait<HttpProvider> for AbiProvider {
118    async fn provider(&self) -> Result<HttpProvider, Error> {
119        match &self.url {
120            Some(url) => {
121                let url = Url::parse(url)?;
122                let provider = Provider::<HttpTransport>::new(Http::new(url));
123                assert_chain_id!(self.chain, provider);
124                Ok(provider)
125            }
126            None => Err(Error::Error(String::from("Provider url is None"))),
127        }
128    }
129}
130
131#[async_trait]
132impl AbiProviderTrait<RetryProvider> for AbiProvider {
133    async fn provider(&self) -> Result<RetryProvider, Error> {
134        match &self.url {
135            Some(url) => {
136                let url = Url::parse(url)?;
137                let retry_config = match self.chain {
138                    Some(chain) => chain.retry_client_config(),
139                    None => RetryClientConfig::default(),
140                };
141
142                let provider = Provider::new(
143                    RetryClientBuilder::default()
144                        .rate_limit_retries(retry_config.rate_limit_retries)
145                        .timeout_retries(retry_config.timeout_retries)
146                        .initial_backoff(Duration::from_millis(retry_config.initial_backoff_ms))
147                        .build(Http::new(url), Box::new(HttpRateLimitRetryPolicy)),
148                );
149                assert_chain_id!(self.chain, provider);
150                Ok(provider)
151            }
152            None => Err(Error::Error(String::from("Provider url is None"))),
153        }
154    }
155}
156
157#[async_trait]
158impl AbiProviderTrait<MockProvider> for AbiProvider {
159    async fn provider(&self) -> Result<MockProvider, Error> {
160        match &self.url {
161            Some(_) => Err(Error::Error(String::from("MockProvider url is not None"))),
162            None => {
163                let (provider, _mock) = Provider::mocked();
164                Ok(provider)
165            }
166        }
167    }
168}