123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990#![cfg_attr(not(feature = "std"), no_std)]9192mod benchmarking;93#[cfg(test)]94mod tests;95mod types;96pub mod weights;9798use frame_support::traits::{BalanceStatus, Currency, OnUnbalanced, ReservableCurrency};99use sp_runtime::traits::{AppendZerosInput, Hash, Saturating, StaticLookup, Zero};100use sp_std::prelude::*;101pub use weights::WeightInfo;102103pub use pallet::*;104pub use types::{105 Data, IdentityField, IdentityFields, IdentityInfo, Judgement, RegistrarIndex, RegistrarInfo,106 Registration,107};108109pub type BalanceOf<T> =110 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;111type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<112 <T as frame_system::Config>::AccountId,113>>::NegativeImbalance;114type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;115116#[frame_support::pallet]117pub mod pallet {118 use super::*;119 use frame_support::pallet_prelude::*;120 use frame_system::pallet_prelude::*;121122 #[pallet::config]123 pub trait Config: frame_system::Config {124 125 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;126127 128 type Currency: ReservableCurrency<Self::AccountId>;129130 131 #[pallet::constant]132 type BasicDeposit: Get<BalanceOf<Self>>;133134 135 #[pallet::constant]136 type FieldDeposit: Get<BalanceOf<Self>>;137138 139 140 141 #[pallet::constant]142 type SubAccountDeposit: Get<BalanceOf<Self>>;143144 145 #[pallet::constant]146 type MaxSubAccounts: Get<u32>;147148 149 150 #[pallet::constant]151 type MaxAdditionalFields: Get<u32>;152153 154 155 #[pallet::constant]156 type MaxRegistrars: Get<u32>;157158 159 type Slashed: OnUnbalanced<NegativeImbalanceOf<Self>>;160161 162 type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;163164 165 type RegistrarOrigin: EnsureOrigin<Self::RuntimeOrigin>;166167 168 type WeightInfo: WeightInfo;169 }170171 #[pallet::pallet]172 #[pallet::generate_store(pub(super) trait Store)]173 pub struct Pallet<T>(_);174175 176 177 178 #[pallet::storage]179 #[pallet::getter(fn identity)]180 pub type IdentityOf<T: Config> = StorageMap<181 _,182 Twox64Concat,183 T::AccountId,184 Registration<BalanceOf<T>, T::MaxRegistrars, T::MaxAdditionalFields>,185 OptionQuery,186 >;187188 189 190 #[pallet::storage]191 #[pallet::getter(fn super_of)]192 pub(super) type SuperOf<T: Config> =193 StorageMap<_, Blake2_128Concat, T::AccountId, (T::AccountId, Data), OptionQuery>;194195 196 197 198 199 200 #[pallet::storage]201 #[pallet::getter(fn subs_of)]202 pub(super) type SubsOf<T: Config> = StorageMap<203 _,204 Twox64Concat,205 T::AccountId,206 (BalanceOf<T>, BoundedVec<T::AccountId, T::MaxSubAccounts>),207 ValueQuery,208 >;209210 211 212 213 214 #[pallet::storage]215 #[pallet::getter(fn registrars)]216 pub(super) type Registrars<T: Config> = StorageValue<217 _,218 BoundedVec<Option<RegistrarInfo<BalanceOf<T>, T::AccountId>>, T::MaxRegistrars>,219 ValueQuery,220 >;221222 #[pallet::error]223 pub enum Error<T> {224 225 TooManySubAccounts,226 227 NotFound,228 229 NotNamed,230 231 EmptyIndex,232 233 FeeChanged,234 235 NoIdentity,236 237 StickyJudgement,238 239 JudgementGiven,240 241 InvalidJudgement,242 243 InvalidIndex,244 245 InvalidTarget,246 247 TooManyFields,248 249 TooManyRegistrars,250 251 AlreadyClaimed,252 253 NotSub,254 255 NotOwned,256 257 JudgementForDifferentIdentity,258 259 JudgementPaymentFailed,260 }261262 #[pallet::event]263 #[pallet::generate_deposit(pub(super) fn deposit_event)]264 pub enum Event<T: Config> {265 266 IdentitySet { who: T::AccountId },267 268 IdentityCleared { who: T::AccountId, deposit: BalanceOf<T> },269 270 IdentityKilled { who: T::AccountId, deposit: BalanceOf<T> },271 272 JudgementRequested { who: T::AccountId, registrar_index: RegistrarIndex },273 274 JudgementUnrequested { who: T::AccountId, registrar_index: RegistrarIndex },275 276 JudgementGiven { target: T::AccountId, registrar_index: RegistrarIndex },277 278 RegistrarAdded { registrar_index: RegistrarIndex },279 280 SubIdentityAdded { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf<T> },281 282 SubIdentityRemoved { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf<T> },283 284 285 SubIdentityRevoked { sub: T::AccountId, main: T::AccountId, deposit: BalanceOf<T> },286 }287288 #[pallet::call]289 290 impl<T: Config> Pallet<T> {291 292 293 294 295 296 297 298 299 300 301 302 303 304 #[pallet::call_index(0)]305 #[pallet::weight(T::WeightInfo::add_registrar(T::MaxRegistrars::get()))]306 pub fn add_registrar(307 origin: OriginFor<T>,308 account: AccountIdLookupOf<T>,309 ) -> DispatchResultWithPostInfo {310 T::RegistrarOrigin::ensure_origin(origin)?;311 let account = T::Lookup::lookup(account)?;312313 let (i, registrar_count) = <Registrars<T>>::try_mutate(314 |registrars| -> Result<(RegistrarIndex, usize), DispatchError> {315 registrars316 .try_push(Some(RegistrarInfo {317 account,318 fee: Zero::zero(),319 fields: Default::default(),320 }))321 .map_err(|_| Error::<T>::TooManyRegistrars)?;322 Ok(((registrars.len() - 1) as RegistrarIndex, registrars.len()))323 },324 )?;325326 Self::deposit_event(Event::RegistrarAdded { registrar_index: i });327328 Ok(Some(T::WeightInfo::add_registrar(registrar_count as u32)).into())329 }330331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 #[pallet::call_index(1)]351 #[pallet::weight( T::WeightInfo::set_identity(352 T::MaxRegistrars::get(), 353 T::MaxAdditionalFields::get(), 354 ))]355 pub fn set_identity(356 origin: OriginFor<T>,357 info: Box<IdentityInfo<T::MaxAdditionalFields>>,358 ) -> DispatchResultWithPostInfo {359 let sender = ensure_signed(origin)?;360 let extra_fields = info.additional.len() as u32;361 ensure!(extra_fields <= T::MaxAdditionalFields::get(), Error::<T>::TooManyFields);362 let fd = <BalanceOf<T>>::from(extra_fields) * T::FieldDeposit::get();363364 let mut id = match <IdentityOf<T>>::get(&sender) {365 Some(mut id) => {366 367 id.judgements.retain(|j| j.1.is_sticky());368 id.info = *info;369 id370 },371 None => Registration {372 info: *info,373 judgements: BoundedVec::default(),374 deposit: Zero::zero(),375 },376 };377378 let old_deposit = id.deposit;379 id.deposit = T::BasicDeposit::get() + fd;380 if id.deposit > old_deposit {381 T::Currency::reserve(&sender, id.deposit - old_deposit)?;382 }383 if old_deposit > id.deposit {384 let err_amount = T::Currency::unreserve(&sender, old_deposit - id.deposit);385 debug_assert!(err_amount.is_zero());386 }387388 let judgements = id.judgements.len();389 <IdentityOf<T>>::insert(&sender, id);390 Self::deposit_event(Event::IdentitySet { who: sender });391392 Ok(Some(T::WeightInfo::set_identity(393 judgements as u32, 394 extra_fields, 395 ))396 .into())397 }398399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 #[pallet::call_index(2)]427 #[pallet::weight(T::WeightInfo::set_subs_old(T::MaxSubAccounts::get()) 428 .saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32)) 429 )]430 pub fn set_subs(431 origin: OriginFor<T>,432 subs: Vec<(T::AccountId, Data)>,433 ) -> DispatchResultWithPostInfo {434 let sender = ensure_signed(origin)?;435 ensure!(<IdentityOf<T>>::contains_key(&sender), Error::<T>::NotFound);436 ensure!(437 subs.len() <= T::MaxSubAccounts::get() as usize,438 Error::<T>::TooManySubAccounts439 );440441 let (old_deposit, old_ids) = <SubsOf<T>>::get(&sender);442 let new_deposit = T::SubAccountDeposit::get() * <BalanceOf<T>>::from(subs.len() as u32);443444 let not_other_sub =445 subs.iter().filter_map(|i| SuperOf::<T>::get(&i.0)).all(|i| i.0 == sender);446 ensure!(not_other_sub, Error::<T>::AlreadyClaimed);447448 if old_deposit < new_deposit {449 T::Currency::reserve(&sender, new_deposit - old_deposit)?;450 } else if old_deposit > new_deposit {451 let err_amount = T::Currency::unreserve(&sender, old_deposit - new_deposit);452 debug_assert!(err_amount.is_zero());453 }454 455456 for s in old_ids.iter() {457 <SuperOf<T>>::remove(s);458 }459 let mut ids = BoundedVec::<T::AccountId, T::MaxSubAccounts>::default();460 for (id, name) in subs {461 <SuperOf<T>>::insert(&id, (sender.clone(), name));462 ids.try_push(id).expect("subs length is less than T::MaxSubAccounts; qed");463 }464 let new_subs = ids.len();465466 if ids.is_empty() {467 <SubsOf<T>>::remove(&sender);468 } else {469 <SubsOf<T>>::insert(&sender, (new_deposit, ids));470 }471472 Ok(Some(473 T::WeightInfo::set_subs_old(old_ids.len() as u32) 474 475 .saturating_add(T::WeightInfo::set_subs_new(new_subs as u32)),476 )477 .into())478 }479480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 #[pallet::call_index(3)]499 #[pallet::weight(T::WeightInfo::clear_identity(500 T::MaxRegistrars::get(), 501 T::MaxSubAccounts::get(), 502 T::MaxAdditionalFields::get(), 503 ))]504 pub fn clear_identity(origin: OriginFor<T>) -> DispatchResultWithPostInfo {505 let sender = ensure_signed(origin)?;506507 let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&sender);508 let id = <IdentityOf<T>>::take(&sender).ok_or(Error::<T>::NotNamed)?;509 let deposit = id.total_deposit() + subs_deposit;510 for sub in sub_ids.iter() {511 <SuperOf<T>>::remove(sub);512 }513514 let err_amount = T::Currency::unreserve(&sender, deposit);515 debug_assert!(err_amount.is_zero());516517 Self::deposit_event(Event::IdentityCleared { who: sender, deposit });518519 Ok(Some(T::WeightInfo::clear_identity(520 id.judgements.len() as u32, 521 sub_ids.len() as u32, 522 id.info.additional.len() as u32, 523 ))524 .into())525 }526527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 #[pallet::call_index(4)]551 #[pallet::weight(T::WeightInfo::request_judgement(552 T::MaxRegistrars::get(), 553 T::MaxAdditionalFields::get(), 554 ))]555 pub fn request_judgement(556 origin: OriginFor<T>,557 #[pallet::compact] reg_index: RegistrarIndex,558 #[pallet::compact] max_fee: BalanceOf<T>,559 ) -> DispatchResultWithPostInfo {560 let sender = ensure_signed(origin)?;561 let registrars = <Registrars<T>>::get();562 let registrar = registrars563 .get(reg_index as usize)564 .and_then(Option::as_ref)565 .ok_or(Error::<T>::EmptyIndex)?;566 ensure!(max_fee >= registrar.fee, Error::<T>::FeeChanged);567 let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;568569 let item = (reg_index, Judgement::FeePaid(registrar.fee));570 match id.judgements.binary_search_by_key(®_index, |x| x.0) {571 Ok(i) =>572 if id.judgements[i].1.is_sticky() {573 return Err(Error::<T>::StickyJudgement.into())574 } else {575 id.judgements[i] = item576 },577 Err(i) =>578 id.judgements.try_insert(i, item).map_err(|_| Error::<T>::TooManyRegistrars)?,579 }580581 T::Currency::reserve(&sender, registrar.fee)?;582583 let judgements = id.judgements.len();584 let extra_fields = id.info.additional.len();585 <IdentityOf<T>>::insert(&sender, id);586587 Self::deposit_event(Event::JudgementRequested {588 who: sender,589 registrar_index: reg_index,590 });591592 Ok(Some(T::WeightInfo::request_judgement(judgements as u32, extra_fields as u32))593 .into())594 }595596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 #[pallet::call_index(5)]614 #[pallet::weight(T::WeightInfo::cancel_request(615 T::MaxRegistrars::get(), 616 T::MaxAdditionalFields::get(), 617 ))]618 pub fn cancel_request(619 origin: OriginFor<T>,620 reg_index: RegistrarIndex,621 ) -> DispatchResultWithPostInfo {622 let sender = ensure_signed(origin)?;623 let mut id = <IdentityOf<T>>::get(&sender).ok_or(Error::<T>::NoIdentity)?;624625 let pos = id626 .judgements627 .binary_search_by_key(®_index, |x| x.0)628 .map_err(|_| Error::<T>::NotFound)?;629 let fee = if let Judgement::FeePaid(fee) = id.judgements.remove(pos).1 {630 fee631 } else {632 return Err(Error::<T>::JudgementGiven.into())633 };634635 let err_amount = T::Currency::unreserve(&sender, fee);636 debug_assert!(err_amount.is_zero());637 let judgements = id.judgements.len();638 let extra_fields = id.info.additional.len();639 <IdentityOf<T>>::insert(&sender, id);640641 Self::deposit_event(Event::JudgementUnrequested {642 who: sender,643 registrar_index: reg_index,644 });645646 Ok(Some(T::WeightInfo::cancel_request(judgements as u32, extra_fields as u32)).into())647 }648649 650 651 652 653 654 655 656 657 658 659 660 661 662 #[pallet::call_index(6)]663 #[pallet::weight(T::WeightInfo::set_fee(T::MaxRegistrars::get()))] 664 pub fn set_fee(665 origin: OriginFor<T>,666 #[pallet::compact] index: RegistrarIndex,667 #[pallet::compact] fee: BalanceOf<T>,668 ) -> DispatchResultWithPostInfo {669 let who = ensure_signed(origin)?;670671 let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {672 rs.get_mut(index as usize)673 .and_then(|x| x.as_mut())674 .and_then(|r| {675 if r.account == who {676 r.fee = fee;677 Some(())678 } else {679 None680 }681 })682 .ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;683 Ok(rs.len())684 })?;685 Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) 686 }687688 689 690 691 692 693 694 695 696 697 698 699 700 701 #[pallet::call_index(7)]702 #[pallet::weight(T::WeightInfo::set_account_id(T::MaxRegistrars::get()))] 703 pub fn set_account_id(704 origin: OriginFor<T>,705 #[pallet::compact] index: RegistrarIndex,706 new: AccountIdLookupOf<T>,707 ) -> DispatchResultWithPostInfo {708 let who = ensure_signed(origin)?;709 let new = T::Lookup::lookup(new)?;710711 let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {712 rs.get_mut(index as usize)713 .and_then(|x| x.as_mut())714 .and_then(|r| {715 if r.account == who {716 r.account = new;717 Some(())718 } else {719 None720 }721 })722 .ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;723 Ok(rs.len())724 })?;725 Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) 726 }727728 729 730 731 732 733 734 735 736 737 738 739 740 741 #[pallet::call_index(8)]742 #[pallet::weight(T::WeightInfo::set_fields(T::MaxRegistrars::get()))] 743 pub fn set_fields(744 origin: OriginFor<T>,745 #[pallet::compact] index: RegistrarIndex,746 fields: IdentityFields,747 ) -> DispatchResultWithPostInfo {748 let who = ensure_signed(origin)?;749750 let registrars = <Registrars<T>>::mutate(|rs| -> Result<usize, DispatchError> {751 rs.get_mut(index as usize)752 .and_then(|x| x.as_mut())753 .and_then(|r| {754 if r.account == who {755 r.fields = fields;756 Some(())757 } else {758 None759 }760 })761 .ok_or_else(|| DispatchError::from(Error::<T>::InvalidIndex))?;762 Ok(rs.len())763 })?;764 Ok(Some(T::WeightInfo::set_fields(765 registrars as u32, 766 ))767 .into())768 }769770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 #[pallet::call_index(9)]791 #[pallet::weight(T::WeightInfo::provide_judgement(792 T::MaxRegistrars::get(), 793 T::MaxAdditionalFields::get(), 794 ))]795 pub fn provide_judgement(796 origin: OriginFor<T>,797 #[pallet::compact] reg_index: RegistrarIndex,798 target: AccountIdLookupOf<T>,799 judgement: Judgement<BalanceOf<T>>,800 identity: T::Hash,801 ) -> DispatchResultWithPostInfo {802 let sender = ensure_signed(origin)?;803 let target = T::Lookup::lookup(target)?;804 ensure!(!judgement.has_deposit(), Error::<T>::InvalidJudgement);805 <Registrars<T>>::get()806 .get(reg_index as usize)807 .and_then(Option::as_ref)808 .filter(|r| r.account == sender)809 .ok_or(Error::<T>::InvalidIndex)?;810 let mut id = <IdentityOf<T>>::get(&target).ok_or(Error::<T>::InvalidTarget)?;811812 if T::Hashing::hash_of(&id.info) != identity {813 return Err(Error::<T>::JudgementForDifferentIdentity.into())814 }815816 let item = (reg_index, judgement);817 match id.judgements.binary_search_by_key(®_index, |x| x.0) {818 Ok(position) => {819 if let Judgement::FeePaid(fee) = id.judgements[position].1 {820 T::Currency::repatriate_reserved(821 &target,822 &sender,823 fee,824 BalanceStatus::Free,825 )826 .map_err(|_| Error::<T>::JudgementPaymentFailed)?;827 }828 id.judgements[position] = item829 },830 Err(position) => id831 .judgements832 .try_insert(position, item)833 .map_err(|_| Error::<T>::TooManyRegistrars)?,834 }835836 let judgements = id.judgements.len();837 let extra_fields = id.info.additional.len();838 <IdentityOf<T>>::insert(&target, id);839 Self::deposit_event(Event::JudgementGiven { target, registrar_index: reg_index });840841 Ok(Some(T::WeightInfo::provide_judgement(judgements as u32, extra_fields as u32))842 .into())843 }844845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 #[pallet::call_index(10)]865 #[pallet::weight(T::WeightInfo::kill_identity(866 T::MaxRegistrars::get(), 867 T::MaxSubAccounts::get(), 868 T::MaxAdditionalFields::get(), 869 ))]870 pub fn kill_identity(871 origin: OriginFor<T>,872 target: AccountIdLookupOf<T>,873 ) -> DispatchResultWithPostInfo {874 T::ForceOrigin::ensure_origin(origin)?;875876 877 let target = T::Lookup::lookup(target)?;878 879 let (subs_deposit, sub_ids) = <SubsOf<T>>::take(&target);880 let id = <IdentityOf<T>>::take(&target).ok_or(Error::<T>::NotNamed)?;881 let deposit = id.total_deposit() + subs_deposit;882 for sub in sub_ids.iter() {883 <SuperOf<T>>::remove(sub);884 }885 886 T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit).0);887888 Self::deposit_event(Event::IdentityKilled { who: target, deposit });889890 Ok(Some(T::WeightInfo::kill_identity(891 id.judgements.len() as u32, 892 sub_ids.len() as u32, 893 id.info.additional.len() as u32, 894 ))895 .into())896 }897898 899 900 901 902 903 904 905 #[pallet::call_index(11)]906 #[pallet::weight(T::WeightInfo::add_sub(T::MaxSubAccounts::get()))]907 pub fn add_sub(908 origin: OriginFor<T>,909 sub: AccountIdLookupOf<T>,910 data: Data,911 ) -> DispatchResult {912 let sender = ensure_signed(origin)?;913 let sub = T::Lookup::lookup(sub)?;914 ensure!(IdentityOf::<T>::contains_key(&sender), Error::<T>::NoIdentity);915916 917 ensure!(!SuperOf::<T>::contains_key(&sub), Error::<T>::AlreadyClaimed);918919 SubsOf::<T>::try_mutate(&sender, |(ref mut subs_deposit, ref mut sub_ids)| {920 921 ensure!(922 sub_ids.len() < T::MaxSubAccounts::get() as usize,923 Error::<T>::TooManySubAccounts924 );925 let deposit = T::SubAccountDeposit::get();926 T::Currency::reserve(&sender, deposit)?;927928 SuperOf::<T>::insert(&sub, (sender.clone(), data));929 sub_ids.try_push(sub.clone()).expect("sub ids length checked above; qed");930 *subs_deposit = subs_deposit.saturating_add(deposit);931932 Self::deposit_event(Event::SubIdentityAdded { sub, main: sender.clone(), deposit });933 Ok(())934 })935 }936937 938 939 940 941 #[pallet::call_index(12)]942 #[pallet::weight(T::WeightInfo::rename_sub(T::MaxSubAccounts::get()))]943 pub fn rename_sub(944 origin: OriginFor<T>,945 sub: AccountIdLookupOf<T>,946 data: Data,947 ) -> DispatchResult {948 let sender = ensure_signed(origin)?;949 let sub = T::Lookup::lookup(sub)?;950 ensure!(IdentityOf::<T>::contains_key(&sender), Error::<T>::NoIdentity);951 ensure!(SuperOf::<T>::get(&sub).map_or(false, |x| x.0 == sender), Error::<T>::NotOwned);952 SuperOf::<T>::insert(&sub, (sender, data));953 Ok(())954 }955956 957 958 959 960 961 962 963 #[pallet::call_index(13)]964 #[pallet::weight(T::WeightInfo::remove_sub(T::MaxSubAccounts::get()))]965 pub fn remove_sub(origin: OriginFor<T>, sub: AccountIdLookupOf<T>) -> DispatchResult {966 let sender = ensure_signed(origin)?;967 ensure!(IdentityOf::<T>::contains_key(&sender), Error::<T>::NoIdentity);968 let sub = T::Lookup::lookup(sub)?;969 let (sup, _) = SuperOf::<T>::get(&sub).ok_or(Error::<T>::NotSub)?;970 ensure!(sup == sender, Error::<T>::NotOwned);971 SuperOf::<T>::remove(&sub);972 SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {973 sub_ids.retain(|x| x != &sub);974 let deposit = T::SubAccountDeposit::get().min(*subs_deposit);975 *subs_deposit -= deposit;976 let err_amount = T::Currency::unreserve(&sender, deposit);977 debug_assert!(err_amount.is_zero());978 Self::deposit_event(Event::SubIdentityRemoved { sub, main: sender, deposit });979 });980 Ok(())981 }982983 984 985 986 987 988 989 990 991 992 993 #[pallet::call_index(14)]994 #[pallet::weight(T::WeightInfo::quit_sub(T::MaxSubAccounts::get()))]995 pub fn quit_sub(origin: OriginFor<T>) -> DispatchResult {996 let sender = ensure_signed(origin)?;997 let (sup, _) = SuperOf::<T>::take(&sender).ok_or(Error::<T>::NotSub)?;998 SubsOf::<T>::mutate(&sup, |(ref mut subs_deposit, ref mut sub_ids)| {999 sub_ids.retain(|x| x != &sender);1000 let deposit = T::SubAccountDeposit::get().min(*subs_deposit);1001 *subs_deposit -= deposit;1002 let _ =1003 T::Currency::repatriate_reserved(&sup, &sender, deposit, BalanceStatus::Free);1004 Self::deposit_event(Event::SubIdentityRevoked {1005 sub: sender,1006 main: sup.clone(),1007 deposit,1008 });1009 });1010 Ok(())1011 }1012 }1013}10141015impl<T: Config> Pallet<T> {1016 1017 pub fn subs(who: &T::AccountId) -> Vec<(T::AccountId, Data)> {1018 SubsOf::<T>::get(who)1019 .11020 .into_iter()1021 .filter_map(|a| SuperOf::<T>::get(&a).map(|x| (a, x.1)))1022 .collect()1023 }10241025 1026 pub fn has_identity(who: &T::AccountId, fields: u64) -> bool {1027 IdentityOf::<T>::get(who)1028 .map_or(false, |registration| (registration.info.fields().0.bits() & fields) == fields)1029 }1030}