use primitives::{
eip4844::{GAS_PER_BLOB, MIN_BLOB_GASPRICE},
eip7918,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct BlobExcessGasAndPrice {
pub excess_blob_gas: u64,
pub blob_gasprice: u128,
}
impl BlobExcessGasAndPrice {
pub fn new(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> Self {
let blob_gasprice = calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction);
Self {
excess_blob_gas,
blob_gasprice,
}
}
#[deprecated(
note = "Use `calc_excess_blob_gas` and `BlobExcessGasAndPrice::new` instead. Only works for forks before Osaka."
)]
pub fn from_parent_and_target(
parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
parent_target_blob_gas_per_block: u64,
blob_base_fee_update_fraction: u64,
) -> Self {
Self::new(
calc_excess_blob_gas(
parent_excess_blob_gas,
parent_blob_gas_used,
parent_target_blob_gas_per_block,
),
blob_base_fee_update_fraction,
)
}
}
#[inline]
pub fn calc_excess_blob_gas(
parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
parent_target_blob_gas_per_block: u64,
) -> u64 {
calc_excess_blob_gas_osaka(
parent_excess_blob_gas,
parent_blob_gas_used,
parent_target_blob_gas_per_block,
false,
0,
0,
0,
0,
0,
)
}
#[allow(clippy::too_many_arguments)]
#[inline]
pub fn calc_excess_blob_gas_osaka(
parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
parent_target_blob_gas_per_block: u64,
is_osaka: bool,
parent_base_fee_per_gas: u64,
parent_blob_base_fee_per_gas: u64,
parent_blob_base_fee_update_fraction: u64,
max_blob_count: u64,
target_blob_count: u64,
) -> u64 {
let excess_and_used = parent_excess_blob_gas.saturating_add(parent_blob_gas_used);
if is_osaka {
if excess_and_used < parent_target_blob_gas_per_block {
return 0;
}
if (eip7918::BLOB_BASE_COST.saturating_mul(parent_base_fee_per_gas) as u128)
> (GAS_PER_BLOB as u128).saturating_mul(get_base_fee_per_blob_gas(
parent_blob_base_fee_per_gas,
parent_blob_base_fee_update_fraction,
))
{
return excess_and_used.saturating_add(
parent_blob_gas_used.saturating_mul(max_blob_count - target_blob_count)
/ max_blob_count,
);
}
}
excess_and_used.saturating_sub(parent_target_blob_gas_per_block)
}
#[inline]
pub fn calc_blob_gasprice(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
fake_exponential(
MIN_BLOB_GASPRICE,
excess_blob_gas,
blob_base_fee_update_fraction,
)
}
pub fn get_base_fee_per_blob_gas(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> u128 {
calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction)
}
#[inline]
pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
assert_ne!(denominator, 0, "attempt to divide by zero");
let factor = factor as u128;
let numerator = numerator as u128;
let denominator = denominator as u128;
let mut i = 1;
let mut output = 0;
let mut numerator_accum = factor * denominator;
while numerator_accum > 0 {
output += numerator_accum;
numerator_accum = (numerator_accum * numerator) / (denominator * i);
i += 1;
}
output / denominator
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::eip4844::{
self, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, GAS_PER_BLOB,
TARGET_BLOB_GAS_PER_BLOCK_CANCUN as TARGET_BLOB_GAS_PER_BLOCK,
};
#[test]
fn test_calc_excess_blob_gas() {
for t @ &(excess, blobs, expected) in &[
(0, 0, 0),
(0, 1, 0),
(0, TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, 0),
(
0,
(TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
GAS_PER_BLOB,
),
(
1,
(TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1,
GAS_PER_BLOB + 1,
),
(
1,
(TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 2,
2 * GAS_PER_BLOB + 1,
),
(
TARGET_BLOB_GAS_PER_BLOCK,
TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB,
TARGET_BLOB_GAS_PER_BLOCK,
),
(
TARGET_BLOB_GAS_PER_BLOCK,
(TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
TARGET_BLOB_GAS_PER_BLOCK - GAS_PER_BLOB,
),
(
TARGET_BLOB_GAS_PER_BLOCK,
(TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 2,
TARGET_BLOB_GAS_PER_BLOCK - (2 * GAS_PER_BLOB),
),
(
GAS_PER_BLOB - 1,
(TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1,
0,
),
] {
let actual = calc_excess_blob_gas(
excess,
blobs * GAS_PER_BLOB,
eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN,
);
assert_eq!(actual, expected, "test: {t:?}");
}
}
#[test]
fn test_calc_blob_fee_cancun() {
let blob_fee_vectors = &[
(0, 1),
(2314057, 1),
(2314058, 2),
(10 * 1024 * 1024, 23),
(148099578, 18446739238971471609), (148099579, 18446744762204311910), (161087488, 902580055246494526580),
];
for &(excess, expected) in blob_fee_vectors {
let actual = calc_blob_gasprice(excess, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN);
assert_eq!(actual, expected, "test: {excess}");
}
}
#[test]
fn fake_exp() {
for t @ &(factor, numerator, denominator, expected) in &[
(1u64, 0u64, 1u64, 1u128),
(38493, 0, 1000, 38493),
(0, 1234, 2345, 0),
(1, 2, 1, 6), (1, 4, 2, 6),
(1, 3, 1, 16), (1, 6, 2, 18),
(1, 4, 1, 49), (1, 8, 2, 50),
(10, 8, 2, 542), (11, 8, 2, 596), (1, 5, 1, 136), (1, 5, 2, 11), (2, 5, 2, 23), (1, 50000000, 2225652, 5709098764),
(1, 380928, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, 1),
] {
let actual = fake_exponential(factor, numerator, denominator);
assert_eq!(actual, expected, "test: {t:?}");
}
}
}