#![cfg_attr(not(feature = "std"), no_std)]
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use frame_support::{
dispatch::{
DispatchClass, DispatchInfo, DispatchResult, GetDispatchInfo, Pays, PostDispatchInfo,
},
pallet_prelude::TransactionSource,
traits::{Defensive, EstimateCallFee, Get, Imbalance, SuppressedDrop},
weights::{Weight, WeightToFee},
DebugNoBound,
};
pub use pallet::*;
pub use payment::*;
use sp_runtime::{
traits::{
Convert, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf, SaturatedConversion,
Saturating, TransactionExtension, Zero,
},
transaction_validity::{TransactionPriority, TransactionValidityError, ValidTransaction},
Debug, FixedPointNumber, FixedU128, Perbill, Perquintill,
};
pub use types::{FeeDetails, InclusionFee, RuntimeDispatchInfo};
pub use weights::WeightInfo;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(feature = "runtime-benchmarks")]
pub use benchmarking::Config as BenchmarkConfig;
mod payment;
mod types;
pub mod weights;
pub type Multiplier = FixedU128;
type BalanceOf<T> = <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
type CreditOf<T> = <StoredCreditOf<T> as SuppressedDrop>::Inner;
type StoredCreditOf<T> = <<T as Config>::OnChargeTransaction as TxCreditHold<T>>::Credit;
const LOG_TARGET: &str = "runtime::txpayment";
pub struct TargetedFeeAdjustment<T, S, V, M, X>(core::marker::PhantomData<(T, S, V, M, X)>);
pub trait MultiplierUpdate: Convert<Multiplier, Multiplier> {
fn min() -> Multiplier;
fn max() -> Multiplier;
fn target() -> Perquintill;
fn variability() -> Multiplier;
}
impl MultiplierUpdate for () {
fn min() -> Multiplier {
Default::default()
}
fn max() -> Multiplier {
<Multiplier as sp_runtime::traits::Bounded>::max_value()
}
fn target() -> Perquintill {
Default::default()
}
fn variability() -> Multiplier {
Default::default()
}
}
impl<T, S, V, M, X> MultiplierUpdate for TargetedFeeAdjustment<T, S, V, M, X>
where
T: frame_system::Config,
S: Get<Perquintill>,
V: Get<Multiplier>,
M: Get<Multiplier>,
X: Get<Multiplier>,
{
fn min() -> Multiplier {
M::get()
}
fn max() -> Multiplier {
X::get()
}
fn target() -> Perquintill {
S::get()
}
fn variability() -> Multiplier {
V::get()
}
}
impl<T, S, V, M, X> Convert<Multiplier, Multiplier> for TargetedFeeAdjustment<T, S, V, M, X>
where
T: frame_system::Config,
S: Get<Perquintill>,
V: Get<Multiplier>,
M: Get<Multiplier>,
X: Get<Multiplier>,
{
fn convert(previous: Multiplier) -> Multiplier {
let min_multiplier = M::get();
let max_multiplier = X::get();
let previous = previous.max(min_multiplier);
let weights = T::BlockWeights::get();
let normal_max_weight =
weights.get(DispatchClass::Normal).max_total.unwrap_or(weights.max_block);
let current_block_weight = frame_system::Pallet::<T>::block_weight();
let normal_block_weight =
current_block_weight.get(DispatchClass::Normal).min(normal_max_weight);
let normalized_ref_time = Perbill::from_rational(
normal_block_weight.ref_time(),
normal_max_weight.ref_time().max(1),
);
let normalized_proof_size = Perbill::from_rational(
normal_block_weight.proof_size(),
normal_max_weight.proof_size().max(1),
);
let (normal_limiting_dimension, max_limiting_dimension) =
if normalized_ref_time < normalized_proof_size {
(normal_block_weight.proof_size(), normal_max_weight.proof_size())
} else {
(normal_block_weight.ref_time(), normal_max_weight.ref_time())
};
let target_block_fullness = S::get();
let adjustment_variable = V::get();
let target_weight = (target_block_fullness * max_limiting_dimension) as u128;
let block_weight = normal_limiting_dimension as u128;
let positive = block_weight >= target_weight;
let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight);
let diff = Multiplier::saturating_from_rational(diff_abs, max_limiting_dimension.max(1));
let diff_squared = diff.saturating_mul(diff);
let v_squared_2 = adjustment_variable.saturating_mul(adjustment_variable) /
Multiplier::saturating_from_integer(2);
let first_term = adjustment_variable.saturating_mul(diff);
let second_term = v_squared_2.saturating_mul(diff_squared);
if positive {
let excess = first_term.saturating_add(second_term).saturating_mul(previous);
previous.saturating_add(excess).clamp(min_multiplier, max_multiplier)
} else {
let negative = first_term.saturating_sub(second_term).saturating_mul(previous);
previous.saturating_sub(negative).clamp(min_multiplier, max_multiplier)
}
}
}
pub struct ConstFeeMultiplier<M: Get<Multiplier>>(core::marker::PhantomData<M>);
impl<M: Get<Multiplier>> MultiplierUpdate for ConstFeeMultiplier<M> {
fn min() -> Multiplier {
M::get()
}
fn max() -> Multiplier {
M::get()
}
fn target() -> Perquintill {
Default::default()
}
fn variability() -> Multiplier {
Default::default()
}
}
impl<M> Convert<Multiplier, Multiplier> for ConstFeeMultiplier<M>
where
M: Get<Multiplier>,
{
fn convert(_previous: Multiplier) -> Multiplier {
Self::min()
}
}
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
pub enum Releases {
V1Ancient,
V2,
}
impl Default for Releases {
fn default() -> Self {
Releases::V1Ancient
}
}
const MULTIPLIER_DEFAULT_VALUE: Multiplier = Multiplier::from_u32(1);
#[frame_support::pallet]
pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use super::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
pub mod config_preludes {
use super::*;
use frame_support::derive_impl;
pub struct TestDefaultConfig;
#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
impl frame_system::DefaultConfig for TestDefaultConfig {}
#[frame_support::register_default_impl(TestDefaultConfig)]
impl DefaultConfig for TestDefaultConfig {
#[inject_runtime_type]
type RuntimeEvent = ();
type FeeMultiplierUpdate = ();
type OperationalFeeMultiplier = ();
type WeightInfo = ();
}
}
#[pallet::config(with_default)]
pub trait Config: frame_system::Config {
#[pallet::no_default_bounds]
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
#[pallet::no_default]
type OnChargeTransaction: OnChargeTransaction<Self>;
#[pallet::no_default]
type WeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
#[pallet::no_default]
type LengthToFee: WeightToFee<Balance = BalanceOf<Self>>;
type FeeMultiplierUpdate: MultiplierUpdate;
#[pallet::constant]
type OperationalFeeMultiplier: Get<u8>;
type WeightInfo: WeightInfo;
}
#[pallet::type_value]
pub fn NextFeeMultiplierOnEmpty() -> Multiplier {
MULTIPLIER_DEFAULT_VALUE
}
#[pallet::storage]
#[pallet::whitelist_storage]
pub type NextFeeMultiplier<T: Config> =
StorageValue<_, Multiplier, ValueQuery, NextFeeMultiplierOnEmpty>;
#[pallet::storage]
pub type StorageVersion<T: Config> = StorageValue<_, Releases, ValueQuery>;
#[pallet::storage]
#[pallet::whitelist_storage]
pub(crate) type TxPaymentCredit<T: Config> = StorageValue<_, StoredCreditOf<T>>;
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub multiplier: Multiplier,
#[serde(skip)]
pub _config: core::marker::PhantomData<T>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self { multiplier: MULTIPLIER_DEFAULT_VALUE, _config: Default::default() }
}
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
StorageVersion::<T>::put(Releases::V2);
NextFeeMultiplier::<T>::put(self.multiplier);
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
TransactionFeePaid { who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T> },
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_finalize(_: frame_system::pallet_prelude::BlockNumberFor<T>) {
NextFeeMultiplier::<T>::mutate(|fm| {
*fm = T::FeeMultiplierUpdate::convert(*fm);
});
TxPaymentCredit::<T>::take().map(|credit| {
log::error!(target: LOG_TARGET, "The `TxPaymentCredit` was stored between blocks. This is a bug.");
credit.into_inner()
});
}
#[cfg(feature = "std")]
fn integrity_test() {
assert!(
<Multiplier as sp_runtime::traits::Bounded>::max_value() >=
Multiplier::checked_from_integer::<u128>(
T::BlockWeights::get().max_block.ref_time().try_into().unwrap()
)
.unwrap(),
);
let target = T::FeeMultiplierUpdate::target() *
T::BlockWeights::get().get(DispatchClass::Normal).max_total.expect(
"Setting `max_total` for `Normal` dispatch class is not compatible with \
`transaction-payment` pallet.",
);
let addition = target / 100;
if addition == Weight::zero() {
return;
}
let min_value = T::FeeMultiplierUpdate::min();
let target = target + addition;
frame_system::Pallet::<T>::set_block_consumed_resources(target, 0);
let next = T::FeeMultiplierUpdate::convert(min_value);
assert!(
next > min_value,
"The minimum bound of the multiplier is too low. When \
block saturation is more than target by 1% and multiplier is minimal then \
the multiplier doesn't increase."
);
}
}
}
impl<T: Config> Pallet<T> {
pub fn next_fee_multiplier() -> Multiplier {
NextFeeMultiplier::<T>::get()
}
pub fn query_info<Extrinsic: sp_runtime::traits::ExtrinsicLike + GetDispatchInfo>(
unchecked_extrinsic: Extrinsic,
len: u32,
) -> RuntimeDispatchInfo<BalanceOf<T>>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
{
let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
let partial_fee = if unchecked_extrinsic.is_bare() {
0u32.into()
} else {
Self::compute_fee(len, &dispatch_info, 0u32.into())
};
let DispatchInfo { class, .. } = dispatch_info;
RuntimeDispatchInfo { weight: dispatch_info.total_weight(), class, partial_fee }
}
pub fn query_fee_details<Extrinsic: sp_runtime::traits::ExtrinsicLike + GetDispatchInfo>(
unchecked_extrinsic: Extrinsic,
len: u32,
) -> FeeDetails<BalanceOf<T>>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
{
let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
let tip = 0u32.into();
if unchecked_extrinsic.is_bare() {
FeeDetails { inclusion_fee: None, tip }
} else {
Self::compute_fee_details(len, &dispatch_info, tip)
}
}
pub fn query_call_info(call: T::RuntimeCall, len: u32) -> RuntimeDispatchInfo<BalanceOf<T>>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
{
let dispatch_info = <T::RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
let DispatchInfo { class, .. } = dispatch_info;
RuntimeDispatchInfo {
weight: dispatch_info.total_weight(),
class,
partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()),
}
}
pub fn query_call_fee_details(call: T::RuntimeCall, len: u32) -> FeeDetails<BalanceOf<T>>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
{
let dispatch_info = <T::RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
let tip = 0u32.into();
Self::compute_fee_details(len, &dispatch_info, tip)
}
pub fn compute_fee(
len: u32,
info: &DispatchInfoOf<T::RuntimeCall>,
tip: BalanceOf<T>,
) -> BalanceOf<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
{
Self::compute_fee_details(len, info, tip).final_fee()
}
pub fn compute_fee_details(
len: u32,
info: &DispatchInfoOf<T::RuntimeCall>,
tip: BalanceOf<T>,
) -> FeeDetails<BalanceOf<T>>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
{
Self::compute_fee_raw(len, info.total_weight(), tip, info.pays_fee, info.class)
}
pub fn compute_actual_fee(
len: u32,
info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
tip: BalanceOf<T>,
) -> BalanceOf<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
Self::compute_actual_fee_details(len, info, post_info, tip).final_fee()
}
pub fn compute_actual_fee_details(
len: u32,
info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
tip: BalanceOf<T>,
) -> FeeDetails<BalanceOf<T>>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
Self::compute_fee_raw(
len,
post_info.calc_actual_weight(info),
tip,
post_info.pays_fee(info),
info.class,
)
}
fn compute_fee_raw(
len: u32,
weight: Weight,
tip: BalanceOf<T>,
pays_fee: Pays,
class: DispatchClass,
) -> FeeDetails<BalanceOf<T>> {
if pays_fee == Pays::Yes {
let unadjusted_weight_fee = Self::weight_to_fee(weight);
let multiplier = NextFeeMultiplier::<T>::get();
let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee);
let len_fee = Self::length_to_fee(len);
let base_fee = Self::weight_to_fee(T::BlockWeights::get().get(class).base_extrinsic);
FeeDetails {
inclusion_fee: Some(InclusionFee { base_fee, len_fee, adjusted_weight_fee }),
tip,
}
} else {
FeeDetails { inclusion_fee: None, tip }
}
}
pub fn length_to_fee(length: u32) -> BalanceOf<T> {
T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0))
}
pub fn weight_to_fee(weight: Weight) -> BalanceOf<T> {
let capped_weight = weight.min(T::BlockWeights::get().max_block);
T::WeightToFee::weight_to_fee(&capped_weight)
}
pub fn deposit_fee_paid_event(who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T>) {
Self::deposit_event(Event::TransactionFeePaid { who, actual_fee, tip });
}
pub fn withdraw_txfee<Balance>(amount: Balance) -> Option<CreditOf<T>>
where
CreditOf<T>: Imbalance<Balance>,
Balance: PartialOrd,
{
<TxPaymentCredit<T>>::mutate(|credit| {
let credit = SuppressedDrop::as_mut(credit.as_mut()?);
if amount > credit.peek() {
return None;
}
Some(credit.extract(amount))
})
}
pub fn deposit_txfee<Balance>(deposit: CreditOf<T>)
where
CreditOf<T>: Imbalance<Balance>,
{
<TxPaymentCredit<T>>::mutate(|credit| {
if let Some(credit) = credit.as_mut().map(SuppressedDrop::as_mut) {
credit.subsume(deposit);
} else {
*credit = Some(SuppressedDrop::new(deposit))
}
});
}
pub fn remaining_txfee<Balance>() -> Balance
where
CreditOf<T>: Imbalance<Balance>,
Balance: Default,
{
<TxPaymentCredit<T>>::get()
.map(|c| SuppressedDrop::as_ref(&c).peek())
.unwrap_or_default()
}
}
impl<T> Convert<Weight, BalanceOf<T>> for Pallet<T>
where
T: Config,
{
fn convert(weight: Weight) -> BalanceOf<T> {
NextFeeMultiplier::<T>::get().saturating_mul_int(Self::weight_to_fee(weight))
}
}
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);
impl<T: Config> ChargeTransactionPayment<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<T>: Send + Sync,
{
pub fn from(fee: BalanceOf<T>) -> Self {
Self(fee)
}
pub fn tip(&self) -> BalanceOf<T> {
self.0
}
fn withdraw_fee(
&self,
who: &T::AccountId,
call: &T::RuntimeCall,
info: &DispatchInfoOf<T::RuntimeCall>,
fee_with_tip: BalanceOf<T>,
) -> Result<
(
BalanceOf<T>,
<<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
),
TransactionValidityError,
> {
let tip = self.0;
<<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::withdraw_fee(
who,
call,
info,
fee_with_tip,
tip,
)
.map(|liquidity_info| (fee_with_tip, liquidity_info))
}
fn can_withdraw_fee(
&self,
who: &T::AccountId,
call: &T::RuntimeCall,
info: &DispatchInfoOf<T::RuntimeCall>,
len: usize,
) -> Result<BalanceOf<T>, TransactionValidityError> {
let tip = self.0;
let fee_with_tip = Pallet::<T>::compute_fee(len as u32, info, tip);
<<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::can_withdraw_fee(
who,
call,
info,
fee_with_tip,
tip,
)?;
Ok(fee_with_tip)
}
pub fn get_priority(
info: &DispatchInfoOf<T::RuntimeCall>,
len: usize,
tip: BalanceOf<T>,
final_fee_with_tip: BalanceOf<T>,
) -> TransactionPriority {
let max_block_weight = T::BlockWeights::get().max_block;
let max_block_length = *T::BlockLength::get().max.get(info.class) as u64;
let bounded_weight =
info.total_weight().max(Weight::from_parts(1, 1)).min(max_block_weight);
let bounded_length = (len as u64).clamp(1, max_block_length);
let max_tx_per_block_weight = max_block_weight
.checked_div_per_component(&bounded_weight)
.defensive_proof("bounded_weight is non-zero; qed")
.unwrap_or(1);
let max_tx_per_block_length = max_block_length / bounded_length;
let max_tx_per_block = max_tx_per_block_length
.min(max_tx_per_block_weight)
.saturated_into::<BalanceOf<T>>();
let max_reward = |val: BalanceOf<T>| val.saturating_mul(max_tx_per_block);
let tip = tip.saturating_add(One::one());
let scaled_tip = max_reward(tip);
match info.class {
DispatchClass::Normal => {
scaled_tip
},
DispatchClass::Mandatory => {
scaled_tip
},
DispatchClass::Operational => {
let fee_multiplier = T::OperationalFeeMultiplier::get().saturated_into();
let virtual_tip = final_fee_with_tip.saturating_mul(fee_multiplier);
let scaled_virtual_tip = max_reward(virtual_tip);
scaled_tip.saturating_add(scaled_virtual_tip)
},
}
.saturated_into::<TransactionPriority>()
}
}
impl<T: Config> core::fmt::Debug for ChargeTransactionPayment<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "ChargeTransactionPayment<{:?}>", self.0)
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
#[derive(DebugNoBound)]
pub enum Val<T: Config> {
Charge {
tip: BalanceOf<T>,
who: T::AccountId,
fee_with_tip: BalanceOf<T>,
},
NoCharge,
}
pub enum Pre<T: Config> {
Charge {
tip: BalanceOf<T>,
who: T::AccountId,
liquidity_info:
<<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
},
NoCharge {
refund: Weight,
},
}
impl<T: Config> core::fmt::Debug for Pre<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Pre::Charge { tip, who, liquidity_info: _ } => {
write!(f, "Charge {{ tip: {:?}, who: {:?}, imbalance: <stripped> }}", tip, who)
},
Pre::NoCharge { refund } => write!(f, "NoCharge {{ refund: {:?} }}", refund),
}
}
#[cfg(not(feature = "std"))]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.write_str("<wasm:stripped>")
}
}
impl<T: Config> TransactionExtension<T::RuntimeCall> for ChargeTransactionPayment<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
const IDENTIFIER: &'static str = "ChargeTransactionPayment";
type Implicit = ();
type Val = Val<T>;
type Pre = Pre<T>;
fn weight(&self, _: &T::RuntimeCall) -> Weight {
T::WeightInfo::charge_transaction_payment()
}
fn validate(
&self,
origin: <T::RuntimeCall as Dispatchable>::RuntimeOrigin,
call: &T::RuntimeCall,
info: &DispatchInfoOf<T::RuntimeCall>,
len: usize,
_: (),
_implication: &impl Encode,
_source: TransactionSource,
) -> Result<
(ValidTransaction, Self::Val, <T::RuntimeCall as Dispatchable>::RuntimeOrigin),
TransactionValidityError,
> {
let Ok(who) = frame_system::ensure_signed(origin.clone()) else {
return Ok((ValidTransaction::default(), Val::NoCharge, origin));
};
let fee_with_tip = self.can_withdraw_fee(&who, call, info, len)?;
let tip = self.0;
Ok((
ValidTransaction {
priority: Self::get_priority(info, len, tip, fee_with_tip),
..Default::default()
},
Val::Charge { tip: self.0, who, fee_with_tip },
origin,
))
}
fn prepare(
self,
val: Self::Val,
_origin: &<T::RuntimeCall as Dispatchable>::RuntimeOrigin,
call: &T::RuntimeCall,
info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
match val {
Val::Charge { tip, who, fee_with_tip } => {
let (_fee_with_tip, liquidity_info) =
self.withdraw_fee(&who, call, info, fee_with_tip)?;
Ok(Pre::Charge { tip, who, liquidity_info })
},
Val::NoCharge => Ok(Pre::NoCharge { refund: self.weight(call) }),
}
}
fn post_dispatch_details(
pre: Self::Pre,
info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
let (tip, who, liquidity_info) = match pre {
Pre::Charge { tip, who, liquidity_info } => (tip, who, liquidity_info),
Pre::NoCharge { refund } => {
return Ok(refund);
},
};
let actual_fee_with_tip =
Pallet::<T>::compute_actual_fee(len as u32, info, &post_info, tip);
T::OnChargeTransaction::correct_and_deposit_fee(
&who,
info,
&post_info,
actual_fee_with_tip,
tip,
liquidity_info,
)?;
Pallet::<T>::deposit_event(Event::<T>::TransactionFeePaid {
who,
actual_fee: actual_fee_with_tip,
tip,
});
Ok(Weight::zero())
}
}
impl<T: Config, AnyCall: GetDispatchInfo + Encode> EstimateCallFee<AnyCall, BalanceOf<T>>
for Pallet<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
fn estimate_call_fee(call: &AnyCall, post_info: PostDispatchInfo) -> BalanceOf<T> {
let len = call.encoded_size() as u32;
let info = call.get_dispatch_info();
Self::compute_actual_fee(len, &info, &post_info, Zero::zero())
}
}