Skip to content

Commit

Permalink
feat(providers): connect_boxed api (#342)
Browse files Browse the repository at this point in the history
* feat(providers): connect_boxed api

* parse error handling and nits

* enhance parse str to transport util

* extract auth info for ws in connect_boxed

* ci nits

* fix: dependencies, refactor provider utils

* return conn_str in enum

* fix: parsing urls tests and resolve conflicts

* udpate utils and remove unwraps

* fix: cfg for ws, ipc features

* nits and fix test

* more nits

* smol nits and url in enum

* fix ci: make ipc and ws optional

* ci nits

* refactor: bump down and bubble up

* feature: connect_builtin

* feature: connect_builtin

* feature: on_ws and on_ipc

* fix: http feature handling

* fix: parsing

* fix: test

* fix: feature check

* fix: import

* fix: unreachable match

* fix: matches are such a pain

* fix: imports some more

* fix: activate url for ws

---------

Co-authored-by: James <james@prestwi.ch>
  • Loading branch information
yash-atreya and prestwich committed Mar 26, 2024
1 parent 73d648b commit 68952c0
Show file tree
Hide file tree
Showing 10 changed files with 455 additions and 24 deletions.
4 changes: 2 additions & 2 deletions crates/alloy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ alloy-genesis = { workspace = true, default-features = false, optional = true }
alloy-network = { workspace = true, default-features = false, optional = true }
alloy-node-bindings = { workspace = true, default-features = false, optional = true }
alloy-provider = { workspace = true, default-features = false, optional = true }
alloy-pubsub = { workspace = true, default-features = false, optional = true }
alloy-pubsub = { workspace = true, optional = true }

# rpc
alloy-json-rpc = { workspace = true, default-features = false, optional = true }
alloy-rpc-client = { workspace = true, default-features = false, optional = true }
alloy-rpc-client = { workspace = true, optional = true }
alloy-rpc-engine-types = { workspace = true, default-features = false, optional = true }
alloy-rpc-trace-types = { workspace = true, default-features = false, optional = true }
alloy-rpc-types = { workspace = true, default-features = false, optional = true }
Expand Down
8 changes: 5 additions & 3 deletions crates/provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ alloy-rpc-client = { workspace = true, features = ["reqwest"] }
alloy-rpc-trace-types.workspace = true
alloy-rpc-types.workspace = true
alloy-transport-http = { workspace = true, features = ["reqwest"] }
alloy-transport-ws = { workspace = true, optional = true }
alloy-transport-ipc = { workspace = true, optional = true }
alloy-transport.workspace = true

alloy-primitives.workspace = true

async-stream = "0.3"
Expand All @@ -43,8 +44,9 @@ alloy-signer.workspace = true
alloy-signer-wallet.workspace = true
tokio = { workspace = true, features = ["macros"] }
tracing-subscriber = { workspace = true, features = ["fmt"] }
tempfile.workspace = true

[features]
pubsub = ["alloy-rpc-client/pubsub", "dep:alloy-pubsub"]
ws = ["pubsub", "alloy-rpc-client/ws"]
ipc = ["pubsub", "alloy-rpc-client/ipc"]
ws = ["pubsub", "alloy-rpc-client/ws", "alloy-transport-ws"]
ipc = ["pubsub", "alloy-rpc-client/ipc", "alloy-transport-ipc"]
76 changes: 74 additions & 2 deletions crates/provider/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::{
Provider, RootProvider,
};
use alloy_network::{Ethereum, Network};
use alloy_rpc_client::RpcClient;
use alloy_transport::Transport;
use alloy_rpc_client::{BuiltInConnectionString, ClientBuilder, RpcClient};
use alloy_transport::{BoxTransport, Transport, TransportError};
use std::marker::PhantomData;

/// A layering abstraction in the vein of [`tower::Layer`]
Expand Down Expand Up @@ -172,6 +172,78 @@ impl<L, N> ProviderBuilder<L, N> {
{
self.provider(RootProvider::new(client))
}

/// Finish the layer stack by providing a connection string for a built-in
/// transport type, outputting the final [`Provider`] type with all stack
/// components.
///
/// This is a convenience function for
pub async fn on_builtin(self, s: &str) -> Result<L::Provider, TransportError>
where
L: ProviderLayer<RootProvider<N, BoxTransport>, N, BoxTransport>,
N: Network,
{
let connect: BuiltInConnectionString = s.parse()?;
let client = ClientBuilder::default().connect_boxed(connect).await?;
Ok(self.on_client(client))
}

/// Build this provider with a websocket connection.
#[cfg(feature = "ws")]
pub async fn on_ws(
self,
connect: alloy_transport_ws::WsConnect,
) -> Result<L::Provider, TransportError>
where
L: ProviderLayer<
RootProvider<N, alloy_pubsub::PubSubFrontend>,
N,
alloy_pubsub::PubSubFrontend,
>,
N: Network,
{
let client = ClientBuilder::default().ws(connect).await?;
Ok(self.on_client(client))
}

/// Build this provider with an IPC connection.
#[cfg(feature = "ipc")]
pub async fn on_ipc<T>(
self,
connect: alloy_transport_ipc::IpcConnect<T>,
) -> Result<L::Provider, TransportError>
where
alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
L: ProviderLayer<
RootProvider<N, alloy_pubsub::PubSubFrontend>,
N,
alloy_pubsub::PubSubFrontend,
>,
N: Network,
{
let client = ClientBuilder::default().ipc(connect).await?;
Ok(self.on_client(client))
}

#[cfg(feature = "reqwest")]
pub fn on_reqwest_http(self, url: url::Url) -> Result<L::Provider, TransportError>
where
L: ProviderLayer<RootProvider<N, BoxTransport>, N, BoxTransport>,
N: Network,
{
let client = ClientBuilder::default().reqwest_http(url);
Ok(self.on_client(client))
}

#[cfg(feature = "hyper")]
pub fn on_hyper_http(self, url: url::Url) -> Result<L::Provider, TransportError>
where
L: ProviderLayer<RootProvider<N, BoxTransport>, N, BoxTransport>,
N: Network,
{
let client = ClientBuilder::default().hyper_http(url);
Ok(self.on_client(client))
}
}

// Copyright (c) 2019 Tower Contributors
Expand Down
47 changes: 45 additions & 2 deletions crates/provider/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use alloy_network::{Network, TransactionBuilder};
use alloy_primitives::{
hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U256, U64,
};
use alloy_rpc_client::{ClientRef, PollerBuilder, RpcClient, WeakClient};
use alloy_rpc_client::{
BuiltInConnectionString, ClientBuilder, ClientRef, PollerBuilder, RpcClient, WeakClient,
};
use alloy_rpc_trace_types::{
geth::{GethDebugTracingOptions, GethTrace},
parity::{LocalizedTransactionTrace, TraceResults, TraceType},
Expand All @@ -20,7 +22,10 @@ use alloy_rpc_types::{
state::StateOverride, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag,
EIP1186AccountProofResponse, FeeHistory, Filter, FilterChanges, Log, SyncStatus,
};
use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult};
use alloy_transport::{
BoxTransport, BoxTransportConnect, Transport, TransportError, TransportErrorKind,
TransportResult,
};
use alloy_transport_http::Http;
use serde_json::value::RawValue;
use std::{
Expand Down Expand Up @@ -71,6 +76,21 @@ impl<N: Network, T: Transport> RootProvider<N, T> {
}
}

impl<N: Network> RootProvider<N, BoxTransport> {
/// Connects to a boxed transport with the given connector.
pub async fn connect_boxed<C: BoxTransportConnect>(conn: C) -> Result<Self, TransportError> {
let client = ClientBuilder::default().connect_boxed(conn).await?;
Ok(Self::new(client))
}

/// Creates a new root provider from the provided connection details.
pub async fn connect_builtin(s: &str) -> Result<Self, TransportError> {
let conn: BuiltInConnectionString = s.parse()?;
let client = ClientBuilder::default().connect_boxed(conn).await?;
Ok(Self::new(client))
}
}

impl<N: Network, T: Transport + Clone> RootProvider<N, T> {
/// Boxes the inner client.
///
Expand Down Expand Up @@ -1377,4 +1397,27 @@ mod tests {
"0x9dae5cf33694a02e8a7d5de3fe31e9d05ca0ba6e9180efac4ab20a06c9e598a3"
);
}

#[tokio::test]
async fn connect_boxed() {
init_tracing();
let (_provider, anvil) = spawn_anvil();

let provider =
RootProvider::<Ethereum, BoxTransport>::connect_builtin(anvil.endpoint().as_str())
.await;

match provider {
Ok(provider) => {
let num = provider.get_block_number().await.unwrap();
assert_eq!(0, num);
}
Err(e) => {
assert_eq!(
format!("{}",e),
"hyper not supported by BuiltinConnectionString. Please instantiate a hyper client manually"
);
}
}
}
}
2 changes: 1 addition & 1 deletion crates/rpc-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ default = ["reqwest"]
reqwest = ["dep:url", "dep:reqwest", "alloy-transport-http/reqwest"]
hyper = ["dep:url", "dep:hyper-util", "alloy-transport-http/hyper"]
pubsub = ["dep:alloy-pubsub", "dep:alloy-primitives"]
ws = ["pubsub", "dep:alloy-transport-ws"]
ws = ["pubsub", "dep:alloy-transport-ws", "dep:url"]
ipc = ["pubsub", "dep:alloy-transport-ipc"]
15 changes: 15 additions & 0 deletions crates/rpc-client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ impl<L> ClientBuilder<L> {
self.pubsub(ws_connect).await
}

/// Connect an IPC transport, producing an [`RpcClient`] with the provided
/// connection.
#[cfg(feature = "ipc")]
pub async fn ipc<T>(
self,
ipc_connect: alloy_transport_ipc::IpcConnect<T>,
) -> TransportResult<RpcClient<L::Service>>
where
alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
L: Layer<alloy_pubsub::PubSubFrontend>,
L::Service: Transport,
{
self.pubsub(ipc_connect).await
}

/// Connect a transport, producing an [`RpcClient`] with the provided
/// connection.
pub async fn connect<C>(self, connect: C) -> TransportResult<RpcClient<L::Service>>
Expand Down
Loading

0 comments on commit 68952c0

Please sign in to comment.