Skip to main content

circles_rpc/
client.rs

1use crate::error::{CirclesRpcError, Result};
2use alloy_json_rpc::{RpcRecv, RpcSend};
3#[cfg(feature = "ws")]
4use alloy_provider::GetSubscription;
5use alloy_provider::{Identity, Provider, ProviderBuilder, RootProvider};
6#[cfg(feature = "ws")]
7use alloy_transport_ws::WsConnect;
8use serde::de::DeserializeOwned;
9use std::borrow::Cow;
10
11/// Thin wrapper around an Alloy provider. This will be expanded with WebSocket support
12/// and reconnection logic as we port the TypeScript client behavior.
13#[derive(Clone, Debug)]
14pub struct RpcClient {
15    provider: RootProvider,
16}
17
18impl RpcClient {
19    /// Create a client from an existing provider.
20    pub fn new(provider: RootProvider) -> Self {
21        Self { provider }
22    }
23
24    /// Build a client from an HTTP URL using the vanilla provider (no fillers).
25    pub fn http(url: reqwest::Url) -> Self {
26        let provider: RootProvider =
27            ProviderBuilder::<Identity, Identity>::default().connect_http(url);
28        Self { provider }
29    }
30
31    /// Build a client from a WebSocket URL (requires the `ws` feature).
32    #[cfg(feature = "ws")]
33    pub async fn ws(url: reqwest::Url) -> Result<Self> {
34        let provider: RootProvider = ProviderBuilder::<Identity, Identity>::default()
35            .connect_ws(WsConnect::new(url.to_string()))
36            .await?;
37        Ok(Self { provider })
38    }
39
40    /// Perform a JSON-RPC call using typed params and response.
41    pub async fn call<Req, Resp>(&self, method: &str, params: Req) -> Result<Resp>
42    where
43        Req: RpcSend,
44        Resp: RpcRecv + DeserializeOwned,
45    {
46        let method: Cow<'static, str> = Cow::Owned(method.to_string());
47        self.provider
48            .raw_request(method, params)
49            .await
50            .map_err(CirclesRpcError::from)
51    }
52
53    /// Access the inner provider. This is useful for lower-level calls or subscriptions.
54    pub fn provider(&self) -> &RootProvider {
55        &self.provider
56    }
57}
58
59#[cfg(feature = "ws")]
60impl RpcClient {
61    /// Subscribe via `eth_subscribe` with arbitrary params. Returns an Alloy subscription handle.
62    pub fn subscribe<P, R>(&self, params: P) -> Result<GetSubscription<P, R>>
63    where
64        P: RpcSend,
65        R: RpcRecv,
66    {
67        Ok(self.provider.subscribe(params))
68    }
69}