diff --git a/.cargo/config.toml b/.cargo/config.toml
index 2efc177777fdf4686ddbbc33ef253a1375c1ce1d..767a123c86da2b30638a55e39c7828fb7fae945f 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,6 +1,7 @@
 [alias]
 sanity-gdev = "test -Zgit=shallow-deps -p duniter-live-tests --test sanity_gdev -- --nocapture"
-tu = "test -Zgit=shallow-deps --workspace --exclude duniter-end2end-tests --exclude duniter-live-tests --features constant-fees"
+tu = "test -Zgit=shallow-deps --workspace --exclude duniter-end2end-tests --exclude duniter-live-tests --features constant-fees" # Unit tests with constant-fees
+tf = "test -Zgit=shallow-deps --workspace --exclude duniter-end2end-tests --exclude duniter-live-tests test_fee" # Custom fee model tests
 # `te` and `cucumber` are synonyms
 te = "test -p duniter-end2end-tests --test cucumber_tests --features constant-fees --"
 cucumber-build = "build -Zgit=shallow-deps --features constant-fees"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 241c8e95a068be581b733e9d32a3b52019eb23ff..04e3d841c36a2093850d028ea852a50c44658587 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -156,6 +156,7 @@ tests:
     - apt-get update
     - apt-get install -y clang cmake protobuf-compiler
     - cargo tu
+    - cargo tf
     - cargo cucumber-build
     - cargo cucumber
 
