feat(provider): add RwClient (#1016)
* feat(provider): add RwClient * docs: fix failing doc test
This commit is contained in:
parent
19a2ecd58d
commit
02ad93a1cf
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
pubsub::{PubsubClient, SubscriptionStream},
|
pubsub::{PubsubClient, SubscriptionStream},
|
||||||
stream::{FilterWatcher, DEFAULT_POLL_INTERVAL},
|
stream::{FilterWatcher, DEFAULT_POLL_INTERVAL},
|
||||||
FromErr, Http as HttpProvider, JsonRpcClient, JsonRpcClientWrapper, MockProvider,
|
FromErr, Http as HttpProvider, JsonRpcClient, JsonRpcClientWrapper, MockProvider,
|
||||||
PendingTransaction, QuorumProvider, SyncingStatus,
|
PendingTransaction, QuorumProvider, RwClient, SyncingStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "celo")]
|
#[cfg(feature = "celo")]
|
||||||
|
@ -1235,6 +1235,19 @@ impl Provider<crate::Ipc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Read, Write> Provider<RwClient<Read, Write>>
|
||||||
|
where
|
||||||
|
Read: JsonRpcClient + 'static,
|
||||||
|
<Read as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
Write: JsonRpcClient + 'static,
|
||||||
|
<Write as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
{
|
||||||
|
/// Creates a new [Provider] with a [RwClient]
|
||||||
|
pub fn rw(r: Read, w: Write) -> Self {
|
||||||
|
Self::new(RwClient::new(r, w))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: JsonRpcClientWrapper> Provider<QuorumProvider<T>> {
|
impl<T: JsonRpcClientWrapper> Provider<QuorumProvider<T>> {
|
||||||
/// Provider that uses a quorum
|
/// Provider that uses a quorum
|
||||||
pub fn quorum(inner: QuorumProvider<T>) -> Self {
|
pub fn quorum(inner: QuorumProvider<T>) -> Self {
|
||||||
|
|
|
@ -36,5 +36,8 @@ mod quorum;
|
||||||
pub(crate) use quorum::JsonRpcClientWrapper;
|
pub(crate) use quorum::JsonRpcClientWrapper;
|
||||||
pub use quorum::{Quorum, QuorumError, QuorumProvider, WeightedProvider};
|
pub use quorum::{Quorum, QuorumError, QuorumProvider, WeightedProvider};
|
||||||
|
|
||||||
|
mod rw;
|
||||||
|
pub use rw::{RwClient, RwClientError};
|
||||||
|
|
||||||
mod mock;
|
mod mock;
|
||||||
pub use mock::{MockError, MockProvider};
|
pub use mock::{MockError, MockProvider};
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
//! A [JsonRpcClient] implementation that serves as a wrapper around two different [JsonRpcClient]
|
||||||
|
//! and uses a dedicated client for read and the other for write operations
|
||||||
|
|
||||||
|
use crate::{provider::ProviderError, JsonRpcClient};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// A client contains two clients.
|
||||||
|
///
|
||||||
|
/// One is used for _read_ operations
|
||||||
|
/// One is used for _write_ operations that consume gas `["eth_sendTransaction",
|
||||||
|
/// "eth_sendRawTransaction"]`
|
||||||
|
///
|
||||||
|
/// **Note**: if the method is unknown this client falls back to the _read_ client
|
||||||
|
// # Example
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RwClient<Read, Write> {
|
||||||
|
/// client used to read
|
||||||
|
r: Read,
|
||||||
|
/// client used to write
|
||||||
|
w: Write,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Read, Write> RwClient<Read, Write> {
|
||||||
|
/// Creates a new client using two different clients
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use url::Url;
|
||||||
|
/// async fn t(){
|
||||||
|
/// use ethers_providers::{Http, RwClient, Ws};
|
||||||
|
/// let http = Http::new(Url::parse("http://localhost:8545").unwrap());
|
||||||
|
/// let ws = Ws::connect("ws://localhost:8545").await.unwrap();
|
||||||
|
/// let rw = RwClient::new(http, ws);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn new(r: Read, w: Write) -> RwClient<Read, Write> {
|
||||||
|
Self { r, w }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the client used for read operations
|
||||||
|
pub fn read_client(&self) -> &Read {
|
||||||
|
&self.r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the client used for read operations
|
||||||
|
pub fn write_client(&self) -> &Write {
|
||||||
|
&self.w
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `RwClient` with transposed clients
|
||||||
|
pub fn transpose(self) -> RwClient<Write, Read> {
|
||||||
|
let RwClient { r, w } = self;
|
||||||
|
RwClient::new(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the client and returns the underlying clients
|
||||||
|
pub fn split(self) -> (Read, Write) {
|
||||||
|
let RwClient { r, w } = self;
|
||||||
|
(r, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
/// Error thrown when using either read or write client
|
||||||
|
pub enum RwClientError<Read, Write>
|
||||||
|
where
|
||||||
|
Read: JsonRpcClient,
|
||||||
|
<Read as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
Write: JsonRpcClient,
|
||||||
|
<Write as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
{
|
||||||
|
/// Thrown if the _read_ request failed
|
||||||
|
#[error(transparent)]
|
||||||
|
Read(Read::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
/// Thrown if the _write_ request failed
|
||||||
|
Write(Write::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Read, Write> From<RwClientError<Read, Write>> for ProviderError
|
||||||
|
where
|
||||||
|
Read: JsonRpcClient + 'static,
|
||||||
|
<Read as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
Write: JsonRpcClient + 'static,
|
||||||
|
<Write as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
{
|
||||||
|
fn from(src: RwClientError<Read, Write>) -> Self {
|
||||||
|
ProviderError::JsonRpcClientError(Box::new(src))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<Read, Write> JsonRpcClient for RwClient<Read, Write>
|
||||||
|
where
|
||||||
|
Read: JsonRpcClient + 'static,
|
||||||
|
<Read as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
Write: JsonRpcClient + 'static,
|
||||||
|
<Write as JsonRpcClient>::Error: Sync + Send + 'static,
|
||||||
|
{
|
||||||
|
type Error = RwClientError<Read, Write>;
|
||||||
|
|
||||||
|
/// Sends a POST request with the provided method and the params serialized as JSON
|
||||||
|
/// over HTTP
|
||||||
|
async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
|
||||||
|
&self,
|
||||||
|
method: &str,
|
||||||
|
params: T,
|
||||||
|
) -> Result<R, Self::Error>
|
||||||
|
where
|
||||||
|
T: std::fmt::Debug + Serialize + Send + Sync,
|
||||||
|
R: DeserializeOwned,
|
||||||
|
{
|
||||||
|
match method {
|
||||||
|
"eth_sendTransaction" | "eth_sendRawTransaction" => {
|
||||||
|
self.w.request(method, params).await.map_err(RwClientError::Write)
|
||||||
|
}
|
||||||
|
_ => self.r.request(method, params).await.map_err(RwClientError::Read),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
//! Example usage for the `RwClinet` that uses a didicated client to send transaction and nother one
|
||||||
|
//! for read ops
|
||||||
|
|
||||||
|
use ethers::{prelude::*, utils::Ganache};
|
||||||
|
use std::{str::FromStr, time::Duration};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> eyre::Result<()> {
|
||||||
|
let ganache = Ganache::new().spawn();
|
||||||
|
|
||||||
|
let http = Http::from_str(&ganache.endpoint())?;
|
||||||
|
let ws = Ws::connect(ganache.ws_endpoint()).await?;
|
||||||
|
|
||||||
|
let provider = Provider::rw(http, ws).interval(Duration::from_millis(10u64));
|
||||||
|
|
||||||
|
dbg!(provider.get_accounts().await?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue