Skip to content

Commit

Permalink
RpcWithBlock (#674)
Browse files Browse the repository at this point in the history
* wip: RpcWithBlock

* wip

* wip

* feat: working maps

* lint: clippy and simplify

* feat: convert get_proof

* feat: convert create_access_list

* feat: convert trace_call

* refactor: finish converting RPC methods

* fix: remove unpin bounds

* nit: comment

* fix: remove clone bounds

* nit: reduce nesting
  • Loading branch information
prestwich authored May 13, 2024
1 parent 7fc77db commit 3f6c744
Show file tree
Hide file tree
Showing 11 changed files with 384 additions and 86 deletions.
2 changes: 1 addition & 1 deletion crates/contract/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ impl<T: Transport + Clone, P: Provider<T, N>, D: CallDecoder, N: Network> CallBu

/// Returns the estimated gas cost for the underlying transaction to be executed
pub async fn estimate_gas(&self) -> Result<u128> {
self.provider.estimate_gas(&self.request, self.block).await.map_err(Into::into)
self.provider.estimate_gas(&self.request).block_id(self.block).await.map_err(Into::into)
}

/// Queries the blockchain via an `eth_call` without submitting a transaction to the network.
Expand Down
1 change: 1 addition & 0 deletions crates/provider/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ 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"] }
Expand Down
14 changes: 8 additions & 6 deletions crates/provider/src/fillers/gas.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::future::IntoFuture;

use crate::{
fillers::{FillerControlFlow, TxFiller},
provider::SendableTx,
Expand Down Expand Up @@ -78,13 +80,13 @@ impl GasFiller {
let gas_price_fut = if let Some(gas_price) = tx.gas_price() {
async move { Ok(gas_price) }.left_future()
} else {
async { provider.get_gas_price().await }.right_future()
provider.get_gas_price().right_future()
};

let gas_limit_fut = if let Some(gas_limit) = tx.gas_limit() {
async move { Ok(gas_limit) }.left_future()
} else {
async { provider.estimate_gas(tx, Default::default()).await }.right_future()
provider.estimate_gas(tx).into_future().right_future()
};

let (gas_price, gas_limit) = futures::try_join!(gas_price_fut, gas_limit_fut)?;
Expand All @@ -105,7 +107,7 @@ impl GasFiller {
let gas_limit_fut = if let Some(gas_limit) = tx.gas_limit() {
async move { Ok(gas_limit) }.left_future()
} else {
async { provider.estimate_gas(tx, Default::default()).await }.right_future()
provider.estimate_gas(tx).into_future().right_future()
};

let eip1559_fees_fut = if let (Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) =
Expand All @@ -114,7 +116,7 @@ impl GasFiller {
async move { Ok(Eip1559Estimation { max_fee_per_gas, max_priority_fee_per_gas }) }
.left_future()
} else {
async { provider.estimate_eip1559_fees(None).await }.right_future()
provider.estimate_eip1559_fees(None).right_future()
};

let (gas_limit, estimate) = futures::try_join!(gas_limit_fut, eip1559_fees_fut)?;
Expand All @@ -135,7 +137,7 @@ impl GasFiller {
let gas_limit_fut = if let Some(gas_limit) = tx.gas_limit() {
async move { Ok(gas_limit) }.left_future()
} else {
async { provider.estimate_gas(tx, Default::default()).await }.right_future()
provider.estimate_gas(tx).into_future().right_future()
};

let eip1559_fees_fut = if let (Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) =
Expand All @@ -144,7 +146,7 @@ impl GasFiller {
async move { Ok(Eip1559Estimation { max_fee_per_gas, max_priority_fee_per_gas }) }
.left_future()
} else {
async { provider.estimate_eip1559_fees(None).await }.right_future()
provider.estimate_eip1559_fees(None).right_future()
};

let max_fee_per_blob_gas_fut = if let Some(max_fee_per_blob_gas) = tx.max_fee_per_blob_gas()
Expand Down
3 changes: 1 addition & 2 deletions crates/provider/src/fillers/nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ impl NonceFiller {
}
None => {
// initialize the nonce if we haven't seen this account before
let initial_nonce =
provider.get_transaction_count(from, Default::default()).await?;
let initial_nonce = provider.get_transaction_count(from).await?;
*nonce = Some(initial_nonce);
Ok(initial_nonce)
}
Expand Down
3 changes: 2 additions & 1 deletion crates/provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ pub use heart::{PendingTransaction, PendingTransactionBuilder, PendingTransactio

mod provider;
pub use provider::{
EthCall, FilterPollerBuilder, Provider, RootProvider, SendableTx, WalletProvider,
EthCall, FilterPollerBuilder, Provider, RootProvider, RpcWithBlock, SendableTx, TraceCallList,
WalletProvider,
};

pub mod utils;
Expand Down
3 changes: 1 addition & 2 deletions crates/provider/src/provider/call.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::{borrow::Cow, future::Future, task::Poll};

use alloy_eips::BlockId;
use alloy_network::Network;
use alloy_primitives::Bytes;
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};

/// States for the [`EthCallFut`] future.
#[derive(Debug, Clone)]
Expand Down
5 changes: 4 additions & 1 deletion crates/provider/src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ mod sendable;
pub use sendable::SendableTx;

mod r#trait;
pub use r#trait::{FilterPollerBuilder, Provider};
pub use r#trait::{FilterPollerBuilder, Provider, TraceCallList};

mod wallet;
pub use wallet::WalletProvider;

mod with_block;
pub use with_block::RpcWithBlock;
104 changes: 47 additions & 57 deletions crates/provider/src/provider/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
utils::{self, Eip1559Estimation, EstimatorFunction},
EthCall, PendingTransaction, PendingTransactionBuilder, PendingTransactionConfig, RootProvider,
SendableTx,
RpcWithBlock, SendableTx,
};
use alloy_eips::eip2718::Encodable2718;
use alloy_json_rpc::{RpcError, RpcParam, RpcReturn};
Expand All @@ -12,7 +12,7 @@ use alloy_primitives::{
hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U128,
U256, U64,
};
use alloy_rpc_client::{ClientRef, PollerBuilder, WeakClient};
use alloy_rpc_client::{ClientRef, PollerBuilder, RpcCall, WeakClient};
use alloy_rpc_types::{
AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, EIP1186AccountProofResponse,
FeeHistory, Filter, FilterChanges, Log, SyncStatus,
Expand All @@ -27,6 +27,9 @@ use std::borrow::Cow;
/// See [`PollerBuilder`] for more details.
pub type FilterPollerBuilder<T, R> = PollerBuilder<T, (U256,), Vec<R>>;

/// List of trace calls for use with [`Provider::trace_call_many`]
pub type TraceCallList<'a, N> = &'a [(<N as Network>::TransactionRequest, Vec<TraceType>)];

// todo: adjust docs
// todo: reorder
/// Provider is parameterized with a network and a transport. The default
Expand Down Expand Up @@ -419,18 +422,16 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
}

/// Get the last block number available.
async fn get_block_number(&self) -> TransportResult<BlockNumber> {
self.client().request("eth_blockNumber", ()).await.map(|num: U64| num.to::<u64>())
fn get_block_number(&self) -> RpcCall<T, (), U64, BlockNumber> {
self.client().request("eth_blockNumber", ()).map_resp(crate::utils::convert_u64)
}

/// Gets the transaction count (AKA "nonce") of the corresponding address.
#[doc(alias = "get_nonce")]
#[doc(alias = "get_account_nonce")]
async fn get_transaction_count(&self, address: Address, tag: BlockId) -> TransportResult<u64> {
self.client()
.request("eth_getTransactionCount", (address, tag))
.await
.map(|count: U64| count.to::<u64>())
fn get_transaction_count(&self, address: Address) -> RpcWithBlock<T, Address, U64, u64> {
RpcWithBlock::new(self.weak_client(), "eth_getTransactionCount", address)
.map_resp(crate::utils::convert_u64)
}

/// Get a block by its number.
Expand Down Expand Up @@ -510,8 +511,8 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
}

/// Gets the balance of the account at the specified tag, which defaults to latest.
async fn get_balance(&self, address: Address, tag: BlockId) -> TransportResult<U256> {
self.client().request("eth_getBalance", (address, tag)).await
fn get_balance(&self, address: Address) -> RpcWithBlock<T, Address, U256> {
RpcWithBlock::new(self.weak_client(), "eth_getBalance", address)
}

/// Gets a block by either its hash, tag, or number, with full transactions or only hashes.
Expand All @@ -537,28 +538,27 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
}

/// Gets the chain ID.
async fn get_chain_id(&self) -> TransportResult<u64> {
self.client().request("eth_chainId", ()).await.map(|id: U64| id.to::<u64>())
fn get_chain_id(&self) -> RpcCall<T, (), U64, u64> {
self.client().request("eth_chainId", ()).map_resp(crate::utils::convert_u64)
}

/// Gets the network ID. Same as `eth_chainId`.
async fn get_net_version(&self) -> TransportResult<u64> {
self.client().request("net_version", ()).await.map(|id: U64| id.to::<u64>())
fn get_net_version(&self) -> RpcCall<T, (), U64, u64> {
self.client().request("net_version", ()).map_resp(crate::utils::convert_u64)
}

/// Gets the specified storage value from [Address].
async fn get_storage_at(
fn get_storage_at(
&self,
address: Address,
key: U256,
tag: BlockId,
) -> TransportResult<StorageValue> {
self.client().request("eth_getStorageAt", (address, key, tag)).await
) -> RpcWithBlock<T, (Address, U256), StorageValue> {
RpcWithBlock::new(self.weak_client(), "eth_getStorageAt", (address, key))
}

/// Gets the bytecode located at the corresponding [Address].
async fn get_code_at(&self, address: Address, tag: BlockId) -> TransportResult<Bytes> {
self.client().request("eth_getCode", (address, tag)).await
fn get_code_at(&self, address: Address) -> RpcWithBlock<T, Address, Bytes> {
RpcWithBlock::new(self.weak_client(), "eth_getCode", address)
}

/// Gets a transaction by its [TxHash].
Expand All @@ -581,8 +581,8 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
}

/// Gets the current gas price in wei.
async fn get_gas_price(&self) -> TransportResult<u128> {
self.client().request("eth_gasPrice", ()).await.map(|price: U128| price.to::<u128>())
fn get_gas_price(&self) -> RpcCall<T, (), U128, u128> {
self.client().request("eth_gasPrice", ()).map_resp(crate::utils::convert_u128)
}

/// Returns a suggestion for the current `maxPriorityFeePerGas` in wei.
Expand Down Expand Up @@ -706,15 +706,12 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
}

/// Estimate the gas needed for a transaction.
async fn estimate_gas(
fn estimate_gas<'a>(
&self,
tx: &N::TransactionRequest,
block: BlockId,
) -> TransportResult<u128> {
self.client()
.request("eth_estimateGas", (tx, block))
.await
.map(|gas: U128| gas.to::<u128>())
tx: &'a N::TransactionRequest,
) -> RpcWithBlock<T, &'a N::TransactionRequest, U128, u128> {
RpcWithBlock::new(self.weak_client(), "eth_estimateGas", tx)
.map_resp(crate::utils::convert_u128)
}

/// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields.
Expand Down Expand Up @@ -757,38 +754,35 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
/// Get the account and storage values of the specified account including the merkle proofs.
///
/// This call can be used to verify that the data has not been tampered with.
async fn get_proof(
fn get_proof(
&self,
address: Address,
keys: Vec<StorageKey>,
block: BlockId,
) -> TransportResult<EIP1186AccountProofResponse> {
self.client().request("eth_getProof", (address, keys, block)).await
) -> RpcWithBlock<T, (Address, Vec<StorageKey>), EIP1186AccountProofResponse> {
RpcWithBlock::new(self.weak_client(), "eth_getProof", (address, keys))
}

/// Create an [EIP-2930] access list.
///
/// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930
async fn create_access_list(
fn create_access_list<'a>(
&self,
request: &N::TransactionRequest,
block: BlockId,
) -> TransportResult<AccessListWithGasUsed> {
self.client().request("eth_createAccessList", (request, block)).await
request: &'a N::TransactionRequest,
) -> RpcWithBlock<T, &'a N::TransactionRequest, AccessListWithGasUsed> {
RpcWithBlock::new(self.weak_client(), "eth_createAccessList", request)
}

/// Executes the given transaction and returns a number of possible traces.
///
/// # Note
///
/// Not all nodes support this call.
async fn trace_call(
fn trace_call<'a, 'b>(
&self,
request: &N::TransactionRequest,
trace_type: &[TraceType],
block: BlockId,
) -> TransportResult<TraceResults> {
self.client().request("trace_call", (request, trace_type, block)).await
request: &'a N::TransactionRequest,
trace_type: &'b [TraceType],
) -> RpcWithBlock<T, (&'a N::TransactionRequest, &'b [TraceType]), TraceResults> {
RpcWithBlock::new(self.weak_client(), "trace_call", (request, trace_type))
}

/// Traces multiple transactions on top of the same block, i.e. transaction `n` will be executed
Expand All @@ -799,12 +793,11 @@ pub trait Provider<T: Transport + Clone = BoxTransport, N: Network = Ethereum>:
/// # Note
///
/// Not all nodes support this call.
async fn trace_call_many(
fn trace_call_many<'a>(
&self,
request: &[(N::TransactionRequest, Vec<TraceType>)],
block: BlockId,
) -> TransportResult<TraceResults> {
self.client().request("trace_callMany", (request, block)).await
request: TraceCallList<'a, N>,
) -> RpcWithBlock<T, TraceCallList<'a, N>, TraceResults> {
RpcWithBlock::new(self.weak_client(), "trace_callMany", request)
}

// todo: move to extension trait
Expand Down Expand Up @@ -1092,10 +1085,7 @@ mod tests {
init_tracing();
let provider = ProviderBuilder::new().on_anvil();
let count = provider
.get_transaction_count(
address!("328375e18E7db8F1CA9d9bA8bF3E9C94ee34136A"),
BlockNumberOrTag::Latest.into(),
)
.get_transaction_count(address!("328375e18E7db8F1CA9d9bA8bF3E9C94ee34136A"))
.await
.unwrap();
assert_eq!(count, 0);
Expand Down Expand Up @@ -1183,15 +1173,15 @@ mod tests {
// Set the code
let addr = Address::with_last_byte(16);
provider.set_code(addr, "0xbeef").await.unwrap();
let _code = provider.get_code_at(addr, BlockId::default()).await.unwrap();
let _code = provider.get_code_at(addr).await.unwrap();
}

#[tokio::test]
async fn gets_storage_at() {
init_tracing();
let provider = ProviderBuilder::new().on_anvil();
let addr = Address::with_last_byte(16);
let storage = provider.get_storage_at(addr, U256::ZERO, BlockId::default()).await.unwrap();
let storage = provider.get_storage_at(addr, U256::ZERO).await.unwrap();
assert_eq!(storage, U256::ZERO);
}

Expand Down
Loading

0 comments on commit 3f6c744

Please sign in to comment.