1#![cfg_attr(not(feature = "std"), no_std)]
145mod benchmarking;
146mod impl_currency;
147mod impl_fungible;
148pub mod migration;
149mod tests;
150mod types;
151pub mod weights;
152
153extern crate alloc;
154
155use alloc::vec::Vec;
156use codec::{Codec, MaxEncodedLen};
157use core::{cmp, fmt::Debug, mem, result};
158use frame_support::{
159 ensure,
160 pallet_prelude::DispatchResult,
161 traits::{
162 tokens::{
163 fungible, BalanceStatus as Status, DepositConsequence,
164 Fortitude::{self, Force, Polite},
165 IdAmount,
166 Preservation::{Expendable, Preserve, Protect},
167 WithdrawConsequence,
168 },
169 Currency, Defensive, Get, OnUnbalanced, ReservableCurrency, StoredMap,
170 },
171 BoundedSlice, WeakBoundedVec,
172};
173use frame_system as system;
174pub use impl_currency::{NegativeImbalance, PositiveImbalance};
175use scale_info::TypeInfo;
176use sp_runtime::{
177 traits::{
178 AtLeast32BitUnsigned, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Saturating,
179 StaticLookup, Zero,
180 },
181 ArithmeticError, DispatchError, FixedPointOperand, Perbill, RuntimeDebug, TokenError,
182};
183pub use types::{
184 AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, Reasons, ReserveData,
185};
186pub use weights::WeightInfo;
187
188pub use pallet::*;
189
190const LOG_TARGET: &str = "runtime::balances";
191
192type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
193
194#[frame_support::pallet]
195pub mod pallet {
196 use super::*;
197 use frame_support::{
198 pallet_prelude::*,
199 traits::{fungible::Credit, tokens::Precision, VariantCount, VariantCountOf},
200 };
201 use frame_system::pallet_prelude::*;
202
203 pub type CreditOf<T, I> = Credit<<T as frame_system::Config>::AccountId, Pallet<T, I>>;
204
205 pub mod config_preludes {
207 use super::*;
208 use frame_support::{derive_impl, traits::ConstU64};
209
210 pub struct TestDefaultConfig;
211
212 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
213 impl frame_system::DefaultConfig for TestDefaultConfig {}
214
215 #[frame_support::register_default_impl(TestDefaultConfig)]
216 impl DefaultConfig for TestDefaultConfig {
217 #[inject_runtime_type]
218 type RuntimeEvent = ();
219 #[inject_runtime_type]
220 type RuntimeHoldReason = ();
221 #[inject_runtime_type]
222 type RuntimeFreezeReason = ();
223
224 type Balance = u64;
225 type ExistentialDeposit = ConstU64<1>;
226
227 type ReserveIdentifier = ();
228 type FreezeIdentifier = Self::RuntimeFreezeReason;
229
230 type DustRemoval = ();
231
232 type MaxLocks = ConstU32<100>;
233 type MaxReserves = ConstU32<100>;
234 type MaxFreezes = VariantCountOf<Self::RuntimeFreezeReason>;
235
236 type WeightInfo = ();
237 type DoneSlashHandler = ();
238 }
239 }
240
241 #[pallet::config(with_default)]
242 pub trait Config<I: 'static = ()>: frame_system::Config {
243 #[pallet::no_default_bounds]
245 type RuntimeEvent: From<Event<Self, I>>
246 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
247
248 #[pallet::no_default_bounds]
250 type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount;
251
252 #[pallet::no_default_bounds]
254 type RuntimeFreezeReason: VariantCount;
255
256 type WeightInfo: WeightInfo;
258
259 type Balance: Parameter
261 + Member
262 + AtLeast32BitUnsigned
263 + Codec
264 + Default
265 + Copy
266 + MaybeSerializeDeserialize
267 + Debug
268 + MaxEncodedLen
269 + TypeInfo
270 + FixedPointOperand;
271
272 #[pallet::no_default_bounds]
274 type DustRemoval: OnUnbalanced<CreditOf<Self, I>>;
275
276 #[pallet::constant]
285 #[pallet::no_default_bounds]
286 type ExistentialDeposit: Get<Self::Balance>;
287
288 #[pallet::no_default]
290 type AccountStore: StoredMap<Self::AccountId, AccountData<Self::Balance>>;
291
292 type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
296
297 type FreezeIdentifier: Parameter + Member + MaxEncodedLen + Copy;
299
300 #[pallet::constant]
305 type MaxLocks: Get<u32>;
306
307 #[pallet::constant]
311 type MaxReserves: Get<u32>;
312
313 #[pallet::constant]
315 type MaxFreezes: Get<u32>;
316
317 type DoneSlashHandler: fungible::hold::DoneSlash<
320 Self::RuntimeHoldReason,
321 Self::AccountId,
322 Self::Balance,
323 >;
324 }
325
326 const STORAGE_VERSION: frame_support::traits::StorageVersion =
328 frame_support::traits::StorageVersion::new(1);
329
330 #[pallet::pallet]
331 #[pallet::storage_version(STORAGE_VERSION)]
332 pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
333
334 #[pallet::event]
335 #[pallet::generate_deposit(pub(super) fn deposit_event)]
336 pub enum Event<T: Config<I>, I: 'static = ()> {
337 Endowed { account: T::AccountId, free_balance: T::Balance },
339 DustLost { account: T::AccountId, amount: T::Balance },
342 Transfer { from: T::AccountId, to: T::AccountId, amount: T::Balance },
344 BalanceSet { who: T::AccountId, free: T::Balance },
346 Reserved { who: T::AccountId, amount: T::Balance },
348 Unreserved { who: T::AccountId, amount: T::Balance },
350 ReserveRepatriated {
353 from: T::AccountId,
354 to: T::AccountId,
355 amount: T::Balance,
356 destination_status: Status,
357 },
358 Deposit { who: T::AccountId, amount: T::Balance },
360 Withdraw { who: T::AccountId, amount: T::Balance },
362 Slashed { who: T::AccountId, amount: T::Balance },
364 Minted { who: T::AccountId, amount: T::Balance },
366 Burned { who: T::AccountId, amount: T::Balance },
368 Suspended { who: T::AccountId, amount: T::Balance },
370 Restored { who: T::AccountId, amount: T::Balance },
372 Upgraded { who: T::AccountId },
374 Issued { amount: T::Balance },
376 Rescinded { amount: T::Balance },
378 Locked { who: T::AccountId, amount: T::Balance },
380 Unlocked { who: T::AccountId, amount: T::Balance },
382 Frozen { who: T::AccountId, amount: T::Balance },
384 Thawed { who: T::AccountId, amount: T::Balance },
386 TotalIssuanceForced { old: T::Balance, new: T::Balance },
388 }
389
390 #[pallet::error]
391 pub enum Error<T, I = ()> {
392 VestingBalance,
394 LiquidityRestrictions,
396 InsufficientBalance,
398 ExistentialDeposit,
400 Expendability,
402 ExistingVestingSchedule,
404 DeadAccount,
406 TooManyReserves,
408 TooManyHolds,
410 TooManyFreezes,
412 IssuanceDeactivated,
414 DeltaZero,
416 }
417
418 #[pallet::storage]
420 #[pallet::whitelist_storage]
421 pub type TotalIssuance<T: Config<I>, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>;
422
423 #[pallet::storage]
425 #[pallet::whitelist_storage]
426 pub type InactiveIssuance<T: Config<I>, I: 'static = ()> =
427 StorageValue<_, T::Balance, ValueQuery>;
428
429 #[pallet::storage]
454 pub type Account<T: Config<I>, I: 'static = ()> =
455 StorageMap<_, Blake2_128Concat, T::AccountId, AccountData<T::Balance>, ValueQuery>;
456
457 #[pallet::storage]
462 pub type Locks<T: Config<I>, I: 'static = ()> = StorageMap<
463 _,
464 Blake2_128Concat,
465 T::AccountId,
466 WeakBoundedVec<BalanceLock<T::Balance>, T::MaxLocks>,
467 ValueQuery,
468 >;
469
470 #[pallet::storage]
474 pub type Reserves<T: Config<I>, I: 'static = ()> = StorageMap<
475 _,
476 Blake2_128Concat,
477 T::AccountId,
478 BoundedVec<ReserveData<T::ReserveIdentifier, T::Balance>, T::MaxReserves>,
479 ValueQuery,
480 >;
481
482 #[pallet::storage]
484 pub type Holds<T: Config<I>, I: 'static = ()> = StorageMap<
485 _,
486 Blake2_128Concat,
487 T::AccountId,
488 BoundedVec<
489 IdAmount<T::RuntimeHoldReason, T::Balance>,
490 VariantCountOf<T::RuntimeHoldReason>,
491 >,
492 ValueQuery,
493 >;
494
495 #[pallet::storage]
497 pub type Freezes<T: Config<I>, I: 'static = ()> = StorageMap<
498 _,
499 Blake2_128Concat,
500 T::AccountId,
501 BoundedVec<IdAmount<T::FreezeIdentifier, T::Balance>, T::MaxFreezes>,
502 ValueQuery,
503 >;
504
505 #[pallet::genesis_config]
506 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
507 pub balances: Vec<(T::AccountId, T::Balance)>,
508 }
509
510 impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
511 fn default() -> Self {
512 Self { balances: Default::default() }
513 }
514 }
515
516 #[pallet::genesis_build]
517 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
518 fn build(&self) {
519 let total = self.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n);
520
521 <TotalIssuance<T, I>>::put(total);
522
523 for (_, balance) in &self.balances {
524 assert!(
525 *balance >= <T as Config<I>>::ExistentialDeposit::get(),
526 "the balance of any account should always be at least the existential deposit.",
527 )
528 }
529
530 let endowed_accounts = self
532 .balances
533 .iter()
534 .map(|(x, _)| x)
535 .cloned()
536 .collect::<alloc::collections::btree_set::BTreeSet<_>>();
537
538 assert!(
539 endowed_accounts.len() == self.balances.len(),
540 "duplicate balances in genesis."
541 );
542
543 for &(ref who, free) in self.balances.iter() {
544 frame_system::Pallet::<T>::inc_providers(who);
545 assert!(T::AccountStore::insert(who, AccountData { free, ..Default::default() })
546 .is_ok());
547 }
548 }
549 }
550
551 #[pallet::hooks]
552 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
553 fn integrity_test() {
554 #[cfg(not(feature = "insecure_zero_ed"))]
555 assert!(
556 !<T as Config<I>>::ExistentialDeposit::get().is_zero(),
557 "The existential deposit must be greater than zero!"
558 );
559
560 assert!(
561 T::MaxFreezes::get() >= <T::RuntimeFreezeReason as VariantCount>::VARIANT_COUNT,
562 "MaxFreezes should be greater than or equal to the number of freeze reasons: {} < {}",
563 T::MaxFreezes::get(), <T::RuntimeFreezeReason as VariantCount>::VARIANT_COUNT,
564 );
565 }
566
567 #[cfg(feature = "try-runtime")]
568 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
569 Holds::<T, I>::iter_keys().try_for_each(|k| {
570 if Holds::<T, I>::decode_len(k).unwrap_or(0) >
571 T::RuntimeHoldReason::VARIANT_COUNT as usize
572 {
573 Err("Found `Hold` with too many elements")
574 } else {
575 Ok(())
576 }
577 })?;
578
579 Freezes::<T, I>::iter_keys().try_for_each(|k| {
580 if Freezes::<T, I>::decode_len(k).unwrap_or(0) > T::MaxFreezes::get() as usize {
581 Err("Found `Freeze` with too many elements")
582 } else {
583 Ok(())
584 }
585 })?;
586
587 Ok(())
588 }
589 }
590
591 #[pallet::call(weight(<T as Config<I>>::WeightInfo))]
592 impl<T: Config<I>, I: 'static> Pallet<T, I> {
593 #[pallet::call_index(0)]
601 pub fn transfer_allow_death(
602 origin: OriginFor<T>,
603 dest: AccountIdLookupOf<T>,
604 #[pallet::compact] value: T::Balance,
605 ) -> DispatchResult {
606 let source = ensure_signed(origin)?;
607 let dest = T::Lookup::lookup(dest)?;
608 <Self as fungible::Mutate<_>>::transfer(&source, &dest, value, Expendable)?;
609 Ok(())
610 }
611
612 #[pallet::call_index(2)]
615 pub fn force_transfer(
616 origin: OriginFor<T>,
617 source: AccountIdLookupOf<T>,
618 dest: AccountIdLookupOf<T>,
619 #[pallet::compact] value: T::Balance,
620 ) -> DispatchResult {
621 ensure_root(origin)?;
622 let source = T::Lookup::lookup(source)?;
623 let dest = T::Lookup::lookup(dest)?;
624 <Self as fungible::Mutate<_>>::transfer(&source, &dest, value, Expendable)?;
625 Ok(())
626 }
627
628 #[pallet::call_index(3)]
635 pub fn transfer_keep_alive(
636 origin: OriginFor<T>,
637 dest: AccountIdLookupOf<T>,
638 #[pallet::compact] value: T::Balance,
639 ) -> DispatchResult {
640 let source = ensure_signed(origin)?;
641 let dest = T::Lookup::lookup(dest)?;
642 <Self as fungible::Mutate<_>>::transfer(&source, &dest, value, Preserve)?;
643 Ok(())
644 }
645
646 #[pallet::call_index(4)]
662 pub fn transfer_all(
663 origin: OriginFor<T>,
664 dest: AccountIdLookupOf<T>,
665 keep_alive: bool,
666 ) -> DispatchResult {
667 let transactor = ensure_signed(origin)?;
668 let keep_alive = if keep_alive { Preserve } else { Expendable };
669 let reducible_balance = <Self as fungible::Inspect<_>>::reducible_balance(
670 &transactor,
671 keep_alive,
672 Fortitude::Polite,
673 );
674 let dest = T::Lookup::lookup(dest)?;
675 <Self as fungible::Mutate<_>>::transfer(
676 &transactor,
677 &dest,
678 reducible_balance,
679 keep_alive,
680 )?;
681 Ok(())
682 }
683
684 #[pallet::call_index(5)]
688 pub fn force_unreserve(
689 origin: OriginFor<T>,
690 who: AccountIdLookupOf<T>,
691 amount: T::Balance,
692 ) -> DispatchResult {
693 ensure_root(origin)?;
694 let who = T::Lookup::lookup(who)?;
695 let _leftover = <Self as ReservableCurrency<_>>::unreserve(&who, amount);
696 Ok(())
697 }
698
699 #[pallet::call_index(6)]
708 #[pallet::weight(T::WeightInfo::upgrade_accounts(who.len() as u32))]
709 pub fn upgrade_accounts(
710 origin: OriginFor<T>,
711 who: Vec<T::AccountId>,
712 ) -> DispatchResultWithPostInfo {
713 ensure_signed(origin)?;
714 if who.is_empty() {
715 return Ok(Pays::Yes.into())
716 }
717 let mut upgrade_count = 0;
718 for i in &who {
719 let upgraded = Self::ensure_upgraded(i);
720 if upgraded {
721 upgrade_count.saturating_inc();
722 }
723 }
724 let proportion_upgraded = Perbill::from_rational(upgrade_count, who.len() as u32);
725 if proportion_upgraded >= Perbill::from_percent(90) {
726 Ok(Pays::No.into())
727 } else {
728 Ok(Pays::Yes.into())
729 }
730 }
731
732 #[pallet::call_index(8)]
736 #[pallet::weight(
737 T::WeightInfo::force_set_balance_creating() .max(T::WeightInfo::force_set_balance_killing()) )]
740 pub fn force_set_balance(
741 origin: OriginFor<T>,
742 who: AccountIdLookupOf<T>,
743 #[pallet::compact] new_free: T::Balance,
744 ) -> DispatchResult {
745 ensure_root(origin)?;
746 let who = T::Lookup::lookup(who)?;
747 let existential_deposit = Self::ed();
748
749 let wipeout = new_free < existential_deposit;
750 let new_free = if wipeout { Zero::zero() } else { new_free };
751
752 let old_free = Self::mutate_account_handling_dust(&who, |account| {
754 let old_free = account.free;
755 account.free = new_free;
756 old_free
757 })?;
758
759 if new_free > old_free {
762 mem::drop(PositiveImbalance::<T, I>::new(new_free - old_free));
763 } else if new_free < old_free {
764 mem::drop(NegativeImbalance::<T, I>::new(old_free - new_free));
765 }
766
767 Self::deposit_event(Event::BalanceSet { who, free: new_free });
768 Ok(())
769 }
770
771 #[doc = docify::embed!("./src/tests/dispatchable_tests.rs", force_adjust_total_issuance_example)]
777 #[pallet::call_index(9)]
778 #[pallet::weight(T::WeightInfo::force_adjust_total_issuance())]
779 pub fn force_adjust_total_issuance(
780 origin: OriginFor<T>,
781 direction: AdjustmentDirection,
782 #[pallet::compact] delta: T::Balance,
783 ) -> DispatchResult {
784 ensure_root(origin)?;
785
786 ensure!(delta > Zero::zero(), Error::<T, I>::DeltaZero);
787
788 let old = TotalIssuance::<T, I>::get();
789 let new = match direction {
790 AdjustmentDirection::Increase => old.saturating_add(delta),
791 AdjustmentDirection::Decrease => old.saturating_sub(delta),
792 };
793
794 ensure!(InactiveIssuance::<T, I>::get() <= new, Error::<T, I>::IssuanceDeactivated);
795 TotalIssuance::<T, I>::set(new);
796
797 Self::deposit_event(Event::<T, I>::TotalIssuanceForced { old, new });
798
799 Ok(())
800 }
801
802 #[pallet::call_index(10)]
810 #[pallet::weight(if *keep_alive {T::WeightInfo::burn_allow_death() } else {T::WeightInfo::burn_keep_alive()})]
811 pub fn burn(
812 origin: OriginFor<T>,
813 #[pallet::compact] value: T::Balance,
814 keep_alive: bool,
815 ) -> DispatchResult {
816 let source = ensure_signed(origin)?;
817 let preservation = if keep_alive { Preserve } else { Expendable };
818 <Self as fungible::Mutate<_>>::burn_from(
819 &source,
820 value,
821 preservation,
822 Precision::Exact,
823 Polite,
824 )?;
825 Ok(())
826 }
827 }
828
829 impl<T: Config<I>, I: 'static> Pallet<T, I> {
830 pub fn total_issuance() -> T::Balance {
832 TotalIssuance::<T, I>::get()
833 }
834
835 pub fn inactive_issuance() -> T::Balance {
837 InactiveIssuance::<T, I>::get()
838 }
839
840 pub fn locks(who: &T::AccountId) -> WeakBoundedVec<BalanceLock<T::Balance>, T::MaxLocks> {
842 Locks::<T, I>::get(who)
843 }
844
845 pub fn reserves(
847 who: &T::AccountId,
848 ) -> BoundedVec<ReserveData<T::ReserveIdentifier, T::Balance>, T::MaxReserves> {
849 Reserves::<T, I>::get(who)
850 }
851
852 fn ed() -> T::Balance {
853 T::ExistentialDeposit::get()
854 }
855 pub fn ensure_upgraded(who: &T::AccountId) -> bool {
859 let mut a = T::AccountStore::get(who);
860 if a.flags.is_new_logic() {
861 return false
862 }
863 a.flags.set_new_logic();
864 if !a.reserved.is_zero() && a.frozen.is_zero() {
865 if system::Pallet::<T>::providers(who) == 0 {
866 log::warn!(
870 target: LOG_TARGET,
871 "account with a non-zero reserve balance has no provider refs, account_id: '{:?}'.",
872 who
873 );
874 a.free = a.free.max(Self::ed());
875 system::Pallet::<T>::inc_providers(who);
876 }
877 let _ = system::Pallet::<T>::inc_consumers_without_limit(who).defensive();
878 }
879 let _ = T::AccountStore::try_mutate_exists(who, |account| -> DispatchResult {
881 *account = Some(a);
882 Ok(())
883 });
884 Self::deposit_event(Event::Upgraded { who: who.clone() });
885 return true
886 }
887
888 pub fn free_balance(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
890 Self::account(who.borrow()).free
891 }
892
893 pub fn usable_balance(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
896 <Self as fungible::Inspect<_>>::reducible_balance(who.borrow(), Expendable, Polite)
897 }
898
899 pub fn usable_balance_for_fees(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
904 <Self as fungible::Inspect<_>>::reducible_balance(who.borrow(), Protect, Polite)
905 }
906
907 pub fn reserved_balance(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
909 Self::account(who.borrow()).reserved
910 }
911
912 pub(crate) fn account(who: &T::AccountId) -> AccountData<T::Balance> {
914 T::AccountStore::get(who)
915 }
916
917 pub(crate) fn mutate_account_handling_dust<R>(
929 who: &T::AccountId,
930 f: impl FnOnce(&mut AccountData<T::Balance>) -> R,
931 ) -> Result<R, DispatchError> {
932 let (r, maybe_dust) = Self::mutate_account(who, f)?;
933 if let Some(dust) = maybe_dust {
934 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
935 }
936 Ok(r)
937 }
938
939 pub(crate) fn try_mutate_account_handling_dust<R, E: From<DispatchError>>(
951 who: &T::AccountId,
952 f: impl FnOnce(&mut AccountData<T::Balance>, bool) -> Result<R, E>,
953 ) -> Result<R, E> {
954 let (r, maybe_dust) = Self::try_mutate_account(who, f)?;
955 if let Some(dust) = maybe_dust {
956 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
957 }
958 Ok(r)
959 }
960
961 pub(crate) fn mutate_account<R>(
974 who: &T::AccountId,
975 f: impl FnOnce(&mut AccountData<T::Balance>) -> R,
976 ) -> Result<(R, Option<T::Balance>), DispatchError> {
977 Self::try_mutate_account(who, |a, _| -> Result<R, DispatchError> { Ok(f(a)) })
978 }
979
980 #[cfg(not(feature = "insecure_zero_ed"))]
983 fn have_providers_or_no_zero_ed(_: &T::AccountId) -> bool {
984 true
985 }
986
987 #[cfg(feature = "insecure_zero_ed")]
990 fn have_providers_or_no_zero_ed(who: &T::AccountId) -> bool {
991 frame_system::Pallet::<T>::providers(who) > 0
992 }
993
994 pub(crate) fn try_mutate_account<R, E: From<DispatchError>>(
1008 who: &T::AccountId,
1009 f: impl FnOnce(&mut AccountData<T::Balance>, bool) -> Result<R, E>,
1010 ) -> Result<(R, Option<T::Balance>), E> {
1011 Self::ensure_upgraded(who);
1012 let result = T::AccountStore::try_mutate_exists(who, |maybe_account| {
1013 let is_new = maybe_account.is_none();
1014 let mut account = maybe_account.take().unwrap_or_default();
1015 let did_provide =
1016 account.free >= Self::ed() && Self::have_providers_or_no_zero_ed(who);
1017 let did_consume =
1018 !is_new && (!account.reserved.is_zero() || !account.frozen.is_zero());
1019
1020 let result = f(&mut account, is_new)?;
1021
1022 let does_provide = account.free >= Self::ed();
1023 let does_consume = !account.reserved.is_zero() || !account.frozen.is_zero();
1024
1025 if !did_provide && does_provide {
1026 frame_system::Pallet::<T>::inc_providers(who);
1027 }
1028 if did_consume && !does_consume {
1029 frame_system::Pallet::<T>::dec_consumers(who);
1030 }
1031 if !did_consume && does_consume {
1032 frame_system::Pallet::<T>::inc_consumers(who)?;
1033 }
1034 if does_consume && frame_system::Pallet::<T>::consumers(who) == 0 {
1035 log::error!(target: LOG_TARGET, "Defensively bumping a consumer ref.");
1039 frame_system::Pallet::<T>::inc_consumers(who)?;
1040 }
1041 if did_provide && !does_provide {
1042 frame_system::Pallet::<T>::dec_providers(who).inspect_err(|_| {
1044 if did_consume && !does_consume {
1046 let _ = frame_system::Pallet::<T>::inc_consumers(who).defensive();
1047 }
1048 if !did_consume && does_consume {
1049 let _ = frame_system::Pallet::<T>::dec_consumers(who);
1050 }
1051 })?;
1052 }
1053
1054 let maybe_endowed = if is_new { Some(account.free) } else { None };
1055
1056 let ed = Self::ed();
1068 let maybe_dust = if account.free < ed && account.reserved.is_zero() {
1069 if account.free.is_zero() {
1070 None
1071 } else {
1072 Some(account.free)
1073 }
1074 } else {
1075 assert!(
1076 account.free.is_zero() || account.free >= ed || !account.reserved.is_zero()
1077 );
1078 *maybe_account = Some(account);
1079 None
1080 };
1081 Ok((maybe_endowed, maybe_dust, result))
1082 });
1083 result.map(|(maybe_endowed, maybe_dust, result)| {
1084 if let Some(endowed) = maybe_endowed {
1085 Self::deposit_event(Event::Endowed {
1086 account: who.clone(),
1087 free_balance: endowed,
1088 });
1089 }
1090 if let Some(amount) = maybe_dust {
1091 Pallet::<T, I>::deposit_event(Event::DustLost { account: who.clone(), amount });
1092 }
1093 (result, maybe_dust)
1094 })
1095 }
1096
1097 pub(crate) fn update_locks(who: &T::AccountId, locks: &[BalanceLock<T::Balance>]) {
1099 let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from(
1100 locks.to_vec(),
1101 Some("Balances Update Locks"),
1102 );
1103
1104 if locks.len() as u32 > T::MaxLocks::get() {
1105 log::warn!(
1106 target: LOG_TARGET,
1107 "Warning: A user has more currency locks than expected. \
1108 A runtime configuration adjustment may be needed."
1109 );
1110 }
1111 let freezes = Freezes::<T, I>::get(who);
1112 let mut prev_frozen = Zero::zero();
1113 let mut after_frozen = Zero::zero();
1114 let res = Self::mutate_account(who, |b| {
1117 prev_frozen = b.frozen;
1118 b.frozen = Zero::zero();
1119 for l in locks.iter() {
1120 b.frozen = b.frozen.max(l.amount);
1121 }
1122 for l in freezes.iter() {
1123 b.frozen = b.frozen.max(l.amount);
1124 }
1125 after_frozen = b.frozen;
1126 });
1127 debug_assert!(res.is_ok());
1128 if let Ok((_, maybe_dust)) = res {
1129 debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed");
1130 }
1131
1132 match locks.is_empty() {
1133 true => Locks::<T, I>::remove(who),
1134 false => Locks::<T, I>::insert(who, bounded_locks),
1135 }
1136
1137 if prev_frozen > after_frozen {
1138 let amount = prev_frozen.saturating_sub(after_frozen);
1139 Self::deposit_event(Event::Unlocked { who: who.clone(), amount });
1140 } else if after_frozen > prev_frozen {
1141 let amount = after_frozen.saturating_sub(prev_frozen);
1142 Self::deposit_event(Event::Locked { who: who.clone(), amount });
1143 }
1144 }
1145
1146 pub(crate) fn update_freezes(
1148 who: &T::AccountId,
1149 freezes: BoundedSlice<IdAmount<T::FreezeIdentifier, T::Balance>, T::MaxFreezes>,
1150 ) -> DispatchResult {
1151 let mut prev_frozen = Zero::zero();
1152 let mut after_frozen = Zero::zero();
1153 let (_, maybe_dust) = Self::mutate_account(who, |b| {
1154 prev_frozen = b.frozen;
1155 b.frozen = Zero::zero();
1156 for l in Locks::<T, I>::get(who).iter() {
1157 b.frozen = b.frozen.max(l.amount);
1158 }
1159 for l in freezes.iter() {
1160 b.frozen = b.frozen.max(l.amount);
1161 }
1162 after_frozen = b.frozen;
1163 })?;
1164 debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed");
1165 if freezes.is_empty() {
1166 Freezes::<T, I>::remove(who);
1167 } else {
1168 Freezes::<T, I>::insert(who, freezes);
1169 }
1170 if prev_frozen > after_frozen {
1171 let amount = prev_frozen.saturating_sub(after_frozen);
1172 Self::deposit_event(Event::Thawed { who: who.clone(), amount });
1173 } else if after_frozen > prev_frozen {
1174 let amount = after_frozen.saturating_sub(prev_frozen);
1175 Self::deposit_event(Event::Frozen { who: who.clone(), amount });
1176 }
1177 Ok(())
1178 }
1179
1180 pub(crate) fn do_transfer_reserved(
1187 slashed: &T::AccountId,
1188 beneficiary: &T::AccountId,
1189 value: T::Balance,
1190 precision: Precision,
1191 fortitude: Fortitude,
1192 status: Status,
1193 ) -> Result<T::Balance, DispatchError> {
1194 if value.is_zero() {
1195 return Ok(Zero::zero())
1196 }
1197
1198 let max = <Self as fungible::InspectHold<_>>::reducible_total_balance_on_hold(
1199 slashed, fortitude,
1200 );
1201 let actual = match precision {
1202 Precision::BestEffort => value.min(max),
1203 Precision::Exact => value,
1204 };
1205 ensure!(actual <= max, TokenError::FundsUnavailable);
1206 if slashed == beneficiary {
1207 return match status {
1208 Status::Free => Ok(actual.saturating_sub(Self::unreserve(slashed, actual))),
1209 Status::Reserved => Ok(actual),
1210 }
1211 }
1212
1213 let ((_, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account(
1214 beneficiary,
1215 |to_account, is_new| -> Result<((), Option<T::Balance>), DispatchError> {
1216 ensure!(!is_new, Error::<T, I>::DeadAccount);
1217 Self::try_mutate_account(slashed, |from_account, _| -> DispatchResult {
1218 match status {
1219 Status::Free =>
1220 to_account.free = to_account
1221 .free
1222 .checked_add(&actual)
1223 .ok_or(ArithmeticError::Overflow)?,
1224 Status::Reserved =>
1225 to_account.reserved = to_account
1226 .reserved
1227 .checked_add(&actual)
1228 .ok_or(ArithmeticError::Overflow)?,
1229 }
1230 from_account.reserved.saturating_reduce(actual);
1231 Ok(())
1232 })
1233 },
1234 )?;
1235
1236 if let Some(dust) = maybe_dust_1 {
1237 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
1238 }
1239 if let Some(dust) = maybe_dust_2 {
1240 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
1241 }
1242
1243 Self::deposit_event(Event::ReserveRepatriated {
1244 from: slashed.clone(),
1245 to: beneficiary.clone(),
1246 amount: actual,
1247 destination_status: status,
1248 });
1249 Ok(actual)
1250 }
1251 }
1252}