1#![cfg_attr(not(feature = "std"), no_std)]
49
50use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
51use scale_info::TypeInfo;
52
53use frame_support::{
54 dispatch::{
55 DispatchClass, DispatchInfo, DispatchResult, GetDispatchInfo, Pays, PostDispatchInfo,
56 },
57 pallet_prelude::TransactionSource,
58 traits::{Defensive, EstimateCallFee, Get, Imbalance, SuppressedDrop},
59 weights::{Weight, WeightToFee},
60 DebugNoBound,
61};
62pub use pallet::*;
63pub use payment::*;
64use sp_runtime::{
65 traits::{
66 Convert, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf, SaturatedConversion,
67 Saturating, TransactionExtension, Zero,
68 },
69 transaction_validity::{TransactionPriority, TransactionValidityError, ValidTransaction},
70 Debug, FixedPointNumber, FixedU128, Perbill, Perquintill,
71};
72pub use types::{FeeDetails, InclusionFee, RuntimeDispatchInfo};
73pub use weights::WeightInfo;
74
75#[cfg(test)]
76mod mock;
77#[cfg(test)]
78mod tests;
79
80#[cfg(feature = "runtime-benchmarks")]
81mod benchmarking;
82#[cfg(feature = "runtime-benchmarks")]
83pub use benchmarking::Config as BenchmarkConfig;
84
85mod payment;
86mod types;
87pub mod weights;
88
89pub type Multiplier = FixedU128;
91
92type BalanceOf<T> = <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
93type CreditOf<T> = <StoredCreditOf<T> as SuppressedDrop>::Inner;
94type StoredCreditOf<T> = <<T as Config>::OnChargeTransaction as TxCreditHold<T>>::Credit;
95
96const LOG_TARGET: &str = "runtime::txpayment";
97
98pub struct TargetedFeeAdjustment<T, S, V, M, X>(core::marker::PhantomData<(T, S, V, M, X)>);
151
152pub trait MultiplierUpdate: Convert<Multiplier, Multiplier> {
154 fn min() -> Multiplier;
156 fn max() -> Multiplier;
158 fn target() -> Perquintill;
160 fn variability() -> Multiplier;
162}
163
164impl MultiplierUpdate for () {
165 fn min() -> Multiplier {
166 Default::default()
167 }
168 fn max() -> Multiplier {
169 <Multiplier as sp_runtime::traits::Bounded>::max_value()
170 }
171 fn target() -> Perquintill {
172 Default::default()
173 }
174 fn variability() -> Multiplier {
175 Default::default()
176 }
177}
178
179impl<T, S, V, M, X> MultiplierUpdate for TargetedFeeAdjustment<T, S, V, M, X>
180where
181 T: frame_system::Config,
182 S: Get<Perquintill>,
183 V: Get<Multiplier>,
184 M: Get<Multiplier>,
185 X: Get<Multiplier>,
186{
187 fn min() -> Multiplier {
188 M::get()
189 }
190 fn max() -> Multiplier {
191 X::get()
192 }
193 fn target() -> Perquintill {
194 S::get()
195 }
196 fn variability() -> Multiplier {
197 V::get()
198 }
199}
200
201impl<T, S, V, M, X> Convert<Multiplier, Multiplier> for TargetedFeeAdjustment<T, S, V, M, X>
202where
203 T: frame_system::Config,
204 S: Get<Perquintill>,
205 V: Get<Multiplier>,
206 M: Get<Multiplier>,
207 X: Get<Multiplier>,
208{
209 fn convert(previous: Multiplier) -> Multiplier {
210 let min_multiplier = M::get();
214 let max_multiplier = X::get();
215 let previous = previous.max(min_multiplier);
216
217 let weights = T::BlockWeights::get();
218 let normal_max_weight =
220 weights.get(DispatchClass::Normal).max_total.unwrap_or(weights.max_block);
221 let current_block_weight = frame_system::Pallet::<T>::block_weight();
222 let normal_block_weight =
223 current_block_weight.get(DispatchClass::Normal).min(normal_max_weight);
224
225 let normalized_ref_time = Perbill::from_rational(
227 normal_block_weight.ref_time(),
228 normal_max_weight.ref_time().max(1),
229 );
230 let normalized_proof_size = Perbill::from_rational(
231 normal_block_weight.proof_size(),
232 normal_max_weight.proof_size().max(1),
233 );
234
235 let (normal_limiting_dimension, max_limiting_dimension) =
238 if normalized_ref_time < normalized_proof_size {
239 (normal_block_weight.proof_size(), normal_max_weight.proof_size())
240 } else {
241 (normal_block_weight.ref_time(), normal_max_weight.ref_time())
242 };
243
244 let target_block_fullness = S::get();
245 let adjustment_variable = V::get();
246
247 let target_weight = (target_block_fullness * max_limiting_dimension) as u128;
248 let block_weight = normal_limiting_dimension as u128;
249
250 let positive = block_weight >= target_weight;
252 let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight);
253
254 let diff = Multiplier::saturating_from_rational(diff_abs, max_limiting_dimension.max(1));
257 let diff_squared = diff.saturating_mul(diff);
258
259 let v_squared_2 = adjustment_variable.saturating_mul(adjustment_variable) /
260 Multiplier::saturating_from_integer(2);
261
262 let first_term = adjustment_variable.saturating_mul(diff);
263 let second_term = v_squared_2.saturating_mul(diff_squared);
264
265 if positive {
266 let excess = first_term.saturating_add(second_term).saturating_mul(previous);
267 previous.saturating_add(excess).clamp(min_multiplier, max_multiplier)
268 } else {
269 let negative = first_term.saturating_sub(second_term).saturating_mul(previous);
271 previous.saturating_sub(negative).clamp(min_multiplier, max_multiplier)
272 }
273 }
274}
275
276pub struct ConstFeeMultiplier<M: Get<Multiplier>>(core::marker::PhantomData<M>);
278
279impl<M: Get<Multiplier>> MultiplierUpdate for ConstFeeMultiplier<M> {
280 fn min() -> Multiplier {
281 M::get()
282 }
283 fn max() -> Multiplier {
284 M::get()
285 }
286 fn target() -> Perquintill {
287 Default::default()
288 }
289 fn variability() -> Multiplier {
290 Default::default()
291 }
292}
293
294impl<M> Convert<Multiplier, Multiplier> for ConstFeeMultiplier<M>
295where
296 M: Get<Multiplier>,
297{
298 fn convert(_previous: Multiplier) -> Multiplier {
299 Self::min()
300 }
301}
302
303#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
305pub enum Releases {
306 V1Ancient,
308 V2,
310}
311
312impl Default for Releases {
313 fn default() -> Self {
314 Releases::V1Ancient
315 }
316}
317
318const MULTIPLIER_DEFAULT_VALUE: Multiplier = Multiplier::from_u32(1);
321
322#[frame_support::pallet]
323pub mod pallet {
324 use frame_support::pallet_prelude::*;
325 use frame_system::pallet_prelude::*;
326
327 use super::*;
328
329 #[pallet::pallet]
330 pub struct Pallet<T>(_);
331
332 pub mod config_preludes {
333 use super::*;
334 use frame_support::derive_impl;
335
336 pub struct TestDefaultConfig;
338
339 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
340 impl frame_system::DefaultConfig for TestDefaultConfig {}
341
342 #[frame_support::register_default_impl(TestDefaultConfig)]
343 impl DefaultConfig for TestDefaultConfig {
344 #[inject_runtime_type]
345 type RuntimeEvent = ();
346 type FeeMultiplierUpdate = ();
347 type OperationalFeeMultiplier = ();
348 type WeightInfo = ();
349 }
350 }
351
352 #[pallet::config(with_default)]
353 pub trait Config: frame_system::Config {
354 #[pallet::no_default_bounds]
356 #[allow(deprecated)]
357 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
358
359 #[pallet::no_default]
366 type OnChargeTransaction: OnChargeTransaction<Self>;
367
368 #[pallet::no_default]
370 type WeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
371
372 #[pallet::no_default]
374 type LengthToFee: WeightToFee<Balance = BalanceOf<Self>>;
375
376 type FeeMultiplierUpdate: MultiplierUpdate;
378
379 #[pallet::constant]
401 type OperationalFeeMultiplier: Get<u8>;
402
403 type WeightInfo: WeightInfo;
405 }
406
407 #[pallet::type_value]
408 pub fn NextFeeMultiplierOnEmpty() -> Multiplier {
409 MULTIPLIER_DEFAULT_VALUE
410 }
411
412 #[pallet::storage]
413 #[pallet::whitelist_storage]
414 pub type NextFeeMultiplier<T: Config> =
415 StorageValue<_, Multiplier, ValueQuery, NextFeeMultiplierOnEmpty>;
416
417 #[pallet::storage]
418 pub type StorageVersion<T: Config> = StorageValue<_, Releases, ValueQuery>;
419
420 #[pallet::storage]
424 #[pallet::whitelist_storage]
425 pub(crate) type TxPaymentCredit<T: Config> = StorageValue<_, StoredCreditOf<T>>;
426
427 #[pallet::genesis_config]
428 pub struct GenesisConfig<T: Config> {
429 pub multiplier: Multiplier,
430 #[serde(skip)]
431 pub _config: core::marker::PhantomData<T>,
432 }
433
434 impl<T: Config> Default for GenesisConfig<T> {
435 fn default() -> Self {
436 Self { multiplier: MULTIPLIER_DEFAULT_VALUE, _config: Default::default() }
437 }
438 }
439
440 #[pallet::genesis_build]
441 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
442 fn build(&self) {
443 StorageVersion::<T>::put(Releases::V2);
444 NextFeeMultiplier::<T>::put(self.multiplier);
445 }
446 }
447
448 #[pallet::event]
449 #[pallet::generate_deposit(pub(super) fn deposit_event)]
450 pub enum Event<T: Config> {
451 TransactionFeePaid { who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T> },
454 }
455
456 #[pallet::hooks]
457 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
458 fn on_finalize(_: frame_system::pallet_prelude::BlockNumberFor<T>) {
459 NextFeeMultiplier::<T>::mutate(|fm| {
460 *fm = T::FeeMultiplierUpdate::convert(*fm);
461 });
462
463 TxPaymentCredit::<T>::take().map(|credit| {
467 log::error!(target: LOG_TARGET, "The `TxPaymentCredit` was stored between blocks. This is a bug.");
468 credit.into_inner()
470 });
471 }
472
473 #[cfg(feature = "std")]
474 fn integrity_test() {
475 assert!(
479 <Multiplier as sp_runtime::traits::Bounded>::max_value() >=
480 Multiplier::checked_from_integer::<u128>(
481 T::BlockWeights::get().max_block.ref_time().try_into().unwrap()
482 )
483 .unwrap(),
484 );
485
486 let target = T::FeeMultiplierUpdate::target() *
487 T::BlockWeights::get().get(DispatchClass::Normal).max_total.expect(
488 "Setting `max_total` for `Normal` dispatch class is not compatible with \
489 `transaction-payment` pallet.",
490 );
491 let addition = target / 100;
493 if addition == Weight::zero() {
494 return;
497 }
498
499 let min_value = T::FeeMultiplierUpdate::min();
504 let target = target + addition;
505
506 frame_system::Pallet::<T>::set_block_consumed_resources(target, 0);
507 let next = T::FeeMultiplierUpdate::convert(min_value);
508 assert!(
509 next > min_value,
510 "The minimum bound of the multiplier is too low. When \
511 block saturation is more than target by 1% and multiplier is minimal then \
512 the multiplier doesn't increase."
513 );
514 }
515 }
516}
517
518impl<T: Config> Pallet<T> {
519 pub fn next_fee_multiplier() -> Multiplier {
521 NextFeeMultiplier::<T>::get()
522 }
523
524 pub fn query_info<Extrinsic: sp_runtime::traits::ExtrinsicLike + GetDispatchInfo>(
533 unchecked_extrinsic: Extrinsic,
534 len: u32,
535 ) -> RuntimeDispatchInfo<BalanceOf<T>>
536 where
537 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
538 {
539 let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
545
546 let partial_fee = if unchecked_extrinsic.is_bare() {
547 0u32.into()
549 } else {
550 Self::compute_fee(len, &dispatch_info, 0u32.into())
551 };
552
553 let DispatchInfo { class, .. } = dispatch_info;
554
555 RuntimeDispatchInfo { weight: dispatch_info.total_weight(), class, partial_fee }
556 }
557
558 pub fn query_fee_details<Extrinsic: sp_runtime::traits::ExtrinsicLike + GetDispatchInfo>(
560 unchecked_extrinsic: Extrinsic,
561 len: u32,
562 ) -> FeeDetails<BalanceOf<T>>
563 where
564 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
565 {
566 let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
567
568 let tip = 0u32.into();
569
570 if unchecked_extrinsic.is_bare() {
571 FeeDetails { inclusion_fee: None, tip }
573 } else {
574 Self::compute_fee_details(len, &dispatch_info, tip)
575 }
576 }
577
578 pub fn query_call_info(call: T::RuntimeCall, len: u32) -> RuntimeDispatchInfo<BalanceOf<T>>
580 where
581 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
582 {
583 let dispatch_info = <T::RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
584 let DispatchInfo { class, .. } = dispatch_info;
585
586 RuntimeDispatchInfo {
587 weight: dispatch_info.total_weight(),
588 class,
589 partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()),
590 }
591 }
592
593 pub fn query_call_fee_details(call: T::RuntimeCall, len: u32) -> FeeDetails<BalanceOf<T>>
595 where
596 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
597 {
598 let dispatch_info = <T::RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
599 let tip = 0u32.into();
600
601 Self::compute_fee_details(len, &dispatch_info, tip)
602 }
603
604 pub fn compute_fee(
606 len: u32,
607 info: &DispatchInfoOf<T::RuntimeCall>,
608 tip: BalanceOf<T>,
609 ) -> BalanceOf<T>
610 where
611 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
612 {
613 Self::compute_fee_details(len, info, tip).final_fee()
614 }
615
616 pub fn compute_fee_details(
618 len: u32,
619 info: &DispatchInfoOf<T::RuntimeCall>,
620 tip: BalanceOf<T>,
621 ) -> FeeDetails<BalanceOf<T>>
622 where
623 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
624 {
625 Self::compute_fee_raw(len, info.total_weight(), tip, info.pays_fee, info.class)
626 }
627
628 pub fn compute_actual_fee(
633 len: u32,
634 info: &DispatchInfoOf<T::RuntimeCall>,
635 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
636 tip: BalanceOf<T>,
637 ) -> BalanceOf<T>
638 where
639 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
640 {
641 Self::compute_actual_fee_details(len, info, post_info, tip).final_fee()
642 }
643
644 pub fn compute_actual_fee_details(
646 len: u32,
647 info: &DispatchInfoOf<T::RuntimeCall>,
648 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
649 tip: BalanceOf<T>,
650 ) -> FeeDetails<BalanceOf<T>>
651 where
652 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
653 {
654 Self::compute_fee_raw(
655 len,
656 post_info.calc_actual_weight(info),
657 tip,
658 post_info.pays_fee(info),
659 info.class,
660 )
661 }
662
663 fn compute_fee_raw(
664 len: u32,
665 weight: Weight,
666 tip: BalanceOf<T>,
667 pays_fee: Pays,
668 class: DispatchClass,
669 ) -> FeeDetails<BalanceOf<T>> {
670 if pays_fee == Pays::Yes {
671 let unadjusted_weight_fee = Self::weight_to_fee(weight);
673 let multiplier = NextFeeMultiplier::<T>::get();
674 let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee);
676
677 let len_fee = Self::length_to_fee(len);
679
680 let base_fee = Self::weight_to_fee(T::BlockWeights::get().get(class).base_extrinsic);
681 FeeDetails {
682 inclusion_fee: Some(InclusionFee { base_fee, len_fee, adjusted_weight_fee }),
683 tip,
684 }
685 } else {
686 FeeDetails { inclusion_fee: None, tip }
687 }
688 }
689
690 pub fn length_to_fee(length: u32) -> BalanceOf<T> {
692 T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0))
693 }
694
695 pub fn weight_to_fee(weight: Weight) -> BalanceOf<T> {
698 let capped_weight = weight.min(T::BlockWeights::get().max_block);
701 T::WeightToFee::weight_to_fee(&capped_weight)
702 }
703
704 pub fn deposit_fee_paid_event(who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T>) {
706 Self::deposit_event(Event::TransactionFeePaid { who, actual_fee, tip });
707 }
708
709 pub fn withdraw_txfee<Balance>(amount: Balance) -> Option<CreditOf<T>>
727 where
728 CreditOf<T>: Imbalance<Balance>,
729 Balance: PartialOrd,
730 {
731 <TxPaymentCredit<T>>::mutate(|credit| {
732 let credit = SuppressedDrop::as_mut(credit.as_mut()?);
733 if amount > credit.peek() {
734 return None;
735 }
736 Some(credit.extract(amount))
737 })
738 }
739
740 pub fn deposit_txfee<Balance>(deposit: CreditOf<T>)
742 where
743 CreditOf<T>: Imbalance<Balance>,
744 {
745 <TxPaymentCredit<T>>::mutate(|credit| {
746 if let Some(credit) = credit.as_mut().map(SuppressedDrop::as_mut) {
747 credit.subsume(deposit);
748 } else {
749 *credit = Some(SuppressedDrop::new(deposit))
750 }
751 });
752 }
753
754 pub fn remaining_txfee<Balance>() -> Balance
760 where
761 CreditOf<T>: Imbalance<Balance>,
762 Balance: Default,
763 {
764 <TxPaymentCredit<T>>::get()
765 .map(|c| SuppressedDrop::as_ref(&c).peek())
766 .unwrap_or_default()
767 }
768}
769
770impl<T> Convert<Weight, BalanceOf<T>> for Pallet<T>
771where
772 T: Config,
773{
774 fn convert(weight: Weight) -> BalanceOf<T> {
780 NextFeeMultiplier::<T>::get().saturating_mul_int(Self::weight_to_fee(weight))
781 }
782}
783
784#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
795#[scale_info(skip_type_params(T))]
796pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);
797
798impl<T: Config> ChargeTransactionPayment<T>
799where
800 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
801 BalanceOf<T>: Send + Sync,
802{
803 pub fn from(fee: BalanceOf<T>) -> Self {
805 Self(fee)
806 }
807
808 pub fn tip(&self) -> BalanceOf<T> {
810 self.0
811 }
812
813 fn withdraw_fee(
814 &self,
815 who: &T::AccountId,
816 call: &T::RuntimeCall,
817 info: &DispatchInfoOf<T::RuntimeCall>,
818 fee_with_tip: BalanceOf<T>,
819 ) -> Result<
820 (
821 BalanceOf<T>,
822 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
823 ),
824 TransactionValidityError,
825 > {
826 let tip = self.0;
827
828 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::withdraw_fee(
829 who,
830 call,
831 info,
832 fee_with_tip,
833 tip,
834 )
835 .map(|liquidity_info| (fee_with_tip, liquidity_info))
836 }
837
838 fn can_withdraw_fee(
839 &self,
840 who: &T::AccountId,
841 call: &T::RuntimeCall,
842 info: &DispatchInfoOf<T::RuntimeCall>,
843 len: usize,
844 ) -> Result<BalanceOf<T>, TransactionValidityError> {
845 let tip = self.0;
846 let fee_with_tip = Pallet::<T>::compute_fee(len as u32, info, tip);
847
848 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::can_withdraw_fee(
849 who,
850 call,
851 info,
852 fee_with_tip,
853 tip,
854 )?;
855 Ok(fee_with_tip)
856 }
857
858 pub fn get_priority(
872 info: &DispatchInfoOf<T::RuntimeCall>,
873 len: usize,
874 tip: BalanceOf<T>,
875 final_fee_with_tip: BalanceOf<T>,
876 ) -> TransactionPriority {
877 let max_block_weight = T::BlockWeights::get().max_block;
880 let max_block_length = *T::BlockLength::get().max.get(info.class) as u64;
881
882 let bounded_weight =
884 info.total_weight().max(Weight::from_parts(1, 1)).min(max_block_weight);
885 let bounded_length = (len as u64).clamp(1, max_block_length);
886
887 let max_tx_per_block_weight = max_block_weight
889 .checked_div_per_component(&bounded_weight)
890 .defensive_proof("bounded_weight is non-zero; qed")
891 .unwrap_or(1);
892 let max_tx_per_block_length = max_block_length / bounded_length;
893 let max_tx_per_block = max_tx_per_block_length
898 .min(max_tx_per_block_weight)
899 .saturated_into::<BalanceOf<T>>();
900 let max_reward = |val: BalanceOf<T>| val.saturating_mul(max_tx_per_block);
901
902 let tip = tip.saturating_add(One::one());
905 let scaled_tip = max_reward(tip);
906
907 match info.class {
908 DispatchClass::Normal => {
909 scaled_tip
911 },
912 DispatchClass::Mandatory => {
913 scaled_tip
916 },
917 DispatchClass::Operational => {
918 let fee_multiplier = T::OperationalFeeMultiplier::get().saturated_into();
924 let virtual_tip = final_fee_with_tip.saturating_mul(fee_multiplier);
925 let scaled_virtual_tip = max_reward(virtual_tip);
926
927 scaled_tip.saturating_add(scaled_virtual_tip)
928 },
929 }
930 .saturated_into::<TransactionPriority>()
931 }
932}
933
934impl<T: Config> core::fmt::Debug for ChargeTransactionPayment<T> {
935 #[cfg(feature = "std")]
936 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
937 write!(f, "ChargeTransactionPayment<{:?}>", self.0)
938 }
939 #[cfg(not(feature = "std"))]
940 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
941 Ok(())
942 }
943}
944
945#[derive(DebugNoBound)]
947pub enum Val<T: Config> {
948 Charge {
949 tip: BalanceOf<T>,
950 who: T::AccountId,
952 fee_with_tip: BalanceOf<T>,
954 },
955 NoCharge,
956}
957
958pub enum Pre<T: Config> {
961 Charge {
962 tip: BalanceOf<T>,
963 who: T::AccountId,
965 liquidity_info:
967 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
968 },
969 NoCharge {
970 refund: Weight,
972 },
973}
974
975impl<T: Config> core::fmt::Debug for Pre<T> {
976 #[cfg(feature = "std")]
977 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
978 match self {
979 Pre::Charge { tip, who, liquidity_info: _ } => {
980 write!(f, "Charge {{ tip: {:?}, who: {:?}, imbalance: <stripped> }}", tip, who)
981 },
982 Pre::NoCharge { refund } => write!(f, "NoCharge {{ refund: {:?} }}", refund),
983 }
984 }
985
986 #[cfg(not(feature = "std"))]
987 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
988 f.write_str("<wasm:stripped>")
989 }
990}
991
992impl<T: Config> TransactionExtension<T::RuntimeCall> for ChargeTransactionPayment<T>
993where
994 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
995{
996 const IDENTIFIER: &'static str = "ChargeTransactionPayment";
997 type Implicit = ();
998 type Val = Val<T>;
999 type Pre = Pre<T>;
1000
1001 fn weight(&self, _: &T::RuntimeCall) -> Weight {
1002 T::WeightInfo::charge_transaction_payment()
1003 }
1004
1005 fn validate(
1006 &self,
1007 origin: <T::RuntimeCall as Dispatchable>::RuntimeOrigin,
1008 call: &T::RuntimeCall,
1009 info: &DispatchInfoOf<T::RuntimeCall>,
1010 len: usize,
1011 _: (),
1012 _implication: &impl Encode,
1013 _source: TransactionSource,
1014 ) -> Result<
1015 (ValidTransaction, Self::Val, <T::RuntimeCall as Dispatchable>::RuntimeOrigin),
1016 TransactionValidityError,
1017 > {
1018 let Ok(who) = frame_system::ensure_signed(origin.clone()) else {
1019 return Ok((ValidTransaction::default(), Val::NoCharge, origin));
1020 };
1021 let fee_with_tip = self.can_withdraw_fee(&who, call, info, len)?;
1022 let tip = self.0;
1023 Ok((
1024 ValidTransaction {
1025 priority: Self::get_priority(info, len, tip, fee_with_tip),
1026 ..Default::default()
1027 },
1028 Val::Charge { tip: self.0, who, fee_with_tip },
1029 origin,
1030 ))
1031 }
1032
1033 fn prepare(
1034 self,
1035 val: Self::Val,
1036 _origin: &<T::RuntimeCall as Dispatchable>::RuntimeOrigin,
1037 call: &T::RuntimeCall,
1038 info: &DispatchInfoOf<T::RuntimeCall>,
1039 _len: usize,
1040 ) -> Result<Self::Pre, TransactionValidityError> {
1041 match val {
1042 Val::Charge { tip, who, fee_with_tip } => {
1043 let (_fee_with_tip, liquidity_info) =
1045 self.withdraw_fee(&who, call, info, fee_with_tip)?;
1046 Ok(Pre::Charge { tip, who, liquidity_info })
1047 },
1048 Val::NoCharge => Ok(Pre::NoCharge { refund: self.weight(call) }),
1049 }
1050 }
1051
1052 fn post_dispatch_details(
1053 pre: Self::Pre,
1054 info: &DispatchInfoOf<T::RuntimeCall>,
1055 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
1056 len: usize,
1057 _result: &DispatchResult,
1058 ) -> Result<Weight, TransactionValidityError> {
1059 let (tip, who, liquidity_info) = match pre {
1060 Pre::Charge { tip, who, liquidity_info } => (tip, who, liquidity_info),
1061 Pre::NoCharge { refund } => {
1062 return Ok(refund);
1064 },
1065 };
1066 let actual_fee_with_tip =
1067 Pallet::<T>::compute_actual_fee(len as u32, info, &post_info, tip);
1068 T::OnChargeTransaction::correct_and_deposit_fee(
1069 &who,
1070 info,
1071 &post_info,
1072 actual_fee_with_tip,
1073 tip,
1074 liquidity_info,
1075 )?;
1076 Pallet::<T>::deposit_event(Event::<T>::TransactionFeePaid {
1077 who,
1078 actual_fee: actual_fee_with_tip,
1079 tip,
1080 });
1081 Ok(Weight::zero())
1082 }
1083}
1084
1085impl<T: Config, AnyCall: GetDispatchInfo + Encode> EstimateCallFee<AnyCall, BalanceOf<T>>
1086 for Pallet<T>
1087where
1088 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
1089{
1090 fn estimate_call_fee(call: &AnyCall, post_info: PostDispatchInfo) -> BalanceOf<T> {
1091 let len = call.encoded_size() as u32;
1092 let info = call.get_dispatch_info();
1093 Self::compute_actual_fee(len, &info, &post_info, Zero::zero())
1094 }
1095}