bitski_provider/
authenticated_web3_provider.rs

1use crate::access_token_providers::AccessTokenProvider;
2use crate::USER_AGENT;
3use bitski_chain_models::networks::Network;
4use jsonrpc_core::futures::future::BoxFuture;
5use jsonrpc_core::Call;
6use reqwest::header::HeaderValue;
7use reqwest::{header, Client, Url};
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::sync::Arc;
10use web3::futures::{FutureExt, TryFutureExt};
11use web3::transports::Http;
12use web3::{helpers, BatchTransport, RequestId, Transport};
13
14#[derive(Clone, Debug)]
15pub struct AuthenticatedWeb3Provider {
16    pub network: Network,
17    pub client_id: String,
18    pub auth_token_provider: Arc<dyn AccessTokenProvider + Sync + Send>,
19    id: Arc<AtomicUsize>,
20}
21
22impl AuthenticatedWeb3Provider {
23    pub fn new(
24        network: Network,
25        client_id: &dyn ToString,
26        auth_token_provider: Arc<dyn AccessTokenProvider + Sync + Send>,
27    ) -> Self {
28        AuthenticatedWeb3Provider {
29            network,
30            client_id: client_id.to_string(),
31            auth_token_provider,
32            id: Arc::new(AtomicUsize::new(0)),
33        }
34    }
35
36    async fn send_with_auth(
37        url: Url,
38        client_id: String,
39        token: String,
40        id: RequestId,
41        request: Call,
42    ) -> Result<jsonrpc_core::Value, web3::error::Error> {
43        let auth_header_value = format!("Bearer {}", token)
44            .parse()
45            .map_err(|_error| web3::error::Error::Internal)?;
46
47        let mut headers = header::HeaderMap::new();
48        headers.insert(header::AUTHORIZATION, auth_header_value);
49        headers.insert("X-API-Key", HeaderValue::from_str(&client_id).unwrap());
50
51        let client = Client::builder()
52            .user_agent(USER_AGENT.clone())
53            .default_headers(headers)
54            .build()
55            .map_err(|_error| web3::error::Error::Internal)?;
56
57        let transport = Http::with_client(client, url);
58        transport.send(id, request).await
59    }
60
61    async fn convert_to_batch(
62        url: Url,
63        client_id: String,
64        requests: Vec<(RequestId, Call)>,
65        token: String,
66    ) -> web3::error::Result<Vec<web3::error::Result<jsonrpc_core::Value>>> {
67        let mut results = Vec::new();
68        for (id, call) in requests {
69            let result =
70                Self::send_with_auth(url.clone(), client_id.clone(), token.clone(), id, call).await;
71            results.push(result);
72        }
73        Ok(results)
74    }
75}
76
77impl Transport for AuthenticatedWeb3Provider {
78    type Out = BoxFuture<'static, web3::error::Result<jsonrpc_core::Value>>;
79
80    fn prepare(&self, method: &str, params: Vec<serde_json::value::Value>) -> (RequestId, Call) {
81        let id = self.id.fetch_add(1, Ordering::AcqRel);
82        let request = helpers::build_request(id, method, params);
83        (id, request)
84    }
85
86    fn send(&self, id: RequestId, request: Call) -> Self::Out {
87        let url = self
88            .network
89            .rpc_url
90            .parse()
91            .map_err(|_error| web3::error::Error::Internal)
92            .unwrap();
93        let client_id = self.client_id.clone();
94
95        self.auth_token_provider
96            .get_access_token()
97            .map_err(|_error| web3::error::Error::Internal)
98            .and_then(move |token| Self::send_with_auth(url, client_id, token, id, request))
99            .boxed()
100    }
101}
102
103impl BatchTransport for AuthenticatedWeb3Provider {
104    type Batch =
105        BoxFuture<'static, web3::error::Result<Vec<web3::error::Result<jsonrpc_core::Value>>>>;
106
107    fn send_batch<T>(&self, requests: T) -> Self::Batch
108    where
109        T: IntoIterator<Item = (RequestId, Call)>,
110    {
111        let url = self
112            .network
113            .rpc_url
114            .parse()
115            .map_err(|_error| web3::error::Error::Internal)
116            .unwrap();
117        let client_id = self.client_id.clone();
118
119        let requests_vec = requests.into_iter().collect();
120
121        self.auth_token_provider
122            .get_access_token()
123            .map_err(|_error| web3::error::Error::Internal)
124            .and_then(move |token: String| {
125                Self::convert_to_batch(url, client_id, requests_vec, token).boxed()
126            })
127            .boxed()
128    }
129}