From 5610541430d58b0d7cf7895d7772ee47a35e3346 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 24 May 2024 20:22:54 +0200 Subject: [PATCH] fix: correctly serialize eth_call params --- crates/contract/src/eth_call.rs | 4 +- crates/provider/Cargo.toml | 6 ++- crates/provider/src/provider/call.rs | 60 ++++++++++++++++++--------- crates/provider/src/provider/trait.rs | 19 +++++++++ 4 files changed, 65 insertions(+), 24 deletions(-) diff --git a/crates/contract/src/eth_call.rs b/crates/contract/src/eth_call.rs index e593ff16dda..63776d98a79 100644 --- a/crates/contract/src/eth_call.rs +++ b/crates/contract/src/eth_call.rs @@ -22,7 +22,7 @@ mod private { /// An [`alloy_provider::EthCall`] with an abi decoder. #[must_use = "EthCall must be awaited to execute the call"] -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct EthCall<'req, 'state, 'coder, D, T, N> where T: Transport + Clone, @@ -120,7 +120,7 @@ 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(Debug, Clone)] +#[derive(Clone, Debug)] pub struct EthCallFut<'req, 'state, 'coder, D, T, N> where T: Transport + Clone, diff --git a/crates/provider/Cargo.toml b/crates/provider/Cargo.toml index a360bdf97a8..d1ac80f94e8 100644 --- a/crates/provider/Cargo.toml +++ b/crates/provider/Cargo.toml @@ -33,21 +33,23 @@ async-stream = "0.3" async-trait.workspace = true auto_impl.workspace = true dashmap = "5.5" +futures-utils-wasm.workspace = true futures.workspace = true lru = "0.12" +pin-project.workspace = true reqwest = { workspace = true, optional = true } serde_json.workspace = true +serde.workspace = true tokio = { workspace = true, features = ["sync", "macros"] } tracing.workspace = true url = { workspace = true, optional = true } -futures-utils-wasm.workspace = true -pin-project.workspace = true [dev-dependencies] alloy-consensus = { workspace = true, features = ["std"] } alloy-node-bindings.workspace = true alloy-rpc-client = { workspace = true, features = ["reqwest"] } alloy-rlp.workspace = true +alloy-sol-types.workspace = true alloy-signer.workspace = true alloy-signer-wallet.workspace = true alloy-transport-http = { workspace = true, features = ["reqwest"] } diff --git a/crates/provider/src/provider/call.rs b/crates/provider/src/provider/call.rs index 5b25a3ba1a6..134b4fb5455 100644 --- a/crates/provider/src/provider/call.rs +++ b/crates/provider/src/provider/call.rs @@ -5,21 +5,41 @@ use alloy_rpc_client::{RpcCall, WeakClient}; use alloy_rpc_types::state::StateOverride; use alloy_transport::{Transport, TransportErrorKind, TransportResult}; use futures::FutureExt; -use std::{ - borrow::Cow, - future::Future, - task::Poll::{self, Ready}, -}; - -type RunningFut<'req, 'state, T, N> = RpcCall< - T, - (&'req ::TransactionRequest, BlockId, Option>), - Bytes, ->; +use serde::ser::SerializeSeq; +use std::{future::Future, task::Poll}; + +type RunningFut<'req, 'state, T, N> = RpcCall, Bytes>; + +#[derive(Clone, Debug)] +struct EthCallParams<'req, 'state, N: Network> { + data: &'req N::TransactionRequest, + block: BlockId, + overrides: Option<&'state StateOverride>, +} + +impl serde::Serialize for EthCallParams<'_, '_, N> { + fn serialize(&self, serializer: S) -> Result { + let len = if self.overrides.is_some() { 3 } else { 2 }; + let mut seq = serializer.serialize_seq(Some(len))?; + seq.serialize_element(&self.data)?; + seq.serialize_element(&self.block)?; + if let Some(overrides) = self.overrides { + seq.serialize_element(overrides)?; + } + seq.end() + } +} /// The [`EthCallFut`] future is the future type for an `eth_call` RPC request. -#[derive(Debug, Clone)] -pub enum EthCallFut<'req, 'state, T, N> +#[derive(Clone, Debug)] +#[doc(hidden)] // Not public API. +pub struct EthCallFut<'req, 'state, T, N>(EthCallFutInner<'req, 'state, T, N>) +where + T: Transport + Clone, + N: Network; + +#[derive(Clone, Debug)] +enum EthCallFutInner<'req, 'state, T, N: Network> where T: Transport + Clone, N: Network, @@ -34,7 +54,7 @@ where Polling, } -impl<'req, 'state, T, N> EthCallFut<'req, 'state, T, N> +impl<'req, 'state, T, N> EthCallFutInner<'req, 'state, T, N> where T: Transport + Clone, N: Network, @@ -58,12 +78,12 @@ where let client = match client.upgrade().ok_or_else(TransportErrorKind::backend_gone) { Ok(client) => client, - Err(e) => return Ready(Err(e)), + Err(e) => return Poll::Ready(Err(e)), }; - let overrides = overrides.map(Cow::Borrowed); + let params = EthCallParams { data, block: block.unwrap_or_default(), overrides }; - let fut = client.request("eth_call", (data, block.unwrap_or_default(), overrides)); + let fut = client.request("eth_call", params); *self = Self::Running(fut); self.poll_running(cx) @@ -87,7 +107,7 @@ where self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - let this = self.get_mut(); + let this = &mut self.get_mut().0; if this.is_preparing() { this.poll_preparing(cx) } else if this.is_running() { @@ -155,11 +175,11 @@ where type IntoFuture = EthCallFut<'req, 'state, T, N>; fn into_future(self) -> Self::IntoFuture { - EthCallFut::Preparing { + EthCallFut(EthCallFutInner::Preparing { client: self.client, data: self.data, overrides: self.overrides, block: self.block, - } + }) } } diff --git a/crates/provider/src/provider/trait.rs b/crates/provider/src/provider/trait.rs index 5e7df26afdb..650e9a12bed 100644 --- a/crates/provider/src/provider/trait.rs +++ b/crates/provider/src/provider/trait.rs @@ -922,9 +922,11 @@ impl Provider for RootProvider { mod tests { use super::*; use crate::{ProviderBuilder, WalletProvider}; + use alloy_network::TransactionBuilder; use alloy_node_bindings::Anvil; use alloy_primitives::{address, b256, bytes}; use alloy_rpc_types::request::TransactionRequest; + use alloy_sol_types::SolValue; fn init_tracing() { let _ = tracing_subscriber::fmt::try_init(); @@ -1338,4 +1340,21 @@ mod tests { let count = provider.get_uncle_count(0.into()).await.unwrap(); assert_eq!(count, 0); } + + #[tokio::test] + #[cfg(any( + feature = "reqwest-default-tls", + feature = "reqwest-rustls-tls", + feature = "reqwest-native-tls", + ))] + async fn call_mainnet() { + init_tracing(); + let url = "https://eth-mainnet.alchemyapi.io/v2/jGiK5vwDfC3F4r0bqukm-W2GqgdrxdSr"; + let provider = ProviderBuilder::new().on_http(url.parse().unwrap()); + let req = TransactionRequest::default() + .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")) // WETH + .with_input(bytes!("06fdde03")); // `name()` + let result = provider.call(&req).await.unwrap(); + assert_eq!(String::abi_decode(&result, true).unwrap(), "Wrapped Ether"); + } }