use brk_types::{FeeRate, RecommendedFees};
use super::stats::BlockStats;
const MIN_INCREMENT: FeeRate = FeeRate::new(0.001);
const PRIORITY_FACTOR: FeeRate = FeeRate::new(0.5);
const MIN_FASTEST_FEE: FeeRate = FeeRate::new(1.0);
const MIN_HALF_HOUR_FEE: FeeRate = FeeRate::new(0.5);
const EMPTY_BLOCK_VSIZE: u64 = 500_000;
const FULL_BLOCK_VSIZE: u64 = 950_000;
pub struct Fees;
impl Fees {
pub fn compute(stats: &[BlockStats], min_fee: FeeRate) -> RecommendedFees {
let minimum_fee = min_fee.ceil_to(MIN_INCREMENT).max(MIN_INCREMENT);
let first = Self::block_fee(stats, 0, None, minimum_fee);
let second = Self::block_fee(stats, 1, Some(first), minimum_fee);
let third = Self::block_fee(stats, 2, Some(second), minimum_fee);
let economy = third.clamp(minimum_fee, minimum_fee * 2.0);
let hour = minimum_fee.max(third).max(economy);
let half_hour = minimum_fee.max(second).max(hour);
let fastest = minimum_fee.max(first).max(half_hour);
let fastest = (fastest + PRIORITY_FACTOR).max(MIN_FASTEST_FEE);
let half_hour = (half_hour + PRIORITY_FACTOR / 2.0).max(MIN_HALF_HOUR_FEE);
RecommendedFees {
fastest_fee: fastest.round_milli(),
half_hour_fee: half_hour.round_milli(),
hour_fee: hour.round_milli(),
economy_fee: economy.round_milli(),
minimum_fee: minimum_fee.round_milli(),
}
}
fn block_fee(
stats: &[BlockStats],
i: usize,
prev: Option<FeeRate>,
min_fee: FeeRate,
) -> FeeRate {
stats.get(i).map_or(min_fee, |b| {
Self::optimize_median_fee(b, stats.get(i + 1), prev, min_fee)
})
}
fn optimize_median_fee(
block: &BlockStats,
next_block: Option<&BlockStats>,
previous_fee: Option<FeeRate>,
min_fee: FeeRate,
) -> FeeRate {
let median = block.median_fee_rate();
let use_fee = previous_fee.map_or(median, |prev| FeeRate::mean(median, prev));
let vsize = u64::from(block.total_vsize);
if vsize <= EMPTY_BLOCK_VSIZE || median < min_fee {
return min_fee;
}
if vsize <= FULL_BLOCK_VSIZE && next_block.is_none() {
let multiplier = (vsize - EMPTY_BLOCK_VSIZE) as f64 / EMPTY_BLOCK_VSIZE as f64;
return (use_fee * multiplier).round_to(MIN_INCREMENT).max(min_fee);
}
use_fee.ceil_to(MIN_INCREMENT).max(min_fee)
}
}