1use 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
39mod 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 #[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 pub fn new(amount: T::Balance) -> Self {
61 PositiveImbalance(amount)
62 }
63 }
64
65 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
583fn 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 let new_free_balance = free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
596
597 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 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 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 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 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 account.free = account.free.defensive_saturating_add(actual);
665 actual
666 }) {
667 Ok(x) => x,
668 Err(_) => {
669 return value;
673 },
674 };
675
676 Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual });
677 value - actual
678 }
679
680 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 match Self::mutate_account_handling_dust(who, false, |account| {
699 let actual = value.min(account.reserved);
700 account.reserved.saturating_reduce(actual);
701
702 (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 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 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 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 let actual = to_change.defensive_saturating_sub(remain);
804
805 reserves[index].amount -= actual;
807
808 if reserves[index].amount.is_zero() {
809 if reserves.len() == 1 {
810 *maybe_reserves = None;
812 } else {
813 reserves.remove(index);
815 }
816 }
817
818 value - actual
819 },
820 Err(_) => value,
821 }
822 } else {
823 value
824 }
825 })
826 }
827
828 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 let actual = to_change.defensive_saturating_sub(remain);
851
852 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 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 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 let actual = to_change.defensive_saturating_sub(remain);
912
913 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 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 to_change.defensive_saturating_sub(remain)
954 };
955
956 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 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 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}