init4_bin_base/utils/
provider.rs

1use crate::utils::from_env::{FromEnvErr, FromEnvVar};
2use alloy::{
3    providers::{IpcConnect, RootProvider, WsConnect},
4    pubsub::{ConnectionHandle, PubSubConnect},
5    rpc::client::BuiltInConnectionString,
6    transports::{
7        BoxTransport, TransportConnect, TransportError, TransportErrorKind, TransportResult,
8    },
9};
10
11impl FromEnvVar for BuiltInConnectionString {
12    type Error = TransportError;
13
14    fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
15        let conn_str = String::from_env_var(env_var).map_err(FromEnvErr::infallible_into)?;
16        conn_str.parse().map_err(Into::into)
17    }
18}
19
20/// Configuration for an Alloy provider, sourced from an environment variable.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ProviderConfig {
23    connection_string: BuiltInConnectionString,
24}
25
26impl ProviderConfig {
27    /// Creates a new `ProviderConfig` from a connection string.
28    pub const fn new(connection_string: BuiltInConnectionString) -> Self {
29        Self { connection_string }
30    }
31
32    /// Returns the connection string.
33    pub const fn connection_string(&self) -> &BuiltInConnectionString {
34        &self.connection_string
35    }
36
37    /// Connects to the provider using the connection string.
38    pub async fn connect(&self) -> TransportResult<RootProvider> {
39        RootProvider::connect_with(self.clone()).await
40    }
41}
42
43impl FromEnvVar for ProviderConfig {
44    type Error = TransportError;
45
46    fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
47        let connection_string = BuiltInConnectionString::from_env_var(env_var)?;
48        Ok(Self { connection_string })
49    }
50}
51
52impl TransportConnect for ProviderConfig {
53    fn is_local(&self) -> bool {
54        self.connection_string.is_local()
55    }
56
57    fn get_transport(
58        &self,
59    ) -> alloy::transports::impl_future!(<Output = Result<BoxTransport, TransportError>>) {
60        self.connection_string.get_transport()
61    }
62}
63
64/// Configuration for an Alloy provider, used to create a client, enforces
65/// pubsub availability (WS or IPC connection).
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct PubSubConfig {
68    connection_string: BuiltInConnectionString,
69}
70
71impl PubSubConfig {
72    /// Returns the connection string.
73    pub const fn connection_string(&self) -> &BuiltInConnectionString {
74        &self.connection_string
75    }
76
77    /// Connects to the provider using the connection string.
78    pub async fn connect(&self) -> TransportResult<RootProvider> {
79        RootProvider::connect_with(self.clone()).await
80    }
81}
82
83impl TryFrom<BuiltInConnectionString> for PubSubConfig {
84    type Error = TransportError;
85
86    fn try_from(connection_string: BuiltInConnectionString) -> Result<Self, Self::Error> {
87        if !matches!(
88            connection_string,
89            BuiltInConnectionString::Ws(_, _) | BuiltInConnectionString::Ipc(_)
90        ) {
91            return Err(TransportErrorKind::pubsub_unavailable());
92        }
93        Ok(Self { connection_string })
94    }
95}
96
97impl FromEnvVar for PubSubConfig {
98    type Error = TransportError;
99
100    fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
101        let cs = BuiltInConnectionString::from_env_var(env_var)?;
102        Self::try_from(cs).map_err(FromEnvErr::ParseError)
103    }
104}
105
106impl TransportConnect for PubSubConfig {
107    fn is_local(&self) -> bool {
108        self.connection_string.is_local()
109    }
110
111    fn get_transport(
112        &self,
113    ) -> alloy::transports::impl_future!(<Output = Result<BoxTransport, TransportError>>) {
114        self.connection_string.get_transport()
115    }
116}
117
118impl PubSubConnect for PubSubConfig {
119    fn is_local(&self) -> bool {
120        self.connection_string.is_local()
121    }
122
123    fn connect(
124        &self,
125    ) -> alloy::transports::impl_future!(<Output = TransportResult<ConnectionHandle>>) {
126        async move {
127            match &self.connection_string {
128                BuiltInConnectionString::Ws(ws, auth) => {
129                    WsConnect::new(ws.as_str())
130                        .with_auth_opt(auth.clone())
131                        .connect()
132                        .await
133                }
134                BuiltInConnectionString::Ipc(ipc) => IpcConnect::new(ipc.clone()).connect().await,
135                _ => unreachable!("can't instantiate http variant"),
136            }
137        }
138    }
139}