Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dynamic Fee support for Moonbeam #2166

Merged
merged 11 commits into from
Apr 12, 2023
27 changes: 21 additions & 6 deletions runtime/moonbeam/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,25 +369,40 @@ parameter_types! {
pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
/// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to
/// change the fees more rapidly. This low value causes changes to occur slowly over time.
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000);
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(4, 1_000);
/// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure
/// that combined with `AdjustmentVariable`, we can recover from the minimum.
/// See `multiplier_can_grow_from_zero` in integration_tests.rs.
/// This value is currently only used by pallet-transaction-payment as an assertion that the
/// next multiplier is always > min value.
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128);
pub MinimumMultiplier: Multiplier = Multiplier::from(1u128);
/// Maximum multiplier. We pick a value that is expensive but not impossibly so; it should act
/// as a safety net.
pub MaximumMultiplier: Multiplier = Multiplier::from(100_000u128);
pub PrecompilesValue: MoonbeamPrecompiles<Runtime> = MoonbeamPrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_ref_time(WEIGHT_PER_GAS);
}

pub struct FixedGasPrice;
impl FeeCalculator for FixedGasPrice {
pub struct TransactionPaymentAsGasPrice;
impl FeeCalculator for TransactionPaymentAsGasPrice {
fn min_gas_price() -> (U256, Weight) {
// note: transaction-payment differs from EIP-1559 in that its tip and length fees are not
// scaled by the multiplier, which means its multiplier will be overstated when
// applied to an ethereum transaction
// note: transaction-payment uses both a congestion modifier (next_fee_multiplier, which is
// updated once per block in on_finalize) and a 'WeightToFee' implementation. Our
// runtime implements this as a 'ConstantModifier', so we can get away with a simple
// multiplication here.
// It is imperative that `saturating_mul_int` be performed as late as possible in the
// expression since it involves fixed point multiplication with a division by a fixed
// divisor. This leads to truncation and subsequent precision loss if performed too early.
// This can lead to min_gas_price being same across blocks even if the multiplier changes.
// There's still some precision loss when the final `gas_price` (used_gas * min_gas_price)
// is computed in frontier, but that's currently unavoidable.
let min_gas_price = TransactionPayment::next_fee_multiplier()
.saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128));
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
(
(1 * currency::GIGAWEI * currency::SUPPLY_FACTOR).into(),
min_gas_price.into(),
<Runtime as frame_system::Config>::DbWeight::get().reads(1),
)
}
Expand Down Expand Up @@ -435,7 +450,7 @@ where
moonbeam_runtime_common::impl_on_charge_evm_transaction!();

impl pallet_evm::Config for Runtime {
type FeeCalculator = FixedGasPrice;
type FeeCalculator = TransactionPaymentAsGasPrice;
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;
Expand Down
12 changes: 6 additions & 6 deletions runtime/moonbeam/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ pub use moonbeam_runtime::{
currency::{GIGAWEI, GLMR, SUPPLY_FACTOR, WEI},
xcm_config::AssetType,
AccountId, AssetId, AssetManager, Assets, AuthorInherent, Balance, Balances, CrowdloanRewards,
Ethereum, Executive, FixedGasPrice, InflationInfo, LocalAssets, ParachainStaking, Range,
Runtime, RuntimeCall, RuntimeEvent, System, TransactionConverter, UncheckedExtrinsic, HOURS,
WEEKS,
Ethereum, Executive, InflationInfo, LocalAssets, ParachainStaking, Range, Runtime, RuntimeCall,
RuntimeEvent, System, TransactionConverter, TransactionPaymentAsGasPrice, UncheckedExtrinsic,
HOURS, WEEKS,
};
use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID};
use sp_core::{Encode, H160};
Expand All @@ -42,9 +42,9 @@ use fp_rpc::ConvertTransaction;

// A valid signed Alice transfer.
pub const VALID_ETH_TX: &str =
"f8648085174876e8008252089412cb274aad8251c875c0bf6872b67d9983e53fdd01801ba05deb036\
17e9c2d82e0f4e897ef8fbb01c91244abfc4bd9c3206bc87f9fc71a01a0719f146637fe2b462ccae80\
e462ecefa560635d933257ec117a1f7701b178c93";
"02f869820501808085e8d4a51000825208943cd0a705a2dc65e5b1e1205896baa2be8a07c6e00180c\
001a061087911e877a5802142a89a40d231d50913db399eb50839bb2d04e612b22ec8a01aa313efdf2\
793bea76da6813bda611444af16a6207a8cfef2d9c8aa8f8012f7";

