diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index 7779c58..e96732d 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -714,6 +714,12 @@ impl<'a, T: Platform> ResolverApi for BlockPinnedResolver<'a, T> { .await } + async fn block_base_fee(&self, number: BlockNumberOrTag) -> anyhow::Result { + self.node + .block_base_fee(self.resolve_block_number_or_tag(number)) + .await + } + async fn block_hash( &self, number: BlockNumberOrTag, diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index 32b96b7..890d521 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -533,6 +533,7 @@ impl CalldataToken { const GAS_LIMIT_VARIABLE: &str = "$GAS_LIMIT"; const COINBASE_VARIABLE: &str = "$COINBASE"; const DIFFICULTY_VARIABLE: &str = "$DIFFICULTY"; + const BLOCK_BASE_FEE_VARIABLE: &str = "$BASE_FEE"; const BLOCK_HASH_VARIABLE_PREFIX: &str = "$BLOCK_HASH"; const BLOCK_NUMBER_VARIABLE: &str = "$BLOCK_NUMBER"; const BLOCK_TIMESTAMP_VARIABLE: &str = "$BLOCK_TIMESTAMP"; @@ -601,6 +602,11 @@ impl> CalldataToken { let block_difficulty = resolver.block_difficulty(BlockNumberOrTag::Latest).await?; Ok(block_difficulty) + } else if item == Self::BLOCK_BASE_FEE_VARIABLE { + resolver + .block_base_fee(BlockNumberOrTag::Latest) + .await + .map(U256::from) } else if item.starts_with(Self::BLOCK_HASH_VARIABLE_PREFIX) { let offset: u64 = item .split(':') @@ -671,48 +677,43 @@ impl<'de> Deserialize<'de> for EtherValue { mod tests { use super::*; - use alloy::json_abi::JsonAbi; - use alloy_primitives::address; + use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi}; + use alloy_primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, address}; use alloy_sol_types::SolValue; use std::collections::HashMap; struct MockResolver; impl ResolverApi for MockResolver { - async fn chain_id(&self) -> anyhow::Result { + async fn chain_id(&self) -> anyhow::Result { Ok(0x123) } - async fn block_gas_limit(&self, _: alloy::eips::BlockNumberOrTag) -> anyhow::Result { + async fn block_gas_limit(&self, _: BlockNumberOrTag) -> anyhow::Result { Ok(0x1234) } - async fn block_coinbase( - &self, - _: alloy::eips::BlockNumberOrTag, - ) -> anyhow::Result
{ + async fn block_coinbase(&self, _: BlockNumberOrTag) -> anyhow::Result
{ Ok(Address::ZERO) } - async fn block_difficulty(&self, _: alloy::eips::BlockNumberOrTag) -> anyhow::Result { + async fn block_difficulty(&self, _: BlockNumberOrTag) -> anyhow::Result { Ok(U256::from(0x12345u128)) } - async fn block_hash( - &self, - _: alloy::eips::BlockNumberOrTag, - ) -> anyhow::Result { + async fn block_base_fee(&self, _: BlockNumberOrTag) -> anyhow::Result { + Ok(0x100) + } + + async fn block_hash(&self, _: BlockNumberOrTag) -> anyhow::Result { Ok([0xEE; 32].into()) } - async fn block_timestamp( - &self, - _: alloy::eips::BlockNumberOrTag, - ) -> anyhow::Result { + async fn block_timestamp(&self, _: BlockNumberOrTag) -> anyhow::Result { Ok(0x123456) } - async fn last_block_number(&self) -> anyhow::Result { + async fn last_block_number(&self) -> anyhow::Result { Ok(0x1234567) } } @@ -945,6 +946,26 @@ mod tests { ) } + #[tokio::test] + async fn resolver_can_resolve_block_base_fee_variable() { + // Arrange + let input = "$BASE_FEE"; + + // Act + let resolved = resolve_calldata_item(input, &Default::default(), &MockResolver).await; + + // Assert + let resolved = resolved.expect("Failed to resolve argument"); + assert_eq!( + resolved, + MockResolver + .block_base_fee(Default::default()) + .await + .map(U256::from) + .unwrap() + ) + } + #[tokio::test] async fn resolver_can_resolve_block_hash_variable() { // Arrange diff --git a/crates/format/src/traits.rs b/crates/format/src/traits.rs index 3d302f9..d8721e7 100644 --- a/crates/format/src/traits.rs +++ b/crates/format/src/traits.rs @@ -19,6 +19,9 @@ pub trait ResolverApi { /// Returns the difficulty of the specified block. fn block_difficulty(&self, number: BlockNumberOrTag) -> impl Future>; + /// Returns the base fee of the specified block. + fn block_base_fee(&self, number: BlockNumberOrTag) -> impl Future>; + /// Returns the hash of the specified block. fn block_hash(&self, number: BlockNumberOrTag) -> impl Future>; diff --git a/crates/node/src/geth.rs b/crates/node/src/geth.rs index 9ba595a..59c7b4a 100644 --- a/crates/node/src/geth.rs +++ b/crates/node/src/geth.rs @@ -29,6 +29,7 @@ use alloy::{ }, signers::local::PrivateKeySigner, }; +use anyhow::Context; use tracing::{Instrument, Level}; use revive_dt_common::{fs::clear_directory, futures::poll}; @@ -412,6 +413,21 @@ impl ResolverApi for GethNode { .map(|block| block.header.difficulty) } + #[tracing::instrument(skip_all, fields(geth_node_id = self.id))] + async fn block_base_fee(&self, number: BlockNumberOrTag) -> anyhow::Result { + self.provider() + .await? + .get_block_by_number(number) + .await? + .ok_or(anyhow::Error::msg("Blockchain has no blocks")) + .and_then(|block| { + block + .header + .base_fee_per_gas + .context("Failed to get the base fee per gas") + }) + } + #[tracing::instrument(skip_all, fields(geth_node_id = self.id))] async fn block_hash(&self, number: BlockNumberOrTag) -> anyhow::Result { self.provider() diff --git a/crates/node/src/kitchensink.rs b/crates/node/src/kitchensink.rs index 126b972..917c75d 100644 --- a/crates/node/src/kitchensink.rs +++ b/crates/node/src/kitchensink.rs @@ -30,6 +30,7 @@ use alloy::{ }, signers::local::PrivateKeySigner, }; +use anyhow::Context; use revive_dt_common::fs::clear_directory; use revive_dt_format::traits::ResolverApi; use serde::{Deserialize, Serialize}; @@ -468,6 +469,21 @@ impl ResolverApi for KitchensinkNode { .map(|block| block.header.difficulty) } + #[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))] + async fn block_base_fee(&self, number: BlockNumberOrTag) -> anyhow::Result { + self.provider() + .await? + .get_block_by_number(number) + .await? + .ok_or(anyhow::Error::msg("Blockchain has no blocks")) + .and_then(|block| { + block + .header + .base_fee_per_gas + .context("Failed to get the base fee per gas") + }) + } + #[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))] async fn block_hash(&self, number: BlockNumberOrTag) -> anyhow::Result { self.provider()