use alloy_consensus::BlockHeader;
use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_BLOBS_PER_BLOCK_DENCUN};
use alloy_eips::eip7840::BlobParams;
use alloy_network::{BlockResponse, Network};
use alloy_primitives::U256;
use alloy_provider::Provider;
use alloy_rpc_types::BlockNumberOrTag;
use crate::errors::RpcError;
use crate::types::gas::{BlobCount, BlobGasPrice};
pub async fn get_blob_base_fee<N, P>(provider: &P) -> Result<BlobGasPrice, RpcError>
where
N: Network,
P: Provider<N>,
{
let latest_block_number = provider
.get_block_number()
.await
.map_err(RpcError::get_block_number_failed)?;
let block = provider
.get_block_by_number(BlockNumberOrTag::Latest)
.await
.map_err(|e| RpcError::get_block_failed(latest_block_number, e))?
.ok_or_else(|| RpcError::BlockNotFound {
block_number: latest_block_number,
})?;
let blob_base_fee = block.header().blob_fee(BlobParams::cancun()).unwrap_or(0);
Ok(BlobGasPrice::from(blob_base_fee))
}
pub async fn get_blob_base_fee_at_block<N, P>(
provider: &P,
block_number: u64,
) -> Result<BlobGasPrice, RpcError>
where
N: Network,
P: Provider<N>,
{
let block = provider
.get_block_by_number(BlockNumberOrTag::Number(block_number))
.await
.map_err(|e| RpcError::get_block_failed(block_number, e))?
.ok_or_else(|| RpcError::BlockNotFound { block_number })?;
let blob_base_fee = block.header().blob_fee(BlobParams::cancun()).unwrap_or(0);
Ok(BlobGasPrice::from(blob_base_fee))
}
pub async fn estimate_blob_cost<N, P>(provider: &P, blob_count: BlobCount) -> Result<U256, RpcError>
where
N: Network,
P: Provider<N>,
{
let blob_base_fee = get_blob_base_fee(provider).await?;
Ok(blob_base_fee.cost_for_blobs(blob_count))
}
pub const fn calculate_blob_gas(blob_count: BlobCount) -> u64 {
blob_count.as_usize() as u64 * DATA_GAS_PER_BLOB
}
pub const fn max_blob_gas_per_block() -> u64 {
MAX_BLOBS_PER_BLOCK_DENCUN as u64 * DATA_GAS_PER_BLOB
}
pub fn estimate_total_tx_cost(
execution_gas: u64,
gas_price: U256,
blob_count: BlobCount,
blob_gas_price: BlobGasPrice,
) -> U256 {
let execution_cost = U256::from(execution_gas).saturating_mul(gas_price);
let blob_cost = blob_gas_price.cost_for_blobs(blob_count);
execution_cost.saturating_add(blob_cost)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_blob_gas() {
assert_eq!(calculate_blob_gas(BlobCount::new(0)), 0);
assert_eq!(calculate_blob_gas(BlobCount::new(1)), 131_072);
assert_eq!(calculate_blob_gas(BlobCount::new(2)), 262_144);
assert_eq!(calculate_blob_gas(BlobCount::new(6)), 786_432);
}
#[test]
fn test_max_blob_gas_per_block() {
assert_eq!(max_blob_gas_per_block(), 786_432);
}
#[test]
fn test_estimate_total_tx_cost() {
let execution_gas = 21_000u64;
let gas_price = U256::from(30_000_000_000u64); let blob_count = BlobCount::new(2);
let blob_gas_price = BlobGasPrice::from_gwei(1);
let total = estimate_total_tx_cost(execution_gas, gas_price, blob_count, blob_gas_price);
let expected_execution = U256::from(630_000_000_000_000u64);
let expected_blob = U256::from(262_144_000_000_000u64);
assert_eq!(total, expected_execution + expected_blob);
}
#[test]
fn test_estimate_total_tx_cost_no_blobs() {
let total = estimate_total_tx_cost(
21_000,
U256::from(30_000_000_000u64),
BlobCount::ZERO,
BlobGasPrice::ZERO,
);
assert_eq!(total, U256::from(630_000_000_000_000u64));
}
}