use std::{
cmp::max,
num::NonZeroU64,
};
#[cfg(test)]
mod tests;
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum Error {
#[error("Skipped L2 block update: expected {expected:?}, got {got:?}")]
SkippedL2Block { expected: u32, got: u32 },
}
#[derive(Debug, Clone, PartialEq)]
pub struct AlgorithmV0 {
new_exec_price: u64,
for_height: u32,
percentage: u64,
}
impl AlgorithmV0 {
pub fn calculate(&self) -> u64 {
self.new_exec_price
}
#[allow(clippy::cast_possible_truncation)]
pub fn worst_case(&self, height: u32) -> u64 {
let price = self.new_exec_price as f64;
let blocks = height.saturating_sub(self.for_height) as f64;
let percentage = self.percentage as f64;
let percentage_as_decimal = percentage / 100.0;
let multiple = (1.0f64 + percentage_as_decimal).powf(blocks);
let mut approx = price * multiple;
const ARB_CUTOFF: f64 = 16948547188989277.0;
if approx > ARB_CUTOFF {
const ARB_ADDITION: f64 = 2000.0;
approx += ARB_ADDITION;
}
approx.ceil() as u64
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
pub struct AlgorithmUpdaterV0 {
pub new_exec_price: u64,
pub min_exec_gas_price: u64,
pub exec_gas_price_change_percent: u64,
pub l2_block_height: u32,
pub l2_block_fullness_threshold_percent: u64,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
pub struct BlockBytes {
pub height: u32,
pub block_bytes: u64,
}
impl AlgorithmUpdaterV0 {
pub fn update_l2_block_data(
&mut self,
height: u32,
used: u64,
capacity: NonZeroU64,
) -> Result<(), Error> {
let expected = self.l2_block_height.saturating_add(1);
if height != expected {
Err(Error::SkippedL2Block {
expected,
got: height,
})
} else {
self.l2_block_height = height;
self.update_exec_gas_price(used, capacity);
Ok(())
}
}
fn update_exec_gas_price(&mut self, used: u64, capacity: NonZeroU64) {
let mut exec_gas_price = self.new_exec_price;
let fullness_percent = used
.saturating_mul(100)
.checked_div(capacity.into())
.unwrap_or(self.l2_block_fullness_threshold_percent);
match fullness_percent.cmp(&self.l2_block_fullness_threshold_percent) {
std::cmp::Ordering::Greater | std::cmp::Ordering::Equal => {
let change_amount = self.change_amount(exec_gas_price);
exec_gas_price = exec_gas_price.saturating_add(change_amount);
}
std::cmp::Ordering::Less => {
let change_amount = self.change_amount(exec_gas_price);
exec_gas_price = exec_gas_price.saturating_sub(change_amount);
}
}
self.new_exec_price = max(self.min_exec_gas_price, exec_gas_price);
}
fn change_amount(&self, principle: u64) -> u64 {
principle
.saturating_mul(self.exec_gas_price_change_percent)
.saturating_div(100)
}
pub fn algorithm(&self) -> AlgorithmV0 {
AlgorithmV0 {
new_exec_price: self.new_exec_price,
for_height: self.l2_block_height,
percentage: self.exec_gas_price_change_percent,
}
}
}