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 impl<T: Config<I>, I: 'static> fungible::HandleImbalanceDrop<T::Balance>
233 for NegativeImbalance<T, I>
234 {
235 fn handle(amount: T::Balance) {
236 <super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_sub(amount));
237 Pallet::<T, I>::deposit_event(Event::<T, I>::BurnedDebt { amount });
238 }
239 }
240
241 impl<T: Config<I>, I: 'static> fungible::HandleImbalanceDrop<T::Balance>
242 for PositiveImbalance<T, I>
243 {
244 fn handle(amount: T::Balance) {
245 <super::TotalIssuance<T, I>>::mutate(|v| *v = v.saturating_add(amount));
246 Pallet::<T, I>::deposit_event(Event::<T, I>::MintedCredit { amount });
247 }
248 }
249}
250
251impl<T: Config<I>, I: 'static> Currency<T::AccountId> for Pallet<T, I>
252where
253 T::Balance: MaybeSerializeDeserialize + Debug,
254{
255 type Balance = T::Balance;
256 type PositiveImbalance = PositiveImbalance<T, I>;
257 type NegativeImbalance = NegativeImbalance<T, I>;
258
259 fn total_balance(who: &T::AccountId) -> Self::Balance {
260 Self::account(who).total()
261 }
262
263 fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
265 if value.is_zero() {
266 return true
267 }
268 Self::free_balance(who) >= value
269 }
270
271 fn total_issuance() -> Self::Balance {
272 TotalIssuance::<T, I>::get()
273 }
274
275 fn active_issuance() -> Self::Balance {
276 <Self as fungible::Inspect<_>>::active_issuance()
277 }
278
279 fn deactivate(amount: Self::Balance) {
280 <Self as fungible::Unbalanced<_>>::deactivate(amount);
281 }
282
283 fn reactivate(amount: Self::Balance) {
284 <Self as fungible::Unbalanced<_>>::reactivate(amount);
285 }
286
287 fn minimum_balance() -> Self::Balance {
288 T::ExistentialDeposit::get()
289 }
290
291 fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
294 if amount.is_zero() {
295 return PositiveImbalance::zero()
296 }
297 <TotalIssuance<T, I>>::mutate(|issued| {
298 *issued = issued.checked_sub(&amount).unwrap_or_else(|| {
299 amount = *issued;
300 Zero::zero()
301 });
302 });
303
304 Pallet::<T, I>::deposit_event(Event::<T, I>::Rescinded { amount });
305 PositiveImbalance::new(amount)
306 }
307
308 fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
312 if amount.is_zero() {
313 return NegativeImbalance::zero()
314 }
315 <TotalIssuance<T, I>>::mutate(|issued| {
316 *issued = issued.checked_add(&amount).unwrap_or_else(|| {
317 amount = Self::Balance::max_value() - *issued;
318 Self::Balance::max_value()
319 })
320 });
321
322 Pallet::<T, I>::deposit_event(Event::<T, I>::Issued { amount });
323 NegativeImbalance::new(amount)
324 }
325
326 fn free_balance(who: &T::AccountId) -> Self::Balance {
327 Self::account(who).free
328 }
329
330 fn ensure_can_withdraw(
334 who: &T::AccountId,
335 amount: T::Balance,
336 _reasons: WithdrawReasons,
337 new_balance: T::Balance,
338 ) -> DispatchResult {
339 if amount.is_zero() {
340 return Ok(())
341 }
342 ensure!(new_balance >= Self::account(who).frozen, Error::<T, I>::LiquidityRestrictions);
343 Ok(())
344 }
345
346 fn transfer(
349 transactor: &T::AccountId,
350 dest: &T::AccountId,
351 value: Self::Balance,
352 existence_requirement: ExistenceRequirement,
353 ) -> DispatchResult {
354 if value.is_zero() || transactor == dest {
355 return Ok(())
356 }
357 let keep_alive = match existence_requirement {
358 ExistenceRequirement::KeepAlive => Preserve,
359 ExistenceRequirement::AllowDeath => Expendable,
360 };
361 <Self as fungible::Mutate<_>>::transfer(transactor, dest, value, keep_alive)?;
362 Ok(())
363 }
364
365 fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
375 if value.is_zero() {
376 return (NegativeImbalance::zero(), Zero::zero())
377 }
378 if Self::total_balance(who).is_zero() {
379 return (NegativeImbalance::zero(), value)
380 }
381
382 let result = match Self::try_mutate_account_handling_dust(
383 who,
384 false,
385 |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> {
386 let ed = T::ExistentialDeposit::get();
388 let actual = match system::Pallet::<T>::can_dec_provider(who) {
389 true => value.min(account.free),
390 false => value.min(account.free.saturating_sub(ed)),
391 };
392 account.free.saturating_reduce(actual);
393 let remaining = value.saturating_sub(actual);
394 Ok((NegativeImbalance::new(actual), remaining))
395 },
396 ) {
397 Ok((imbalance, remaining)) => {
398 Self::deposit_event(Event::Slashed {
399 who: who.clone(),
400 amount: value.saturating_sub(remaining),
401 });
402 (imbalance, remaining)
403 },
404 Err(_) => (Self::NegativeImbalance::zero(), value),
405 };
406 result
407 }
408
409 fn deposit_into_existing(
413 who: &T::AccountId,
414 value: Self::Balance,
415 ) -> Result<Self::PositiveImbalance, DispatchError> {
416 if value.is_zero() {
417 return Ok(PositiveImbalance::zero())
418 }
419
420 Self::try_mutate_account_handling_dust(
421 who,
422 false,
423 |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
424 ensure!(!is_new, Error::<T, I>::DeadAccount);
425 account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
426 Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
427 Ok(PositiveImbalance::new(value))
428 },
429 )
430 }
431
432 fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
442 if value.is_zero() {
443 return Self::PositiveImbalance::zero()
444 }
445
446 Self::try_mutate_account_handling_dust(
447 who,
448 false,
449 |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
450 let ed = T::ExistentialDeposit::get();
451 ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
452
453 account.free = match account.free.checked_add(&value) {
456 Some(x) => x,
457 None => return Ok(Self::PositiveImbalance::zero()),
458 };
459
460 Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
461 Ok(PositiveImbalance::new(value))
462 },
463 )
464 .unwrap_or_else(|_| Self::PositiveImbalance::zero())
465 }
466
467 fn withdraw(
471 who: &T::AccountId,
472 value: Self::Balance,
473 reasons: WithdrawReasons,
474 liveness: ExistenceRequirement,
475 ) -> result::Result<Self::NegativeImbalance, DispatchError> {
476 if value.is_zero() {
477 return Ok(NegativeImbalance::zero())
478 }
479
480 Self::try_mutate_account_handling_dust(
481 who,
482 false,
483 |account, _| -> Result<Self::NegativeImbalance, DispatchError> {
484 let new_free_account =
485 account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
486
487 let ed = T::ExistentialDeposit::get();
489 let would_be_dead = new_free_account < ed;
490 let would_kill = would_be_dead && account.free >= ed;
491 ensure!(liveness == AllowDeath || !would_kill, Error::<T, I>::Expendability);
492
493 Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
494
495 account.free = new_free_account;
496
497 Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value });
498 Ok(NegativeImbalance::new(value))
499 },
500 )
501 }
502
503 fn make_free_balance_be(
505 who: &T::AccountId,
506 value: Self::Balance,
507 ) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
508 Self::try_mutate_account_handling_dust(
509 who,
510 false,
511 |account,
512 is_new|
513 -> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, DispatchError> {
514 let ed = T::ExistentialDeposit::get();
515 ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
523
524 let imbalance = if account.free <= value {
525 SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
526 } else {
527 SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
528 };
529 account.free = value;
530 Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free });
531 Ok(imbalance)
532 },
533 )
534 .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero()))
535 }
536}
537
538fn ensure_can_reserve<T: Config<I>, I: 'static>(
543 who: &T::AccountId,
544 value: T::Balance,
545 check_existential_deposit: bool,
546) -> DispatchResult {
547 let AccountData { free, .. } = Pallet::<T, I>::account(who);
548
549 let new_free_balance = free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
551
552 if check_existential_deposit {
554 let existential_deposit = T::ExistentialDeposit::get();
555 ensure!(new_free_balance >= existential_deposit, Error::<T, I>::Expendability);
556 }
557
558 Ok(())
559}
560
561impl<T: Config<I>, I: 'static> ReservableCurrency<T::AccountId> for Pallet<T, I>
562where
563 T::Balance: MaybeSerializeDeserialize + Debug,
564{
565 fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
569 if value.is_zero() {
570 return true
571 }
572 ensure_can_reserve::<T, I>(who, value, true).is_ok()
573 }
574
575 fn reserved_balance(who: &T::AccountId) -> Self::Balance {
576 Self::account(who).reserved
577 }
578
579 fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
583 if value.is_zero() {
584 return Ok(())
585 }
586
587 Self::try_mutate_account_handling_dust(who, false, |account, _| -> DispatchResult {
588 account.free =
589 account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
590 account.reserved =
591 account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
592
593 ensure_can_reserve::<T, I>(who, value, false)
595 })?;
596
597 Self::deposit_event(Event::Reserved { who: who.clone(), amount: value });
598 Ok(())
599 }
600
601 fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
607 if value.is_zero() {
608 return Zero::zero()
609 }
610 if Self::total_balance(who).is_zero() {
611 return value
612 }
613
614 let actual = match Self::mutate_account_handling_dust(who, false, |account| {
615 let actual = cmp::min(account.reserved, value);
616 account.reserved -= actual;
617 account.free = account.free.defensive_saturating_add(actual);
620 actual
621 }) {
622 Ok(x) => x,
623 Err(_) => {
624 return value
628 },
629 };
630
631 Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual });
632 value - actual
633 }
634
635 fn slash_reserved(
640 who: &T::AccountId,
641 value: Self::Balance,
642 ) -> (Self::NegativeImbalance, Self::Balance) {
643 if value.is_zero() {
644 return (NegativeImbalance::zero(), Zero::zero())
645 }
646 if Self::total_balance(who).is_zero() {
647 return (NegativeImbalance::zero(), value)
648 }
649
650 match Self::mutate_account_handling_dust(who, false, |account| {
654 let actual = value.min(account.reserved);
655 account.reserved.saturating_reduce(actual);
656
657 (NegativeImbalance::new(actual), value.saturating_sub(actual))
659 }) {
660 Ok((imbalance, not_slashed)) => {
661 Self::deposit_event(Event::Slashed {
662 who: who.clone(),
663 amount: value.saturating_sub(not_slashed),
664 });
665 (imbalance, not_slashed)
666 },
667 Err(_) => (Self::NegativeImbalance::zero(), value),
668 }
669 }
670
671 fn repatriate_reserved(
681 slashed: &T::AccountId,
682 beneficiary: &T::AccountId,
683 value: Self::Balance,
684 status: Status,
685 ) -> Result<Self::Balance, DispatchError> {
686 let actual =
687 Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?;
688 Ok(value.saturating_sub(actual))
689 }
690}
691
692impl<T: Config<I>, I: 'static> NamedReservableCurrency<T::AccountId> for Pallet<T, I>
693where
694 T::Balance: MaybeSerializeDeserialize + Debug,
695{
696 type ReserveIdentifier = T::ReserveIdentifier;
697
698 fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance {
699 let reserves = Self::reserves(who);
700 reserves
701 .binary_search_by_key(id, |data| data.id)
702 .map(|index| reserves[index].amount)
703 .unwrap_or_default()
704 }
705
706 fn reserve_named(
710 id: &Self::ReserveIdentifier,
711 who: &T::AccountId,
712 value: Self::Balance,
713 ) -> DispatchResult {
714 if value.is_zero() {
715 return Ok(())
716 }
717
718 Reserves::<T, I>::try_mutate(who, |reserves| -> DispatchResult {
719 match reserves.binary_search_by_key(id, |data| data.id) {
720 Ok(index) => {
721 reserves[index].amount = reserves[index]
722 .amount
723 .checked_add(&value)
724 .ok_or(ArithmeticError::Overflow)?;
725 },
726 Err(index) => {
727 reserves
728 .try_insert(index, ReserveData { id: *id, amount: value })
729 .map_err(|_| Error::<T, I>::TooManyReserves)?;
730 },
731 };
732 <Self as ReservableCurrency<_>>::reserve(who, value)?;
733 Ok(())
734 })
735 }
736
737 fn unreserve_named(
741 id: &Self::ReserveIdentifier,
742 who: &T::AccountId,
743 value: Self::Balance,
744 ) -> Self::Balance {
745 if value.is_zero() {
746 return Zero::zero()
747 }
748
749 Reserves::<T, I>::mutate_exists(who, |maybe_reserves| -> Self::Balance {
750 if let Some(reserves) = maybe_reserves.as_mut() {
751 match reserves.binary_search_by_key(id, |data| data.id) {
752 Ok(index) => {
753 let to_change = cmp::min(reserves[index].amount, value);
754
755 let remain = <Self as ReservableCurrency<_>>::unreserve(who, to_change);
756
757 let actual = to_change.defensive_saturating_sub(remain);
759
760 reserves[index].amount -= actual;
762
763 if reserves[index].amount.is_zero() {
764 if reserves.len() == 1 {
765 *maybe_reserves = None;
767 } else {
768 reserves.remove(index);
770 }
771 }
772
773 value - actual
774 },
775 Err(_) => value,
776 }
777 } else {
778 value
779 }
780 })
781 }
782
783 fn slash_reserved_named(
788 id: &Self::ReserveIdentifier,
789 who: &T::AccountId,
790 value: Self::Balance,
791 ) -> (Self::NegativeImbalance, Self::Balance) {
792 if value.is_zero() {
793 return (NegativeImbalance::zero(), Zero::zero())
794 }
795
796 Reserves::<T, I>::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) {
797 match reserves.binary_search_by_key(id, |data| data.id) {
798 Ok(index) => {
799 let to_change = cmp::min(reserves[index].amount, value);
800
801 let (imb, remain) =
802 <Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
803
804 let actual = to_change.defensive_saturating_sub(remain);
806
807 reserves[index].amount -= actual;
809
810 Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual });
811 (imb, value - actual)
812 },
813 Err(_) => (NegativeImbalance::zero(), value),
814 }
815 })
816 }
817
818 fn repatriate_reserved_named(
825 id: &Self::ReserveIdentifier,
826 slashed: &T::AccountId,
827 beneficiary: &T::AccountId,
828 value: Self::Balance,
829 status: Status,
830 ) -> Result<Self::Balance, DispatchError> {
831 if value.is_zero() {
832 return Ok(Zero::zero())
833 }
834
835 if slashed == beneficiary {
836 return match status {
837 Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
838 Status::Reserved =>
839 Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))),
840 }
841 }
842
843 Reserves::<T, I>::try_mutate(slashed, |reserves| -> Result<Self::Balance, DispatchError> {
844 match reserves.binary_search_by_key(id, |data| data.id) {
845 Ok(index) => {
846 let to_change = cmp::min(reserves[index].amount, value);
847
848 let actual = if status == Status::Reserved {
849 Reserves::<T, I>::try_mutate(
851 beneficiary,
852 |reserves| -> Result<T::Balance, DispatchError> {
853 match reserves.binary_search_by_key(id, |data| data.id) {
854 Ok(index) => {
855 let remain =
856 <Self as ReservableCurrency<_>>::repatriate_reserved(
857 slashed,
858 beneficiary,
859 to_change,
860 status,
861 )?;
862
863 let actual = to_change.defensive_saturating_sub(remain);
866
867 reserves[index].amount =
869 reserves[index].amount.defensive_saturating_add(actual);
870
871 Ok(actual)
872 },
873 Err(index) => {
874 let remain =
875 <Self as ReservableCurrency<_>>::repatriate_reserved(
876 slashed,
877 beneficiary,
878 to_change,
879 status,
880 )?;
881
882 let actual = to_change.defensive_saturating_sub(remain);
885
886 reserves
887 .try_insert(
888 index,
889 ReserveData { id: *id, amount: actual },
890 )
891 .map_err(|_| Error::<T, I>::TooManyReserves)?;
892
893 Ok(actual)
894 },
895 }
896 },
897 )?
898 } else {
899 let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(
900 slashed,
901 beneficiary,
902 to_change,
903 status,
904 )?;
905
906 to_change.defensive_saturating_sub(remain)
908 };
909
910 reserves[index].amount -= actual;
912
913 Ok(value - actual)
914 },
915 Err(_) => Ok(value),
916 }
917 })
918 }
919}
920
921impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
922where
923 T::Balance: MaybeSerializeDeserialize + Debug,
924{
925 type Moment = BlockNumberFor<T>;
926
927 type MaxLocks = T::MaxLocks;
928
929 fn set_lock(
931 id: LockIdentifier,
932 who: &T::AccountId,
933 amount: T::Balance,
934 reasons: WithdrawReasons,
935 ) {
936 if reasons.is_empty() || amount.is_zero() {
937 Self::remove_lock(id, who);
938 return
939 }
940
941 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
942 let mut locks = Self::locks(who)
943 .into_iter()
944 .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
945 .collect::<Vec<_>>();
946 if let Some(lock) = new_lock {
947 locks.push(lock)
948 }
949 Self::update_locks(who, &locks[..]);
950 }
951
952 fn extend_lock(
955 id: LockIdentifier,
956 who: &T::AccountId,
957 amount: T::Balance,
958 reasons: WithdrawReasons,
959 ) {
960 if amount.is_zero() || reasons.is_empty() {
961 return
962 }
963 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
964 let mut locks = Self::locks(who)
965 .into_iter()
966 .filter_map(|l| {
967 if l.id == id {
968 new_lock.take().map(|nl| BalanceLock {
969 id: l.id,
970 amount: l.amount.max(nl.amount),
971 reasons: l.reasons | nl.reasons,
972 })
973 } else {
974 Some(l)
975 }
976 })
977 .collect::<Vec<_>>();
978 if let Some(lock) = new_lock {
979 locks.push(lock)
980 }
981 Self::update_locks(who, &locks[..]);
982 }
983
984 fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
985 let mut locks = Self::locks(who);
986 locks.retain(|l| l.id != id);
987 Self::update_locks(who, &locks[..]);
988 }
989}
990
991impl<T: Config<I>, I: 'static> InspectLockableCurrency<T::AccountId> for Pallet<T, I> {
992 fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance {
993 Self::locks(who)
994 .into_iter()
995 .filter(|l| l.id == id)
996 .fold(Zero::zero(), |acc, l| acc + l.amount)
997 }
998}