From a7634fa5c1289161df8e23c77d149b0e3166c3b5 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Wed, 13 Sep 2023 17:55:25 +0200 Subject: [PATCH 01/23] Fee Alignment - Shibuya/local --- Cargo.lock | 14 +++ pallets/dynamic-evm-base-fee/Cargo.toml | 43 +++++++++ pallets/dynamic-evm-base-fee/src/lib.rs | 118 ++++++++++++++++++++++++ runtime/shibuya/src/lib.rs | 11 ++- 4 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 pallets/dynamic-evm-base-fee/Cargo.toml create mode 100644 pallets/dynamic-evm-base-fee/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 28f586a9b3..e82eced0c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7146,6 +7146,20 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-dynamic-evm-base-fee" +version = "0.1.0" +dependencies = [ + "fp-evm", + "frame-support", + "frame-system", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + [[package]] name = "pallet-election-provider-multi-phase" version = "4.0.0-dev" diff --git a/pallets/dynamic-evm-base-fee/Cargo.toml b/pallets/dynamic-evm-base-fee/Cargo.toml new file mode 100644 index 0000000000..6ba0e41c13 --- /dev/null +++ b/pallets/dynamic-evm-base-fee/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "pallet-dynamic-evm-base-fee" +version = "0.1.0" +license = "GPL-3.0-or-later" +description = "Handler for dynamic EVM base fee for Astar tokenomics v2." +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } + +# Substrate +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +pallet-transaction-payment = { workspace = true } + +# Frontier +fp-evm = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "scale-info/std", + # Substrate + "frame-support/std", + "frame-system/std", + "sp-core/std", + "sp-runtime/std", + "pallet-transaction-payment/std", + # Frontier + "fp-evm/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-transaction-payment/try-runtime", +] diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs new file mode 100644 index 0000000000..7ed9c2cebc --- /dev/null +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -0,0 +1,118 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_core::U256; +use sp_runtime::{traits::Convert, traits::UniqueSaturatedInto}; + +pub use self::pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + use super::*; + + #[pallet::pallet] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From + IsType<::RuntimeEvent>; + type DefaultBaseFeePerGas: Get; + type MinBaseFeePerGas: Get; + type MaxBaseFeePerGas: Get; + type AdjustmentLogic: Convert; + } + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub base_fee_per_gas: U256, + _marker: PhantomData, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { + base_fee_per_gas: T::DefaultBaseFeePerGas::get(), + _marker: PhantomData, + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + BaseFeePerGas::::put(self.base_fee_per_gas); + } + } + + #[pallet::type_value] + pub fn DefaultBaseFeePerGas() -> U256 { + T::DefaultBaseFeePerGas::get() + } + + #[pallet::storage] + pub type BaseFeePerGas = StorageValue<_, U256, ValueQuery, DefaultBaseFeePerGas>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + NewBaseFeePerGas { fee: U256 }, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_: T::BlockNumber) -> Weight { + // TODO: benchmark this! + let db_weight = ::DbWeight::get(); + db_weight.reads_writes(2, 1) + } + + fn on_finalize(_n: ::BlockNumber) { + BaseFeePerGas::::mutate(|base_fee_per_gas| { + let new_base_fee_per_gas = + T::AdjustmentLogic::convert(base_fee_per_gas.clone().unique_saturated_into()); + + *base_fee_per_gas = U256::from(new_base_fee_per_gas) + .clamp(T::MinBaseFeePerGas::get(), T::MaxBaseFeePerGas::get()); + }) + } + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] + pub fn set_base_fee_per_gas(origin: OriginFor, fee: U256) -> DispatchResult { + ensure_root(origin)?; + BaseFeePerGas::::put(fee); + Self::deposit_event(Event::NewBaseFeePerGas { fee }); + Ok(()) + } + } +} + +impl fp_evm::FeeCalculator for Pallet { + fn min_gas_price() -> (U256, Weight) { + (BaseFeePerGas::::get(), T::DbWeight::get().reads(1)) + } +} diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 24c352cc60..2ffdf7848f 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -103,7 +103,7 @@ pub const MICROSBY: Balance = 1_000_000_000_000; pub const MILLISBY: Balance = 1_000 * MICROSBY; pub const SBY: Balance = 1_000 * MILLISBY; -pub const STORAGE_BYTE_FEE: Balance = 100 * MICROSBY; +pub const STORAGE_BYTE_FEE: Balance = MICROSBY; /// Charge fee for stored bytes and items. pub const fn deposit(items: u32, bytes: u32) -> Balance { @@ -117,7 +117,7 @@ pub const fn deposit(items: u32, bytes: u32) -> Balance { /// /// TODO: using this requires storage migration (good to test on Shibuya first!) pub const fn contracts_deposit(items: u32, bytes: u32) -> Balance { - items as Balance * 4 * MILLISBY + (bytes as Balance) * STORAGE_BYTE_FEE + items as Balance * 40 * MICROSBY + (bytes as Balance) * STORAGE_BYTE_FEE } /// Change this to adjust the block time. @@ -735,11 +735,12 @@ impl WeightToFeePolynomial for WeightToFee { pub struct DealWithFees; impl OnUnbalanced for DealWithFees { fn on_unbalanceds(mut fees_then_tips: impl Iterator) { - if let Some(mut fees) = fees_then_tips.next() { + if let Some(fees) = fees_then_tips.next() { + // Burn 80% of fees, rest goes to collators, including 100% of the tips. + let (to_burn, mut collators) = fees.ration(80, 20); if let Some(tips) = fees_then_tips.next() { - tips.merge_into(&mut fees); + tips.merge_into(&mut collators); } - let (to_burn, collators) = fees.ration(20, 80); // burn part of fees drop(to_burn); From 04cfd6d4ebdec423fe62f1770339fceb589de027 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 14 Sep 2023 17:40:46 +0200 Subject: [PATCH 02/23] Further modifications --- pallets/dynamic-evm-base-fee/src/lib.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 7ed9c2cebc..baf7d136e0 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -20,7 +20,9 @@ use frame_support::{traits::Get, weights::Weight}; use sp_core::U256; -use sp_runtime::{traits::Convert, traits::UniqueSaturatedInto}; +use sp_runtime::{traits::Convert, traits::UniqueSaturatedInto, Perquintill}; + +// TODO: not sure if Perbill will be precise enough here. Need to write tests to check. pub use self::pallet::*; @@ -40,7 +42,11 @@ pub mod pallet { type DefaultBaseFeePerGas: Get; type MinBaseFeePerGas: Get; type MaxBaseFeePerGas: Get; - type AdjustmentLogic: Convert; + + type AdjustmentFactor: Get; + type WeightFactor: Get; + + type MaxRatio: Perquintill; } #[pallet::genesis_config] @@ -89,10 +95,17 @@ pub mod pallet { fn on_finalize(_n: ::BlockNumber) { BaseFeePerGas::::mutate(|base_fee_per_gas| { - let new_base_fee_per_gas = - T::AdjustmentLogic::convert(base_fee_per_gas.clone().unique_saturated_into()); + let old_bfpg = *base_fee_per_gas; + let new_bfpg = T::AdjustmentFactor::get() + .saturating_mul(T::WeightFactor::get()) + .saturating_mul(25) + .saturating_div(98974); + + // TODO: continue here - I can either use Perquintill or Rational but have to see which one is better for my purpose. + // The problem with Perquintill is that it saturates at 1, so I would have to express the "opposite" ratio - smaller_value/larger_value for it to work. Which is fine... + // if new_bfpg > old_bfpg && - *base_fee_per_gas = U256::from(new_base_fee_per_gas) + *base_fee_per_gas = U256::from(new_bfpg) .clamp(T::MinBaseFeePerGas::get(), T::MaxBaseFeePerGas::get()); }) } From 6600a4e860d0c8bf59092ee4df34aa9890044514 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Fri, 15 Sep 2023 17:08:57 +0200 Subject: [PATCH 03/23] Some improvements --- pallets/dynamic-evm-base-fee/src/lib.rs | 47 ++++++++++++++++++------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index baf7d136e0..03cbf9c040 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -20,9 +20,7 @@ use frame_support::{traits::Get, weights::Weight}; use sp_core::U256; -use sp_runtime::{traits::Convert, traits::UniqueSaturatedInto, Perquintill}; - -// TODO: not sure if Perbill will be precise enough here. Need to write tests to check. +use sp_runtime::{traits::UniqueSaturatedInto, Perquintill}; pub use self::pallet::*; @@ -38,15 +36,22 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// Overarching event type type RuntimeEvent: From + IsType<::RuntimeEvent>; + /// Default base fee per gas value. Used in genesis if no other value specified explicitly. type DefaultBaseFeePerGas: Get; + /// Minimum value 'base fee per gas' can be adjusted to. This is a defensive measure to prevent the fee from being too low. type MinBaseFeePerGas: Get; + /// Maximum value 'base fee per gas' can be adjusted to. This is a defensive measure to prevent the fee from being too high. type MaxBaseFeePerGas: Get; - + /// Getter for the fee adjustment factor used in 'base fee per gas' formula. This is expected to change in-between the blocks (doesn't have to though). type AdjustmentFactor: Get; + /// The so-called `weight_factor` in the 'base fee per gas' formula. type WeightFactor: Get; - - type MaxRatio: Perquintill; + /// Ratio limit on how much the 'base fee per gas' can change in-between two blocks. + /// It's expressed as percentage, and used to calculate the delta between the old and new value. + /// E.g. if the current 'base fee per gas' is 100, and the limit is 10%, then the new base fee per gas can be between 90 and 110. + type StepLimitRatio: Get; } #[pallet::genesis_config] @@ -82,6 +87,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// New `base fee per gas` value has been forced-set. NewBaseFeePerGas { fee: U256 }, } @@ -93,20 +99,35 @@ 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: ::BlockNumber) { BaseFeePerGas::::mutate(|base_fee_per_gas| { let old_bfpg = *base_fee_per_gas; - let new_bfpg = T::AdjustmentFactor::get() + + // Maximum step we're allowed to move the base fee per gas by. + // TODO: this is lossy - can it be avoided? `U256` does not implement `num_traits::sign::Unsigned` trait + let max_step = { + let old_bfpg_u128: u128 = old_bfpg.unique_saturated_into(); + let step = T::StepLimitRatio::get() * old_bfpg_u128; + 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. + + // 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)); + + // Calculate ideal new 'base_fee_per_gas' according to the formula + let ideal_new_bfpg = T::AdjustmentFactor::get() .saturating_mul(T::WeightFactor::get()) .saturating_mul(25) .saturating_div(98974); - // TODO: continue here - I can either use Perquintill or Rational but have to see which one is better for my purpose. - // The problem with Perquintill is that it saturates at 1, so I would have to express the "opposite" ratio - smaller_value/larger_value for it to work. Which is fine... - // if new_bfpg > old_bfpg && - - *base_fee_per_gas = U256::from(new_bfpg) - .clamp(T::MinBaseFeePerGas::get(), T::MaxBaseFeePerGas::get()); + // Clamp the ideal value in between the allowed limits + *base_fee_per_gas = U256::from(ideal_new_bfpg).clamp(lower_limit, upper_limit); }) } } From c9fd2375686c3d6f636337f50931170bf6fe6dcd Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 21 Sep 2023 13:56:01 +0100 Subject: [PATCH 04/23] Mock/tests, type fixes --- Cargo.lock | 2 + pallets/dynamic-evm-base-fee/Cargo.toml | 12 +- pallets/dynamic-evm-base-fee/src/lib.rs | 28 +++- pallets/dynamic-evm-base-fee/src/mock.rs | 167 ++++++++++++++++++++++ pallets/dynamic-evm-base-fee/src/tests.rs | 91 ++++++++++++ 5 files changed, 293 insertions(+), 7 deletions(-) create mode 100644 pallets/dynamic-evm-base-fee/src/mock.rs create mode 100644 pallets/dynamic-evm-base-fee/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index e82eced0c5..20d049c330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7153,6 +7153,8 @@ dependencies = [ "fp-evm", "frame-support", "frame-system", + "pallet-balances", + "pallet-timestamp", "pallet-transaction-payment", "parity-scale-codec", "scale-info", diff --git a/pallets/dynamic-evm-base-fee/Cargo.toml b/pallets/dynamic-evm-base-fee/Cargo.toml index 6ba0e41c13..90dc93dfe5 100644 --- a/pallets/dynamic-evm-base-fee/Cargo.toml +++ b/pallets/dynamic-evm-base-fee/Cargo.toml @@ -15,13 +15,17 @@ scale-info = { workspace = true } # Substrate frame-support = { workspace = true } frame-system = { workspace = true } +pallet-transaction-payment = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } -pallet-transaction-payment = { workspace = true } # Frontier fp-evm = { workspace = true } +[dev-dependencies] +pallet-balances = { workspace = true } +pallet-timestamp = { workspace = true } + [features] default = ["std"] std = [ @@ -32,12 +36,14 @@ std = [ "frame-system/std", "sp-core/std", "sp-runtime/std", - "pallet-transaction-payment/std", + "pallet-transaction-payment/std", + "pallet-balances/std", + "pallet-timestamp/std", # Frontier "fp-evm/std", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", - "pallet-transaction-payment/try-runtime", + "pallet-transaction-payment/try-runtime", ] diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 03cbf9c040..b4a6fa4397 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -16,14 +16,21 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . +//! TODO: Rustdoc!!! + #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{traits::Get, weights::Weight}; use sp_core::U256; -use sp_runtime::{traits::UniqueSaturatedInto, Perquintill}; +use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber, FixedU128, Perquintill}; pub use self::pallet::*; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; @@ -45,7 +52,7 @@ pub mod pallet { /// Maximum value 'base fee per gas' can be adjusted to. This is a defensive measure to prevent the fee from being too high. type MaxBaseFeePerGas: Get; /// Getter for the fee adjustment factor used in 'base fee per gas' formula. This is expected to change in-between the blocks (doesn't have to though). - type AdjustmentFactor: Get; + type AdjustmentFactor: Get; /// The so-called `weight_factor` in the 'base fee per gas' formula. type WeightFactor: Get; /// Ratio limit on how much the 'base fee per gas' can change in-between two blocks. @@ -87,10 +94,16 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// New `base fee per gas` value has been forced-set. + /// New `base fee per gas` value has been force-set. NewBaseFeePerGas { fee: U256 }, } + #[pallet::error] + pub enum Error { + /// Specified value is outside of the allowed range. + ValueOutOfBounds, + } + #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_: T::BlockNumber) -> Weight { @@ -115,6 +128,7 @@ pub mod pallet { // 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)); @@ -122,7 +136,8 @@ pub mod pallet { // Calculate ideal new 'base_fee_per_gas' according to the formula let ideal_new_bfpg = T::AdjustmentFactor::get() - .saturating_mul(T::WeightFactor::get()) + // Weight factor should be multiplied first since it's a larger number, to avoid precision loss. + .saturating_mul_int(T::WeightFactor::get()) .saturating_mul(25) .saturating_div(98974); @@ -138,6 +153,11 @@ pub mod pallet { #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] pub fn set_base_fee_per_gas(origin: OriginFor, fee: U256) -> DispatchResult { ensure_root(origin)?; + ensure!( + fee >= T::MinBaseFeePerGas::get() && fee <= T::MaxBaseFeePerGas::get(), + Error::::ValueOutOfBounds + ); + BaseFeePerGas::::put(fee); Self::deposit_event(Event::NewBaseFeePerGas { fee }); Ok(()) diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs new file mode 100644 index 0000000000..19b3c45288 --- /dev/null +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -0,0 +1,167 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg(test)] + +use super::*; +use crate as pallet_dynamic_evm_base_fee; + +use frame_support::{ + construct_runtime, parameter_types, + sp_io::TestExternalities, + storage, + traits::{ConstU128, ConstU32, ConstU64}, + weights::Weight, +}; +use parity_scale_codec::Encode; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup, One}, + FixedU128, Perquintill, +}; + +pub(crate) type AccountId = u128; +pub(crate) type BlockNumber = u64; +pub(crate) type Balance = u128; + +parameter_types! { + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0)); +} + +impl frame_system::Config for TestRuntime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type RuntimeCall = RuntimeCall; + type BlockNumber = BlockNumber; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for TestRuntime { + type MaxLocks = ConstU32<4>; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU128<2>; + type AccountStore = System; + type WeightInfo = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +impl pallet_timestamp::Config for TestRuntime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = ConstU64<3>; + type WeightInfo = (); +} + +parameter_types! { + pub WeightPerGas: Weight = Weight::from_parts(1, 0); + pub DefaultBaseFeePerGas: U256 = U256::from(1_500_000_000_000_u128); + pub MinBaseFeePerGas: U256 = U256::from(800_000_000_000_u128); + pub MaxBaseFeePerGas: U256 = U256::from(80_000_000_000_000_u128); + pub StepLimitRation: Perquintill = Perquintill::from_rational(30_u128, 1_000_000); +} + +impl pallet_dynamic_evm_base_fee::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type DefaultBaseFeePerGas = DefaultBaseFeePerGas; + type MinBaseFeePerGas = MinBaseFeePerGas; + type MaxBaseFeePerGas = MaxBaseFeePerGas; + type AdjustmentFactor = GetAdjustmentFactor; + type WeightFactor = ConstU128<30_000_000_000_000_000>; + type StepLimitRatio = StepLimitRation; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct TestRuntime + where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Timestamp: pallet_timestamp, + Balances: pallet_balances, + DynamicEvmBaseFee: pallet_dynamic_evm_base_fee, + } +); + +const ADJUSTMENT_FACTOR: &[u8] = b":adj_factor_evm"; + +/// Helper method to set the adjustment factor used by the pallet. +pub fn set_adjustment_factor(factor: FixedU128) { + storage::unhashed::put_raw(&ADJUSTMENT_FACTOR, &factor.encode()); +} + +/// Helper function to get the adjustment factor used by the pallet. +/// In case it's unset, it returns 0. +pub fn get_adjustment_factor() -> FixedU128 { + storage::unhashed::get::(&ADJUSTMENT_FACTOR).unwrap_or_default() +} + +pub struct GetAdjustmentFactor; +impl Get for GetAdjustmentFactor { + fn get() -> FixedU128 { + get_adjustment_factor() + } +} + +pub struct ExtBuilder; +impl ExtBuilder { + pub fn build() -> TestExternalities { + let storage = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let mut ext = TestExternalities::from(storage); + ext.execute_with(|| { + set_adjustment_factor(FixedU128::one()); + System::set_block_number(1); + }); + ext + } +} diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs new file mode 100644 index 0000000000..c44f60a3f2 --- /dev/null +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -0,0 +1,91 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg(test)] + +use super::*; +use mock::*; + +use frame_support::{assert_noop, assert_ok}; +use sp_runtime::traits::BadOrigin; + +#[test] +fn set_base_fee_per_gas_works() { + ExtBuilder::build().execute_with(|| { + // sanity check + assert_eq!( + BaseFeePerGas::::get(), + ::DefaultBaseFeePerGas::get() + ); + + // Ensure we can change the bfpg value via root + for new_base_fee_per_gas in [ + ::MinBaseFeePerGas::get(), + ::MaxBaseFeePerGas::get(), + ] { + assert_ok!(DynamicEvmBaseFee::set_base_fee_per_gas( + RuntimeOrigin::root(), + new_base_fee_per_gas + )); + System::assert_last_event(mock::RuntimeEvent::DynamicEvmBaseFee( + Event::NewBaseFeePerGas { + fee: new_base_fee_per_gas, + }, + )); + assert_eq!(BaseFeePerGas::::get(), new_base_fee_per_gas); + } + }); +} + +#[test] +fn set_base_fee_per_gas_value_out_of_bounds_fails() { + ExtBuilder::build().execute_with(|| { + // Out of bound values + let too_small_base_fee_per_gas = + ::MinBaseFeePerGas::get() - 1; + let too_big_base_fee_per_gas = ::MaxBaseFeePerGas::get() + 1; + + assert_noop!( + DynamicEvmBaseFee::set_base_fee_per_gas( + RuntimeOrigin::root(), + too_small_base_fee_per_gas + ), + Error::::ValueOutOfBounds + ); + assert_noop!( + DynamicEvmBaseFee::set_base_fee_per_gas( + RuntimeOrigin::root(), + too_big_base_fee_per_gas + ), + Error::::ValueOutOfBounds + ); + }); +} + +#[test] +fn set_base_fee_per_gas_non_root_fails() { + ExtBuilder::build().execute_with(|| { + assert_noop!( + DynamicEvmBaseFee::set_base_fee_per_gas( + RuntimeOrigin::signed(1), + ::MinBaseFeePerGas::get() + ), + BadOrigin + ); + }); +} From ed3b4dff94a6701ffefea04de5e3b7856c5a6370 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 21 Sep 2023 16:22:29 +0100 Subject: [PATCH 05/23] Tests --- pallets/dynamic-evm-base-fee/src/lib.rs | 1 + pallets/dynamic-evm-base-fee/src/mock.rs | 4 ++-- pallets/dynamic-evm-base-fee/src/tests.rs | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index b4a6fa4397..d5f1a31dda 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -120,6 +120,7 @@ pub mod pallet { // Maximum step we're allowed to move the base fee per gas by. // TODO: this is lossy - can it be avoided? `U256` does not implement `num_traits::sign::Unsigned` trait + // TODO2: maybe ensure that BFPG can never exceed u128::MAX? Could be part of integration tests. let max_step = { let old_bfpg_u128: u128 = old_bfpg.unique_saturated_into(); let step = T::StepLimitRatio::get() * old_bfpg_u128; diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index 19b3c45288..af701153b1 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -26,7 +26,7 @@ use frame_support::{ sp_io::TestExternalities, storage, traits::{ConstU128, ConstU32, ConstU64}, - weights::Weight, + weights::constants::RocksDbWeight, }; use parity_scale_codec::Encode; use sp_core::H256; @@ -60,7 +60,7 @@ impl frame_system::Config for TestRuntime { type Header = Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; - type DbWeight = (); + type DbWeight = RocksDbWeight; type Version = (); type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs index c44f60a3f2..38f0e92b26 100644 --- a/pallets/dynamic-evm-base-fee/src/tests.rs +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -24,6 +24,8 @@ use mock::*; use frame_support::{assert_noop, assert_ok}; use sp_runtime::traits::BadOrigin; +use fp_evm::FeeCalculator; + #[test] fn set_base_fee_per_gas_works() { ExtBuilder::build().execute_with(|| { @@ -89,3 +91,23 @@ fn set_base_fee_per_gas_non_root_fails() { ); }); } + +#[test] +fn min_gas_price_works() { + ExtBuilder::build().execute_with(|| { + let new_base_fee_per_gas = + ::MinBaseFeePerGas::get() + 19 * 17; + assert_ok!(DynamicEvmBaseFee::set_base_fee_per_gas( + RuntimeOrigin::root(), + new_base_fee_per_gas + )); + + assert_eq!( + DynamicEvmBaseFee::min_gas_price(), + ( + new_base_fee_per_gas, + ::DbWeight::get().reads(1) + ) + ); + }); +} From 6bd462e467594dcacdb30b072648a901b3638697 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 21 Sep 2023 21:40:48 +0100 Subject: [PATCH 06/23] Integration test, removal of genesis --- pallets/dynamic-evm-base-fee/src/lib.rs | 37 +++++++++--------------- pallets/dynamic-evm-base-fee/src/mock.rs | 2 +- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index d5f1a31dda..9bb23f042f 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -61,28 +61,6 @@ pub mod pallet { type StepLimitRatio: Get; } - #[pallet::genesis_config] - pub struct GenesisConfig { - pub base_fee_per_gas: U256, - _marker: PhantomData, - } - - impl Default for GenesisConfig { - fn default() -> Self { - Self { - base_fee_per_gas: T::DefaultBaseFeePerGas::get(), - _marker: PhantomData, - } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - BaseFeePerGas::::put(self.base_fee_per_gas); - } - } - #[pallet::type_value] pub fn DefaultBaseFeePerGas() -> U256 { T::DefaultBaseFeePerGas::get() @@ -119,8 +97,6 @@ pub mod pallet { let old_bfpg = *base_fee_per_gas; // Maximum step we're allowed to move the base fee per gas by. - // TODO: this is lossy - can it be avoided? `U256` does not implement `num_traits::sign::Unsigned` trait - // TODO2: maybe ensure that BFPG can never exceed u128::MAX? Could be part of integration tests. let max_step = { let old_bfpg_u128: u128 = old_bfpg.unique_saturated_into(); let step = T::StepLimitRatio::get() * old_bfpg_u128; @@ -146,6 +122,19 @@ pub mod pallet { *base_fee_per_gas = U256::from(ideal_new_bfpg).clamp(lower_limit, upper_limit); }) } + + fn integrity_test() { + assert!(T::MinBaseFeePerGas::get() <= T::MaxBaseFeePerGas::get(), + "Minimum base fee per gas has to be equal or lower than maximum allowed base fee per gas."); + + assert!(T::DefaultBaseFeePerGas::get() >= T::MinBaseFeePerGas::get(), + "Default base fee per gas has to be equal or higher than minimum allowed base fee per gas."); + assert!(T::DefaultBaseFeePerGas::get() <= T::MaxBaseFeePerGas::get(), + "Default base fee per gas has to be equal or lower than maximum allowed base fee per gas."); + + assert!(T::MaxBaseFeePerGas::get() <= U256::from(u128::MAX), + "Maximum base fee per gas has to be equal or lower than u128::MAX, otherwise precision loss will occur."); + } } #[pallet::call] diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index af701153b1..0f13caa740 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -95,8 +95,8 @@ 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 WeightPerGas: Weight = Weight::from_parts(1, 0); pub DefaultBaseFeePerGas: U256 = U256::from(1_500_000_000_000_u128); pub MinBaseFeePerGas: U256 = U256::from(800_000_000_000_u128); pub MaxBaseFeePerGas: U256 = U256::from(80_000_000_000_000_u128); From 7244d47572ed9216e9b7a705f93b88131dae447a Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Fri, 22 Sep 2023 09:58:47 +0100 Subject: [PATCH 07/23] Tests --- pallets/dynamic-evm-base-fee/src/mock.rs | 21 +++++++++++++------- pallets/dynamic-evm-base-fee/src/tests.rs | 24 +++++++++++++++++++++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index 0f13caa740..19c8a9b9b1 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -137,16 +137,10 @@ pub fn set_adjustment_factor(factor: FixedU128) { storage::unhashed::put_raw(&ADJUSTMENT_FACTOR, &factor.encode()); } -/// Helper function to get the adjustment factor used by the pallet. -/// In case it's unset, it returns 0. -pub fn get_adjustment_factor() -> FixedU128 { - storage::unhashed::get::(&ADJUSTMENT_FACTOR).unwrap_or_default() -} - pub struct GetAdjustmentFactor; impl Get for GetAdjustmentFactor { fn get() -> FixedU128 { - get_adjustment_factor() + storage::unhashed::get::(&ADJUSTMENT_FACTOR).unwrap_or_default() } } @@ -165,3 +159,16 @@ impl ExtBuilder { ext } } + +/// Ideal `base fee per gas` value according to the fee alignment formula. +/// It changes dynamically based on `adjustment factor` and `weight factor` parameters. +pub fn get_ideal_bfpg() -> U256 { + U256::from( + ::AdjustmentFactor::get() + .saturating_mul_int::( + ::WeightFactor::get(), + ) + .saturating_mul(25) + .saturating_div(98974), + ) +} diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs index 38f0e92b26..706dd77014 100644 --- a/pallets/dynamic-evm-base-fee/src/tests.rs +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -21,8 +21,8 @@ use super::*; use mock::*; -use frame_support::{assert_noop, assert_ok}; -use sp_runtime::traits::BadOrigin; +use frame_support::{assert_noop, assert_ok, traits::OnFinalize}; +use sp_runtime::traits::{BadOrigin, One}; use fp_evm::FeeCalculator; @@ -111,3 +111,23 @@ fn min_gas_price_works() { ); }); } + +#[test] +fn unit_adjustment_factor_no_change() { + ExtBuilder::build().execute_with(|| { + // Prep init values + let init_bfpg = get_ideal_bfpg(); + BaseFeePerGas::::set(init_bfpg); + set_adjustment_factor(FixedU128::one()); + + DynamicEvmBaseFee::on_finalize(1); + assert_eq!( + BaseFeePerGas::::get(), + init_bfpg, + "bfpg should remain the same" + ); + }); +} + +#[test] +fn bfpg_limits_are_respected() {} From 878e09f45eca736dbdead1c8a10907512d5c503a Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Fri, 22 Sep 2023 14:21:11 +0100 Subject: [PATCH 08/23] Bounds tests --- Cargo.lock | 1 + pallets/dynamic-evm-base-fee/Cargo.toml | 2 + pallets/dynamic-evm-base-fee/src/lib.rs | 2 +- pallets/dynamic-evm-base-fee/src/mock.rs | 8 +++ pallets/dynamic-evm-base-fee/src/tests.rs | 82 ++++++++++++++++++++++- 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20d049c330..296de3f4b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7153,6 +7153,7 @@ dependencies = [ "fp-evm", "frame-support", "frame-system", + "num-traits", "pallet-balances", "pallet-timestamp", "pallet-transaction-payment", diff --git a/pallets/dynamic-evm-base-fee/Cargo.toml b/pallets/dynamic-evm-base-fee/Cargo.toml index 90dc93dfe5..7106dd350f 100644 --- a/pallets/dynamic-evm-base-fee/Cargo.toml +++ b/pallets/dynamic-evm-base-fee/Cargo.toml @@ -23,6 +23,7 @@ sp-runtime = { workspace = true } fp-evm = { workspace = true } [dev-dependencies] +num-traits = { workspace = true } pallet-balances = { workspace = true } pallet-timestamp = { workspace = true } @@ -31,6 +32,7 @@ default = ["std"] std = [ "parity-scale-codec/std", "scale-info/std", + "num-traits/std", # Substrate "frame-support/std", "frame-system/std", diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 9bb23f042f..d4f475a42b 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -140,7 +140,7 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO: weight! pub fn set_base_fee_per_gas(origin: OriginFor, fee: U256) -> DispatchResult { ensure_root(origin)?; ensure!( diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index 19c8a9b9b1..b608979c3e 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -172,3 +172,11 @@ pub fn get_ideal_bfpg() -> U256 { .saturating_div(98974), ) } + +/// Max step limit described how much `base fee per gas` can move in any direction during one block. +pub fn get_max_step_limit() -> U256 { + let bfpg: u128 = BaseFeePerGas::::get().unique_saturated_into(); + let max_allowed_step: u128 = ::StepLimitRatio::get() * bfpg; + + U256::from(max_allowed_step) +} diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs index 706dd77014..754b1a3bf0 100644 --- a/pallets/dynamic-evm-base-fee/src/tests.rs +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -22,10 +22,23 @@ use super::*; use mock::*; use frame_support::{assert_noop, assert_ok, traits::OnFinalize}; -use sp_runtime::traits::{BadOrigin, One}; +use num_traits::Bounded; +use sp_runtime::traits::{BadOrigin, One, Zero}; use fp_evm::FeeCalculator; +#[test] +fn default_base_fee_per_gas_works() { + ExtBuilder::build().execute_with(|| { + // Genesis state check + assert_eq!( + BaseFeePerGas::::get(), + ::DefaultBaseFeePerGas::get(), + "Init bfpg should be equal to the specified default one." + ) + }); +} + #[test] fn set_base_fee_per_gas_works() { ExtBuilder::build().execute_with(|| { @@ -115,7 +128,7 @@ fn min_gas_price_works() { #[test] fn unit_adjustment_factor_no_change() { ExtBuilder::build().execute_with(|| { - // Prep init values + // Prep init values - ideal bfpg, and unit adjustment factor let init_bfpg = get_ideal_bfpg(); BaseFeePerGas::::set(init_bfpg); set_adjustment_factor(FixedU128::one()); @@ -130,4 +143,67 @@ fn unit_adjustment_factor_no_change() { } #[test] -fn bfpg_limits_are_respected() {} +fn bfpg_bounds_are_respected() { + ExtBuilder::build().execute_with(|| { + // Lower bound + let lower_bfpg = ::MinBaseFeePerGas::get(); + BaseFeePerGas::::set(lower_bfpg); + + // This should bring the ideal bfpg value to zero + set_adjustment_factor(FixedU128::zero()); + assert!(get_ideal_bfpg().is_zero(), "Sanity check"); + + DynamicEvmBaseFee::on_finalize(1); + assert_eq!( + BaseFeePerGas::::get(), + lower_bfpg, + "bfpg must not go below lower threshold." + ); + + // Upper limit + let upper_bfpg = ::MaxBaseFeePerGas::get(); + BaseFeePerGas::::set(upper_bfpg); + + // This should bring the ideal bfpg very high, well above max value + set_adjustment_factor(FixedU128::max_value()); + assert!(get_ideal_bfpg() > upper_bfpg, "Sanity check"); + + DynamicEvmBaseFee::on_finalize(2); + assert_eq!( + BaseFeePerGas::::get(), + upper_bfpg, + "bfpg must not go above threshold" + ); + }); +} + +#[test] +fn step_limit_ratio_is_respected() { + ExtBuilder::build().execute_with(|| { + // Lower bound, high adjustment factor + let lower_bfpg = ::MinBaseFeePerGas::get(); + BaseFeePerGas::::set(lower_bfpg); + set_adjustment_factor(FixedU128::max_value()); + let step_limit = get_max_step_limit(); + + DynamicEvmBaseFee::on_finalize(1); + assert_eq!( + BaseFeePerGas::::get(), + lower_bfpg + step_limit, + "Step limit ratio in ascending direction was not respected." + ); + + // Upper bound, low adjustment factor + let higher_bfpg = ::MaxBaseFeePerGas::get(); + BaseFeePerGas::::set(higher_bfpg); + set_adjustment_factor(FixedU128::zero()); + let step_limit = get_max_step_limit(); + + DynamicEvmBaseFee::on_finalize(2); + assert_eq!( + BaseFeePerGas::::get(), + higher_bfpg - step_limit, + "Step limit ratio in descending direction was not respected." + ); + }); +} From 52bfe147381098b5299b6d01aa057253b53ff9b2 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Fri, 22 Sep 2023 14:52:54 +0100 Subject: [PATCH 09/23] Spectrum test --- pallets/dynamic-evm-base-fee/src/mock.rs | 2 +- pallets/dynamic-evm-base-fee/src/tests.rs | 38 +++++++++++++++++------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index b608979c3e..7a6a60f854 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -173,7 +173,7 @@ pub fn get_ideal_bfpg() -> U256 { ) } -/// Max step limit described how much `base fee per gas` can move in any direction during one block. +/// Max step limit describes how much `base fee per gas` can move in any direction during one block. pub fn get_max_step_limit() -> U256 { let bfpg: u128 = BaseFeePerGas::::get().unique_saturated_into(); let max_allowed_step: u128 = ::StepLimitRatio::get() * bfpg; diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs index 754b1a3bf0..76dc929235 100644 --- a/pallets/dynamic-evm-base-fee/src/tests.rs +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -146,8 +146,8 @@ fn unit_adjustment_factor_no_change() { fn bfpg_bounds_are_respected() { ExtBuilder::build().execute_with(|| { // Lower bound - let lower_bfpg = ::MinBaseFeePerGas::get(); - BaseFeePerGas::::set(lower_bfpg); + let min_bfpg = ::MinBaseFeePerGas::get(); + BaseFeePerGas::::set(min_bfpg); // This should bring the ideal bfpg value to zero set_adjustment_factor(FixedU128::zero()); @@ -156,7 +156,7 @@ fn bfpg_bounds_are_respected() { DynamicEvmBaseFee::on_finalize(1); assert_eq!( BaseFeePerGas::::get(), - lower_bfpg, + min_bfpg, "bfpg must not go below lower threshold." ); @@ -181,29 +181,49 @@ fn bfpg_bounds_are_respected() { fn step_limit_ratio_is_respected() { ExtBuilder::build().execute_with(|| { // Lower bound, high adjustment factor - let lower_bfpg = ::MinBaseFeePerGas::get(); - BaseFeePerGas::::set(lower_bfpg); + let min_bfpg = ::MinBaseFeePerGas::get(); + BaseFeePerGas::::set(min_bfpg); set_adjustment_factor(FixedU128::max_value()); let step_limit = get_max_step_limit(); DynamicEvmBaseFee::on_finalize(1); assert_eq!( BaseFeePerGas::::get(), - lower_bfpg + step_limit, + min_bfpg + step_limit, "Step limit ratio in ascending direction was not respected." ); // Upper bound, low adjustment factor - let higher_bfpg = ::MaxBaseFeePerGas::get(); - BaseFeePerGas::::set(higher_bfpg); + let max_bfpg = ::MaxBaseFeePerGas::get(); + BaseFeePerGas::::set(max_bfpg); set_adjustment_factor(FixedU128::zero()); let step_limit = get_max_step_limit(); DynamicEvmBaseFee::on_finalize(2); assert_eq!( BaseFeePerGas::::get(), - higher_bfpg - step_limit, + max_bfpg - step_limit, "Step limit ratio in descending direction was not respected." ); }); } + +#[test] +fn bfpg_full_spectrum_change_works() { + ExtBuilder::build().execute_with(|| { + // Set bfpg to lowest possible, and adjustment factor to highest possible + let min_bfpg = ::MinBaseFeePerGas::get(); + BaseFeePerGas::::set(min_bfpg); + set_adjustment_factor(FixedU128::max_value()); + + // Run for limited amount of iterations until upper bound is reached + let target_bfpg = ::MaxBaseFeePerGas::get(); + let mut counter = 1; + let iter_limit = 500_000; // safety limit to avoid endless loop + while counter <= iter_limit && BaseFeePerGas::::get() < target_bfpg { + DynamicEvmBaseFee::on_finalize(counter); + counter += 1; + } + assert_eq!(BaseFeePerGas::::get(), target_bfpg); + }); +} From 2984b23ce58465eb94d7765a65659a2ce0573410 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Mon, 25 Sep 2023 12:49:46 +0200 Subject: [PATCH 10/23] Updates to logic, more comments, more tests --- pallets/dynamic-evm-base-fee/src/lib.rs | 39 ++++++--- pallets/dynamic-evm-base-fee/src/mock.rs | 1 - pallets/dynamic-evm-base-fee/src/tests.rs | 98 ++++++++++++++++++++++- 3 files changed, 126 insertions(+), 12 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index d4f475a42b..494ce047e1 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -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}; @@ -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: ::BlockNumber) { BaseFeePerGas::::mutate(|base_fee_per_gas| { let old_bfpg = *base_fee_per_gas; @@ -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() diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index 7a6a60f854..452f87c2ff 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -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); diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs index 76dc929235..c26b0096ae 100644 --- a/pallets/dynamic-evm-base-fee/src/tests.rs +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -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; @@ -224,6 +227,97 @@ fn bfpg_full_spectrum_change_works() { DynamicEvmBaseFee::on_finalize(counter); counter += 1; } - assert_eq!(BaseFeePerGas::::get(), target_bfpg); + + assert_eq!(BaseFeePerGas::::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::::set(init_bfpg); + let init_adj_factor = ::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 = ::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::::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::::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 = ::MinBaseFeePerGas::get() - delta; + BaseFeePerGas::::set(too_small_bfpg); + DynamicEvmBaseFee::on_finalize(1); + + assert!( + BaseFeePerGas::::get() > too_small_bfpg, + "Bfpg should have increased slightly." + ); + assert!( + BaseFeePerGas::::get() + < ::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 = ::MaxBaseFeePerGas::get() + delta; + BaseFeePerGas::::set(too_big_bfpg); + DynamicEvmBaseFee::on_finalize(2); + + assert!( + BaseFeePerGas::::get() < too_big_bfpg, + "Bfpg should have decreased slightly." + ); + assert!( + BaseFeePerGas::::get() + < ::MaxBaseFeePerGas::get(), + "For this test, bfpg should still be above the maximum limit." + ); }); } From de96e49a143de68210582bf31d77aa158ddb7bd6 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Mon, 25 Sep 2023 16:57:17 +0200 Subject: [PATCH 11/23] Documentation & benchmarking code --- Cargo.lock | 1 + pallets/dynamic-evm-base-fee/Cargo.toml | 8 ++ .../dynamic-evm-base-fee/src/benchmarking.rs | 75 ++++++++++++++++ pallets/dynamic-evm-base-fee/src/lib.rs | 85 +++++++++++++++---- 4 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 pallets/dynamic-evm-base-fee/src/benchmarking.rs diff --git a/Cargo.lock b/Cargo.lock index 296de3f4b1..48840c4151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7151,6 +7151,7 @@ name = "pallet-dynamic-evm-base-fee" version = "0.1.0" dependencies = [ "fp-evm", + "frame-benchmarking", "frame-support", "frame-system", "num-traits", diff --git a/pallets/dynamic-evm-base-fee/Cargo.toml b/pallets/dynamic-evm-base-fee/Cargo.toml index 7106dd350f..e9c2177b6d 100644 --- a/pallets/dynamic-evm-base-fee/Cargo.toml +++ b/pallets/dynamic-evm-base-fee/Cargo.toml @@ -13,6 +13,7 @@ parity-scale-codec = { workspace = true } scale-info = { workspace = true } # Substrate +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -41,9 +42,16 @@ std = [ "pallet-transaction-payment/std", "pallet-balances/std", "pallet-timestamp/std", + "frame-benchmarking/std", # Frontier "fp-evm/std", ] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", diff --git a/pallets/dynamic-evm-base-fee/src/benchmarking.rs b/pallets/dynamic-evm-base-fee/src/benchmarking.rs new file mode 100644 index 0000000000..4b3da9bc05 --- /dev/null +++ b/pallets/dynamic-evm-base-fee/src/benchmarking.rs @@ -0,0 +1,75 @@ +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +use super::*; + +use frame_benchmarking::v2::*; +use frame_support::traits::Hooks; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn base_fee_per_gas_adjustment() { + let (first_block, second_block) = (T::BlockNumber::from(1u32), T::BlockNumber::from(2u32)); + let init_bfpg = BaseFeePerGas::::get(); + + // Setup actions, should ensure default value is written to storage. + Pallet::::on_initialize(first_block); + Pallet::::on_finalize(first_block); + Pallet::::on_initialize(second_block); + + #[block] + { + Pallet::::on_finalize(second_block); + } + + // Ensure that the value has changed. + assert!(BaseFeePerGas::::get() != init_bfpg); + } + + #[benchmark] + fn set_base_fee_per_gas() { + let old_bfpg = BaseFeePerGas::::get(); + let new_bfpg = old_bfpg + 1; + + #[extrinsic_call] + _(RawOrigin::Root, new_bfpg); + + // Ensure that the value has changed. + assert_eq!(BaseFeePerGas::::get(), new_bfpg); + } + + impl_benchmark_test_suite!( + Pallet, + crate::benchmarking::tests::new_test_ext(), + crate::mock::TestRuntime, + ); +} + +#[cfg(test)] +mod tests { + use crate::mock; + use sp_io::TestExternalities; + + pub fn new_test_ext() -> TestExternalities { + mock::ExtBuilder::default().build() + } +} diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 494ce047e1..c5fd9d170d 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -16,19 +16,59 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . -//! 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. +//! Dynamic Evm Base Fee Pallet +//! +//! ## Overview +//! +//! The pallet is responsible for calculating `Base Fee Per Gas` value, according to the current system parameters. +//! This is not like `EIP-1559`, instead it's intended for `Astar` and `Astar-like` networks, which allow both +//! **Substrate native transactions** (which in `Astar` case reuse Polkadot transaction fee approach) +//! and **EVM transactions** (which use `Base Fee Per Gas`). +//! +//! For a more detailed description, reader is advised to refer to Astar Network forum post about [Tokenomics 2.0](https://forum.astar.network/t/astar-tokenomics-2-0-a-dynamically-adjusted-inflation/4924). +//! +//! ## Approach +//! +//! The core formula this pallet tries to satisfy is: +//! +//! base_fee_per_gas = adjustment_factor * weight_factor * 25 / 98974 +//! +//! Where: +//! * **adjustment_factor** - is a value that changes in-between the blocks, related to the block fill ratio. +//! * **weight_factor** - fixed constant, used to convert consumed _weight_ to _fee_. +//! +//! The implementation doesn't make any hard requirements on these values, and only requires that a type implementing `Get<_>` provides them. +//! +//! ## Implementation +//! +//! The core logic is implemented in `on_finalize` hook, which is called at the end of each block. +//! This pallet's hook should be called AFTER whicever pallet's hook is responsible for updating **adjustment factor**. +//! +//! The hook will calculate the ideal new `base_fee_per_gas` value, and then clamp it in between the allowed limits. +//! +//! ## Interface +//! +//! Pallet provides an implementation of `FeeCalculator` trait. This makes it usable directly in `pallet-evm`. +//! +//! A _root-only_ extrinsic is provided to allow setting the `base_fee_per_gas` value manually. +//! +//! ## Practical Remarks +//! +//! According to the proposed **Tokenomics 2.0**, max amount that adjustment factor will be able to change on live networks in-between blocks is: +//! +//! adjustment_new = adjustment_old * (1 + adj + adj^2/2) +//! +//! adj = v * (s - s*) +//! --> recommended _v_ value: 0.000_015 +//! --> larges 's' delta: (1 - 0.25) = **0.75** +//! +//! adj = 0.000015 * (1 - 0.25) = **0.000_011_25** +//! (1 + 0.000_011_25 + 0.000_011_25^2/2) = (1 + 0.000_011_25 + 0.000_000_000_063_281) = **1,000_011_250_063_281** +//! +//! Discarding the **1**, and only considering the decimals, this can be expressed as ratio: +//! Expressed as ratio: 11_250_063_281 / 1_000_000_000_000_000. +//! This is a much smaller change compared to the max step limit ratio we'll use to limit bfpg alignment. +//! This means that once equilibrium is reached (fees are aligned), the `StepLimitRatio` will be larger than the max possible adjustment, essentially eliminating it's effect. #![cfg_attr(not(feature = "std"), no_std)] @@ -43,6 +83,15 @@ mod mock; #[cfg(test)] mod tests; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +// TODO: remove this after proper benchmarking! +pub trait WeightInfo { + fn base_fee_per_gas_adjustment() -> Weight; + fn set_base_fee_per_gas() -> Weight; +} + #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; @@ -71,6 +120,8 @@ pub mod pallet { /// It's expressed as percentage, and used to calculate the delta between the old and new value. /// E.g. if the current 'base fee per gas' is 100, and the limit is 10%, then the new base fee per gas can be between 90 and 110. type StepLimitRatio: Get; + /// Weight information for extrinsics & functions of this pallet. + type WeightInfo: WeightInfo; } #[pallet::type_value] @@ -97,9 +148,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_: T::BlockNumber) -> Weight { - // TODO: benchmark this! - let db_weight = ::DbWeight::get(); - db_weight.reads_writes(2, 1) + T::WeightInfo::base_fee_per_gas_adjustment() } fn on_finalize(_n: ::BlockNumber) { @@ -160,8 +209,10 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// `root-only` extrinsic to set the `base_fee_per_gas` value manually. + /// The specified value has to respect min & max limits configured in the runtime. #[pallet::call_index(0)] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO: weight! + #[pallet::weight(T::WeightInfo::set_base_fee_per_gas())] pub fn set_base_fee_per_gas(origin: OriginFor, fee: U256) -> DispatchResult { ensure_root(origin)?; ensure!( From 3d1d15df952a06f4a4c8770dee330704f8bc07cd Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Mon, 25 Sep 2023 18:08:09 +0200 Subject: [PATCH 12/23] Fixes --- pallets/dynamic-evm-base-fee/src/benchmarking.rs | 4 ++-- pallets/dynamic-evm-base-fee/src/lib.rs | 11 ++++++++++- pallets/dynamic-evm-base-fee/src/mock.rs | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/benchmarking.rs b/pallets/dynamic-evm-base-fee/src/benchmarking.rs index 4b3da9bc05..73aa0fac9c 100644 --- a/pallets/dynamic-evm-base-fee/src/benchmarking.rs +++ b/pallets/dynamic-evm-base-fee/src/benchmarking.rs @@ -67,9 +67,9 @@ mod benchmarks { #[cfg(test)] mod tests { use crate::mock; - use sp_io::TestExternalities; + use frame_support::sp_io::TestExternalities; pub fn new_test_ext() -> TestExternalities { - mock::ExtBuilder::default().build() + mock::ExtBuilder::build() } } diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index c5fd9d170d..c2da00c304 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -31,7 +31,7 @@ //! //! The core formula this pallet tries to satisfy is: //! -//! base_fee_per_gas = adjustment_factor * weight_factor * 25 / 98974 +//! base_fee_per_gas = adjustment_factor * weight_factor * 25 / 98974 //! //! Where: //! * **adjustment_factor** - is a value that changes in-between the blocks, related to the block fill ratio. @@ -92,6 +92,15 @@ pub trait WeightInfo { fn set_base_fee_per_gas() -> Weight; } +impl WeightInfo for () { + fn base_fee_per_gas_adjustment() -> Weight { + Weight::zero() + } + fn set_base_fee_per_gas() -> Weight { + Weight::zero() + } +} + #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index 452f87c2ff..f0e8d0ecde 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -110,6 +110,7 @@ impl pallet_dynamic_evm_base_fee::Config for TestRuntime { type AdjustmentFactor = GetAdjustmentFactor; type WeightFactor = ConstU128<30_000_000_000_000_000>; type StepLimitRatio = StepLimitRation; + type WeightInfo = (); } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; From 7c207a909211303f666c02cfe5032714bbfe4c01 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Tue, 26 Sep 2023 14:45:42 +0200 Subject: [PATCH 13/23] Typo fixes --- pallets/dynamic-evm-base-fee/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index c2da00c304..7e69a07da7 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -42,7 +42,7 @@ //! ## Implementation //! //! The core logic is implemented in `on_finalize` hook, which is called at the end of each block. -//! This pallet's hook should be called AFTER whicever pallet's hook is responsible for updating **adjustment factor**. +//! This pallet's hook should be called AFTER whichever pallet's hook is responsible for updating **adjustment factor**. //! //! The hook will calculate the ideal new `base_fee_per_gas` value, and then clamp it in between the allowed limits. //! @@ -60,7 +60,7 @@ //! //! adj = v * (s - s*) //! --> recommended _v_ value: 0.000_015 -//! --> larges 's' delta: (1 - 0.25) = **0.75** +//! --> largest 's' delta: (1 - 0.25) = **0.75** //! //! adj = 0.000015 * (1 - 0.25) = **0.000_011_25** //! (1 + 0.000_011_25 + 0.000_011_25^2/2) = (1 + 0.000_011_25 + 0.000_000_000_063_281) = **1,000_011_250_063_281** From 19527325f05f8550934e5e1ca13fc7f754c8ef06 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Tue, 26 Sep 2023 18:27:56 +0200 Subject: [PATCH 14/23] Extra rustdocs --- pallets/dynamic-evm-base-fee/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 7e69a07da7..ea8c3003be 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -62,6 +62,9 @@ //! --> recommended _v_ value: 0.000_015 //! --> largest 's' delta: (1 - 0.25) = **0.75** //! +//! (for variable explanation please check the linked forum post above) +//! (in short: `v` - variability factor, `s` - current block fill ratio, `s*` - ideal block fill ratio) +//! //! adj = 0.000015 * (1 - 0.25) = **0.000_011_25** //! (1 + 0.000_011_25 + 0.000_011_25^2/2) = (1 + 0.000_011_25 + 0.000_000_000_063_281) = **1,000_011_250_063_281** //! From 7f7acdf4bad85bf343fd207643f15e6615462776 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Wed, 27 Sep 2023 14:14:54 +0200 Subject: [PATCH 15/23] Shibuya updates, addressing review comments for the pallet --- Cargo.lock | 2 +- Cargo.toml | 1 + .../src/parachain/chain_spec/shibuya.rs | 13 +- .../dynamic-evm-base-fee/src/benchmarking.rs | 9 +- pallets/dynamic-evm-base-fee/src/lib.rs | 3 +- runtime/shibuya/Cargo.toml | 7 +- runtime/shibuya/src/lib.rs | 116 ++++++++++-------- 7 files changed, 86 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 48840c4151..185fcc413a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12406,7 +12406,6 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", - "pallet-base-fee", "pallet-block-reward", "pallet-chain-extension-assets", "pallet-chain-extension-dapps-staking", @@ -12418,6 +12417,7 @@ dependencies = [ "pallet-custom-signatures", "pallet-dapps-staking", "pallet-democracy", + "pallet-dynamic-evm-base-fee", "pallet-ethereum", "pallet-ethereum-checked", "pallet-evm", diff --git a/Cargo.toml b/Cargo.toml index fd6f3ee412..93ccc699d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -278,6 +278,7 @@ pallet-xc-asset-config = { path = "./pallets/xc-asset-config", default-features pallet-xvm = { path = "./pallets/xvm", default-features = false } pallet-xcm = { path = "./pallets/pallet-xcm", default-features = false } pallet-ethereum-checked = { path = "./pallets/ethereum-checked", default-features = false } +pallet-dynamic-evm-base-fee = { path = "./pallets/dynamic-evm-base-fee", default-features = false } astar-primitives = { path = "./primitives", default-features = false } diff --git a/bin/collator/src/parachain/chain_spec/shibuya.rs b/bin/collator/src/parachain/chain_spec/shibuya.rs index 843073ab3b..bdd00127c0 100644 --- a/bin/collator/src/parachain/chain_spec/shibuya.rs +++ b/bin/collator/src/parachain/chain_spec/shibuya.rs @@ -21,11 +21,10 @@ use cumulus_primitives_core::ParaId; use sc_service::ChainType; use shibuya_runtime::{ - wasm_binary_unwrap, AccountId, AuraConfig, AuraId, Balance, BalancesConfig, BaseFeeConfig, - BlockRewardConfig, CollatorSelectionConfig, CouncilConfig, DemocracyConfig, EVMChainIdConfig, - EVMConfig, GenesisConfig, ParachainInfoConfig, Precompiles, SessionConfig, SessionKeys, - Signature, SudoConfig, SystemConfig, TechnicalCommitteeConfig, TreasuryConfig, VestingConfig, - SBY, + wasm_binary_unwrap, AccountId, AuraConfig, AuraId, Balance, BalancesConfig, BlockRewardConfig, + CollatorSelectionConfig, CouncilConfig, DemocracyConfig, EVMChainIdConfig, EVMConfig, + GenesisConfig, ParachainInfoConfig, Precompiles, SessionConfig, SessionKeys, Signature, + SudoConfig, SystemConfig, TechnicalCommitteeConfig, TreasuryConfig, VestingConfig, SBY, }; use sp_core::{sr25519, Pair, Public}; @@ -158,10 +157,6 @@ fn make_genesis( }) .collect(), }, - base_fee: BaseFeeConfig::new( - sp_core::U256::from(1_000_000_000), - sp_runtime::Permill::zero(), - ), evm_chain_id: EVMChainIdConfig { chain_id: 0x51 }, ethereum: Default::default(), polkadot_xcm: Default::default(), diff --git a/pallets/dynamic-evm-base-fee/src/benchmarking.rs b/pallets/dynamic-evm-base-fee/src/benchmarking.rs index 73aa0fac9c..c30ea7e0f9 100644 --- a/pallets/dynamic-evm-base-fee/src/benchmarking.rs +++ b/pallets/dynamic-evm-base-fee/src/benchmarking.rs @@ -29,12 +29,17 @@ mod benchmarks { #[benchmark] fn base_fee_per_gas_adjustment() { let (first_block, second_block) = (T::BlockNumber::from(1u32), T::BlockNumber::from(2u32)); - let init_bfpg = BaseFeePerGas::::get(); - // Setup actions, should ensure default value is written to storage. + // Setup actions, should ensure some value is written to storage. Pallet::::on_initialize(first_block); Pallet::::on_finalize(first_block); + assert!( + BaseFeePerGas::::exists(), + "Value should exist in storage after first on_finalize call" + ); + Pallet::::on_initialize(second_block); + let init_bfpg = BaseFeePerGas::::get(); #[block] { diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index ea8c3003be..1c91973d10 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -71,7 +71,7 @@ //! Discarding the **1**, and only considering the decimals, this can be expressed as ratio: //! Expressed as ratio: 11_250_063_281 / 1_000_000_000_000_000. //! This is a much smaller change compared to the max step limit ratio we'll use to limit bfpg alignment. -//! This means that once equilibrium is reached (fees are aligned), the `StepLimitRatio` will be larger than the max possible adjustment, essentially eliminating it's effect. +//! This means that once equilibrium is reached (fees are aligned), the `StepLimitRatio` will be larger than the max possible adjustment, essentially eliminating its effect. #![cfg_attr(not(feature = "std"), no_std)] @@ -241,6 +241,7 @@ pub mod pallet { impl fp_evm::FeeCalculator for Pallet { fn min_gas_price() -> (U256, Weight) { + // TODO: account for PoV? (BaseFeePerGas::::get(), T::DbWeight::get().reads(1)) } } diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index 20e1cf7efd..0a257f302e 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -41,7 +41,6 @@ pallet-assets = { workspace = true } pallet-aura = { workspace = true } pallet-authorship = { workspace = true } pallet-balances = { workspace = true } -pallet-base-fee = { workspace = true } pallet-collective = { workspace = true } pallet-contracts = { workspace = true } pallet-contracts-primitives = { workspace = true } @@ -102,6 +101,7 @@ pallet-chain-extension-xvm = { workspace = true } pallet-collator-selection = { workspace = true } pallet-custom-signatures = { workspace = true } pallet-dapps-staking = { workspace = true } +pallet-dynamic-evm-base-fee = { workspace = true } pallet-ethereum-checked = { workspace = true } pallet-evm-precompile-assets-erc20 = { workspace = true } pallet-evm-precompile-batch = { workspace = true } @@ -167,7 +167,7 @@ std = [ "pallet-chain-extension-dapps-staking/std", "pallet-chain-extension-xvm/std", "pallet-custom-signatures/std", - "pallet-base-fee/std", + "pallet-dynamic-evm-base-fee/std", "pallet-ethereum/std", "pallet-preimage/std", "pallet-evm/std", @@ -258,6 +258,7 @@ runtime-benchmarks = [ "orml-xtokens/runtime-benchmarks", "astar-primitives/runtime-benchmarks", "pallet-assets/runtime-benchmarks", + "pallet-dynamic-evm-base-fee/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", @@ -301,7 +302,7 @@ try-runtime = [ "parachain-info/try-runtime", "pallet-xvm/try-runtime", "pallet-preimage/try-runtime", - "pallet-base-fee/try-runtime", + "pallet-dynamic-evm-base-fee/try-runtime", "pallet-evm/try-runtime", "pallet-ethereum-checked/try-runtime", "orml-xtokens/try-runtime", diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 2ffdf7848f..571ff256c9 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -58,7 +58,7 @@ use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, + AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, DispatchInfoOf, Dispatchable, OpaqueKeys, PostDispatchInfoOf, UniqueSaturatedInto, Verify, }, transaction_validity::{ @@ -114,8 +114,6 @@ pub const fn deposit(items: u32, bytes: u32) -> Balance { /// /// The slight difference to general `deposit` function is because there is fixed bound on how large the DB /// key can grow so it doesn't make sense to have as high deposit per item as in the general approach. -/// -/// TODO: using this requires storage migration (good to test on Shibuya first!) pub const fn contracts_deposit(items: u32, bytes: u32) -> Balance { items as Balance * 40 * MICROSBY + (bytes as Balance) * STORAGE_BYTE_FEE } @@ -656,10 +654,9 @@ impl pallet_vesting::Config for Runtime { const MAX_VESTING_SCHEDULES: u32 = 28; } -// TODO: changing depost per item and per byte to `deposit` function will require storage migration it seems parameter_types! { - pub const DepositPerItem: Balance = MILLISBY / 1_000_000; - pub const DepositPerByte: Balance = MILLISBY / 1_000_000; + pub const DepositPerItem: Balance = contracts_deposit(1, 0); + pub const DepositPerByte: Balance = contracts_deposit(0, 1); // Fallback value if storage deposit limit not set by the user pub const DefaultDepositLimit: Balance = contracts_deposit(16, 16 * 1024); pub Schedule: pallet_contracts::Schedule = Default::default(); @@ -697,13 +694,15 @@ impl pallet_contracts::Config for Runtime { type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; } +// These values are based on the Astar 2.0 Tokenomics Modeling report. parameter_types! { - pub const TransactionByteFee: Balance = MILLISBY / 100; + pub const TransactionLengthFeeFactor: Balance = 23_500_000_000_000; // 0.000_023_500_000_000_000 SBY per byte + pub const WeightFeeFactor: Balance = 30_855_000_000_000_000; // Around 0.03 SBY per unit of ref time. pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); pub const OperationalFeeMultiplier: u8 = 5; - pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 100_000); - pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000_000u128); - pub MaximumMultiplier: Multiplier = Bounded::max_value(); + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 666_667); // 0.000_015 + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 10); // 0.1 + pub MaximumMultiplier: Multiplier = Multiplier::saturating_from_integer(10); // 10 } /// Handles converting a weight scalar to a fee value, based on the scale and granularity of the @@ -720,9 +719,8 @@ pub struct WeightToFee; impl WeightToFeePolynomial for WeightToFee { type Balance = Balance; fn polynomial() -> WeightToFeeCoefficients { - // in Shibuya, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 mSBY: - let p = MILLISBY; - let q = 10 * Balance::from(ExtrinsicBaseWeight::get().ref_time()); + let p = WeightFeeFactor::get(); + let q = Balance::from(ExtrinsicBaseWeight::get().ref_time()); smallvec::smallvec![WeightToFeeCoefficient { degree: 1, negative: false, @@ -763,7 +761,33 @@ impl pallet_transaction_payment::Config for Runtime { MinimumMultiplier, MaximumMultiplier, >; - type LengthToFee = ConstantMultiplier; + type LengthToFee = ConstantMultiplier; +} + +parameter_types! { + pub DefaultBaseFeePerGas: U256 = U256::from(1_470_000_000_000_u128); + pub MinBaseFeePerGas: U256 = U256::from(800_000_000_000_u128); + pub MaxBaseFeePerGas: U256 = U256::from(80_000_000_000_000_u128); + pub StepLimitRatio: Perquintill = Perquintill::from_rational(5_u128, 100_000); +} + +/// Simple wrapper for fetching current native transaction fee weight fee multiplier. +pub struct AdjustmentFactorGetter; +impl Get for AdjustmentFactorGetter { + fn get() -> Multiplier { + pallet_transaction_payment::NextFeeMultiplier::::get() + } +} + +impl pallet_dynamic_evm_base_fee::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DefaultBaseFeePerGas = DefaultBaseFeePerGas; + type MinBaseFeePerGas = MinBaseFeePerGas; + type MaxBaseFeePerGas = MaxBaseFeePerGas; + type AdjustmentFactor = AdjustmentFactorGetter; + type WeightFactor = WeightFeeFactor; + type StepLimitRatio = StepLimitRatio; + type WeightInfo = (); } ///TODO: Placeholder account mapping. This would be replaced once account abstraction is finished. @@ -797,33 +821,6 @@ impl pallet_xvm::Config for Runtime { type WeightInfo = pallet_xvm::weights::SubstrateWeight; } -parameter_types! { - // Tells `pallet_base_fee` whether to calculate a new BaseFee `on_finalize` or not. - pub DefaultBaseFeePerGas: U256 = (MILLISBY / 1_000_000).into(); - // At the moment, we don't use dynamic fee calculation for Shibuya by default - pub DefaultElasticity: Permill = Permill::zero(); -} - -pub struct BaseFeeThreshold; -impl pallet_base_fee::BaseFeeThreshold for BaseFeeThreshold { - fn lower() -> Permill { - Permill::zero() - } - fn ideal() -> Permill { - Permill::from_parts(500_000) - } - fn upper() -> Permill { - Permill::from_parts(1_000_000) - } -} - -impl pallet_base_fee::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Threshold = BaseFeeThreshold; - type DefaultBaseFeePerGas = DefaultBaseFeePerGas; - type DefaultElasticity = DefaultElasticity; -} - /// Current approximation of the gas/s consumption considering /// EVM execution over compiled WASM (on 4.4Ghz CPU). /// Given the 500ms Weight, from which 75% only are used for transactions, @@ -866,7 +863,7 @@ parameter_types! { } impl pallet_evm::Config for Runtime { - type FeeCalculator = BaseFee; + type FeeCalculator = DynamicEvmBaseFee; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type WeightPerGas = WeightPerGas; type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; @@ -1142,7 +1139,7 @@ impl InstanceFilter for ProxyType { // Skip entire EVM pallet // Skip entire Ethereum pallet // Skip entire EthCall pallet - | RuntimeCall::BaseFee(..) + | RuntimeCall::DynamicEvmBaseFee(..) // Skip entire Contracts pallet | RuntimeCall::Democracy(..) | RuntimeCall::Council(..) @@ -1274,7 +1271,7 @@ construct_runtime!( EVM: pallet_evm = 60, Ethereum: pallet_ethereum = 61, - BaseFee: pallet_base_fee = 62, + DynamicEvmBaseFee: pallet_dynamic_evm_base_fee = 62, EVMChainId: pallet_evm_chain_id = 63, EthereumChecked: pallet_ethereum_checked = 64, @@ -1326,17 +1323,37 @@ pub type Executive = frame_executive::Executive< Migrations, >; -// Used to cleanup StateTrieMigration storage - remove once cleanup is done. +// Used to cleanup BaseFee storage - remove once cleanup is done. parameter_types! { - pub const StateTrieMigrationStr: &'static str = "StateTrieMigration"; + pub const BaseFeeStr: &'static str = "BaseFee"; +} + +/// Simple `OnRuntimeUpgrade` logic to prepare Shibuya runtime for `DynamicEvmBaseFee` pallet. +pub use frame_support::traits::OnRuntimeUpgrade; +pub struct DynamicEvmBaseFeeMigration; +impl OnRuntimeUpgrade for DynamicEvmBaseFeeMigration { + fn on_runtime_upgrade() -> Weight { + // Safety check to ensure we don't execute this migration twice + if pallet_dynamic_evm_base_fee::BaseFeePerGas::::exists() { + return ::DbWeight::get().reads(1); + } + + // Set the init value to what was set before on the old `BaseFee` pallet. + pallet_dynamic_evm_base_fee::BaseFeePerGas::::put(U256::from(1_000_000_000_u128)); + + // Shibuya's multiplier is so low that we have to set it to minimum value directly. + pallet_transaction_payment::NextFeeMultiplier::::put(MinimumMultiplier::get()); + + ::DbWeight::get().reads_writes(1, 2) + } } /// All migrations that will run on the next runtime upgrade. /// /// Once done, migrations should be removed from the tuple. pub type Migrations = ( - frame_support::migrations::RemovePallet, - pallet_contracts::Migration, + frame_support::migrations::RemovePallet, + DynamicEvmBaseFeeMigration, ); type EventRecord = frame_system::EventRecord< @@ -1421,6 +1438,7 @@ mod benches { [pallet_xcm, PolkadotXcm] [pallet_ethereum_checked, EthereumChecked] [pallet_xvm, Xvm] + [pallet_dynamic_evm_base_fee, DynamicEvmBaseFee] ); } @@ -1783,7 +1801,7 @@ impl_runtime_apis! { } fn elasticity() -> Option { - Some(pallet_base_fee::Elasticity::::get()) + Some(Permill::zero()) } fn gas_limit_multiplier_support() {} From 866a0bac55e13b739ce4b1c2617c4e71733e57b8 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Wed, 27 Sep 2023 15:08:39 +0200 Subject: [PATCH 16/23] Temp weight benchmarks --- pallets/dynamic-evm-base-fee/src/lib.rs | 16 +-- pallets/dynamic-evm-base-fee/src/weights.rs | 109 ++++++++++++++++++++ runtime/shibuya/src/lib.rs | 2 +- 3 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 pallets/dynamic-evm-base-fee/src/weights.rs diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 1c91973d10..2baeccd89f 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -89,20 +89,8 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -// TODO: remove this after proper benchmarking! -pub trait WeightInfo { - fn base_fee_per_gas_adjustment() -> Weight; - fn set_base_fee_per_gas() -> Weight; -} - -impl WeightInfo for () { - fn base_fee_per_gas_adjustment() -> Weight { - Weight::zero() - } - fn set_base_fee_per_gas() -> Weight { - Weight::zero() - } -} +pub mod weights; +pub use weights::WeightInfo; #[frame_support::pallet] pub mod pallet { diff --git a/pallets/dynamic-evm-base-fee/src/weights.rs b/pallets/dynamic-evm-base-fee/src/weights.rs new file mode 100644 index 0000000000..467ff6470d --- /dev/null +++ b/pallets/dynamic-evm-base-fee/src/weights.rs @@ -0,0 +1,109 @@ + +// This file is part of Astar. + +// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +//! Autogenerated weights for pallet_dynamic_evm_base_fee +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-09-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Dinos-MBP.fritz.box`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=shibuya-dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_dynamic_evm_base_fee +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=temp.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_dynamic_evm_base_fee. +pub trait WeightInfo { + fn base_fee_per_gas_adjustment() -> Weight; + fn set_base_fee_per_gas() -> Weight; +} + +/// Weights for pallet_dynamic_evm_base_fee using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: DynamicEvmBaseFee BaseFeePerGas (r:1 w:1) + /// Proof: DynamicEvmBaseFee BaseFeePerGas (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + fn base_fee_per_gas_adjustment() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 1517) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: DynamicEvmBaseFee BaseFeePerGas (r:0 w:1) + /// Proof: DynamicEvmBaseFee BaseFeePerGas (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_base_fee_per_gas() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: DynamicEvmBaseFee BaseFeePerGas (r:1 w:1) + /// Proof: DynamicEvmBaseFee BaseFeePerGas (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + /// Storage: TransactionPayment NextFeeMultiplier (r:1 w:0) + /// Proof: TransactionPayment NextFeeMultiplier (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + fn base_fee_per_gas_adjustment() -> Weight { + // Proof Size summary in bytes: + // Measured: `165` + // Estimated: `1517` + // Minimum execution time: 9_000_000 picoseconds. + Weight::from_parts(9_000_000, 1517) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: DynamicEvmBaseFee BaseFeePerGas (r:0 w:1) + /// Proof: DynamicEvmBaseFee BaseFeePerGas (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) + fn set_base_fee_per_gas() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 571ff256c9..571fa3a297 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -787,7 +787,7 @@ impl pallet_dynamic_evm_base_fee::Config for Runtime { type AdjustmentFactor = AdjustmentFactorGetter; type WeightFactor = WeightFeeFactor; type StepLimitRatio = StepLimitRatio; - type WeightInfo = (); + type WeightInfo = pallet_dynamic_evm_base_fee::weights::SubstrateWeight; } ///TODO: Placeholder account mapping. This would be replaced once account abstraction is finished. From a8900aa52d3e663f68e60da8dceebc7dc0a907fc Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Wed, 27 Sep 2023 16:42:02 +0200 Subject: [PATCH 17/23] Storage version, additional benchmark --- .../dynamic-evm-base-fee/src/benchmarking.rs | 18 ++++++++++++++++++ pallets/dynamic-evm-base-fee/src/lib.rs | 9 ++++++--- pallets/dynamic-evm-base-fee/src/weights.rs | 7 +++++++ runtime/shibuya/src/lib.rs | 5 ++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/benchmarking.rs b/pallets/dynamic-evm-base-fee/src/benchmarking.rs index c30ea7e0f9..c0d4ab9f23 100644 --- a/pallets/dynamic-evm-base-fee/src/benchmarking.rs +++ b/pallets/dynamic-evm-base-fee/src/benchmarking.rs @@ -62,6 +62,24 @@ mod benchmarks { assert_eq!(BaseFeePerGas::::get(), new_bfpg); } + #[benchmark] + fn min_gas_price() { + let first_block = T::BlockNumber::from(1u32); + + // Setup actions, should ensure some value is written to storage. + Pallet::::on_initialize(first_block); + Pallet::::on_finalize(first_block); + assert!( + BaseFeePerGas::::exists(), + "Value should exist in storage after first on_finalize call" + ); + + #[block] + { + let _ = Pallet::::min_gas_price(); + } + } + impl_benchmark_test_suite!( Pallet, crate::benchmarking::tests::new_test_ext(), diff --git a/pallets/dynamic-evm-base-fee/src/lib.rs b/pallets/dynamic-evm-base-fee/src/lib.rs index 2baeccd89f..5574346825 100644 --- a/pallets/dynamic-evm-base-fee/src/lib.rs +++ b/pallets/dynamic-evm-base-fee/src/lib.rs @@ -75,7 +75,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{traits::Get, weights::Weight}; +use frame_support::weights::Weight; use sp_core::U256; use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber, FixedU128, Perquintill}; @@ -99,7 +99,11 @@ pub mod pallet { use super::*; + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); #[pallet::config] @@ -229,7 +233,6 @@ pub mod pallet { impl fp_evm::FeeCalculator for Pallet { fn min_gas_price() -> (U256, Weight) { - // TODO: account for PoV? - (BaseFeePerGas::::get(), T::DbWeight::get().reads(1)) + (BaseFeePerGas::::get(), T::WeightInfo::min_gas_price()) } } diff --git a/pallets/dynamic-evm-base-fee/src/weights.rs b/pallets/dynamic-evm-base-fee/src/weights.rs index 467ff6470d..aabce1015f 100644 --- a/pallets/dynamic-evm-base-fee/src/weights.rs +++ b/pallets/dynamic-evm-base-fee/src/weights.rs @@ -51,6 +51,7 @@ use core::marker::PhantomData; pub trait WeightInfo { fn base_fee_per_gas_adjustment() -> Weight; fn set_base_fee_per_gas() -> Weight; + fn min_gas_price() -> Weight; } /// Weights for pallet_dynamic_evm_base_fee using the Substrate node and recommended hardware. @@ -79,6 +80,9 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(9_000_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } + fn min_gas_price() -> Weight { + Weight::from_parts(0, 0) + } } // For backwards compatibility and tests @@ -106,4 +110,7 @@ impl WeightInfo for () { Weight::from_parts(9_000_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + fn min_gas_price() -> Weight { + Weight::from_parts(0, 0) + } } diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 571fa3a297..242bfb82fc 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -1329,7 +1329,7 @@ parameter_types! { } /// Simple `OnRuntimeUpgrade` logic to prepare Shibuya runtime for `DynamicEvmBaseFee` pallet. -pub use frame_support::traits::OnRuntimeUpgrade; +pub use frame_support::traits::{OnRuntimeUpgrade, StorageVersion}; pub struct DynamicEvmBaseFeeMigration; impl OnRuntimeUpgrade for DynamicEvmBaseFeeMigration { fn on_runtime_upgrade() -> Weight { @@ -1344,6 +1344,9 @@ impl OnRuntimeUpgrade for DynamicEvmBaseFeeMigration { // Shibuya's multiplier is so low that we have to set it to minimum value directly. pallet_transaction_payment::NextFeeMultiplier::::put(MinimumMultiplier::get()); + // Set init storage version for the pallet + StorageVersion::new(1).put::>(); + ::DbWeight::get().reads_writes(1, 2) } } From aabd2286dc9184bf6472f596f0f338383b912678 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Wed, 27 Sep 2023 16:53:19 +0200 Subject: [PATCH 18/23] Fix writes --- runtime/shibuya/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 242bfb82fc..6f188c38bf 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -1347,7 +1347,7 @@ impl OnRuntimeUpgrade for DynamicEvmBaseFeeMigration { // Set init storage version for the pallet StorageVersion::new(1).put::>(); - ::DbWeight::get().reads_writes(1, 2) + ::DbWeight::get().reads_writes(1, 3) } } From 2a60a5c6f78c5fa9c553b2b34a8b16f9f99bdf02 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 28 Sep 2023 08:48:20 +0200 Subject: [PATCH 19/23] Compilation & test fixes --- pallets/dynamic-evm-base-fee/src/benchmarking.rs | 1 + pallets/dynamic-evm-base-fee/src/mock.rs | 2 +- pallets/dynamic-evm-base-fee/src/tests.rs | 12 +++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/benchmarking.rs b/pallets/dynamic-evm-base-fee/src/benchmarking.rs index c0d4ab9f23..eb94e5fc67 100644 --- a/pallets/dynamic-evm-base-fee/src/benchmarking.rs +++ b/pallets/dynamic-evm-base-fee/src/benchmarking.rs @@ -18,6 +18,7 @@ use super::*; +use fp_evm::FeeCalculator; use frame_benchmarking::v2::*; use frame_support::traits::Hooks; use frame_system::RawOrigin; diff --git a/pallets/dynamic-evm-base-fee/src/mock.rs b/pallets/dynamic-evm-base-fee/src/mock.rs index f0e8d0ecde..3d7db1e4cb 100644 --- a/pallets/dynamic-evm-base-fee/src/mock.rs +++ b/pallets/dynamic-evm-base-fee/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ construct_runtime, parameter_types, sp_io::TestExternalities, storage, - traits::{ConstU128, ConstU32, ConstU64}, + traits::{ConstU128, ConstU32, ConstU64, Get}, weights::constants::RocksDbWeight, }; use parity_scale_codec::Encode; diff --git a/pallets/dynamic-evm-base-fee/src/tests.rs b/pallets/dynamic-evm-base-fee/src/tests.rs index c26b0096ae..dc65f31fe6 100644 --- a/pallets/dynamic-evm-base-fee/src/tests.rs +++ b/pallets/dynamic-evm-base-fee/src/tests.rs @@ -21,7 +21,10 @@ use super::*; use mock::*; -use frame_support::{assert_noop, assert_ok, traits::OnFinalize}; +use frame_support::{ + assert_noop, assert_ok, + traits::{Get, OnFinalize}, +}; use num_traits::Bounded; use sp_runtime::{ traits::{BadOrigin, One, Zero}, @@ -118,12 +121,11 @@ fn min_gas_price_works() { new_base_fee_per_gas )); + let expected_weight: Weight = + <::WeightInfo as weights::WeightInfo>::min_gas_price(); assert_eq!( DynamicEvmBaseFee::min_gas_price(), - ( - new_base_fee_per_gas, - ::DbWeight::get().reads(1) - ) + (new_base_fee_per_gas, expected_weight) ); }); } From 8f616da60b882f523da899304e83fef48a5b7433 Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 28 Sep 2023 09:20:59 +0200 Subject: [PATCH 20/23] Integrated local --- Cargo.lock | 3 +- bin/collator/src/local/chain_spec.rs | 12 +-- runtime/local/Cargo.toml | 8 +- runtime/local/src/lib.rs | 118 +++++++++++++++++---------- 4 files changed, 88 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 185fcc413a..6ffd811f28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5515,7 +5515,6 @@ dependencies = [ "pallet-assets", "pallet-aura", "pallet-balances", - "pallet-base-fee", "pallet-block-reward", "pallet-chain-extension-assets", "pallet-chain-extension-dapps-staking", @@ -5526,6 +5525,7 @@ dependencies = [ "pallet-custom-signatures", "pallet-dapps-staking", "pallet-democracy", + "pallet-dynamic-evm-base-fee", "pallet-ethereum", "pallet-ethereum-checked", "pallet-evm", @@ -5557,6 +5557,7 @@ dependencies = [ "pallet-xvm", "parity-scale-codec", "scale-info", + "smallvec", "sp-api", "sp-block-builder", "sp-consensus-aura", diff --git a/bin/collator/src/local/chain_spec.rs b/bin/collator/src/local/chain_spec.rs index 8d56c8747c..798370c075 100644 --- a/bin/collator/src/local/chain_spec.rs +++ b/bin/collator/src/local/chain_spec.rs @@ -19,10 +19,10 @@ //! Chain specifications. use local_runtime::{ - wasm_binary_unwrap, AccountId, AuraConfig, AuraId, BalancesConfig, BaseFeeConfig, - BlockRewardConfig, CouncilConfig, DemocracyConfig, EVMConfig, GenesisConfig, GrandpaConfig, - GrandpaId, Precompiles, Signature, SudoConfig, SystemConfig, TechnicalCommitteeConfig, - TreasuryConfig, VestingConfig, + wasm_binary_unwrap, AccountId, AuraConfig, AuraId, BalancesConfig, BlockRewardConfig, + CouncilConfig, DemocracyConfig, EVMConfig, GenesisConfig, GrandpaConfig, GrandpaId, + Precompiles, Signature, SudoConfig, SystemConfig, TechnicalCommitteeConfig, TreasuryConfig, + VestingConfig, }; use sc_service::ChainType; use sp_core::{crypto::Ss58Codec, sr25519, Pair, Public}; @@ -154,10 +154,6 @@ fn testnet_genesis( .collect(), }, ethereum: Default::default(), - base_fee: BaseFeeConfig::new( - sp_core::U256::from(1_000_000_000u64), - sp_runtime::Permill::zero(), - ), sudo: SudoConfig { key: Some(root_key), }, diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml index 44012e1760..9b85a0a9d5 100644 --- a/runtime/local/Cargo.toml +++ b/runtime/local/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true log = { workspace = true, optional = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } +smallvec = { workspace = true } fp-rpc = { workspace = true } fp-self-contained = { workspace = true } @@ -20,7 +21,6 @@ frame-system = { workspace = true } pallet-assets = { workspace = true } pallet-aura = { workspace = true } pallet-balances = { workspace = true } -pallet-base-fee = { workspace = true } pallet-collective = { workspace = true } pallet-contracts = { workspace = true } pallet-contracts-primitives = { workspace = true } @@ -71,6 +71,7 @@ pallet-chain-extension-dapps-staking = { workspace = true } pallet-chain-extension-xvm = { workspace = true } pallet-custom-signatures = { workspace = true } pallet-dapps-staking = { workspace = true } +pallet-dynamic-evm-base-fee = { workspace = true } pallet-evm-precompile-assets-erc20 = { workspace = true } pallet-evm-precompile-dapps-staking = { workspace = true } pallet-evm-precompile-sr25519 = { workspace = true } @@ -117,7 +118,7 @@ std = [ "pallet-chain-extension-xvm/std", "pallet-custom-signatures/std", "pallet-dapps-staking/std", - "pallet-base-fee/std", + "pallet-dynamic-evm-base-fee/std", "pallet-ethereum/std", "pallet-evm/std", "pallet-evm-precompile-blake2/std", @@ -185,6 +186,7 @@ runtime-benchmarks = [ "pallet-ethereum-checked/runtime-benchmarks", "astar-primitives/runtime-benchmarks", "pallet-assets/runtime-benchmarks", + "pallet-dynamic-evm-base-fee/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", @@ -215,7 +217,7 @@ try-runtime = [ "pallet-proxy/try-runtime", "pallet-treasury/try-runtime", "pallet-preimage/try-runtime", - "pallet-base-fee/try-runtime", + "pallet-dynamic-evm-base-fee/try-runtime", "pallet-evm/try-runtime", "pallet-ethereum-checked/try-runtime", ] diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs index 40c1e7e9a3..23f9f5b67f 100644 --- a/runtime/local/src/lib.rs +++ b/runtime/local/src/lib.rs @@ -31,8 +31,9 @@ use frame_support::{ EqualPrivilegeOnly, FindAuthor, Get, InstanceFilter, Nothing, OnFinalize, WithdrawReasons, }, weights::{ - constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, - ConstantMultiplier, IdentityFee, Weight, + constants::{ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}, + ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, + WeightToFeePolynomial, }, ConsensusEngineId, PalletId, }; @@ -44,6 +45,7 @@ use pallet_ethereum::PostLogContent; use pallet_evm::{FeeCalculator, GasWeightMapping, Runner}; use pallet_evm_precompile_assets_erc20::AddressToAssetId; use pallet_grandpa::{fg_primitives, AuthorityList as GrandpaAuthorityList}; +use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use parity_scale_codec::{Compact, Decode, Encode, MaxEncodedLen}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, ConstBool, OpaqueMetadata, H160, H256, U256}; @@ -56,7 +58,7 @@ use sp_runtime::{ transaction_validity::{ TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, }, - ApplyExtrinsicResult, RuntimeDebug, + ApplyExtrinsicResult, FixedPointNumber, Perbill, Permill, Perquintill, RuntimeDebug, }; use sp_std::prelude::*; @@ -73,12 +75,9 @@ pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_grandpa::AuthorityId as GrandpaId; pub use pallet_timestamp::Call as TimestampCall; -use pallet_transaction_payment::CurrencyAdapter; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -pub use sp_runtime::{Perbill, Permill}; - #[cfg(feature = "std")] /// Wasm binary unwrapped. If built with `BUILD_DUMMY_WASM_BINARY`, the function panics. pub fn wasm_binary_unwrap() -> &'static [u8] { @@ -332,18 +331,81 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = astar_primitives::benchmarks::AssetsBenchmarkHelper; } +// These values are based on the Astar 2.0 Tokenomics Modeling report. parameter_types! { - pub const TransactionByteFee: Balance = 1; + pub const TransactionLengthFeeFactor: Balance = 23_500_000_000_000; // 0.000_023_500_000_000_000 SBY per byte + pub const WeightFeeFactor: Balance = 30_855_000_000_000_000; // Around 0.03 SBY per unit of ref time. + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); pub const OperationalFeeMultiplier: u8 = 5; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 666_667); // 0.000_015 + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 10); // 0.1 + pub MaximumMultiplier: Multiplier = Multiplier::saturating_from_integer(10); // 10 +} + +/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the +/// node's balance type. +/// +/// This should typically create a mapping between the following ranges: +/// - [0, MAXIMUM_BLOCK_WEIGHT] +/// - [Balance::min, Balance::max] +/// +/// Yet, it can be used for any other sort of change to weight-fee. Some examples being: +/// - Setting it to `0` will essentially disable the weight fee. +/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged. +pub struct WeightToFee; +impl WeightToFeePolynomial for WeightToFee { + type Balance = Balance; + fn polynomial() -> WeightToFeeCoefficients { + let p = WeightFeeFactor::get(); + let q = Balance::from(ExtrinsicBaseWeight::get().ref_time()); + smallvec::smallvec![WeightToFeeCoefficient { + degree: 1, + negative: false, + coeff_frac: Perbill::from_rational(p % q, q), + coeff_integer: p / q, + }] + } } impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = CurrencyAdapter; - type WeightToFee = IdentityFee; + type WeightToFee = WeightToFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; - type FeeMultiplierUpdate = (); - type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = TargetedFeeAdjustment< + Self, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type LengthToFee = ConstantMultiplier; +} + +parameter_types! { + pub DefaultBaseFeePerGas: U256 = U256::from(1_470_000_000_000_u128); + pub MinBaseFeePerGas: U256 = U256::from(800_000_000_000_u128); + pub MaxBaseFeePerGas: U256 = U256::from(80_000_000_000_000_u128); + pub StepLimitRatio: Perquintill = Perquintill::from_rational(5_u128, 100_000); +} + +/// Simple wrapper for fetching current native transaction fee weight fee multiplier. +pub struct AdjustmentFactorGetter; +impl Get for AdjustmentFactorGetter { + fn get() -> Multiplier { + pallet_transaction_payment::NextFeeMultiplier::::get() + } +} + +impl pallet_dynamic_evm_base_fee::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type DefaultBaseFeePerGas = DefaultBaseFeePerGas; + type MinBaseFeePerGas = MinBaseFeePerGas; + type MaxBaseFeePerGas = MaxBaseFeePerGas; + type AdjustmentFactor = AdjustmentFactorGetter; + type WeightFactor = WeightFeeFactor; + type StepLimitRatio = StepLimitRatio; + type WeightInfo = pallet_dynamic_evm_base_fee::weights::SubstrateWeight; } parameter_types! { @@ -478,33 +540,6 @@ impl pallet_xvm::Config for Runtime { type WeightInfo = pallet_xvm::weights::SubstrateWeight; } -parameter_types! { - // Tells `pallet_base_fee` whether to calculate a new BaseFee `on_finalize` or not. - pub DefaultBaseFeePerGas: U256 = (MILLIAST / 1_000_000).into(); - // At the moment, we don't use dynamic fee calculation for local chain by default - pub DefaultElasticity: Permill = Permill::zero(); -} - -pub struct BaseFeeThreshold; -impl pallet_base_fee::BaseFeeThreshold for BaseFeeThreshold { - fn lower() -> Permill { - Permill::zero() - } - fn ideal() -> Permill { - Permill::from_parts(500_000) - } - fn upper() -> Permill { - Permill::from_parts(1_000_000) - } -} - -impl pallet_base_fee::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Threshold = BaseFeeThreshold; - type DefaultBaseFeePerGas = DefaultBaseFeePerGas; - type DefaultElasticity = DefaultElasticity; -} - /// Current approximation of the gas/s consumption considering /// EVM execution over compiled WASM (on 4.4Ghz CPU). /// Given the 500ms Weight, from which 75% only are used for transactions, @@ -553,7 +588,7 @@ parameter_types! { } impl pallet_evm::Config for Runtime { - type FeeCalculator = BaseFee; + type FeeCalculator = DynamicEvmBaseFee; type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type WeightPerGas = WeightPerGas; type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; @@ -911,7 +946,7 @@ impl InstanceFilter for ProxyType { // Skip entire EVM pallet // Skip entire Ethereum pallet // Skip entire EthCall pallet - | RuntimeCall::BaseFee(..) + | RuntimeCall::DynamicEvmBaseFee(..) // Skip entire Contracts pallet | RuntimeCall::Democracy(..) | RuntimeCall::Council(..) @@ -1010,7 +1045,7 @@ construct_runtime!( EVM: pallet_evm, Ethereum: pallet_ethereum, EthCall: pallet_custom_signatures, - BaseFee: pallet_base_fee, + DynamicEvmBaseFee: pallet_dynamic_evm_base_fee, Contracts: pallet_contracts, Sudo: pallet_sudo, Assets: pallet_assets, @@ -1137,6 +1172,7 @@ mod benches { [pallet_dapps_staking, DappsStaking] [pallet_block_reward, BlockReward] [pallet_ethereum_checked, EthereumChecked] + [pallet_dynamic_evm_base_fee, DynamicEvmBaseFee] ); } @@ -1529,7 +1565,7 @@ impl_runtime_apis! { } fn elasticity() -> Option { - Some(pallet_base_fee::Elasticity::::get()) + Some(Permill::zero()) } fn gas_limit_multiplier_support() {} From cd1e8e3cf45d8b970f9229ca47260a27fe1fca9d Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 28 Sep 2023 14:02:49 +0200 Subject: [PATCH 21/23] Integration adaptation --- tests/integration/src/setup.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/src/setup.rs b/tests/integration/src/setup.rs index aa46e80b12..bccdb2c244 100644 --- a/tests/integration/src/setup.rs +++ b/tests/integration/src/setup.rs @@ -217,7 +217,7 @@ pub fn run_to_block(n: u32) { AuraExt::on_finalize(block_number); PolkadotXcm::on_finalize(block_number); Ethereum::on_finalize(block_number); - BaseFee::on_finalize(block_number); + DynamicEvmBaseFee::on_finalize(block_number); System::set_block_number(block_number + 1); @@ -227,7 +227,7 @@ pub fn run_to_block(n: u32) { Aura::on_initialize(block_number); AuraExt::on_initialize(block_number); Ethereum::on_initialize(block_number); - BaseFee::on_initialize(block_number); + DynamicEvmBaseFee::on_initialize(block_number); #[cfg(any(feature = "shibuya", feature = "shiden", features = "astar"))] RandomnessCollectiveFlip::on_initialize(block_number); From 79a21a446e837354e166eda2646719830086719a Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 28 Sep 2023 14:29:31 +0200 Subject: [PATCH 22/23] Fixes for integration compilation --- tests/integration/src/setup.rs | 7 +++++++ tests/integration/src/xvm.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/integration/src/setup.rs b/tests/integration/src/setup.rs index bccdb2c244..d0de6fb599 100644 --- a/tests/integration/src/setup.rs +++ b/tests/integration/src/setup.rs @@ -211,12 +211,16 @@ pub fn run_to_block(n: u32) { while System::block_number() < n { let block_number = System::block_number(); Timestamp::set_timestamp(block_number as u64 * BLOCK_TIME); + TransactionPayment::on_finalize(block_number); DappsStaking::on_finalize(block_number); Authorship::on_finalize(block_number); Session::on_finalize(block_number); AuraExt::on_finalize(block_number); PolkadotXcm::on_finalize(block_number); Ethereum::on_finalize(block_number); + #[cfg(any(feature = "shiden", features = "astar"))] + BaseFee::on_finalize(block_number); + #[cfg(any(feature = "shibuya"))] DynamicEvmBaseFee::on_finalize(block_number); System::set_block_number(block_number + 1); @@ -227,6 +231,9 @@ pub fn run_to_block(n: u32) { Aura::on_initialize(block_number); AuraExt::on_initialize(block_number); Ethereum::on_initialize(block_number); + #[cfg(any(feature = "shiden", features = "astar"))] + BaseFee::on_initialize(block_number); + #[cfg(any(feature = "shibuya"))] DynamicEvmBaseFee::on_initialize(block_number); #[cfg(any(feature = "shibuya", feature = "shiden", features = "astar"))] RandomnessCollectiveFlip::on_initialize(block_number); diff --git a/tests/integration/src/xvm.rs b/tests/integration/src/xvm.rs index 0ec64a74f7..5d2874ce91 100644 --- a/tests/integration/src/xvm.rs +++ b/tests/integration/src/xvm.rs @@ -762,7 +762,7 @@ fn calling_wasm_from_evm_works_if_sufficient_storage_deposit_limit() { let wasm_callee_addr = deploy_wasm_contract(WASM_SIMPLE_STORAGE_NAME); let evm_caller_addr = deploy_evm_contract(CALL_XVM_PAYABLE_WITH_SDL); - // Fund the EVM caller to pay for storage deposit. + // Fund the EVM contract to pay for storage deposit. let _ = Balances::deposit_creating(&account_id_from(evm_caller_addr.clone()), UNIT); assert_ok!(EVM::call( From 43d73b92df2d1b3929ac0b03c9450b758e8d29ec Mon Sep 17 00:00:00 2001 From: Dino Pacandi Date: Thu, 28 Sep 2023 14:57:12 +0200 Subject: [PATCH 23/23] Integration test fix & new weights --- pallets/dynamic-evm-base-fee/src/weights.rs | 40 ++++++++++++++------- tests/integration/src/xvm.rs | 4 +-- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/pallets/dynamic-evm-base-fee/src/weights.rs b/pallets/dynamic-evm-base-fee/src/weights.rs index aabce1015f..46c67f3946 100644 --- a/pallets/dynamic-evm-base-fee/src/weights.rs +++ b/pallets/dynamic-evm-base-fee/src/weights.rs @@ -20,9 +20,9 @@ //! Autogenerated weights for pallet_dynamic_evm_base_fee //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-09-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `Dinos-MBP.fritz.box`, CPU: `` +//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024 // Executed Command: @@ -37,7 +37,7 @@ // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=temp.rs +// --output=./benchmark-results/dynamic_evm_base_fee_weights.rs // --template=./scripts/templates/weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -65,8 +65,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 9_000_000 picoseconds. - Weight::from_parts(9_000_000, 1517) + // Minimum execution time: 8_560_000 picoseconds. + Weight::from_parts(8_778_000, 1517) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -76,12 +76,19 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_000_000 picoseconds. - Weight::from_parts(9_000_000, 0) + // Minimum execution time: 7_883_000 picoseconds. + Weight::from_parts(8_060_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: DynamicEvmBaseFee BaseFeePerGas (r:1 w:0) + /// Proof: DynamicEvmBaseFee BaseFeePerGas (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) fn min_gas_price() -> Weight { - Weight::from_parts(0, 0) + // Proof Size summary in bytes: + // Measured: `98` + // Estimated: `1517` + // Minimum execution time: 4_201_000 picoseconds. + Weight::from_parts(4_399_000, 1517) + .saturating_add(T::DbWeight::get().reads(1_u64)) } } @@ -95,8 +102,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `165` // Estimated: `1517` - // Minimum execution time: 9_000_000 picoseconds. - Weight::from_parts(9_000_000, 1517) + // Minimum execution time: 8_560_000 picoseconds. + Weight::from_parts(8_778_000, 1517) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -106,11 +113,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_000_000 picoseconds. - Weight::from_parts(9_000_000, 0) + // Minimum execution time: 7_883_000 picoseconds. + Weight::from_parts(8_060_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + /// Storage: DynamicEvmBaseFee BaseFeePerGas (r:1 w:0) + /// Proof: DynamicEvmBaseFee BaseFeePerGas (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) fn min_gas_price() -> Weight { - Weight::from_parts(0, 0) + // Proof Size summary in bytes: + // Measured: `98` + // Estimated: `1517` + // Minimum execution time: 4_201_000 picoseconds. + Weight::from_parts(4_399_000, 1517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } } diff --git a/tests/integration/src/xvm.rs b/tests/integration/src/xvm.rs index 5d2874ce91..bf179f5ab5 100644 --- a/tests/integration/src/xvm.rs +++ b/tests/integration/src/xvm.rs @@ -772,8 +772,8 @@ fn calling_wasm_from_evm_works_if_sufficient_storage_deposit_limit() { // to: 0x0e0ddb5a5f0b99d7be468a3051a94073ec6b1900178316401a52b93415026999 // input: 0x0000002a (store) // value: 0 - // storage_deposit_limit: 1_000_000_000_000 - hex::decode("2d9338da000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e8d4a5100000000000000000000000000000000000000000000000000000000000000000200e0ddb5a5f0b99d7be468a3051a94073ec6b1900178316401a52b9341502699900000000000000000000000000000000000000000000000000000000000000040000002a00000000000000000000000000000000000000000000000000000000").expect("invalid call input hex"), + // storage_deposit_limit: 1_000_000_000_000_000 + hex::decode("2d9338da000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000200e0ddb5a5f0b99d7be468a3051a94073ec6b1900178316401a52b9341502699900000000000000000000000000000000000000000000000000000000000000040000002a00000000000000000000000000000000000000000000000000000000").expect("invalid call input hex"), U256::zero(), 1_000_000, U256::from(DefaultBaseFeePerGas::get()),