referrerpolicy=no-referrer-when-downgrade

pallet_balances/
impl_currency.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Implementations for the `Currency` family of traits.
19//!
20//! Note that `WithdrawReasons` are intentionally not used for anything in this implementation and
21//! are expected to be removed in the near future, once migration to `fungible::*` traits is done.
22
23use super::*;
24use frame_support::{
25	ensure,
26	pallet_prelude::DispatchResult,
27	traits::{
28		tokens::{fungible, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort},
29		Currency, DefensiveSaturating, ExistenceRequirement,
30		ExistenceRequirement::AllowDeath,
31		Get, Imbalance, InspectLockableCurrency, LockIdentifier, LockableCurrency,
32		NamedReservableCurrency, ReservableCurrency, SignedImbalance, TryDrop, WithdrawReasons,
33	},
34};
35use frame_system::pallet_prelude::BlockNumberFor;
36pub use imbalances::{NegativeImbalance, PositiveImbalance};
37use sp_runtime::traits::Bounded;
38
39// wrapping these imbalances in a private module is necessary to ensure absolute privacy
40// of the inner member.
41mod imbalances {
42	use super::*;
43	use alloc::boxed::Box;
44	use core::mem;
45	use frame_support::traits::{
46		tokens::imbalance::{
47			ImbalanceAccounting, TryMerge, UnsafeConstructorDestructor, UnsafeManualAccounting,
48		},
49		SameOrOther,
50	};
51
52	/// Opaque, move-only struct with private fields that serves as a token denoting that
53	/// funds have been created without any equal and opposite accounting.
54	#[must_use]
55	#[derive(Debug, PartialEq, Eq)]
56	pub struct PositiveImbalance<T: Config<I>, I: 'static = ()>(T::Balance);
57
58	impl<T: Config<I>, I: 'static> PositiveImbalance<T, I> {
59		/// Create a new positive imbalance from a balance.
60		pub fn new(amount: T::Balance) -> Self {
61			PositiveImbalance(amount)
62		}
63	}
64
65	/// Opaque, move-only struct with private fields that serves as a token denoting that
66	/// funds have been destroyed without any equal and opposite accounting.
67	#[must_use]
68	#[derive(Debug, PartialEq, Eq)]
69	pub struct NegativeImbalance<T: Config<I>, I: 'static = ()>(T::Balance);
70
71	impl<T, I> UnsafeConstructorDestructor<u128> for NegativeImbalance<T, I>
72	where
73		T: Config<I, Balance: From<u128> + Into<u128>>,
74		I: 'static,
75	{
76		fn unsafe_clone(&self) -> Box<dyn ImbalanceAccounting<u128>> {
77			Box::new(Self(self.0))
78		}
79		fn forget_imbalance(&mut self) -> u128 {
80			let amount = self.0.into();
81			self.0 = Zero::zero();
82			amount
83		}
84	}
85
86	impl<T, I> UnsafeManualAccounting<u128> for NegativeImbalance<T, I>
87	where
88		T: Config<I, Balance: From<u128> + Into<u128>>,
89		I: 'static,
90	{
91		fn saturating_subsume(&mut self, mut other: Box<dyn ImbalanceAccounting<u128>>) {
92			let amount = other.forget_imbalance();
93			self.0 = self.0.saturating_add(amount.into())
94		}
95	}
96
97	impl<T, I> ImbalanceAccounting<u128> for NegativeImbalance<T, I>
98	where
99		T: Config<I, Balance: From<u128> + Into<u128>>,
100		I: 'static,
101	{
102		fn amount(&self) -> u128 {
103			self.0.into()
104		}
105		fn saturating_take(&mut self, amount: u128) -> Box<dyn ImbalanceAccounting<u128>> {
106			Box::new(self.extract(amount.into()))
107		}
108	}
109
110	impl<T: Config<I>, I: 'static> NegativeImbalance<T, I> {
111		/// Create a new negative imbalance from a balance.
112		pub fn new(amount: T::Balance) -> Self {
113			NegativeImbalance(amount)
114		}
115	}
116
117	impl<T: Config<I>, I: 'static> TryDrop for PositiveImbalance<T, I> {
118		fn try_drop(self) -> result::Result<(), Self> {
119			self.drop_zero()
120		}
121	}
122
123	impl<T: Config<I>, I: 'static> Default for PositiveImbalance<T, I> {
124		fn default() -> Self {
125			Self::zero()
126		}
127	}
128
129	impl<T: Config<I>, I: 'static> Imbalance<T::Balance> for PositiveImbalance<T, I> {
130		type Opposite = NegativeImbalance<T, I>;
131
132		fn zero() -> Self {
133			Self(Zero::zero())
134		}
135		fn drop_zero(self) -> result::Result<(), Self> {
136			if self.0.is_zero() {
137				Ok(())
138			} else {
139				Err(self)
140			}
141		}
142		fn split(self, amount: T::Balance) -> (Self, Self) {
143			let first = self.0.min(amount);
144			let second = self.0 - first;
145
146			mem::forget(self);
147			(Self(first), Self(second))
148		}
149		fn extract(&mut self, amount: T::Balance) -> Self {
150			let new = self.0.min(amount);
151			self.0 = self.0 - new;
152			Self(new)
153		}
154		fn merge(mut self, other: Self) -> Self {
155			self.0 = self.0.saturating_add(other.0);
156			mem::forget(other);
157
158			self
159		}
160		fn subsume(&mut self, other: Self) {
161			self.0 = self.0.saturating_add(other.0);
162			mem::forget(other);
163		}
164		fn offset(self, other: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
165			let (a, b) = (self.0, other.0);
166			mem::forget((self, other));
167
168			if a > b {
169				SameOrOther::Same(Self(a - b))
170			} else if b > a {
171				SameOrOther::Other(NegativeImbalance::new(b - a))
172			} else {
173				SameOrOther::None
174			}
175		}
176		fn peek(&self) -> T::Balance {
177			self.0
178		}
179	}
180
181	impl<T: Config<I>, I: 'static> TryMerge for PositiveImbalance<T, I> {
182		fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
183			Ok(self.merge(other))
184		}
185	}
186
187	impl<T: Config<I>, I: 'static> TryDrop for NegativeImbalance<T, I> {
188		fn try_drop(self) -> result::Result<(), Self> {
189			self.drop_zero()
190		}
191	}
192
193	impl<T: Config<I>, I: 'static> Default for NegativeImbalance<T, I> {
194		fn default() -> Self {
195			Self::zero()
196		}
197	}
198
199	impl<T: Config<I>, I: 'static> Imbalance<T::Balance> for NegativeImbalance<T, I> {
200		type Opposite = PositiveImbalance<T, I>;
201
202		fn zero() -> Self {
203			Self(Zero::zero())
204		}
205		fn drop_zero(self) -> result::Result<(), Self> {
206			if self.0.is_zero() {
207				Ok(())
208			} else {
209				Err(self)
210			}
211		}
212		fn split(self, amount: T::Balance) -> (Self, Self) {
213			let first = self.0.min(amount);
214			let second = self.0 - first;
215
216			mem::forget(self);
217			(Self(first), Self(second))
218		}
219		fn extract(&mut self, amount: T::Balance) -> Self {
220			let new = self.0.min(amount);
221			self.0 = self.0 - new;
222			Self(new)
223		}
224		fn merge(mut self, other: Self) -> Self {
225			self.0 = self.0.saturating_add(other.0);
226			mem::forget(other);
227
228			self
229		}
230		fn subsume(&mut self, other: Self) {
231			self.0 = self.0.saturating_add(other.0);
232			mem::forget(other);
233		}
234		fn offset(self, other: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
235			let (a, b) = (self.0, other.0);
236			mem::forget((self, other));
237
238			if a > b {
239				SameOrOther::Same(Self(a - b))
240			} else if b > a {
241				SameOrOther::Other(PositiveImbalance::new(b - a))
242			} else {
243				SameOrOther::None
244			}
245		}
246		fn peek(&self) -> T::Balance {
247			self.0
248		}
249	}
250
251	impl<T: Config<I>, I: 'static> TryMerge for NegativeImbalance<T, I> {
252		fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
253			Ok(self.merge(other))
254		}
255	}
256
257	impl<T: Config<I>, I: 'static> Drop for PositiveImbalance<T, I> {
258		/// Basic drop handler will just square up the total issuance.
259		fn drop(&mut self) {
260			if !self.0.is_zero() {
261				<super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_add(self.0));
262				Pallet::<T, I>::deposit_event(Event::<T, I>::Issued { amount: self.0 });
263			}
264		}
265	}
266
267	impl<T: Config<I>, I: 'static> Drop for NegativeImbalance<T, I> {
268		/// Basic drop handler will just square up the total issuance.
269		fn drop(&mut self) {
270			if !self.0.is_zero() {
271				<super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_sub(self.0));
272				Pallet::<T, I>::deposit_event(Event::<T, I>::Rescinded { amount: self.0 });
273			}
274		}
275	}
276
277	impl<T: Config<I>, I: 'static> fungible::HandleImbalanceDrop<T::Balance>
278		for NegativeImbalance<T, I>
279	{
280		fn handle(amount: T::Balance) {
281			<super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_sub(amount));
282			Pallet::<T, I>::deposit_event(Event::<T, I>::BurnedDebt { amount });
283		}
284	}
285
286	impl<T: Config<I>, I: 'static> fungible::HandleImbalanceDrop<T::Balance>
287		for PositiveImbalance<T, I>
288	{
289		fn handle(amount: T::Balance) {
290			<super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_add(amount));
291			Pallet::<T, I>::deposit_event(Event::<T, I>::MintedCredit { amount });
292		}
293	}
294}
295
296impl<T: Config<I>, I: 'static> Currency<T::AccountId> for Pallet<T, I>
297where
298	T::Balance: MaybeSerializeDeserialize + Debug,
299{
300	type Balance = T::Balance;
301	type PositiveImbalance = PositiveImbalance<T, I>;
302	type NegativeImbalance = NegativeImbalance<T, I>;
303
304	fn total_balance(who: &T::AccountId) -> Self::Balance {
305		Self::account(who).total()
306	}
307
308	// Check if `value` amount of free balance can be slashed from `who`.
309	fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
310		if value.is_zero() {
311			return true;
312		}
313		Self::free_balance(who) >= value
314	}
315
316	fn total_issuance() -> Self::Balance {
317		TotalIssuance::<T, I>::get()
318	}
319
320	fn active_issuance() -> Self::Balance {
321		<Self as fungible::Inspect<_>>::active_issuance()
322	}
323
324	fn deactivate(amount: Self::Balance) {
325		<Self as fungible::Unbalanced<_>>::deactivate(amount);
326	}
327
328	fn reactivate(amount: Self::Balance) {
329		<Self as fungible::Unbalanced<_>>::reactivate(amount);
330	}
331
332	fn minimum_balance() -> Self::Balance {
333		T::ExistentialDeposit::get()
334	}
335
336	// Burn funds from the total issuance, returning a positive imbalance for the amount burned.
337	// Is a no-op if amount to be burned is zero.
338	fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
339		if amount.is_zero() {
340			return PositiveImbalance::zero();
341		}
342		<TotalIssuance<T, I>>::mutate(|issued| {
343			*issued = issued.checked_sub(&amount).unwrap_or_else(|| {
344				amount = *issued;
345				Zero::zero()
346			});
347		});
348
349		Pallet::<T, I>::deposit_event(Event::<T, I>::Rescinded { amount });
350		PositiveImbalance::new(amount)
351	}
352
353	// Create new funds into the total issuance, returning a negative imbalance
354	// for the amount issued.
355	// Is a no-op if amount to be issued it zero.
356	fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
357		if amount.is_zero() {
358			return NegativeImbalance::zero();
359		}
360		<TotalIssuance<T, I>>::mutate(|issued| {
361			*issued = issued.checked_add(&amount).unwrap_or_else(|| {
362				amount = Self::Balance::max_value() - *issued;
363				Self::Balance::max_value()
364			})
365		});
366
367		Pallet::<T, I>::deposit_event(Event::<T, I>::Issued { amount });
368		NegativeImbalance::new(amount)
369	}
370
371	fn free_balance(who: &T::AccountId) -> Self::Balance {
372		Self::account(who).free
373	}
374
375	// Ensure that an account can withdraw from their free balance given any existing withdrawal
376	// restrictions like locks and vesting balance.
377	// Is a no-op if amount to be withdrawn is zero.
378	fn ensure_can_withdraw(
379		who: &T::AccountId,
380		amount: T::Balance,
381		_reasons: WithdrawReasons,
382		new_balance: T::Balance,
383	) -> DispatchResult {
384		if amount.is_zero() {
385			return Ok(());
386		}
387		ensure!(new_balance >= Self::account(who).frozen, Error::<T, I>::LiquidityRestrictions);
388		Ok(())
389	}
390
391	// Transfer some free balance from `transactor` to `dest`, respecting existence requirements.
392	// Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`.
393	fn transfer(
394		transactor: &T::AccountId,
395		dest: &T::AccountId,
396		value: Self::Balance,
397		existence_requirement: ExistenceRequirement,
398	) -> DispatchResult {
399		if value.is_zero() || transactor == dest {
400			return Ok(());
401		}
402		let keep_alive = match existence_requirement {
403			ExistenceRequirement::KeepAlive => Preserve,
404			ExistenceRequirement::AllowDeath => Expendable,
405		};
406		<Self as fungible::Mutate<_>>::transfer(transactor, dest, value, keep_alive)?;
407		Ok(())
408	}
409
410	/// Slash a target account `who`, returning the negative imbalance created and any left over
411	/// amount that could not be slashed.
412	///
413	/// Is a no-op if `value` to be slashed is zero or the account does not exist.
414	///
415	/// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn
416	/// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid
417	/// having to draw from reserved funds, however we err on the side of punishment if things are
418	/// inconsistent or `can_slash` wasn't used appropriately.
419	fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
420		if value.is_zero() {
421			return (NegativeImbalance::zero(), Zero::zero());
422		}
423		if Self::total_balance(who).is_zero() {
424			return (NegativeImbalance::zero(), value);
425		}
426
427		let result = match Self::try_mutate_account_handling_dust(
428			who,
429			false,
430			|account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> {
431				// Best value is the most amount we can slash following liveness rules.
432				let ed = T::ExistentialDeposit::get();
433				let actual = match system::Pallet::<T>::can_dec_provider(who) {
434					true => value.min(account.free),
435					false => value.min(account.free.saturating_sub(ed)),
436				};
437				account.free.saturating_reduce(actual);
438				let remaining = value.saturating_sub(actual);
439				Ok((NegativeImbalance::new(actual), remaining))
440			},
441		) {
442			Ok((imbalance, remaining)) => {
443				Self::deposit_event(Event::Slashed {
444					who: who.clone(),
445					amount: value.saturating_sub(remaining),
446				});
447				(imbalance, remaining)
448			},
449			Err(_) => (Self::NegativeImbalance::zero(), value),
450		};
451		result
452	}
453
454	/// Deposit some `value` into the free balance of an existing target account `who`.
455	///
456	/// Is a no-op if the `value` to be deposited is zero.
457	fn deposit_into_existing(
458		who: &T::AccountId,
459		value: Self::Balance,
460	) -> Result<Self::PositiveImbalance, DispatchError> {
461		if value.is_zero() {
462			return Ok(PositiveImbalance::zero());
463		}
464
465		Self::try_mutate_account_handling_dust(
466			who,
467			false,
468			|account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
469				ensure!(!is_new, Error::<T, I>::DeadAccount);
470				account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
471				Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
472				Ok(PositiveImbalance::new(value))
473			},
474		)
475	}
476
477	/// Deposit some `value` into the free balance of `who`, possibly creating a new account.
478	///
479	/// This function is a no-op if:
480	/// - the `value` to be deposited is zero; or
481	/// - the `value` to be deposited is less than the required ED and the account does not yet
482	///   exist; or
483	/// - the deposit would necessitate the account to exist and there are no provider references;
484	///   or
485	/// - `value` is so large it would cause the balance of `who` to overflow.
486	fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
487		if value.is_zero() {
488			return Self::PositiveImbalance::zero();
489		}
490
491		Self::try_mutate_account_handling_dust(
492			who,
493			false,
494			|account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
495				let ed = T::ExistentialDeposit::get();
496				ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
497
498				// defensive only: overflow should never happen, however in case it does, then this
499				// operation is a no-op.
500				account.free = match account.free.checked_add(&value) {
501					Some(x) => x,
502					None => return Ok(Self::PositiveImbalance::zero()),
503				};
504
505				Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
506				Ok(PositiveImbalance::new(value))
507			},
508		)
509		.unwrap_or_else(|_| Self::PositiveImbalance::zero())
510	}
511
512	/// Withdraw some free balance from an account, respecting existence requirements.
513	///
514	/// Is a no-op if value to be withdrawn is zero.
515	fn withdraw(
516		who: &T::AccountId,
517		value: Self::Balance,
518		reasons: WithdrawReasons,
519		liveness: ExistenceRequirement,
520	) -> result::Result<Self::NegativeImbalance, DispatchError> {
521		if value.is_zero() {
522			return Ok(NegativeImbalance::zero());
523		}
524
525		Self::try_mutate_account_handling_dust(
526			who,
527			false,
528			|account, _| -> Result<Self::NegativeImbalance, DispatchError> {
529				let new_free_account =
530					account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
531
532				// bail if we need to keep the account alive and this would kill it.
533				let ed = T::ExistentialDeposit::get();
534				let would_be_dead = new_free_account < ed;
535				let would_kill = would_be_dead && account.free >= ed;
536				ensure!(liveness == AllowDeath || !would_kill, Error::<T, I>::Expendability);
537
538				Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
539
540				account.free = new_free_account;
541
542				Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value });
543				Ok(NegativeImbalance::new(value))
544			},
545		)
546	}
547
548	/// Force the new free balance of a target account `who` to some new value `balance`.
549	fn make_free_balance_be(
550		who: &T::AccountId,
551		value: Self::Balance,
552	) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
553		Self::try_mutate_account_handling_dust(
554			who,
555			false,
556			|account,
557			 is_new|
558			 -> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, DispatchError> {
559				let ed = T::ExistentialDeposit::get();
560				// If we're attempting to set an existing account to less than ED, then
561				// bypass the entire operation. It's a no-op if you follow it through, but
562				// since this is an instance where we might account for a negative imbalance
563				// (in the dust cleaner of set_account) before we account for its actual
564				// equal and opposite cause (returned as an Imbalance), then in the
565				// instance that there's no other accounts on the system at all, we might
566				// underflow the issuance and our arithmetic will be off.
567				ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
568
569				let imbalance = if account.free <= value {
570					SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
571				} else {
572					SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
573				};
574				account.free = value;
575				Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free });
576				Ok(imbalance)
577			},
578		)
579		.unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero()))
580	}
581}
582
583/// Validates whether an account can create a reserve without violating
584/// liquidity constraints.
585///
586/// This method performs liquidity checks without modifying the account state.
587fn ensure_can_reserve<T: Config<I>, I: 'static>(
588	who: &T::AccountId,
589	value: T::Balance,
590	check_existential_deposit: bool,
591) -> DispatchResult {
592	let AccountData { free, .. } = Pallet::<T, I>::account(who);
593
594	// Early validation: Check sufficient free balance
595	let new_free_balance = free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
596
597	// Conditionally validate existential deposit preservation
598	if check_existential_deposit {
599		let existential_deposit = T::ExistentialDeposit::get();
600		ensure!(new_free_balance >= existential_deposit, Error::<T, I>::Expendability);
601	}
602
603	Ok(())
604}
605
606impl<T: Config<I>, I: 'static> ReservableCurrency<T::AccountId> for Pallet<T, I>
607where
608	T::Balance: MaybeSerializeDeserialize + Debug,
609{
610	/// Check if `who` can reserve `value` from their free balance.
611	///
612	/// Always `true` if value to be reserved is zero.
613	fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
614		if value.is_zero() {
615			return true;
616		}
617		ensure_can_reserve::<T, I>(who, value, true).is_ok()
618	}
619
620	fn reserved_balance(who: &T::AccountId) -> Self::Balance {
621		Self::account(who).reserved
622	}
623
624	/// Move `value` from the free balance from `who` to their reserved balance.
625	///
626	/// Is a no-op if value to be reserved is zero.
627	fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
628		if value.is_zero() {
629			return Ok(());
630		}
631
632		Self::try_mutate_account_handling_dust(who, false, |account, _| -> DispatchResult {
633			account.free =
634				account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
635			account.reserved =
636				account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
637
638			// Check if it is possible to reserve before trying to mutate the account
639			ensure_can_reserve::<T, I>(who, value, false)
640		})?;
641
642		Self::deposit_event(Event::Reserved { who: who.clone(), amount: value });
643		Ok(())
644	}
645
646	/// Unreserve some funds, returning any amount that was unable to be unreserved.
647	///
648	/// Is a no-op if the value to be unreserved is zero or the account does not exist.
649	///
650	/// NOTE: returns amount value which wasn't successfully unreserved.
651	fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
652		if value.is_zero() {
653			return Zero::zero();
654		}
655		if Self::total_balance(who).is_zero() {
656			return value;
657		}
658
659		let actual = match Self::mutate_account_handling_dust(who, false, |account| {
660			let actual = cmp::min(account.reserved, value);
661			account.reserved -= actual;
662			// defensive only: this can never fail since total issuance which is at least
663			// free+reserved fits into the same data type.
664			account.free = account.free.defensive_saturating_add(actual);
665			actual
666		}) {
667			Ok(x) => x,
668			Err(_) => {
669				// This should never happen since we don't alter the total amount in the account.
670				// If it ever does, then we should fail gracefully though, indicating that nothing
671				// could be done.
672				return value;
673			},
674		};
675
676		Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual });
677		value - actual
678	}
679
680	/// Slash from reserved balance, returning the negative imbalance created,
681	/// and any amount that was unable to be slashed.
682	///
683	/// Is a no-op if the value to be slashed is zero or the account does not exist.
684	fn slash_reserved(
685		who: &T::AccountId,
686		value: Self::Balance,
687	) -> (Self::NegativeImbalance, Self::Balance) {
688		if value.is_zero() {
689			return (NegativeImbalance::zero(), Zero::zero());
690		}
691		if Self::total_balance(who).is_zero() {
692			return (NegativeImbalance::zero(), value);
693		}
694
695		// NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an
696		//   account is attempted to be illegally destroyed.
697
698		match Self::mutate_account_handling_dust(who, false, |account| {
699			let actual = value.min(account.reserved);
700			account.reserved.saturating_reduce(actual);
701
702			// underflow should never happen, but it if does, there's nothing to be done here.
703			(NegativeImbalance::new(actual), value.saturating_sub(actual))
704		}) {
705			Ok((imbalance, not_slashed)) => {
706				Self::deposit_event(Event::Slashed {
707					who: who.clone(),
708					amount: value.saturating_sub(not_slashed),
709				});
710				(imbalance, not_slashed)
711			},
712			Err(_) => (Self::NegativeImbalance::zero(), value),
713		}
714	}
715
716	/// Move the reserved balance of one account into the balance of another, according to `status`.
717	///
718	/// Is a no-op if:
719	/// - the value to be moved is zero; or
720	/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
721	///
722	/// This is `Polite` and thus will not repatriate any funds which would lead the total balance
723	/// to be less than the frozen amount. Returns `Ok` with the actual amount of funds moved,
724	/// which may be less than `value` since the operation is done on a `BestEffort` basis.
725	fn repatriate_reserved(
726		slashed: &T::AccountId,
727		beneficiary: &T::AccountId,
728		value: Self::Balance,
729		status: Status,
730	) -> Result<Self::Balance, DispatchError> {
731		let actual =
732			Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?;
733		Ok(value.saturating_sub(actual))
734	}
735}
736
737impl<T: Config<I>, I: 'static> NamedReservableCurrency<T::AccountId> for Pallet<T, I>
738where
739	T::Balance: MaybeSerializeDeserialize + Debug,
740{
741	type ReserveIdentifier = T::ReserveIdentifier;
742
743	fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance {
744		let reserves = Self::reserves(who);
745		reserves
746			.binary_search_by_key(id, |data| data.id)
747			.map(|index| reserves[index].amount)
748			.unwrap_or_default()
749	}
750
751	/// Move `value` from the free balance from `who` to a named reserve balance.
752	///
753	/// Is a no-op if value to be reserved is zero.
754	fn reserve_named(
755		id: &Self::ReserveIdentifier,
756		who: &T::AccountId,
757		value: Self::Balance,
758	) -> DispatchResult {
759		if value.is_zero() {
760			return Ok(());
761		}
762
763		Reserves::<T, I>::try_mutate(who, |reserves| -> DispatchResult {
764			match reserves.binary_search_by_key(id, |data| data.id) {
765				Ok(index) => {
766					reserves[index].amount = reserves[index]
767						.amount
768						.checked_add(&value)
769						.ok_or(ArithmeticError::Overflow)?;
770				},
771				Err(index) => {
772					reserves
773						.try_insert(index, ReserveData { id: *id, amount: value })
774						.map_err(|_| Error::<T, I>::TooManyReserves)?;
775				},
776			};
777			<Self as ReservableCurrency<_>>::reserve(who, value)?;
778			Ok(())
779		})
780	}
781
782	/// Unreserve some funds, returning any amount that was unable to be unreserved.
783	///
784	/// Is a no-op if the value to be unreserved is zero.
785	fn unreserve_named(
786		id: &Self::ReserveIdentifier,
787		who: &T::AccountId,
788		value: Self::Balance,
789	) -> Self::Balance {
790		if value.is_zero() {
791			return Zero::zero();
792		}
793
794		Reserves::<T, I>::mutate_exists(who, |maybe_reserves| -> Self::Balance {
795			if let Some(reserves) = maybe_reserves.as_mut() {
796				match reserves.binary_search_by_key(id, |data| data.id) {
797					Ok(index) => {
798						let to_change = cmp::min(reserves[index].amount, value);
799
800						let remain = <Self as ReservableCurrency<_>>::unreserve(who, to_change);
801
802						// remain should always be zero but just to be defensive here.
803						let actual = to_change.defensive_saturating_sub(remain);
804
805						// `actual <= to_change` and `to_change <= amount`; qed;
806						reserves[index].amount -= actual;
807
808						if reserves[index].amount.is_zero() {
809							if reserves.len() == 1 {
810								// no more named reserves
811								*maybe_reserves = None;
812							} else {
813								// remove this named reserve
814								reserves.remove(index);
815							}
816						}
817
818						value - actual
819					},
820					Err(_) => value,
821				}
822			} else {
823				value
824			}
825		})
826	}
827
828	/// Slash from reserved balance, returning the negative imbalance created,
829	/// and any amount that was unable to be slashed.
830	///
831	/// Is a no-op if the value to be slashed is zero.
832	fn slash_reserved_named(
833		id: &Self::ReserveIdentifier,
834		who: &T::AccountId,
835		value: Self::Balance,
836	) -> (Self::NegativeImbalance, Self::Balance) {
837		if value.is_zero() {
838			return (NegativeImbalance::zero(), Zero::zero());
839		}
840
841		Reserves::<T, I>::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) {
842			match reserves.binary_search_by_key(id, |data| data.id) {
843				Ok(index) => {
844					let to_change = cmp::min(reserves[index].amount, value);
845
846					let (imb, remain) =
847						<Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
848
849					// remain should always be zero but just to be defensive here.
850					let actual = to_change.defensive_saturating_sub(remain);
851
852					// `actual <= to_change` and `to_change <= amount`; qed;
853					reserves[index].amount -= actual;
854
855					Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual });
856					(imb, value - actual)
857				},
858				Err(_) => (NegativeImbalance::zero(), value),
859			}
860		})
861	}
862
863	/// Move the reserved balance of one account into the balance of another, according to `status`.
864	/// If `status` is `Reserved`, the balance will be reserved with given `id`.
865	///
866	/// Is a no-op if:
867	/// - the value to be moved is zero; or
868	/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
869	fn repatriate_reserved_named(
870		id: &Self::ReserveIdentifier,
871		slashed: &T::AccountId,
872		beneficiary: &T::AccountId,
873		value: Self::Balance,
874		status: Status,
875	) -> Result<Self::Balance, DispatchError> {
876		if value.is_zero() {
877			return Ok(Zero::zero());
878		}
879
880		if slashed == beneficiary {
881			return match status {
882				Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
883				Status::Reserved => {
884					Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed)))
885				},
886			};
887		}
888
889		Reserves::<T, I>::try_mutate(slashed, |reserves| -> Result<Self::Balance, DispatchError> {
890			match reserves.binary_search_by_key(id, |data| data.id) {
891				Ok(index) => {
892					let to_change = cmp::min(reserves[index].amount, value);
893
894					let actual = if status == Status::Reserved {
895						// make it the reserved under same identifier
896						Reserves::<T, I>::try_mutate(
897							beneficiary,
898							|reserves| -> Result<T::Balance, DispatchError> {
899								match reserves.binary_search_by_key(id, |data| data.id) {
900									Ok(index) => {
901										let remain =
902											<Self as ReservableCurrency<_>>::repatriate_reserved(
903												slashed,
904												beneficiary,
905												to_change,
906												status,
907											)?;
908
909										// remain should always be zero but just to be defensive
910										// here.
911										let actual = to_change.defensive_saturating_sub(remain);
912
913										// this add can't overflow but just to be defensive.
914										reserves[index].amount =
915											reserves[index].amount.defensive_saturating_add(actual);
916
917										Ok(actual)
918									},
919									Err(index) => {
920										let remain =
921											<Self as ReservableCurrency<_>>::repatriate_reserved(
922												slashed,
923												beneficiary,
924												to_change,
925												status,
926											)?;
927
928										// remain should always be zero but just to be defensive
929										// here
930										let actual = to_change.defensive_saturating_sub(remain);
931
932										reserves
933											.try_insert(
934												index,
935												ReserveData { id: *id, amount: actual },
936											)
937											.map_err(|_| Error::<T, I>::TooManyReserves)?;
938
939										Ok(actual)
940									},
941								}
942							},
943						)?
944					} else {
945						let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(
946							slashed,
947							beneficiary,
948							to_change,
949							status,
950						)?;
951
952						// remain should always be zero but just to be defensive here
953						to_change.defensive_saturating_sub(remain)
954					};
955
956					// `actual <= to_change` and `to_change <= amount`; qed;
957					reserves[index].amount -= actual;
958
959					Ok(value - actual)
960				},
961				Err(_) => Ok(value),
962			}
963		})
964	}
965}
966
967impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
968where
969	T::Balance: MaybeSerializeDeserialize + Debug,
970{
971	type Moment = BlockNumberFor<T>;
972
973	type MaxLocks = T::MaxLocks;
974
975	// Set or alter a lock on the balance of `who`.
976	fn set_lock(
977		id: LockIdentifier,
978		who: &T::AccountId,
979		amount: T::Balance,
980		reasons: WithdrawReasons,
981	) {
982		if reasons.is_empty() || amount.is_zero() {
983			Self::remove_lock(id, who);
984			return;
985		}
986
987		let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
988		let mut locks = Self::locks(who)
989			.into_iter()
990			.filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
991			.collect::<Vec<_>>();
992		if let Some(lock) = new_lock {
993			locks.push(lock)
994		}
995		Self::update_locks(who, &locks[..]);
996	}
997
998	// Extend a lock on the balance of `who`.
999	// Is a no-op if lock amount is zero or `reasons` `is_none()`.
1000	fn extend_lock(
1001		id: LockIdentifier,
1002		who: &T::AccountId,
1003		amount: T::Balance,
1004		reasons: WithdrawReasons,
1005	) {
1006		if amount.is_zero() || reasons.is_empty() {
1007			return;
1008		}
1009		let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
1010		let mut locks = Self::locks(who)
1011			.into_iter()
1012			.filter_map(|l| {
1013				if l.id == id {
1014					new_lock.take().map(|nl| BalanceLock {
1015						id: l.id,
1016						amount: l.amount.max(nl.amount),
1017						reasons: l.reasons | nl.reasons,
1018					})
1019				} else {
1020					Some(l)
1021				}
1022			})
1023			.collect::<Vec<_>>();
1024		if let Some(lock) = new_lock {
1025			locks.push(lock)
1026		}
1027		Self::update_locks(who, &locks[..]);
1028	}
1029
1030	fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
1031		let mut locks = Self::locks(who);
1032		locks.retain(|l| l.id != id);
1033		Self::update_locks(who, &locks[..]);
1034	}
1035}
1036
1037impl<T: Config<I>, I: 'static> InspectLockableCurrency<T::AccountId> for Pallet<T, I> {
1038	fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance {
1039		Self::locks(who)
1040			.into_iter()
1041			.filter(|l| l.id == id)
1042			.fold(Zero::zero(), |acc, l| acc + l.amount)
1043	}
1044}