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 scale_info::TypeInfo;
68use sp_arithmetic::traits::{Saturating, Zero};
69use sp_runtime::RuntimeDebug;
70
71use frame_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 pallet::*;
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#[frame_support::pallet]
185pub mod pallet {
186	use super::*;
187	use frame_support::{
188		dispatch::Pays,
189		pallet_prelude::*,
190		traits::{tokens::GetSalary, EnsureOrigin},
191	};
192	use frame_system::{ensure_root, pallet_prelude::*};
193	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
195
196	#[pallet::pallet]
197	#[pallet::storage_version(STORAGE_VERSION)]
198	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
199
200	#[pallet::config]
201	pub trait Config<I: 'static = ()>: frame_system::Config {
202		type WeightInfo: WeightInfo;
204
205		#[allow(deprecated)]
207		type RuntimeEvent: From<Event<Self, I>>
208			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
209
210		type Members: RankedMembers<
212			AccountId = <Self as frame_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>;
228
229		type ApproveOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
232
233		type PromoteOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
236
237		type FastPromoteOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = RankOf<Self, I>>;
240
241		#[pallet::constant]
243		type EvidenceSize: Get<u32>;
244
245		#[pallet::constant]
249		type MaxRank: Get<u16>;
250	}
251
252	pub type ParamsOf<T, I> = ParamsType<
253		<T as Config<I>>::Balance,
254		BlockNumberFor<T>,
255		ConvertU16ToU32<<T as Config<I>>::MaxRank>,
256	>;
257	pub type PartialParamsOf<T, I> = ParamsType<
258		Option<<T as Config<I>>::Balance>,
259		Option<BlockNumberFor<T>>,
260		ConvertU16ToU32<<T as Config<I>>::MaxRank>,
261	>;
262	pub type MemberStatusOf<T> = MemberStatus<BlockNumberFor<T>>;
263	pub type RankOf<T, I> = <<T as Config<I>>::Members as RankedMembers>::Rank;
264
265	#[pallet::storage]
267	pub type Params<T: Config<I>, I: 'static = ()> = StorageValue<_, ParamsOf<T, I>, ValueQuery>;
268
269	#[pallet::storage]
271	pub type Member<T: Config<I>, I: 'static = ()> =
272		StorageMap<_, Twox64Concat, T::AccountId, MemberStatusOf<T>, OptionQuery>;
273
274	#[pallet::storage]
276	pub type MemberEvidence<T: Config<I>, I: 'static = ()> =
277		StorageMap<_, Twox64Concat, T::AccountId, (Wish, Evidence<T, I>), OptionQuery>;
278
279	#[pallet::event]
280	#[pallet::generate_deposit(pub(super) fn deposit_event)]
281	pub enum Event<T: Config<I>, I: 'static = ()> {
282		ParamsChanged { params: ParamsOf<T, I> },
284		ActiveChanged { who: T::AccountId, is_active: bool },
286		Inducted { who: T::AccountId },
288		Offboarded { who: T::AccountId },
291		Promoted { who: T::AccountId, to_rank: RankOf<T, I> },
293		Demoted { who: T::AccountId, to_rank: RankOf<T, I> },
295		Proven { who: T::AccountId, at_rank: RankOf<T, I> },
297		Requested { who: T::AccountId, wish: Wish },
299		EvidenceJudged {
302			who: T::AccountId,
304			wish: Wish,
306			evidence: Evidence<T, I>,
308			old_rank: u16,
310			new_rank: Option<u16>,
312		},
313		Imported { who: T::AccountId, rank: RankOf<T, I> },
315		Swapped { who: T::AccountId, new_who: T::AccountId },
317	}
318
319	#[pallet::error]
320	pub enum Error<T, I = ()> {
321		Unranked,
323		Ranked,
325		UnexpectedRank,
328		InvalidRank,
330		NoPermission,
332		NothingDoing,
334		AlreadyInducted,
337		NotTracked,
339		TooSoon,
341	}
342
343	#[pallet::call]
344	impl<T: Config<I>, I: 'static> Pallet<T, I> {
345		#[pallet::weight(T::WeightInfo::bump_offboard().max(T::WeightInfo::bump_demote()))]
353		#[pallet::call_index(0)]
354		pub fn bump(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
355			ensure_signed(origin)?;
356			let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
357			let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
358
359			let params = Params::<T, I>::get();
360			let demotion_period = if rank == 0 {
361				params.offboard_timeout
362			} else {
363				let rank_index = Self::rank_to_index(rank).ok_or(Error::<T, I>::InvalidRank)?;
364				params.demotion_period[rank_index]
365			};
366
367			if demotion_period.is_zero() {
368				return Err(Error::<T, I>::NothingDoing.into())
369			}
370
371			let demotion_block = member.last_proof.saturating_add(demotion_period);
372
373			let now = frame_system::Pallet::<T>::block_number();
375			if now >= demotion_block {
376				T::Members::demote(&who)?;
377				let maybe_to_rank = T::Members::rank_of(&who);
378				Self::dispose_evidence(who.clone(), rank, maybe_to_rank);
379				let event = if let Some(to_rank) = maybe_to_rank {
380					member.last_proof = now;
381					Member::<T, I>::insert(&who, &member);
382					Event::<T, I>::Demoted { who, to_rank }
383				} else {
384					Member::<T, I>::remove(&who);
385					Event::<T, I>::Offboarded { who }
386				};
387				Self::deposit_event(event);
388				return Ok(Pays::No.into())
389			}
390
391			Err(Error::<T, I>::NothingDoing.into())
392		}
393
394		#[pallet::weight(T::WeightInfo::set_params())]
399		#[pallet::call_index(1)]
400		pub fn set_params(origin: OriginFor<T>, params: Box<ParamsOf<T, I>>) -> DispatchResult {
401			T::ParamsOrigin::ensure_origin_or_root(origin)?;
402
403			Params::<T, I>::put(params.as_ref());
404			Self::deposit_event(Event::<T, I>::ParamsChanged { params: *params });
405
406			Ok(())
407		}
408
409		#[pallet::weight(T::WeightInfo::set_active())]
414		#[pallet::call_index(2)]
415		pub fn set_active(origin: OriginFor<T>, is_active: bool) -> DispatchResult {
416			let who = ensure_signed(origin)?;
417			ensure!(
418				T::Members::rank_of(&who).map_or(false, |r| !r.is_zero()),
419				Error::<T, I>::Unranked
420			);
421			let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
422			member.is_active = is_active;
423			Member::<T, I>::insert(&who, &member);
424			Self::deposit_event(Event::<T, I>::ActiveChanged { who, is_active });
425			Ok(())
426		}
427
428		#[pallet::weight(T::WeightInfo::approve())]
438		#[pallet::call_index(3)]
439		pub fn approve(
440			origin: OriginFor<T>,
441			who: T::AccountId,
442			at_rank: RankOf<T, I>,
443		) -> DispatchResult {
444			match T::ApproveOrigin::try_origin(origin) {
445				Ok(allow_rank) => ensure!(allow_rank >= at_rank, Error::<T, I>::NoPermission),
446				Err(origin) => ensure_root(origin)?,
447			}
448			ensure!(at_rank > 0, Error::<T, I>::InvalidRank);
449			let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
450			ensure!(rank == at_rank, Error::<T, I>::UnexpectedRank);
451			let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
452
453			member.last_proof = frame_system::Pallet::<T>::block_number();
454			Member::<T, I>::insert(&who, &member);
455
456			Self::dispose_evidence(who.clone(), at_rank, Some(at_rank));
457			Self::deposit_event(Event::<T, I>::Proven { who, at_rank });
458
459			Ok(())
460		}
461
462		#[pallet::weight(T::WeightInfo::induct())]
467		#[pallet::call_index(4)]
468		pub fn induct(origin: OriginFor<T>, who: T::AccountId) -> DispatchResult {
469			match T::InductOrigin::try_origin(origin) {
470				Ok(_) => {},
471				Err(origin) => ensure_root(origin)?,
472			}
473			ensure!(!Member::<T, I>::contains_key(&who), Error::<T, I>::AlreadyInducted);
474			ensure!(T::Members::rank_of(&who).is_none(), Error::<T, I>::Ranked);
475
476			T::Members::induct(&who)?;
477			let now = frame_system::Pallet::<T>::block_number();
478			Member::<T, I>::insert(
479				&who,
480				MemberStatus { is_active: true, last_promotion: now, last_proof: now },
481			);
482			Self::deposit_event(Event::<T, I>::Inducted { who });
483			Ok(())
484		}
485
486		#[pallet::weight(T::WeightInfo::promote())]
493		#[pallet::call_index(5)]
494		pub fn promote(
495			origin: OriginFor<T>,
496			who: T::AccountId,
497			to_rank: RankOf<T, I>,
498		) -> DispatchResult {
499			match T::PromoteOrigin::try_origin(origin) {
500				Ok(allow_rank) => ensure!(allow_rank >= to_rank, Error::<T, I>::NoPermission),
501				Err(origin) => ensure_root(origin)?,
502			}
503			let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
504			ensure!(
505				rank.checked_add(1).map_or(false, |i| i == to_rank),
506				Error::<T, I>::UnexpectedRank
507			);
508
509			let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
510			let now = frame_system::Pallet::<T>::block_number();
511
512			let params = Params::<T, I>::get();
513			let rank_index = Self::rank_to_index(to_rank).ok_or(Error::<T, I>::InvalidRank)?;
514			let min_period = params.min_promotion_period[rank_index];
515			ensure!(
517				member.last_promotion.saturating_add(min_period) <= now,
518				Error::<T, I>::TooSoon,
519			);
520
521			T::Members::promote(&who)?;
522			member.last_promotion = now;
523			member.last_proof = now;
524			Member::<T, I>::insert(&who, &member);
525			Self::dispose_evidence(who.clone(), rank, Some(to_rank));
526
527			Self::deposit_event(Event::<T, I>::Promoted { who, to_rank });
528
529			Ok(())
530		}
531
532		#[pallet::weight(T::WeightInfo::promote_fast(*to_rank as u32))]
538		#[pallet::call_index(10)]
539		pub fn promote_fast(
540			origin: OriginFor<T>,
541			who: T::AccountId,
542			to_rank: RankOf<T, I>,
543		) -> DispatchResult {
544			match T::FastPromoteOrigin::try_origin(origin) {
545				Ok(allow_rank) => ensure!(allow_rank >= to_rank, Error::<T, I>::NoPermission),
546				Err(origin) => ensure_root(origin)?,
547			}
548			ensure!(to_rank <= T::MaxRank::get(), Error::<T, I>::InvalidRank);
549			let curr_rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
550			ensure!(to_rank > curr_rank, Error::<T, I>::UnexpectedRank);
551
552			let mut member = Member::<T, I>::get(&who).ok_or(Error::<T, I>::NotTracked)?;
553			let now = frame_system::Pallet::<T>::block_number();
554			member.last_promotion = now;
555			member.last_proof = now;
556
557			for rank in (curr_rank + 1)..=to_rank {
558				T::Members::promote(&who)?;
559
560				Member::<T, I>::insert(&who, &member);
562
563				Self::dispose_evidence(who.clone(), rank.saturating_sub(1), Some(rank));
564				Self::deposit_event(Event::<T, I>::Promoted { who: who.clone(), to_rank: rank });
565			}
566
567			Ok(())
568		}
569
570		#[pallet::weight(T::WeightInfo::offboard())]
576		#[pallet::call_index(6)]
577		pub fn offboard(origin: OriginFor<T>, who: T::AccountId) -> DispatchResultWithPostInfo {
578			ensure_signed(origin)?;
579			ensure!(T::Members::rank_of(&who).is_none(), Error::<T, I>::Ranked);
580			ensure!(Member::<T, I>::contains_key(&who), Error::<T, I>::NotTracked);
581			Member::<T, I>::remove(&who);
582			MemberEvidence::<T, I>::remove(&who);
583			Self::deposit_event(Event::<T, I>::Offboarded { who });
584			Ok(Pays::No.into())
585		}
586
587		#[pallet::weight(T::WeightInfo::submit_evidence())]
598		#[pallet::call_index(7)]
599		pub fn submit_evidence(
600			origin: OriginFor<T>,
601			wish: Wish,
602			evidence: Evidence<T, I>,
603		) -> DispatchResultWithPostInfo {
604			let who = ensure_signed(origin)?;
605			ensure!(Member::<T, I>::contains_key(&who), Error::<T, I>::NotTracked);
606			let replaced = MemberEvidence::<T, I>::contains_key(&who);
607			MemberEvidence::<T, I>::insert(&who, (wish, evidence));
608			Self::deposit_event(Event::<T, I>::Requested { who, wish });
609			Ok(if replaced { Pays::Yes } else { Pays::No }.into())
610		}
611
612		#[pallet::weight(T::WeightInfo::import())]
620		#[pallet::call_index(8)]
621		#[deprecated = "Use `import_member` instead"]
622		#[allow(deprecated)] pub fn import(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
624			let who = ensure_signed(origin)?;
625			Self::do_import(who)?;
626
627			Ok(Pays::No.into()) }
629
630		#[pallet::weight(T::WeightInfo::set_partial_params())]
641		#[pallet::call_index(11)]
642		pub fn import_member(
643			origin: OriginFor<T>,
644			who: T::AccountId,
645		) -> DispatchResultWithPostInfo {
646			ensure_signed(origin)?;
647			Self::do_import(who)?;
648
649			Ok(Pays::No.into()) }
651
652		#[pallet::weight(T::WeightInfo::set_partial_params())]
660		#[pallet::call_index(9)]
661		pub fn set_partial_params(
662			origin: OriginFor<T>,
663			partial_params: Box<PartialParamsOf<T, I>>,
664		) -> DispatchResult {
665			T::ParamsOrigin::ensure_origin_or_root(origin)?;
666			let params = Params::<T, I>::mutate(|p| {
667				Self::set_partial_params_slice(&mut p.active_salary, partial_params.active_salary);
668				Self::set_partial_params_slice(
669					&mut p.passive_salary,
670					partial_params.passive_salary,
671				);
672				Self::set_partial_params_slice(
673					&mut p.demotion_period,
674					partial_params.demotion_period,
675				);
676				Self::set_partial_params_slice(
677					&mut p.min_promotion_period,
678					partial_params.min_promotion_period,
679				);
680				if let Some(new_offboard_timeout) = partial_params.offboard_timeout {
681					p.offboard_timeout = new_offboard_timeout;
682				}
683				p.clone()
684			});
685			Self::deposit_event(Event::<T, I>::ParamsChanged { params });
686			Ok(())
687		}
688	}
689
690	impl<T: Config<I>, I: 'static> Pallet<T, I> {
691		pub(crate) fn set_partial_params_slice<S>(
695			base_slice: &mut BoundedVec<S, ConvertU16ToU32<T::MaxRank>>,
696			new_slice: BoundedVec<Option<S>, ConvertU16ToU32<T::MaxRank>>,
697		) {
698			for (base_element, new_element) in base_slice.iter_mut().zip(new_slice) {
699				if let Some(element) = new_element {
700					*base_element = element;
701				}
702			}
703		}
704
705		pub(crate) fn do_import(who: T::AccountId) -> DispatchResult {
709			ensure!(!Member::<T, I>::contains_key(&who), Error::<T, I>::AlreadyInducted);
710			let rank = T::Members::rank_of(&who).ok_or(Error::<T, I>::Unranked)?;
711
712			let now = frame_system::Pallet::<T>::block_number();
713			Member::<T, I>::insert(
714				&who,
715				MemberStatus { is_active: true, last_promotion: 0u32.into(), last_proof: now },
716			);
717			Self::deposit_event(Event::<T, I>::Imported { who, rank });
718
719			Ok(())
720		}
721
722		pub(crate) fn rank_to_index(rank: RankOf<T, I>) -> Option<usize> {
727			if rank == 0 || rank > T::MaxRank::get() {
728				None
729			} else {
730				Some((rank - 1) as usize)
731			}
732		}
733
734		fn dispose_evidence(who: T::AccountId, old_rank: u16, new_rank: Option<u16>) {
735			if let Some((wish, evidence)) = MemberEvidence::<T, I>::take(&who) {
736				let e = Event::<T, I>::EvidenceJudged { who, wish, evidence, old_rank, new_rank };
737				Self::deposit_event(e);
738			}
739		}
740	}
741
742	impl<T: Config<I>, I: 'static> GetSalary<RankOf<T, I>, T::AccountId, T::Balance> for Pallet<T, I> {
743		fn get_salary(rank: RankOf<T, I>, who: &T::AccountId) -> T::Balance {
744			let index = match Self::rank_to_index(rank) {
745				Some(i) => i,
746				None => return Zero::zero(),
747			};
748			let member = match Member::<T, I>::get(who) {
749				Some(m) => m,
750				None => return Zero::zero(),
751			};
752			let params = Params::<T, I>::get();
753			let salary =
754				if member.is_active { params.active_salary } else { params.passive_salary };
755			salary[index]
756		}
757	}
758}
759
760pub struct EnsureInducted<T, I, const MIN_RANK: u16>(PhantomData<(T, I)>);
763impl<T: Config<I>, I: 'static, const MIN_RANK: u16> EnsureOrigin<T::RuntimeOrigin>
764	for EnsureInducted<T, I, MIN_RANK>
765{
766	type Success = T::AccountId;
767
768	fn try_origin(o: T::RuntimeOrigin) -> Result<Self::Success, T::RuntimeOrigin> {
769		let who = <frame_system::EnsureSigned<_> as EnsureOrigin<_>>::try_origin(o)?;
770		match T::Members::rank_of(&who) {
771			Some(rank) if rank >= MIN_RANK && Member::<T, I>::contains_key(&who) => Ok(who),
772			_ => Err(frame_system::RawOrigin::Signed(who).into()),
773		}
774	}
775
776	#[cfg(feature = "runtime-benchmarks")]
777	fn try_successful_origin() -> Result<T::RuntimeOrigin, ()> {
778		let who = frame_benchmarking::account::<T::AccountId>("successful_origin", 0, 0);
779		if T::Members::rank_of(&who).is_none() {
780			T::Members::induct(&who).map_err(|_| ())?;
781		}
782		for _ in 0..MIN_RANK {
783			if T::Members::rank_of(&who).ok_or(())? < MIN_RANK {
784				T::Members::promote(&who).map_err(|_| ())?;
785			}
786		}
787		Ok(frame_system::RawOrigin::Signed(who).into())
788	}
789}
790
791impl_ensure_origin_with_arg_ignoring_arg! {
792	impl< { T: Config<I>, I: 'static, const MIN_RANK: u16, A } >
793		EnsureOriginWithArg<T::RuntimeOrigin, A> for EnsureInducted<T, I, MIN_RANK>
794	{}
795}
796
797impl<T: Config<I>, I: 'static> RankedMembersSwapHandler<T::AccountId, u16> for Pallet<T, I> {
798	fn swapped(old: &T::AccountId, new: &T::AccountId, _rank: u16) {
799		if old == new {
800			defensive!("Should not try to swap with self");
801			return
802		}
803		if !Member::<T, I>::contains_key(old) {
804			defensive!("Should not try to swap non-member");
805			return
806		}
807		if Member::<T, I>::contains_key(new) {
808			defensive!("Should not try to overwrite existing member");
809			return
810		}
811
812		if let Some(member) = Member::<T, I>::take(old) {
813			Member::<T, I>::insert(new, member);
814		}
815		if let Some(we) = MemberEvidence::<T, I>::take(old) {
816			MemberEvidence::<T, I>::insert(new, we);
817		}
818
819		Self::deposit_event(Event::<T, I>::Swapped { who: old.clone(), new_who: new.clone() });
820	}
821}
822
823#[cfg(feature = "runtime-benchmarks")]
824impl<T: Config<I>, I: 'static>
825	pallet_ranked_collective::BenchmarkSetup<<T as frame_system::Config>::AccountId> for Pallet<T, I>
826{
827	fn ensure_member(who: &<T as frame_system::Config>::AccountId) {
828		#[allow(deprecated)]
829		Self::import(frame_system::RawOrigin::Signed(who.clone()).into()).unwrap();
830	}
831}