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 |account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), DispatchError> {
367 let ed = T::ExistentialDeposit::get();
369 let actual = match system::Pallet::<T>::can_dec_provider(who) {
370 true => value.min(account.free),
371 false => value.min(account.free.saturating_sub(ed)),
372 };
373 account.free.saturating_reduce(actual);
374 let remaining = value.saturating_sub(actual);
375 Ok((NegativeImbalance::new(actual), remaining))
376 },
377 ) {
378 Ok((imbalance, remaining)) => {
379 Self::deposit_event(Event::Slashed {
380 who: who.clone(),
381 amount: value.saturating_sub(remaining),
382 });
383 (imbalance, remaining)
384 },
385 Err(_) => (Self::NegativeImbalance::zero(), value),
386 };
387 result
388 }
389
390 fn deposit_into_existing(
394 who: &T::AccountId,
395 value: Self::Balance,
396 ) -> Result<Self::PositiveImbalance, DispatchError> {
397 if value.is_zero() {
398 return Ok(PositiveImbalance::zero())
399 }
400
401 Self::try_mutate_account_handling_dust(
402 who,
403 |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
404 ensure!(!is_new, Error::<T, I>::DeadAccount);
405 account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
406 Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
407 Ok(PositiveImbalance::new(value))
408 },
409 )
410 }
411
412 fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
422 if value.is_zero() {
423 return Self::PositiveImbalance::zero()
424 }
425
426 Self::try_mutate_account_handling_dust(
427 who,
428 |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
429 let ed = T::ExistentialDeposit::get();
430 ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
431
432 account.free = match account.free.checked_add(&value) {
435 Some(x) => x,
436 None => return Ok(Self::PositiveImbalance::zero()),
437 };
438
439 Self::deposit_event(Event::Deposit { who: who.clone(), amount: value });
440 Ok(PositiveImbalance::new(value))
441 },
442 )
443 .unwrap_or_else(|_| Self::PositiveImbalance::zero())
444 }
445
446 fn withdraw(
450 who: &T::AccountId,
451 value: Self::Balance,
452 reasons: WithdrawReasons,
453 liveness: ExistenceRequirement,
454 ) -> result::Result<Self::NegativeImbalance, DispatchError> {
455 if value.is_zero() {
456 return Ok(NegativeImbalance::zero())
457 }
458
459 Self::try_mutate_account_handling_dust(
460 who,
461 |account, _| -> Result<Self::NegativeImbalance, DispatchError> {
462 let new_free_account =
463 account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
464
465 let ed = T::ExistentialDeposit::get();
467 let would_be_dead = new_free_account < ed;
468 let would_kill = would_be_dead && account.free >= ed;
469 ensure!(liveness == AllowDeath || !would_kill, Error::<T, I>::Expendability);
470
471 Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
472
473 account.free = new_free_account;
474
475 Self::deposit_event(Event::Withdraw { who: who.clone(), amount: value });
476 Ok(NegativeImbalance::new(value))
477 },
478 )
479 }
480
481 fn make_free_balance_be(
483 who: &T::AccountId,
484 value: Self::Balance,
485 ) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
486 Self::try_mutate_account_handling_dust(
487 who,
488 |account,
489 is_new|
490 -> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, DispatchError> {
491 let ed = T::ExistentialDeposit::get();
492 ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
500
501 let imbalance = if account.free <= value {
502 SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
503 } else {
504 SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
505 };
506 account.free = value;
507 Self::deposit_event(Event::BalanceSet { who: who.clone(), free: account.free });
508 Ok(imbalance)
509 },
510 )
511 .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero()))
512 }
513}
514
515impl<T: Config<I>, I: 'static> ReservableCurrency<T::AccountId> for Pallet<T, I>
516where
517 T::Balance: MaybeSerializeDeserialize + Debug,
518{
519 fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
523 if value.is_zero() {
524 return true
525 }
526 Self::account(who).free.checked_sub(&value).map_or(false, |new_balance| {
527 new_balance >= T::ExistentialDeposit::get() &&
528 Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance)
529 .is_ok()
530 })
531 }
532
533 fn reserved_balance(who: &T::AccountId) -> Self::Balance {
534 Self::account(who).reserved
535 }
536
537 fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
541 if value.is_zero() {
542 return Ok(())
543 }
544
545 Self::try_mutate_account_handling_dust(who, |account, _| -> DispatchResult {
546 account.free =
547 account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
548 account.reserved =
549 account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
550 Self::ensure_can_withdraw(&who, value, WithdrawReasons::RESERVE, account.free)
551 })?;
552
553 Self::deposit_event(Event::Reserved { who: who.clone(), amount: value });
554 Ok(())
555 }
556
557 fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
563 if value.is_zero() {
564 return Zero::zero()
565 }
566 if Self::total_balance(who).is_zero() {
567 return value
568 }
569
570 let actual = match Self::mutate_account_handling_dust(who, |account| {
571 let actual = cmp::min(account.reserved, value);
572 account.reserved -= actual;
573 account.free = account.free.defensive_saturating_add(actual);
576 actual
577 }) {
578 Ok(x) => x,
579 Err(_) => {
580 return value
584 },
585 };
586
587 Self::deposit_event(Event::Unreserved { who: who.clone(), amount: actual });
588 value - actual
589 }
590
591 fn slash_reserved(
596 who: &T::AccountId,
597 value: Self::Balance,
598 ) -> (Self::NegativeImbalance, Self::Balance) {
599 if value.is_zero() {
600 return (NegativeImbalance::zero(), Zero::zero())
601 }
602 if Self::total_balance(who).is_zero() {
603 return (NegativeImbalance::zero(), value)
604 }
605
606 match Self::mutate_account_handling_dust(who, |account| {
610 let actual = value.min(account.reserved);
611 account.reserved.saturating_reduce(actual);
612
613 (NegativeImbalance::new(actual), value.saturating_sub(actual))
615 }) {
616 Ok((imbalance, not_slashed)) => {
617 Self::deposit_event(Event::Slashed {
618 who: who.clone(),
619 amount: value.saturating_sub(not_slashed),
620 });
621 (imbalance, not_slashed)
622 },
623 Err(_) => (Self::NegativeImbalance::zero(), value),
624 }
625 }
626
627 fn repatriate_reserved(
637 slashed: &T::AccountId,
638 beneficiary: &T::AccountId,
639 value: Self::Balance,
640 status: Status,
641 ) -> Result<Self::Balance, DispatchError> {
642 let actual =
643 Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?;
644 Ok(value.saturating_sub(actual))
645 }
646}
647
648impl<T: Config<I>, I: 'static> NamedReservableCurrency<T::AccountId> for Pallet<T, I>
649where
650 T::Balance: MaybeSerializeDeserialize + Debug,
651{
652 type ReserveIdentifier = T::ReserveIdentifier;
653
654 fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance {
655 let reserves = Self::reserves(who);
656 reserves
657 .binary_search_by_key(id, |data| data.id)
658 .map(|index| reserves[index].amount)
659 .unwrap_or_default()
660 }
661
662 fn reserve_named(
666 id: &Self::ReserveIdentifier,
667 who: &T::AccountId,
668 value: Self::Balance,
669 ) -> DispatchResult {
670 if value.is_zero() {
671 return Ok(())
672 }
673
674 Reserves::<T, I>::try_mutate(who, |reserves| -> DispatchResult {
675 match reserves.binary_search_by_key(id, |data| data.id) {
676 Ok(index) => {
677 reserves[index].amount = reserves[index]
678 .amount
679 .checked_add(&value)
680 .ok_or(ArithmeticError::Overflow)?;
681 },
682 Err(index) => {
683 reserves
684 .try_insert(index, ReserveData { id: *id, amount: value })
685 .map_err(|_| Error::<T, I>::TooManyReserves)?;
686 },
687 };
688 <Self as ReservableCurrency<_>>::reserve(who, value)?;
689 Ok(())
690 })
691 }
692
693 fn unreserve_named(
697 id: &Self::ReserveIdentifier,
698 who: &T::AccountId,
699 value: Self::Balance,
700 ) -> Self::Balance {
701 if value.is_zero() {
702 return Zero::zero()
703 }
704
705 Reserves::<T, I>::mutate_exists(who, |maybe_reserves| -> Self::Balance {
706 if let Some(reserves) = maybe_reserves.as_mut() {
707 match reserves.binary_search_by_key(id, |data| data.id) {
708 Ok(index) => {
709 let to_change = cmp::min(reserves[index].amount, value);
710
711 let remain = <Self as ReservableCurrency<_>>::unreserve(who, to_change);
712
713 let actual = to_change.defensive_saturating_sub(remain);
715
716 reserves[index].amount -= actual;
718
719 if reserves[index].amount.is_zero() {
720 if reserves.len() == 1 {
721 *maybe_reserves = None;
723 } else {
724 reserves.remove(index);
726 }
727 }
728
729 value - actual
730 },
731 Err(_) => value,
732 }
733 } else {
734 value
735 }
736 })
737 }
738
739 fn slash_reserved_named(
744 id: &Self::ReserveIdentifier,
745 who: &T::AccountId,
746 value: Self::Balance,
747 ) -> (Self::NegativeImbalance, Self::Balance) {
748 if value.is_zero() {
749 return (NegativeImbalance::zero(), Zero::zero())
750 }
751
752 Reserves::<T, I>::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) {
753 match reserves.binary_search_by_key(id, |data| data.id) {
754 Ok(index) => {
755 let to_change = cmp::min(reserves[index].amount, value);
756
757 let (imb, remain) =
758 <Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
759
760 let actual = to_change.defensive_saturating_sub(remain);
762
763 reserves[index].amount -= actual;
765
766 Self::deposit_event(Event::Slashed { who: who.clone(), amount: actual });
767 (imb, value - actual)
768 },
769 Err(_) => (NegativeImbalance::zero(), value),
770 }
771 })
772 }
773
774 fn repatriate_reserved_named(
781 id: &Self::ReserveIdentifier,
782 slashed: &T::AccountId,
783 beneficiary: &T::AccountId,
784 value: Self::Balance,
785 status: Status,
786 ) -> Result<Self::Balance, DispatchError> {
787 if value.is_zero() {
788 return Ok(Zero::zero())
789 }
790
791 if slashed == beneficiary {
792 return match status {
793 Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
794 Status::Reserved =>
795 Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))),
796 }
797 }
798
799 Reserves::<T, I>::try_mutate(slashed, |reserves| -> Result<Self::Balance, DispatchError> {
800 match reserves.binary_search_by_key(id, |data| data.id) {
801 Ok(index) => {
802 let to_change = cmp::min(reserves[index].amount, value);
803
804 let actual = if status == Status::Reserved {
805 Reserves::<T, I>::try_mutate(
807 beneficiary,
808 |reserves| -> Result<T::Balance, DispatchError> {
809 match reserves.binary_search_by_key(id, |data| data.id) {
810 Ok(index) => {
811 let remain =
812 <Self as ReservableCurrency<_>>::repatriate_reserved(
813 slashed,
814 beneficiary,
815 to_change,
816 status,
817 )?;
818
819 let actual = to_change.defensive_saturating_sub(remain);
822
823 reserves[index].amount =
825 reserves[index].amount.defensive_saturating_add(actual);
826
827 Ok(actual)
828 },
829 Err(index) => {
830 let remain =
831 <Self as ReservableCurrency<_>>::repatriate_reserved(
832 slashed,
833 beneficiary,
834 to_change,
835 status,
836 )?;
837
838 let actual = to_change.defensive_saturating_sub(remain);
841
842 reserves
843 .try_insert(
844 index,
845 ReserveData { id: *id, amount: actual },
846 )
847 .map_err(|_| Error::<T, I>::TooManyReserves)?;
848
849 Ok(actual)
850 },
851 }
852 },
853 )?
854 } else {
855 let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(
856 slashed,
857 beneficiary,
858 to_change,
859 status,
860 )?;
861
862 to_change.defensive_saturating_sub(remain)
864 };
865
866 reserves[index].amount -= actual;
868
869 Ok(value - actual)
870 },
871 Err(_) => Ok(value),
872 }
873 })
874 }
875}
876
877impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
878where
879 T::Balance: MaybeSerializeDeserialize + Debug,
880{
881 type Moment = BlockNumberFor<T>;
882
883 type MaxLocks = T::MaxLocks;
884
885 fn set_lock(
887 id: LockIdentifier,
888 who: &T::AccountId,
889 amount: T::Balance,
890 reasons: WithdrawReasons,
891 ) {
892 if reasons.is_empty() || amount.is_zero() {
893 Self::remove_lock(id, who);
894 return
895 }
896
897 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
898 let mut locks = Self::locks(who)
899 .into_iter()
900 .filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
901 .collect::<Vec<_>>();
902 if let Some(lock) = new_lock {
903 locks.push(lock)
904 }
905 Self::update_locks(who, &locks[..]);
906 }
907
908 fn extend_lock(
911 id: LockIdentifier,
912 who: &T::AccountId,
913 amount: T::Balance,
914 reasons: WithdrawReasons,
915 ) {
916 if amount.is_zero() || reasons.is_empty() {
917 return
918 }
919 let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
920 let mut locks = Self::locks(who)
921 .into_iter()
922 .filter_map(|l| {
923 if l.id == id {
924 new_lock.take().map(|nl| BalanceLock {
925 id: l.id,
926 amount: l.amount.max(nl.amount),
927 reasons: l.reasons | nl.reasons,
928 })
929 } else {
930 Some(l)
931 }
932 })
933 .collect::<Vec<_>>();
934 if let Some(lock) = new_lock {
935 locks.push(lock)
936 }
937 Self::update_locks(who, &locks[..]);
938 }
939
940 fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
941 let mut locks = Self::locks(who);
942 locks.retain(|l| l.id != id);
943 Self::update_locks(who, &locks[..]);
944 }
945}
946
947impl<T: Config<I>, I: 'static> InspectLockableCurrency<T::AccountId> for Pallet<T, I> {
948 fn balance_locked(id: LockIdentifier, who: &T::AccountId) -> Self::Balance {
949 Self::locks(who)
950 .into_iter()
951 .filter(|l| l.id == id)
952 .fold(Zero::zero(), |acc, l| acc + l.amount)
953 }
954}