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::{
156 format,
157 string::{String, ToString},
158 vec::Vec,
159};
160use codec::{Codec, MaxEncodedLen};
161use core::{cmp, fmt::Debug, mem, result};
162use frame_support::{
163 ensure,
164 pallet_prelude::DispatchResult,
165 traits::{
166 tokens::{
167 fungible, BalanceStatus as Status, DepositConsequence,
168 Fortitude::{self, Force, Polite},
169 IdAmount,
170 Preservation::{Expendable, Preserve, Protect},
171 WithdrawConsequence,
172 },
173 Currency, Defensive, Get, OnUnbalanced, ReservableCurrency, StoredMap,
174 },
175 BoundedSlice, WeakBoundedVec,
176};
177use frame_system as system;
178pub use impl_currency::{NegativeImbalance, PositiveImbalance};
179use scale_info::TypeInfo;
180use sp_core::{sr25519::Pair as SrPair, Pair};
181use sp_runtime::{
182 traits::{
183 AtLeast32BitUnsigned, CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Saturating,
184 StaticLookup, Zero,
185 },
186 ArithmeticError, DispatchError, FixedPointOperand, Perbill, TokenError,
187};
188
189pub use types::{
190 AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, Reasons, ReserveData,
191};
192pub use weights::WeightInfo;
193
194pub use pallet::*;
195
196const LOG_TARGET: &str = "runtime::balances";
197
198const DEFAULT_ADDRESS_URI: &str = "//Sender//{}";
200
201type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
202
203#[frame_support::pallet]
204pub mod pallet {
205 use super::*;
206 use codec::HasCompact;
207 use frame_support::{
208 pallet_prelude::*,
209 traits::{fungible::Credit, tokens::Precision, VariantCount, VariantCountOf},
210 };
211 use frame_system::pallet_prelude::*;
212
213 pub type CreditOf<T, I> = Credit<<T as frame_system::Config>::AccountId, Pallet<T, I>>;
214
215 pub mod config_preludes {
217 use super::*;
218 use frame_support::derive_impl;
219
220 pub struct TestDefaultConfig;
221
222 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
223 impl frame_system::DefaultConfig for TestDefaultConfig {}
224
225 #[frame_support::register_default_impl(TestDefaultConfig)]
226 impl DefaultConfig for TestDefaultConfig {
227 #[inject_runtime_type]
228 type RuntimeEvent = ();
229 #[inject_runtime_type]
230 type RuntimeHoldReason = ();
231 #[inject_runtime_type]
232 type RuntimeFreezeReason = ();
233
234 type Balance = u64;
235 type ExistentialDeposit = ConstUint<1>;
236
237 type ReserveIdentifier = ();
238
239 type DustRemoval = ();
240
241 type MaxLocks = ConstU32<100>;
242 type MaxReserves = ConstU32<100>;
243
244 type WeightInfo = ();
245 type DoneSlashHandler = ();
246 }
247 }
248
249 #[pallet::config(with_default)]
250 pub trait Config<I: 'static = ()>: frame_system::Config {
251 #[pallet::no_default_bounds]
253 #[allow(deprecated)]
254 type RuntimeEvent: From<Event<Self, I>>
255 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
256
257 #[pallet::no_default_bounds]
259 type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount;
260
261 #[pallet::no_default_bounds]
266 type RuntimeFreezeReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount;
267
268 type WeightInfo: WeightInfo;
270
271 type Balance: Parameter
273 + Member
274 + AtLeast32BitUnsigned
275 + Codec
276 + HasCompact<Type: DecodeWithMemTracking>
277 + Default
278 + Copy
279 + MaybeSerializeDeserialize
280 + Debug
281 + MaxEncodedLen
282 + TypeInfo
283 + FixedPointOperand;
284
285 #[pallet::no_default_bounds]
287 type DustRemoval: OnUnbalanced<CreditOf<Self, I>>;
288
289 #[pallet::constant]
298 #[pallet::no_default_bounds]
299 type ExistentialDeposit: Get<Self::Balance>;
300
301 #[pallet::no_default]
303 type AccountStore: StoredMap<Self::AccountId, AccountData<Self::Balance>>;
304
305 type ReserveIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
309
310 #[pallet::constant]
315 type MaxLocks: Get<u32>;
316
317 #[pallet::constant]
321 type MaxReserves: Get<u32>;
322
323 type DoneSlashHandler: fungible::hold::DoneSlash<
326 Self::RuntimeHoldReason,
327 Self::AccountId,
328 Self::Balance,
329 >;
330 }
331
332 const STORAGE_VERSION: frame_support::traits::StorageVersion =
334 frame_support::traits::StorageVersion::new(1);
335
336 #[pallet::pallet]
337 #[pallet::storage_version(STORAGE_VERSION)]
338 pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
339
340 #[pallet::event]
341 #[pallet::generate_deposit(pub(super) fn deposit_event)]
342 pub enum Event<T: Config<I>, I: 'static = ()> {
343 Endowed { account: T::AccountId, free_balance: T::Balance },
345 DustLost { account: T::AccountId, amount: T::Balance },
348 Transfer { from: T::AccountId, to: T::AccountId, amount: T::Balance },
350 BalanceSet { who: T::AccountId, free: T::Balance },
352 Reserved { who: T::AccountId, amount: T::Balance },
354 Unreserved { who: T::AccountId, amount: T::Balance },
356 ReserveRepatriated {
359 from: T::AccountId,
360 to: T::AccountId,
361 amount: T::Balance,
362 destination_status: Status,
363 },
364 Deposit { who: T::AccountId, amount: T::Balance },
366 Withdraw { who: T::AccountId, amount: T::Balance },
368 Slashed { who: T::AccountId, amount: T::Balance },
370 Minted { who: T::AccountId, amount: T::Balance },
372 MintedCredit { amount: T::Balance },
374 Burned { who: T::AccountId, amount: T::Balance },
376 BurnedDebt { amount: T::Balance },
378 Suspended { who: T::AccountId, amount: T::Balance },
380 Restored { who: T::AccountId, amount: T::Balance },
382 Upgraded { who: T::AccountId },
384 Issued { amount: T::Balance },
386 Rescinded { amount: T::Balance },
388 Locked { who: T::AccountId, amount: T::Balance },
390 Unlocked { who: T::AccountId, amount: T::Balance },
392 Frozen { who: T::AccountId, amount: T::Balance },
394 Thawed { who: T::AccountId, amount: T::Balance },
396 TotalIssuanceForced { old: T::Balance, new: T::Balance },
398 Held { reason: T::RuntimeHoldReason, who: T::AccountId, amount: T::Balance },
400 BurnedHeld { reason: T::RuntimeHoldReason, who: T::AccountId, amount: T::Balance },
402 TransferOnHold {
404 reason: T::RuntimeHoldReason,
405 source: T::AccountId,
406 dest: T::AccountId,
407 amount: T::Balance,
408 },
409 TransferAndHold {
411 reason: T::RuntimeHoldReason,
412 source: T::AccountId,
413 dest: T::AccountId,
414 transferred: T::Balance,
415 },
416 Released { reason: T::RuntimeHoldReason, who: T::AccountId, amount: T::Balance },
418 Unexpected(UnexpectedKind),
420 }
421
422 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, Debug)]
426 pub enum UnexpectedKind {
427 BalanceUpdated,
429 FailedToMutateAccount,
432 }
433
434 #[pallet::error]
435 pub enum Error<T, I = ()> {
436 VestingBalance,
438 LiquidityRestrictions,
440 InsufficientBalance,
442 ExistentialDeposit,
444 Expendability,
446 ExistingVestingSchedule,
448 DeadAccount,
450 TooManyReserves,
452 TooManyHolds,
454 TooManyFreezes,
456 IssuanceDeactivated,
458 DeltaZero,
460 }
461
462 #[pallet::storage]
464 #[pallet::whitelist_storage]
465 pub type TotalIssuance<T: Config<I>, I: 'static = ()> = StorageValue<_, T::Balance, ValueQuery>;
466
467 #[pallet::storage]
469 #[pallet::whitelist_storage]
470 pub type InactiveIssuance<T: Config<I>, I: 'static = ()> =
471 StorageValue<_, T::Balance, ValueQuery>;
472
473 #[pallet::storage]
498 pub type Account<T: Config<I>, I: 'static = ()> =
499 StorageMap<_, Blake2_128Concat, T::AccountId, AccountData<T::Balance>, ValueQuery>;
500
501 #[pallet::storage]
506 pub type Locks<T: Config<I>, I: 'static = ()> = StorageMap<
507 _,
508 Blake2_128Concat,
509 T::AccountId,
510 WeakBoundedVec<BalanceLock<T::Balance>, T::MaxLocks>,
511 ValueQuery,
512 >;
513
514 #[pallet::storage]
518 pub type Reserves<T: Config<I>, I: 'static = ()> = StorageMap<
519 _,
520 Blake2_128Concat,
521 T::AccountId,
522 BoundedVec<ReserveData<T::ReserveIdentifier, T::Balance>, T::MaxReserves>,
523 ValueQuery,
524 >;
525
526 #[pallet::storage]
528 pub type Holds<T: Config<I>, I: 'static = ()> = StorageMap<
529 _,
530 Blake2_128Concat,
531 T::AccountId,
532 BoundedVec<
533 IdAmount<T::RuntimeHoldReason, T::Balance>,
534 VariantCountOf<T::RuntimeHoldReason>,
535 >,
536 ValueQuery,
537 >;
538
539 #[pallet::storage]
541 pub type Freezes<T: Config<I>, I: 'static = ()> = StorageMap<
542 _,
543 Blake2_128Concat,
544 T::AccountId,
545 BoundedVec<
546 IdAmount<T::RuntimeFreezeReason, T::Balance>,
547 VariantCountOf<T::RuntimeFreezeReason>,
548 >,
549 ValueQuery,
550 >;
551
552 #[pallet::genesis_config]
553 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
554 pub balances: Vec<(T::AccountId, T::Balance)>,
555 pub dev_accounts: Option<(u32, T::Balance, Option<String>)>,
562 }
563
564 impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
565 fn default() -> Self {
566 Self { balances: Default::default(), dev_accounts: None }
567 }
568 }
569
570 #[pallet::genesis_build]
571 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
572 fn build(&self) {
573 let total = self.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n);
574
575 <TotalIssuance<T, I>>::put(total);
576
577 for (_, balance) in &self.balances {
578 assert!(
579 *balance >= <T as Config<I>>::ExistentialDeposit::get(),
580 "the balance of any account should always be at least the existential deposit.",
581 )
582 }
583
584 let endowed_accounts = self
586 .balances
587 .iter()
588 .map(|(x, _)| x)
589 .cloned()
590 .collect::<alloc::collections::btree_set::BTreeSet<_>>();
591
592 assert!(
593 endowed_accounts.len() == self.balances.len(),
594 "duplicate balances in genesis."
595 );
596
597 if let Some((num_accounts, balance, ref derivation)) = self.dev_accounts {
599 Pallet::<T, I>::derive_dev_account(
601 num_accounts,
602 balance,
603 derivation.as_deref().unwrap_or(DEFAULT_ADDRESS_URI),
604 );
605 }
606 for &(ref who, free) in self.balances.iter() {
607 frame_system::Pallet::<T>::inc_providers(who);
608 assert!(T::AccountStore::insert(who, AccountData { free, ..Default::default() })
609 .is_ok());
610 }
611 }
612 }
613
614 #[pallet::hooks]
615 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
616 fn integrity_test() {
617 #[cfg(not(feature = "insecure_zero_ed"))]
618 assert!(
619 !<T as Config<I>>::ExistentialDeposit::get().is_zero(),
620 "The existential deposit must be greater than zero!"
621 );
622 }
623
624 #[cfg(feature = "try-runtime")]
625 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
626 Self::do_try_state(n)
627 }
628 }
629
630 #[pallet::call(weight(<T as Config<I>>::WeightInfo))]
631 impl<T: Config<I>, I: 'static> Pallet<T, I> {
632 #[pallet::call_index(0)]
640 pub fn transfer_allow_death(
641 origin: OriginFor<T>,
642 dest: AccountIdLookupOf<T>,
643 #[pallet::compact] value: T::Balance,
644 ) -> DispatchResult {
645 let source = ensure_signed(origin)?;
646 let dest = T::Lookup::lookup(dest)?;
647 <Self as fungible::Mutate<_>>::transfer(&source, &dest, value, Expendable)?;
648 Ok(())
649 }
650
651 #[pallet::call_index(2)]
654 pub fn force_transfer(
655 origin: OriginFor<T>,
656 source: AccountIdLookupOf<T>,
657 dest: AccountIdLookupOf<T>,
658 #[pallet::compact] value: T::Balance,
659 ) -> DispatchResult {
660 ensure_root(origin)?;
661 let source = T::Lookup::lookup(source)?;
662 let dest = T::Lookup::lookup(dest)?;
663 <Self as fungible::Mutate<_>>::transfer(&source, &dest, value, Expendable)?;
664 Ok(())
665 }
666
667 #[pallet::call_index(3)]
674 pub fn transfer_keep_alive(
675 origin: OriginFor<T>,
676 dest: AccountIdLookupOf<T>,
677 #[pallet::compact] value: T::Balance,
678 ) -> DispatchResult {
679 let source = ensure_signed(origin)?;
680 let dest = T::Lookup::lookup(dest)?;
681 <Self as fungible::Mutate<_>>::transfer(&source, &dest, value, Preserve)?;
682 Ok(())
683 }
684
685 #[pallet::call_index(4)]
701 pub fn transfer_all(
702 origin: OriginFor<T>,
703 dest: AccountIdLookupOf<T>,
704 keep_alive: bool,
705 ) -> DispatchResult {
706 let transactor = ensure_signed(origin)?;
707 let keep_alive = if keep_alive { Preserve } else { Expendable };
708 let reducible_balance = <Self as fungible::Inspect<_>>::reducible_balance(
709 &transactor,
710 keep_alive,
711 Fortitude::Polite,
712 );
713 let dest = T::Lookup::lookup(dest)?;
714 <Self as fungible::Mutate<_>>::transfer(
715 &transactor,
716 &dest,
717 reducible_balance,
718 keep_alive,
719 )?;
720 Ok(())
721 }
722
723 #[pallet::call_index(5)]
727 pub fn force_unreserve(
728 origin: OriginFor<T>,
729 who: AccountIdLookupOf<T>,
730 amount: T::Balance,
731 ) -> DispatchResult {
732 ensure_root(origin)?;
733 let who = T::Lookup::lookup(who)?;
734 let _leftover = <Self as ReservableCurrency<_>>::unreserve(&who, amount);
735 Ok(())
736 }
737
738 #[pallet::call_index(6)]
747 #[pallet::weight(T::WeightInfo::upgrade_accounts(who.len() as u32))]
748 pub fn upgrade_accounts(
749 origin: OriginFor<T>,
750 who: Vec<T::AccountId>,
751 ) -> DispatchResultWithPostInfo {
752 ensure_signed(origin)?;
753 if who.is_empty() {
754 return Ok(Pays::Yes.into());
755 }
756 let mut upgrade_count = 0;
757 for i in &who {
758 let upgraded = Self::ensure_upgraded(i);
759 if upgraded {
760 upgrade_count.saturating_inc();
761 }
762 }
763 let proportion_upgraded = Perbill::from_rational(upgrade_count, who.len() as u32);
764 if proportion_upgraded >= Perbill::from_percent(90) {
765 Ok(Pays::No.into())
766 } else {
767 Ok(Pays::Yes.into())
768 }
769 }
770
771 #[pallet::call_index(8)]
775 #[pallet::weight(
776 T::WeightInfo::force_set_balance_creating() .max(T::WeightInfo::force_set_balance_killing()) )]
779 pub fn force_set_balance(
780 origin: OriginFor<T>,
781 who: AccountIdLookupOf<T>,
782 #[pallet::compact] new_free: T::Balance,
783 ) -> DispatchResult {
784 ensure_root(origin)?;
785 let who = T::Lookup::lookup(who)?;
786 let existential_deposit = Self::ed();
787
788 let wipeout = new_free < existential_deposit;
789 let new_free = if wipeout { Zero::zero() } else { new_free };
790
791 let old_free = Self::mutate_account_handling_dust(&who, false, |account| {
793 let old_free = account.free;
794 account.free = new_free;
795 old_free
796 })?;
797
798 if new_free > old_free {
801 mem::drop(PositiveImbalance::<T, I>::new(new_free - old_free));
802 } else if new_free < old_free {
803 mem::drop(NegativeImbalance::<T, I>::new(old_free - new_free));
804 }
805
806 Self::deposit_event(Event::BalanceSet { who, free: new_free });
807 Ok(())
808 }
809
810 #[doc = docify::embed!("./src/tests/dispatchable_tests.rs", force_adjust_total_issuance_example)]
816 #[pallet::call_index(9)]
817 #[pallet::weight(T::WeightInfo::force_adjust_total_issuance())]
818 pub fn force_adjust_total_issuance(
819 origin: OriginFor<T>,
820 direction: AdjustmentDirection,
821 #[pallet::compact] delta: T::Balance,
822 ) -> DispatchResult {
823 ensure_root(origin)?;
824
825 ensure!(delta > Zero::zero(), Error::<T, I>::DeltaZero);
826
827 let old = TotalIssuance::<T, I>::get();
828 let new = match direction {
829 AdjustmentDirection::Increase => old.saturating_add(delta),
830 AdjustmentDirection::Decrease => old.saturating_sub(delta),
831 };
832
833 ensure!(InactiveIssuance::<T, I>::get() <= new, Error::<T, I>::IssuanceDeactivated);
834 TotalIssuance::<T, I>::set(new);
835
836 Self::deposit_event(Event::<T, I>::TotalIssuanceForced { old, new });
837
838 Ok(())
839 }
840
841 #[pallet::call_index(10)]
849 #[pallet::weight(if *keep_alive {T::WeightInfo::burn_keep_alive()} else {T::WeightInfo::burn_allow_death()})]
850 pub fn burn(
851 origin: OriginFor<T>,
852 #[pallet::compact] value: T::Balance,
853 keep_alive: bool,
854 ) -> DispatchResult {
855 let source = ensure_signed(origin)?;
856 let preservation = if keep_alive { Preserve } else { Expendable };
857 <Self as fungible::Mutate<_>>::burn_from(
858 &source,
859 value,
860 preservation,
861 Precision::Exact,
862 Polite,
863 )?;
864 Ok(())
865 }
866 }
867
868 impl<T: Config<I>, I: 'static> Pallet<T, I> {
869 pub fn total_issuance() -> T::Balance {
871 TotalIssuance::<T, I>::get()
872 }
873
874 pub fn inactive_issuance() -> T::Balance {
876 InactiveIssuance::<T, I>::get()
877 }
878
879 pub fn locks(who: &T::AccountId) -> WeakBoundedVec<BalanceLock<T::Balance>, T::MaxLocks> {
881 Locks::<T, I>::get(who)
882 }
883
884 pub fn reserves(
886 who: &T::AccountId,
887 ) -> BoundedVec<ReserveData<T::ReserveIdentifier, T::Balance>, T::MaxReserves> {
888 Reserves::<T, I>::get(who)
889 }
890
891 fn ed() -> T::Balance {
892 T::ExistentialDeposit::get()
893 }
894 pub fn ensure_upgraded(who: &T::AccountId) -> bool {
898 let mut a = T::AccountStore::get(who);
899 if a.flags.is_new_logic() {
900 return false;
901 }
902 a.flags.set_new_logic();
903 if !a.reserved.is_zero() && a.frozen.is_zero() {
904 if system::Pallet::<T>::providers(who) == 0 {
905 log::warn!(
909 target: LOG_TARGET,
910 "account with a non-zero reserve balance has no provider refs, account_id: '{:?}'.",
911 who
912 );
913 a.free = a.free.max(Self::ed());
914 system::Pallet::<T>::inc_providers(who);
915 }
916 let _ = system::Pallet::<T>::inc_consumers_without_limit(who).defensive();
917 }
918 let _ = T::AccountStore::try_mutate_exists(who, |account| -> DispatchResult {
920 *account = Some(a);
921 Ok(())
922 });
923 Self::deposit_event(Event::Upgraded { who: who.clone() });
924 return true;
925 }
926
927 pub fn free_balance(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
929 Self::account(who.borrow()).free
930 }
931
932 pub fn usable_balance(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
935 <Self as fungible::Inspect<_>>::reducible_balance(who.borrow(), Expendable, Polite)
936 }
937
938 pub fn usable_balance_for_fees(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
943 <Self as fungible::Inspect<_>>::reducible_balance(who.borrow(), Protect, Polite)
944 }
945
946 pub fn reserved_balance(who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
948 Self::account(who.borrow()).reserved
949 }
950
951 pub(crate) fn account(who: &T::AccountId) -> AccountData<T::Balance> {
953 T::AccountStore::get(who)
954 }
955
956 pub(crate) fn mutate_account_handling_dust<R>(
968 who: &T::AccountId,
969 force_consumer_bump: bool,
970 f: impl FnOnce(&mut AccountData<T::Balance>) -> R,
971 ) -> Result<R, DispatchError> {
972 let (r, maybe_dust) = Self::mutate_account(who, force_consumer_bump, f)?;
973 if let Some(dust) = maybe_dust {
974 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
975 }
976 Ok(r)
977 }
978
979 pub(crate) fn try_mutate_account_handling_dust<R, E: From<DispatchError>>(
991 who: &T::AccountId,
992 force_consumer_bump: bool,
993 f: impl FnOnce(&mut AccountData<T::Balance>, bool) -> Result<R, E>,
994 ) -> Result<R, E> {
995 let (r, maybe_dust) = Self::try_mutate_account(who, force_consumer_bump, f)?;
996 if let Some(dust) = maybe_dust {
997 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
998 }
999 Ok(r)
1000 }
1001
1002 pub(crate) fn mutate_account<R>(
1019 who: &T::AccountId,
1020 force_consumer_bump: bool,
1021 f: impl FnOnce(&mut AccountData<T::Balance>) -> R,
1022 ) -> Result<(R, Option<T::Balance>), DispatchError> {
1023 Self::try_mutate_account(who, force_consumer_bump, |a, _| -> Result<R, DispatchError> {
1024 Ok(f(a))
1025 })
1026 }
1027
1028 #[cfg(not(feature = "insecure_zero_ed"))]
1031 fn have_providers_or_no_zero_ed(_: &T::AccountId) -> bool {
1032 true
1033 }
1034
1035 #[cfg(feature = "insecure_zero_ed")]
1038 fn have_providers_or_no_zero_ed(who: &T::AccountId) -> bool {
1039 frame_system::Pallet::<T>::providers(who) > 0
1040 }
1041
1042 pub(crate) fn try_mutate_account<R, E: From<DispatchError>>(
1056 who: &T::AccountId,
1057 force_consumer_bump: bool,
1058 f: impl FnOnce(&mut AccountData<T::Balance>, bool) -> Result<R, E>,
1059 ) -> Result<(R, Option<T::Balance>), E> {
1060 Self::ensure_upgraded(who);
1061 let result = T::AccountStore::try_mutate_exists(who, |maybe_account| {
1062 let is_new = maybe_account.is_none();
1063 let mut account = maybe_account.take().unwrap_or_default();
1064 let did_provide =
1065 account.free >= Self::ed() && Self::have_providers_or_no_zero_ed(who);
1066 let did_consume =
1067 !is_new && (!account.reserved.is_zero() || !account.frozen.is_zero());
1068
1069 let result = f(&mut account, is_new)?;
1070
1071 let does_provide = account.free >= Self::ed();
1072 let does_consume = !account.reserved.is_zero() || !account.frozen.is_zero();
1073
1074 if !did_provide && does_provide {
1075 frame_system::Pallet::<T>::inc_providers(who);
1076 }
1077 if did_consume && !does_consume {
1078 frame_system::Pallet::<T>::dec_consumers(who);
1079 }
1080 if !did_consume && does_consume {
1081 if force_consumer_bump {
1082 frame_system::Pallet::<T>::inc_consumers_without_limit(who)?;
1084 } else {
1085 frame_system::Pallet::<T>::inc_consumers(who)?;
1086 }
1087 }
1088 if does_consume && frame_system::Pallet::<T>::consumers(who) == 0 {
1089 log::error!(target: LOG_TARGET, "Defensively bumping a consumer ref.");
1093 frame_system::Pallet::<T>::inc_consumers(who)?;
1094 }
1095 if did_provide && !does_provide {
1096 frame_system::Pallet::<T>::dec_providers(who).inspect_err(|_| {
1098 if did_consume && !does_consume {
1100 let _ = frame_system::Pallet::<T>::inc_consumers(who).defensive();
1101 }
1102 if !did_consume && does_consume {
1103 let _ = frame_system::Pallet::<T>::dec_consumers(who);
1104 }
1105 })?;
1106 }
1107
1108 let maybe_endowed = if is_new { Some(account.free) } else { None };
1109
1110 let ed = Self::ed();
1122 let maybe_dust = if account.free < ed && account.reserved.is_zero() {
1123 if account.free.is_zero() {
1124 None
1125 } else {
1126 Some(account.free)
1127 }
1128 } else {
1129 *maybe_account = Some(account);
1130 None
1131 };
1132 Ok((maybe_endowed, maybe_dust, result))
1133 });
1134 result.map(|(maybe_endowed, maybe_dust, result)| {
1135 if let Some(endowed) = maybe_endowed {
1136 Self::deposit_event(Event::Endowed {
1137 account: who.clone(),
1138 free_balance: endowed,
1139 });
1140 }
1141 if let Some(amount) = maybe_dust {
1142 Pallet::<T, I>::deposit_event(Event::DustLost { account: who.clone(), amount });
1143 }
1144 (result, maybe_dust)
1145 })
1146 }
1147
1148 pub(crate) fn update_locks(who: &T::AccountId, locks: &[BalanceLock<T::Balance>]) {
1150 let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from(
1151 locks.to_vec(),
1152 Some("Balances Update Locks"),
1153 );
1154
1155 if locks.len() as u32 > T::MaxLocks::get() {
1156 log::warn!(
1157 target: LOG_TARGET,
1158 "Warning: A user has more currency locks than expected. \
1159 A runtime configuration adjustment may be needed."
1160 );
1161 }
1162 let freezes = Freezes::<T, I>::get(who);
1163 let mut prev_frozen = Zero::zero();
1164 let mut after_frozen = Zero::zero();
1165 let res = Self::mutate_account(who, true, |b| {
1168 prev_frozen = b.frozen;
1169 b.frozen = Zero::zero();
1170 for l in locks.iter() {
1171 b.frozen = b.frozen.max(l.amount);
1172 }
1173 for l in freezes.iter() {
1174 b.frozen = b.frozen.max(l.amount);
1175 }
1176 after_frozen = b.frozen;
1177 });
1178 match res {
1179 Ok((_, None)) => {
1180 },
1182 Ok((_, Some(_dust))) => {
1183 Self::deposit_event(Event::Unexpected(UnexpectedKind::BalanceUpdated));
1184 defensive!("caused unexpected dusting/balance update.");
1185 },
1186 _ => {
1187 Self::deposit_event(Event::Unexpected(UnexpectedKind::FailedToMutateAccount));
1188 defensive!("errored in mutate_account");
1189 },
1190 }
1191
1192 match locks.is_empty() {
1193 true => Locks::<T, I>::remove(who),
1194 false => Locks::<T, I>::insert(who, bounded_locks),
1195 }
1196
1197 if prev_frozen > after_frozen {
1198 let amount = prev_frozen.saturating_sub(after_frozen);
1199 Self::deposit_event(Event::Unlocked { who: who.clone(), amount });
1200 } else if after_frozen > prev_frozen {
1201 let amount = after_frozen.saturating_sub(prev_frozen);
1202 Self::deposit_event(Event::Locked { who: who.clone(), amount });
1203 }
1204 }
1205
1206 pub(crate) fn update_freezes(
1208 who: &T::AccountId,
1209 freezes: BoundedSlice<
1210 IdAmount<T::RuntimeFreezeReason, T::Balance>,
1211 VariantCountOf<T::RuntimeFreezeReason>,
1212 >,
1213 ) -> DispatchResult {
1214 let mut prev_frozen = Zero::zero();
1215 let mut after_frozen = Zero::zero();
1216 let (_, maybe_dust) = Self::mutate_account(who, false, |b| {
1217 prev_frozen = b.frozen;
1218 b.frozen = Zero::zero();
1219 for l in Locks::<T, I>::get(who).iter() {
1220 b.frozen = b.frozen.max(l.amount);
1221 }
1222 for l in freezes.iter() {
1223 b.frozen = b.frozen.max(l.amount);
1224 }
1225 after_frozen = b.frozen;
1226 })?;
1227 if maybe_dust.is_some() {
1228 Self::deposit_event(Event::Unexpected(UnexpectedKind::BalanceUpdated));
1229 defensive!("caused unexpected dusting/balance update.");
1230 }
1231 if freezes.is_empty() {
1232 Freezes::<T, I>::remove(who);
1233 } else {
1234 Freezes::<T, I>::insert(who, freezes);
1235 }
1236 if prev_frozen > after_frozen {
1237 let amount = prev_frozen.saturating_sub(after_frozen);
1238 Self::deposit_event(Event::Thawed { who: who.clone(), amount });
1239 } else if after_frozen > prev_frozen {
1240 let amount = after_frozen.saturating_sub(prev_frozen);
1241 Self::deposit_event(Event::Frozen { who: who.clone(), amount });
1242 }
1243 Ok(())
1244 }
1245
1246 pub(crate) fn do_transfer_reserved(
1253 slashed: &T::AccountId,
1254 beneficiary: &T::AccountId,
1255 value: T::Balance,
1256 precision: Precision,
1257 fortitude: Fortitude,
1258 status: Status,
1259 ) -> Result<T::Balance, DispatchError> {
1260 if value.is_zero() {
1261 return Ok(Zero::zero());
1262 }
1263
1264 let max = <Self as fungible::InspectHold<_>>::reducible_total_balance_on_hold(
1265 slashed, fortitude,
1266 );
1267 let actual = match precision {
1268 Precision::BestEffort => value.min(max),
1269 Precision::Exact => value,
1270 };
1271 ensure!(actual <= max, TokenError::FundsUnavailable);
1272 if slashed == beneficiary {
1273 return match status {
1274 Status::Free => Ok(actual.saturating_sub(Self::unreserve(slashed, actual))),
1275 Status::Reserved => Ok(actual),
1276 };
1277 }
1278
1279 let ((_, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account(
1280 beneficiary,
1281 false,
1282 |to_account, is_new| -> Result<((), Option<T::Balance>), DispatchError> {
1283 ensure!(!is_new, Error::<T, I>::DeadAccount);
1284 Self::try_mutate_account(slashed, false, |from_account, _| -> DispatchResult {
1285 match status {
1286 Status::Free => {
1287 to_account.free = to_account
1288 .free
1289 .checked_add(&actual)
1290 .ok_or(ArithmeticError::Overflow)?
1291 },
1292 Status::Reserved => {
1293 to_account.reserved = to_account
1294 .reserved
1295 .checked_add(&actual)
1296 .ok_or(ArithmeticError::Overflow)?
1297 },
1298 }
1299 from_account.reserved.saturating_reduce(actual);
1300 Ok(())
1301 })
1302 },
1303 )?;
1304
1305 if let Some(dust) = maybe_dust_1 {
1306 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
1307 }
1308 if let Some(dust) = maybe_dust_2 {
1309 <Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
1310 }
1311
1312 Self::deposit_event(Event::ReserveRepatriated {
1313 from: slashed.clone(),
1314 to: beneficiary.clone(),
1315 amount: actual,
1316 destination_status: status,
1317 });
1318 Ok(actual)
1319 }
1320
1321 pub fn derive_dev_account(num_accounts: u32, balance: T::Balance, derivation: &str) {
1323 assert!(num_accounts > 0, "num_accounts must be greater than zero");
1325
1326 assert!(
1327 balance >= <T as Config<I>>::ExistentialDeposit::get(),
1328 "the balance of any account should always be at least the existential deposit.",
1329 );
1330
1331 assert!(
1332 derivation.contains("{}"),
1333 "Invalid derivation, expected `{{}}` as part of the derivation"
1334 );
1335
1336 for index in 0..num_accounts {
1337 let derivation_string = derivation.replace("{}", &index.to_string());
1339
1340 let pair: SrPair = Pair::from_string(&derivation_string, None)
1342 .expect(&format!("Failed to parse derivation string: {derivation_string}"));
1343
1344 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
1346 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
1347
1348 Self::mutate_account_handling_dust(&who, false, |account| {
1350 account.free = balance;
1351 })
1352 .expect(&format!("Failed to add account to keystore: {:?}", who));
1353 }
1354 }
1355 }
1356
1357 #[cfg(any(test, feature = "try-runtime"))]
1358 impl<T: Config<I>, I: 'static> Pallet<T, I> {
1359 pub(crate) fn do_try_state(
1360 _n: BlockNumberFor<T>,
1361 ) -> Result<(), sp_runtime::TryRuntimeError> {
1362 Self::hold_and_freeze_count()?;
1363 Self::account_frozen_greater_than_locks()?;
1364 Self::account_frozen_greater_than_freezes()?;
1365 Ok(())
1366 }
1367
1368 fn hold_and_freeze_count() -> Result<(), sp_runtime::TryRuntimeError> {
1369 Holds::<T, I>::iter_keys().try_for_each(|k| {
1370 if Holds::<T, I>::decode_len(k).unwrap_or(0) >
1371 T::RuntimeHoldReason::VARIANT_COUNT as usize
1372 {
1373 Err("Found `Hold` with too many elements")
1374 } else {
1375 Ok(())
1376 }
1377 })?;
1378
1379 Freezes::<T, I>::iter_keys().try_for_each(|k| {
1380 if Freezes::<T, I>::decode_len(k).unwrap_or(0) >
1381 T::RuntimeFreezeReason::VARIANT_COUNT as usize
1382 {
1383 Err("Found `Freeze` with too many elements")
1384 } else {
1385 Ok(())
1386 }
1387 })?;
1388
1389 Ok(())
1390 }
1391
1392 fn account_frozen_greater_than_locks() -> Result<(), sp_runtime::TryRuntimeError> {
1393 Locks::<T, I>::iter().try_for_each(|(who, locks)| {
1394 let max_locks = locks.iter().map(|l| l.amount).max().unwrap_or_default();
1395 let frozen = T::AccountStore::get(&who).frozen;
1396 if max_locks > frozen {
1397 log::warn!(
1398 target: crate::LOG_TARGET,
1399 "Maximum lock of {:?} ({:?}) is greater than the frozen balance {:?}",
1400 who,
1401 max_locks,
1402 frozen
1403 );
1404 Err("bad locks".into())
1405 } else {
1406 Ok(())
1407 }
1408 })
1409 }
1410
1411 fn account_frozen_greater_than_freezes() -> Result<(), sp_runtime::TryRuntimeError> {
1412 Freezes::<T, I>::iter().try_for_each(|(who, freezes)| {
1413 let max_locks = freezes.iter().map(|l| l.amount).max().unwrap_or_default();
1414 let frozen = T::AccountStore::get(&who).frozen;
1415 if max_locks > frozen {
1416 log::warn!(
1417 target: crate::LOG_TARGET,
1418 "Maximum freeze of {:?} ({:?}) is greater than the frozen balance {:?}",
1419 who,
1420 max_locks,
1421 frozen
1422 );
1423 Err("bad freezes".into())
1424 } else {
1425 Ok(())
1426 }
1427 })
1428 }
1429 }
1430}