Skip to content

Commit

Permalink
feature: ProviderCall (#788)
Browse files Browse the repository at this point in the history
* feature: ProviderCall

fix: rename to prov_call for disambiguation

* refactor: replace output types

* doc: add note about fn pointers

* feat: convert `EthCall` to `ProviderCall`

* use `ClientRef` in EthCall instead of `WeakClient`

* change lifetimes

* add *_internal methods that return `EthCall`

* doc nits

* use `ClientRef` instead of `WeakClient` in `RpcWithBlock`

* Revert "use `ClientRef` instead of `WeakClient` in `RpcWithBlock`"

This reverts commit 569d168.

* feat: RpcCallWithBlock enum val in provider call

* nits

* feat: integrate `RpcWithBlock` into `ProviderCall`

* rm `RpcWithBlock` val from ProviderCall

* feat(provider): caller trait for with_block calls

* rm `WeakClient` from `RpcWithBlock`

* introduce `Caller` into `RpcWithBlock`

* use `WithBlockCall` in `RpcWithBlock`. polling doesn't work.

* nit

* feat(provider): add `WithBlockFut` - polls `ProviderCall` and maps response.

* nit

* revert 2ff402

* Revert "feat: convert `EthCall` to `ProviderCall`"

This reverts commit 1cb1579.

* doc nits

* feat(provider): introduce `Caller` to `EthCall`. Arc it so that its cloneable

* feat(provider): add `EthCaller` - converts EthCall into ProviderCall and strengthen test

* update `EthCallFut`
* holds `ProviderCall`
* removes Clone

* impl poll_preparing and poll_running on `EthCallFut` instead of `EthCallFutInner`

* poll `ProviderCall` in `EthCall`using `EthCaller`
* removes WeakClient from `EthCall`
* updates `EthCallFutInner::Running` to store map

* nit

* wrap Caller in Arc to make `RpcWithBlock` cloneable

* store in `ProvideCall` fut instance in `States`

* store `ProviderCall` fut in `EthCallFutInner`

* feat(provider): add `ParamsWithBlock` for `RpcWithBlock`

* rm `block_id` from `Caller` trait

* impl `Caller` for `WeakClient`

* refactor(`ProviderCall`): don't rely on `Caller` in `RpcWithBlock` (#1159)

* feat: RpcCall:map_params

* WithBlockInner

* Ready variant

* fmt

* use ProviderCall

* clippy + fmt

* add From<F>

* make ProviderCall::Ready return a result

* add docs

* Update request.rs

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>

* fmt

---------

Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>

* refac(provider): use `ProviderCall` as return in trait

* nit

* doc nits

* fix

---------

Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com>
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
  • Loading branch information
4 people authored Sep 18, 2024
1 parent 79fe182 commit 6369bcc
Show file tree
Hide file tree
Showing 13 changed files with 770 additions and 366 deletions.
4 changes: 2 additions & 2 deletions crates/contract/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ impl<T: Transport + Clone, P: Provider<T, N>, D: CallDecoder, N: Network> CallBu
/// If this is not desired, use [`call_raw`](Self::call_raw) to get the raw output data.
#[doc(alias = "eth_call")]
#[doc(alias = "call_with_overrides")]
pub fn call(&self) -> EthCall<'_, '_, '_, D, T, N> {
pub fn call(&self) -> EthCall<'_, '_, D, T, N> {
self.call_raw().with_decoder(&self.decoder)
}

Expand All @@ -457,7 +457,7 @@ impl<T: Transport + Clone, P: Provider<T, N>, D: CallDecoder, N: Network> CallBu
/// Does not decode the output of the call, returning the raw output data instead.
///
/// See [`call`](Self::call) for more information.
pub fn call_raw(&self) -> EthCall<'_, '_, '_, (), T, N> {
pub fn call_raw(&self) -> EthCall<'_, '_, (), T, N> {
let call = self.provider.call(&self.request).block(self.block);
let call = match &self.state {
Some(state) => call.overrides(state),
Expand Down
38 changes: 18 additions & 20 deletions crates/contract/src/eth_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,44 +24,44 @@ mod private {
/// An [`alloy_provider::EthCall`] with an abi decoder.
#[must_use = "EthCall must be awaited to execute the call"]
#[derive(Clone, Debug)]
pub struct EthCall<'req, 'state, 'coder, D, T, N>
pub struct EthCall<'req, 'coder, D, T, N>
where
T: Transport + Clone,
N: Network,
D: CallDecoder,
{
inner: alloy_provider::EthCall<'req, 'state, T, N, Bytes>,
inner: alloy_provider::EthCall<'req, T, N, Bytes>,

decoder: &'coder D,
}

impl<'req, 'state, 'coder, D, T, N> EthCall<'req, 'state, 'coder, D, T, N>
impl<'req, 'coder, D, T, N> EthCall<'req, 'coder, D, T, N>
where
T: Transport + Clone,
N: Network,
D: CallDecoder,
{
/// Create a new [`EthCall`].
pub const fn new(
inner: alloy_provider::EthCall<'req, 'state, T, N, Bytes>,
inner: alloy_provider::EthCall<'req, T, N, Bytes>,
decoder: &'coder D,
) -> Self {
Self { inner, decoder }
}
}

impl<'req, 'state, T, N> EthCall<'req, 'state, 'static, (), T, N>
impl<'req, T, N> EthCall<'req, 'static, (), T, N>
where
T: Transport + Clone,
N: Network,
{
/// Create a new [`EthCall`].
pub const fn new_raw(inner: alloy_provider::EthCall<'req, 'state, T, N, Bytes>) -> Self {
pub const fn new_raw(inner: alloy_provider::EthCall<'req, T, N, Bytes>) -> Self {
Self::new(inner, &RAW_CODER)
}
}

impl<'req, 'state, 'coder, D, T, N> EthCall<'req, 'state, 'coder, D, T, N>
impl<'req, 'coder, D, T, N> EthCall<'req, 'coder, D, T, N>
where
T: Transport + Clone,
N: Network,
Expand All @@ -71,15 +71,15 @@ where
pub fn with_decoder<'new_coder, E>(
self,
decoder: &'new_coder E,
) -> EthCall<'req, 'state, 'new_coder, E, T, N>
) -> EthCall<'req, 'new_coder, E, T, N>
where
E: CallDecoder,
{
EthCall { inner: self.inner, decoder }
}

/// Set the state overrides for this call.
pub fn overrides(mut self, overrides: &'state StateOverride) -> Self {
pub fn overrides(mut self, overrides: &'req StateOverride) -> Self {
self.inner = self.inner.overrides(overrides);
self
}
Expand All @@ -91,27 +91,26 @@ where
}
}

impl<'req, 'state, T, N> From<alloy_provider::EthCall<'req, 'state, T, N, Bytes>>
for EthCall<'req, 'state, 'static, (), T, N>
impl<'req, T, N> From<alloy_provider::EthCall<'req, T, N, Bytes>>
for EthCall<'req, 'static, (), T, N>
where
T: Transport + Clone,
N: Network,
{
fn from(inner: alloy_provider::EthCall<'req, 'state, T, N, Bytes>) -> Self {
fn from(inner: alloy_provider::EthCall<'req, T, N, Bytes>) -> Self {
Self { inner, decoder: &RAW_CODER }
}
}

impl<'req, 'state, 'coder, D, T, N> std::future::IntoFuture
for EthCall<'req, 'state, 'coder, D, T, N>
impl<'req, 'coder, D, T, N> std::future::IntoFuture for EthCall<'req, 'coder, D, T, N>
where
D: CallDecoder + Unpin,
T: Transport + Clone,
N: Network,
{
type Output = Result<D::CallOutput>;

type IntoFuture = EthCallFut<'req, 'state, 'coder, D, T, N>;
type IntoFuture = EthCallFut<'req, 'coder, D, T, N>;

fn into_future(self) -> Self::IntoFuture {
EthCallFut { inner: self.inner.into_future(), decoder: self.decoder }
Expand All @@ -121,20 +120,19 @@ where
/// Future for the [`EthCall`] type. This future wraps an RPC call with an abi
/// decoder.
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Clone, Debug)]
#[derive(Debug)]
#[allow(unnameable_types)]
pub struct EthCallFut<'req, 'state, 'coder, D, T, N>
pub struct EthCallFut<'req, 'coder, D, T, N>
where
T: Transport + Clone,
N: Network,
D: CallDecoder,
{
inner: <alloy_provider::EthCall<'req, 'state, T, N, Bytes> as IntoFuture>::IntoFuture,
inner: <alloy_provider::EthCall<'req, T, N, Bytes> as IntoFuture>::IntoFuture,
decoder: &'coder D,
}

impl<'req, 'state, 'coder, D, T, N> std::future::Future
for EthCallFut<'req, 'state, 'coder, D, T, N>
impl<'req, 'coder, D, T, N> std::future::Future for EthCallFut<'req, 'coder, D, T, N>
where
D: CallDecoder + Unpin,
T: Transport + Clone,
Expand Down
15 changes: 12 additions & 3 deletions crates/json-rpc/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ impl<Params> Request<Params> {
pub fn set_subscription_status(&mut self, sub: bool) {
self.meta.set_subscription_status(sub);
}

/// Change type of the request parameters.
pub fn map_params<NewParams>(
self,
map: impl FnOnce(Params) -> NewParams,
) -> Request<NewParams> {
Request { meta: self.meta, params: map(self.params) }
}
}

/// A [`Request`] that has been partially serialized.
Expand Down Expand Up @@ -113,11 +121,12 @@ where

impl<Params> Request<&Params>
where
Params: Clone,
Params: ToOwned,
Params::Owned: RpcParam,
{
/// Clone the request, including the request parameters.
pub fn into_owned_params(self) -> Request<Params> {
Request { meta: self.meta, params: self.params.clone() }
pub fn into_owned_params(self) -> Request<Params::Owned> {
Request { meta: self.meta, params: self.params.to_owned() }
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/provider/src/ext/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ where
trace_types: &'b [TraceType],
) -> RpcWithBlock<T, (&'a <N as Network>::TransactionRequest, &'b [TraceType]), TraceResults>
{
RpcWithBlock::new(self.weak_client(), "trace_call", (request, trace_types))
self.client().request("trace_call", (request, trace_types)).into()
}

fn trace_call_many<'a>(
&self,
request: TraceCallList<'a, N>,
) -> RpcWithBlock<T, (TraceCallList<'a, N>,), Vec<TraceResults>> {
RpcWithBlock::new(self.weak_client(), "trace_callMany", (request,))
self.client().request("trace_callMany", (request,)).into()
}

async fn trace_transaction(
Expand Down
11 changes: 6 additions & 5 deletions crates/provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,24 @@ extern crate tracing;
mod builder;
pub use builder::{Identity, ProviderBuilder, ProviderLayer, Stack};

mod chain;

pub mod ext;

pub mod fillers;
pub mod layers;

mod chain;

mod heart;
pub use heart::{
PendingTransaction, PendingTransactionBuilder, PendingTransactionConfig,
PendingTransactionError, WatchTxError,
};

pub mod layers;

mod provider;
pub use provider::{
builder, EthCall, FilterPollerBuilder, Provider, RootProvider, RpcWithBlock, SendableTx,
WalletProvider,
builder, Caller, EthCall, EthCallParams, FilterPollerBuilder, ParamsWithBlock, Provider,
ProviderCall, RootProvider, RpcWithBlock, SendableTx, WalletProvider,
};

pub mod utils;
Expand Down
47 changes: 47 additions & 0 deletions crates/provider/src/provider/caller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::ProviderCall;
use alloy_json_rpc::{RpcParam, RpcReturn};
use alloy_rpc_client::WeakClient;
use alloy_transport::{RpcError, Transport, TransportErrorKind, TransportResult};
use std::borrow::Cow;

// TODO: Make `EthCall` specific. Ref: https://github.com/alloy-rs/alloy/pull/788#discussion_r1748862509.

/// Trait that helpes convert `EthCall` into a `ProviderCall`.
pub trait Caller<T, Params, Resp>: Send + Sync
where
T: Transport + Clone,
Params: RpcParam,
Resp: RpcReturn,
{
/// Method that needs to be implemented to convert to a `ProviderCall`.
///
/// This method handles serialization of the params and sends the request to relevant data
/// source and returns a `ProviderCall`.
fn call(
&self,
method: Cow<'static, str>,
params: Params,
) -> TransportResult<ProviderCall<T, serde_json::Value, Resp>>;
}

impl<T, Params, Resp> Caller<T, Params, Resp> for WeakClient<T>
where
T: Transport + Clone,
Params: RpcParam,
Resp: RpcReturn,
{
fn call(
&self,
method: Cow<'static, str>,
params: Params,
) -> TransportResult<ProviderCall<T, serde_json::Value, Resp>> {
let client = self.upgrade().ok_or_else(TransportErrorKind::backend_gone)?;

// serialize the params
let ser = serde_json::to_value(params).map_err(RpcError::ser_err)?;

let rpc_call = client.request(method, ser);

Ok(ProviderCall::RpcCall(rpc_call))
}
}
Loading

0 comments on commit 6369bcc

Please sign in to comment.