From 467024445f10d6ebc76b4d6882b24686f858a1c0 Mon Sep 17 00:00:00 2001 From: Guy Nir Date: Sun, 26 Jan 2025 16:45:53 +0200 Subject: [PATCH] feat(starknet_l1_provider): add baselayer interface to query ethereum gas price --- .../papyrus_base_layer/src/base_layer_test.rs | 34 ++++++++ .../src/ethereum_base_layer_contract.rs | 78 ++++++++++++++++++- crates/papyrus_base_layer/src/lib.rs | 26 +++++++ 3 files changed, 136 insertions(+), 2 deletions(-) diff --git a/crates/papyrus_base_layer/src/base_layer_test.rs b/crates/papyrus_base_layer/src/base_layer_test.rs index da19c23880..07b128f639 100644 --- a/crates/papyrus_base_layer/src/base_layer_test.rs +++ b/crates/papyrus_base_layer/src/base_layer_test.rs @@ -78,3 +78,37 @@ async fn get_proved_block_at_unknown_block_number() { .contains("BlockOutOfRangeError") ); } + +#[tokio::test] +async fn get_gas_price_and_timestamps() { + // if !in_ci() { + // return; + // } + + let (node_handle, starknet_contract_address) = get_test_ethereum_node(); + let node_url = node_handle.0.endpoint().parse().unwrap(); + let contract = ethereum_base_layer_contract(node_url, starknet_contract_address); + + let block_number = 30; + let gas_price = contract.get_block_gas_price(block_number).await.unwrap().unwrap(); + + // TODO(guyn): Figure out why the data gas is None. + let data_gas_price = contract.get_block_data_gas_price(block_number).await.unwrap(); + + let timestamp = contract.get_block_timestamp(block_number).await.unwrap().unwrap(); + + // TODO(guyn): Figure out how these numbers are calculated, instead of just printing and testing + // against what we got. Use this println! to get the numbers to put into the asserts. + println!( + "Gas price: {}, data gas price: {:?}, timestamp: {}", + gas_price, data_gas_price, timestamp + ); + assert_eq!(gas_price, 20168195); + assert_eq!(data_gas_price, None); + assert_eq!(timestamp, 1676992456); + + let price_sample = contract.get_price_sample(block_number).await.unwrap().unwrap(); + assert_eq!(price_sample.timestamp, timestamp); + assert_eq!(price_sample.base_fee_per_gas, gas_price); + assert_eq!(price_sample.blob_fee, 0); +} diff --git a/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs b/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs index 0fa9e91d25..2671f90c70 100644 --- a/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs +++ b/crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs @@ -7,7 +7,12 @@ use alloy_json_rpc::RpcError; use alloy_primitives::Address as EthereumContractAddress; use alloy_provider::network::Ethereum; use alloy_provider::{Provider, ProviderBuilder, RootProvider}; -use alloy_rpc_types_eth::{BlockId, BlockTransactionsKind, Filter as EthEventFilter}; +use alloy_rpc_types_eth::{ + BlockId, + BlockNumberOrTag, + BlockTransactionsKind, + Filter as EthEventFilter, +}; use alloy_sol_types::{sol, sol_data}; use alloy_transport::TransportErrorKind; use alloy_transport_http::{Client, Http}; @@ -21,7 +26,7 @@ use starknet_api::StarknetApiError; use url::Url; use validator::Validate; -use crate::{BaseLayerContract, L1BlockNumber, L1BlockReference, L1Event}; +use crate::{BaseLayerContract, L1BlockNumber, L1BlockReference, L1Event, PriceSample}; pub type EthereumBaseLayerResult = Result; @@ -131,6 +136,75 @@ impl BaseLayerContract for EthereumBaseLayerContract { hash: block.header.hash.0, })) } + + async fn get_block_timestamp(&self, block_number: u64) -> EthereumBaseLayerResult> { + let block = self + .contract + .provider() + .get_block( + BlockId::Number(BlockNumberOrTag::Number(block_number)), + BlockTransactionsKind::Hashes, + ) + .await?; + Ok(block.map(|block| block.header.timestamp)) + } + + async fn get_block_gas_price( + &self, + block_number: u64, + ) -> EthereumBaseLayerResult> { + let block = self + .contract + .provider() + .get_block( + BlockId::Number(BlockNumberOrTag::Number(block_number)), + BlockTransactionsKind::Hashes, + ) + .await?; + Ok(block.and_then(|block| block.header.base_fee_per_gas)) + } + + async fn get_block_data_gas_price( + &self, + block_number: u64, + ) -> EthereumBaseLayerResult> { + let block = self + .contract + .provider() + .get_block( + BlockId::Number(BlockNumberOrTag::Number(block_number)), + BlockTransactionsKind::Hashes, + ) + .await?; + Ok(block.and_then(|block| block.header.blob_fee())) + } + + // Combine the above three functions into one call, which is more efficient. + async fn get_price_sample( + &self, + block_number: u64, + ) -> EthereumBaseLayerResult> { + let block = self + .contract + .provider() + .get_block( + BlockId::Number(BlockNumberOrTag::Number(block_number)), + BlockTransactionsKind::Hashes, + ) + .await?; + if let Some(block) = block { + match (block.header.timestamp, block.header.base_fee_per_gas) { + (timestamp, Some(base_fee_per_gas)) => Ok(Some(PriceSample { + timestamp, + base_fee_per_gas, + blob_fee: block.header.blob_fee().unwrap_or(0), + })), + _ => Ok(None), + } + } else { + Ok(None) + } + } } #[derive(thiserror::Error, Debug)] diff --git a/crates/papyrus_base_layer/src/lib.rs b/crates/papyrus_base_layer/src/lib.rs index a3ac9e3070..3822b2156a 100644 --- a/crates/papyrus_base_layer/src/lib.rs +++ b/crates/papyrus_base_layer/src/lib.rs @@ -60,6 +60,32 @@ pub trait BaseLayerContract { block_range: RangeInclusive, event_identifiers: &[&str], ) -> Result, Self::Error>; + + async fn get_block_timestamp(&self, block_number: u64) -> Result, Self::Error>; + async fn get_block_gas_price(&self, block_number: u64) -> Result, Self::Error>; + async fn get_block_data_gas_price( + &self, + block_number: u64, + ) -> Result, Self::Error>; + async fn get_price_sample(&self, block_number: u64) + -> Result, Self::Error>; +} + +/// A struct the holds together the data on the baselayer's gas prices, for a given timestamp. +pub struct PriceSample { + timestamp: u64, + base_fee_per_gas: u128, + blob_fee: u128, +} + +impl Display for PriceSample { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "PriceSample {{ timestamp: {}, base_fee_per_gas: {}, blob_fee: {} }}", + self.timestamp, self.base_fee_per_gas, self.blob_fee + ) + } } /// Reference to an L1 block, extend as needed.