diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 5e197aa3250b..52c7511cac4f 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -155,6 +155,12 @@ impl SealedBlock { } } +impl From for Block { + fn from(block: SealedBlock) -> Self { + block.unseal() + } +} + impl Deref for SealedBlock { type Target = SealedHeader; fn deref(&self) -> &Self::Target { diff --git a/crates/rpc/rpc-api/src/eth.rs b/crates/rpc/rpc-api/src/eth.rs index b7116324a35b..0b9921f11f93 100644 --- a/crates/rpc/rpc-api/src/eth.rs +++ b/crates/rpc/rpc-api/src/eth.rs @@ -176,6 +176,10 @@ pub trait EthApi { #[method(name = "eth_gasPrice")] async fn gas_price(&self) -> Result; + /// Introduced in EIP-1159, returns suggestion for the priority for dynamic fee transactions. + #[method(name = "eth_maxPriorityFeePerGas")] + async fn max_priority_fee_per_gas(&self) -> Result; + /// Returns the Transaction fee history /// /// Introduced in EIP-1159 for getting information on the appropriate priority fee to use. @@ -191,10 +195,6 @@ pub trait EthApi { reward_percentiles: Option>, ) -> Result; - /// Returns the current maxPriorityFeePerGas per gas in wei. - #[method(name = "eth_maxPriorityFeePerGas")] - async fn max_priority_fee_per_gas(&self) -> Result; - /// Returns whether the client is actively mining new blocks. #[method(name = "eth_mining")] async fn is_mining(&self) -> Result; diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index 76d7c2c27bd9..8810f801d232 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -97,14 +97,14 @@ where EthApiClient::send_transaction(client, transaction_request).await.unwrap_err(); EthApiClient::hashrate(client).await.unwrap(); EthApiClient::submit_hashrate(client, U256::default(), H256::default()).await.unwrap(); + EthApiClient::gas_price(client).await.unwrap(); + EthApiClient::max_priority_fee_per_gas(client).await.unwrap(); // Unimplemented assert!(is_unimplemented( EthApiClient::get_proof(client, address, vec![], None).await.err().unwrap() )); assert!(is_unimplemented(EthApiClient::author(client).await.err().unwrap())); - assert!(is_unimplemented(EthApiClient::gas_price(client).await.err().unwrap())); - assert!(is_unimplemented(EthApiClient::max_priority_fee_per_gas(client).await.err().unwrap())); assert!(is_unimplemented(EthApiClient::is_mining(client).await.err().unwrap())); assert!(is_unimplemented(EthApiClient::get_work(client).await.err().unwrap())); assert!(is_unimplemented( diff --git a/crates/rpc/rpc/src/eth/api/block.rs b/crates/rpc/rpc/src/eth/api/block.rs index b01155f4e3d3..3419085640bb 100644 --- a/crates/rpc/rpc/src/eth/api/block.rs +++ b/crates/rpc/rpc/src/eth/api/block.rs @@ -56,15 +56,19 @@ where Ok(self.cache().get_block_transactions(block_hash).await?.map(|txs| txs.len())) } - /// Returns the rpc block object for the given block id. - /// - /// If `full` is true, the block object will contain all transaction objects, otherwise it will - /// only contain the transaction hashes. + /// Returns the block header for the given block id. + pub(crate) async fn header( + &self, + block_id: impl Into, + ) -> EthResult> { + Ok(self.block(block_id).await?.map(|block| block.header)) + } + + /// Returns the block object for the given block id. pub(crate) async fn block( &self, block_id: impl Into, - full: bool, - ) -> EthResult> { + ) -> EthResult> { let block_id = block_id.into(); // TODO support pending block let block_hash = match self.client().block_hash_for_id(block_id)? { @@ -72,14 +76,27 @@ where None => return Ok(None), }; - let block = match self.cache().get_block(block_hash).await? { + Ok(self.cache().get_block(block_hash).await?.map(|block| block.seal(block_hash))) + } + + /// Returns the populated rpc block object for the given block id. + /// + /// If `full` is true, the block object will contain all transaction objects, otherwise it will + /// only contain the transaction hashes. + pub(crate) async fn rpc_block( + &self, + block_id: impl Into, + full: bool, + ) -> EthResult> { + let block = match self.block(block_id).await? { Some(block) => block, None => return Ok(None), }; - + let block_hash = block.hash; let total_difficulty = self.client().header_td(&block_hash)?.ok_or(EthApiError::UnknownBlockNumber)?; - let block = Block::from_block(block, total_difficulty, full.into(), Some(block_hash))?; + let block = + Block::from_block(block.into(), total_difficulty, full.into(), Some(block_hash))?; Ok(Some(block.into())) } } diff --git a/crates/rpc/rpc/src/eth/api/fees.rs b/crates/rpc/rpc/src/eth/api/fees.rs index 1ad8495aa45a..5027a7643209 100644 --- a/crates/rpc/rpc/src/eth/api/fees.rs +++ b/crates/rpc/rpc/src/eth/api/fees.rs @@ -5,7 +5,7 @@ use crate::{ EthApi, }; use reth_network_api::NetworkInfo; -use reth_primitives::{BlockId, U256}; +use reth_primitives::{BlockId, BlockNumberOrTag, U256}; use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderFactory}; use reth_rpc_types::{FeeHistory, FeeHistoryCacheItem, TxGasAndReward}; use reth_transaction_pool::TransactionPool; @@ -17,6 +17,23 @@ where Client: BlockProvider + StateProviderFactory + EvmEnvProvider + 'static, Network: NetworkInfo + Send + Sync + 'static, { + /// Returns a suggestion for a gas price for legacy transactions. + /// + /// See also: + pub(crate) async fn gas_price(&self) -> EthResult { + let header = self.block(BlockNumberOrTag::Latest); + let suggested_tip = self.suggested_priority_fee(); + let (header, suggested_tip) = futures::try_join!(header, suggested_tip)?; + let base_fee = header.and_then(|h| h.base_fee_per_gas).unwrap_or_default(); + Ok(suggested_tip + U256::from(base_fee)) + } + + /// Returns a suggestion for the priority fee (the tip) + pub(crate) async fn suggested_priority_fee(&self) -> EthResult { + // TODO: properly implement sampling https://github.com/ethereum/pm/issues/328#issuecomment-853234014 + Ok(U256::from(1e9 as u64)) + } + /// Reports the fee history, for the given amount of blocks, up until the newest block /// provided. pub(crate) async fn fee_history( diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index 613828c2a758..dad1060221cf 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -73,7 +73,7 @@ where /// Handler for: `eth_getBlockByHash` async fn block_by_hash(&self, hash: H256, full: bool) -> Result> { trace!(target: "rpc::eth", ?hash, ?full, "Serving eth_getBlockByHash"); - Ok(EthApi::block(self, hash, full).await?) + Ok(EthApi::rpc_block(self, hash, full).await?) } /// Handler for: `eth_getBlockByNumber` @@ -83,7 +83,7 @@ where full: bool, ) -> Result> { trace!(target: "rpc::eth", ?number, ?full, "Serving eth_getBlockByNumber"); - Ok(EthApi::block(self, number, full).await?) + Ok(EthApi::rpc_block(self, number, full).await?) } /// Handler for: `eth_getBlockTransactionCountByHash` @@ -248,7 +248,14 @@ where /// Handler for: `eth_gasPrice` async fn gas_price(&self) -> Result { - Err(internal_rpc_err("unimplemented")) + trace!(target: "rpc::eth", "Serving eth_gasPrice"); + return Ok(EthApi::gas_price(self).await?) + } + + /// Handler for: `eth_maxPriorityFeePerGas` + async fn max_priority_fee_per_gas(&self) -> Result { + trace!(target: "rpc::eth", "Serving eth_maxPriorityFeePerGas"); + return Ok(EthApi::suggested_priority_fee(self).await?) } // FeeHistory is calculated based on lazy evaluation of fees for historical blocks, and further @@ -271,11 +278,6 @@ where .await?) } - /// Handler for: `eth_maxPriorityFeePerGas` - async fn max_priority_fee_per_gas(&self) -> Result { - Err(internal_rpc_err("unimplemented")) - } - /// Handler for: `eth_mining` async fn is_mining(&self) -> Result { Err(internal_rpc_err("unimplemented"))