Skip to content

feat: Provider and PubSub configs #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
139 changes: 139 additions & 0 deletions src/utils/provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use crate::utils::from_env::{FromEnvErr, FromEnvVar};
use alloy::{
providers::{IpcConnect, RootProvider, 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<Self, FromEnvErr<Self::Error>> {
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 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> {
RootProvider::connect_with(self.clone()).await
}
}

impl FromEnvVar for ProviderConfig {
type Error = TransportError;

fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
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!(<Output = Result<BoxTransport, TransportError>>) {
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 PubSubConfig {
/// 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> {
RootProvider::connect_with(self.clone()).await
}
}

impl TryFrom<BuiltInConnectionString> for PubSubConfig {
type Error = TransportError;

fn try_from(connection_string: BuiltInConnectionString) -> Result<Self, Self::Error> {
if !matches!(
connection_string,
BuiltInConnectionString::Ws(_, _) | BuiltInConnectionString::Ipc(_)
) {
return Err(TransportErrorKind::pubsub_unavailable());
}
Ok(Self { connection_string })
}
}

impl FromEnvVar for PubSubConfig {
type Error = TransportError;

fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
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()
}

fn get_transport(
&self,
) -> alloy::transports::impl_future!(<Output = Result<BoxTransport, TransportError>>) {
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!(<Output = TransportResult<ConnectionHandle>>) {
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"),
}
}
}
}