diff --git a/docs/user/fees.md b/docs/user/fees.md new file mode 100644 index 0000000000000000000000000000000000000000..bec58ecccece2f6b6220dcee2f4b0e41aafb4dec --- /dev/null +++ b/docs/user/fees.md @@ -0,0 +1,35 @@ +## Weights and Fees Calculation + +### Introduction + +Transaction weights and fees ensure network efficiency, fairness, and security in Substrate-based blockchains. These concepts are designed to manage resource allocation and incentivize proper usage of the blockchain infrastructure. + +### Transaction Weights + +Transaction weight measures the computational resources required to process a transaction. It is determined by factors such as the complexity of the transaction logic and the amount of data involved. Transactions with higher weights consume more resources and thus contribute to the overall load on the network. + +### Transaction Fees + +Transaction fees in Substrate-based blockchains are crucial for efficiently managing network resources and sustaining economic viability. They regulate resource allocation by ensuring transactions consuming more computational resources incur higher fees, discouraging spam, and promoting fair use of network capacity. +The fees are computed as follows: +`fee = base_fee + weight2fee * fee_multiplier + length2fee + tip` + +## Fees in Duniter + +### Fees Implementation Details + +Implementing a zero-fee chain in Duniter involves configuring the blockchain to waive transaction fees when the current block weight is below a specified target. This approach aims to promote accessibility and encourage participation by eliminating fees during periods of lower network activity. +However, transaction fees are applied when the block weight surpasses the defined target to ensure network security and stability during increased usage. Additionally, leveraging the fee multiplier mechanism helps deter potential prolonged network attacks by dynamically adjusting fee levels based on previous network conditions. +Duniter members benefit from the quota system, which refunds transaction fees during high network activity periods. + +Fees are computed as follows: +* If `current_weight < 0.25 * max_weight` and `fee_multiplier = 1`, ie. normal load: +`fee = 0` +* If `current_weight > 0.25 * max_weight`, ie. heavy usage (approximately more than 25 transactions per second): +`fee = `5cĞ1 + extrinsic_weight * (5cĞ1/base_extrinsic_weight)* fee_multiplier + extrinsic_length/100 + tip` + +The multiplier is updated as follows: +* If `current_weight > 0.25 * max_weight`: +`Min(fee_multiplier += 1, 10)` +* If `current_weight < 0.25 * max_weight`: +`Max(fee_multiplier -= 1, 1)` diff --git a/runtime/common/src/fees.rs b/runtime/common/src/fees.rs index 80310f2e1eac606f5f6478a4d3474d9ea6fb3a32..acee3e2c2dc81e7cb61076ec0ecd707f5d7ae1d4 100644 --- a/runtime/common/src/fees.rs +++ b/runtime/common/src/fees.rs @@ -64,7 +64,7 @@ where /// Function to convert weight to fee when "constant-fees" feature is not enabled. /// /// This function calculates the fee based on the length of the transaction in bytes. - /// If the current block weight is less than a fraction of the max block weight and the fee multiplier is zero, + /// If the current block weight is less than a fraction of the max block weight and the fee multiplier is one, /// it returns a zero fee. Otherwise, it calculates the fee based on the length in bytes. #[cfg(not(feature = "constant-fees"))] fn weight_to_fee(length_in_bytes: &Weight) -> Self::Balance { @@ -120,7 +120,7 @@ where /// Function to get the polynomial coefficients for weight to fee conversion. /// /// This function calculates the polynomial coefficients for converting transaction weight to fee. - /// If the current block weight is less than a fraction of the block max weight and the fee multiplier is zero, + /// If the current block weight is less than a fraction of the block max weight and the fee multiplier is one, /// it returns zero. Otherwise, it calculates the coefficients based on the extrinsic base weight mapped to 5 cents. fn polynomial() -> WeightToFeeCoefficients<Self::Balance> { let weights = U::BlockWeights::get(); diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs index 4c03112ffc08a56473781d71460798690f06da47..967139552b6cbbf29ff5c93245f2aebd3073d12e 100644 --- a/runtime/common/src/pallets_config.rs +++ b/runtime/common/src/pallets_config.rs @@ -190,7 +190,7 @@ macro_rules! pallets_config { pub struct OnChargeTransaction; parameter_types! { - pub Target: Perquintill = Perquintill::from_percent(10); + pub Target: Perquintill = Perquintill::from_percent(25); pub MaxMultiplier: sp_runtime::FixedU128 = 10.into(); } impl pallet_transaction_payment::Config for Runtime { diff --git a/runtime/gdev/tests/fee_tests.rs b/runtime/gdev/tests/fee_tests.rs index 8773c158066e09bc32c52fd7fb07beb8e2bf80df..99d7b83387cf56e43453b8888f566e5086f30ade 100644 --- a/runtime/gdev/tests/fee_tests.rs +++ b/runtime/gdev/tests/fee_tests.rs @@ -91,6 +91,8 @@ fn test_fees_full() { Balances::free_balance(AccountKeyring::Alice.to_account_id()), 10_000 - 5 * transactions ); + // Ensure that the next extrinsic exceeds the limit. + System::set_block_consumed_resources(Target::get() * normal_max_weight, 100_usize); // The block will reach the fee limit, so the next extrinsic should start incurring fees. let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: AccountKeyring::Eve.to_account_id().into(), @@ -152,7 +154,7 @@ fn test_fees_multiplier() { let mut current = 10u128; for i in 20..50u32 { run_to_block(i); - current = current.saturating_sub(1).max(1u128.into()); + current = current.saturating_sub(1).max(1u128); assert_eq!( pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(), current.into()