pezpallet_whitelist/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
33
34#[cfg(feature = "runtime-benchmarks")]
35mod benchmarking;
36#[cfg(test)]
37mod mock;
38#[cfg(test)]
39mod tests;
40pub mod weights;
41pub use weights::WeightInfo;
42
43extern crate alloc;
44
45use alloc::boxed::Box;
46use codec::{DecodeLimit, Encode, FullCodec};
47use frame::{
48 prelude::*,
49 traits::{QueryPreimage, StorePreimage},
50};
51use scale_info::TypeInfo;
52
53pub use pezpallet::*;
54
55#[frame::pezpallet]
56pub mod pezpallet {
57 use super::*;
58
59 #[pezpallet::config]
60 pub trait Config: pezframe_system::Config {
61 #[allow(deprecated)]
63 type RuntimeEvent: From<Event<Self>>
64 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
65
66 type RuntimeCall: IsType<<Self as pezframe_system::Config>::RuntimeCall>
68 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
69 + GetDispatchInfo
70 + FullCodec
71 + TypeInfo
72 + From<pezframe_system::Call<Self>>
73 + Parameter;
74
75 type WhitelistOrigin: EnsureOrigin<Self::RuntimeOrigin>;
77
78 type DispatchWhitelistedOrigin: EnsureOrigin<Self::RuntimeOrigin>;
80
81 type Preimages: QueryPreimage<H = Self::Hashing> + StorePreimage;
83
84 type WeightInfo: WeightInfo;
86 }
87
88 #[pezpallet::pezpallet]
89 pub struct Pezpallet<T>(_);
90
91 #[pezpallet::event]
92 #[pezpallet::generate_deposit(pub(super) fn deposit_event)]
93 pub enum Event<T: Config> {
94 CallWhitelisted { call_hash: T::Hash },
95 WhitelistedCallRemoved { call_hash: T::Hash },
96 WhitelistedCallDispatched { call_hash: T::Hash, result: DispatchResultWithPostInfo },
97 }
98
99 #[pezpallet::error]
100 pub enum Error<T> {
101 UnavailablePreImage,
103 UndecodableCall,
105 InvalidCallWeightWitness,
107 CallIsNotWhitelisted,
109 CallAlreadyWhitelisted,
111 }
112
113 #[pezpallet::storage]
114 pub type WhitelistedCall<T: Config> = StorageMap<_, Twox64Concat, T::Hash, (), OptionQuery>;
115
116 #[pezpallet::call]
117 impl<T: Config> Pezpallet<T> {
118 #[pezpallet::call_index(0)]
119 #[pezpallet::weight(T::WeightInfo::whitelist_call())]
120 pub fn whitelist_call(origin: OriginFor<T>, call_hash: T::Hash) -> DispatchResult {
121 T::WhitelistOrigin::ensure_origin(origin)?;
122
123 ensure!(
124 !WhitelistedCall::<T>::contains_key(call_hash),
125 Error::<T>::CallAlreadyWhitelisted,
126 );
127
128 WhitelistedCall::<T>::insert(call_hash, ());
129 T::Preimages::request(&call_hash);
130
131 Self::deposit_event(Event::<T>::CallWhitelisted { call_hash });
132
133 Ok(())
134 }
135
136 #[pezpallet::call_index(1)]
137 #[pezpallet::weight(T::WeightInfo::remove_whitelisted_call())]
138 pub fn remove_whitelisted_call(origin: OriginFor<T>, call_hash: T::Hash) -> DispatchResult {
139 T::WhitelistOrigin::ensure_origin(origin)?;
140
141 WhitelistedCall::<T>::take(call_hash).ok_or(Error::<T>::CallIsNotWhitelisted)?;
142
143 T::Preimages::unrequest(&call_hash);
144
145 Self::deposit_event(Event::<T>::WhitelistedCallRemoved { call_hash });
146
147 Ok(())
148 }
149
150 #[pezpallet::call_index(2)]
151 #[pezpallet::weight(
152 T::WeightInfo::dispatch_whitelisted_call(*call_encoded_len)
153 .saturating_add(*call_weight_witness)
154 )]
155 pub fn dispatch_whitelisted_call(
156 origin: OriginFor<T>,
157 call_hash: T::Hash,
158 call_encoded_len: u32,
159 call_weight_witness: Weight,
160 ) -> DispatchResultWithPostInfo {
161 T::DispatchWhitelistedOrigin::ensure_origin(origin)?;
162
163 ensure!(
164 WhitelistedCall::<T>::contains_key(call_hash),
165 Error::<T>::CallIsNotWhitelisted,
166 );
167
168 let call = T::Preimages::fetch(&call_hash, Some(call_encoded_len))
169 .map_err(|_| Error::<T>::UnavailablePreImage)?;
170
171 let call = <T as Config>::RuntimeCall::decode_all_with_depth_limit(
172 frame::deps::pezframe_support::MAX_EXTRINSIC_DEPTH,
173 &mut &call[..],
174 )
175 .map_err(|_| Error::<T>::UndecodableCall)?;
176
177 ensure!(
178 call.get_dispatch_info().call_weight.all_lte(call_weight_witness),
179 Error::<T>::InvalidCallWeightWitness
180 );
181
182 let actual_weight = Self::clean_and_dispatch(call_hash, call).map(|w| {
183 w.saturating_add(T::WeightInfo::dispatch_whitelisted_call(call_encoded_len))
184 });
185
186 Ok(actual_weight.into())
187 }
188
189 #[pezpallet::call_index(3)]
190 #[pezpallet::weight({
191 let call_weight = call.get_dispatch_info().call_weight;
192 let call_len = call.encoded_size() as u32;
193
194 T::WeightInfo::dispatch_whitelisted_call_with_preimage(call_len)
195 .saturating_add(call_weight)
196 })]
197 pub fn dispatch_whitelisted_call_with_preimage(
198 origin: OriginFor<T>,
199 call: Box<<T as Config>::RuntimeCall>,
200 ) -> DispatchResultWithPostInfo {
201 T::DispatchWhitelistedOrigin::ensure_origin(origin)?;
202
203 let call_hash = T::Hashing::hash_of(&call).into();
204
205 ensure!(
206 WhitelistedCall::<T>::contains_key(call_hash),
207 Error::<T>::CallIsNotWhitelisted,
208 );
209
210 let call_len = call.encoded_size() as u32;
211 let actual_weight = Self::clean_and_dispatch(call_hash, *call).map(|w| {
212 w.saturating_add(T::WeightInfo::dispatch_whitelisted_call_with_preimage(call_len))
213 });
214
215 Ok(actual_weight.into())
216 }
217 }
218}
219
220impl<T: Config> Pezpallet<T> {
221 fn clean_and_dispatch(call_hash: T::Hash, call: <T as Config>::RuntimeCall) -> Option<Weight> {
225 WhitelistedCall::<T>::remove(call_hash);
226
227 T::Preimages::unrequest(&call_hash);
228
229 let result = call.dispatch(pezframe_system::Origin::<T>::Root.into());
230
231 let call_actual_weight = match result {
232 Ok(call_post_info) => call_post_info.actual_weight,
233 Err(call_err) => call_err.post_info.actual_weight,
234 };
235
236 Self::deposit_event(Event::<T>::WhitelistedCallDispatched { call_hash, result });
237
238 call_actual_weight
239 }
240}