feat: add support for `eth_getBlockReceipts` (#365)
* feat: add support for `eth_getBlockReceipts` * Add comment to the `get_block_receipts` function * Add `client_version()` function * Refactor `Provider` into a proper struct
This commit is contained in:
parent
746d8b7bf2
commit
f165c13009
|
@ -182,6 +182,10 @@ pub trait Middleware: Sync + Send + Debug {
|
||||||
self.inner().provider()
|
self.inner().provider()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn client_version(&self) -> Result<String, Self::Error> {
|
||||||
|
self.inner().client_version().await.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_block_number(&self) -> Result<U64, Self::Error> {
|
async fn get_block_number(&self) -> Result<U64, Self::Error> {
|
||||||
self.inner().get_block_number().await.map_err(FromErr::from)
|
self.inner().get_block_number().await.map_err(FromErr::from)
|
||||||
}
|
}
|
||||||
|
@ -289,6 +293,16 @@ pub trait Middleware: Sync + Send + Debug {
|
||||||
.map_err(FromErr::from)
|
.map_err(FromErr::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_block_receipts<T: Into<BlockNumber> + Send + Sync>(
|
||||||
|
&self,
|
||||||
|
block: T,
|
||||||
|
) -> Result<Vec<TransactionReceipt>, Self::Error> {
|
||||||
|
self.inner()
|
||||||
|
.get_block_receipts(block)
|
||||||
|
.await
|
||||||
|
.map_err(FromErr::from)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_gas_price(&self) -> Result<U256, Self::Error> {
|
async fn get_gas_price(&self) -> Result<U256, Self::Error> {
|
||||||
self.inner().get_gas_price().await.map_err(FromErr::from)
|
self.inner().get_gas_price().await.map_err(FromErr::from)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,16 @@ use tracing_futures::Instrument;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
// TODO: Convert to proper struct
|
pub struct Provider<P> {
|
||||||
pub struct Provider<P>(P, Option<Address>, Option<Duration>, Option<Address>);
|
inner: P,
|
||||||
|
ens: Option<Address>,
|
||||||
|
interval: Option<Duration>,
|
||||||
|
from: Option<Address>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<P> AsRef<P> for Provider<P> {
|
impl<P> AsRef<P> for Provider<P> {
|
||||||
fn as_ref(&self) -> &P {
|
fn as_ref(&self) -> &P {
|
||||||
&self.0
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +107,16 @@ pub enum FilterKind<'a> {
|
||||||
impl<P: JsonRpcClient> Provider<P> {
|
impl<P: JsonRpcClient> Provider<P> {
|
||||||
/// Instantiate a new provider with a backend.
|
/// Instantiate a new provider with a backend.
|
||||||
pub fn new(provider: P) -> Self {
|
pub fn new(provider: P) -> Self {
|
||||||
Self(provider, None, None, None)
|
Self {
|
||||||
|
inner: provider,
|
||||||
|
ens: None,
|
||||||
|
interval: None,
|
||||||
|
from: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_sender(mut self, address: impl Into<Address>) -> Self {
|
pub fn with_sender(mut self, address: impl Into<Address>) -> Self {
|
||||||
self.3 = Some(address.into());
|
self.from = Some(address.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +130,11 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
// https://docs.rs/tracing/0.1.22/tracing/span/struct.Span.html#in-asynchronous-code
|
// https://docs.rs/tracing/0.1.22/tracing/span/struct.Span.html#in-asynchronous-code
|
||||||
let res = async move {
|
let res = async move {
|
||||||
trace!("tx");
|
trace!("tx");
|
||||||
let res: R = self.0.request(method, params).await.map_err(Into::into)?;
|
let res: R = self
|
||||||
|
.inner
|
||||||
|
.request(method, params)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)?;
|
||||||
trace!(rx = ?serde_json::to_string(&res)?);
|
trace!(rx = ?serde_json::to_string(&res)?);
|
||||||
Ok::<_, ProviderError>(res)
|
Ok::<_, ProviderError>(res)
|
||||||
}
|
}
|
||||||
|
@ -183,6 +196,11 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
//
|
//
|
||||||
// Functions for querying the state of the blockchain
|
// Functions for querying the state of the blockchain
|
||||||
|
|
||||||
|
/// Returns the current client version using the `web3_clientVersion` RPC.
|
||||||
|
async fn client_version(&self) -> Result<String, Self::Error> {
|
||||||
|
self.request("web3_clientVersion", ()).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the latest block number via the `eth_BlockNumber` API
|
/// Gets the latest block number via the `eth_BlockNumber` API
|
||||||
async fn get_block_number(&self) -> Result<U64, ProviderError> {
|
async fn get_block_number(&self) -> Result<U64, ProviderError> {
|
||||||
self.request("eth_blockNumber", ()).await
|
self.request("eth_blockNumber", ()).await
|
||||||
|
@ -222,6 +240,17 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
self.request("eth_getTransactionReceipt", [hash]).await
|
self.request("eth_getTransactionReceipt", [hash]).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns all receipts for a block.
|
||||||
|
///
|
||||||
|
/// Note that this uses the `eth_getBlockReceipts` RPC, which is
|
||||||
|
/// non-standard and currently supported by Erigon.
|
||||||
|
async fn get_block_receipts<T: Into<BlockNumber> + Send + Sync>(
|
||||||
|
&self,
|
||||||
|
block: T,
|
||||||
|
) -> Result<Vec<TransactionReceipt>, Self::Error> {
|
||||||
|
self.request("eth_getBlockReceipts", [block.into()]).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the current gas price as estimated by the node
|
/// Gets the current gas price as estimated by the node
|
||||||
async fn get_gas_price(&self) -> Result<U256, ProviderError> {
|
async fn get_gas_price(&self) -> Result<U256, ProviderError> {
|
||||||
self.request("eth_gasPrice", ()).await
|
self.request("eth_gasPrice", ()).await
|
||||||
|
@ -301,7 +330,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
_: Option<BlockId>,
|
_: Option<BlockId>,
|
||||||
) -> Result<PendingTransaction<'_, P>, ProviderError> {
|
) -> Result<PendingTransaction<'_, P>, ProviderError> {
|
||||||
if tx.from.is_none() {
|
if tx.from.is_none() {
|
||||||
tx.from = self.3;
|
tx.from = self.from;
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.gas.is_none() {
|
if tx.gas.is_none() {
|
||||||
|
@ -335,7 +364,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
|
||||||
/// The JSON-RPC provider is at the bottom-most position in the middleware stack. Here we check
|
/// The JSON-RPC provider is at the bottom-most position in the middleware stack. Here we check
|
||||||
/// if it has the key for the sender address unlocked, as well as supports the `eth_sign` call.
|
/// if it has the key for the sender address unlocked, as well as supports the `eth_sign` call.
|
||||||
async fn is_signer(&self) -> bool {
|
async fn is_signer(&self) -> bool {
|
||||||
match self.3 {
|
match self.from {
|
||||||
Some(sender) => self.sign(vec![], &sender).await.is_ok(),
|
Some(sender) => self.sign(vec![], &sender).await.is_ok(),
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
|
@ -675,7 +704,7 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
) -> Result<T, ProviderError> {
|
) -> Result<T, ProviderError> {
|
||||||
// Get the ENS address, prioritize the local override variable
|
// Get the ENS address, prioritize the local override variable
|
||||||
let ens_addr = self.1.unwrap_or(ens::ENS_ADDRESS);
|
let ens_addr = self.ens.unwrap_or(ens::ENS_ADDRESS);
|
||||||
|
|
||||||
// first get the resolver responsible for this name
|
// first get the resolver responsible for this name
|
||||||
// the call will return a Bytes array which we convert to an address
|
// the call will return a Bytes array which we convert to an address
|
||||||
|
@ -700,7 +729,7 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
/// ganache-only function for mining empty blocks
|
/// ganache-only function for mining empty blocks
|
||||||
pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
|
pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
|
||||||
for _ in 0..num_blocks {
|
for _ in 0..num_blocks {
|
||||||
self.0
|
self.inner
|
||||||
.request::<_, U256>("evm_mine", None::<()>)
|
.request::<_, U256>("evm_mine", None::<()>)
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)?;
|
.map_err(Into::into)?;
|
||||||
|
@ -710,21 +739,21 @@ impl<P: JsonRpcClient> Provider<P> {
|
||||||
|
|
||||||
/// Sets the ENS Address (default: mainnet)
|
/// Sets the ENS Address (default: mainnet)
|
||||||
pub fn ens<T: Into<Address>>(mut self, ens: T) -> Self {
|
pub fn ens<T: Into<Address>>(mut self, ens: T) -> Self {
|
||||||
self.1 = Some(ens.into());
|
self.ens = Some(ens.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the default polling interval for event filters and pending transactions
|
/// Sets the default polling interval for event filters and pending transactions
|
||||||
/// (default: 7 seconds)
|
/// (default: 7 seconds)
|
||||||
pub fn interval<T: Into<Duration>>(mut self, interval: T) -> Self {
|
pub fn interval<T: Into<Duration>>(mut self, interval: T) -> Self {
|
||||||
self.2 = Some(interval.into());
|
self.interval = Some(interval.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the polling interval which the provider currently uses for event filters
|
/// Gets the polling interval which the provider currently uses for event filters
|
||||||
/// and pending transactions (default: 7 seconds)
|
/// and pending transactions (default: 7 seconds)
|
||||||
pub fn get_interval(&self) -> Duration {
|
pub fn get_interval(&self) -> Duration {
|
||||||
self.2.unwrap_or(DEFAULT_POLL_INTERVAL)
|
self.interval.unwrap_or(DEFAULT_POLL_INTERVAL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,12 +820,7 @@ impl TryFrom<&str> for Provider<HttpProvider> {
|
||||||
type Error = ParseError;
|
type Error = ParseError;
|
||||||
|
|
||||||
fn try_from(src: &str) -> Result<Self, Self::Error> {
|
fn try_from(src: &str) -> Result<Self, Self::Error> {
|
||||||
Ok(Provider(
|
Ok(Provider::new(HttpProvider::new(Url::parse(src)?)))
|
||||||
HttpProvider::new(Url::parse(src)?),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,21 @@ mod eth_tests {
|
||||||
.is_none());
|
.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn client_version() {
|
||||||
|
let provider = Provider::<Http>::try_from(
|
||||||
|
"https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// e.g., Geth/v1.10.6-omnibus-1af33248/linux-amd64/go1.16.6
|
||||||
|
assert!(provider
|
||||||
|
.client_version()
|
||||||
|
.await
|
||||||
|
.expect("Could not make web3_clientVersion call to provider")
|
||||||
|
.starts_with("Geth/v"));
|
||||||
|
}
|
||||||
|
|
||||||
// Without TLS this would error with "TLS Support not compiled in"
|
// Without TLS this would error with "TLS Support not compiled in"
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn ssl_websocket() {
|
async fn ssl_websocket() {
|
||||||
|
|
Loading…
Reference in New Issue