// An invalid signed Alice transfer with a gas limit artifically set to 0.
pub const INVALID_ETH_TX: &str =
Expand Down
88 changes: 61 additions & 27 deletions runtime/moonbeam/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
mod common;
use common::*;

use fp_evm::{Context, FeeCalculator};
use fp_evm::Context;
use frame_support::{
assert_noop, assert_ok,
dispatch::{DispatchClass, Dispatchable},
Expand All @@ -39,14 +39,15 @@ use moonbeam_runtime::{
xcm_config::{CurrencyId, SelfReserve, UnitWeightCost},
AccountId, Balances, CouncilCollective, CrowdloanRewards, ParachainStaking, PolkadotXcm,
Precompiles, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, System,
TechCommitteeCollective, TreasuryCouncilCollective, XTokens, XcmTransactor,
TechCommitteeCollective, TransactionPayment, TreasuryCouncilCollective, XTokens, XcmTransactor,
FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX, LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX,
};
use nimbus_primitives::NimbusId;
use pallet_evm::PrecompileSet;
use pallet_evm_precompileset_assets_erc20::{
AccountIdAssetIdConversion, IsLocal, SELECTOR_LOG_APPROVAL, SELECTOR_LOG_TRANSFER,
};
use pallet_transaction_payment::Multiplier;
use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights};
use parity_scale_codec::Encode;
use polkadot_parachain::primitives::Sibling;
Expand Down Expand Up @@ -76,6 +77,8 @@ type LocalAssetsPCall = pallet_evm_precompileset_assets_erc20::Erc20AssetsPrecom
type XcmTransactorV2PCall =
pallet_evm_precompile_xcm_transactor::v2::XcmTransactorPrecompileV2Call<Runtime>;

const BASE_FEE_GENESIS: u128 = 10000 * GIGAWEI;

