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