From 680100d23c89fcbf1885dc8192c31785ef4ecdc7 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Sun, 5 Nov 2023 22:11:00 +0100 Subject: [PATCH 01/37] wip --- crates/providers/Cargo.toml | 7 +- crates/providers/src/provider.rs | 353 +++++++++++++++++++++++-------- 2 files changed, 272 insertions(+), 88 deletions(-) diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml index 1fed9b3f1fb..bd3fc3b2d82 100644 --- a/crates/providers/Cargo.toml +++ b/crates/providers/Cargo.toml @@ -18,11 +18,14 @@ alloy-rpc-client.workspace = true alloy-rpc-types.workspace = true alloy-transport-http.workspace = true alloy-transport.workspace = true - async-trait.workspace = true -reqwest.workspace = true serde.workspace = true thiserror.workspace = true +futures-util = "0.3.28" +serde_json = { workspace = true, features = ["raw_value"] } +once_cell = "1.17" +serial_test = "2.0.0" +auto_impl = "1.1.0" [dev-dependencies] tokio = { version = "1.33.0", features = ["macros"] } diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index aac3c9a01e5..084384fc6d5 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -1,15 +1,15 @@ //! Alloy main Provider abstraction. use crate::utils::{self, EstimatorFunction}; -use alloy_primitives::{Address, BlockHash, Bytes, TxHash, U256, U64}; -use alloy_rpc_client::{ClientBuilder, RpcClient}; +use alloy_rpc_client::{ RpcClient}; +use alloy_primitives::{Address, BlockHash, Bytes, StorageKey, StorageValue, TxHash, U256, U64}; use alloy_rpc_types::{ Block, BlockId, BlockNumberOrTag, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, Transaction, TransactionReceipt, TransactionRequest, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; -use alloy_transport_http::Http; -use reqwest::Client; +use async_trait::async_trait; +use auto_impl::auto_impl; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use thiserror::Error; @@ -31,29 +31,231 @@ pub struct Provider { from: Option
, } +// todo: docs explaining that this is patchwork +#[async_trait] +#[auto_impl(&, Arc, Box)] +pub trait TempProvider: Send + Sync { + /// Gets the transaction count of the corresponding address. + async fn get_transaction_count( + &self, + address: Address, + tag: Option, + ) -> TransportResult + where + Self: Sync; + + /// Gets the last block number available. + async fn get_block_number(&self) -> TransportResult + where + Self: Sync; + + /// Gets the balance of the account at the specified tag, which defaults to latest. + async fn get_balance( + &self, + address: Address, + tag: Option, + ) -> TransportResult + where + Self: Sync; + + /// Gets a block by its [BlockHash], with full transactions or only hashes. + async fn get_block_by_hash( + &self, + hash: BlockHash, + full: bool, + ) -> TransportResult> + where + Self: Sync; + + /// Gets a block by [BlockNumberOrTag], with full transactions or only hashes. + async fn get_block_by_number + Send + Sync>( + &self, + number: B, + full: bool, + ) -> TransportResult> + where + Self: Sync; + + /// Gets the chain ID. + async fn get_chain_id(&self) -> TransportResult + where + Self: Sync; + + /// Gets the specified storage value from [Address]. + async fn get_storage_at( + &self, + address: Address, + key: StorageKey, + tag: Option, + ) -> TransportResult; + + /// Gets the bytecode located at the corresponding [Address]. + async fn get_code_at + Send + Sync>( + &self, + address: Address, + tag: B, + ) -> TransportResult + where + Self: Sync; + + /// Gets a [Transaction] by its [TxHash]. + async fn get_transaction_by_hash( + &self, + hash: TxHash, + ) -> TransportResult + where + Self: Sync; + + /// Retrieves a [`Vec`] with the given [Filter]. + async fn get_logs(&self, filter: Filter) -> TransportResult> + where + Self: Sync; + + /// Gets the accounts in the remote node. This is usually empty unless you're using a local node. + async fn get_accounts(&self) -> TransportResult> + where + Self: Sync; + + /// Gets the current gas price. + async fn get_gas_price(&self) -> TransportResult + where + Self: Sync; + + /// Gets a [TransactionReceipt] if it exists, by its [TxHash]. + async fn get_transaction_receipt( + &self, + hash: TxHash, + ) -> TransportResult> + where + Self: Sync; + + /// Returns a collection of historical gas information [FeeHistory] which + /// can be used to calculate the EIP1559 fields `maxFeePerGas` and `maxPriorityFeePerGas`. + async fn get_fee_history + Send + Sync>( + &self, + block_count: U256, + last_block: B, + reward_percentiles: &[f64], + ) -> TransportResult + where + Self: Sync; + + /// Gets the selected block [BlockNumberOrTag] receipts. + async fn get_block_receipts( + &self, + block: BlockNumberOrTag, + ) -> TransportResult> + where + Self: Sync; + + /// Gets an uncle block through the tag [BlockId] and index [U64]. + async fn get_uncle + Send + Sync>( + &self, + tag: B, + idx: U64, + ) -> TransportResult> + where + Self: Sync; + + /// Gets syncing info. + async fn syncing(&self) -> TransportResult + where + Self: Sync; + + /// Execute a smart contract call with [TransactionRequest] without publishing a transaction. + async fn call( + &self, + tx: TransactionRequest, + block: Option, + ) -> TransportResult + where + Self: Sync; + + /// Estimate the gas needed for a transaction. + async fn estimate_gas( + &self, + tx: TransactionRequest, + block: Option, + ) -> TransportResult + where + Self: Sync; + + /// Sends an already-signed transaction. + async fn send_raw_transaction( + &self, + tx: Bytes, + ) -> TransportResult + where + Self: Sync; + + /// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields. + /// Receives an optional [EstimatorFunction] that can be used to modify + /// how to estimate these fees. + async fn estimate_eip1559_fees( + &self, + estimator: Option, + ) -> TransportResult<(U256, U256)> + where + Self: Sync; + + #[cfg(feature = "anvil")] + async fn set_code( + &self, + address: Address, + code: &'static str, + ) -> TransportResult<()> + where + Self: Sync; +} + +impl Provider { + pub fn new(transport: T) -> Self { + Self { + // todo(onbjerg): do we just default to false + inner: RpcClient::new(transport, false), + from: None, + } + } + + pub fn with_sender(mut self, from: Address) -> Self { + self.from = Some(from); + self + } + + pub fn inner(&self) -> &RpcClient { + &self.inner + } +} + +// todo: validate usage of BlockId vs BlockNumberOrTag vs Option etc. // Simple JSON-RPC bindings. // In the future, this will be replaced by a Provider trait, // but as the interface is not stable yet, we define the bindings ourselves // until we can use the trait and the client abstraction that will use it. -impl Provider { +#[async_trait] +impl TempProvider for Provider { /// Gets the transaction count of the corresponding address. - pub async fn get_transaction_count( + async fn get_transaction_count( &self, address: Address, - ) -> TransportResult + tag: Option, + ) -> TransportResult where Self: Sync, { self.inner .prepare( "eth_getTransactionCount", - Cow::<(Address, &'static str)>::Owned((address, "latest")), + Cow::<(Address, BlockId)>::Owned(( + address, + tag.unwrap_or(BlockNumberOrTag::Latest.into()), + )), ) .await } /// Gets the last block number available. - pub async fn get_block_number(&self) -> TransportResult + async fn get_block_number(&self) -> TransportResult where Self: Sync, { @@ -61,7 +263,7 @@ impl Provider { } /// Gets the balance of the account at the specified tag, which defaults to latest. - pub async fn get_balance(&self, address: Address, tag: Option) -> TransportResult + async fn get_balance(&self, address: Address, tag: Option) -> TransportResult where Self: Sync, { @@ -77,7 +279,7 @@ impl Provider { } /// Gets a block by its [BlockHash], with full transactions or only hashes. - pub async fn get_block_by_hash( + async fn get_block_by_hash( &self, hash: BlockHash, full: bool, @@ -91,7 +293,7 @@ impl Provider { } /// Gets a block by [BlockNumberOrTag], with full transactions or only hashes. - pub async fn get_block_by_number + Send + Sync>( + async fn get_block_by_number + Send + Sync>( &self, number: B, full: bool, @@ -108,14 +310,34 @@ impl Provider { } /// Gets the chain ID. - pub async fn get_chain_id(&self) -> TransportResult + async fn get_chain_id(&self) -> TransportResult where Self: Sync, { self.inner.prepare("eth_chainId", Cow::<()>::Owned(())).await } + + /// Gets the specified storage value from [Address]. + async fn get_storage_at( + &self, + address: Address, + key: StorageKey, + tag: Option, + ) -> TransportResult { + self.inner + .prepare( + "eth_getStorageAt", + Cow::<(Address, StorageKey, BlockId)>::Owned(( + address, + key, + tag.unwrap_or(BlockNumberOrTag::Latest.into()), + )), + ) + .await + } + /// Gets the bytecode located at the corresponding [Address]. - pub async fn get_code_at + Send + Sync>( + async fn get_code_at + Send + Sync>( &self, address: Address, tag: B, @@ -129,7 +351,7 @@ impl Provider { } /// Gets a [Transaction] by its [TxHash]. - pub async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult + async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult where Self: Sync, { @@ -144,7 +366,7 @@ impl Provider { } /// Retrieves a [`Vec`] with the given [Filter]. - pub async fn get_logs(&self, filter: Filter) -> TransportResult> + async fn get_logs(&self, filter: Filter) -> TransportResult> where Self: Sync, { @@ -153,7 +375,7 @@ impl Provider { /// Gets the accounts in the remote node. This is usually empty unless you're using a local /// node. - pub async fn get_accounts(&self) -> TransportResult> + async fn get_accounts(&self) -> TransportResult> where Self: Sync, { @@ -161,7 +383,7 @@ impl Provider { } /// Gets the current gas price. - pub async fn get_gas_price(&self) -> TransportResult + async fn get_gas_price(&self) -> TransportResult where Self: Sync, { @@ -169,7 +391,7 @@ impl Provider { } /// Gets a [TransactionReceipt] if it exists, by its [TxHash]. - pub async fn get_transaction_receipt( + async fn get_transaction_receipt( &self, hash: TxHash, ) -> TransportResult> @@ -181,7 +403,7 @@ impl Provider { /// Returns a collection of historical gas information [FeeHistory] which /// can be used to calculate the EIP1559 fields `maxFeePerGas` and `maxPriorityFeePerGas`. - pub async fn get_fee_history + Send + Sync>( + async fn get_fee_history + Send + Sync>( &self, block_count: U256, last_block: B, @@ -203,7 +425,7 @@ impl Provider { } /// Gets the selected block [BlockNumberOrTag] receipts. - pub async fn get_block_receipts( + async fn get_block_receipts( &self, block: BlockNumberOrTag, ) -> TransportResult> @@ -214,7 +436,7 @@ impl Provider { } /// Gets an uncle block through the tag [BlockId] and index [U64]. - pub async fn get_uncle + Send + Sync>( + async fn get_uncle + Send + Sync>( &self, tag: B, idx: U64, @@ -244,7 +466,7 @@ impl Provider { } /// Gets syncing info. - pub async fn syncing(&self) -> TransportResult + async fn syncing(&self) -> TransportResult where Self: Sync, { @@ -252,7 +474,7 @@ impl Provider { } /// Execute a smart contract call with [TransactionRequest] without publishing a transaction. - pub async fn call( + async fn call( &self, tx: TransactionRequest, block: Option, @@ -272,7 +494,7 @@ impl Provider { } /// Estimate the gas needed for a transaction. - pub async fn estimate_gas( + async fn estimate_gas( &self, tx: TransactionRequest, block: Option, @@ -290,7 +512,7 @@ impl Provider { } /// Sends an already-signed transaction. - pub async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult + async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult where Self: Sync, { @@ -300,7 +522,7 @@ impl Provider { /// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields. /// Receives an optional [EstimatorFunction] that can be used to modify /// how to estimate these fees. - pub async fn estimate_eip1559_fees( + async fn estimate_eip1559_fees( &self, estimator: Option, ) -> TransportResult<(U256, U256)> @@ -353,63 +575,19 @@ impl Provider { .prepare("anvil_setCode", Cow::<(Address, &'static str)>::Owned((address, code))) .await } - - pub fn with_sender(mut self, from: Address) -> Self { - self.from = Some(from); - self - } - - pub fn inner(&self) -> &RpcClient { - &self.inner - } -} - -// HTTP Transport Provider implementation -impl Provider> { - pub fn new(url: &str) -> Result { - let url = url.parse().map_err(|_e| ClientError::ParseError)?; - let inner = ClientBuilder::default().reqwest_http(url); - - Ok(Self { inner, from: None }) - } -} - -impl TryFrom<&str> for Provider> { - type Error = ClientError; - - fn try_from(value: &str) -> Result { - Provider::new(value) - } -} - -impl TryFrom for Provider> { - type Error = ClientError; - - fn try_from(value: String) -> Result { - Provider::try_from(value.as_str()) - } -} - -impl<'a> TryFrom<&'a String> for Provider> { - type Error = ClientError; - - fn try_from(value: &'a String) -> Result { - Provider::try_from(value.as_str()) - } } #[cfg(test)] mod providers_test { - use crate::{provider::Provider, utils}; - use alloy_primitives::{address, b256, U256, U64}; - use alloy_rpc_types::{BlockNumberOrTag, Filter}; - + use crate::{provider::{TempProvider, Provider}, utils}; + use alloy_primitives::{address, b256, Address, U256, U64}; + use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter}; use ethers_core::utils::Anvil; #[tokio::test] async fn gets_block_number() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let num = provider.get_block_number().await.unwrap(); assert_eq!(U64::ZERO, num) } @@ -417,9 +595,12 @@ mod providers_test { #[tokio::test] async fn gets_transaction_count() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let count = provider - .get_transaction_count(address!("328375e18E7db8F1CA9d9bA8bF3E9C94ee34136A")) + .get_transaction_count( + address!("328375e18E7db8F1CA9d9bA8bF3E9C94ee34136A"), + Some(BlockNumberOrTag::Latest), + ) .await .unwrap(); assert_eq!(count, U256::from(0)); @@ -428,7 +609,7 @@ mod providers_test { #[tokio::test] async fn gets_block_by_hash() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let num = 0; let tag: BlockNumberOrTag = num.into(); let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap(); @@ -440,7 +621,7 @@ mod providers_test { #[tokio::test] async fn gets_block_by_number_full() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let num = 0; let tag: BlockNumberOrTag = num.into(); let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap(); @@ -450,7 +631,7 @@ mod providers_test { #[tokio::test] async fn gets_block_by_number() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let num = 0; let tag: BlockNumberOrTag = num.into(); let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap(); @@ -460,7 +641,7 @@ mod providers_test { #[tokio::test] async fn gets_chain_id() { let anvil = Anvil::new().args(vec!["--chain-id", "13371337"]).spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let chain_id = provider.get_chain_id().await.unwrap(); assert_eq!(chain_id, U64::from(13371337)); } @@ -469,7 +650,7 @@ mod providers_test { #[cfg(feature = "anvil")] async fn gets_code_at() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); // Set the code let addr = alloy_primitives::Address::with_last_byte(16); provider.set_code(addr, "0xbeef").await.unwrap(); @@ -486,7 +667,7 @@ mod providers_test { #[ignore] async fn gets_transaction_by_hash() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let tx = provider .get_transaction_by_hash(b256!( "5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95" @@ -504,7 +685,7 @@ mod providers_test { #[ignore] async fn gets_logs() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let filter = Filter::new() .at_block_hash(b256!( "b20e6f35d4b46b3c4cd72152faec7143da851a0dc281d390bdd50f58bfbdb5d3" @@ -520,7 +701,7 @@ mod providers_test { #[ignore] async fn gets_tx_receipt() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let receipt = provider .get_transaction_receipt(b256!( "5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95" @@ -538,7 +719,7 @@ mod providers_test { #[tokio::test] async fn gets_fee_history() { let anvil = Anvil::new().spawn(); - let provider = Provider::new(&anvil.endpoint()).unwrap(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let block_number = provider.get_block_number().await.unwrap(); let fee_history = provider .get_fee_history( From 1abecfd3c5553f596cb1118128a51ac160f12368 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Mon, 6 Nov 2023 10:33:45 -0400 Subject: [PATCH 02/37] chore: docs --- crates/providers/src/provider.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 084384fc6d5..6ad80951fad 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -31,7 +31,9 @@ pub struct Provider { from: Option
, } -// todo: docs explaining that this is patchwork +/// Temporary Provider trait to be used until the new Provider trait with +/// the Network abstraction is stable. +/// Once the new Provider trait is stable, this trait will be removed. #[async_trait] #[auto_impl(&, Arc, Box)] pub trait TempProvider: Send + Sync { From 7fd528dfa732b9bc1a36ce276364de7bb88b9127 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Mon, 13 Nov 2023 02:11:43 +0100 Subject: [PATCH 03/37] feat: add `get_block` --- crates/providers/src/provider.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 6ad80951fad..2cf615b0494 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -60,6 +60,18 @@ pub trait TempProvider: Send + Sync { where Self: Sync; + /// Gets a block by either its hash, tag, or number, with full transactions or only hashes. + async fn get_block( + &self, + id: BlockId, + full: bool, + ) -> RpcResult, Box, TransportError> { + match id { + BlockId::Hash(hash) => self.get_block_by_hash(hash.into(), full).await, + BlockId::Number(number) => self.get_block_by_number(number, full).await, + } + } + /// Gets a block by its [BlockHash], with full transactions or only hashes. async fn get_block_by_hash( &self, @@ -601,7 +613,7 @@ mod providers_test { let count = provider .get_transaction_count( address!("328375e18E7db8F1CA9d9bA8bF3E9C94ee34136A"), - Some(BlockNumberOrTag::Latest), + Some(BlockNumberOrTag::Latest.into()), ) .await .unwrap(); From 95159614b693d17e3795cf7188920171a06586cf Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:25:05 +0100 Subject: [PATCH 04/37] fmt --- crates/providers/src/provider.rs | 68 +++++++++++--------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 2cf615b0494..f9067541bc2 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -1,8 +1,8 @@ //! Alloy main Provider abstraction. use crate::utils::{self, EstimatorFunction}; -use alloy_rpc_client::{ RpcClient}; use alloy_primitives::{Address, BlockHash, Bytes, StorageKey, StorageValue, TxHash, U256, U64}; +use alloy_rpc_client::RpcClient; use alloy_rpc_types::{ Block, BlockId, BlockNumberOrTag, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, Transaction, TransactionReceipt, TransactionRequest, @@ -52,11 +52,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Gets the balance of the account at the specified tag, which defaults to latest. - async fn get_balance( - &self, - address: Address, - tag: Option, - ) -> TransportResult + async fn get_balance(&self, address: Address, tag: Option) -> TransportResult where Self: Sync; @@ -113,10 +109,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Gets a [Transaction] by its [TxHash]. - async fn get_transaction_by_hash( - &self, - hash: TxHash, - ) -> TransportResult + async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult where Self: Sync; @@ -125,7 +118,8 @@ pub trait TempProvider: Send + Sync { where Self: Sync; - /// Gets the accounts in the remote node. This is usually empty unless you're using a local node. + /// Gets the accounts in the remote node. This is usually empty unless you're using a local + /// node. async fn get_accounts(&self) -> TransportResult> where Self: Sync; @@ -177,11 +171,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Execute a smart contract call with [TransactionRequest] without publishing a transaction. - async fn call( - &self, - tx: TransactionRequest, - block: Option, - ) -> TransportResult + async fn call(&self, tx: TransactionRequest, block: Option) -> TransportResult where Self: Sync; @@ -195,10 +185,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Sends an already-signed transaction. - async fn send_raw_transaction( - &self, - tx: Bytes, - ) -> TransportResult + async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult where Self: Sync; @@ -213,11 +200,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; #[cfg(feature = "anvil")] - async fn set_code( - &self, - address: Address, - code: &'static str, - ) -> TransportResult<()> + async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> where Self: Sync; } @@ -269,7 +252,7 @@ impl TempProvider for Provider { } /// Gets the last block number available. - async fn get_block_number(&self) -> TransportResult + async fn get_block_number(&self) -> TransportResult where Self: Sync, { @@ -277,7 +260,7 @@ impl TempProvider for Provider { } /// Gets the balance of the account at the specified tag, which defaults to latest. - async fn get_balance(&self, address: Address, tag: Option) -> TransportResult + async fn get_balance(&self, address: Address, tag: Option) -> TransportResult where Self: Sync, { @@ -293,11 +276,7 @@ impl TempProvider for Provider { } /// Gets a block by its [BlockHash], with full transactions or only hashes. - async fn get_block_by_hash( - &self, - hash: BlockHash, - full: bool, - ) -> TransportResult> + async fn get_block_by_hash(&self, hash: BlockHash, full: bool) -> TransportResult> where Self: Sync, { @@ -324,7 +303,7 @@ impl TempProvider for Provider { } /// Gets the chain ID. - async fn get_chain_id(&self) -> TransportResult + async fn get_chain_id(&self) -> TransportResult where Self: Sync, { @@ -365,7 +344,7 @@ impl TempProvider for Provider { } /// Gets a [Transaction] by its [TxHash]. - async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult + async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult where Self: Sync, { @@ -380,7 +359,7 @@ impl TempProvider for Provider { } /// Retrieves a [`Vec`] with the given [Filter]. - async fn get_logs(&self, filter: Filter) -> TransportResult> + async fn get_logs(&self, filter: Filter) -> TransportResult> where Self: Sync, { @@ -389,7 +368,7 @@ impl TempProvider for Provider { /// Gets the accounts in the remote node. This is usually empty unless you're using a local /// node. - async fn get_accounts(&self) -> TransportResult> + async fn get_accounts(&self) -> TransportResult> where Self: Sync, { @@ -397,7 +376,7 @@ impl TempProvider for Provider { } /// Gets the current gas price. - async fn get_gas_price(&self) -> TransportResult + async fn get_gas_price(&self) -> TransportResult where Self: Sync, { @@ -480,7 +459,7 @@ impl TempProvider for Provider { } /// Gets syncing info. - async fn syncing(&self) -> TransportResult + async fn syncing(&self) -> TransportResult where Self: Sync, { @@ -488,11 +467,7 @@ impl TempProvider for Provider { } /// Execute a smart contract call with [TransactionRequest] without publishing a transaction. - async fn call( - &self, - tx: TransactionRequest, - block: Option, - ) -> TransportResult + async fn call(&self, tx: TransactionRequest, block: Option) -> TransportResult where Self: Sync, { @@ -526,7 +501,7 @@ impl TempProvider for Provider { } /// Sends an already-signed transaction. - async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult + async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult where Self: Sync, { @@ -593,7 +568,10 @@ impl TempProvider for Provider { #[cfg(test)] mod providers_test { - use crate::{provider::{TempProvider, Provider}, utils}; + use crate::{ + provider::{Provider, TempProvider}, + utils, + }; use alloy_primitives::{address, b256, Address, U256, U64}; use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter}; use ethers_core::utils::Anvil; From 9a87d48d6011c8f7df2425b2b9755a3eeed7bfe0 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:29:44 +0100 Subject: [PATCH 05/37] fix rebase --- crates/providers/src/provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index f9067541bc2..38bf4f1634c 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -61,7 +61,7 @@ pub trait TempProvider: Send + Sync { &self, id: BlockId, full: bool, - ) -> RpcResult, Box, TransportError> { + ) -> TransportResult> { match id { BlockId::Hash(hash) => self.get_block_by_hash(hash.into(), full).await, BlockId::Number(number) => self.get_block_by_number(number, full).await, From b3fe160b490312845395034f84fda82c2893dda5 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:33:22 +0100 Subject: [PATCH 06/37] more fix --- crates/providers/src/provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 38bf4f1634c..8070bf91395 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -556,7 +556,7 @@ impl TempProvider for Provider { } #[cfg(feature = "anvil")] - pub async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> + async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> where Self: Sync, { From 66d48eba7dbbec00178136cc33e8f7844cb5b4bd Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:34:16 +0100 Subject: [PATCH 07/37] fmt --- crates/providers/src/provider.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 8070bf91395..0df79351ca9 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -57,11 +57,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Gets a block by either its hash, tag, or number, with full transactions or only hashes. - async fn get_block( - &self, - id: BlockId, - full: bool, - ) -> TransportResult> { + async fn get_block(&self, id: BlockId, full: bool) -> TransportResult> { match id { BlockId::Hash(hash) => self.get_block_by_hash(hash.into(), full).await, BlockId::Number(number) => self.get_block_by_number(number, full).await, From 71355478b37193e2d53f5bab3aaea8f5cb4fcab8 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:40:08 +0100 Subject: [PATCH 08/37] more rebase fix --- crates/providers/Cargo.toml | 1 + crates/providers/src/provider.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml index bd3fc3b2d82..dc48d97fb44 100644 --- a/crates/providers/Cargo.toml +++ b/crates/providers/Cargo.toml @@ -21,6 +21,7 @@ alloy-transport.workspace = true async-trait.workspace = true serde.workspace = true thiserror.workspace = true +reqwest.workspace = true futures-util = "0.3.28" serde_json = { workspace = true, features = ["raw_value"] } once_cell = "1.17" diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 0df79351ca9..52361d07654 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -8,8 +8,10 @@ use alloy_rpc_types::{ Transaction, TransactionReceipt, TransactionRequest, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; +use alloy_transport_http::Http; use async_trait::async_trait; use auto_impl::auto_impl; +use reqwest::Client; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use thiserror::Error; @@ -562,6 +564,30 @@ impl TempProvider for Provider { } } +impl TryFrom<&str> for Provider> { + type Error = ClientError; + + fn try_from(value: &str) -> Result { + Provider::new(value) + } +} + +impl TryFrom for Provider> { + type Error = ClientError; + + fn try_from(value: String) -> Result { + Provider::try_from(value.as_str()) + } +} + +impl<'a> TryFrom<&'a String> for Provider> { + type Error = ClientError; + + fn try_from(value: &'a String) -> Result { + Provider::try_from(value.as_str()) + } +} + #[cfg(test)] mod providers_test { use crate::{ @@ -569,7 +595,7 @@ mod providers_test { utils, }; use alloy_primitives::{address, b256, Address, U256, U64}; - use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter}; + use alloy_rpc_types::{BlockNumberOrTag, Filter}; use ethers_core::utils::Anvil; #[tokio::test] From f9f230eb18726b8d75fdbac21480e22be4898990 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:44:26 +0100 Subject: [PATCH 09/37] more fix --- crates/providers/src/provider.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 52361d07654..7fb3b3e9cbb 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -15,6 +15,7 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use thiserror::Error; +use alloy_rpc_client::ClientBuilder; #[derive(Debug, Error, Serialize, Deserialize)] pub enum ClientError { @@ -567,8 +568,11 @@ impl TempProvider for Provider { impl TryFrom<&str> for Provider> { type Error = ClientError; - fn try_from(value: &str) -> Result { - Provider::new(value) + fn try_from(url: &str) -> Result { + let url = url.parse().map_err(|_e| ClientError::ParseError)?; + let inner = ClientBuilder::default().reqwest_http(url); + + Ok(Self { inner, from: None }) } } From f6a17af4b82029e519833426ebef6465f58ec4d2 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:45:15 +0100 Subject: [PATCH 10/37] fmt --- crates/providers/src/provider.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 7fb3b3e9cbb..dd56fdef64f 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -2,7 +2,7 @@ use crate::utils::{self, EstimatorFunction}; use alloy_primitives::{Address, BlockHash, Bytes, StorageKey, StorageValue, TxHash, U256, U64}; -use alloy_rpc_client::RpcClient; +use alloy_rpc_client::{ClientBuilder, RpcClient}; use alloy_rpc_types::{ Block, BlockId, BlockNumberOrTag, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, Transaction, TransactionReceipt, TransactionRequest, @@ -15,7 +15,6 @@ use reqwest::Client; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use thiserror::Error; -use alloy_rpc_client::ClientBuilder; #[derive(Debug, Error, Serialize, Deserialize)] pub enum ClientError { From 8b18d74dd888a1ec5e2feedc5e1a023e4b578533 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:45:54 +0100 Subject: [PATCH 11/37] rm unused crates --- crates/providers/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml index dc48d97fb44..b2202351acc 100644 --- a/crates/providers/Cargo.toml +++ b/crates/providers/Cargo.toml @@ -22,9 +22,6 @@ async-trait.workspace = true serde.workspace = true thiserror.workspace = true reqwest.workspace = true -futures-util = "0.3.28" -serde_json = { workspace = true, features = ["raw_value"] } -once_cell = "1.17" serial_test = "2.0.0" auto_impl = "1.1.0" From 536a4acd68e756588a20f56f427a5cf9e4b3080c Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 15:47:55 +0100 Subject: [PATCH 12/37] more fix --- crates/providers/Cargo.toml | 1 - crates/providers/src/provider.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml index b2202351acc..66627309beb 100644 --- a/crates/providers/Cargo.toml +++ b/crates/providers/Cargo.toml @@ -22,7 +22,6 @@ async-trait.workspace = true serde.workspace = true thiserror.workspace = true reqwest.workspace = true -serial_test = "2.0.0" auto_impl = "1.1.0" [dev-dependencies] diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index dd56fdef64f..fb506da8a25 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -597,7 +597,7 @@ mod providers_test { provider::{Provider, TempProvider}, utils, }; - use alloy_primitives::{address, b256, Address, U256, U64}; + use alloy_primitives::{address, b256, U256, U64}; use alloy_rpc_types::{BlockNumberOrTag, Filter}; use ethers_core::utils::Anvil; From b06e5e86318c7bd0c747727b039d11b59418ee0f Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 16:07:53 +0100 Subject: [PATCH 13/37] async trait wasm awesome hack --- crates/providers/src/provider.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index fb506da8a25..614477de25e 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -36,8 +36,9 @@ pub struct Provider { /// Temporary Provider trait to be used until the new Provider trait with /// the Network abstraction is stable. /// Once the new Provider trait is stable, this trait will be removed. -#[async_trait] #[auto_impl(&, Arc, Box)] +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] pub trait TempProvider: Send + Sync { /// Gets the transaction count of the corresponding address. async fn get_transaction_count( @@ -228,6 +229,8 @@ impl Provider { // but as the interface is not stable yet, we define the bindings ourselves // until we can use the trait and the client abstraction that will use it. #[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TempProvider for Provider { /// Gets the transaction count of the corresponding address. async fn get_transaction_count( From bf8055625c46bfc252b8bf9fedc8e8f0a3220d2f Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 16:09:30 +0100 Subject: [PATCH 14/37] ? --- crates/providers/src/provider.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 614477de25e..e841d38b822 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -229,8 +229,6 @@ impl Provider { // but as the interface is not stable yet, we define the bindings ourselves // until we can use the trait and the client abstraction that will use it. #[async_trait] -#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TempProvider for Provider { /// Gets the transaction count of the corresponding address. async fn get_transaction_count( From 367c8d2b0dfef81c014b81b57e8bf3f1deca09b7 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 16:11:00 +0100 Subject: [PATCH 15/37] ?? --- crates/providers/src/provider.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index e841d38b822..3d01d6d6a86 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -36,7 +36,6 @@ pub struct Provider { /// Temporary Provider trait to be used until the new Provider trait with /// the Network abstraction is stable. /// Once the new Provider trait is stable, this trait will be removed. -#[auto_impl(&, Arc, Box)] #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] pub trait TempProvider: Send + Sync { From 009e125bc04c0cbdb1eeb4172975858f6db36317 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 16:12:11 +0100 Subject: [PATCH 16/37] ?????????? --- crates/providers/src/provider.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 3d01d6d6a86..6165d4b98c0 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -227,7 +227,8 @@ impl Provider { // In the future, this will be replaced by a Provider trait, // but as the interface is not stable yet, we define the bindings ourselves // until we can use the trait and the client abstraction that will use it. -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] impl TempProvider for Provider { /// Gets the transaction count of the corresponding address. async fn get_transaction_count( From b028f147e1d6c60bd1d624771fe6d468e1abacc2 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 17 Nov 2023 16:14:42 +0100 Subject: [PATCH 17/37] hokus pokus --- crates/providers/src/provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 6165d4b98c0..040329cbe39 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -9,7 +9,6 @@ use alloy_rpc_types::{ }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; use alloy_transport_http::Http; -use async_trait::async_trait; use auto_impl::auto_impl; use reqwest::Client; use serde::{Deserialize, Serialize}; @@ -38,6 +37,7 @@ pub struct Provider { /// Once the new Provider trait is stable, this trait will be removed. #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +#[auto_impl(&, Arc, Box)] pub trait TempProvider: Send + Sync { /// Gets the transaction count of the corresponding address. async fn get_transaction_count( From 1235be1833e01895bf8e0eda8f91b55258e725f1 Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 17 Nov 2023 12:10:04 -0400 Subject: [PATCH 18/37] feat: add tracing methods and a couple other ones (#32) --- crates/providers/src/provider.rs | 123 ++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 040329cbe39..9b46379c625 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -4,8 +4,10 @@ use crate::utils::{self, EstimatorFunction}; use alloy_primitives::{Address, BlockHash, Bytes, StorageKey, StorageValue, TxHash, U256, U64}; use alloy_rpc_client::{ClientBuilder, RpcClient}; use alloy_rpc_types::{ - Block, BlockId, BlockNumberOrTag, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, - Transaction, TransactionReceipt, TransactionRequest, + trace::{GethDebugTracingOptions, GethTrace, LocalizedTransactionTrace}, + AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, CallRequest, + EIP1186AccountProofResponse, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, Transaction, + TransactionReceipt, TransactionRequest, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; use alloy_transport_http::Http; @@ -201,6 +203,46 @@ pub trait TempProvider: Send + Sync { async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> where Self: Sync; + + async fn get_proof( + &self, + address: Address, + keys: Vec, + block: Option, + ) -> TransportResult + where + Self: Sync; + + async fn create_access_list( + &self, + request: CallRequest, + block: Option, + ) -> TransportResult + where + Self: Sync; + + /// Parity trace transaction. + async fn trace_transaction( + &self, + hash: TxHash, + ) -> TransportResult> + where + Self: Sync; + + async fn debug_trace_transaction( + &self, + hash: TxHash, + trace_options: GethDebugTracingOptions, + ) -> TransportResult + where + Self: Sync; + + async fn trace_block( + &self, + block: BlockNumberOrTag, + ) -> TransportResult> + where + Self: Sync; } impl Provider { @@ -554,6 +596,83 @@ impl TempProvider for Provider { Ok((max_fee_per_gas, max_priority_fee_per_gas)) } + async fn get_proof( + &self, + address: Address, + keys: Vec, + block: Option, + ) -> TransportResult + where + Self: Sync, + { + self.inner + .prepare( + "eth_getProof", + Cow::<(Address, Vec, BlockId)>::Owned(( + address, + keys, + block.unwrap_or(BlockNumberOrTag::Latest.into()), + )), + ) + .await + } + + async fn create_access_list( + &self, + request: CallRequest, + block: Option, + ) -> TransportResult + where + Self: Sync, + { + self.inner + .prepare( + "eth_createAccessList", + Cow::<(CallRequest, BlockId)>::Owned(( + request, + block.unwrap_or(BlockNumberOrTag::Latest.into()), + )), + ) + .await + } + + /// Parity trace transaction. + async fn trace_transaction( + &self, + hash: TxHash, + ) -> TransportResult> + where + Self: Sync, + { + self.inner.prepare("trace_transaction", Cow::>::Owned(vec![hash])).await + } + + async fn debug_trace_transaction( + &self, + hash: TxHash, + trace_options: GethDebugTracingOptions, + ) -> TransportResult + where + Self: Sync, + { + self.inner + .prepare( + "debug_traceTransaction", + Cow::<(TxHash, GethDebugTracingOptions)>::Owned((hash, trace_options)), + ) + .await + } + + async fn trace_block( + &self, + block: BlockNumberOrTag, + ) -> TransportResult> + where + Self: Sync, + { + self.inner.prepare("trace_block", Cow::::Owned(block)).await + } + #[cfg(feature = "anvil")] async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> where From f38cf95c7374f09158111442e41e79781ffa4f85 Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 17 Nov 2023 12:28:31 -0400 Subject: [PATCH 19/37] chore: TransactionRequest -> CallRequest (#33) --- crates/providers/src/provider.rs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 9b46379c625..3dd3ac77589 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -7,7 +7,7 @@ use alloy_rpc_types::{ trace::{GethDebugTracingOptions, GethTrace, LocalizedTransactionTrace}, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, CallRequest, EIP1186AccountProofResponse, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, Transaction, - TransactionReceipt, TransactionRequest, + TransactionReceipt, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; use alloy_transport_http::Http; @@ -170,17 +170,13 @@ pub trait TempProvider: Send + Sync { where Self: Sync; - /// Execute a smart contract call with [TransactionRequest] without publishing a transaction. - async fn call(&self, tx: TransactionRequest, block: Option) -> TransportResult + /// Execute a smart contract call with [CallRequest] without publishing a transaction. + async fn call(&self, tx: CallRequest, block: Option) -> TransportResult where Self: Sync; /// Estimate the gas needed for a transaction. - async fn estimate_gas( - &self, - tx: TransactionRequest, - block: Option, - ) -> TransportResult + async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult where Self: Sync; @@ -507,15 +503,15 @@ impl TempProvider for Provider { self.inner.prepare("eth_syncing", Cow::<()>::Owned(())).await } - /// Execute a smart contract call with [TransactionRequest] without publishing a transaction. - async fn call(&self, tx: TransactionRequest, block: Option) -> TransportResult + /// Execute a smart contract call with [CallRequest] without publishing a transaction. + async fn call(&self, tx: CallRequest, block: Option) -> TransportResult where Self: Sync, { self.inner .prepare( "eth_call", - Cow::<(TransactionRequest, BlockId)>::Owned(( + Cow::<(CallRequest, BlockId)>::Owned(( tx, block.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), )), @@ -524,19 +520,15 @@ impl TempProvider for Provider { } /// Estimate the gas needed for a transaction. - async fn estimate_gas( - &self, - tx: TransactionRequest, - block: Option, - ) -> TransportResult + async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult where Self: Sync, { if let Some(block_id) = block { - let params = Cow::<(TransactionRequest, BlockId)>::Owned((tx, block_id)); + let params = Cow::<(CallRequest, BlockId)>::Owned((tx, block_id)); self.inner.prepare("eth_estimateGas", params).await } else { - let params = Cow::::Owned(tx); + let params = Cow::::Owned(tx); self.inner.prepare("eth_estimateGas", params).await } } From 385b05747fdd10df88505ddc5ea1b7d59f658aac Mon Sep 17 00:00:00 2001 From: evalir Date: Fri, 17 Nov 2023 13:01:28 -0400 Subject: [PATCH 20/37] fix: return U256 for estimate_gas (#35) --- crates/providers/src/provider.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 3dd3ac77589..5b5b5c8f50c 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -176,7 +176,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Estimate the gas needed for a transaction. - async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult + async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult where Self: Sync; @@ -520,7 +520,7 @@ impl TempProvider for Provider { } /// Estimate the gas needed for a transaction. - async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult + async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult where Self: Sync, { From a8eb6708a601cb29373c03498367f884aed25bae Mon Sep 17 00:00:00 2001 From: Bjerg Date: Tue, 21 Nov 2023 03:27:02 +0100 Subject: [PATCH 21/37] fix: serialize block num w/o leading zeros (#37) --- crates/rpc-types/src/eth/block.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/rpc-types/src/eth/block.rs b/crates/rpc-types/src/eth/block.rs index 8135a693e63..648cad252a1 100644 --- a/crates/rpc-types/src/eth/block.rs +++ b/crates/rpc-types/src/eth/block.rs @@ -308,7 +308,9 @@ impl Serialize for BlockNumberOrTag { S: Serializer, { match *self { - BlockNumberOrTag::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")), + BlockNumberOrTag::Number(ref x) => { + serializer.serialize_str(&format!("0x{:x}", x.to::())) + }, BlockNumberOrTag::Latest => serializer.serialize_str("latest"), BlockNumberOrTag::Finalized => serializer.serialize_str("finalized"), BlockNumberOrTag::Safe => serializer.serialize_str("safe"), From 423d9bf96c8d63290480ac5c3746f642eb59070a Mon Sep 17 00:00:00 2001 From: Bjerg Date: Tue, 21 Nov 2023 03:53:44 +0100 Subject: [PATCH 22/37] fix: use u64 for block numbers (#38) * fix: serialize block num w/o leading zeros * fix: use `u64` for block numbers --- crates/providers/src/provider.rs | 6 +++--- crates/rpc-types/src/eth/block.rs | 14 +++++++------- crates/rpc-types/src/eth/filter.rs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 5b5b5c8f50c..1d7e335f1b2 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -51,7 +51,7 @@ pub trait TempProvider: Send + Sync { Self: Sync; /// Gets the last block number available. - async fn get_block_number(&self) -> TransportResult + async fn get_block_number(&self) -> TransportResult where Self: Sync; @@ -289,7 +289,7 @@ impl TempProvider for Provider { } /// Gets the last block number available. - async fn get_block_number(&self) -> TransportResult + async fn get_block_number(&self) -> TransportResult where Self: Sync, { @@ -718,7 +718,7 @@ mod providers_test { let anvil = Anvil::new().spawn(); let provider = Provider::try_from(&anvil.endpoint()).unwrap(); let num = provider.get_block_number().await.unwrap(); - assert_eq!(U64::ZERO, num) + assert_eq!(0, num) } #[tokio::test] diff --git a/crates/rpc-types/src/eth/block.rs b/crates/rpc-types/src/eth/block.rs index 648cad252a1..9819935a5aa 100644 --- a/crates/rpc-types/src/eth/block.rs +++ b/crates/rpc-types/src/eth/block.rs @@ -247,12 +247,12 @@ pub enum BlockNumberOrTag { /// Pending block (not yet part of the blockchain) Pending, /// Block by number from canon chain - Number(U64), + Number(u64), } impl BlockNumberOrTag { /// Returns the numeric block number if explicitly set - pub const fn as_number(&self) -> Option { + pub const fn as_number(&self) -> Option { match *self { BlockNumberOrTag::Number(num) => Some(num), _ => None, @@ -292,13 +292,13 @@ impl BlockNumberOrTag { impl From for BlockNumberOrTag { fn from(num: u64) -> Self { - BlockNumberOrTag::Number(U64::from(num)) + BlockNumberOrTag::Number(num) } } impl From for BlockNumberOrTag { fn from(num: U64) -> Self { - BlockNumberOrTag::Number(num) + num.to::().into() } } @@ -342,7 +342,7 @@ impl FromStr for BlockNumberOrTag { "pending" => Self::Pending, _number => { if let Some(hex_val) = s.strip_prefix("0x") { - let number = U64::from_str_radix(hex_val, 16); + let number = u64::from_str_radix(hex_val, 16); BlockNumberOrTag::Number(number?) } else { return Err(HexStringMissingPrefixError::default().into()); @@ -420,13 +420,13 @@ impl BlockId { impl From for BlockId { fn from(num: u64) -> Self { - BlockNumberOrTag::Number(U64::from(num)).into() + BlockNumberOrTag::Number(num).into() } } impl From for BlockId { fn from(num: U64) -> Self { - BlockNumberOrTag::Number(num).into() + BlockNumberOrTag::Number(num.to()).into() } } diff --git a/crates/rpc-types/src/eth/filter.rs b/crates/rpc-types/src/eth/filter.rs index 6c714e05d0e..d61b10e11b6 100644 --- a/crates/rpc-types/src/eth/filter.rs +++ b/crates/rpc-types/src/eth/filter.rs @@ -455,12 +455,12 @@ impl Filter { } /// Returns the numeric value of the `toBlock` field - pub fn get_to_block(&self) -> Option { + pub fn get_to_block(&self) -> Option { self.block_option.get_to_block().and_then(|b| b.as_number()) } /// Returns the numeric value of the `fromBlock` field - pub fn get_from_block(&self) -> Option { + pub fn get_from_block(&self) -> Option { self.block_option.get_from_block().and_then(|b| b.as_number()) } @@ -759,7 +759,7 @@ impl FilteredParams { } /// Returns true if the filter matches the given block number - pub fn filter_block_range(&self, block_number: U64) -> bool { + pub fn filter_block_range(&self, block_number: u64) -> bool { if self.filter.is_none() { return true; } From b806dca44de8f7a5f8d8d5c007047e606d79f24f Mon Sep 17 00:00:00 2001 From: Bjerg Date: Tue, 21 Nov 2023 04:26:07 +0100 Subject: [PATCH 23/37] fix: deserialize block num as U64 (#39) --- crates/providers/src/provider.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 1d7e335f1b2..cb2baaf12a8 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -288,12 +288,13 @@ impl TempProvider for Provider { .await } + /// Gets the last block number available. /// Gets the last block number available. async fn get_block_number(&self) -> TransportResult - where - Self: Sync, + where + Self: Sync, { - self.inner.prepare("eth_blockNumber", Cow::<()>::Owned(())).await + self.inner.prepare("eth_blockNumber", Cow::<()>::Owned(())).await.map(|num: U64| num.to::()) } /// Gets the balance of the account at the specified tag, which defaults to latest. From a9cfe2871acd7c7cd52e32badd6b0d4f7eddc160 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Tue, 21 Nov 2023 04:34:07 +0100 Subject: [PATCH 24/37] fix: typo --- crates/rpc-types/src/eth/block.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/rpc-types/src/eth/block.rs b/crates/rpc-types/src/eth/block.rs index 9819935a5aa..452f53474d5 100644 --- a/crates/rpc-types/src/eth/block.rs +++ b/crates/rpc-types/src/eth/block.rs @@ -308,9 +308,7 @@ impl Serialize for BlockNumberOrTag { S: Serializer, { match *self { - BlockNumberOrTag::Number(ref x) => { - serializer.serialize_str(&format!("0x{:x}", x.to::())) - }, + BlockNumberOrTag::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")), BlockNumberOrTag::Latest => serializer.serialize_str("latest"), BlockNumberOrTag::Finalized => serializer.serialize_str("finalized"), BlockNumberOrTag::Safe => serializer.serialize_str("safe"), From fa0aac7f4b0fb638860a251e40509d331600a37e Mon Sep 17 00:00:00 2001 From: Bjerg Date: Tue, 21 Nov 2023 05:37:55 +0100 Subject: [PATCH 25/37] fix: dont skip gas used ratio if empty (#40) * workaround * fix: actual fix * fix: actual fix part 2 --- crates/rpc-types/src/eth/fee.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/rpc-types/src/eth/fee.rs b/crates/rpc-types/src/eth/fee.rs index c7717994ea1..3662d63686a 100644 --- a/crates/rpc-types/src/eth/fee.rs +++ b/crates/rpc-types/src/eth/fee.rs @@ -37,8 +37,8 @@ pub struct FeeHistory { /// # Note /// /// The `Option` is only for compatability with Erigon and Geth. - #[serde(skip_serializing_if = "Vec::is_empty")] - #[serde(default)] + /// Empty list is skipped only for compatability with Erigon and Geth. + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub base_fee_per_gas: Vec, /// An array of block gas used ratios. These are calculated as the ratio /// of `gasUsed` and `gasLimit`. @@ -46,13 +46,11 @@ pub struct FeeHistory { /// # Note /// /// The `Option` is only for compatability with Erigon and Geth. - #[serde(skip_serializing_if = "Vec::is_empty")] - #[serde(default)] pub gas_used_ratio: Vec, /// Lowest number block of the returned range. pub oldest_block: U256, /// An (optional) array of effective priority fee per gas data points from a single /// block. All zeroes are returned if the block is empty. - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub reward: Option>>, } From f7d77a8f0a98f5eb1388b20fb3dcd7167485a481 Mon Sep 17 00:00:00 2001 From: Bjerg Date: Tue, 21 Nov 2023 05:39:33 +0100 Subject: [PATCH 26/37] fix(rpc-types): access list keys (#42) * workaround * fix: actual fix * fix: actual fix part 2 * fix access list keys --------- Co-authored-by: Enrique Ortiz --- crates/providers/src/provider.rs | 2 +- .../src/eth/transaction/access_list.rs | 34 ++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index cb2baaf12a8..ff029b7fcff 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -854,7 +854,7 @@ mod providers_test { let fee_history = provider .get_fee_history( U256::from(utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS), - BlockNumberOrTag::Number(block_number.to()), + BlockNumberOrTag::Number(block_number), &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE], ) .await diff --git a/crates/rpc-types/src/eth/transaction/access_list.rs b/crates/rpc-types/src/eth/transaction/access_list.rs index c4a7e8d1abe..a59877ee87d 100644 --- a/crates/rpc-types/src/eth/transaction/access_list.rs +++ b/crates/rpc-types/src/eth/transaction/access_list.rs @@ -1,4 +1,4 @@ -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, U256, B256}; use serde::{Deserialize, Serialize}; /// A list of addresses and storage keys that the transaction plans to access. @@ -9,7 +9,7 @@ pub struct AccessListItem { /// Account addresses that would be loaded at the start of execution pub address: Address, /// Keys of storage that would be loaded at the start of execution - pub storage_keys: Vec, + pub storage_keys: Vec, } /// AccessList as defined in EIP-2930 @@ -29,12 +29,28 @@ impl AccessList { /// Consumes the type and returns an iterator over the list's addresses and storage keys. pub fn into_flatten(self) -> impl Iterator)> { - self.0.into_iter().map(|item| (item.address, item.storage_keys)) + self.0.into_iter().map(|item| { + ( + item.address, + item.storage_keys + .into_iter() + .map(|slot| U256::from_be_bytes(slot.0)) + .collect(), + ) + }) } /// Returns an iterator over the list's addresses and storage keys. - pub fn flatten(&self) -> impl Iterator + '_ { - self.0.iter().map(|item| (item.address, item.storage_keys.as_slice())) + pub fn flatten(&self) -> impl Iterator)> + '_ { + self.0.iter().map(|item| { + ( + item.address, + item.storage_keys + .iter() + .map(|slot| U256::from_be_bytes(slot.0)) + .collect(), + ) + }) } } @@ -55,8 +71,8 @@ mod tests { #[test] fn access_list_serde() { let list = AccessList(vec![ - AccessListItem { address: Address::ZERO, storage_keys: vec![U256::ZERO] }, - AccessListItem { address: Address::ZERO, storage_keys: vec![U256::ZERO] }, + AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, + AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, ]); let json = serde_json::to_string(&list).unwrap(); let list2 = serde_json::from_str::(&json).unwrap(); @@ -67,8 +83,8 @@ mod tests { fn access_list_with_gas_used() { let list = AccessListWithGasUsed { access_list: AccessList(vec![ - AccessListItem { address: Address::ZERO, storage_keys: vec![U256::ZERO] }, - AccessListItem { address: Address::ZERO, storage_keys: vec![U256::ZERO] }, + AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, + AccessListItem { address: Address::ZERO, storage_keys: vec![B256::ZERO] }, ]), gas_used: U256::from(100), }; From 3ca3c9078718323478a5a2ccb3d80dcdd82eaefb Mon Sep 17 00:00:00 2001 From: evalir Date: Wed, 22 Nov 2023 12:48:22 -0400 Subject: [PATCH 27/37] chore: remove Cow from `TempProvider` (#43) * chore: start removing cows * chore: uncow tempProvider * Apply suggestions from code review Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> * switch to single element tuple --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/providers/src/provider.rs | 79 +++++++++++++++----------------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index ff029b7fcff..6aee89ac599 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -6,7 +6,7 @@ use alloy_rpc_client::{ClientBuilder, RpcClient}; use alloy_rpc_types::{ trace::{GethDebugTracingOptions, GethTrace, LocalizedTransactionTrace}, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag, CallRequest, - EIP1186AccountProofResponse, FeeHistory, Filter, Log, RpcBlockHash, SyncStatus, Transaction, + EIP1186AccountProofResponse, FeeHistory, Filter, Log, SyncStatus, Transaction, TransactionReceipt, }; use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResult}; @@ -14,7 +14,6 @@ use alloy_transport_http::Http; use auto_impl::auto_impl; use reqwest::Client; use serde::{Deserialize, Serialize}; -use std::borrow::Cow; use thiserror::Error; #[derive(Debug, Error, Serialize, Deserialize)] @@ -280,10 +279,10 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getTransactionCount", - Cow::<(Address, BlockId)>::Owned(( + ( address, tag.unwrap_or(BlockNumberOrTag::Latest.into()), - )), + ), ) .await } @@ -294,7 +293,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_blockNumber", Cow::<()>::Owned(())).await.map(|num: U64| num.to::()) + self.inner.prepare("eth_blockNumber", ()).await.map(|num: U64| num.to::()) } /// Gets the balance of the account at the specified tag, which defaults to latest. @@ -305,10 +304,10 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getBalance", - Cow::<(Address, BlockId)>::Owned(( + ( address, tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - )), + ), ) .await } @@ -319,7 +318,7 @@ impl TempProvider for Provider { Self: Sync, { self.inner - .prepare("eth_getBlockByHash", Cow::<(BlockHash, bool)>::Owned((hash, full))) + .prepare("eth_getBlockByHash", (hash, full)) .await } @@ -335,7 +334,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getBlockByNumber", - Cow::<(BlockNumberOrTag, bool)>::Owned((number.into(), full)), + (number.into(), full), ) .await } @@ -345,7 +344,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_chainId", Cow::<()>::Owned(())).await + self.inner.prepare("eth_chainId", ()).await } /// Gets the specified storage value from [Address]. @@ -358,11 +357,11 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getStorageAt", - Cow::<(Address, StorageKey, BlockId)>::Owned(( + ( address, key, tag.unwrap_or(BlockNumberOrTag::Latest.into()), - )), + ), ) .await } @@ -377,7 +376,7 @@ impl TempProvider for Provider { Self: Sync, { self.inner - .prepare("eth_getCode", Cow::<(Address, BlockId)>::Owned((address, tag.into()))) + .prepare("eth_getCode", (address, tag.into())) .await } @@ -389,9 +388,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getTransactionByHash", - // Force alloy-rs/alloy to encode this an array of strings, - // even if we only need to send one hash. - Cow::>::Owned(vec![hash]), + (hash,), ) .await } @@ -401,7 +398,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_getLogs", Cow::>::Owned(vec![filter])).await + self.inner.prepare("eth_getLogs", vec![filter]).await } /// Gets the accounts in the remote node. This is usually empty unless you're using a local @@ -410,7 +407,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_accounts", Cow::<()>::Owned(())).await + self.inner.prepare("eth_accounts", ()).await } /// Gets the current gas price. @@ -418,7 +415,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_gasPrice", Cow::<()>::Owned(())).await + self.inner.prepare("eth_gasPrice", ()).await } /// Gets a [TransactionReceipt] if it exists, by its [TxHash]. @@ -429,7 +426,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_getTransactionReceipt", Cow::>::Owned(vec![hash])).await + self.inner.prepare("eth_getTransactionReceipt", (hash,)).await } /// Returns a collection of historical gas information [FeeHistory] which @@ -446,11 +443,11 @@ impl TempProvider for Provider { self.inner .prepare( "eth_feeHistory", - Cow::<(U256, BlockNumberOrTag, Vec)>::Owned(( + ( block_count, last_block.into(), - reward_percentiles.to_vec(), - )), + reward_percentiles, + ), ) .await } @@ -463,7 +460,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_getBlockReceipts", Cow::::Owned(block)).await + self.inner.prepare("eth_getBlockReceipts", block).await } /// Gets an uncle block through the tag [BlockId] and index [U64]. @@ -481,7 +478,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getUncleByBlockHashAndIndex", - Cow::<(RpcBlockHash, U64)>::Owned((hash, idx)), + (hash, idx), ) .await } @@ -489,7 +486,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getUncleByBlockNumberAndIndex", - Cow::<(BlockNumberOrTag, U64)>::Owned((number, idx)), + (number, idx), ) .await } @@ -501,7 +498,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("eth_syncing", Cow::<()>::Owned(())).await + self.inner.prepare("eth_syncing", ()).await } /// Execute a smart contract call with [CallRequest] without publishing a transaction. @@ -512,10 +509,10 @@ impl TempProvider for Provider { self.inner .prepare( "eth_call", - Cow::<(CallRequest, BlockId)>::Owned(( + ( tx, block.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - )), + ), ) .await } @@ -526,20 +523,20 @@ impl TempProvider for Provider { Self: Sync, { if let Some(block_id) = block { - let params = Cow::<(CallRequest, BlockId)>::Owned((tx, block_id)); + let params = (tx, block_id); self.inner.prepare("eth_estimateGas", params).await } else { - let params = Cow::::Owned(tx); + let params = tx; self.inner.prepare("eth_estimateGas", params).await } } - /// Sends an already-signed transaction. + /// Sends an already-signed transaction. async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult where Self: Sync, { - self.inner.prepare("eth_sendRawTransaction", Cow::::Owned(tx)).await + self.inner.prepare("eth_sendRawTransaction", tx).await } /// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields. @@ -601,11 +598,11 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getProof", - Cow::<(Address, Vec, BlockId)>::Owned(( + ( address, keys, block.unwrap_or(BlockNumberOrTag::Latest.into()), - )), + ), ) .await } @@ -621,10 +618,10 @@ impl TempProvider for Provider { self.inner .prepare( "eth_createAccessList", - Cow::<(CallRequest, BlockId)>::Owned(( + ( request, block.unwrap_or(BlockNumberOrTag::Latest.into()), - )), + ), ) .await } @@ -637,7 +634,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("trace_transaction", Cow::>::Owned(vec![hash])).await + self.inner.prepare("trace_transaction", vec![hash]).await } async fn debug_trace_transaction( @@ -651,7 +648,7 @@ impl TempProvider for Provider { self.inner .prepare( "debug_traceTransaction", - Cow::<(TxHash, GethDebugTracingOptions)>::Owned((hash, trace_options)), + (hash, trace_options), ) .await } @@ -663,7 +660,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner.prepare("trace_block", Cow::::Owned(block)).await + self.inner.prepare("trace_block", block).await } #[cfg(feature = "anvil")] @@ -672,7 +669,7 @@ impl TempProvider for Provider { Self: Sync, { self.inner - .prepare("anvil_setCode", Cow::<(Address, &'static str)>::Owned((address, code))) + .prepare("anvil_setCode", (address, code)) .await } } From 8ad7b7e5f72d73558320f92bf4155996cb2514e0 Mon Sep 17 00:00:00 2001 From: evalir Date: Wed, 22 Nov 2023 20:59:18 -0400 Subject: [PATCH 28/37] feat(`rpc-types`): RLP encoding/decoding for transaction types (#36) * feat: add rlp encoding/decoding to tx types * feat: add encodable/decodable traits to tx * chore: remove out-of-scope func * chore: remove bad links on comments * chore: fix docs * clippy --- .../src/eth/transaction/access_list.rs | 26 +- crates/rpc-types/src/eth/transaction/mod.rs | 6 +- .../src/eth/transaction/signature.rs | 168 +++++- .../rpc-types/src/eth/transaction/tx_type.rs | 52 ++ crates/rpc-types/src/eth/transaction/typed.rs | 555 +++++++++++++++++- 5 files changed, 798 insertions(+), 9 deletions(-) create mode 100644 crates/rpc-types/src/eth/transaction/tx_type.rs diff --git a/crates/rpc-types/src/eth/transaction/access_list.rs b/crates/rpc-types/src/eth/transaction/access_list.rs index a59877ee87d..c69517809ac 100644 --- a/crates/rpc-types/src/eth/transaction/access_list.rs +++ b/crates/rpc-types/src/eth/transaction/access_list.rs @@ -1,9 +1,13 @@ +use std::mem; +use alloy_rlp::{RlpDecodable, RlpEncodable}; use alloy_primitives::{Address, U256, B256}; use serde::{Deserialize, Serialize}; /// A list of addresses and storage keys that the transaction plans to access. /// Accesses outside the list are possible, but become more expensive. -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash, Default)] +#[derive( + Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash, Default, RlpEncodable, RlpDecodable, +)] #[serde(rename_all = "camelCase")] pub struct AccessListItem { /// Account addresses that would be loaded at the start of execution @@ -12,8 +16,18 @@ pub struct AccessListItem { pub storage_keys: Vec, } +impl AccessListItem { + /// Calculates a heuristic for the in-memory size of the [AccessListItem]. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::
() + self.storage_keys.capacity() * mem::size_of::() + } +} + /// AccessList as defined in EIP-2930 -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash, Default)] +#[derive( + Deserialize, Serialize, Clone, Debug, PartialEq, Eq, Hash, Default, RlpEncodable, RlpDecodable, +)] pub struct AccessList(pub Vec); impl AccessList { @@ -52,6 +66,14 @@ impl AccessList { ) }) } + + /// Calculates a heuristic for the in-memory size of the [AccessList]. + #[inline] + pub fn size(&self) -> usize { + // take into account capacity + self.0.iter().map(AccessListItem::size).sum::() + + self.0.capacity() * mem::size_of::() + } } /// Access list with gas used appended. diff --git a/crates/rpc-types/src/eth/transaction/mod.rs b/crates/rpc-types/src/eth/transaction/mod.rs index 937e0d3b0ff..6dfe965d83d 100644 --- a/crates/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc-types/src/eth/transaction/mod.rs @@ -12,8 +12,11 @@ mod common; mod receipt; mod request; mod signature; +mod tx_type; mod typed; +pub use tx_type::*; + /// Transaction object used in RPC #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -66,9 +69,6 @@ pub struct Transaction { #[serde(skip_serializing_if = "Option::is_none")] pub access_list: Option>, /// EIP2718 - /// - /// Transaction type, Some(2) for EIP-1559 transaction, - /// Some(1) for AccessList transaction, None for Legacy #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub transaction_type: Option, } diff --git a/crates/rpc-types/src/eth/transaction/signature.rs b/crates/rpc-types/src/eth/transaction/signature.rs index 82866a639a6..481de806f34 100644 --- a/crates/rpc-types/src/eth/transaction/signature.rs +++ b/crates/rpc-types/src/eth/transaction/signature.rs @@ -1,5 +1,6 @@ //! Signature related RPC values use alloy_primitives::U256; +use alloy_rlp::{Bytes, Decodable, Encodable, Error as RlpError}; use serde::{Deserialize, Serialize}; /// Container type for all signature fields in RPC @@ -23,10 +24,119 @@ pub struct Signature { pub y_parity: Option, } +impl Signature { + /// Output the length of the signature without the length of the RLP header, using the legacy + /// scheme with EIP-155 support depends on chain_id. + pub fn payload_len_with_eip155_chain_id(&self, chain_id: Option) -> usize { + self.v(chain_id).length() + self.r.length() + self.s.length() + } + + /// Encode the `v`, `r`, `s` values without a RLP header. + /// Encodes the `v` value using the legacy scheme with EIP-155 support depends on chain_id. + pub fn encode_with_eip155_chain_id( + &self, + out: &mut dyn alloy_rlp::BufMut, + chain_id: Option, + ) { + self.v(chain_id).encode(out); + self.r.encode(out); + self.s.encode(out); + } + + /// Output the `v` of the signature depends on chain_id + #[inline] + pub fn v(&self, chain_id: Option) -> u64 { + if let Some(chain_id) = chain_id { + // EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35 + let y_parity = u64::from(self.y_parity.unwrap_or(Parity(false))); + y_parity + chain_id * 2 + 35 + } else { + u64::from(self.y_parity.unwrap_or(Parity(false))) + 27 + } + } + + /// Decodes the `v`, `r`, `s` values without a RLP header. + /// This will return a chain ID if the `v` value is [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) compatible. + pub fn decode_with_eip155_chain_id(buf: &mut &[u8]) -> alloy_rlp::Result<(Self, Option)> { + let v = u64::decode(buf)?; + let r = Decodable::decode(buf)?; + let s = Decodable::decode(buf)?; + if v >= 35 { + // EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35 + let y_parity = ((v - 35) % 2) != 0; + let chain_id = (v - 35) >> 1; + Ok(( + Signature { r, s, y_parity: Some(Parity(y_parity)), v: U256::from(v) }, + Some(chain_id), + )) + } else { + // non-EIP-155 legacy scheme, v = 27 for even y-parity, v = 28 for odd y-parity + if v != 27 && v != 28 { + return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")); + } + let y_parity = v == 28; + Ok((Signature { r, s, y_parity: Some(Parity(y_parity)), v: U256::from(v) }, None)) + } + } + + /// Output the length of the signature without the length of the RLP header + pub fn payload_len(&self) -> usize { + let y_parity_len = match self.y_parity { + Some(parity) => parity.0 as usize, + None => 0_usize, + }; + y_parity_len + self.r.length() + self.s.length() + } + + /// Encode the `y_parity`, `r`, `s` values without a RLP header. + /// Panics if the y parity is not set. + pub fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + self.y_parity.expect("y_parity not set").encode(out); + self.r.encode(out); + self.s.encode(out); + } + + /// Decodes the `y_parity`, `r`, `s` values without a RLP header. + pub fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + let mut sig = + Signature { + y_parity: Some(Decodable::decode(buf)?), + r: Decodable::decode(buf)?, + s: Decodable::decode(buf)?, + v: U256::ZERO, + }; + sig.v = sig.y_parity.unwrap().into(); + Ok(sig) + } + + /// Turn this signature into its byte + /// (hex) representation. + /// Panics: if the y_parity field is not set. + pub fn to_bytes(&self) -> [u8; 65] { + let mut sig = [0u8; 65]; + sig[..32].copy_from_slice(&self.r.to_be_bytes::<32>()); + sig[32..64].copy_from_slice(&self.s.to_be_bytes::<32>()); + let v = u8::from(self.y_parity.expect("y_parity not set")) + 27; + sig[64] = v; + sig + } + + /// Turn this signature into its hex-encoded representation. + pub fn to_hex_bytes(&self) -> Bytes { + alloy_primitives::hex::encode(self.to_bytes()).into() + } + + /// Calculates a heuristic for the in-memory size of the [Signature]. + #[inline] + pub fn size(&self) -> usize { + std::mem::size_of::() + } +} + /// Type that represents the signature parity byte, meant for use in RPC. /// /// This will be serialized as "0x0" if false, and "0x1" if true. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Parity( #[serde(serialize_with = "serialize_parity", deserialize_with = "deserialize_parity")] pub bool, ); @@ -37,6 +147,62 @@ impl From for Parity { } } +impl From for Parity { + fn from(value: U256) -> Self { + match value { + U256::ZERO => Self(false), + _ => Self(true), + } + } +} + +impl From for U256 { + fn from(p: Parity) -> Self { + if p.0 { + U256::from(1) + } else { + U256::ZERO + } + } +} + +impl From for u64 { + fn from(p: Parity) -> Self { + if p.0 { + 1 + } else { + 0 + } + } +} + +impl From for u8 { + fn from(value: Parity) -> Self { + match value.0 { + true => 1, + false => 0, + } + } +} + +impl Encodable for Parity { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + let v = u8::from(*self); + v.encode(out); + } + + fn length(&self) -> usize { + 1 + } +} + +impl Decodable for Parity { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + let v = u8::decode(buf)?; + Ok(Self(v != 0)) + } +} + fn serialize_parity(parity: &bool, serializer: S) -> Result where S: serde::Serializer, diff --git a/crates/rpc-types/src/eth/transaction/tx_type.rs b/crates/rpc-types/src/eth/transaction/tx_type.rs new file mode 100644 index 00000000000..81f60b533e8 --- /dev/null +++ b/crates/rpc-types/src/eth/transaction/tx_type.rs @@ -0,0 +1,52 @@ +use alloy_primitives::U8; +use serde::{Deserialize, Serialize}; + +/// Identifier for legacy transaction, however a legacy tx is technically not +/// typed. +pub const LEGACY_TX_TYPE_ID: u8 = 0; + +/// Identifier for an EIP2930 transaction. +pub const EIP2930_TX_TYPE_ID: u8 = 1; + +/// Identifier for an EIP1559 transaction. +pub const EIP1559_TX_TYPE_ID: u8 = 2; + +/// Identifier for an EIP4844 transaction. +pub const EIP4844_TX_TYPE_ID: u8 = 3; + +/// Transaction Type +/// +/// Currently being used as 2-bit type when encoding it to Compact on +/// crate::TransactionSignedNoHash (see Reth's Compact encoding). Adding more transaction types will break the codec and +/// database format on Reth. +/// +/// Other required changes when adding a new type can be seen on [PR#3953](https://github.com/paradigmxyz/reth/pull/3953/files). +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)] +pub enum TxType { + /// Legacy transaction pre EIP-2929 + #[default] + Legacy = 0_isize, + /// AccessList transaction + EIP2930 = 1_isize, + /// Transaction with Priority fee + EIP1559 = 2_isize, + /// Shard Blob Transactions - EIP-4844 + EIP4844 = 3_isize, +} + +impl From for u8 { + fn from(value: TxType) -> Self { + match value { + TxType::Legacy => LEGACY_TX_TYPE_ID, + TxType::EIP2930 => EIP2930_TX_TYPE_ID, + TxType::EIP1559 => EIP1559_TX_TYPE_ID, + TxType::EIP4844 => EIP4844_TX_TYPE_ID, + } + } +} + +impl From for U8 { + fn from(value: TxType) -> Self { + U8::from(u8::from(value)) + } +} diff --git a/crates/rpc-types/src/eth/transaction/typed.rs b/crates/rpc-types/src/eth/transaction/typed.rs index e529d37b9f9..98c2cd4b99a 100644 --- a/crates/rpc-types/src/eth/transaction/typed.rs +++ b/crates/rpc-types/src/eth/transaction/typed.rs @@ -3,9 +3,11 @@ //! transaction deserialized from the json input of an RPC call. Depending on what fields are set, //! it can be converted into the container type [`TypedTransactionRequest`]. -use crate::eth::transaction::AccessList; -use alloy_primitives::{Address, Bytes, U128, U256, U64}; -use alloy_rlp::{BufMut, Decodable, Encodable, Error as RlpError}; +use std::{mem, cmp::Ordering}; + +use crate::{eth::transaction::AccessList, Signature, TxType}; +use alloy_primitives::{keccak256, Address, Bytes, B256, U128, U256, U64}; +use alloy_rlp::{bytes, length_of_length, BufMut, Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE, Buf}; use serde::{Deserialize, Serialize}; /// Container type for various Ethereum transaction requests @@ -24,6 +26,76 @@ pub enum TypedTransactionRequest { EIP1559(EIP1559TransactionRequest), } +impl Encodable for TypedTransactionRequest { + fn encode(&self, out: &mut dyn BufMut) { + match self { + // Just encode as such + TypedTransactionRequest::Legacy(tx) => tx.encode(out), + // For EIP2930 and EIP1559 txs, we need to "envelop" the RLP encoding with the tx type. + // For EIP2930, it's 1. + TypedTransactionRequest::EIP2930(tx) => { + let id = 1_u8; + id.encode(out); + tx.encode(out) + }, + // For EIP1559, it's 2. + TypedTransactionRequest::EIP1559(tx) => { + let id = 2_u8; + id.encode(out); + tx.encode(out) + }, + } + } + + fn length(&self) -> usize { + match self { + TypedTransactionRequest::Legacy(tx) => tx.length(), + TypedTransactionRequest::EIP2930(tx) => tx.length(), + TypedTransactionRequest::EIP1559(tx) => tx.length(), + } + } +} + +impl Decodable for TypedTransactionRequest { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + // First, decode the tx type. + let tx_type = u8::decode(buf)?; + // Then, decode the tx based on the type. + match tx_type.cmp(&EMPTY_LIST_CODE) { + Ordering::Less => { + // strip out the string header + // NOTE: typed transaction encodings either contain a "rlp header" which contains + // the type of the payload and its length, or they do not contain a header and + // start with the tx type byte. + // + // This line works for both types of encodings because byte slices starting with + // 0x01 and 0x02 return a Header { list: false, payload_length: 1 } when input to + // Header::decode. + // If the encoding includes a header, the header will be properly decoded and + // consumed. + // Otherwise, header decoding will succeed but nothing is consumed. + let _header = Header::decode(buf)?; + let tx_type = *buf.first().ok_or(RlpError::Custom( + "typed tx cannot be decoded from an empty slice", + ))?; + if tx_type == 0x01 { + buf.advance(1); + EIP2930TransactionRequest::decode(buf) + .map(TypedTransactionRequest::EIP2930) + } else if tx_type == 0x02 { + buf.advance(1); + EIP1559TransactionRequest::decode(buf) + .map(TypedTransactionRequest::EIP1559) + } else { + Err(RlpError::Custom("invalid tx type")) + } + }, + Ordering::Equal => Err(RlpError::Custom("an empty list is not a valid transaction encoding")), + Ordering::Greater => LegacyTransactionRequest::decode(buf).map(TypedTransactionRequest::Legacy), + } + } +} + /// Represents a legacy transaction request #[derive(Debug, Clone, PartialEq, Eq)] pub struct LegacyTransactionRequest { @@ -36,6 +108,131 @@ pub struct LegacyTransactionRequest { pub chain_id: Option, } +impl Encodable for LegacyTransactionRequest { + fn encode(&self, out: &mut dyn BufMut) { + self.nonce.encode(out); + self.gas_price.encode(out); + self.gas_limit.encode(out); + self.kind.encode(out); + self.value.encode(out); + self.input.0.encode(out); + } + + fn length(&self) -> usize { + self.nonce.length() + + self.gas_price.length() + + self.gas_limit.length() + + self.kind.length() + + self.value.length() + + self.input.0.length() + } +} + +impl Decodable for LegacyTransactionRequest { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + Ok(Self { + nonce: Decodable::decode(buf)?, + gas_price: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + kind: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + chain_id: None, + }) + } +} + +impl LegacyTransactionRequest { + /// Calculates a heuristic for the in-memory size of the [LegacyTransactionRequest] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::>() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_price + mem::size_of::() + // gas_limit + self.kind.size() + // to + mem::size_of::() + // value + self.input.len() // input + } + + /// Outputs the length of the transaction's fields, without a RLP header or length of the + /// eip155 fields. + pub fn fields_len(&self) -> usize { + let mut len = 0; + len += self.nonce.length(); + len += self.gas_price.length(); + len += self.gas_limit.length(); + len += self.kind.length(); + len += self.value.length(); + len += self.input.0.length(); + len + } + + /// Encodes only the transaction's fields into the desired buffer, without a RLP header or + /// eip155 fields. + pub fn encode_fields(&self, out: &mut dyn bytes::BufMut) { + self.nonce.encode(out); + self.gas_price.encode(out); + self.gas_limit.encode(out); + self.kind.encode(out); + self.value.encode(out); + self.input.0.encode(out); + } + + /// Outputs the length of EIP-155 fields. Only outputs a non-zero value for EIP-155 legacy + /// transactions. + pub fn eip155_fields_len(&self) -> usize { + if let Some(id) = self.chain_id { + // EIP-155 encodes the chain ID and two zeroes, so we add 2 to the length of the chain + // ID to get the length of all 3 fields + // len(chain_id) + (0x00) + (0x00) + id.length() + 2 + } else { + // this is either a pre-EIP-155 legacy transaction or a typed transaction + 0 + } + } + + /// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy + /// transactions. + pub fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) { + // if this is a legacy transaction without a chain ID, it must be pre-EIP-155 + // and does not need to encode the chain ID for the signature hash encoding + if let Some(id) = self.chain_id { + // EIP-155 encodes the chain ID and two zeroes + id.encode(out); + 0x00u8.encode(out); + 0x00u8.encode(out); + } + } + + /// Encodes the legacy transaction in RLP for signing, including the EIP-155 fields if possible. + pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { + Header { list: true, payload_length: self.fields_len() + self.eip155_fields_len() } + .encode(out); + self.encode_fields(out); + self.encode_eip155_fields(out); + } + + /// Outputs the length of the signature RLP encoding for the transaction, including the length + /// of the EIP-155 fields if possible. + pub fn payload_len_for_signature(&self) -> usize { + let payload_length = self.fields_len() + self.eip155_fields_len(); + // 'header length' + 'payload length' + length_of_length(payload_length) + payload_length + } + + /// Outputs the signature hash of the transaction by first encoding without a signature, then + /// hashing. + /// + /// See [Self::encode_for_signing] for more information on the encoding format. + pub fn signature_hash(&self) -> B256 { + let mut buf = bytes::BytesMut::with_capacity(self.payload_len_for_signature()); + self.encode_for_signing(&mut buf); + keccak256(&buf) + } +} + /// Represents an EIP-2930 transaction request #[derive(Debug, Clone, PartialEq, Eq)] pub struct EIP2930TransactionRequest { @@ -49,6 +246,175 @@ pub struct EIP2930TransactionRequest { pub access_list: AccessList, } +impl Encodable for EIP2930TransactionRequest { + fn encode(&self, out: &mut dyn BufMut) { + self.chain_id.encode(out); + self.nonce.encode(out); + self.gas_price.encode(out); + self.gas_limit.encode(out); + self.kind.encode(out); + self.value.encode(out); + self.input.0.encode(out); + self.access_list.encode(out); + } + + fn length(&self) -> usize { + self.chain_id.length() + + self.nonce.length() + + self.gas_price.length() + + self.gas_limit.length() + + self.kind.length() + + self.value.length() + + self.input.0.length() + + self.access_list.length() + } +} + +impl Decodable for EIP2930TransactionRequest { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + Ok(Self { + chain_id: Decodable::decode(buf)?, + nonce: Decodable::decode(buf)?, + gas_price: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + kind: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + access_list: Decodable::decode(buf)?, + }) + } +} + +impl EIP2930TransactionRequest { + /// Calculates a heuristic for the in-memory size of the transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_price + mem::size_of::() + // gas_limit + self.kind.size() + // to + mem::size_of::() + // value + self.access_list.size() + // access_list + self.input.len() // input + } + + /// Decodes the inner fields from RLP bytes. + /// + /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following + /// RLP fields in the following order: + /// + /// - `chain_id` + /// - `nonce` + /// - `gas_price` + /// - `gas_limit` + /// - `to` + /// - `value` + /// - `data` (`input`) + /// - `access_list` + pub fn decode_inner(buf: &mut &[u8]) -> alloy_rlp::Result { + Ok(Self { + chain_id: Decodable::decode(buf)?, + nonce: Decodable::decode(buf)?, + gas_price: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + kind: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + access_list: Decodable::decode(buf)?, + }) + } + + /// Outputs the length of the transaction's fields, without a RLP header. + pub fn fields_len(&self) -> usize { + let mut len = 0; + len += self.chain_id.length(); + len += self.nonce.length(); + len += self.gas_price.length(); + len += self.gas_limit.length(); + len += self.kind.length(); + len += self.value.length(); + len += self.input.0.length(); + len += self.access_list.length(); + len + } + + /// Encodes only the transaction's fields into the desired buffer, without a RLP header. + pub fn encode_fields(&self, out: &mut dyn bytes::BufMut) { + self.chain_id.encode(out); + self.nonce.encode(out); + self.gas_price.encode(out); + self.gas_limit.encode(out); + self.kind.encode(out); + self.value.encode(out); + self.input.0.encode(out); + self.access_list.encode(out); + } + + /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating + /// hash that for eip2718 does not require rlp header + pub fn encode_with_signature( + &self, + signature: &Signature, + out: &mut dyn bytes::BufMut, + with_header: bool, + ) { + let payload_length = self.fields_len() + signature.payload_len(); + if with_header { + Header { + list: false, + payload_length: 1 + length_of_length(payload_length) + payload_length, + } + .encode(out); + } + out.put_u8(self.tx_type() as u8); + let header = Header { list: true, payload_length }; + header.encode(out); + self.encode_fields(out); + signature.encode(out); + } + + /// Output the length of the RLP signed transaction encoding, _without_ a RLP string header. + pub fn payload_len_with_signature_without_header(&self, signature: &Signature) -> usize { + let payload_length = self.fields_len() + signature.payload_len(); + // 'transaction type byte length' + 'header length' + 'payload length' + 1 + length_of_length(payload_length) + payload_length + } + + /// Output the length of the RLP signed transaction encoding. This encodes with a RLP header. + pub fn payload_len_with_signature(&self, signature: &Signature) -> usize { + let len = self.payload_len_with_signature_without_header(signature); + length_of_length(len) + len + } + + /// Get transaction type + pub fn tx_type(&self) -> TxType { + TxType::EIP2930 + } + + /// Encodes the EIP-2930 transaction in RLP for signing. + pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { + out.put_u8(self.tx_type() as u8); + Header { list: true, payload_length: self.fields_len() }.encode(out); + self.encode_fields(out); + } + + /// Outputs the length of the signature RLP encoding for the transaction. + pub fn payload_len_for_signature(&self) -> usize { + let payload_length = self.fields_len(); + // 'transaction type byte length' + 'header length' + 'payload length' + 1 + length_of_length(payload_length) + payload_length + } + + /// Outputs the signature hash of the transaction by first encoding without a signature, then + /// hashing. + pub fn signature_hash(&self) -> B256 { + let mut buf = bytes::BytesMut::with_capacity(self.payload_len_for_signature()); + self.encode_for_signing(&mut buf); + keccak256(&buf) + } +} + /// Represents an EIP-1559 transaction request #[derive(Debug, Clone, PartialEq, Eq)] pub struct EIP1559TransactionRequest { @@ -63,6 +429,183 @@ pub struct EIP1559TransactionRequest { pub access_list: AccessList, } +impl Encodable for EIP1559TransactionRequest { + fn encode(&self, out: &mut dyn BufMut) { + self.chain_id.encode(out); + self.nonce.encode(out); + self.max_priority_fee_per_gas.encode(out); + self.max_fee_per_gas.encode(out); + self.gas_limit.encode(out); + self.kind.encode(out); + self.value.encode(out); + self.input.0.encode(out); + self.access_list.encode(out); + } + + fn length(&self) -> usize { + self.chain_id.length() + + self.nonce.length() + + self.max_priority_fee_per_gas.length() + + self.max_fee_per_gas.length() + + self.gas_limit.length() + + self.kind.length() + + self.value.length() + + self.input.0.length() + + self.access_list.length() + } +} + +impl Decodable for EIP1559TransactionRequest { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + Ok(Self { + chain_id: Decodable::decode(buf)?, + nonce: Decodable::decode(buf)?, + max_priority_fee_per_gas: Decodable::decode(buf)?, + max_fee_per_gas: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + kind: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + access_list: Decodable::decode(buf)?, + }) + } +} + +impl EIP1559TransactionRequest { + /// Decodes the inner fields from RLP bytes. + /// + /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following + /// RLP fields in the following order: + /// + /// - `chain_id` + /// - `nonce` + /// - `max_priority_fee_per_gas` + /// - `max_fee_per_gas` + /// - `gas_limit` + /// - `to` + /// - `value` + /// - `data` (`input`) + /// - `access_list` + pub fn decode_inner(buf: &mut &[u8]) -> alloy_rlp::Result { + Ok(Self { + chain_id: Decodable::decode(buf)?, + nonce: Decodable::decode(buf)?, + max_priority_fee_per_gas: Decodable::decode(buf)?, + max_fee_per_gas: Decodable::decode(buf)?, + gas_limit: Decodable::decode(buf)?, + kind: Decodable::decode(buf)?, + value: Decodable::decode(buf)?, + input: Decodable::decode(buf)?, + access_list: Decodable::decode(buf)?, + }) + } + + /// Encodes only the transaction's fields into the desired buffer, without a RLP header. + pub fn fields_len(&self) -> usize { + let mut len = 0; + len += self.chain_id.length(); + len += self.nonce.length(); + len += self.max_priority_fee_per_gas.length(); + len += self.max_fee_per_gas.length(); + len += self.gas_limit.length(); + len += self.kind.length(); + len += self.value.length(); + len += self.input.0.length(); + len += self.access_list.length(); + len + } + + /// Encodes only the transaction's fields into the desired buffer, without a RLP header. + pub fn encode_fields(&self, out: &mut dyn bytes::BufMut) { + self.chain_id.encode(out); + self.nonce.encode(out); + self.max_priority_fee_per_gas.encode(out); + self.max_fee_per_gas.encode(out); + self.gas_limit.encode(out); + self.kind.encode(out); + self.value.encode(out); + self.input.0.encode(out); + self.access_list.encode(out); + } + + /// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating + /// hash that for eip2718 does not require rlp header + pub fn encode_with_signature( + &self, + signature: &Signature, + out: &mut dyn bytes::BufMut, + with_header: bool, + ) { + let payload_length = self.fields_len() + signature.payload_len(); + if with_header { + Header { + list: false, + payload_length: 1 + length_of_length(payload_length) + payload_length, + } + .encode(out); + } + out.put_u8(self.tx_type() as u8); + let header = Header { list: true, payload_length }; + header.encode(out); + self.encode_fields(out); + signature.encode(out); + } + + /// Output the length of the RLP signed transaction encoding, _without_ a RLP string header. + pub fn payload_len_with_signature_without_header(&self, signature: &Signature) -> usize { + let payload_length = self.fields_len() + signature.payload_len(); + // 'transaction type byte length' + 'header length' + 'payload length' + 1 + length_of_length(payload_length) + payload_length + } + + /// Output the length of the RLP signed transaction encoding. This encodes with a RLP header. + pub fn payload_len_with_signature(&self, signature: &Signature) -> usize { + let len = self.payload_len_with_signature_without_header(signature); + length_of_length(len) + len + } + + /// Get transaction type + pub fn tx_type(&self) -> TxType { + TxType::EIP1559 + } + + /// Calculates a heuristic for the in-memory size of the transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_limit + mem::size_of::() + // max_fee_per_gas + mem::size_of::() + // max_priority_fee_per_gas + self.kind.size() + // to + mem::size_of::() + // value + self.access_list.size() + // access_list + self.input.len() // input + } + + /// Encodes the legacy transaction in RLP for signing. + pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) { + out.put_u8(self.tx_type() as u8); + Header { list: true, payload_length: self.fields_len() }.encode(out); + self.encode_fields(out); + } + + /// Outputs the length of the signature RLP encoding for the transaction. + pub fn payload_len_for_signature(&self) -> usize { + let payload_length = self.fields_len(); + // 'transaction type byte length' + 'header length' + 'payload length' + 1 + length_of_length(payload_length) + payload_length + } + + /// Outputs the signature hash of the transaction by first encoding without a signature, then + /// hashing. + pub fn signature_hash(&self) -> B256 { + let mut buf = bytes::BytesMut::with_capacity(self.payload_len_for_signature()); + self.encode_for_signing(&mut buf); + keccak256(&buf) + } +} + /// Represents the `to` field of a transaction request /// /// This determines what kind of transaction this is @@ -84,6 +627,12 @@ impl TransactionKind { TransactionKind::Create => None, } } + + /// Calculates a heuristic for the in-memory size of the [TransactionKind]. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + } } impl Encodable for TransactionKind { From 07022532bc8c535de19231689af6107696d615a8 Mon Sep 17 00:00:00 2001 From: evalir Date: Thu, 23 Nov 2023 16:51:14 -0400 Subject: [PATCH 29/37] feat(`TempProvider`): `raw_request` (#45) * feat(): raw_request * chore: remove unneeded async block --- crates/providers/src/provider.rs | 50 ++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 6aee89ac599..8ab5ad2df65 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -13,7 +13,7 @@ use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResu use alloy_transport_http::Http; use auto_impl::auto_impl; use reqwest::Client; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use thiserror::Error; #[derive(Debug, Error, Serialize, Deserialize)] @@ -238,6 +238,16 @@ pub trait TempProvider: Send + Sync { ) -> TransportResult> where Self: Sync; + + async fn raw_request( + &self, + method: &'static str, + params: P, + ) -> TransportResult + where + P: Serialize + Send + Sync + Clone, + R: Serialize + DeserializeOwned + Send + Sync + Unpin + 'static, + Self: Sync; } impl Provider { @@ -663,6 +673,22 @@ impl TempProvider for Provider { self.inner.prepare("trace_block", block).await } + /// Sends a raw request with the methods and params specified to the internal connection, + /// and returns the result. + async fn raw_request( + &self, + method: &'static str, + params: P, + ) -> TransportResult + where + P: Serialize + Send + Sync + Clone, + R: Serialize + DeserializeOwned + Send + Sync + Unpin + 'static, + Self: Sync + { + let res: R = self.inner.prepare(method, ¶ms).await?; + Ok(res) + } + #[cfg(feature = "anvil")] async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> where @@ -708,7 +734,7 @@ mod providers_test { utils, }; use alloy_primitives::{address, b256, U256, U64}; - use alloy_rpc_types::{BlockNumberOrTag, Filter}; + use alloy_rpc_types::{BlockNumberOrTag, Filter, Block}; use ethers_core::utils::Anvil; #[tokio::test] @@ -719,6 +745,14 @@ mod providers_test { assert_eq!(0, num) } + #[tokio::test] + async fn gets_block_number_with_raw_req() { + let anvil = Anvil::new().spawn(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); + let num: U64 = provider.raw_request("eth_blockNumber", ()).await.unwrap(); + assert_eq!(0, num.to::()) + } + #[tokio::test] async fn gets_transaction_count() { let anvil = Anvil::new().spawn(); @@ -745,6 +779,18 @@ mod providers_test { assert_eq!(block.header.hash.unwrap(), hash); } + #[tokio::test] + async fn gets_block_by_hash_with_raw_req() { + let anvil = Anvil::new().spawn(); + let provider = Provider::try_from(&anvil.endpoint()).unwrap(); + let num = 0; + let tag: BlockNumberOrTag = num.into(); + let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap(); + let hash = block.header.hash.unwrap(); + let block: Block = provider.raw_request::<(alloy_primitives::FixedBytes<32>, bool), Block>("eth_getBlockByHash", (hash, true)).await.unwrap(); + assert_eq!(block.header.hash.unwrap(), hash); + } + #[tokio::test] async fn gets_block_by_number_full() { let anvil = Anvil::new().spawn(); From ef5a20d940bb35906228d06f7a6dc0ba2418f437 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Sat, 25 Nov 2023 08:57:33 +0100 Subject: [PATCH 30/37] chore: bump alloy-primitives --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a332db33199..b44147a1e3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ alloy-networks = { version = "0.1.0", path = "crates/networks" } alloy-rpc-types = { version = "0.1.0", path = "crates/rpc-types" } alloy-rpc-client = { version = "0.1.0", path = "crates/rpc-client" } -alloy-primitives = { version = "0.4.2", features = ["serde"] } +alloy-primitives = { version = "0.5.0", features = ["serde"] } alloy-rlp = "0.3" async-trait = "0.1.74" From d6ec3478d8c68e3c12b0eb9bdb65870a113e46a6 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 29 Nov 2023 14:51:10 -0400 Subject: [PATCH 31/37] chore: set uncle as default when missing transactions field --- crates/rpc-types/src/eth/block.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/rpc-types/src/eth/block.rs b/crates/rpc-types/src/eth/block.rs index 452f53474d5..9808fd953f2 100644 --- a/crates/rpc-types/src/eth/block.rs +++ b/crates/rpc-types/src/eth/block.rs @@ -40,6 +40,11 @@ impl BlockTransactions { pub fn iter(&self) -> BlockTransactionsHashIterator<'_> { BlockTransactionsHashIterator::new(self) } + + /// Returns an instance of BlockTransactions with the Uncle special case. + pub fn uncle() -> Self { + Self::Uncle + } } /// An Iterator over the transaction hashes of a block. @@ -123,6 +128,7 @@ pub struct Block { pub uncles: Vec, /// Transactions #[serde(skip_serializing_if = "BlockTransactions::is_uncle")] + #[serde(default = "BlockTransactions::uncle")] pub transactions: BlockTransactions, /// Integer the size of this block in bytes. pub size: Option, From de1b5f3c734a36f03c231e97e7cf10dcb2d5aa4f Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Fri, 8 Dec 2023 16:24:41 -0400 Subject: [PATCH 32/37] fmt --- crates/providers/src/provider.rs | 119 +++++------------- .../src/eth/transaction/access_list.rs | 14 +-- .../src/eth/transaction/signature.rs | 13 +- .../rpc-types/src/eth/transaction/tx_type.rs | 4 +- crates/rpc-types/src/eth/transaction/typed.rs | 79 ++++++------ 5 files changed, 82 insertions(+), 147 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 680d0de6607..1688778d159 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -13,7 +13,7 @@ use alloy_transport::{BoxTransport, Transport, TransportErrorKind, TransportResu use alloy_transport_http::Http; use auto_impl::auto_impl; use reqwest::Client; -use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use thiserror::Error; #[derive(Debug, Error, Serialize, Deserialize)] @@ -239,11 +239,7 @@ pub trait TempProvider: Send + Sync { where Self: Sync; - async fn raw_request( - &self, - method: &'static str, - params: P, - ) -> TransportResult + async fn raw_request(&self, method: &'static str, params: P) -> TransportResult where P: Serialize + Send + Sync + Clone, R: Serialize + DeserializeOwned + Send + Sync + Unpin + 'static, @@ -286,10 +282,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getTransactionCount", - ( - address, - tag.unwrap_or(BlockNumberOrTag::Latest.into()), - ), + (address, tag.unwrap_or(BlockNumberOrTag::Latest.into())), ) .await } @@ -305,10 +298,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getBalance", - ( - address, - tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - ), + (address, tag.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest))), ) .await } @@ -319,9 +309,7 @@ impl TempProvider for Provider { hash: BlockHash, full: bool, ) -> TransportResult> { - self.inner - .prepare("eth_getBlockByHash", (hash, full)) - .await + self.inner.prepare("eth_getBlockByHash", (hash, full)).await } /// Gets a block by [BlockNumberOrTag], with full transactions or only hashes. @@ -330,12 +318,7 @@ impl TempProvider for Provider { number: B, full: bool, ) -> TransportResult> { - self.inner - .prepare( - "eth_getBlockByNumber", - (number.into(), full), - ) - .await + self.inner.prepare("eth_getBlockByNumber", (number.into(), full)).await } /// Gets the chain ID. @@ -353,11 +336,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getStorageAt", - ( - address, - key, - tag.unwrap_or(BlockNumberOrTag::Latest.into()), - ), + (address, key, tag.unwrap_or(BlockNumberOrTag::Latest.into())), ) .await } @@ -368,19 +347,12 @@ impl TempProvider for Provider { address: Address, tag: B, ) -> TransportResult { - self.inner - .prepare("eth_getCode", (address, tag.into())) - .await + self.inner.prepare("eth_getCode", (address, tag.into())).await } /// Gets a [Transaction] by its [TxHash]. pub async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult { - self.inner - .prepare( - "eth_getTransactionByHash", - (hash,), - ) - .await + self.inner.prepare("eth_getTransactionByHash", (hash,)).await } /// Retrieves a [`Vec`] with the given [Filter]. @@ -416,14 +388,7 @@ impl TempProvider for Provider { reward_percentiles: &[f64], ) -> TransportResult { self.inner - .prepare( - "eth_feeHistory", - ( - block_count, - last_block.into(), - reward_percentiles, - ), - ) + .prepare("eth_feeHistory", (block_count, last_block.into(), reward_percentiles)) .await } @@ -447,20 +412,10 @@ impl TempProvider for Provider { let tag = tag.into(); match tag { BlockId::Hash(hash) => { - self.inner - .prepare( - "eth_getUncleByBlockHashAndIndex", - (hash, idx), - ) - .await + self.inner.prepare("eth_getUncleByBlockHashAndIndex", (hash, idx)).await } BlockId::Number(number) => { - self.inner - .prepare( - "eth_getUncleByBlockNumberAndIndex", - (number, idx), - ) - .await + self.inner.prepare("eth_getUncleByBlockNumberAndIndex", (number, idx)).await } } } @@ -473,13 +428,7 @@ impl TempProvider for Provider { /// Execute a smart contract call with [CallRequest] without publishing a transaction. async fn call(&self, tx: CallRequest, block: Option) -> TransportResult { self.inner - .prepare( - "eth_call", - ( - tx, - block.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - ), - ) + .prepare("eth_call", (tx, block.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)))) .await } @@ -494,7 +443,7 @@ impl TempProvider for Provider { } } - /// Sends an already-signed transaction. + /// Sends an already-signed transaction. async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult where Self: Sync, @@ -555,11 +504,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_getProof", - ( - address, - keys, - block.unwrap_or(BlockNumberOrTag::Latest.into()), - ), + (address, keys, block.unwrap_or(BlockNumberOrTag::Latest.into())), ) .await } @@ -575,10 +520,7 @@ impl TempProvider for Provider { self.inner .prepare( "eth_createAccessList", - ( - request, - block.unwrap_or(BlockNumberOrTag::Latest.into()), - ), + (request, block.unwrap_or(BlockNumberOrTag::Latest.into())), ) .await } @@ -602,12 +544,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner - .prepare( - "debug_traceTransaction", - (hash, trace_options), - ) - .await + self.inner.prepare("debug_traceTransaction", (hash, trace_options)).await } async fn trace_block( @@ -622,15 +559,11 @@ impl TempProvider for Provider { /// Sends a raw request with the methods and params specified to the internal connection, /// and returns the result. - async fn raw_request( - &self, - method: &'static str, - params: P, - ) -> TransportResult + async fn raw_request(&self, method: &'static str, params: P) -> TransportResult where P: Serialize + Send + Sync + Clone, R: Serialize + DeserializeOwned + Send + Sync + Unpin + 'static, - Self: Sync + Self: Sync, { let res: R = self.inner.prepare(method, ¶ms).await?; Ok(res) @@ -641,9 +574,7 @@ impl TempProvider for Provider { where Self: Sync, { - self.inner - .prepare("anvil_setCode", (address, code)) - .await + self.inner.prepare("anvil_setCode", (address, code)).await } } @@ -681,7 +612,7 @@ mod providers_test { utils, }; use alloy_primitives::{address, b256, U256, U64}; - use alloy_rpc_types::{BlockNumberOrTag, Filter, Block}; + use alloy_rpc_types::{Block, BlockNumberOrTag, Filter}; use ethers_core::utils::Anvil; #[tokio::test] @@ -734,7 +665,13 @@ mod providers_test { let tag: BlockNumberOrTag = num.into(); let block = provider.get_block_by_number(tag, true).await.unwrap().unwrap(); let hash = block.header.hash.unwrap(); - let block: Block = provider.raw_request::<(alloy_primitives::FixedBytes<32>, bool), Block>("eth_getBlockByHash", (hash, true)).await.unwrap(); + let block: Block = provider + .raw_request::<(alloy_primitives::FixedBytes<32>, bool), Block>( + "eth_getBlockByHash", + (hash, true), + ) + .await + .unwrap(); assert_eq!(block.header.hash.unwrap(), hash); } diff --git a/crates/rpc-types/src/eth/transaction/access_list.rs b/crates/rpc-types/src/eth/transaction/access_list.rs index c69517809ac..f0b8025a0cc 100644 --- a/crates/rpc-types/src/eth/transaction/access_list.rs +++ b/crates/rpc-types/src/eth/transaction/access_list.rs @@ -1,7 +1,7 @@ -use std::mem; +use alloy_primitives::{Address, B256, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; -use alloy_primitives::{Address, U256, B256}; use serde::{Deserialize, Serialize}; +use std::mem; /// A list of addresses and storage keys that the transaction plans to access. /// Accesses outside the list are possible, but become more expensive. @@ -46,10 +46,7 @@ impl AccessList { self.0.into_iter().map(|item| { ( item.address, - item.storage_keys - .into_iter() - .map(|slot| U256::from_be_bytes(slot.0)) - .collect(), + item.storage_keys.into_iter().map(|slot| U256::from_be_bytes(slot.0)).collect(), ) }) } @@ -59,10 +56,7 @@ impl AccessList { self.0.iter().map(|item| { ( item.address, - item.storage_keys - .iter() - .map(|slot| U256::from_be_bytes(slot.0)) - .collect(), + item.storage_keys.iter().map(|slot| U256::from_be_bytes(slot.0)).collect(), ) }) } diff --git a/crates/rpc-types/src/eth/transaction/signature.rs b/crates/rpc-types/src/eth/transaction/signature.rs index 481de806f34..072f7c3d64c 100644 --- a/crates/rpc-types/src/eth/transaction/signature.rs +++ b/crates/rpc-types/src/eth/transaction/signature.rs @@ -98,13 +98,12 @@ impl Signature { /// Decodes the `y_parity`, `r`, `s` values without a RLP header. pub fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - let mut sig = - Signature { - y_parity: Some(Decodable::decode(buf)?), - r: Decodable::decode(buf)?, - s: Decodable::decode(buf)?, - v: U256::ZERO, - }; + let mut sig = Signature { + y_parity: Some(Decodable::decode(buf)?), + r: Decodable::decode(buf)?, + s: Decodable::decode(buf)?, + v: U256::ZERO, + }; sig.v = sig.y_parity.unwrap().into(); Ok(sig) } diff --git a/crates/rpc-types/src/eth/transaction/tx_type.rs b/crates/rpc-types/src/eth/transaction/tx_type.rs index 81f60b533e8..f589f639cb0 100644 --- a/crates/rpc-types/src/eth/transaction/tx_type.rs +++ b/crates/rpc-types/src/eth/transaction/tx_type.rs @@ -17,8 +17,8 @@ pub const EIP4844_TX_TYPE_ID: u8 = 3; /// Transaction Type /// /// Currently being used as 2-bit type when encoding it to Compact on -/// crate::TransactionSignedNoHash (see Reth's Compact encoding). Adding more transaction types will break the codec and -/// database format on Reth. +/// crate::TransactionSignedNoHash (see Reth's Compact encoding). Adding more transaction types will +/// break the codec and database format on Reth. /// /// Other required changes when adding a new type can be seen on [PR#3953](https://github.com/paradigmxyz/reth/pull/3953/files). #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)] diff --git a/crates/rpc-types/src/eth/transaction/typed.rs b/crates/rpc-types/src/eth/transaction/typed.rs index 98c2cd4b99a..054a3137fdf 100644 --- a/crates/rpc-types/src/eth/transaction/typed.rs +++ b/crates/rpc-types/src/eth/transaction/typed.rs @@ -3,11 +3,14 @@ //! transaction deserialized from the json input of an RPC call. Depending on what fields are set, //! it can be converted into the container type [`TypedTransactionRequest`]. -use std::{mem, cmp::Ordering}; +use std::{cmp::Ordering, mem}; use crate::{eth::transaction::AccessList, Signature, TxType}; use alloy_primitives::{keccak256, Address, Bytes, B256, U128, U256, U64}; -use alloy_rlp::{bytes, length_of_length, BufMut, Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE, Buf}; +use alloy_rlp::{ + bytes, length_of_length, Buf, BufMut, Decodable, Encodable, Error as RlpError, Header, + EMPTY_LIST_CODE, +}; use serde::{Deserialize, Serialize}; /// Container type for various Ethereum transaction requests @@ -37,13 +40,13 @@ impl Encodable for TypedTransactionRequest { let id = 1_u8; id.encode(out); tx.encode(out) - }, + } // For EIP1559, it's 2. TypedTransactionRequest::EIP1559(tx) => { let id = 2_u8; id.encode(out); tx.encode(out) - }, + } } } @@ -75,23 +78,25 @@ impl Decodable for TypedTransactionRequest { // consumed. // Otherwise, header decoding will succeed but nothing is consumed. let _header = Header::decode(buf)?; - let tx_type = *buf.first().ok_or(RlpError::Custom( - "typed tx cannot be decoded from an empty slice", - ))?; + let tx_type = *buf + .first() + .ok_or(RlpError::Custom("typed tx cannot be decoded from an empty slice"))?; if tx_type == 0x01 { buf.advance(1); - EIP2930TransactionRequest::decode(buf) - .map(TypedTransactionRequest::EIP2930) + EIP2930TransactionRequest::decode(buf).map(TypedTransactionRequest::EIP2930) } else if tx_type == 0x02 { buf.advance(1); - EIP1559TransactionRequest::decode(buf) - .map(TypedTransactionRequest::EIP1559) + EIP1559TransactionRequest::decode(buf).map(TypedTransactionRequest::EIP1559) } else { Err(RlpError::Custom("invalid tx type")) } - }, - Ordering::Equal => Err(RlpError::Custom("an empty list is not a valid transaction encoding")), - Ordering::Greater => LegacyTransactionRequest::decode(buf).map(TypedTransactionRequest::Legacy), + } + Ordering::Equal => { + Err(RlpError::Custom("an empty list is not a valid transaction encoding")) + } + Ordering::Greater => { + LegacyTransactionRequest::decode(buf).map(TypedTransactionRequest::Legacy) + } } } } @@ -119,12 +124,12 @@ impl Encodable for LegacyTransactionRequest { } fn length(&self) -> usize { - self.nonce.length() + - self.gas_price.length() + - self.gas_limit.length() + - self.kind.length() + - self.value.length() + - self.input.0.length() + self.nonce.length() + + self.gas_price.length() + + self.gas_limit.length() + + self.kind.length() + + self.value.length() + + self.input.0.length() } } @@ -259,14 +264,14 @@ impl Encodable for EIP2930TransactionRequest { } fn length(&self) -> usize { - self.chain_id.length() + - self.nonce.length() + - self.gas_price.length() + - self.gas_limit.length() + - self.kind.length() + - self.value.length() + - self.input.0.length() + - self.access_list.length() + self.chain_id.length() + + self.nonce.length() + + self.gas_price.length() + + self.gas_limit.length() + + self.kind.length() + + self.value.length() + + self.input.0.length() + + self.access_list.length() } } @@ -443,15 +448,15 @@ impl Encodable for EIP1559TransactionRequest { } fn length(&self) -> usize { - self.chain_id.length() + - self.nonce.length() + - self.max_priority_fee_per_gas.length() + - self.max_fee_per_gas.length() + - self.gas_limit.length() + - self.kind.length() + - self.value.length() + - self.input.0.length() + - self.access_list.length() + self.chain_id.length() + + self.nonce.length() + + self.max_priority_fee_per_gas.length() + + self.max_fee_per_gas.length() + + self.gas_limit.length() + + self.kind.length() + + self.value.length() + + self.input.0.length() + + self.access_list.length() } } From e359e2722d3aa17a014a07a932f52453824f8bb2 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Fri, 8 Dec 2023 16:25:47 -0400 Subject: [PATCH 33/37] chore: remove pub --- crates/providers/src/provider.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 1688778d159..65743acecfb 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -304,7 +304,7 @@ impl TempProvider for Provider { } /// Gets a block by its [BlockHash], with full transactions or only hashes. - pub async fn get_block_by_hash( + async fn get_block_by_hash( &self, hash: BlockHash, full: bool, @@ -351,7 +351,7 @@ impl TempProvider for Provider { } /// Gets a [Transaction] by its [TxHash]. - pub async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult { + async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult { self.inner.prepare("eth_getTransactionByHash", (hash,)).await } From 46bf17a5bd7403ccb4623dc2a18a1ac887bfbf9f Mon Sep 17 00:00:00 2001 From: evalir Date: Mon, 11 Dec 2023 15:18:47 -0400 Subject: [PATCH 34/37] feat: support for additional fields on TransactionReceipt and Block (#66) --- crates/rpc-types/src/eth/block.rs | 15 ++- crates/rpc-types/src/eth/mod.rs | 1 + crates/rpc-types/src/eth/other.rs | 117 ++++++++++++++++++ .../rpc-types/src/eth/transaction/receipt.rs | 5 +- 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 crates/rpc-types/src/eth/other.rs diff --git a/crates/rpc-types/src/eth/block.rs b/crates/rpc-types/src/eth/block.rs index 9808fd953f2..ce3c115ee43 100644 --- a/crates/rpc-types/src/eth/block.rs +++ b/crates/rpc-types/src/eth/block.rs @@ -1,5 +1,5 @@ //! Contains types that represent ethereum types when used in RPC -use crate::{Transaction, Withdrawal}; +use crate::{other::OtherFields, Transaction, Withdrawal}; use alloy_primitives::{ ruint::ParseError, Address, BlockHash, BlockNumber, Bloom, Bytes, B256, B64, U256, U64, }; @@ -117,24 +117,27 @@ pub enum BlockError { #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Block { - /// Header of the block + /// Header of the block. #[serde(flatten)] pub header: Header, /// Total difficulty, this field is None only if representing /// an Uncle block. #[serde(skip_serializing_if = "Option::is_none")] pub total_difficulty: Option, - /// Uncles' hashes + /// Uncles' hashes. pub uncles: Vec, - /// Transactions + /// Transactions. #[serde(skip_serializing_if = "BlockTransactions::is_uncle")] #[serde(default = "BlockTransactions::uncle")] pub transactions: BlockTransactions, /// Integer the size of this block in bytes. pub size: Option, - /// Withdrawals in the block + /// Withdrawals in the block. #[serde(default, skip_serializing_if = "Option::is_none")] pub withdrawals: Option>, + /// Support for arbitrary additional fields. + #[serde(flatten)] + pub other: OtherFields, } impl Block { @@ -861,6 +864,7 @@ mod tests { transactions: BlockTransactions::Hashes(vec![B256::with_last_byte(18)]), size: Some(U256::from(19)), withdrawals: Some(vec![]), + other: Default::default(), }; let serialized = serde_json::to_string(&block).unwrap(); assert_eq!( @@ -902,6 +906,7 @@ mod tests { transactions: BlockTransactions::Hashes(vec![B256::with_last_byte(18)]), size: Some(U256::from(19)), withdrawals: None, + other: Default::default(), }; let serialized = serde_json::to_string(&block).unwrap(); assert_eq!( diff --git a/crates/rpc-types/src/eth/mod.rs b/crates/rpc-types/src/eth/mod.rs index 569dde4ca41..9da97f85a79 100644 --- a/crates/rpc-types/src/eth/mod.rs +++ b/crates/rpc-types/src/eth/mod.rs @@ -6,6 +6,7 @@ mod call; mod fee; mod filter; mod log; +pub mod other; pub mod pubsub; pub mod raw_log; pub mod state; diff --git a/crates/rpc-types/src/eth/other.rs b/crates/rpc-types/src/eth/other.rs new file mode 100644 index 00000000000..e6efe12dc53 --- /dev/null +++ b/crates/rpc-types/src/eth/other.rs @@ -0,0 +1,117 @@ +//! Support for capturing other fields +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Map; +use std::{ + collections::BTreeMap, + ops::{Deref, DerefMut}, +}; + +/// A type that is supposed to capture additional fields that are not native to ethereum but included in ethereum adjacent networks, for example fields the [optimism `eth_getTransactionByHash` request](https://docs.alchemy.com/alchemy/apis/optimism/eth-gettransactionbyhash) returns additional fields that this type will capture +/// +/// This type is supposed to be used with [`#[serde(flatten)`](https://serde.rs/field-attrs.html#flatten) +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)] +#[serde(transparent)] +pub struct OtherFields { + /// Contains all unknown fields + inner: BTreeMap, +} + +// === impl OtherFields === + +impl OtherFields { + /// Returns the deserialized value of the field, if it exists. + /// Deserializes the value with the given closure + pub fn get_with(&self, key: impl AsRef, with: F) -> Option + where + F: FnOnce(serde_json::Value) -> V, + { + self.inner.get(key.as_ref()).cloned().map(with) + } + + /// Returns the deserialized value of the field, if it exists + pub fn get_deserialized( + &self, + key: impl AsRef, + ) -> Option> { + self.inner.get(key.as_ref()).cloned().map(serde_json::from_value) + } + + /// Removes the deserialized value of the field, if it exists + /// + /// **Note:** this will also remove the value if deserializing it resulted in an error + pub fn remove_deserialized( + &mut self, + key: impl AsRef, + ) -> Option> { + self.inner.remove(key.as_ref()).map(serde_json::from_value) + } + + /// Removes the deserialized value of the field, if it exists. + /// Deserializes the value with the given closure + /// + /// **Note:** this will also remove the value if deserializing it resulted in an error + pub fn remove_with(&mut self, key: impl AsRef, with: F) -> Option + where + F: FnOnce(serde_json::Value) -> V, + { + self.inner.remove(key.as_ref()).map(with) + } + + /// Removes the deserialized value of the field, if it exists and also returns the key + /// + /// **Note:** this will also remove the value if deserializing it resulted in an error + pub fn remove_entry_deserialized( + &mut self, + key: impl AsRef, + ) -> Option<(String, serde_json::Result)> { + self.inner + .remove_entry(key.as_ref()) + .map(|(key, value)| (key, serde_json::from_value(value))) + } + + /// Deserialized this type into another container type + pub fn deserialize_into(self) -> serde_json::Result { + let mut map = Map::with_capacity(self.inner.len()); + map.extend(self); + serde_json::from_value(serde_json::Value::Object(map)) + } +} + +impl Deref for OtherFields { + type Target = BTreeMap; + + #[inline] + fn deref(&self) -> &BTreeMap { + self.as_ref() + } +} + +impl DerefMut for OtherFields { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl AsRef> for OtherFields { + fn as_ref(&self) -> &BTreeMap { + &self.inner + } +} + +impl IntoIterator for OtherFields { + type Item = (String, serde_json::Value); + type IntoIter = std::collections::btree_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} + +impl<'a> IntoIterator for &'a OtherFields { + type Item = (&'a String, &'a serde_json::Value); + type IntoIter = std::collections::btree_map::Iter<'a, String, serde_json::Value>; + + fn into_iter(self) -> Self::IntoIter { + self.as_ref().iter() + } +} diff --git a/crates/rpc-types/src/eth/transaction/receipt.rs b/crates/rpc-types/src/eth/transaction/receipt.rs index 7fae30855eb..557e19316df 100644 --- a/crates/rpc-types/src/eth/transaction/receipt.rs +++ b/crates/rpc-types/src/eth/transaction/receipt.rs @@ -1,4 +1,4 @@ -use crate::Log; +use crate::{other::OtherFields, Log}; use alloy_primitives::{Address, Bloom, B256, U128, U256, U64, U8}; use serde::{Deserialize, Serialize}; @@ -51,4 +51,7 @@ pub struct TransactionReceipt { /// EIP-2718 Transaction type, Some(1) for AccessList transaction, None for Legacy #[serde(rename = "type")] pub transaction_type: U8, + /// Support for arbitrary additional fields. + #[serde(flatten)] + pub other: OtherFields, } From ead6a46c6594c9431a0118a5dcefaa04347e1665 Mon Sep 17 00:00:00 2001 From: evalir Date: Mon, 11 Dec 2023 15:46:05 -0400 Subject: [PATCH 35/37] chore: add support for OtherFields in Transaction (#68) --- crates/rpc-types/src/eth/transaction/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/rpc-types/src/eth/transaction/mod.rs b/crates/rpc-types/src/eth/transaction/mod.rs index 6dfe965d83d..7ae7e404b55 100644 --- a/crates/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc-types/src/eth/transaction/mod.rs @@ -17,6 +17,8 @@ mod typed; pub use tx_type::*; +use crate::other::OtherFields; + /// Transaction object used in RPC #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -71,6 +73,9 @@ pub struct Transaction { /// EIP2718 #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub transaction_type: Option, + /// Arbitrary extra fields. + #[serde(flatten)] + pub other: OtherFields, } #[cfg(test)] @@ -105,6 +110,7 @@ mod tests { max_fee_per_gas: Some(U128::from(21)), max_priority_fee_per_gas: Some(U128::from(22)), max_fee_per_blob_gas: None, + other: Default::default(), }; let serialized = serde_json::to_string(&transaction).unwrap(); assert_eq!( @@ -142,6 +148,7 @@ mod tests { max_fee_per_gas: Some(U128::from(21)), max_priority_fee_per_gas: Some(U128::from(22)), max_fee_per_blob_gas: None, + other: Default::default(), }; let serialized = serde_json::to_string(&transaction).unwrap(); assert_eq!( From 1d825ecdbaa54877813aa372b641181e922d2d86 Mon Sep 17 00:00:00 2001 From: Enrique Ortiz Date: Wed, 13 Dec 2023 18:16:27 -0400 Subject: [PATCH 36/37] chore: turn off reqwest default features --- Cargo.toml | 2 +- crates/providers/src/provider.rs | 4 ++++ crates/rpc-client/src/builder.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 270c5fbde22..ee36dd49dca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ futures = "0.3.29" hyper = "0.14.27" itertools = "0.12" pin-project = "1.1" -reqwest = "0.11.18" +reqwest = { version = "0.11.18", default-features = false } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_with = "3.4" diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 65743acecfb..78865849a05 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -255,6 +255,10 @@ impl Provider { } } + pub fn new_with_client(client: RpcClient) -> Self { + Self { inner: client, from: None } + } + pub fn with_sender(mut self, from: Address) -> Self { self.from = Some(from); self diff --git a/crates/rpc-client/src/builder.rs b/crates/rpc-client/src/builder.rs index f6870ee6fe4..1bbcd2baecc 100644 --- a/crates/rpc-client/src/builder.rs +++ b/crates/rpc-client/src/builder.rs @@ -37,7 +37,7 @@ impl ClientBuilder { /// Create a new [`RpcClient`] with the given transport and the configured /// layers. - fn transport(self, transport: T, is_local: bool) -> RpcClient + pub fn transport(self, transport: T, is_local: bool) -> RpcClient where L: Layer, T: Transport, From f767a997f24b21f396ce660e12507f4fa064b8bb Mon Sep 17 00:00:00 2001 From: evalir Date: Mon, 18 Dec 2023 10:22:38 -0400 Subject: [PATCH 37/37] chore: address provider review comments (#87) * chore: remove self: sync from defs * chore: remove most generics * chore: default for BlockId * chore: remove unnecesary reassignment * chore: auto impl on mut/rc * chore: unnecesary assignment --- crates/providers/src/provider.rs | 196 ++++++++---------------------- crates/rpc-types/src/eth/block.rs | 6 + 2 files changed, 56 insertions(+), 146 deletions(-) diff --git a/crates/providers/src/provider.rs b/crates/providers/src/provider.rs index 78865849a05..27f721a1855 100644 --- a/crates/providers/src/provider.rs +++ b/crates/providers/src/provider.rs @@ -38,26 +38,20 @@ pub struct Provider { /// Once the new Provider trait is stable, this trait will be removed. #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -#[auto_impl(&, Arc, Box)] +#[auto_impl(&, &mut, Rc, Arc, Box)] pub trait TempProvider: Send + Sync { /// Gets the transaction count of the corresponding address. async fn get_transaction_count( &self, address: Address, tag: Option, - ) -> TransportResult - where - Self: Sync; + ) -> TransportResult; /// Gets the last block number available. - async fn get_block_number(&self) -> TransportResult - where - Self: Sync; + async fn get_block_number(&self) -> TransportResult; /// Gets the balance of the account at the specified tag, which defaults to latest. - async fn get_balance(&self, address: Address, tag: Option) -> TransportResult - where - Self: Sync; + async fn get_balance(&self, address: Address, tag: Option) -> TransportResult; /// Gets a block by either its hash, tag, or number, with full transactions or only hashes. async fn get_block(&self, id: BlockId, full: bool) -> TransportResult> { @@ -72,23 +66,17 @@ pub trait TempProvider: Send + Sync { &self, hash: BlockHash, full: bool, - ) -> TransportResult> - where - Self: Sync; + ) -> TransportResult>; /// Gets a block by [BlockNumberOrTag], with full transactions or only hashes. - async fn get_block_by_number + Send + Sync>( + async fn get_block_by_number( &self, - number: B, + number: BlockNumberOrTag, full: bool, - ) -> TransportResult> - where - Self: Sync; + ) -> TransportResult>; /// Gets the chain ID. - async fn get_chain_id(&self) -> TransportResult - where - Self: Sync; + async fn get_chain_id(&self) -> TransportResult; /// Gets the specified storage value from [Address]. async fn get_storage_at( @@ -99,90 +87,56 @@ pub trait TempProvider: Send + Sync { ) -> TransportResult; /// Gets the bytecode located at the corresponding [Address]. - async fn get_code_at + Send + Sync>( - &self, - address: Address, - tag: B, - ) -> TransportResult - where - Self: Sync; + async fn get_code_at(&self, address: Address, tag: BlockId) -> TransportResult; /// Gets a [Transaction] by its [TxHash]. - async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult - where - Self: Sync; + async fn get_transaction_by_hash(&self, hash: TxHash) -> TransportResult; /// Retrieves a [`Vec`] with the given [Filter]. - async fn get_logs(&self, filter: Filter) -> TransportResult> - where - Self: Sync; + async fn get_logs(&self, filter: Filter) -> TransportResult>; /// Gets the accounts in the remote node. This is usually empty unless you're using a local /// node. - async fn get_accounts(&self) -> TransportResult> - where - Self: Sync; + async fn get_accounts(&self) -> TransportResult>; /// Gets the current gas price. - async fn get_gas_price(&self) -> TransportResult - where - Self: Sync; + async fn get_gas_price(&self) -> TransportResult; /// Gets a [TransactionReceipt] if it exists, by its [TxHash]. async fn get_transaction_receipt( &self, hash: TxHash, - ) -> TransportResult> - where - Self: Sync; + ) -> TransportResult>; /// Returns a collection of historical gas information [FeeHistory] which /// can be used to calculate the EIP1559 fields `maxFeePerGas` and `maxPriorityFeePerGas`. - async fn get_fee_history + Send + Sync>( + async fn get_fee_history( &self, block_count: U256, - last_block: B, + last_block: BlockNumberOrTag, reward_percentiles: &[f64], - ) -> TransportResult - where - Self: Sync; + ) -> TransportResult; /// Gets the selected block [BlockNumberOrTag] receipts. async fn get_block_receipts( &self, block: BlockNumberOrTag, - ) -> TransportResult> - where - Self: Sync; + ) -> TransportResult>; /// Gets an uncle block through the tag [BlockId] and index [U64]. - async fn get_uncle + Send + Sync>( - &self, - tag: B, - idx: U64, - ) -> TransportResult> - where - Self: Sync; + async fn get_uncle(&self, tag: BlockId, idx: U64) -> TransportResult>; /// Gets syncing info. - async fn syncing(&self) -> TransportResult - where - Self: Sync; + async fn syncing(&self) -> TransportResult; /// Execute a smart contract call with [CallRequest] without publishing a transaction. - async fn call(&self, tx: CallRequest, block: Option) -> TransportResult - where - Self: Sync; + async fn call(&self, tx: CallRequest, block: Option) -> TransportResult; /// Estimate the gas needed for a transaction. - async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult - where - Self: Sync; + async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult; /// Sends an already-signed transaction. - async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult - where - Self: Sync; + async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult; /// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields. /// Receives an optional [EstimatorFunction] that can be used to modify @@ -190,54 +144,40 @@ pub trait TempProvider: Send + Sync { async fn estimate_eip1559_fees( &self, estimator: Option, - ) -> TransportResult<(U256, U256)> - where - Self: Sync; + ) -> TransportResult<(U256, U256)>; #[cfg(feature = "anvil")] - async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> - where - Self: Sync; + async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()>; async fn get_proof( &self, address: Address, keys: Vec, block: Option, - ) -> TransportResult - where - Self: Sync; + ) -> TransportResult; async fn create_access_list( &self, request: CallRequest, block: Option, - ) -> TransportResult - where - Self: Sync; + ) -> TransportResult; /// Parity trace transaction. async fn trace_transaction( &self, hash: TxHash, - ) -> TransportResult> - where - Self: Sync; + ) -> TransportResult>; async fn debug_trace_transaction( &self, hash: TxHash, trace_options: GethDebugTracingOptions, - ) -> TransportResult - where - Self: Sync; + ) -> TransportResult; async fn trace_block( &self, block: BlockNumberOrTag, - ) -> TransportResult> - where - Self: Sync; + ) -> TransportResult>; async fn raw_request(&self, method: &'static str, params: P) -> TransportResult where @@ -317,12 +257,12 @@ impl TempProvider for Provider { } /// Gets a block by [BlockNumberOrTag], with full transactions or only hashes. - async fn get_block_by_number + Send + Sync>( + async fn get_block_by_number( &self, - number: B, + number: BlockNumberOrTag, full: bool, ) -> TransportResult> { - self.inner.prepare("eth_getBlockByNumber", (number.into(), full)).await + self.inner.prepare("eth_getBlockByNumber", (number, full)).await } /// Gets the chain ID. @@ -346,12 +286,8 @@ impl TempProvider for Provider { } /// Gets the bytecode located at the corresponding [Address]. - async fn get_code_at + Send + Sync>( - &self, - address: Address, - tag: B, - ) -> TransportResult { - self.inner.prepare("eth_getCode", (address, tag.into())).await + async fn get_code_at(&self, address: Address, tag: BlockId) -> TransportResult { + self.inner.prepare("eth_getCode", (address, tag)).await } /// Gets a [Transaction] by its [TxHash]. @@ -385,15 +321,13 @@ impl TempProvider for Provider { /// Returns a collection of historical gas information [FeeHistory] which /// can be used to calculate the EIP1559 fields `maxFeePerGas` and `maxPriorityFeePerGas`. - async fn get_fee_history + Send + Sync>( + async fn get_fee_history( &self, block_count: U256, - last_block: B, + last_block: BlockNumberOrTag, reward_percentiles: &[f64], ) -> TransportResult { - self.inner - .prepare("eth_feeHistory", (block_count, last_block.into(), reward_percentiles)) - .await + self.inner.prepare("eth_feeHistory", (block_count, last_block, reward_percentiles)).await } /// Gets the selected block [BlockNumberOrTag] receipts. @@ -401,19 +335,12 @@ impl TempProvider for Provider { &self, block: BlockNumberOrTag, ) -> TransportResult> - where - Self: Sync, - { +where { self.inner.prepare("eth_getBlockReceipts", block).await } /// Gets an uncle block through the tag [BlockId] and index [U64]. - async fn get_uncle + Send + Sync>( - &self, - tag: B, - idx: U64, - ) -> TransportResult> { - let tag = tag.into(); + async fn get_uncle(&self, tag: BlockId, idx: U64) -> TransportResult> { match tag { BlockId::Hash(hash) => { self.inner.prepare("eth_getUncleByBlockHashAndIndex", (hash, idx)).await @@ -431,27 +358,20 @@ impl TempProvider for Provider { /// Execute a smart contract call with [CallRequest] without publishing a transaction. async fn call(&self, tx: CallRequest, block: Option) -> TransportResult { - self.inner - .prepare("eth_call", (tx, block.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)))) - .await + self.inner.prepare("eth_call", (tx, block.unwrap_or_default())).await } /// Estimate the gas needed for a transaction. async fn estimate_gas(&self, tx: CallRequest, block: Option) -> TransportResult { if let Some(block_id) = block { - let params = (tx, block_id); - self.inner.prepare("eth_estimateGas", params).await + self.inner.prepare("eth_estimateGas", (tx, block_id)).await } else { - let params = tx; - self.inner.prepare("eth_estimateGas", params).await + self.inner.prepare("eth_estimateGas", (tx,)).await } } /// Sends an already-signed transaction. - async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult - where - Self: Sync, - { + async fn send_raw_transaction(&self, tx: Bytes) -> TransportResult { self.inner.prepare("eth_sendRawTransaction", tx).await } @@ -517,10 +437,7 @@ impl TempProvider for Provider { &self, request: CallRequest, block: Option, - ) -> TransportResult - where - Self: Sync, - { + ) -> TransportResult { self.inner .prepare( "eth_createAccessList", @@ -533,10 +450,7 @@ impl TempProvider for Provider { async fn trace_transaction( &self, hash: TxHash, - ) -> TransportResult> - where - Self: Sync, - { + ) -> TransportResult> { self.inner.prepare("trace_transaction", vec![hash]).await } @@ -544,20 +458,14 @@ impl TempProvider for Provider { &self, hash: TxHash, trace_options: GethDebugTracingOptions, - ) -> TransportResult - where - Self: Sync, - { + ) -> TransportResult { self.inner.prepare("debug_traceTransaction", (hash, trace_options)).await } async fn trace_block( &self, block: BlockNumberOrTag, - ) -> TransportResult> - where - Self: Sync, - { + ) -> TransportResult> { self.inner.prepare("trace_block", block).await } @@ -567,17 +475,13 @@ impl TempProvider for Provider { where P: Serialize + Send + Sync + Clone, R: Serialize + DeserializeOwned + Send + Sync + Unpin + 'static, - Self: Sync, { let res: R = self.inner.prepare(method, ¶ms).await?; Ok(res) } #[cfg(feature = "anvil")] - async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> - where - Self: Sync, - { + async fn set_code(&self, address: Address, code: &'static str) -> TransportResult<()> { self.inner.prepare("anvil_setCode", (address, code)).await } } diff --git a/crates/rpc-types/src/eth/block.rs b/crates/rpc-types/src/eth/block.rs index 63c15cafccb..d29f966fb6d 100644 --- a/crates/rpc-types/src/eth/block.rs +++ b/crates/rpc-types/src/eth/block.rs @@ -568,6 +568,12 @@ impl BlockId { } } +impl Default for BlockId { + fn default() -> Self { + BlockId::Number(BlockNumberOrTag::Latest) + } +} + impl From for BlockId { fn from(num: u64) -> Self { BlockNumberOrTag::Number(num).into()