diff --git a/Cargo.lock b/Cargo.lock
index a7c3ffebac2d004a19ecc0f7d8731453110c5689..297d0cc531f6e0cc47f02f46dfbd1714254e6adf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1460,6 +1460,7 @@ dependencies = [
  "pallet-smith-members",
  "pallet-sudo",
  "pallet-timestamp",
+ "pallet-transaction-payment",
  "pallet-treasury",
  "pallet-universal-dividend",
  "pallet-upgrade-origin",
diff --git a/docs/user/fees.md b/docs/user/fees.md
new file mode 100644
index 0000000000000000000000000000000000000000..4b79c2a5e0fb9f35ded49c5efb331f4004fc1e51
--- /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 or length surpasses the defined targets 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 `current_length < 0.25 * max_length` and `fee_multiplier = 1`, ie. normal load:
+`fee = 0`
+* If `current_weight > 0.25 * max_weight` or `current_length > 0.25 * max_length` or `fee_multiplier > 1`, ie. heavy usage (approximately more than 135 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` or `current_length > 0.25 * max_length`:
+`Min(fee_multiplier += 1, 10)`
+* If `current_weight < 0.25 * max_weight` and `current_length < 0.25 * max_length`:
+`Max(fee_multiplier -= 1, 1)`
diff --git a/resources/metadata.scale b/resources/metadata.scale
index fef41d8bafe615f1ce210c92a47aa4f81e05496d..bf88b3ce9a6e05e2a98b8c43493369f717191571 100644
Binary files a/resources/metadata.scale and b/resources/metadata.scale differ
diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml
index 355d7ca17ffe51b3a3ba48143e565a593e0b3d14..8b401fb4e5bcadd5a32963e4c05aa5cf436993f5 100644
--- a/runtime/common/Cargo.toml
+++ b/runtime/common/Cargo.toml
@@ -126,6 +126,7 @@ try-runtime = [
 	"pallet-smith-members/try-runtime",
 	"pallet-sudo/try-runtime",
 	"pallet-timestamp/try-runtime",
+	"pallet-transaction-payment/try-runtime",
 	"pallet-treasury/try-runtime",
 	"pallet-universal-dividend/try-runtime",
 	"pallet-upgrade-origin/try-runtime",
@@ -167,6 +168,7 @@ pallet-session = { workspace = true }
 pallet-smith-members = { workspace = true }
 pallet-sudo = { workspace = true }
 pallet-timestamp = { workspace = true }
+pallet-transaction-payment = { workspace = true }
 pallet-treasury = { workspace = true }
 pallet-universal-dividend = { workspace = true }
 pallet-upgrade-origin = { workspace = true }
diff --git a/runtime/common/src/constants.rs b/runtime/common/src/constants.rs
index 135e5bad62db4acbad6a182b07e128ca31d5aa24..77de932695ffab296c72b9370b92ff4f2bda96ed 100644
--- a/runtime/common/src/constants.rs
+++ b/runtime/common/src/constants.rs
@@ -15,7 +15,6 @@
 // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
 
 use crate::{Balance, BlockNumber};
-use frame_support::weights::Weight;
 use sp_runtime::Perbill;
 
 pub use crate::weights::paritydb_weights::constants::ParityDbWeight as DbWeight;
@@ -61,32 +60,3 @@ pub const fn deposit(items: u32, bytes: u32) -> Balance {
 
 // Maximal weight proportion of normal extrinsics per block
 pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
-
-// WEIGHTS CONSTANTS //
-
-// Block weights limits
-pub fn block_weights(
-    expected_block_weight: Weight,
-    normal_ratio: sp_arithmetic::Perbill,
-) -> frame_system::limits::BlockWeights {
-    let base_weight = crate::weights::extrinsic_weights::ExtrinsicBaseWeight::get();
-    let normal_weight = normal_ratio * expected_block_weight;
-    frame_system::limits::BlockWeights::builder()
-        .base_block(crate::weights::block_weights::BlockExecutionWeight::get())
-        .for_class(frame_support::dispatch::DispatchClass::all(), |weights| {
-            weights.base_extrinsic = base_weight;
-        })
-        .for_class(frame_support::dispatch::DispatchClass::Normal, |weights| {
-            weights.max_total = normal_weight.into();
-        })
-        .for_class(
-            frame_support::dispatch::DispatchClass::Operational,
-            |weights| {
-                weights.max_total = expected_block_weight.into();
-                weights.reserved = (expected_block_weight - normal_weight).into();
-            },
-        )
-        .avg_block_initialization(sp_arithmetic::Perbill::from_percent(10))
-        .build()
-        .expect("Fatal error: invalid BlockWeights configuration")
-}
diff --git a/runtime/common/src/fees.rs b/runtime/common/src/fees.rs
index 1bea9d5759d1a77d1c1aabf231c082a4bb72922d..50e40df1feb60767890de542cbcbe431bda37226 100644
--- a/runtime/common/src/fees.rs
+++ b/runtime/common/src/fees.rs
@@ -21,69 +21,250 @@
 // This involves setting a constant weight_to_fee equal to 1 and a constant length_to_fee set to 0, resulting in each extrinsic costing 2 (2cG).
 
 pub use frame_support::weights::{Weight, WeightToFee};
+use pallet_transaction_payment::{Multiplier, MultiplierUpdate};
 use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
-
+use sp_core::Get;
+use sp_runtime::{traits::Convert, Perquintill};
 #[cfg(not(feature = "constant-fees"))]
 use {
     crate::weights::extrinsic_weights::ExtrinsicBaseWeight,
+    frame_support::pallet_prelude::DispatchClass,
     frame_support::weights::{
         WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
     },
     smallvec::smallvec,
     sp_arithmetic::MultiplyRational,
+    sp_runtime::traits::One,
     sp_runtime::Perbill,
     sp_runtime::SaturatedConversion,
+    sp_runtime::Saturating,
 };
 
-pub struct LengthToFeeImpl<T>(sp_std::marker::PhantomData<T>);
+/// A structure to implement Length to Fee conversion.
+/// - `Balance`: The balance type.
+/// - `Runtime`: The system configuration type, providing access to block weights.
+/// - `Target`: A type providing the target block fullness.
+pub struct LengthToFeeImpl<Balance, Runtime, Target>(
+    sp_std::marker::PhantomData<Balance>,
+    sp_std::marker::PhantomData<Runtime>,
+    sp_std::marker::PhantomData<Target>,
+);
 
-impl<T> WeightToFee for LengthToFeeImpl<T>
+/// Trait implementation for converting transaction length to fee.
+impl<Balance, Runtime, Target> WeightToFee for LengthToFeeImpl<Balance, Runtime, Target>
 where
-    T: BaseArithmetic + From<u32> + Copy + Unsigned,
+    Balance: BaseArithmetic + From<u32> + Copy + Unsigned,
+    Runtime: frame_system::Config + pallet_transaction_payment::Config,
+    Target: Get<Perquintill>,
 {
-    type Balance = T;
+    type Balance = Balance;
 
+    /// 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 and length are less than a fraction of the max block weight and length 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 {
-        Self::Balance::saturated_from(length_in_bytes.ref_time() / 100u64)
+        let weights = Runtime::BlockWeights::get();
+        let fee_multiplier = pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier();
+        let normal_max_weight = weights
+            .get(DispatchClass::Normal)
+            .max_total
+            .unwrap_or(weights.max_block);
+        let current_block_weight = <frame_system::Pallet<Runtime>>::block_weight();
+
+        let length = Runtime::BlockLength::get();
+        let normal_max_length = *length.max.get(DispatchClass::Normal) as u64;
+        let current_block_length = <frame_system::Pallet<Runtime>>::all_extrinsics_len() as u64;
+
+        if current_block_weight
+            .get(DispatchClass::Normal)
+            .all_lt(Target::get() * normal_max_weight)
+            && current_block_length < (Target::get() * normal_max_length)
+            && fee_multiplier.is_one()
+        {
+            0u32.into()
+        } else {
+            Self::Balance::saturated_from(length_in_bytes.ref_time() / 100u64)
+        }
     }
 
+    /// Function to convert weight to fee when "constant-fees" feature is enabled.
+    ///
+    /// This function always returns a constant fee of zero when the "constant-fees" feature is enabled.
     #[cfg(feature = "constant-fees")]
     fn weight_to_fee(_length_in_bytes: &Weight) -> Self::Balance {
         0u32.into()
     }
 }
 
-pub struct WeightToFeeImpl<T>(sp_std::marker::PhantomData<T>);
+/// A structure to implement Weight to Fee conversion.
+/// - `Balance`: The balance type.
+/// - `Runtime`: The system configuration type, providing access to block weights.
+/// - `Target`: A type providing the target block fullness.
+pub struct WeightToFeeImpl<Balance, Runtime, Target>(
+    sp_std::marker::PhantomData<Balance>,
+    sp_std::marker::PhantomData<Runtime>,
+    sp_std::marker::PhantomData<Target>,
+);
 
+/// Trait implementation for converting transaction weight to fee.
+///
+/// This implementation is only included when the "constant-fees" feature is not enabled.
 #[cfg(not(feature = "constant-fees"))]
-impl<T> WeightToFeePolynomial for WeightToFeeImpl<T>
+impl<Balance, Runtime, Target> WeightToFeePolynomial for WeightToFeeImpl<Balance, Runtime, Target>
 where
-    T: BaseArithmetic + From<u64> + Copy + Unsigned + From<u32> + MultiplyRational,
+    Balance: BaseArithmetic + From<u64> + Copy + Unsigned + From<u32> + MultiplyRational,
+    Runtime: frame_system::Config + pallet_transaction_payment::Config,
+    Target: Get<Perquintill>,
 {
-    type Balance = T;
+    type Balance = Balance;
 
+    /// 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 and length are less than a fraction of the block max weight and length, 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> {
-        // The extrinsic base weight (smallest non-zero weight) is mapped to 5 cent
-        let p: Self::Balance = 5u64.into();
-        let q: Self::Balance = Self::Balance::from(ExtrinsicBaseWeight::get().ref_time());
-        smallvec![WeightToFeeCoefficient {
-            degree: 1,
-            negative: false,
-            coeff_frac: Perbill::from_rational(p % q, q),
-            coeff_integer: p / q,
-        }]
+        let weights = Runtime::BlockWeights::get();
+        let fee_multiplier = pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier();
+        let normal_max_weight = weights
+            .get(DispatchClass::Normal)
+            .max_total
+            .unwrap_or(weights.max_block);
+        let current_block_weight = <frame_system::Pallet<Runtime>>::block_weight();
+
+        let length = Runtime::BlockLength::get();
+        let normal_max_length = *length.max.get(DispatchClass::Normal) as u64;
+        let current_block_length = <frame_system::Pallet<Runtime>>::all_extrinsics_len() as u64;
+
+        if current_block_weight
+            .get(DispatchClass::Normal)
+            .all_lt(Target::get() * normal_max_weight)
+            && current_block_length < (Target::get() * normal_max_length)
+            && fee_multiplier.is_one()
+        {
+            smallvec![WeightToFeeCoefficient {
+                degree: 1,
+                negative: false,
+                coeff_frac: Perbill::zero(),
+                coeff_integer: Self::Balance::zero(),
+            }]
+        } else {
+            // The extrinsic base weight (smallest non-zero weight) is mapped to 5 cents
+            let p: Self::Balance = 5u64.into();
+            let q: Self::Balance = Self::Balance::from(ExtrinsicBaseWeight::get().ref_time());
+            smallvec![WeightToFeeCoefficient {
+                degree: 1,
+                negative: false,
+                coeff_frac: Perbill::from_rational(p % q, q),
+                coeff_integer: p / q,
+            }]
+        }
     }
 }
 
+/// Trait implementation for converting transaction weight to a constant fee.
+///
+/// This implementation is only included when the "constant-fees" feature is enabled.
 #[cfg(feature = "constant-fees")]
-impl<T> WeightToFee for WeightToFeeImpl<T>
+impl<Balance, Runtime, Target> WeightToFee for WeightToFeeImpl<Balance, Runtime, Target>
 where
-    T: BaseArithmetic + From<u32> + Copy + Unsigned,
+    Balance: BaseArithmetic + From<u32> + Copy + Unsigned,
 {
-    type Balance = T;
+    type Balance = Balance;
 
     fn weight_to_fee(_weight: &Weight) -> Self::Balance {
         1u32.into()
     }
 }
+
+/// A structure to implement fee multiplier adjustments.
+///
+/// - `Runtime`: The system configuration type.
+/// - `Target`: A type providing the target block fullness.
+/// - `MaxMultiplier`: A type providing the maximum multiplier value.
+pub struct FeeMultiplier<Runtime, Target, MaxMultiplier>(
+    sp_std::marker::PhantomData<Runtime>,
+    sp_std::marker::PhantomData<Target>,
+    sp_std::marker::PhantomData<MaxMultiplier>,
+);
+
+/// Trait implementation for updating the fee multiplier.
+impl<Runtime, Target, MaxMultiplier> MultiplierUpdate
+    for FeeMultiplier<Runtime, Target, MaxMultiplier>
+where
+    Runtime: frame_system::Config,
+    Target: Get<Perquintill>,
+    MaxMultiplier: Get<Multiplier>,
+{
+    fn min() -> Multiplier {
+        0.into()
+    }
+
+    fn max() -> Multiplier {
+        MaxMultiplier::get()
+    }
+
+    fn target() -> Perquintill {
+        Target::get()
+    }
+
+    fn variability() -> Multiplier {
+        Default::default()
+    }
+}
+
+/// Trait implementation for converting previous `Multiplier` to another for fee adjustment.
+impl<Runtime, Target, MaxMultiplier> Convert<Multiplier, Multiplier>
+    for FeeMultiplier<Runtime, Target, MaxMultiplier>
+where
+    Runtime: frame_system::Config,
+    Target: Get<Perquintill>,
+    MaxMultiplier: Get<Multiplier>,
+{
+    /// Function to convert the previous fee multiplier to a new fee multiplier.
+    ///
+    /// This function adjusts the fee multiplier based on the current block weight, length and target block fullness.
+    /// - If the current block weight and length are less than the target, it decreases the multiplier by one, with a minimum of one.
+    /// - If the current block weight or length is more than the target, it increases the multiplier by one, up to the maximum multiplier.
+    #[cfg(not(feature = "constant-fees"))]
+    fn convert(previous: Multiplier) -> Multiplier {
+        let max_multiplier = MaxMultiplier::get();
+        let target_block_fullness = Target::get();
+
+        let weights = Runtime::BlockWeights::get();
+        let normal_max_weight = weights
+            .get(DispatchClass::Normal)
+            .max_total
+            .unwrap_or(weights.max_block);
+
+        let length = Runtime::BlockLength::get();
+        let normal_max_length = *length.max.get(DispatchClass::Normal) as u64;
+        let current_block_length = <frame_system::Pallet<Runtime>>::all_extrinsics_len() as u64;
+
+        if <frame_system::Pallet<Runtime>>::block_weight()
+            .get(DispatchClass::Normal)
+            .all_lt(target_block_fullness * normal_max_weight)
+            && current_block_length < (target_block_fullness * normal_max_length)
+        {
+            // If the current block weight and length are less than the target, keep the
+            // multiplier at the minimum or decrease it by one to slowly
+            // return to the minimum.
+            previous.saturating_sub(1.into()).max(1.into())
+        } else {
+            // If the current block weight or length is more than the target, increase
+            // the multiplier by one.
+            previous.saturating_add(1.into()).min(max_multiplier)
+        }
+    }
+
+    /// Function to convert the previous fee multiplier to a constant fee multiplier when "constant-fees" feature is enabled.
+    ///
+    /// This function always returns a constant multiplier of 1 when the "constant-fees" feature is enabled.
+    #[cfg(feature = "constant-fees")]
+    fn convert(_previous: Multiplier) -> Multiplier {
+        1.into()
+    }
+}
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index 694fdec255b008068a2b530b193d8d56050e0ca8..967139552b6cbbf29ff5c93245f2aebd3073d12e 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -190,17 +190,18 @@ macro_rules! pallets_config {
         pub struct OnChargeTransaction;
 
         parameter_types! {
-              pub FeeMultiplier: Multiplier = Multiplier::one();
+        pub Target: Perquintill = Perquintill::from_percent(25);
+        pub MaxMultiplier: sp_runtime::FixedU128 = 10.into();
         }
         impl pallet_transaction_payment::Config for Runtime {
             type FeeMultiplierUpdate =
-                pallet_transaction_payment::ConstFeeMultiplier<FeeMultiplier>;
-            type LengthToFee = common_runtime::fees::LengthToFeeImpl<Balance>;
+                common_runtime::fees::FeeMultiplier<Self, Target, MaxMultiplier>;
+            type LengthToFee = common_runtime::fees::LengthToFeeImpl<Balance, Self, Target>;
             // does a filter on the call
             type OnChargeTransaction = OneshotAccount;
             type OperationalFeeMultiplier = frame_support::traits::ConstU8<5>;
             type RuntimeEvent = RuntimeEvent;
-            type WeightToFee = common_runtime::fees::WeightToFeeImpl<Balance>;
+            type WeightToFee = common_runtime::fees::WeightToFeeImpl<Balance, Self, Target>;
         }
         impl pallet_oneshot_account::Config for Runtime {
             type Currency = Balances;
diff --git a/runtime/g1/src/lib.rs b/runtime/g1/src/lib.rs
index b1a610b3e3caec792796cf60fa38f189cdf4a83b..21378216ab76301ca8bd4e2b2ad8fd4fb3c2c85f 100644
--- a/runtime/g1/src/lib.rs
+++ b/runtime/g1/src/lib.rs
@@ -50,7 +50,7 @@ pub use pallet_identity::{IdtyStatus, IdtyValue};
 pub use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_session::historical as session_historical;
 pub use pallet_timestamp::Call as TimestampCall;
-use pallet_transaction_payment::{FungibleAdapter, Multiplier};
+use pallet_transaction_payment::FungibleAdapter;
 pub use pallet_universal_dividend;
 use sp_api::impl_runtime_apis;
 use sp_core::OpaqueMetadata;
@@ -58,9 +58,9 @@ use sp_core::OpaqueMetadata;
 pub use sp_runtime::BuildStorage;
 use sp_runtime::{
     create_runtime_str, generic, impl_opaque_keys,
-    traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, NumberFor, One, OpaqueKeys},
+    traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, NumberFor, OpaqueKeys},
     transaction_validity::{TransactionSource, TransactionValidity},
-    ApplyExtrinsicResult,
+    ApplyExtrinsicResult, Perquintill,
 };
 pub use sp_runtime::{KeyTypeId, Perbill, Permill};
 use sp_std::prelude::*;
diff --git a/runtime/g1/src/parameters.rs b/runtime/g1/src/parameters.rs
index 280a9f728368e0720ab660e7907f7d031d9c1b71..4be9a23615cb9dd9a6e504f5a959c3127ff079e1 100644
--- a/runtime/g1/src/parameters.rs
+++ b/runtime/g1/src/parameters.rs
@@ -22,8 +22,7 @@ use sp_runtime::transaction_validity::TransactionPriority;
 parameter_types! {
     pub const BlockHashCount: BlockNumber = 2400;
     /// We allow for 2 seconds of compute with a 6 second average block time.
-    pub BlockWeights: frame_system::limits::BlockWeights = block_weights((Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0) * 2)
-        .set_proof_size(5 * 1024 * 1024), NORMAL_DISPATCH_RATIO);
+    pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND * 2u64, u64::MAX), NORMAL_DISPATCH_RATIO);
     pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
         ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
     pub const SS58Prefix: u16 = 4450;
diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs
index 37b9acd9ec60727b1adf2f6595eb41771b5bee24..e7bd13e0ba63e4451b06298a52c200475d79734f 100644
--- a/runtime/gdev/src/lib.rs
+++ b/runtime/gdev/src/lib.rs
@@ -50,7 +50,7 @@ use pallet_grandpa::{
 pub use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_session::historical as session_historical;
 pub use pallet_timestamp::Call as TimestampCall;
-use pallet_transaction_payment::{FungibleAdapter, Multiplier};
+use pallet_transaction_payment::FungibleAdapter;
 pub use pallet_universal_dividend;
 use sp_api::impl_runtime_apis;
 use sp_core::OpaqueMetadata;
@@ -58,9 +58,9 @@ use sp_core::OpaqueMetadata;
 pub use sp_runtime::BuildStorage;
 use sp_runtime::{
     create_runtime_str, generic, impl_opaque_keys,
-    traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, NumberFor, One, OpaqueKeys},
+    traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, NumberFor, OpaqueKeys},
     transaction_validity::{TransactionSource, TransactionValidity},
-    ApplyExtrinsicResult,
+    ApplyExtrinsicResult, Perquintill,
 };
 pub use sp_runtime::{KeyTypeId, Perbill, Permill};
 use sp_std::prelude::*;
diff --git a/runtime/gdev/src/parameters.rs b/runtime/gdev/src/parameters.rs
index 6ff9b244d0b3e8fcee6b9c10fe5ccda747044fa7..1d95aeb66ebad8ace544571dd47d90f4d0a03122 100644
--- a/runtime/gdev/src/parameters.rs
+++ b/runtime/gdev/src/parameters.rs
@@ -24,8 +24,7 @@ use sp_runtime::transaction_validity::TransactionPriority;
 parameter_types! {
     pub const BlockHashCount: BlockNumber = 2400;
     /// We allow for 2 seconds of compute with a 6 second average block time.
-    pub BlockWeights: frame_system::limits::BlockWeights = block_weights((Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0) * 2)
-        .set_proof_size(5 * 1024 * 1024), NORMAL_DISPATCH_RATIO);
+    pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND * 2u64, u64::MAX), NORMAL_DISPATCH_RATIO);
     pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
         ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
     pub const SS58Prefix: u16 = 42;
diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs
index 109d30c453bc7393df26abb9aff7b2f666362d49..35fc931bd4f08d09974062ce35b77112a3349d63 100644
--- a/runtime/gdev/tests/common/mod.rs
+++ b/runtime/gdev/tests/common/mod.rs
@@ -25,10 +25,12 @@ use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
 use sp_consensus_babe::{AuthorityId as BabeId, Slot, VrfInput, VrfProof};
 use sp_consensus_grandpa::AuthorityId as GrandpaId;
 use sp_core::{crypto::IsWrappedBy, sr25519, Encode, Pair, Public, H256};
+use sp_keyring::AccountKeyring;
 use sp_membership::MembershipData;
 use sp_runtime::{
+    generic::SignedPayload,
     testing::{Digest, DigestItem},
-    traits::{IdentifyAccount, Verify},
+    traits::{Extrinsic, IdentifyAccount, Verify},
 };
 use std::collections::BTreeMap;
 
@@ -342,6 +344,8 @@ pub fn run_to_block(n: u32) {
         // Set the new block number and author
         System::reset_events();
         System::set_block_number(System::block_number() + 1);
+        // Reset the block weight
+        System::set_block_consumed_resources(Weight::zero(), 0_usize);
 
         // Current slot is not incremented by BABE
         pallet_babe::CurrentSlot::<Runtime>::put(pallet_babe::CurrentSlot::<Runtime>::get() + 1);
@@ -475,3 +479,35 @@ fn session_keys(
         authority_discovery,
     }
 }
+
+/// get extrinsic for given call
+pub fn get_unchecked_extrinsic(
+    call: RuntimeCall,
+    era: u64,
+    block: u64,
+    signer: AccountKeyring,
+    tip: Balance,
+    nonce: u32,
+) -> gdev_runtime::UncheckedExtrinsic {
+    let extra: gdev_runtime::SignedExtra = (
+        frame_system::CheckNonZeroSender::<gdev_runtime::Runtime>::new(),
+        frame_system::CheckSpecVersion::<gdev_runtime::Runtime>::new(),
+        frame_system::CheckTxVersion::<gdev_runtime::Runtime>::new(),
+        frame_system::CheckGenesis::<gdev_runtime::Runtime>::new(),
+        frame_system::CheckMortality::<gdev_runtime::Runtime>::from(
+            sp_runtime::generic::Era::mortal(era, block),
+        ),
+        frame_system::CheckNonce::<gdev_runtime::Runtime>::from(nonce).into(),
+        frame_system::CheckWeight::<gdev_runtime::Runtime>::new(),
+        pallet_transaction_payment::ChargeTransactionPayment::<gdev_runtime::Runtime>::from(tip),
+    );
+    let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap();
+    let origin = signer;
+    let sig = payload.using_encoded(|payload| origin.pair().sign(payload));
+
+    gdev_runtime::UncheckedExtrinsic::new(
+        call,
+        Some((origin.to_account_id().into(), sig.into(), extra)),
+    )
+    .unwrap()
+}
diff --git a/runtime/gdev/tests/fee_tests.rs b/runtime/gdev/tests/fee_tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..45983d7e935c30a22ea4622bbb01ad1866e4e935
--- /dev/null
+++ b/runtime/gdev/tests/fee_tests.rs
@@ -0,0 +1,275 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Duniter-v2S 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+
+#![cfg(not(feature = "constant-fees"))]
+
+mod common;
+
+use common::*;
+use frame_support::{assert_ok, pallet_prelude::DispatchClass};
+use gdev_runtime::*;
+use sp_keyring::AccountKeyring;
+use sp_runtime::Perquintill;
+
+/// This test checks that an almost empty block incurs no fees for an extrinsic.
+#[test]
+fn test_fees_empty() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![
+            (AccountKeyring::Alice.to_account_id(), 10_000),
+            (AccountKeyring::Eve.to_account_id(), 10_000),
+        ])
+        .build()
+        .execute_with(|| {
+            let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death {
+                dest: AccountKeyring::Eve.to_account_id().into(),
+                value: 500,
+            });
+
+            let xt =
+                common::get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 0u64, 0);
+            assert_ok!(Executive::apply_extrinsic(xt));
+            // The block is almost empty, so the extrinsic should incur no fee
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                10_000 - 500
+            );
+        })
+}
+
+/// This test checks the fee behavior when the block is almost full.
+/// - Multiple extrinsics are applied successfully without incurring fees until the block is under target weight.
+/// - The last extrinsic incurs additional fees as the block reaches its target, verifying fee calculation under high load conditions.
+#[test]
+fn test_fees_weight() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![
+            (AccountKeyring::Alice.to_account_id(), 10_000),
+            (AccountKeyring::Eve.to_account_id(), 10_000),
+        ])
+        .build()
+        .execute_with(|| {
+            let mut transactions = 0u64;
+            let weights = BlockWeights::get();
+            let normal_max_weight = weights
+                .get(DispatchClass::Normal)
+                .max_total
+                .unwrap_or(weights.max_block);
+            // Stopping just below the limit
+            while System::block_weight()
+                .get(DispatchClass::Normal)
+                .all_lt(Target::get() * normal_max_weight * Perbill::from_percent(99))
+            {
+                let call = RuntimeCall::System(SystemCall::remark {
+                    remark: vec![255u8; 1],
+                });
+                let xt = get_unchecked_extrinsic(
+                    call,
+                    4u64,
+                    8u64,
+                    AccountKeyring::Alice,
+                    0u64,
+                    transactions as u32,
+                );
+                assert_ok!(Executive::apply_extrinsic(xt));
+                transactions += 1;
+            }
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                10_000
+            );
+            // Ensure that the next extrinsic exceeds the limit.
+            System::set_block_consumed_resources(Target::get() * normal_max_weight, 0_usize);
+            // The block will reach the fee limit, so the next extrinsic should start incurring fees.
+            let call = RuntimeCall::System(SystemCall::remark {
+                remark: vec![255u8; 1],
+            });
+
+            let xt = get_unchecked_extrinsic(
+                call,
+                4u64,
+                8u64,
+                AccountKeyring::Alice,
+                0u64,
+                transactions as u32,
+            );
+            assert_ok!(Executive::apply_extrinsic(xt));
+            assert_ne!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                10_000
+            );
+        })
+}
+
+/// This test checks the fee behavior when the block is almost full.
+/// - Multiple extrinsics are applied successfully without incurring fees until the block is under target length.
+/// - The last extrinsic incurs additional fees as the block reaches its target, verifying fee calculation under high load conditions.
+#[test]
+fn test_fees_length() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![
+            (AccountKeyring::Alice.to_account_id(), 10_000),
+            (AccountKeyring::Eve.to_account_id(), 10_000),
+        ])
+        .build()
+        .execute_with(|| {
+            let mut transactions = 0u64;
+            let length = BlockLength::get();
+            let normal_max_length = *length.max.get(DispatchClass::Normal) as u64;
+
+            // Stopping just below the limit
+            while u64::from(System::all_extrinsics_len())
+                < (Target::get() * Perquintill::from_percent(99) * normal_max_length)
+            {
+                let call = RuntimeCall::System(SystemCall::remark {
+                    remark: vec![255u8; 1_000],
+                });
+                let xt = get_unchecked_extrinsic(
+                    call,
+                    4u64,
+                    8u64,
+                    AccountKeyring::Alice,
+                    0u64,
+                    transactions as u32,
+                );
+                assert_ok!(Executive::apply_extrinsic(xt));
+                transactions += 1;
+            }
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                10_000
+            );
+            // Ensure that the next extrinsic exceeds the limit.
+            System::set_block_consumed_resources(
+                Weight::zero(),
+                (Target::get() * normal_max_length).try_into().unwrap(),
+            );
+            // The block will reach the fee limit, so the next extrinsic should start incurring fees.
+            let call = RuntimeCall::System(SystemCall::remark {
+                remark: vec![255u8; 1],
+            });
+
+            let xt = get_unchecked_extrinsic(
+                call,
+                4u64,
+                8u64,
+                AccountKeyring::Alice,
+                0u64,
+                transactions as u32,
+            );
+            assert_ok!(Executive::apply_extrinsic(xt));
+            assert_ne!(
+                Balances::free_balance(AccountKeyring::Alice.to_account_id()),
+                10_000
+            );
+        })
+}
+
+/// This test checks the behavior of the fee multiplier based on block weight
+/// and previous block weight.
+#[test]
+fn test_fees_multiplier_weight() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![
+            (AccountKeyring::Alice.to_account_id(), 10_000),
+            (AccountKeyring::Eve.to_account_id(), 10_000),
+        ])
+        .build()
+        .execute_with(|| {
+            let weights = BlockWeights::get();
+            let normal_max_weight = weights
+                .get(DispatchClass::Normal)
+                .max_total
+                .unwrap_or(weights.max_block);
+
+            assert_eq!(
+                pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(),
+                1.into()
+            );
+            // If the block weight is over the target and the previous block was also over the target,
+            // the fee multiplier is increased by one, up to the MaxMultiplier.
+            let mut current = 0u128;
+            for i in 1..20u128 {
+                System::set_block_consumed_resources(Target::get() * normal_max_weight, 0_usize);
+                run_to_block(i as u32);
+                current += 1;
+                assert_eq!(
+                    pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(),
+                    core::cmp::min(current.into(), MaxMultiplier::get())
+                );
+            }
+
+            // If the block weight is under the target and the previous block was also under the target,
+            // the fee multiplier is decreased by one, down to the one.
+            let mut current = 10u128;
+            for i in 20..50u32 {
+                run_to_block(i);
+                current = current.saturating_sub(1).max(1u128);
+                assert_eq!(
+                    pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(),
+                    current.into()
+                );
+            }
+        })
+}
+
+/// This test checks the behavior of the fee multiplier based on block length
+/// and previous block length.
+#[test]
+fn test_fees_multiplier_length() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![
+            (AccountKeyring::Alice.to_account_id(), 10_000),
+            (AccountKeyring::Eve.to_account_id(), 10_000),
+        ])
+        .build()
+        .execute_with(|| {
+            let length = BlockLength::get();
+            let normal_max_length = *length.max.get(DispatchClass::Normal) as u64;
+
+            assert_eq!(
+                pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(),
+                1.into()
+            );
+            // If the block weight is over the target and the previous block was also over the target,
+            // the fee multiplier is increased by one, up to the MaxMultiplier.
+            let mut current = 0u128;
+            for i in 1..20u128 {
+                System::set_block_consumed_resources(
+                    Weight::zero(),
+                    (Target::get() * normal_max_length).try_into().unwrap(),
+                );
+                run_to_block(i as u32);
+                current += 1;
+                assert_eq!(
+                    pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(),
+                    core::cmp::min(current.into(), MaxMultiplier::get())
+                );
+            }
+
+            // If the block weight is under the target and the previous block was also under the target,
+            // the fee multiplier is decreased by one, down to the one.
+            let mut current = 10u128;
+            for i in 20..50u32 {
+                run_to_block(i);
+                current = current.saturating_sub(1).max(1u128);
+                assert_eq!(
+                    pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(),
+                    current.into()
+                );
+            }
+        })
+}
diff --git a/runtime/gdev/tests/offences_tests.rs b/runtime/gdev/tests/offence_tests.rs
similarity index 100%
rename from runtime/gdev/tests/offences_tests.rs
rename to runtime/gdev/tests/offence_tests.rs
diff --git a/runtime/gdev/tests/xt_tests.rs b/runtime/gdev/tests/xt_tests.rs
index 9fa7f52599813bc3005681940268359b9df66557..5bbb96acc988411a73cd0d421eee496a92ee703d 100644
--- a/runtime/gdev/tests/xt_tests.rs
+++ b/runtime/gdev/tests/xt_tests.rs
@@ -17,6 +17,8 @@
 // these integration tests aim to test fees and extrinsic-related externalities
 // they need constant-fees feature to work
 
+#![cfg(feature = "constant-fees")]
+
 mod common;
 
 use common::*;
@@ -25,40 +27,8 @@ use frame_support::{
     traits::{OnIdle, StoredMap},
 };
 use gdev_runtime::*;
-use sp_core::{Encode, Pair};
+use sp_core::Encode;
 use sp_keyring::AccountKeyring;
-use sp_runtime::{generic::SignedPayload, traits::Extrinsic};
-
-/// get extrinsic for given call
-fn get_unchecked_extrinsic(
-    call: RuntimeCall,
-    era: u64,
-    block: u64,
-    signer: AccountKeyring,
-    tip: Balance,
-) -> UncheckedExtrinsic {
-    let extra: gdev_runtime::SignedExtra = (
-        frame_system::CheckNonZeroSender::<gdev_runtime::Runtime>::new(),
-        frame_system::CheckSpecVersion::<gdev_runtime::Runtime>::new(),
-        frame_system::CheckTxVersion::<gdev_runtime::Runtime>::new(),
-        frame_system::CheckGenesis::<gdev_runtime::Runtime>::new(),
-        frame_system::CheckMortality::<gdev_runtime::Runtime>::from(
-            sp_runtime::generic::Era::mortal(era, block),
-        ),
-        frame_system::CheckNonce::<gdev_runtime::Runtime>::from(0u32).into(),
-        frame_system::CheckWeight::<gdev_runtime::Runtime>::new(),
-        pallet_transaction_payment::ChargeTransactionPayment::<gdev_runtime::Runtime>::from(tip),
-    );
-    let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap();
-    let origin = signer;
-    let sig = payload.using_encoded(|payload| origin.pair().sign(payload));
-
-    UncheckedExtrinsic::new(
-        call,
-        Some((origin.to_account_id().into(), sig.into(), extra)),
-    )
-    .unwrap()
-}
 
 /// test currency transfer with extrinsic
 // the signer account should pay fees and a tip
@@ -78,7 +48,7 @@ fn test_transfer_xt() {
             });
 
             // 1 cÄžD of tip
-            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64);
+            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64, 0);
             // let info = xt.get_dispatch_info();
             // println!("dispatch info:\n\t {:?}\n", info);
 
@@ -114,7 +84,7 @@ fn test_refund_queue() {
             });
 
             // 1 cÄžD of tip
-            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64);
+            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64, 0);
             assert_ok!(Executive::apply_extrinsic(xt));
 
             // check that refund was added to the queue
@@ -147,7 +117,7 @@ fn test_refund_on_idle() {
             });
 
             // 1 cÄžD of tip
-            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64);
+            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Alice, 1u64, 0);
             assert_ok!(Executive::apply_extrinsic(xt));
 
             // call on_idle to activate refund
@@ -184,7 +154,7 @@ fn test_no_refund() {
                 dest: AccountKeyring::Alice.to_account_id().into(),
                 value: 500,
             });
-            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Eve, 1u64);
+            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Eve, 1u64, 0);
             assert_ok!(Executive::apply_extrinsic(xt));
             // check that refund queue is empty
             assert!(pallet_quota::RefundQueue::<Runtime>::get().is_empty());
@@ -224,7 +194,7 @@ fn test_refund_reaped_linked_account() {
                 dest: AccountKeyring::Alice.to_account_id().into(),
                 keep_alive: false,
             });
-            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Ferdie, 0u64);
+            let xt = get_unchecked_extrinsic(call, 4u64, 8u64, AccountKeyring::Ferdie, 0u64, 0);
             assert_ok!(Executive::apply_extrinsic(xt));
 
             assert_eq!(Balances::free_balance(ferdie.clone()), 0);
diff --git a/runtime/gtest/src/lib.rs b/runtime/gtest/src/lib.rs
index 124c8b74380ad1fb4491e5fc14bf09214c9abf9d..0a19c2e4b16cea0f306b1eb99303f4804a4ac0d2 100644
--- a/runtime/gtest/src/lib.rs
+++ b/runtime/gtest/src/lib.rs
@@ -49,7 +49,7 @@ use pallet_grandpa::{
 pub use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
 use pallet_session::historical as session_historical;
 pub use pallet_timestamp::Call as TimestampCall;
-use pallet_transaction_payment::{FungibleAdapter, Multiplier};
+use pallet_transaction_payment::FungibleAdapter;
 pub use pallet_universal_dividend;
 use sp_api::impl_runtime_apis;
 use sp_core::OpaqueMetadata;
@@ -57,9 +57,9 @@ use sp_core::OpaqueMetadata;
 pub use sp_runtime::BuildStorage;
 use sp_runtime::{
     create_runtime_str, generic, impl_opaque_keys,
-    traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, NumberFor, One, OpaqueKeys},
+    traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, NumberFor, OpaqueKeys},
     transaction_validity::{TransactionSource, TransactionValidity},
-    ApplyExtrinsicResult,
+    ApplyExtrinsicResult, Perquintill,
 };
 pub use sp_runtime::{KeyTypeId, Perbill, Permill};
 use sp_std::prelude::*;
diff --git a/runtime/gtest/src/parameters.rs b/runtime/gtest/src/parameters.rs
index c32a78c250b3942c16c0e8627e24d2477f7d24b7..6539fa51d5b8b7708a5e329c2682df51eeed9fc0 100644
--- a/runtime/gtest/src/parameters.rs
+++ b/runtime/gtest/src/parameters.rs
@@ -22,8 +22,7 @@ use sp_runtime::transaction_validity::TransactionPriority;
 parameter_types! {
     pub const BlockHashCount: BlockNumber = 2400;
     /// We allow for 2 seconds of compute with a 6 second average block time.
-    pub BlockWeights: frame_system::limits::BlockWeights = block_weights((Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0) * 2)
-        .set_proof_size(5 * 1024 * 1024), NORMAL_DISPATCH_RATIO);
+    pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND * 2u64, u64::MAX), NORMAL_DISPATCH_RATIO);
     pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
         ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
     pub const SS58Prefix: u16 = 42;