web3-proxy/web3_proxy/src/rpcs/provider.rs

81 lines
3.1 KiB
Rust
Raw Normal View History

2023-05-11 23:07:31 +03:00
use ethers::providers::{Authorization, ConnectionDetails};
2023-05-24 00:40:34 +03:00
use std::time::Duration;
2023-05-11 23:07:31 +03:00
use url::Url;
2023-07-15 04:31:00 +03:00
use crate::errors::Web3ProxyResult;
pub type EthersHttpProvider = ethers::providers::Provider<ethers::providers::Http>;
pub type EthersWsProvider = ethers::providers::Provider<ethers::providers::Ws>;
2023-05-24 00:40:34 +03:00
pub fn extract_auth(url: &mut Url) -> Option<Authorization> {
if let Some(pass) = url.password().map(|x| x.to_string()) {
// to_string is needed because we are going to remove these items from the url
let user = url.username().to_string();
2022-08-24 03:11:49 +03:00
// clear username and password from the url
2023-05-24 00:40:34 +03:00
url.set_username("")
.expect("unable to clear username on websocket");
2023-05-24 00:40:34 +03:00
url.set_password(None)
.expect("unable to clear password on websocket");
// keep them
Some(Authorization::basic(user, pass))
} else {
None
}
}
2022-08-24 03:11:49 +03:00
/// Note, if the http url has an authority the http_client param is ignored and a dedicated http_client will be used
/// TODO: take a reqwest::Client or a reqwest::ClientBuilder. that way we can do things like set compression even when auth is set
pub fn connect_http(
2023-05-24 00:40:34 +03:00
mut url: Url,
http_client: Option<reqwest::Client>,
2023-05-24 00:40:34 +03:00
interval: Duration,
2023-07-15 04:31:00 +03:00
) -> Web3ProxyResult<EthersHttpProvider> {
let auth = extract_auth(&mut url);
2023-05-11 23:07:31 +03:00
2023-05-24 00:40:34 +03:00
let mut provider = if url.scheme().starts_with("http") {
let provider = if let Some(auth) = auth {
2023-07-15 04:31:00 +03:00
// TODO: there are two "HttpClientError" in ethers. this one is not in the prelude
ethers::providers::Http::new_with_auth(url, auth)
.map_err(|err| anyhow::anyhow!("http client error: {:?}", err))?
} else if let Some(http_client) = http_client {
2023-05-24 00:40:34 +03:00
ethers::providers::Http::new_with_client(url, http_client)
2023-05-11 23:07:31 +03:00
} else {
2023-05-24 00:40:34 +03:00
ethers::providers::Http::new(url)
2023-05-11 23:07:31 +03:00
};
// TODO: i don't think this interval matters for our uses, but we should probably set it to like `block time / 2`
2023-05-24 00:40:34 +03:00
ethers::providers::Provider::new(provider).interval(Duration::from_secs(2))
} else {
2023-07-15 04:31:00 +03:00
return Err(anyhow::anyhow!("only http servers are supported. cannot use {}", url).into());
};
2023-05-24 00:40:34 +03:00
provider.set_interval(interval);
Ok(provider)
}
2022-08-24 03:11:49 +03:00
2023-07-15 04:31:00 +03:00
pub async fn connect_ws(mut url: Url, reconnects: usize) -> Web3ProxyResult<EthersWsProvider> {
let auth = extract_auth(&mut url);
2023-05-11 23:07:31 +03:00
let provider = if url.scheme().starts_with("ws") {
let provider = if auth.is_some() {
let connection_details = ConnectionDetails::new(url.as_str(), auth);
2022-08-24 03:11:49 +03:00
// if they error, we do our own reconnection with backoff
ethers::providers::Ws::connect_with_reconnects(connection_details, reconnects).await?
2022-08-24 03:11:49 +03:00
} else {
ethers::providers::Ws::connect_with_reconnects(url.as_str(), reconnects).await?
2022-08-24 03:11:49 +03:00
};
// TODO: dry this up (needs https://github.com/gakonst/ethers-rs/issues/592)
// TODO: i don't think this interval matters
2023-05-24 00:40:34 +03:00
ethers::providers::Provider::new(provider)
} else {
2023-07-15 04:31:00 +03:00
return Err(anyhow::anyhow!("ws servers are supported").into());
};
Ok(provider)
2022-08-24 03:11:49 +03:00
}