Skip to content

Commit

Permalink
feat(starknet_l1_provider): add baselayer interface to query ethereum…
Browse files Browse the repository at this point in the history
… gas price
  • Loading branch information
guy-starkware committed Feb 2, 2025
1 parent b830dbb commit 4670244
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 2 deletions.
34 changes: 34 additions & 0 deletions crates/papyrus_base_layer/src/base_layer_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
78 changes: 76 additions & 2 deletions crates/papyrus_base_layer/src/ethereum_base_layer_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<T> = Result<T, EthereumBaseLayerError>;

Expand Down Expand Up @@ -131,6 +136,75 @@ impl BaseLayerContract for EthereumBaseLayerContract {
hash: block.header.hash.0,
}))
}

async fn get_block_timestamp(&self, block_number: u64) -> EthereumBaseLayerResult<Option<u64>> {
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<Option<u128>> {
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<Option<u128>> {
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<Option<PriceSample>> {
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)]
Expand Down
26 changes: 26 additions & 0 deletions crates/papyrus_base_layer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ pub trait BaseLayerContract {
block_range: RangeInclusive<L1BlockNumber>,
event_identifiers: &[&str],
) -> Result<Vec<L1Event>, Self::Error>;

async fn get_block_timestamp(&self, block_number: u64) -> Result<Option<u64>, Self::Error>;
async fn get_block_gas_price(&self, block_number: u64) -> Result<Option<u128>, Self::Error>;
async fn get_block_data_gas_price(
&self,
block_number: u64,
) -> Result<Option<u128>, Self::Error>;
async fn get_price_sample(&self, block_number: u64)
-> Result<Option<PriceSample>, 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.
Expand Down

0 comments on commit 4670244

Please sign in to comment.