#[test]
fn xcmp_queue_controller_origin_is_root() {
// important for the XcmExecutionManager impl of PauseExecution which uses root origin
Expand Down Expand Up @@ -496,7 +499,7 @@ fn transfer_through_evm_to_stake() {
);

let gas_limit = 100000u64;
let gas_price: U256 = (100 * GIGAWEI).into();
let gas_price: U256 = BASE_FEE_GENESIS.into();
// Bob transfers 2_000_000 GLMR to Charlie via EVM
assert_ok!(RuntimeCall::EVM(pallet_evm::Call::<Runtime>::call {
source: H160::from(BOB),
Expand Down Expand Up @@ -917,7 +920,7 @@ fn claim_via_precompile() {

// Alice uses the crowdloan precompile to claim through the EVM
let gas_limit = 100000u64;
let gas_price: U256 = 100_000_000_000u64.into();
let gas_price: U256 = BASE_FEE_GENESIS.into();

// Construct the call data (selector, amount)
let mut call_data = Vec::<u8>::from([0u8; 4]);
Expand Down Expand Up @@ -1163,7 +1166,7 @@ fn update_reward_address_via_precompile() {

// Charlie uses the crowdloan precompile to update address through the EVM
let gas_limit = 100000u64;
let gas_price: U256 = 100_000_000_000u64.into();
let gas_price: U256 = BASE_FEE_GENESIS.into();

// Construct the input data to check if Bob is a contributor
let mut call_data = Vec::<u8>::from([0u8; 36]);
Expand Down Expand Up @@ -1282,6 +1285,46 @@ fn ethereum_invalid_transaction() {
});
}

#[test]
fn initial_gas_fee_is_correct() {
use fp_evm::FeeCalculator;

ExtBuilder::default().build().execute_with(|| {
let multiplier = TransactionPayment::next_fee_multiplier();
assert_eq!(multiplier, Multiplier::from(1u128));

assert_eq!(
TransactionPaymentAsGasPrice::min_gas_price(),
(
125_000_000_000u128.into(),
Weight::from_ref_time(25_000_000u64)
)
);
});
}

#[test]
fn min_gas_fee_is_correct() {
use fp_evm::FeeCalculator;
use frame_support::traits::Hooks;

ExtBuilder::default().build().execute_with(|| {
pallet_transaction_payment::NextFeeMultiplier::<Runtime>::put(Multiplier::from(0));
TransactionPayment::on_finalize(System::block_number()); // should trigger min to kick in

let multiplier = TransactionPayment::next_fee_multiplier();
assert_eq!(multiplier, Multiplier::from(1u128));

assert_eq!(
TransactionPaymentAsGasPrice::min_gas_price(),
(
125_000_000_000u128.into(),
Weight::from_ref_time(25_000_000u64)
)
);
Comment on lines +1318 to +1324
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tgmichel this should address your earlier comment. It is indeed 125 gwei, but this should be in line with other runtimes when you take the SUPPLY_FACTOR into account.

Does 125 gwei still seem unreasonable?

});
}

#[test]
fn transfer_ed_0_substrate() {
ExtBuilder::default()
Expand All @@ -1308,7 +1351,7 @@ fn transfer_ed_0_evm() {
.with_balances(vec![
(
AccountId::from(ALICE),
((1 * GLMR) + (21_000 * (100 * GIGAWEI))) + (1 * WEI),
((1 * GLMR) + (21_000 * BASE_FEE_GENESIS)) + (1 * WEI),
),
(AccountId::from(BOB), 0),
])
Expand All @@ -1321,8 +1364,8 @@ fn transfer_ed_0_evm() {
input: Vec::new(),
value: (1 * GLMR).into(),
gas_limit: 21_000u64,
max_fee_per_gas: U256::from(100 * GIGAWEI),
max_priority_fee_per_gas: None,
max_fee_per_gas: BASE_FEE_GENESIS.into(),
max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()),
nonce: Some(U256::from(0)),
access_list: Vec::new(),
})
Expand All @@ -1338,7 +1381,7 @@ fn refund_ed_0_evm() {
.with_balances(vec![
(
AccountId::from(ALICE),
((1 * GLMR) + (21_777 * (100 * GIGAWEI))),
((1 * GLMR) + (21_777 * BASE_FEE_GENESIS)),
),
(AccountId::from(BOB), 0),
])
Expand All @@ -1351,16 +1394,16 @@ fn refund_ed_0_evm() {
input: Vec::new(),
value: (1 * GLMR).into(),
gas_limit: 21_777u64,
max_fee_per_gas: U256::from(100 * GIGAWEI),
max_priority_fee_per_gas: None,
max_fee_per_gas: BASE_FEE_GENESIS.into(),
max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()),
nonce: Some(U256::from(0)),
access_list: Vec::new(),
})
.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));
// ALICE is refunded
assert_eq!(
Balances::free_balance(AccountId::from(ALICE)),
777 * (100 * GIGAWEI),
777 * BASE_FEE_GENESIS,
);
});
}
Expand Down Expand Up @@ -1440,7 +1483,7 @@ fn total_issuance_after_evm_transaction_without_priority_fee() {
ExtBuilder::default()
.with_balances(vec![(
AccountId::from(BOB),
(1 * GLMR) + (21_000 * (200 * GIGAWEI)),
(1 * GLMR) + (21_000 * BASE_FEE_GENESIS),
)])
.build()
.execute_with(|| {
Expand All @@ -1452,16 +1495,16 @@ fn total_issuance_after_evm_transaction_without_priority_fee() {
input: Vec::new(),
value: (1 * GLMR).into(),
gas_limit: 21_000u64,
max_fee_per_gas: U256::from(100 * GIGAWEI),
max_priority_fee_per_gas: None,
max_fee_per_gas: BASE_FEE_GENESIS.into(),
max_priority_fee_per_gas: Some(BASE_FEE_GENESIS.into()),
nonce: Some(U256::from(0)),
access_list: Vec::new(),
})
.dispatch(<Runtime as frame_system::Config>::RuntimeOrigin::root()));

let issuance_after = <Runtime as pallet_evm::Config>::Currency::total_issuance();
// Fee is 100 GWEI base fee.
let fee = ((100 * GIGAWEI) * 21_000) as f64;
let fee = (BASE_FEE_GENESIS * 21_000) as f64;
// 80% was burned.
let expected_burn = (fee * 0.8) as u128;
assert_eq!(issuance_after, issuance_before - expected_burn,);
Expand Down Expand Up @@ -2875,15 +2918,6 @@ fn removed_precompiles() {
})
}

