Skip to content

Commit

Permalink
Updates to logic, more comments, more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Dinonard committed Sep 25, 2023
1 parent 52bfe14 commit 2984b23
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 12 deletions.
39 changes: 30 additions & 9 deletions pallets/dynamic-evm-base-fee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@

//! TODO: Rustdoc!!!
// TODO: remove this comment later
// Max amount that adjustment factor will be able to change on live networks using the new tokenomics will be:
//
// c_n = c_n-1 * (1 + adjustment + adjustment^2/2)
//
// adjustment = v * (s - s*)
//
// Biggest possible adjustment between 2 blocks is: 0.000015 * (1 - 0.25) = 0.000_011_25
// Expressed as ratio: 11_250 / 1_000_000_000.
// This is a much smaller change compared to the max step limit ratio we'll use to limit bfpg adaptation.
// This means that once equilibrium is reached, the `StepLimitRatio` will be larger than the max possible adjustment, essentially eliminating it's effect.

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::{traits::Get, weights::Weight};
Expand Down Expand Up @@ -90,8 +102,6 @@ pub mod pallet {
db_weight.reads_writes(2, 1)
}

// TODO: it's super important to do double-check possible loss of precision here.
// Do some tests, compare to benchmark values.
fn on_finalize(_n: <T as frame_system::Config>::BlockNumber) {
BaseFeePerGas::<T>::mutate(|base_fee_per_gas| {
let old_bfpg = *base_fee_per_gas;
Expand All @@ -103,13 +113,24 @@ pub mod pallet {
U256::from(step)
};

// TODO: maybe add a DB entry to check until when should we apply max step adjustment?
// Once 'equilibrium' is reached, it's safe to just follow the formula without limit updates.
// Or we could abuse the sudo for this.

// Lower & upper limit between which the new base fee per gas should be clamped.
let lower_limit = T::MinBaseFeePerGas::get().max(old_bfpg.saturating_sub(max_step));
let upper_limit = T::MaxBaseFeePerGas::get().min(old_bfpg.saturating_add(max_step));
// It's possible current base fee per gas is outside of the allowed range.
// This can & will happen when this solution is deployed on live networks.
//
// In such scenario, we will discard the lower & upper bounds configured in the runtime.
// Once these bounds are reached ONCE, the runtime logic will prevent them from going out of bounds again.
let apply_configured_bounds = old_bfpg >= T::MinBaseFeePerGas::get()
&& old_bfpg <= T::MaxBaseFeePerGas::get();
let (lower_limit, upper_limit) = if apply_configured_bounds {
(
T::MinBaseFeePerGas::get().max(old_bfpg.saturating_sub(max_step)),
T::MaxBaseFeePerGas::get().min(old_bfpg.saturating_add(max_step)),
)
} else {
(
old_bfpg.saturating_sub(max_step),
old_bfpg.saturating_add(max_step),
)
};

// Calculate ideal new 'base_fee_per_gas' according to the formula
let ideal_new_bfpg = T::AdjustmentFactor::get()
Expand Down
1 change: 0 additions & 1 deletion pallets/dynamic-evm-base-fee/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ impl pallet_timestamp::Config for TestRuntime {
type WeightInfo = ();
}

// TODO: maybe expand this so all params are configurable? Should make for more dynamic testing.
parameter_types! {
pub DefaultBaseFeePerGas: U256 = U256::from(1_500_000_000_000_u128);
pub MinBaseFeePerGas: U256 = U256::from(800_000_000_000_u128);
Expand Down
98 changes: 96 additions & 2 deletions pallets/dynamic-evm-base-fee/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ use mock::*;

use frame_support::{assert_noop, assert_ok, traits::OnFinalize};
use num_traits::Bounded;
use sp_runtime::traits::{BadOrigin, One, Zero};
use sp_runtime::{
traits::{BadOrigin, One, Zero},
FixedU128,
};

use fp_evm::FeeCalculator;

Expand Down Expand Up @@ -224,6 +227,97 @@ fn bfpg_full_spectrum_change_works() {
DynamicEvmBaseFee::on_finalize(counter);
counter += 1;
}
assert_eq!(BaseFeePerGas::<TestRuntime>::get(), target_bfpg);

assert_eq!(BaseFeePerGas::<TestRuntime>::get(), target_bfpg,
"bfpg upper bound not reached - either it's not enough iterations or some precision loss occurs.");
});
}

#[test]
fn bfpg_matches_expected_value_for_so_called_average_transaction() {
ExtBuilder::build().execute_with(|| {
// The new proposed models suggests to use the following formula to calculate the base fee per gas:
//
// bfpg = (adj_factor * weight_factor * 25_000) / 9_897_4000
let init_bfpg = get_ideal_bfpg();
BaseFeePerGas::<TestRuntime>::set(init_bfpg);
let init_adj_factor = <TestRuntime as pallet::Config>::AdjustmentFactor::get();

// Slighly increase the adjustment factor, and calculate the new base fee per gas
//
// To keep it closer to reality, let's assume we're using the proposed variability factor of 0.000_015.
// Let's also assume that block fullness difference is 0.01 (1%).
// This should result in the adjustment factor of 0.000_001_5.
//
// NOTE: it's important to keep the increase small so that the step doesn't saturate
let change = FixedU128::from_rational(1500, 1_000_000_000);
let new_adj_factor = init_adj_factor + change;
assert!(new_adj_factor > init_adj_factor, "Sanity check");
set_adjustment_factor(new_adj_factor);

// Calculate the new expected base fee per gas
let weight_factor: u128 = <TestRuntime as pallet::Config>::WeightFactor::get();
let expected_bfpg =
U256::from(new_adj_factor.saturating_mul_int(weight_factor) * 25_000 / 9_897_4000);

// Calculate the new base fee per gas in the pallet
DynamicEvmBaseFee::on_finalize(1);

// Assert calculated value is as expected
let new_bfpg = BaseFeePerGas::<TestRuntime>::get();
assert!(new_bfpg > init_bfpg, "Sanity check");
assert_eq!(new_bfpg, expected_bfpg);

// Also check the opposite direction
let new_adj_factor = init_adj_factor - change;
set_adjustment_factor(new_adj_factor);
let expected_bfpg =
U256::from(new_adj_factor.saturating_mul_int(weight_factor) * 25_000 / 9_897_4000);

// Calculate the new base fee per gas in the pallet
DynamicEvmBaseFee::on_finalize(2);
// Assert calculated value is as expected
let new_bfpg = BaseFeePerGas::<TestRuntime>::get();
assert!(new_bfpg < init_bfpg, "Sanity check");
assert_eq!(new_bfpg, expected_bfpg);
});
}

#[test]
fn lower_upper_bounds_ignored_if_bfpg_is_outside() {
ExtBuilder::build().execute_with(|| {
// Set the initial bfpg to be outside of the allowed range.
// It's important reduction is sufficient so we're still below the minimum limit after the adjustment.
let delta = 100_000_000;

// First test when bfpg is too little
let too_small_bfpg = <TestRuntime as pallet::Config>::MinBaseFeePerGas::get() - delta;
BaseFeePerGas::<TestRuntime>::set(too_small_bfpg);
DynamicEvmBaseFee::on_finalize(1);

assert!(
BaseFeePerGas::<TestRuntime>::get() > too_small_bfpg,
"Bfpg should have increased slightly."
);
assert!(
BaseFeePerGas::<TestRuntime>::get()
< <TestRuntime as pallet::Config>::MinBaseFeePerGas::get(),
"For this test, bfpg should still be below the minimum limit."
);

// Repeat the same test but this time bfpg is too big
let too_big_bfpg = <TestRuntime as pallet::Config>::MaxBaseFeePerGas::get() + delta;
BaseFeePerGas::<TestRuntime>::set(too_big_bfpg);
DynamicEvmBaseFee::on_finalize(2);

assert!(
BaseFeePerGas::<TestRuntime>::get() < too_big_bfpg,
"Bfpg should have decreased slightly."
);
assert!(
BaseFeePerGas::<TestRuntime>::get()
< <TestRuntime as pallet::Config>::MaxBaseFeePerGas::get(),
"For this test, bfpg should still be above the maximum limit."
);
});
}

0 comments on commit 2984b23

Please sign in to comment.