1#![cfg_attr(not(feature = "std"), no_std)]
49
50use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
51use scale_info::TypeInfo;
52
53pub use payment::*;
54use pezframe_support::{
55 dispatch::{
56 DispatchClass, DispatchInfo, DispatchResult, GetDispatchInfo, Pays, PostDispatchInfo,
57 },
58 pezpallet_prelude::TransactionSource,
59 traits::{Defensive, EstimateCallFee, Get, Imbalance, SuppressedDrop},
60 weights::{Weight, WeightToFee},
61 RuntimeDebugNoBound,
62};
63pub use pezpallet::*;
64use pezsp_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 pezsp_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: pezframe_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: pezframe_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 = pezframe_system::Pezpallet::<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#[pezframe_support::pezpallet]
321pub mod pezpallet {
322 use pezframe_support::pezpallet_prelude::*;
323 use pezframe_system::pezpallet_prelude::*;
324
325 use super::*;
326
327 #[pezpallet::pezpallet]
328 pub struct Pezpallet<T>(_);
329
330 pub mod config_preludes {
331 use super::*;
332 use pezframe_support::derive_impl;
333
334 pub struct TestDefaultConfig;
336
337 #[derive_impl(pezframe_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
338 impl pezframe_system::DefaultConfig for TestDefaultConfig {}
339
340 #[pezframe_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 #[pezpallet::config(with_default)]
351 pub trait Config: pezframe_system::Config {
352 #[pezpallet::no_default_bounds]
354 #[allow(deprecated)]
355 type RuntimeEvent: From<Event<Self>>
356 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
357
358 #[pezpallet::no_default]
365 type OnChargeTransaction: OnChargeTransaction<Self>;
366
367 #[pezpallet::no_default]
369 type WeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
370
371 #[pezpallet::no_default]
373 type LengthToFee: WeightToFee<Balance = BalanceOf<Self>>;
374
375 type FeeMultiplierUpdate: MultiplierUpdate;
377
378 #[pezpallet::constant]
400 type OperationalFeeMultiplier: Get<u8>;
401
402 type WeightInfo: WeightInfo;
404 }
405
406 #[pezpallet::type_value]
407 pub fn NextFeeMultiplierOnEmpty() -> Multiplier {
408 MULTIPLIER_DEFAULT_VALUE
409 }
410
411 #[pezpallet::storage]
412 #[pezpallet::whitelist_storage]
413 pub type NextFeeMultiplier<T: Config> =
414 StorageValue<_, Multiplier, ValueQuery, NextFeeMultiplierOnEmpty>;
415
416 #[pezpallet::storage]
417 pub type StorageVersion<T: Config> = StorageValue<_, Releases, ValueQuery>;
418
419 #[pezpallet::storage]
423 #[pezpallet::whitelist_storage]
424 pub(crate) type TxPaymentCredit<T: Config> = StorageValue<_, StoredCreditOf<T>>;
425
426 #[pezpallet::genesis_config]
427 pub struct GenesisConfig<T: Config> {
428 pub multiplier: Multiplier,
429 #[serde(skip)]
430 pub _config: core::marker::PhantomData<T>,
431 }
432
433 impl<T: Config> Default for GenesisConfig<T> {
434 fn default() -> Self {
435 Self { multiplier: MULTIPLIER_DEFAULT_VALUE, _config: Default::default() }
436 }
437 }
438
439 #[pezpallet::genesis_build]
440 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
441 fn build(&self) {
442 StorageVersion::<T>::put(Releases::V2);
443 NextFeeMultiplier::<T>::put(self.multiplier);
444 }
445 }
446
447 #[pezpallet::event]
448 #[pezpallet::generate_deposit(pub(super) fn deposit_event)]
449 pub enum Event<T: Config> {
450 TransactionFeePaid { who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T> },
453 }
454
455 #[pezpallet::hooks]
456 impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
457 fn on_finalize(_: pezframe_system::pezpallet_prelude::BlockNumberFor<T>) {
458 NextFeeMultiplier::<T>::mutate(|fm| {
459 *fm = T::FeeMultiplierUpdate::convert(*fm);
460 });
461
462 TxPaymentCredit::<T>::take().map(|credit| {
466 log::error!(target: LOG_TARGET, "The `TxPaymentCredit` was stored between blocks. This is a bug.");
467 credit.into_inner()
469 });
470 }
471
472 #[cfg(feature = "std")]
473 fn integrity_test() {
474 assert!(
478 <Multiplier as pezsp_runtime::traits::Bounded>::max_value()
479 >= Multiplier::checked_from_integer::<u128>(
480 T::BlockWeights::get().max_block.ref_time().try_into().unwrap()
481 )
482 .unwrap(),
483 );
484
485 let target = T::FeeMultiplierUpdate::target()
486 * T::BlockWeights::get().get(DispatchClass::Normal).max_total.expect(
487 "Setting `max_total` for `Normal` dispatch class is not compatible with \
488 `transaction-payment` pezpallet.",
489 );
490 let addition = target / 100;
492 if addition == Weight::zero() {
493 return;
496 }
497
498 let min_value = T::FeeMultiplierUpdate::min();
503 let target = target + addition;
504
505 pezframe_system::Pezpallet::<T>::set_block_consumed_resources(target, 0);
506 let next = T::FeeMultiplierUpdate::convert(min_value);
507 assert!(
508 next > min_value,
509 "The minimum bound of the multiplier is too low. When \
510 block saturation is more than target by 1% and multiplier is minimal then \
511 the multiplier doesn't increase."
512 );
513 }
514 }
515}
516
517impl<T: Config> Pezpallet<T> {
518 pub fn next_fee_multiplier() -> Multiplier {
520 NextFeeMultiplier::<T>::get()
521 }
522
523 pub fn query_info<Extrinsic: pezsp_runtime::traits::ExtrinsicLike + GetDispatchInfo>(
532 unchecked_extrinsic: Extrinsic,
533 len: u32,
534 ) -> RuntimeDispatchInfo<BalanceOf<T>>
535 where
536 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
537 {
538 let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
544
545 let partial_fee = if unchecked_extrinsic.is_bare() {
546 0u32.into()
548 } else {
549 Self::compute_fee(len, &dispatch_info, 0u32.into())
550 };
551
552 let DispatchInfo { class, .. } = dispatch_info;
553
554 RuntimeDispatchInfo { weight: dispatch_info.total_weight(), class, partial_fee }
555 }
556
557 pub fn query_fee_details<Extrinsic: pezsp_runtime::traits::ExtrinsicLike + GetDispatchInfo>(
559 unchecked_extrinsic: Extrinsic,
560 len: u32,
561 ) -> FeeDetails<BalanceOf<T>>
562 where
563 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
564 {
565 let dispatch_info = <Extrinsic as GetDispatchInfo>::get_dispatch_info(&unchecked_extrinsic);
566
567 let tip = 0u32.into();
568
569 if unchecked_extrinsic.is_bare() {
570 FeeDetails { inclusion_fee: None, tip }
572 } else {
573 Self::compute_fee_details(len, &dispatch_info, tip)
574 }
575 }
576
577 pub fn query_call_info(call: T::RuntimeCall, len: u32) -> RuntimeDispatchInfo<BalanceOf<T>>
579 where
580 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
581 {
582 let dispatch_info = <T::RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
583 let DispatchInfo { class, .. } = dispatch_info;
584
585 RuntimeDispatchInfo {
586 weight: dispatch_info.total_weight(),
587 class,
588 partial_fee: Self::compute_fee(len, &dispatch_info, 0u32.into()),
589 }
590 }
591
592 pub fn query_call_fee_details(call: T::RuntimeCall, len: u32) -> FeeDetails<BalanceOf<T>>
594 where
595 T::RuntimeCall: Dispatchable<Info = DispatchInfo> + GetDispatchInfo,
596 {
597 let dispatch_info = <T::RuntimeCall as GetDispatchInfo>::get_dispatch_info(&call);
598 let tip = 0u32.into();
599
600 Self::compute_fee_details(len, &dispatch_info, tip)
601 }
602
603 pub fn compute_fee(
605 len: u32,
606 info: &DispatchInfoOf<T::RuntimeCall>,
607 tip: BalanceOf<T>,
608 ) -> BalanceOf<T>
609 where
610 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
611 {
612 Self::compute_fee_details(len, info, tip).final_fee()
613 }
614
615 pub fn compute_fee_details(
617 len: u32,
618 info: &DispatchInfoOf<T::RuntimeCall>,
619 tip: BalanceOf<T>,
620 ) -> FeeDetails<BalanceOf<T>>
621 where
622 T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
623 {
624 Self::compute_fee_raw(len, info.total_weight(), tip, info.pays_fee, info.class)
625 }
626
627 pub fn compute_actual_fee(
632 len: u32,
633 info: &DispatchInfoOf<T::RuntimeCall>,
634 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
635 tip: BalanceOf<T>,
636 ) -> BalanceOf<T>
637 where
638 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
639 {
640 Self::compute_actual_fee_details(len, info, post_info, tip).final_fee()
641 }
642
643 pub fn compute_actual_fee_details(
645 len: u32,
646 info: &DispatchInfoOf<T::RuntimeCall>,
647 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
648 tip: BalanceOf<T>,
649 ) -> FeeDetails<BalanceOf<T>>
650 where
651 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
652 {
653 Self::compute_fee_raw(
654 len,
655 post_info.calc_actual_weight(info),
656 tip,
657 post_info.pays_fee(info),
658 info.class,
659 )
660 }
661
662 fn compute_fee_raw(
663 len: u32,
664 weight: Weight,
665 tip: BalanceOf<T>,
666 pays_fee: Pays,
667 class: DispatchClass,
668 ) -> FeeDetails<BalanceOf<T>> {
669 if pays_fee == Pays::Yes {
670 let unadjusted_weight_fee = Self::weight_to_fee(weight);
672 let multiplier = NextFeeMultiplier::<T>::get();
673 let adjusted_weight_fee = multiplier.saturating_mul_int(unadjusted_weight_fee);
675
676 let len_fee = Self::length_to_fee(len);
678
679 let base_fee = Self::weight_to_fee(T::BlockWeights::get().get(class).base_extrinsic);
680 FeeDetails {
681 inclusion_fee: Some(InclusionFee { base_fee, len_fee, adjusted_weight_fee }),
682 tip,
683 }
684 } else {
685 FeeDetails { inclusion_fee: None, tip }
686 }
687 }
688
689 pub fn length_to_fee(length: u32) -> BalanceOf<T> {
691 T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0))
692 }
693
694 pub fn weight_to_fee(weight: Weight) -> BalanceOf<T> {
697 let capped_weight = weight.min(T::BlockWeights::get().max_block);
700 T::WeightToFee::weight_to_fee(&capped_weight)
701 }
702
703 pub fn deposit_fee_paid_event(who: T::AccountId, actual_fee: BalanceOf<T>, tip: BalanceOf<T>) {
705 Self::deposit_event(Event::TransactionFeePaid { who, actual_fee, tip });
706 }
707
708 pub fn withdraw_txfee<Balance>(amount: Balance) -> Option<CreditOf<T>>
726 where
727 CreditOf<T>: Imbalance<Balance>,
728 Balance: PartialOrd,
729 {
730 <TxPaymentCredit<T>>::mutate(|credit| {
731 let credit = SuppressedDrop::as_mut(credit.as_mut()?);
732 if amount > credit.peek() {
733 return None;
734 }
735 Some(credit.extract(amount))
736 })
737 }
738
739 pub fn deposit_txfee<Balance>(deposit: CreditOf<T>)
741 where
742 CreditOf<T>: Imbalance<Balance>,
743 {
744 <TxPaymentCredit<T>>::mutate(|credit| {
745 if let Some(credit) = credit.as_mut().map(SuppressedDrop::as_mut) {
746 credit.subsume(deposit);
747 } else {
748 *credit = Some(SuppressedDrop::new(deposit))
749 }
750 });
751 }
752
753 pub fn remaining_txfee<Balance>() -> Balance
759 where
760 CreditOf<T>: Imbalance<Balance>,
761 Balance: Default,
762 {
763 <TxPaymentCredit<T>>::get()
764 .map(|c| SuppressedDrop::as_ref(&c).peek())
765 .unwrap_or_default()
766 }
767}
768
769impl<T> Convert<Weight, BalanceOf<T>> for Pezpallet<T>
770where
771 T: Config,
772{
773 fn convert(weight: Weight) -> BalanceOf<T> {
779 NextFeeMultiplier::<T>::get().saturating_mul_int(Self::weight_to_fee(weight))
780 }
781}
782
783#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
794#[scale_info(skip_type_params(T))]
795pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);
796
797impl<T: Config> ChargeTransactionPayment<T>
798where
799 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
800 BalanceOf<T>: Send + Sync,
801{
802 pub fn from(fee: BalanceOf<T>) -> Self {
804 Self(fee)
805 }
806
807 pub fn tip(&self) -> BalanceOf<T> {
809 self.0
810 }
811
812 fn withdraw_fee(
813 &self,
814 who: &T::AccountId,
815 call: &T::RuntimeCall,
816 info: &DispatchInfoOf<T::RuntimeCall>,
817 fee_with_tip: BalanceOf<T>,
818 ) -> Result<
819 (
820 BalanceOf<T>,
821 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
822 ),
823 TransactionValidityError,
824 > {
825 let tip = self.0;
826
827 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::withdraw_fee(
828 who,
829 call,
830 info,
831 fee_with_tip,
832 tip,
833 )
834 .map(|liquidity_info| (fee_with_tip, liquidity_info))
835 }
836
837 fn can_withdraw_fee(
838 &self,
839 who: &T::AccountId,
840 call: &T::RuntimeCall,
841 info: &DispatchInfoOf<T::RuntimeCall>,
842 len: usize,
843 ) -> Result<BalanceOf<T>, TransactionValidityError> {
844 let tip = self.0;
845 let fee_with_tip = Pezpallet::<T>::compute_fee(len as u32, info, tip);
846
847 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::can_withdraw_fee(
848 who,
849 call,
850 info,
851 fee_with_tip,
852 tip,
853 )?;
854 Ok(fee_with_tip)
855 }
856
857 pub fn get_priority(
871 info: &DispatchInfoOf<T::RuntimeCall>,
872 len: usize,
873 tip: BalanceOf<T>,
874 final_fee_with_tip: BalanceOf<T>,
875 ) -> TransactionPriority {
876 let max_block_weight = T::BlockWeights::get().max_block;
879 let max_block_length = *T::BlockLength::get().max.get(info.class) as u64;
880
881 let bounded_weight =
883 info.total_weight().max(Weight::from_parts(1, 1)).min(max_block_weight);
884 let bounded_length = (len as u64).clamp(1, max_block_length);
885
886 let max_tx_per_block_weight = max_block_weight
888 .checked_div_per_component(&bounded_weight)
889 .defensive_proof("bounded_weight is non-zero; qed")
890 .unwrap_or(1);
891 let max_tx_per_block_length = max_block_length / bounded_length;
892 let max_tx_per_block = max_tx_per_block_length
897 .min(max_tx_per_block_weight)
898 .saturated_into::<BalanceOf<T>>();
899 let max_reward = |val: BalanceOf<T>| val.saturating_mul(max_tx_per_block);
900
901 let tip = tip.saturating_add(One::one());
904 let scaled_tip = max_reward(tip);
905
906 match info.class {
907 DispatchClass::Normal => {
908 scaled_tip
910 },
911 DispatchClass::Mandatory => {
912 scaled_tip
915 },
916 DispatchClass::Operational => {
917 let fee_multiplier = T::OperationalFeeMultiplier::get().saturated_into();
923 let virtual_tip = final_fee_with_tip.saturating_mul(fee_multiplier);
924 let scaled_virtual_tip = max_reward(virtual_tip);
925
926 scaled_tip.saturating_add(scaled_virtual_tip)
927 },
928 }
929 .saturated_into::<TransactionPriority>()
930 }
931}
932
933impl<T: Config> core::fmt::Debug for ChargeTransactionPayment<T> {
934 #[cfg(feature = "std")]
935 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
936 write!(f, "ChargeTransactionPayment<{:?}>", self.0)
937 }
938 #[cfg(not(feature = "std"))]
939 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
940 Ok(())
941 }
942}
943
944#[derive(RuntimeDebugNoBound)]
946pub enum Val<T: Config> {
947 Charge {
948 tip: BalanceOf<T>,
949 who: T::AccountId,
951 fee_with_tip: BalanceOf<T>,
953 },
954 NoCharge,
955}
956
957pub enum Pre<T: Config> {
960 Charge {
961 tip: BalanceOf<T>,
962 who: T::AccountId,
964 liquidity_info:
966 <<T as Config>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
967 },
968 NoCharge {
969 refund: Weight,
971 },
972}
973
974impl<T: Config> core::fmt::Debug for Pre<T> {
975 #[cfg(feature = "std")]
976 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
977 match self {
978 Pre::Charge { tip, who, liquidity_info: _ } => {
979 write!(f, "Charge {{ tip: {:?}, who: {:?}, imbalance: <stripped> }}", tip, who)
980 },
981 Pre::NoCharge { refund } => write!(f, "NoCharge {{ refund: {:?} }}", refund),
982 }
983 }
984
985 #[cfg(not(feature = "std"))]
986 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
987 f.write_str("<wasm:stripped>")
988 }
989}
990
991impl<T: Config> TransactionExtension<T::RuntimeCall> for ChargeTransactionPayment<T>
992where
993 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
994{
995 const IDENTIFIER: &'static str = "ChargeTransactionPayment";
996 type Implicit = ();
997 type Val = Val<T>;
998 type Pre = Pre<T>;
999
1000 fn weight(&self, _: &T::RuntimeCall) -> Weight {
1001 T::WeightInfo::charge_transaction_payment()
1002 }
1003
1004 fn validate(
1005 &self,
1006 origin: <T::RuntimeCall as Dispatchable>::RuntimeOrigin,
1007 call: &T::RuntimeCall,
1008 info: &DispatchInfoOf<T::RuntimeCall>,
1009 len: usize,
1010 _: (),
1011 _implication: &impl Encode,
1012 _source: TransactionSource,
1013 ) -> Result<
1014 (ValidTransaction, Self::Val, <T::RuntimeCall as Dispatchable>::RuntimeOrigin),
1015 TransactionValidityError,
1016 > {
1017 let Ok(who) = pezframe_system::ensure_signed(origin.clone()) else {
1018 return Ok((ValidTransaction::default(), Val::NoCharge, origin));
1019 };
1020 let fee_with_tip = self.can_withdraw_fee(&who, call, info, len)?;
1021 let tip = self.0;
1022 Ok((
1023 ValidTransaction {
1024 priority: Self::get_priority(info, len, tip, fee_with_tip),
1025 ..Default::default()
1026 },
1027 Val::Charge { tip: self.0, who, fee_with_tip },
1028 origin,
1029 ))
1030 }
1031
1032 fn prepare(
1033 self,
1034 val: Self::Val,
1035 _origin: &<T::RuntimeCall as Dispatchable>::RuntimeOrigin,
1036 call: &T::RuntimeCall,
1037 info: &DispatchInfoOf<T::RuntimeCall>,
1038 _len: usize,
1039 ) -> Result<Self::Pre, TransactionValidityError> {
1040 match val {
1041 Val::Charge { tip, who, fee_with_tip } => {
1042 let (_fee_with_tip, liquidity_info) =
1044 self.withdraw_fee(&who, call, info, fee_with_tip)?;
1045 Ok(Pre::Charge { tip, who, liquidity_info })
1046 },
1047 Val::NoCharge => Ok(Pre::NoCharge { refund: self.weight(call) }),
1048 }
1049 }
1050
1051 fn post_dispatch_details(
1052 pre: Self::Pre,
1053 info: &DispatchInfoOf<T::RuntimeCall>,
1054 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
1055 len: usize,
1056 _result: &DispatchResult,
1057 ) -> Result<Weight, TransactionValidityError> {
1058 let (tip, who, liquidity_info) = match pre {
1059 Pre::Charge { tip, who, liquidity_info } => (tip, who, liquidity_info),
1060 Pre::NoCharge { refund } => {
1061 return Ok(refund);
1063 },
1064 };
1065 let actual_fee_with_tip =
1066 Pezpallet::<T>::compute_actual_fee(len as u32, info, &post_info, tip);
1067 T::OnChargeTransaction::correct_and_deposit_fee(
1068 &who,
1069 info,
1070 &post_info,
1071 actual_fee_with_tip,
1072 tip,
1073 liquidity_info,
1074 )?;
1075 Pezpallet::<T>::deposit_event(Event::<T>::TransactionFeePaid {
1076 who,
1077 actual_fee: actual_fee_with_tip,
1078 tip,
1079 });
1080 Ok(Weight::zero())
1081 }
1082}
1083
1084impl<T: Config, AnyCall: GetDispatchInfo + Encode> EstimateCallFee<AnyCall, BalanceOf<T>>
1085 for Pezpallet<T>
1086where
1087 T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
1088{
1089 fn estimate_call_fee(call: &AnyCall, post_info: PostDispatchInfo) -> BalanceOf<T> {
1090 let len = call.encoded_size() as u32;
1091 let info = call.get_dispatch_info();
1092 Self::compute_actual_fee(len, &info, &post_info, Zero::zero())
1093 }
1094}