#[test]
fn base_fee_should_default_to_associate_type_value() {
ExtBuilder::default().build().execute_with(|| {
let (base_fee, _) =
<moonbeam_runtime::Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price();
assert_eq!(base_fee, (1 * GIGAWEI * SUPPLY_FACTOR).into());
});
}

#[test]
fn evm_revert_substrate_events() {
ExtBuilder::default()
Expand All @@ -2907,7 +2941,7 @@ fn evm_revert_substrate_events() {
.into(),
value: U256::zero(), // No value sent in EVM
gas_limit: 500_000,
max_fee_per_gas: U256::from(100 * GIGAWEI),
max_fee_per_gas: BASE_FEE_GENESIS.into(),
max_priority_fee_per_gas: None,
nonce: Some(U256::from(0)),
access_list: Vec::new(),
Expand Down Expand Up @@ -2946,7 +2980,7 @@ fn evm_success_keeps_substrate_events() {
.into(),
value: U256::zero(), // No value sent in EVM
gas_limit: 500_000,
max_fee_per_gas: U256::from(100 * GIGAWEI),
max_fee_per_gas: BASE_FEE_GENESIS.into(),
max_priority_fee_per_gas: None,
nonce: Some(U256::from(0)),
access_list: Vec::new(),
Expand Down
9 changes: 6 additions & 3 deletions runtime/moonbeam/tests/runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ fn ethereum_runtime_rpc_api_account_basic() {
#[test]
fn ethereum_runtime_rpc_api_gas_price() {
ExtBuilder::default().build().execute_with(|| {
assert_eq!(Runtime::gas_price(), FixedGasPrice::min_gas_price().0);
assert_eq!(
Runtime::gas_price(),
TransactionPaymentAsGasPrice::min_gas_price().0
);
});
}

Expand Down Expand Up @@ -186,7 +189,7 @@ fn ethereum_runtime_rpc_api_create() {
#[test]
fn ethereum_runtime_rpc_api_current_transaction_statuses() {
let alith = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(
H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b")
H160::from_str("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")
.expect("internal H160 is valid; qed"),
);
ExtBuilder::default()
Expand Down Expand Up @@ -247,7 +250,7 @@ fn ethereum_runtime_rpc_api_current_block() {
#[test]
fn ethereum_runtime_rpc_api_current_receipts() {
let alith = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(
H160::from_str("6be02d1d3665660d22ff9624b7be0551ee1ac91b")
H160::from_str("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")
.expect("internal H160 is valid; qed"),
);
ExtBuilder::default()
Expand Down
7 changes: 5 additions & 2 deletions tests/smoke-tests/test-dynamic-fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,11 @@ describeSmokeSuite(
this.skip();
}

if (specName.toString() == "moonbeam") {
debug(`Runtime ${specName.toString()} not supported by these tests, skipping.`);
if (specVersion.toNumber() < 2300 && specName.toString() == "moonbeam") {
debug(
`Runtime ${specName.toString()} version ` +
`${specVersion.toString()} is less than 2300, skipping test suite.`
);
this.skip();
}

Expand Down
2 changes: 1 addition & 1 deletion tests/tests/test-precompile/test-precompile-xcm-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ describeDevMoonbeam(
const { result } = await context.createBlock(
createTransaction(context, {
...ALITH_TRANSACTION_TEMPLATE,
gasPrice: 100_000_000_000,
gasPrice: 1_000_000_000_000,
to: PRECOMPILE_XCM_UTILS_ADDRESS,
data: XCM_UTILSTRANSACTOR_INTERFACE.encodeFunctionData("xcmExecute", [
receivedMessage.toU8a(),
Expand Down
8 changes: 4 additions & 4 deletions tests/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ export const RUNTIME_CONSTANTS = {
MAX_BASE_FEE_IN_WEI: "125000000000000",
},
MOONBEAM: {
MIN_FEE_MULTIPLIER: "1000000000000",
MAX_FEE_MULTIPLIER: "100000000000000000",
MIN_BASE_FEE_IN_WEI: "",
MAX_BASE_FEE_IN_WEI: "",
MIN_FEE_MULTIPLIER: "1000000000000000000",
MAX_FEE_MULTIPLIER: "100000000000000000000000",
MIN_BASE_FEE_IN_WEI: "125000000000",
MAX_BASE_FEE_IN_WEI: "12500000000000000",
},
};