#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::{
dispatch::GetDispatchInfo,
pallet_prelude::*,
traits::{IsSubType, OriginTrait},
BoundedVec,
};
use frame_system::pallet_prelude::*;
pub use pallet::*;
use sp_runtime::traits::Dispatchable;
use sp_std::boxed::Box;
pub use weights::WeightInfo;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
mod weights;
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type RemarkDispatchHandler: RemarkDispatchHandler<RemarkArgs<Self>>;
type RuntimeCall: Parameter
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
+ GetDispatchInfo
+ From<frame_system::Call<Self>>
+ IsSubType<Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;
type WeightInfo: WeightInfo;
type Remark: Parameter + Member + Default;
type MaxRemarksPerCall: Get<u32>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
Remark {
remarks: BoundedVec<T::Remark, T::MaxRemarksPerCall>,
call: <T as Config>::RuntimeCall,
},
}
#[pallet::error]
pub enum Error<T> {
NoRemarks,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight({
let dispatch_info = call.get_dispatch_info();
(T::WeightInfo::remark(T::MaxRemarksPerCall::get())
.saturating_add(dispatch_info.weight),
dispatch_info.class)
})]
pub fn remark(
origin: OriginFor<T>,
remarks: BoundedVec<T::Remark, T::MaxRemarksPerCall>,
call: Box<<T as Config>::RuntimeCall>,
) -> DispatchResult {
ensure!(!remarks.is_empty(), Error::<T>::NoRemarks);
T::RemarkDispatchHandler::pre_dispatch_check((
origin.clone(),
remarks.clone(),
call.clone(),
))?;
let mut filtered_origin = origin.clone();
filtered_origin.add_filter(move |c: &<T as frame_system::Config>::RuntimeCall| {
let c = <T as Config>::RuntimeCall::from_ref(c);
!matches!(c.is_sub_type(), Some(Call::remark { .. }))
});
call.clone()
.dispatch(filtered_origin)
.map(|_| ())
.map_err(|e| e.error)?;
T::RemarkDispatchHandler::post_dispatch_check((origin, remarks.clone(), call.clone()))?;
Self::deposit_event(Event::Remark {
remarks,
call: *call,
});
Ok(())
}
}
}
pub type RemarkArgs<T> = (
OriginFor<T>,
BoundedVec<<T as Config>::Remark, <T as Config>::MaxRemarksPerCall>,
Box<<T as Config>::RuntimeCall>,
);
pub trait RemarkDispatchHandler<T> {
fn pre_dispatch_check(t: T) -> DispatchResult;
fn post_dispatch_check(t: T) -> DispatchResult;
}
pub struct NoopRemarkDispatchHandler<T>(PhantomData<T>);
impl<T> RemarkDispatchHandler<RemarkArgs<T>> for NoopRemarkDispatchHandler<T>
where
T: Config,
{
fn pre_dispatch_check(_t: RemarkArgs<T>) -> DispatchResult {
Ok(())
}
fn post_dispatch_check(_t: RemarkArgs<T>) -> DispatchResult {
Ok(())
}
}