pallet_skip_feeless_payment/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
38
39extern crate alloc;
40
41use codec::{Decode, DecodeWithMemTracking, Encode};
42use frame_support::{
43 dispatch::{CheckIfFeeless, DispatchResult},
44 pallet_prelude::TransactionSource,
45 traits::{IsType, OriginTrait},
46 weights::Weight,
47};
48use scale_info::{StaticTypeInfo, TypeInfo};
49use sp_runtime::{
50 traits::{
51 DispatchInfoOf, DispatchOriginOf, Implication, PostDispatchInfoOf, TransactionExtension,
52 ValidateResult,
53 },
54 transaction_validity::TransactionValidityError,
55};
56
57#[cfg(test)]
58mod mock;
59#[cfg(test)]
60mod tests;
61
62pub use pallet::*;
63
64#[frame_support::pallet]
65pub mod pallet {
66 use super::*;
67
68 #[pallet::config]
69 pub trait Config: frame_system::Config {
70 #[allow(deprecated)]
72 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
73 }
74
75 #[pallet::pallet]
76 pub struct Pallet<T>(_);
77
78 #[pallet::event]
79 #[pallet::generate_deposit(pub(super) fn deposit_event)]
80 pub enum Event<T: Config> {
81 FeeSkipped { origin: <T::RuntimeOrigin as OriginTrait>::PalletsOrigin },
83 }
84}
85
86#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq)]
88pub struct SkipCheckIfFeeless<T, S>(pub S, core::marker::PhantomData<T>);
89
90impl<T, S: StaticTypeInfo> TypeInfo for SkipCheckIfFeeless<T, S> {
92 type Identity = S;
93 fn type_info() -> scale_info::Type {
94 S::type_info()
95 }
96}
97
98impl<T, S: Encode> core::fmt::Debug for SkipCheckIfFeeless<T, S> {
99 #[cfg(feature = "std")]
100 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
101 write!(f, "SkipCheckIfFeeless<{:?}>", self.0.encode())
102 }
103 #[cfg(not(feature = "std"))]
104 fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
105 Ok(())
106 }
107}
108
109impl<T, S> From<S> for SkipCheckIfFeeless<T, S> {
110 fn from(s: S) -> Self {
111 Self(s, core::marker::PhantomData)
112 }
113}
114
115pub enum Intermediate<T, O> {
116 Apply(T),
118 Skip(O),
120}
121use Intermediate::*;
122
123impl<T: Config + Send + Sync, S: TransactionExtension<T::RuntimeCall>>
124 TransactionExtension<T::RuntimeCall> for SkipCheckIfFeeless<T, S>
125where
126 T::RuntimeCall: CheckIfFeeless<Origin = frame_system::pallet_prelude::OriginFor<T>>,
127{
128 const IDENTIFIER: &'static str = S::IDENTIFIER;
133 type Implicit = S::Implicit;
134
135 fn metadata() -> alloc::vec::Vec<sp_runtime::traits::TransactionExtensionMetadata> {
136 S::metadata()
137 }
138
139 fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
140 self.0.implicit()
141 }
142 type Val =
143 Intermediate<S::Val, <DispatchOriginOf<T::RuntimeCall> as OriginTrait>::PalletsOrigin>;
144 type Pre =
145 Intermediate<S::Pre, <DispatchOriginOf<T::RuntimeCall> as OriginTrait>::PalletsOrigin>;
146
147 fn weight(&self, call: &T::RuntimeCall) -> frame_support::weights::Weight {
148 self.0.weight(call)
149 }
150
151 fn validate(
152 &self,
153 origin: DispatchOriginOf<T::RuntimeCall>,
154 call: &T::RuntimeCall,
155 info: &DispatchInfoOf<T::RuntimeCall>,
156 len: usize,
157 self_implicit: S::Implicit,
158 inherited_implication: &impl Implication,
159 source: TransactionSource,
160 ) -> ValidateResult<Self::Val, T::RuntimeCall> {
161 if call.is_feeless(&origin) {
162 Ok((Default::default(), Skip(origin.caller().clone()), origin))
163 } else {
164 let (x, y, z) = self.0.validate(
165 origin,
166 call,
167 info,
168 len,
169 self_implicit,
170 inherited_implication,
171 source,
172 )?;
173 Ok((x, Apply(y), z))
174 }
175 }
176
177 fn prepare(
178 self,
179 val: Self::Val,
180 origin: &DispatchOriginOf<T::RuntimeCall>,
181 call: &T::RuntimeCall,
182 info: &DispatchInfoOf<T::RuntimeCall>,
183 len: usize,
184 ) -> Result<Self::Pre, TransactionValidityError> {
185 match val {
186 Apply(val) => self.0.prepare(val, origin, call, info, len).map(Apply),
187 Skip(origin) => Ok(Skip(origin)),
188 }
189 }
190
191 fn post_dispatch_details(
192 pre: Self::Pre,
193 info: &DispatchInfoOf<T::RuntimeCall>,
194 post_info: &PostDispatchInfoOf<T::RuntimeCall>,
195 len: usize,
196 result: &DispatchResult,
197 ) -> Result<Weight, TransactionValidityError> {
198 match pre {
199 Apply(pre) => S::post_dispatch_details(pre, info, post_info, len, result),
200 Skip(origin) => {
201 Pallet::<T>::deposit_event(Event::<T>::FeeSkipped { origin });
202 Ok(Weight::zero())
203 },
204 }
205 }
206}