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 core::mem;
44 use frame_support::traits::{tokens::imbalance::TryMerge, SameOrOther};
45
46 #[must_use]
49 #[derive(RuntimeDebug, PartialEq, Eq)]
50 pub struct PositiveImbalance<T: Config<I>, I: 'static = ()>(T::Balance);
51
52 impl<T: Config<I>, I: 'static> PositiveImbalance<T, I> {
53 pub fn new(amount: T::Balance) -> Self {
55 PositiveImbalance(amount)
56 }
57 }
58
59 #[must_use]
62 #[derive(RuntimeDebug, PartialEq, Eq)]
63 pub struct NegativeImbalance<T: Config<I>, I: 'static = ()>(T::Balance);
64
65 impl<T: Config<I>, I: 'static> NegativeImbalance<T, I> {
66 pub fn new(amount: T::Balance) -> Self {
68 NegativeImbalance(amount)
69 }
70 }
71
72 impl<T: Config<I>, I: 'static> TryDrop for PositiveImbalance<T, I> {
73 fn try_drop(self) -> result::Result<(), Self> {
74 self.drop_zero()
75 }
76 }
77
78 impl<T: Config<I>, I: 'static> Default for PositiveImbalance<T, I> {
79 fn default() -> Self {
80 Self::zero()
81 }
82 }
83
84 impl<T: Config<I>, I: 'static> Imbalance<T::Balance> for PositiveImbalance<T, I> {
85 type Opposite = NegativeImbalance<T, I>;
86
87 fn zero() -> Self {
88 Self(Zero::zero())
89 }
90 fn drop_zero(self) -> result::Result<(), Self> {
91 if self.0.is_zero() {
92 Ok(())
93 } else {
94 Err(self)
95 }
96 }
97 fn split(self, amount: T::Balance) -> (Self, Self) {
98 let first = self.0.min(amount);
99 let second = self.0 - first;
100
101 mem::forget(self);
102 (Self(first), Self(second))
103 }
104 fn extract(&mut self, amount: T::Balance) -> Self {
105 let new = self.0.min(amount);
106 self.0 = self.0 - new;
107 Self(new)
108 }
109 fn merge(mut self, other: Self) -> Self {
110 self.0 = self.0.saturating_add(other.0);
111 mem::forget(other);
112
113 self
114 }
115 fn subsume(&mut self, other: Self) {
116 self.0 = self.0.saturating_add(other.0);
117 mem::forget(other);
118 }
119 fn offset(self, other: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
120 let (a, b) = (self.0, other.0);
121 mem::forget((self, other));
122
123 if a > b {
124 SameOrOther::Same(Self(a - b))
125 } else if b > a {
126 SameOrOther::Other(NegativeImbalance::new(b - a))
127 } else {
128 SameOrOther::None
129 }
130 }
131 fn peek(&self) -> T::Balance {
132 self.0
133 }
134 }
135
136 impl<T: Config<I>, I: 'static> TryMerge for PositiveImbalance<T, I> {
137 fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
138 Ok(self.merge(other))
139 }
140 }
141
142 impl<T: Config<I>, I: 'static> TryDrop for NegativeImbalance<T, I> {
143 fn try_drop(self) -> result::Result<(), Self> {
144 self.drop_zero()
145 }
146 }
147
148 impl<T: Config<I>, I: 'static> Default for NegativeImbalance<T, I> {
149 fn default() -> Self {
150 Self::zero()
151 }
152 }
153
154 impl<T: Config<I>, I: 'static> Imbalance<T::Balance> for NegativeImbalance<T, I> {
155 type Opposite = PositiveImbalance<T, I>;
156
157 fn zero() -> Self {
158 Self(Zero::zero())
159 }
160 fn drop_zero(self) -> result::Result<(), Self> {
161 if self.0.is_zero() {
162 Ok(())
163 } else {
164 Err(self)
165 }
166 }
167 fn split(self, amount: T::Balance) -> (Self, Self) {
168 let first = self.0.min(amount);
169 let second = self.0 - first;
170
171 mem::forget(self);
172 (Self(first), Self(second))
173 }
174 fn extract(&mut self, amount: T::Balance) -> Self {
175 let new = self.0.min(amount);
176 self.0 = self.0 - new;
177 Self(new)
178 }
179 fn merge(mut self, other: Self) -> Self {
180 self.0 = self.0.saturating_add(other.0);
181 mem::forget(other);
182
183 self
184 }
185 fn subsume(&mut self, other: Self) {
186 self.0 = self.0.saturating_add(other.0);
187 mem::forget(other);
188 }
189 fn offset(self, other: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
190 let (a, b) = (self.0, other.0);
191 mem::forget((self, other));
192
193 if a > b {
194 SameOrOther::Same(Self(a - b))
195 } else if b > a {
196 SameOrOther::Other(PositiveImbalance::new(b - a))
197 } else {
198 SameOrOther::None
199 }
200 }
201 fn peek(&self) -> T::Balance {
202 self.0
203 }
204 }
205
206 impl<T: Config<I>, I: 'static> TryMerge for NegativeImbalance<T, I> {
207 fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
208 Ok(self.merge(other))
209 }
210 }
211
212 impl<T: Config<I>, I: 'static> Drop for PositiveImbalance<T, I> {
213 fn drop(&mut self) {
215 if !self.0.is_zero() {
216 <super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_add(self.0));
217 Pallet::<T, I>::deposit_event(Event::<T, I>::Issued { amount: self.0 });
218 }
219 }
220 }
221
222 impl<T: Config<I>, I: 'static> Drop for NegativeImbalance<T, I> {
223 fn drop(&mut self) {
225 if !self.0.is_zero() {
226 <super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_sub(self.0));
227 Pallet::<T, I>::deposit_event(Event::<T, I>::Rescinded { amount: self.0 });
228 }
229 }
230 }
231}
232
233impl<T: Config<I>, I: 'static> Currency<T::AccountId> for Pallet<T, I>
234where
235 T::Balance: MaybeSerializeDeserialize + Debug,
236{
237 type Balance = T::Balance;
238 type PositiveImbalance = PositiveImbalance<T, I>;
239 type NegativeImbalance = NegativeImbalance<T, I>;
240
241 fn total_balance(who: &T::AccountId) -> Self::Balance {
242 Self::account(who).total()
243 }
244
245 fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
247 if value.is_zero() {
248 return true
249 }
250 Self::free_balance(who) >= value
251 }
252
253 fn total_issuance() -> Self::Balance {
254 TotalIssuance::<T, I>::get()
255 }
256
257 fn active_issuance() -> Self::Balance {
258 <Self as fungible::Inspect<_>>::active_issuance()
259 }
260
261 fn deactivate(amount: Self::Balance) {
262 <Self as fungible::Unbalanced<_>>::deactivate(amount);
263 }
264
265 fn reactivate(amount: Self::Balance) {
266 <Self as fungible::Unbalanced<_>>::reactivate(amount);
267 }
268
269 fn minimum_balance() -> Self::Balance {
270 T::ExistentialDeposit::get()
271 }
272
273 fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
276 if amount.is_zero() {
277 return PositiveImbalance::zero()
278 }
279 <TotalIssuance<T, I>>::mutate(|issued| {
280 *issued = issued.checked_sub(&amount).unwrap_or_else(|| {
281 amount = *issued;
282 Zero::zero()
283 });
284 });
285
286 Pallet::<T, I>::deposit_event(Event::<T, I>::Rescinded { amount });
287 PositiveImbalance::new(amount)
288 }
289
290 fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
294 if amount.is_zero() {
295 return NegativeImbalance::zero()
296 }
297 <TotalIssuance<T, I>>::mutate(|issued| {
298 *issued = issued.checked_add(&amount).unwrap_or_else(|| {
299 amount = Self::Balance::max_value() - *issued;
300 Self::Balance::max_value()
301 })
302 });
303
304 Pallet::<T, I>::deposit_event(Event::<T, I>::Issued { amount });
305 NegativeImbalance::new(amount)
306 }
307
308 fn free_balance(who: &T::AccountId) -> Self::Balance {
309 Self::account(who).free
310 }
311
312 fn ensure_can_withdraw(
316 who: &T::AccountId,
317 amount: T::Balance,
318 _reasons: WithdrawReasons,
319 new_balance: T::Balance,
320 ) -> DispatchResult {
321 if amount.is_zero() {
322 return Ok(())
323 }
324 ensure!(new_balance >= Self::account(who).frozen, Error::<T, I>::LiquidityRestrictions);
325 Ok(())
326 }
327
328 fn transfer(
331 transactor: &T::AccountId,
332 dest: &T::AccountId,
333 value: Self::Balance,
334 existence_requirement: ExistenceRequirement,
335 ) -> DispatchResult {
336 if value.is_zero() || transactor == dest {
337 return Ok(())
338 }
339 let keep_alive = match existence_requirement {
340 ExistenceRequirement::KeepAlive => Preserve,
341 ExistenceRequirement::AllowDeath => Expendable,
342 };
343 <Self as fungible::Mutate<_>>::transfer(transactor, dest, value, keep_alive)?;
344 Ok(())
345 }
346
347 fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
357 if value.is_zero() {
358 return (NegativeImbalance::zero(), Zero::zero())
359 }
360 if Self::total_balance(who).is_zero() {
361 return (NegativeImbalance::zero(), value)
362 }
363
364 let result = match Self::try_mutate_account_handling_dust(
365 who,
366 false,
367 |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> {
368 let ed = T::ExistentialDeposit::get();
370 let actual = match system::Pallet::<T>::can_dec_provider(who) {
371 true => value.min(account.free),
372 false => value.min(account.free.saturating_sub(ed)),
373 };
374 account.free.saturating_reduce(actual);
375 let remaining = value.saturating_sub(actual);
376 Ok((NegativeImbalance::new(actual), remaining))
377 },
378 ) {
379 Ok((imbalance, remaining)) => {
380 Self::deposit_event(Event::Slashed {
381 who: who.clone(),
382 amount: value.saturating_sub(remaining),
383 });
384 (imbalance, remaining)
385 },
386 Err(_) => (Self::NegativeImbalance::zero(), value),
387 };
388 result
389 }
390
391 fn deposit_into_existing(
395 who: &T::AccountId,
396 value: Self::Balance,
397 ) -> Result<Self::PositiveImbalance, DispatchError> {
398 if value.is_zero() {
399 return Ok(PositiveImbalance::zero())
400 }
401
402 Self::try_mutate_account_handling_dust(
403 who,
404 false,
405 |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
406 ensure!(!is_new, Error::<T, I>::DeadAccount);
407 account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
408 Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
409 Ok(PositiveImbalance::new(value))
410 },
411 )
412 }
413
414 fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
424 if value.is_zero() {
425 return Self::PositiveImbalance::zero()
426 }
427
428 Self::try_mutate_account_handling_dust(
429 who,
430 false,
431 |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
432 let ed = T::ExistentialDeposit::get();
433 ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
434
435 account.free = match account.free.checked_add(&value) {
438 Some(x) => x,
439 None => return Ok(Self::PositiveImbalance::zero()),
440 };
441
442 Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
443 Ok(PositiveImbalance::new(value))
444 },
445 )
446 .unwrap_or_else(|_| Self::PositiveImbalance::zero())
447 }
448
449 fn withdraw(
453 who: &T::AccountId,
454 value: Self::Balance,
455 reasons: WithdrawReasons,
456 liveness: ExistenceRequirement,
457 ) -> result::Result<Self::NegativeImbalance, DispatchError> {
458 if value.is_zero() {
459 return Ok(NegativeImbalance::zero())
460 }
461
462 Self::try_mutate_account_handling_dust(
463 who,
464 false,
465 |account, _| -> Result<Self::NegativeImbalance, DispatchError> {
466 let new_free_account =
467 account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
468
469 let ed = T::ExistentialDeposit::get();
471 let would_be_dead = new_free_account < ed;
472 let would_kill = would_be_dead && account.free >= ed;
473 ensure!(liveness == AllowDeath || !would_kill, Error::<T, I>::Expendability);
474
475 Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
476
477 account.free = new_free_account;
478
479 Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value });
480 Ok(NegativeImbalance::new(value))
481 },
482 )
483 }
484
485 fn make_free_balance_be(
487 who: &T::AccountId,
488 value: Self::Balance,
489 ) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
490 Self::try_mutate_account_handling_dust(
491 who,
492 false,
493 |account,
494 is_new|
495 -> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, DispatchError> {
496 let ed = T::ExistentialDeposit::get();
497 ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
505
506 let imbalance = if account.free <= value {
507 SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
508 } else {
509 SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
510 };
511 account.free = value;
512 Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free });
513 Ok(imbalance)
514 },
515 )
516 .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero()))
517 }
518}
519
520fn ensure_can_reserve<T: Config<I>, I: 'static>(
525 who: &T::AccountId,
526 value: T::Balance,
527 check_existential_deposit: bool,
528) -> DispatchResult {
529 let AccountData { free, .. } = Pallet::<T, I>::account(who);
530
531 let new_free_balance = free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
533
534 if check_existential_deposit {
536 let existential_deposit = T::ExistentialDeposit::get();
537 ensure!(new_free_balance >= existential_deposit, Error::<T, I>::Expendability);
538 }
539
540 Ok(())
541}
542
543impl<T: Config<I>, I: 'static> ReservableCurrency<T::AccountId> for Pallet<T, I>
544where
545 T::Balance: MaybeSerializeDeserialize + Debug,
546{
547 fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
551 if value.is_zero() {
552 return true
553 }
554 ensure_can_reserve::<T, I>(who, value, true).is_ok()
555 }
556
557 fn reserved_balance(who: &T::AccountId) -> Self::Balance {
558 Self::account(who).reserved
559 }
560
561 fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
565 if value.is_zero() {
566 return Ok(())
567 }
568
569 Self::try_mutate_account_handling_dust(who, false, |account, _| -> DispatchResult {
570 account.free =
571 account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
572 account.reserved =
573 account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
574
575 ensure_can_reserve::<T, I>(who, value, false)
577 })?;
578
579 Self::deposit_event(Event::Reserved { who: who.clone(), amount: value });
580 Ok(())
581 }
582
583 fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
589 if value.is_zero() {
590 return Zero::zero()
591 }
592 if Self::total_balance(who).is_zero() {
593 return value
594 }
595
596 let actual = match Self::mutate_account_handling_dust(who, false, |account| {
597 let actual = cmp::min(account.reserved, value);
598 account.reserved -= actual;
599 account.free = account.free.defensive_saturating_add(actual);
602 actual
603 }) {
604 Ok(x) => x,
605 Err(_) => {
606 return value
610 },
611 };
612
613 Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual });
614 value - actual
615 }
616
617 fn slash_reserved(
622 who: &T::AccountId,
623 value: Self::Balance,
624 ) -> (Self::NegativeImbalance, Self::Balance) {
625 if value.is_zero() {
626 return (NegativeImbalance::zero(), Zero::zero())
627 }
628 if Self::total_balance(who).is_zero() {
629 return (NegativeImbalance::zero(), value)
630 }
631
632 match Self::mutate_account_handling_dust(who, false, |account| {
636 let actual = value.min(account.reserved);
637 account.reserved.saturating_reduce(actual);
638
639 (NegativeImbalance::new(actual), value.saturating_sub(actual))
641 }) {
642 Ok((imbalance, not_slashed)) => {
643 Self::deposit_event(Event::Slashed {
644 who: who.clone(),
645 amount: value.saturating_sub(not_slashed),
646 });
647 (imbalance, not_slashed)
648 },
649 Err(_) => (Self::NegativeImbalance::zero(), value),
650 }
651 }
652
653 fn repatriate_reserved(
663 slashed: &T::AccountId,
664 beneficiary: &T::AccountId,
665 value: Self::Balance,
666 status: Status,
667 ) -> Result<Self::Balance, DispatchError> {
668 let actual =
669 Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?;
670 Ok(value.saturating_sub(actual))
671 }
672}
673
674impl<T: Config<I>, I: 'static> NamedReservableCurrency<T::AccountId> for Pallet<T, I>
675where
676 T::Balance: MaybeSerializeDeserialize + Debug,
677{
678 type ReserveIdentifier = T::ReserveIdentifier;
679
680 fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance {
681 let reserves = Self::reserves(who);
682 reserves
683 .binary_search_by_key(id, |data| data.id)
684 .map(|index| reserves[index].amount)
685 .unwrap_or_default()
686 }
687
688 fn reserve_named(
692 id: &Self::ReserveIdentifier,
693 who: &T::AccountId,
694 value: Self::Balance,
695 ) -> DispatchResult {
696 if value.is_zero() {
697 return Ok(())
698 }
699
700 Reserves::<T, I>::try_mutate(who, |reserves| -> DispatchResult {
701 match reserves.binary_search_by_key(id, |data| data.id) {
702 Ok(index) => {
703 reserves[index].amount = reserves[index]
704 .amount
705 .checked_add(&value)
706 .ok_or(ArithmeticError::Overflow)?;
707 },
708 Err(index) => {
709 reserves
710 .try_insert(index, ReserveData { id: *id, amount: value })
711 .map_err(|_| Error::<T, I>::TooManyReserves)?;
712 },
713 };
714 <Self as ReservableCurrency<_>>::reserve(who, value)?;
715 Ok(())
716 })
717 }
718
719 fn unreserve_named(
723 id: &Self::ReserveIdentifier,
724 who: &T::AccountId,
725 value: Self::Balance,
726 ) -> Self::Balance {
727 if value.is_zero() {
728 return Zero::zero()
729 }
730
731 Reserves::<T, I>::mutate_exists(who, |maybe_reserves| -> Self::Balance {
732 if let Some(reserves) = maybe_reserves.as_mut() {
733 match reserves.binary_search_by_key(id, |data| data.id) {
734 Ok(index) => {
735 let to_change = cmp::min(reserves[index].amount, value);
736
737 let remain = <Self as ReservableCurrency<_>>::unreserve(who, to_change);
738
739 let actual = to_change.defensive_saturating_sub(remain);
741
742 reserves[index].amount -= actual;
744
745 if reserves[index].amount.is_zero() {
746 if reserves.len() == 1 {
747 *maybe_reserves = None;
749 } else {
750 reserves.remove(index);
752 }
753 }
754
755 value - actual
756 },
757 Err(_) => value,
758 }
759 } else {
760 value
761 }
762 })
763 }
764
765 fn slash_reserved_named(
770 id: &Self::ReserveIdentifier,
771 who: &T::AccountId,
772 value: Self::Balance,
773 ) -> (Self::NegativeImbalance, Self::Balance) {
774 if value.is_zero() {
775 return (NegativeImbalance::zero(), Zero::zero())
776 }
777
778 Reserves::<T, I>::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) {
779 match reserves.binary_search_by_key(id, |data| data.id) {
780 Ok(index) => {
781 let to_change = cmp::min(reserves[index].amount, value);
782
783 let (imb, remain) =
784 <Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
785
786 let actual = to_change.defensive_saturating_sub(remain);
788
789 reserves[index].amount -= actual;
791
792 Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual });
793 (imb, value - actual)
794 },
795 Err(_) => (NegativeImbalance::zero(), value),
796 }
797 })
798 }
799
800 fn repatriate_reserved_named(
807 id: &Self::ReserveIdentifier,
808 slashed: &T::AccountId,
809 beneficiary: &T::AccountId,
810 value: Self::Balance,
811 status: Status,
812 ) -> Result<Self::Balance, DispatchError> {
813 if value.is_zero() {
814 return Ok(Zero::zero())
815 }
816
817 if slashed == beneficiary {
818 return match status {
819 Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
820 Status::Reserved =>
821 Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))),
822 }
823 }
824
825 Reserves::<T, I>::try_mutate(slashed, |reserves| -> Result<Self::Balance, DispatchError> {
826 match reserves.binary_search_by_key(id, |data| data.id) {
827 Ok(index) => {
828 let to_change = cmp::min(reserves[index].amount, value);
829
830 let actual = if status == Status::Reserved {
831 Reserves::<T, I>::try_mutate(
833 beneficiary,
834 |reserves| -> Result<T::Balance, DispatchError> {
835 match reserves.binary_search_by_key(id, |data| data.id) {
836 Ok(index) => {
837 let remain =
838 <Self as ReservableCurrency<_>>::repatriate_reserved(
839 slashed,
840 beneficiary,
841 to_change,
842 status,
843 )?;
844
845 let actual = to_change.defensive_saturating_sub(remain);
848
849 reserves[index].amount =
851 reserves[index].amount.defensive_saturating_add(actual);
852
853 Ok(actual)
854 },
855 Err(index) => {
856 let remain =
857 <Self as ReservableCurrency<_>>::repatriate_reserved(
858 slashed,
859 beneficiary,
860 to_change,
861 status,
862 )?;
863
864 let actual = to_change.defensive_saturating_sub(remain);
867
868 reserves
869 .try_insert(
870 index,
871 ReserveData { id: *id, amount: actual },
872 )
873 .map_err(|_| Error::<T, I>::TooManyReserves)?;
874
875 Ok(actual)
876 },
877 }
878 },
879 )?
880 } else {
881 let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(
882 slashed,
883 beneficiary,
884 to_change,
885 status,
886 )?;
887
888 to_change.defensive_saturating_sub(remain)
890 };
891
892 reserves[index].amount -= actual;
894
895 Ok(value - actual)
896 },
897 Err(_) => Ok(value),
898 }
899 })
900 }
901}
902
903impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
904where
905 T::Balance: MaybeSerializeDeserialize + Debug,
906{
907 type Moment = BlockNumberFor<T>;
908
909 type MaxLocks = T::MaxLocks;
910
911 fn set_lock(
913 id: LockIdentifier,
914 who: &T::AccountId,
915 amount: T::Balance,
916 reasons: WithdrawReasons,
917 ) {
918 if reasons.is_empty() || amount.is_zero() {
919 Self::remove_lock(id, who);
920 return
921 }
922
923 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
924 let mut locks = Self::locks(who)
925 .into_iter()
926 .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
927 .collect::<Vec<_>>();
928 if let Some(lock) = new_lock {
929 locks.push(lock)
930 }
931 Self::update_locks(who, &locks[..]);
932 }
933
934 fn extend_lock(
937 id: LockIdentifier,
938 who: &T::AccountId,
939 amount: T::Balance,
940 reasons: WithdrawReasons,
941 ) {
942 if amount.is_zero() || reasons.is_empty() {
943 return
944 }
945 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
946 let mut locks = Self::locks(who)
947 .into_iter()
948 .filter_map(|l| {
949 if l.id == id {
950 new_lock.take().map(|nl| BalanceLock {
951 id: l.id,
952 amount: l.amount.max(nl.amount),
953 reasons: l.reasons | nl.reasons,
954 })
955 } else {
956 Some(l)
957 }
958 })
959 .collect::<Vec<_>>();
960 if let Some(lock) = new_lock {
961 locks.push(lock)
962 }
963 Self::update_locks(who, &locks[..]);
964 }
965
966 fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
967 let mut locks = Self::locks(who);
968 locks.retain(|l| l.id != id);
969 Self::update_locks(who, &locks[..]);
970 }
971}
972
973impl<T: Config<I>, I: 'static> InspectLockableCurrency<T::AccountId> for Pallet<T, I> {
974 fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance {
975 Self::locks(who)
976 .into_iter()
977 .filter(|l| l.id == id)
978 .fold(Zero::zero(), |acc, l| acc + l.amount)
979 }
980}