lib.rs 42.3 KiB
Newer Older
				Ok((maybe_endowed, maybe_dust, result))
			});
			result.map(|(maybe_endowed, maybe_dust, result)| {
				if let Some(endowed) = maybe_endowed {
					Self::deposit_event(Event::Endowed {
						account: who.clone(),
						free_balance: endowed,
				}
				if let Some(amount) = maybe_dust {
					Pallet::<T, I>::deposit_event(Event::DustLost { account: who.clone(), amount });
				}
				(result, maybe_dust)
			})
		/// Update the account entry for `who`, given the locks.
		pub(crate) fn update_locks(who: &T::AccountId, locks: &[BalanceLock<T::Balance>]) {
			let bounded_locks = WeakBoundedVec::<_, T::MaxLocks>::force_from(
				locks.to_vec(),
				Some("Balances Update Locks"),
			);
			if locks.len() as u32 > T::MaxLocks::get() {
				log::warn!(
					target: LOG_TARGET,
					"Warning: A user has more currency locks than expected. \
					A runtime configuration adjustment may be needed."
				);
			}
			let freezes = Freezes::<T, I>::get(who);
			let mut prev_frozen = Zero::zero();
			let mut after_frozen = Zero::zero();
			// TODO: Revisit this assumption. We no manipulate consumer/provider refs.
			// No way this can fail since we do not alter the existential balances.
			let res = Self::mutate_account(who, |b| {
				b.frozen = Zero::zero();
				for l in locks.iter() {
					b.frozen = b.frozen.max(l.amount);
				}
				for l in freezes.iter() {
					b.frozen = b.frozen.max(l.amount);
				}
			});
			debug_assert!(res.is_ok());
			if let Ok((_, maybe_dust)) = res {
				debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed");
			}
			let existed = Locks::<T, I>::contains_key(who);
			if locks.is_empty() {
				Locks::<T, I>::remove(who);
				if existed {
					// TODO: use Locks::<T, I>::hashed_key
					// https://github.com/paritytech/substrate/issues/4969
					system::Pallet::<T>::dec_consumers(who);
Xiliang Chen's avatar
Xiliang Chen committed
				}
			} else {
				Locks::<T, I>::insert(who, bounded_locks);
				if !existed && system::Pallet::<T>::inc_consumers_without_limit(who).is_err() {
					// No providers for the locks. This is impossible under normal circumstances
					// since the funds that are under the lock will themselves be stored in the
					// account and therefore will need a reference.
					log::warn!(
						target: LOG_TARGET,
						"Warning: Attempt to introduce lock consumer reference, yet no providers. \
						This is unexpected but should be safe."
					);
				}
Xiliang Chen's avatar
Xiliang Chen committed
			}

			if prev_frozen > after_frozen {
				let amount = prev_frozen.saturating_sub(after_frozen);
				Self::deposit_event(Event::Unlocked { who: who.clone(), amount });
			} else if after_frozen > prev_frozen {
				let amount = after_frozen.saturating_sub(prev_frozen);
				Self::deposit_event(Event::Locked { who: who.clone(), amount });
			}
		/// Update the account entry for `who`, given the locks.
		pub(crate) fn update_freezes(
			who: &T::AccountId,
			freezes: BoundedSlice<IdAmount<T::FreezeIdentifier, T::Balance>, T::MaxFreezes>,
		) -> DispatchResult {
			let mut prev_frozen = Zero::zero();
			let mut after_frozen = Zero::zero();
			let (_, maybe_dust) = Self::mutate_account(who, |b| {
				prev_frozen = b.frozen;
				b.frozen = Zero::zero();
				for l in Locks::<T, I>::get(who).iter() {
					b.frozen = b.frozen.max(l.amount);
				}
				for l in freezes.iter() {
					b.frozen = b.frozen.max(l.amount);
				}
				after_frozen = b.frozen;
			})?;
			debug_assert!(maybe_dust.is_none(), "Not altering main balance; qed");
			if freezes.is_empty() {
				Freezes::<T, I>::remove(who);
			} else {
				Freezes::<T, I>::insert(who, freezes);
Xiliang Chen's avatar
Xiliang Chen committed
			}
			if prev_frozen > after_frozen {
				let amount = prev_frozen.saturating_sub(after_frozen);
				Self::deposit_event(Event::Thawed { who: who.clone(), amount });
			} else if after_frozen > prev_frozen {
				let amount = after_frozen.saturating_sub(prev_frozen);
				Self::deposit_event(Event::Frozen { who: who.clone(), amount });
			}
		/// Move the reserved balance of one account into the balance of another, according to
		/// `status`. This will respect freezes/locks only if `fortitude` is `Polite`.
		/// Is a no-op if the value to be moved is zero.
		///
		/// NOTE: returns actual amount of transferred value in `Ok` case.
		pub(crate) fn do_transfer_reserved(
			slashed: &T::AccountId,
			beneficiary: &T::AccountId,
			value: T::Balance,
			precision: Precision,
			fortitude: Fortitude,
			status: Status,
		) -> Result<T::Balance, DispatchError> {
			if value.is_zero() {
				return Ok(Zero::zero())
			let max = <Self as fungible::InspectHold<_>>::reducible_total_balance_on_hold(
				slashed, fortitude,
			);
			let actual = match precision {
				Precision::BestEffort => value.min(max),
				Precision::Exact => value,
			};
			ensure!(actual <= max, TokenError::FundsUnavailable);
			if slashed == beneficiary {
				return match status {
					Status::Free => Ok(actual.saturating_sub(Self::unreserve(slashed, actual))),
					Status::Reserved => Ok(actual),
			let ((_, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account(
				|to_account, is_new| -> Result<((), Option<T::Balance>), DispatchError> {
					ensure!(!is_new, Error::<T, I>::DeadAccount);
					Self::try_mutate_account(slashed, |from_account, _| -> DispatchResult {
						match status {
							Status::Free =>
								to_account.free = to_account
									.free
									.checked_add(&actual)
									.ok_or(ArithmeticError::Overflow)?,
							Status::Reserved =>
								to_account.reserved = to_account
									.reserved
									.checked_add(&actual)
									.ok_or(ArithmeticError::Overflow)?,
						}
						from_account.reserved.saturating_reduce(actual);
						Ok(())
					})
Xiliang Chen's avatar
Xiliang Chen committed
				},
			if let Some(dust) = maybe_dust_1 {
				<Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
			}
			if let Some(dust) = maybe_dust_2 {
				<Self as fungible::Unbalanced<_>>::handle_raw_dust(dust);
			}
			Self::deposit_event(Event::ReserveRepatriated {
				from: slashed.clone(),
				to: beneficiary.clone(),
				amount: actual,
				destination_status: status,
			});
			Ok(actual)