From d04a0859b78499de67e7f16c71e8fe3d23b013e8 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 5 Aug 2025 13:58:20 -0400 Subject: [PATCH 1/2] feat: Provider and PubSub configs --- Cargo.toml | 2 +- src/lib.rs | 4 ++ src/utils/provider.rs | 104 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/utils/provider.rs diff --git a/Cargo.toml b/Cargo.toml index 036e2d3..40cc5dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "init4-bin-base" description = "Internal utilities for binaries produced by the init4 team" keywords = ["init4", "bin", "base"] -version = "0.9.0" +version = "0.9.1" edition = "2021" rust-version = "1.81" authors = ["init4", "James Prestwich"] diff --git a/src/lib.rs b/src/lib.rs index a2bd866..9984fd4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,10 @@ pub mod utils { /// OpenTelemetry utilities. pub mod otlp; + #[cfg(feature = "alloy")] + /// Alloy Provider configuration and instantiation + pub mod provider; + #[cfg(feature = "alloy")] /// Signer using a local private key or AWS KMS key. pub mod signer; diff --git a/src/utils/provider.rs b/src/utils/provider.rs new file mode 100644 index 0000000..8bc0678 --- /dev/null +++ b/src/utils/provider.rs @@ -0,0 +1,104 @@ +use crate::utils::from_env::{FromEnvErr, FromEnvVar}; +use alloy::{ + providers::{IpcConnect, WsConnect}, + pubsub::{ConnectionHandle, PubSubConnect}, + rpc::client::BuiltInConnectionString, + transports::{ + BoxTransport, TransportConnect, TransportError, TransportErrorKind, TransportResult, + }, +}; + +impl FromEnvVar for BuiltInConnectionString { + type Error = TransportError; + + fn from_env_var(env_var: &str) -> Result> { + let conn_str = String::from_env_var(env_var).map_err(FromEnvErr::infallible_into)?; + conn_str.parse().map_err(Into::into) + } +} + +/// Configuration for an Alloy provider, sourced from an environment variable. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProviderConfig { + connection_string: BuiltInConnectionString, +} + +impl FromEnvVar for ProviderConfig { + type Error = TransportError; + + fn from_env_var(env_var: &str) -> Result> { + let connection_string = BuiltInConnectionString::from_env_var(env_var)?; + Ok(Self { connection_string }) + } +} + +impl TransportConnect for ProviderConfig { + fn is_local(&self) -> bool { + self.connection_string.is_local() + } + + fn get_transport( + &self, + ) -> alloy::transports::impl_future!(>) { + self.connection_string.get_transport() + } +} + +/// Configuration for an Alloy provider, used to create a client, enforces +/// pubsub availability (WS or IPC connection). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PubSubConfig { + connection_string: BuiltInConnectionString, +} + +impl FromEnvVar for PubSubConfig { + type Error = TransportError; + + fn from_env_var(env_var: &str) -> Result> { + let connection_string = BuiltInConnectionString::from_env_var(env_var)?; + + if !matches!( + connection_string, + BuiltInConnectionString::Ws(_, _) | BuiltInConnectionString::Ipc(_) + ) { + return Err(TransportErrorKind::pubsub_unavailable().into()); + } + + Ok(Self { connection_string }) + } +} + +impl TransportConnect for PubSubConfig { + fn is_local(&self) -> bool { + self.connection_string.is_local() + } + + fn get_transport( + &self, + ) -> alloy::transports::impl_future!(>) { + self.connection_string.get_transport() + } +} + +impl PubSubConnect for PubSubConfig { + fn is_local(&self) -> bool { + self.connection_string.is_local() + } + + fn connect( + &self, + ) -> alloy::transports::impl_future!(>) { + async move { + match &self.connection_string { + BuiltInConnectionString::Ws(ws, auth) => { + WsConnect::new(ws.as_str()) + .with_auth_opt(auth.clone()) + .connect() + .await + } + BuiltInConnectionString::Ipc(ipc) => IpcConnect::new(ipc.clone()).connect().await, + _ => unreachable!("can't instantiate http variant"), + } + } + } +} From 001913e238755519cc16b9d5f460a7ab1547bd97 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 5 Aug 2025 14:41:46 -0400 Subject: [PATCH 2/2] feat: connect fns --- src/utils/provider.rs | 51 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/utils/provider.rs b/src/utils/provider.rs index 8bc0678..eafddaf 100644 --- a/src/utils/provider.rs +++ b/src/utils/provider.rs @@ -1,6 +1,6 @@ use crate::utils::from_env::{FromEnvErr, FromEnvVar}; use alloy::{ - providers::{IpcConnect, WsConnect}, + providers::{IpcConnect, RootProvider, WsConnect}, pubsub::{ConnectionHandle, PubSubConnect}, rpc::client::BuiltInConnectionString, transports::{ @@ -23,10 +23,27 @@ pub struct ProviderConfig { connection_string: BuiltInConnectionString, } +impl ProviderConfig { + /// Creates a new `ProviderConfig` from a connection string. + pub const fn new(connection_string: BuiltInConnectionString) -> Self { + Self { connection_string } + } + + /// Returns the connection string. + pub const fn connection_string(&self) -> &BuiltInConnectionString { + &self.connection_string + } + + /// Connects to the provider using the connection string. + pub async fn connect(&self) -> TransportResult { + RootProvider::connect_with(self.clone()).await + } +} + impl FromEnvVar for ProviderConfig { type Error = TransportError; - fn from_env_var(env_var: &str) -> Result> { + fn from_env_var(env_var: &str) -> Result> { let connection_string = BuiltInConnectionString::from_env_var(env_var)?; Ok(Self { connection_string }) } @@ -51,23 +68,41 @@ pub struct PubSubConfig { connection_string: BuiltInConnectionString, } -impl FromEnvVar for PubSubConfig { - type Error = TransportError; +impl PubSubConfig { + /// Returns the connection string. + pub const fn connection_string(&self) -> &BuiltInConnectionString { + &self.connection_string + } - fn from_env_var(env_var: &str) -> Result> { - let connection_string = BuiltInConnectionString::from_env_var(env_var)?; + /// Connects to the provider using the connection string. + pub async fn connect(&self) -> TransportResult { + RootProvider::connect_with(self.clone()).await + } +} + +impl TryFrom for PubSubConfig { + type Error = TransportError; + fn try_from(connection_string: BuiltInConnectionString) -> Result { if !matches!( connection_string, BuiltInConnectionString::Ws(_, _) | BuiltInConnectionString::Ipc(_) ) { - return Err(TransportErrorKind::pubsub_unavailable().into()); + return Err(TransportErrorKind::pubsub_unavailable()); } - Ok(Self { connection_string }) } } +impl FromEnvVar for PubSubConfig { + type Error = TransportError; + + fn from_env_var(env_var: &str) -> Result> { + let cs = BuiltInConnectionString::from_env_var(env_var)?; + Self::try_from(cs).map_err(FromEnvErr::ParseError) + } +} + impl TransportConnect for PubSubConfig { fn is_local(&self) -> bool { self.connection_string.is_local()