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_exists(
842 who,
843 |maybe_reserves| -> (Self::NegativeImbalance, Self::Balance) {
844 let Some(reserves) = maybe_reserves.as_mut() else {
845 return (NegativeImbalance::zero(), value);
846 };
847 match reserves.binary_search_by_key(id, |data| data.id) {
848 Ok(index) => {
849 let to_change = cmp::min(reserves[index].amount, value);
850
851 let (imb, remain) =
852 <Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
853
854 let actual = to_change.defensive_saturating_sub(remain);
856
857 reserves[index].amount -= actual;
859
860 if reserves[index].amount.is_zero() {
861 if reserves.len() == 1 {
862 *maybe_reserves = None;
864 } else {
865 reserves.remove(index);
867 }
868 }
869
870 (imb, value - actual)
871 },
872 Err(_) => (NegativeImbalance::zero(), value),
873 }
874 },
875 )
876 }
877
878 fn repatriate_reserved_named(
885 id: &Self::ReserveIdentifier,
886 slashed: &T::AccountId,
887 beneficiary: &T::AccountId,
888 value: Self::Balance,
889 status: Status,
890 ) -> Result<Self::Balance, DispatchError> {
891 if value.is_zero() {
892 return Ok(Zero::zero());
893 }
894
895 if slashed == beneficiary {
896 return match status {
897 Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
898 Status::Reserved => {
899 Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed)))
900 },
901 };
902 }
903
904 Reserves::<T, I>::try_mutate_exists(
905 slashed,
906 |maybe_reserves| -> Result<Self::Balance, DispatchError> {
907 let Some(reserves) = maybe_reserves.as_mut() else { return Ok(value) };
908 let Ok(index) = reserves.binary_search_by_key(id, |data| data.id) else {
909 return Ok(value);
910 };
911
912 let to_change = cmp::min(reserves[index].amount, value);
913
914 let actual = match status {
915 Status::Reserved => {
916 Reserves::<T, I>::try_mutate_exists(
918 beneficiary,
919 |maybe_reserves| -> Result<T::Balance, DispatchError> {
920 let reserves = maybe_reserves.get_or_insert_default();
921 let actual = match reserves.binary_search_by_key(id, |data| data.id)
922 {
923 Ok(index) => {
924 let remain =
925 <Self as ReservableCurrency<_>>::repatriate_reserved(
926 slashed,
927 beneficiary,
928 to_change,
929 status,
930 )?;
931
932 let actual = to_change.defensive_saturating_sub(remain);
935
936 reserves[index].amount =
938 reserves[index].amount.defensive_saturating_add(actual);
939
940 actual
941 },
942 Err(index) => {
943 let remain =
944 <Self as ReservableCurrency<_>>::repatriate_reserved(
945 slashed,
946 beneficiary,
947 to_change,
948 status,
949 )?;
950
951 let actual = to_change.defensive_saturating_sub(remain);
954
955 if !actual.is_zero() {
956 reserves
957 .try_insert(
958 index,
959 ReserveData { id: *id, amount: actual },
960 )
961 .map_err(|_| Error::<T, I>::TooManyReserves)?;
962 }
963
964 actual
965 },
966 };
967
968 if reserves.is_empty() {
969 *maybe_reserves = None;
970 }
971
972 Ok(actual)
973 },
974 )?
975 },
976 Status::Free => {
977 let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(
978 slashed,
979 beneficiary,
980 to_change,
981 status,
982 )?;
983
984 to_change.defensive_saturating_sub(remain)
986 },
987 };
988
989 reserves[index].amount -= actual;
991
992 if reserves[index].amount.is_zero() {
993 if reserves.len() == 1 {
994 *maybe_reserves = None;
996 } else {
997 reserves.remove(index);
999 }
1000 }
1001
1002 Ok(value - actual)
1003 },
1004 )
1005 }
1006}
1007
1008impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
1009where
1010 T::Balance: MaybeSerializeDeserialize + Debug,
1011{
1012 type Moment = BlockNumberFor<T>;
1013
1014 type MaxLocks = T::MaxLocks;
1015
1016 fn set_lock(
1018 id: LockIdentifier,
1019 who: &T::AccountId,
1020 amount: T::Balance,
1021 reasons: WithdrawReasons,
1022 ) {
1023 if reasons.is_empty() || amount.is_zero() {
1024 Self::remove_lock(id, who);
1025 return;
1026 }
1027
1028 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
1029 let mut locks = Self::locks(who)
1030 .into_iter()
1031 .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
1032 .collect::<Vec<_>>();
1033 if let Some(lock) = new_lock {
1034 locks.push(lock)
1035 }
1036 Self::update_locks(who, &locks[..]);
1037 }
1038
1039 fn extend_lock(
1042 id: LockIdentifier,
1043 who: &T::AccountId,
1044 amount: T::Balance,
1045 reasons: WithdrawReasons,
1046 ) {
1047 if amount.is_zero() || reasons.is_empty() {
1048 return;
1049 }
1050 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
1051 let mut locks = Self::locks(who)
1052 .into_iter()
1053 .filter_map(|l| {
1054 if l.id == id {
1055 new_lock.take().map(|nl| BalanceLock {
1056 id: l.id,
1057 amount: l.amount.max(nl.amount),
1058 reasons: l.reasons | nl.reasons,
1059 })
1060 } else {
1061 Some(l)
1062 }
1063 })
1064 .collect::<Vec<_>>();
1065 if let Some(lock) = new_lock {
1066 locks.push(lock)
1067 }
1068 Self::update_locks(who, &locks[..]);
1069 }
1070
1071 fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
1072 let mut locks = Self::locks(who);
1073 locks.retain(|l| l.id != id);
1074 Self::update_locks(who, &locks[..]);
1075 }
1076}
1077
1078impl<T: Config<I>, I: 'static> InspectLockableCurrency<T::AccountId> for Pallet<T, I> {
1079 fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance {
1080 Self::locks(who)
1081 .into_iter()
1082 .filter(|l| l.id == id)
1083 .fold(Zero::zero(), |acc, l| acc + l.amount)
1084 }
1085}