lib.rs 71.7 KiB
Newer Older
Xiliang Chen's avatar
Xiliang Chen committed
	fn slash_reserved_named(
		id: &Self::ReserveIdentifier,
		who: &T::AccountId,
		value: Self::Balance,
Xiliang Chen's avatar
Xiliang Chen committed
	) -> (Self::NegativeImbalance, Self::Balance) {
		if value.is_zero() {
			return (NegativeImbalance::zero(), Zero::zero())
		}
Xiliang Chen's avatar
Xiliang Chen committed

		Reserves::<T, I>::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) {
			match reserves.binary_search_by_key(id, |data| data.id) {
				Ok(index) => {
					let to_change = cmp::min(reserves[index].amount, value);

					let (imb, remain) =
						<Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
					// remain should always be zero but just to be defensive here.
					let actual = to_change.defensive_saturating_sub(remain);
Xiliang Chen's avatar
Xiliang Chen committed

					// `actual <= to_change` and `to_change <= amount`; qed;
					reserves[index].amount -= actual;

					Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual });
Xiliang Chen's avatar
Xiliang Chen committed
					(imb, value - actual)
				},
				Err(_) => (NegativeImbalance::zero(), value),
Xiliang Chen's avatar
Xiliang Chen committed
			}
		})
	}

	/// Move the reserved balance of one account into the balance of another, according to `status`.
	/// If `status` is `Reserved`, the balance will be reserved with given `id`.
	///
	/// Is a no-op if:
	/// - the value to be moved is zero; or
	/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
	fn repatriate_reserved_named(
		id: &Self::ReserveIdentifier,
		slashed: &T::AccountId,
		beneficiary: &T::AccountId,
		value: Self::Balance,
		status: Status,
	) -> Result<Self::Balance, DispatchError> {
		if value.is_zero() {
			return Ok(Zero::zero())
		}
Xiliang Chen's avatar
Xiliang Chen committed

		if slashed == beneficiary {
			return match status {
				Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
				Status::Reserved =>
					Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))),
			}
Xiliang Chen's avatar
Xiliang Chen committed
		}

		Reserves::<T, I>::try_mutate(slashed, |reserves| -> Result<Self::Balance, DispatchError> {
			match reserves.binary_search_by_key(id, |data| data.id) {
				Ok(index) => {
					let to_change = cmp::min(reserves[index].amount, value);

					let actual = if status == Status::Reserved {
						// make it the reserved under same identifier
						Reserves::<T, I>::try_mutate(
							beneficiary,
							|reserves| -> Result<T::Balance, DispatchError> {
								match reserves.binary_search_by_key(id, |data| data.id) {
									Ok(index) => {
										let remain =
											<Self as ReservableCurrency<_>>::repatriate_reserved(
												slashed,
												beneficiary,
												to_change,
												status,
											)?;

										// remain should always be zero but just to be defensive
										// here.
										let actual = to_change.defensive_saturating_sub(remain);

										// this add can't overflow but just to be defensive.
										reserves[index].amount =
											reserves[index].amount.defensive_saturating_add(actual);

										Ok(actual)
									},
									Err(index) => {
										let remain =
											<Self as ReservableCurrency<_>>::repatriate_reserved(
												slashed,
												beneficiary,
												to_change,
												status,
											)?;

										// remain should always be zero but just to be defensive
										// here
										let actual = to_change.defensive_saturating_sub(remain);
												ReserveData { id: *id, amount: actual },
											)
											.map_err(|_| Error::<T, I>::TooManyReserves)?;

										Ok(actual)
									},
								}
							},
						)?
Xiliang Chen's avatar
Xiliang Chen committed
					} else {
						let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(
							slashed,
							beneficiary,
							to_change,
							status,
						)?;
Xiliang Chen's avatar
Xiliang Chen committed

						// remain should always be zero but just to be defensive here
						to_change.defensive_saturating_sub(remain)
Xiliang Chen's avatar
Xiliang Chen committed
					};

					// `actual <= to_change` and `to_change <= amount`; qed;
					reserves[index].amount -= actual;

					Ok(value - actual)
				},
				Err(_) => Ok(value),
impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
	T::Balance: MaybeSerializeDeserialize + Debug,
	type MaxLocks = T::MaxLocks;

	// Set a lock on the balance of `who`.
	// Is a no-op if lock amount is zero or `reasons` `is_none()`.
		who: &T::AccountId,
		amount: T::Balance,
		reasons: WithdrawReasons,
	) {
		if amount.is_zero() || reasons.is_empty() {
			return
		}
		let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
		let mut locks = Self::locks(who)
			.into_iter()
Gavin Wood's avatar
Gavin Wood committed
			.filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
			.collect::<Vec<_>>();
		if let Some(lock) = new_lock {
			locks.push(lock)
		}
		Self::update_locks(who, &locks[..]);
	// Extend a lock on the balance of `who`.
	// Is a no-op if lock amount is zero or `reasons` `is_none()`.
		who: &T::AccountId,
		amount: T::Balance,
		reasons: WithdrawReasons,
	) {
		if amount.is_zero() || reasons.is_empty() {
			return
		}
		let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
		let mut locks = Self::locks(who)
			.into_iter()
			.filter_map(|l| {
				if l.id == id {
					new_lock.take().map(|nl| BalanceLock {
						id: l.id,
						amount: l.amount.max(nl.amount),
						reasons: l.reasons | nl.reasons,
					})
				} else {
					Some(l)
				}
			})
			.collect::<Vec<_>>();
		if let Some(lock) = new_lock {
			locks.push(lock)
		}
		Self::update_locks(who, &locks[..]);
	fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
		let mut locks = Self::locks(who);
		locks.retain(|l| l.id != id);
		Self::update_locks(who, &locks[..]);