1#![cfg_attr(not(feature = "std"), no_std)]
61
62extern crate alloc;
63
64use alloc::boxed::Box;
65use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
66use core::{fmt::Debug, marker::PhantomData};
67use pezsp_arithmetic::traits::{Saturating, Zero};
68use pezsp_runtime::RuntimeDebug;
69use scale_info::TypeInfo;
70
71use pezframe_support::{
72 defensive,
73 dispatch::DispatchResultWithPostInfo,
74 ensure, impl_ensure_origin_with_arg_ignoring_arg,
75 traits::{
76 tokens::Balance as BalanceTrait, EnsureOrigin, EnsureOriginWithArg, Get, RankedMembers,
77 RankedMembersSwapHandler,
78 },
79 BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
80};
81
82#[cfg(test)]
83mod tests;
84
85#[cfg(feature = "runtime-benchmarks")]
86mod benchmarking;
87pub mod migration;
88pub mod weights;
89
90pub use pezpallet::*;
91pub use weights::*;
92
93#[derive(
95 Encode,
96 Decode,
97 DecodeWithMemTracking,
98 Eq,
99 PartialEq,
100 Copy,
101 Clone,
102 TypeInfo,
103 MaxEncodedLen,
104 RuntimeDebug,
105)]
106pub enum Wish {
107 Retention,
109 Promotion,
111}
112
113pub type Evidence<T, I> = BoundedVec<u8, <T as Config<I>>::EvidenceSize>;
118
119#[derive(
121 Encode,
122 Decode,
123 DecodeWithMemTracking,
124 CloneNoBound,
125 EqNoBound,
126 PartialEqNoBound,
127 RuntimeDebugNoBound,
128 TypeInfo,
129 MaxEncodedLen,
130)]
131#[scale_info(skip_type_params(Ranks))]
132pub struct ParamsType<
133 Balance: Clone + Eq + PartialEq + Debug,
134 BlockNumber: Clone + Eq + PartialEq + Debug,
135 Ranks: Get<u32>,
136> {
137 pub active_salary: BoundedVec<Balance, Ranks>,
139 pub passive_salary: BoundedVec<Balance, Ranks>,
141 pub demotion_period: BoundedVec<BlockNumber, Ranks>,
143 pub min_promotion_period: BoundedVec<BlockNumber, Ranks>,
145 pub offboard_timeout: BlockNumber,
147}
148
149impl<
150 Balance: Default + Copy + Eq + Debug,
151 BlockNumber: Default + Copy + Eq + Debug,
152 Ranks: Get<u32>,
153 > Default for ParamsType<Balance, BlockNumber, Ranks>
154{
155 fn default() -> Self {
156 Self {
157 active_salary: Default::default(),
158 passive_salary: Default::default(),
159 demotion_period: Default::default(),
160 min_promotion_period: Default::default(),
161 offboard_timeout: BlockNumber::default(),
162 }
163 }
164}
165
166pub struct ConvertU16ToU32<Inner>(PhantomData<Inner>);
167impl<Inner: Get<u16>> Get<u32> for ConvertU16ToU32<Inner> {
168 fn get() -> u32 {
169 Inner::get() as u32
170 }
171}
172
173#[derive(Encode, Decode, Eq, PartialEq, Clone, TypeInfo, MaxEncodedLen, RuntimeDebug)]
175pub struct MemberStatus<BlockNumber> {
176 is_active: bool,
178 last_promotion: BlockNumber,
180 last_proof: BlockNumber,
182}
183
184#[pezframe_support::pezpallet]
185pub mod pezpallet {
186 use super::*;
187 use pezframe_support::{
188 dispatch::Pays,
189 pezpallet_prelude::*,
190 traits::{tokens::GetSalary, EnsureOrigin},
191 };
192 use pezframe_system::{ensure_root, pezpallet_prelude::*};
193 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
195
196 #[pezpallet::pezpallet]
197 #[pezpallet::storage_version(STORAGE_VERSION)]
198 pub struct Pezpallet<T, I = ()>(PhantomData<(T, I)>);
199
200 #[pezpallet::config]
201 pub trait Config<I: 'static = ()>: pezframe_system::Config {
202 type WeightInfo: WeightInfo;
204
205 #[allow(deprecated)]
207 type RuntimeEvent: From<Event<Self, I>>
208 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
209
210 type Members: RankedMembers<
212 AccountId = <Self as pezframe_system::Config>::AccountId,
213 Rank = u16,
214 >;
215
216 type Balance: BalanceTrait;
218
219 type ParamsOrigin: EnsureOrigin<Self::RuntimeOrigin>;
221
222 type InductOrigin: EnsureOrigin<Self::RuntimeOrigin>;
229
230 type ApproveOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
233
234 type PromoteOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
237
238 type FastPromoteOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
241
242 #[pezpallet::constant]
244 type EvidenceSize: Get<u32>;
245
246 #[pezpallet::constant]
250 type MaxRank: Get<u16>;
251 }
252
253 pub type ParamsOf<T, I> = ParamsType<
254 <T as Config<I>>::Balance,
255 BlockNumberFor<T>,
256 ConvertU16ToU32<<T as Config<I>>::MaxRank>,
257 >;
258 pub type PartialParamsOf<T, I> = ParamsType<
259 Option<<T as Config<I>>::Balance>,
260 Option<BlockNumberFor<T>>,
261 ConvertU16ToU32<<T as Config<I>>::MaxRank>,
262 >;
263 pub type MemberStatusOf<T> = MemberStatus<BlockNumberFor<T>>;
264 pub type RankOf<T, I> = <<T as Config<I>>::Members as RankedMembers>::Rank;
265
266 #[pezpallet::storage]
268 pub type Params<T: Config<I>, I: 'static = ()> = StorageValue<_, ParamsOf<T, I>, ValueQuery>;
269
270 #[pezpallet::storage]
272 pub type Member<T: Config<I>, I: 'static = ()> =
273 StorageMap<_, Twox64Concat, T::AccountId, MemberStatusOf<T>, OptionQuery>;
274
275 #[pezpallet::storage]
277 pub type MemberEvidence<T: Config<I>, I: 'static = ()> =
278 StorageMap<_, Twox64Concat, T::AccountId, (Wish, Evidence<T, I>), OptionQuery>;
279
280 #[pezpallet::event]
281 #[pezpallet::generate_deposit(pub(super) fn deposit_event)]
282 pub enum Event<T: Config<I>, I: 'static = ()> {
283 ParamsChanged { params: ParamsOf<T, I> },
285 ActiveChanged { who: T::AccountId, is_active: bool },
287 Inducted { who: T::AccountId },
289 Offboarded { who: T::AccountId },
292 Promoted { who: T::AccountId, to_rank: RankOf<T, I> },
294 Demoted { who: T::AccountId, to_rank: RankOf<T, I> },
296 Proven { who: T::AccountId, at_rank: RankOf<T, I> },
298 Requested { who: T::AccountId, wish: Wish },
300 EvidenceJudged {
303 who: T::AccountId,
305 wish: Wish,
307 evidence: Evidence<T, I>,
309 old_rank: u16,
311 new_rank: Option<u16>,
313 },
314 Imported { who: T::AccountId, rank: RankOf<T, I> },
316 Swapped { who: T::AccountId, new_who: T::AccountId },
318 }
319
320 #[pezpallet::error]
321 pub enum Error<T, I = ()> {
322 Unranked,
324 Ranked,
326 UnexpectedRank,
329 InvalidRank,
331 NoPermission,
333 NothingDoing,
335 AlreadyInducted,
338 NotTracked,
340 TooSoon,
342 }
343
344 #[pezpallet::call]
345 impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
346 #[pezpallet::weight(T::WeightInfo::bump_offboard().max(T::WeightInfo::bump_demote()))]
354 #[pezpallet::call_index(0)]
355 pub fn bump(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
356 ensure_signed(origin)?;
357 let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
358 let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
359
360 let params = Params::<T, I>::get();
361 let demotion_period = if rank == 0 {
362 params.offboard_timeout
363 } else {
364 let rank_index = Self::rank_to_index(rank).ok_or(Error::<T, I>::InvalidRank)?;
365 params.demotion_period[rank_index]
366 };
367
368 if demotion_period.is_zero() {
369 return Err(Error::<T, I>::NothingDoing.into());
370 }
371
372 let demotion_block = member.last_proof.saturating_add(demotion_period);
373
374 let now = pezframe_system::Pezpallet::<T>::block_number();
376 if now >= demotion_block {
377 T::Members::demote(&who)?;
378 let maybe_to_rank = T::Members::rank_of(&who);
379 Self::dispose_evidence(who.clone(), rank, maybe_to_rank);
380 let event = if let Some(to_rank) = maybe_to_rank {
381 member.last_proof = now;
382 Member::<T, I>::insert(&who, &member);
383 Event::<T, I>::Demoted { who, to_rank }
384 } else {
385 Member::<T, I>::remove(&who);
386 Event::<T, I>::Offboarded { who }
387 };
388 Self::deposit_event(event);
389 return Ok(Pays::No.into());
390 }
391
392 Err(Error::<T, I>::NothingDoing.into())
393 }
394
395 #[pezpallet::weight(T::WeightInfo::set_params())]
400 #[pezpallet::call_index(1)]
401 pub fn set_params(origin: OriginFor<T>, params: Box<ParamsOf<T, I>>) -> DispatchResult {
402 T::ParamsOrigin::ensure_origin_or_root(origin)?;
403
404 Params::<T, I>::put(params.as_ref());
405 Self::deposit_event(Event::<T, I>::ParamsChanged { params: *params });
406
407 Ok(())
408 }
409
410 #[pezpallet::weight(T::WeightInfo::set_active())]
415 #[pezpallet::call_index(2)]
416 pub fn set_active(origin: OriginFor<T>, is_active: bool) -> DispatchResult {
417 let who = ensure_signed(origin)?;
418 ensure!(
419 T::Members::rank_of(&who).map_or(false, |r| !r.is_zero()),
420 Error::<T, I>::Unranked
421 );
422 let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
423 member.is_active = is_active;
424 Member::<T, I>::insert(&who, &member);
425 Self::deposit_event(Event::<T, I>::ActiveChanged { who, is_active });
426 Ok(())
427 }
428
429 #[pezpallet::weight(T::WeightInfo::approve())]
439 #[pezpallet::call_index(3)]
440 pub fn approve(
441 origin: OriginFor<T>,
442 who: T::AccountId,
443 at_rank: RankOf<T, I>,
444 ) -> DispatchResult {
445 match T::ApproveOrigin::try_origin(origin) {
446 Ok(allow_rank) => ensure!(allow_rank >= at_rank, Error::<T, I>::NoPermission),
447 Err(origin) => ensure_root(origin)?,
448 }
449 ensure!(at_rank > 0, Error::<T, I>::InvalidRank);
450 let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
451 ensure!(rank == at_rank, Error::<T, I>::UnexpectedRank);
452 let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
453
454 member.last_proof = pezframe_system::Pezpallet::<T>::block_number();
455 Member::<T, I>::insert(&who, &member);
456
457 Self::dispose_evidence(who.clone(), at_rank, Some(at_rank));
458 Self::deposit_event(Event::<T, I>::Proven { who, at_rank });
459
460 Ok(())
461 }
462
463 #[pezpallet::weight(T::WeightInfo::induct())]
468 #[pezpallet::call_index(4)]
469 pub fn induct(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
470 match T::InductOrigin::try_origin(origin) {
471 Ok(_) => {},
472 Err(origin) => ensure_root(origin)?,
473 }
474 ensure!(!Member::<T, I>::contains_key(&who), Error::<T, I>::AlreadyInducted);
475 ensure!(T::Members::rank_of(&who).is_none(), Error::<T, I>::Ranked);
476
477 T::Members::induct(&who)?;
478 let now = pezframe_system::Pezpallet::<T>::block_number();
479 Member::<T, I>::insert(
480 &who,
481 MemberStatus { is_active: true, last_promotion: now, last_proof: now },
482 );
483 Self::deposit_event(Event::<T, I>::Inducted { who });
484 Ok(())
485 }
486
487 #[pezpallet::weight(T::WeightInfo::promote())]
494 #[pezpallet::call_index(5)]
495 pub fn promote(
496 origin: OriginFor<T>,
497 who: T::AccountId,
498 to_rank: RankOf<T, I>,
499 ) -> DispatchResult {
500 match T::PromoteOrigin::try_origin(origin) {
501 Ok(allow_rank) => ensure!(allow_rank >= to_rank, Error::<T, I>::NoPermission),
502 Err(origin) => ensure_root(origin)?,
503 }
504 let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
505 ensure!(
506 rank.checked_add(1).map_or(false, |i| i == to_rank),
507 Error::<T, I>::UnexpectedRank
508 );
509
510 let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
511 let now = pezframe_system::Pezpallet::<T>::block_number();
512
513 let params = Params::<T, I>::get();
514 let rank_index = Self::rank_to_index(to_rank).ok_or(Error::<T, I>::InvalidRank)?;
515 let min_period = params.min_promotion_period[rank_index];
516 ensure!(
518 member.last_promotion.saturating_add(min_period) <= now,
519 Error::<T, I>::TooSoon,
520 );
521
522 T::Members::promote(&who)?;
523 member.last_promotion = now;
524 member.last_proof = now;
525 Member::<T, I>::insert(&who, &member);
526 Self::dispose_evidence(who.clone(), rank, Some(to_rank));
527
528 Self::deposit_event(Event::<T, I>::Promoted { who, to_rank });
529
530 Ok(())
531 }
532
533 #[pezpallet::weight(T::WeightInfo::promote_fast(*to_rank as u32))]
539 #[pezpallet::call_index(10)]
540 pub fn promote_fast(
541 origin: OriginFor<T>,
542 who: T::AccountId,
543 to_rank: RankOf<T, I>,
544 ) -> DispatchResult {
545 match T::FastPromoteOrigin::try_origin(origin) {
546 Ok(allow_rank) => ensure!(allow_rank >= to_rank, Error::<T, I>::NoPermission),
547 Err(origin) => ensure_root(origin)?,
548 }
549 ensure!(to_rank <= T::MaxRank::get(), Error::<T, I>::InvalidRank);
550 let curr_rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
551 ensure!(to_rank > curr_rank, Error::<T, I>::UnexpectedRank);
552
553 let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
554 let now = pezframe_system::Pezpallet::<T>::block_number();
555 member.last_promotion = now;
556 member.last_proof = now;
557
558 for rank in (curr_rank + 1)..=to_rank {
559 T::Members::promote(&who)?;
560
561 Member::<T, I>::insert(&who, &member);
563
564 Self::dispose_evidence(who.clone(), rank.saturating_sub(1), Some(rank));
565 Self::deposit_event(Event::<T, I>::Promoted { who: who.clone(), to_rank: rank });
566 }
567
568 Ok(())
569 }
570
571 #[pezpallet::weight(T::WeightInfo::offboard())]
577 #[pezpallet::call_index(6)]
578 pub fn offboard(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
579 ensure_signed(origin)?;
580 ensure!(T::Members::rank_of(&who).is_none(), Error::<T, I>::Ranked);
581 ensure!(Member::<T, I>::contains_key(&who), Error::<T, I>::NotTracked);
582 Member::<T, I>::remove(&who);
583 MemberEvidence::<T, I>::remove(&who);
584 Self::deposit_event(Event::<T, I>::Offboarded { who });
585 Ok(Pays::No.into())
586 }
587
588 #[pezpallet::weight(T::WeightInfo::submit_evidence())]
599 #[pezpallet::call_index(7)]
600 pub fn submit_evidence(
601 origin: OriginFor<T>,
602 wish: Wish,
603 evidence: Evidence<T, I>,
604 ) -> DispatchResultWithPostInfo {
605 let who = ensure_signed(origin)?;
606 ensure!(Member::<T, I>::contains_key(&who), Error::<T, I>::NotTracked);
607 let replaced = MemberEvidence::<T, I>::contains_key(&who);
608 MemberEvidence::<T, I>::insert(&who, (wish, evidence));
609 Self::deposit_event(Event::<T, I>::Requested { who, wish });
610 Ok(if replaced { Pays::Yes } else { Pays::No }.into())
611 }
612
613 #[pezpallet::weight(T::WeightInfo::import())]
621 #[pezpallet::call_index(8)]
622 #[deprecated = "Use `import_member` instead"]
623 #[allow(deprecated)] pub fn import(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
625 let who = ensure_signed(origin)?;
626 Self::do_import(who)?;
627
628 Ok(Pays::No.into()) }
630
631 #[pezpallet::weight(T::WeightInfo::set_partial_params())]
642 #[pezpallet::call_index(11)]
643 pub fn import_member(
644 origin: OriginFor<T>,
645 who: T::AccountId,
646 ) -> DispatchResultWithPostInfo {
647 ensure_signed(origin)?;
648 Self::do_import(who)?;
649
650 Ok(Pays::No.into()) }
652
653 #[pezpallet::weight(T::WeightInfo::set_partial_params())]
661 #[pezpallet::call_index(9)]
662 pub fn set_partial_params(
663 origin: OriginFor<T>,
664 partial_params: Box<PartialParamsOf<T, I>>,
665 ) -> DispatchResult {
666 T::ParamsOrigin::ensure_origin_or_root(origin)?;
667 let params = Params::<T, I>::mutate(|p| {
668 Self::set_partial_params_slice(&mut p.active_salary, partial_params.active_salary);
669 Self::set_partial_params_slice(
670 &mut p.passive_salary,
671 partial_params.passive_salary,
672 );
673 Self::set_partial_params_slice(
674 &mut p.demotion_period,
675 partial_params.demotion_period,
676 );
677 Self::set_partial_params_slice(
678 &mut p.min_promotion_period,
679 partial_params.min_promotion_period,
680 );
681 if let Some(new_offboard_timeout) = partial_params.offboard_timeout {
682 p.offboard_timeout = new_offboard_timeout;
683 }
684 p.clone()
685 });
686 Self::deposit_event(Event::<T, I>::ParamsChanged { params });
687 Ok(())
688 }
689 }
690
691 impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
692 pub(crate) fn set_partial_params_slice<S>(
696 base_slice: &mut BoundedVec<S, ConvertU16ToU32<T::MaxRank>>,
697 new_slice: BoundedVec<Option<S>, ConvertU16ToU32<T::MaxRank>>,
698 ) {
699 for (base_element, new_element) in base_slice.iter_mut().zip(new_slice) {
700 if let Some(element) = new_element {
701 *base_element = element;
702 }
703 }
704 }
705
706 pub(crate) fn do_import(who: T::AccountId) -> DispatchResult {
710 ensure!(!Member::<T, I>::contains_key(&who), Error::<T, I>::AlreadyInducted);
711 let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
712
713 let now = pezframe_system::Pezpallet::<T>::block_number();
714 Member::<T, I>::insert(
715 &who,
716 MemberStatus { is_active: true, last_promotion: 0u32.into(), last_proof: now },
717 );
718 Self::deposit_event(Event::<T, I>::Imported { who, rank });
719
720 Ok(())
721 }
722
723 pub(crate) fn rank_to_index(rank: RankOf<T, I>) -> Option<usize> {
728 if rank == 0 || rank > T::MaxRank::get() {
729 None
730 } else {
731 Some((rank - 1) as usize)
732 }
733 }
734
735 fn dispose_evidence(who: T::AccountId, old_rank: u16, new_rank: Option<u16>) {
736 if let Some((wish, evidence)) = MemberEvidence::<T, I>::take(&who) {
737 let e = Event::<T, I>::EvidenceJudged { who, wish, evidence, old_rank, new_rank };
738 Self::deposit_event(e);
739 }
740 }
741 }
742
743 impl<T: Config<I>, I: 'static> GetSalary<RankOf<T, I>, T::AccountId, T::Balance>
744 for Pezpallet<T, I>
745 {
746 fn get_salary(rank: RankOf<T, I>, who: &T::AccountId) -> T::Balance {
747 let index = match Self::rank_to_index(rank) {
748 Some(i) => i,
749 None => return Zero::zero(),
750 };
751 let member = match Member::<T, I>::get(who) {
752 Some(m) => m,
753 None => return Zero::zero(),
754 };
755 let params = Params::<T, I>::get();
756 let salary =
757 if member.is_active { params.active_salary } else { params.passive_salary };
758 salary[index]
759 }
760 }
761}
762
763pub struct EnsureInducted<T, I, const MIN_RANK: u16>(PhantomData<(T, I)>);
766impl<T: Config<I>, I: 'static, const MIN_RANK: u16> EnsureOrigin<T::RuntimeOrigin>
767 for EnsureInducted<T, I, MIN_RANK>
768{
769 type Success = T::AccountId;
770
771 fn try_origin(o: T::RuntimeOrigin) -> Result<Self::Success, T::RuntimeOrigin> {
772 let who = <pezframe_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o)?;
773 match T::Members::rank_of(&who) {
774 Some(rank) if rank >= MIN_RANK && Member::<T, I>::contains_key(&who) => Ok(who),
775 _ => Err(pezframe_system::RawOrigin::Signed(who).into()),
776 }
777 }
778
779 #[cfg(feature = "runtime-benchmarks")]
780 fn try_successful_origin() -> Result<T::RuntimeOrigin, ()> {
781 let who = pezframe_benchmarking::account::<T::AccountId>("successful_origin", 0, 0);
782 if T::Members::rank_of(&who).is_none() {
783 T::Members::induct(&who).map_err(|_| ())?;
784 }
785 for _ in 0..MIN_RANK {
786 if T::Members::rank_of(&who).ok_or(())? < MIN_RANK {
787 T::Members::promote(&who).map_err(|_| ())?;
788 }
789 }
790 Ok(pezframe_system::RawOrigin::Signed(who).into())
791 }
792}
793
794impl_ensure_origin_with_arg_ignoring_arg! {
795 impl< { T: Config<I>, I: 'static, const MIN_RANK: u16, A } >
796 EnsureOriginWithArg<T::RuntimeOrigin, A> for EnsureInducted<T, I, MIN_RANK>
797 {}
798}
799
800impl<T: Config<I>, I: 'static> RankedMembersSwapHandler<T::AccountId, u16> for Pezpallet<T, I> {
801 fn swapped(old: &T::AccountId, new: &T::AccountId, _rank: u16) {
802 if old == new {
803 defensive!("Should not try to swap with self");
804 return;
805 }
806 if !Member::<T, I>::contains_key(old) {
807 defensive!("Should not try to swap non-member");
808 return;
809 }
810 if Member::<T, I>::contains_key(new) {
811 defensive!("Should not try to overwrite existing member");
812 return;
813 }
814
815 if let Some(member) = Member::<T, I>::take(old) {
816 Member::<T, I>::insert(new, member);
817 }
818 if let Some(we) = MemberEvidence::<T, I>::take(old) {
819 MemberEvidence::<T, I>::insert(new, we);
820 }
821
822 Self::deposit_event(Event::<T, I>::Swapped { who: old.clone(), new_who: new.clone() });
823 }
824}
825
826#[cfg(feature = "runtime-benchmarks")]
827impl<T: Config<I>, I: 'static>
828 pezpallet_ranked_collective::BenchmarkSetup<<T as pezframe_system::Config>::AccountId>
829 for Pezpallet<T, I>
830{
831 fn ensure_member(who: &<T as pezframe_system::Config>::AccountId) {
832 #[allow(deprecated)]
833 Self::import(pezframe_system::RawOrigin::Signed(who.clone()).into()).unwrap();
834 }
835}