1use crate::{
23 ensure,
24 traits::{
25 tokens::{
26 misc::{
27 Balance, DepositConsequence,
28 Fortitude::{self, Force, Polite},
29 Precision::{self, BestEffort, Exact},
30 Preservation::{self, Expendable},
31 Provenance::{self, Extant},
32 WithdrawConsequence,
33 },
34 Imbalance as ImbalanceT,
35 },
36 SameOrOther, TryDrop,
37 },
38};
39use core::marker::PhantomData;
40use sp_arithmetic::traits::{CheckedAdd, CheckedSub, One, Zero};
41use sp_runtime::{
42 traits::{Bounded, Saturating},
43 ArithmeticError, DispatchError, TokenError,
44};
45
46use super::{Credit, Debt, HandleImbalanceDrop, Imbalance};
47
48pub trait Inspect<AccountId>: Sized {
50 type Balance: Balance;
52
53 fn total_issuance() -> Self::Balance;
55
56 fn active_issuance() -> Self::Balance {
59 Self::total_issuance()
60 }
61
62 fn minimum_balance() -> Self::Balance;
64
65 fn total_balance(who: &AccountId) -> Self::Balance;
77
78 fn balance(who: &AccountId) -> Self::Balance;
84
85 fn reducible_balance(
92 who: &AccountId,
93 preservation: Preservation,
94 force: Fortitude,
95 ) -> Self::Balance;
96
97 fn can_deposit(
104 who: &AccountId,
105 amount: Self::Balance,
106 provenance: Provenance,
107 ) -> DepositConsequence;
108
109 fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance>;
112}
113
114#[must_use]
116pub struct Dust<A, T: Inspect<A>>(pub T::Balance);
117
118impl<A, T: Balanced<A>> Dust<A, T> {
119 pub fn into_credit(self) -> Credit<A, T> {
121 Credit::<A, T>::new(self.0)
122 }
123}
124
125pub trait Unbalanced<AccountId>: Inspect<AccountId> {
134 fn handle_raw_dust(amount: Self::Balance) {
140 Self::handle_dust(Dust(amount.min(Self::minimum_balance().saturating_sub(One::one()))))
141 }
142
143 fn handle_dust(dust: Dust<AccountId, Self>);
146
147 fn write_balance(
164 who: &AccountId,
165 amount: Self::Balance,
166 ) -> Result<Option<Self::Balance>, DispatchError>;
167
168 fn set_total_issuance(amount: Self::Balance);
170
171 fn decrease_balance(
187 who: &AccountId,
188 mut amount: Self::Balance,
189 precision: Precision,
190 preservation: Preservation,
191 force: Fortitude,
192 ) -> Result<Self::Balance, DispatchError> {
193 let old_balance = Self::balance(who);
194 let reducible = Self::reducible_balance(who, preservation, force);
195 match precision {
196 BestEffort => amount = amount.min(reducible),
197 Exact => ensure!(reducible >= amount, TokenError::FundsUnavailable),
198 }
199
200 let new_balance = old_balance.checked_sub(&amount).ok_or(TokenError::FundsUnavailable)?;
201 if let Some(dust) = Self::write_balance(who, new_balance)? {
202 Self::handle_dust(Dust(dust));
203 }
204 Ok(old_balance.saturating_sub(new_balance))
205 }
206
207 fn increase_balance(
214 who: &AccountId,
215 amount: Self::Balance,
216 precision: Precision,
217 ) -> Result<Self::Balance, DispatchError> {
218 let old_balance = Self::balance(who);
219 let reserved = Self::total_balance(who).saturating_sub(old_balance);
220 let new_balance = if let BestEffort = precision {
221 let max_balance = Self::Balance::max_value().saturating_sub(reserved);
222 old_balance.saturating_add(amount).min(max_balance)
223 } else {
224 old_balance.checked_add(&amount).ok_or(ArithmeticError::Overflow)?
225 };
226 new_balance.checked_add(&reserved).ok_or(ArithmeticError::Overflow)?;
227
228 if new_balance < Self::minimum_balance() {
229 if let BestEffort = precision {
231 Ok(Default::default())
232 } else {
233 Err(TokenError::BelowMinimum.into())
234 }
235 } else {
236 if new_balance == old_balance {
237 Ok(Default::default())
238 } else {
239 if let Some(dust) = Self::write_balance(who, new_balance)? {
240 Self::handle_dust(Dust(dust));
241 }
242 Ok(new_balance.saturating_sub(old_balance))
243 }
244 }
245 }
246
247 fn deactivate(_: Self::Balance) {}
249
250 fn reactivate(_: Self::Balance) {}
252}
253
254pub trait Mutate<AccountId>: Inspect<AccountId> + Unbalanced<AccountId>
256where
257 AccountId: Eq,
258{
259 fn mint_into(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
262 Self::total_issuance().checked_add(&amount).ok_or(ArithmeticError::Overflow)?;
263 let actual = Self::increase_balance(who, amount, Exact)?;
264 Self::set_total_issuance(Self::total_issuance().saturating_add(actual));
265 Self::done_mint_into(who, amount);
266 Ok(actual)
267 }
268
269 fn burn_from(
276 who: &AccountId,
277 amount: Self::Balance,
278 preservation: Preservation,
279 precision: Precision,
280 force: Fortitude,
281 ) -> Result<Self::Balance, DispatchError> {
282 let actual = Self::reducible_balance(who, preservation, force).min(amount);
283 ensure!(actual == amount || precision == BestEffort, TokenError::FundsUnavailable);
284 Self::total_issuance().checked_sub(&actual).ok_or(ArithmeticError::Overflow)?;
285 let actual = Self::decrease_balance(who, actual, BestEffort, preservation, force)?;
286 Self::set_total_issuance(Self::total_issuance().saturating_sub(actual));
287 Self::done_burn_from(who, actual);
288 Ok(actual)
289 }
290
291 fn shelve(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
302 let actual = Self::reducible_balance(who, Expendable, Polite).min(amount);
303 ensure!(actual == amount, TokenError::FundsUnavailable);
304 Self::total_issuance().checked_sub(&actual).ok_or(ArithmeticError::Overflow)?;
305 let actual = Self::decrease_balance(who, actual, BestEffort, Expendable, Polite)?;
306 Self::set_total_issuance(Self::total_issuance().saturating_sub(actual));
307 Self::done_shelve(who, actual);
308 Ok(actual)
309 }
310
311 fn restore(who: &AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
322 Self::total_issuance().checked_add(&amount).ok_or(ArithmeticError::Overflow)?;
323 let actual = Self::increase_balance(who, amount, Exact)?;
324 Self::set_total_issuance(Self::total_issuance().saturating_add(actual));
325 Self::done_restore(who, amount);
326 Ok(actual)
327 }
328
329 fn transfer(
343 source: &AccountId,
344 dest: &AccountId,
345 amount: Self::Balance,
346 preservation: Preservation,
347 ) -> Result<Self::Balance, DispatchError> {
348 let _extra = Self::can_withdraw(source, amount).into_result(preservation != Expendable)?;
349 Self::can_deposit(dest, amount, Extant).into_result()?;
350 if source == dest || amount.is_zero() {
351 return Ok(amount);
352 }
353
354 let actual = Self::decrease_balance(source, amount, Exact, preservation, Polite)?;
355 Self::increase_balance(dest, actual, Exact)?;
356 Self::done_transfer(source, dest, actual);
357 Ok(actual)
358 }
359
360 fn set_balance(who: &AccountId, amount: Self::Balance) -> Self::Balance {
366 let b = Self::balance(who);
367 if b > amount {
368 Self::burn_from(who, b - amount, Expendable, BestEffort, Force)
369 .map(|d| b.saturating_sub(d))
370 } else {
371 Self::mint_into(who, amount - b).map(|d| b.saturating_add(d))
372 }
373 .unwrap_or(b)
374 }
375
376 fn done_mint_into(_who: &AccountId, _amount: Self::Balance) {}
377 fn done_burn_from(_who: &AccountId, _amount: Self::Balance) {}
378 fn done_shelve(_who: &AccountId, _amount: Self::Balance) {}
379 fn done_restore(_who: &AccountId, _amount: Self::Balance) {}
380 fn done_transfer(_source: &AccountId, _dest: &AccountId, _amount: Self::Balance) {}
381}
382
383pub struct IncreaseIssuance<AccountId, U>(PhantomData<(AccountId, U)>);
386impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::Balance>
387 for IncreaseIssuance<AccountId, U>
388{
389 fn handle(amount: U::Balance) {
390 U::set_total_issuance(U::total_issuance().saturating_add(amount))
391 }
392}
393
394pub struct DecreaseIssuance<AccountId, U>(PhantomData<(AccountId, U)>);
397impl<AccountId, U: Unbalanced<AccountId>> HandleImbalanceDrop<U::Balance>
398 for DecreaseIssuance<AccountId, U>
399{
400 fn handle(amount: U::Balance) {
401 U::set_total_issuance(U::total_issuance().saturating_sub(amount))
402 }
403}
404
405pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
410 type OnDropDebt: HandleImbalanceDrop<Self::Balance>;
412 type OnDropCredit: HandleImbalanceDrop<Self::Balance>;
415
416 fn rescind(amount: Self::Balance) -> Debt<AccountId, Self> {
422 let old = Self::total_issuance();
423 let new = old.saturating_sub(amount);
424 Self::set_total_issuance(new);
425 let delta = old - new;
426 Self::done_rescind(delta);
427 Imbalance::<Self::Balance, Self::OnDropDebt, Self::OnDropCredit>::new(delta)
428 }
429
430 fn issue(amount: Self::Balance) -> Credit<AccountId, Self> {
437 let old = Self::total_issuance();
438 let new = old.saturating_add(amount);
439 Self::set_total_issuance(new);
440 let delta = new - old;
441 Self::done_issue(delta);
442 Imbalance::<Self::Balance, Self::OnDropCredit, Self::OnDropDebt>::new(delta)
443 }
444
445 fn pair(
454 amount: Self::Balance,
455 ) -> Result<(Debt<AccountId, Self>, Credit<AccountId, Self>), DispatchError> {
456 let issued = Self::issue(amount);
457 let rescinded = Self::rescind(amount);
458 if issued.peek() != rescinded.peek() || issued.peek() != amount {
461 Err("Failed to issue and rescind equal amounts".into())
463 } else {
464 Ok((rescinded, issued))
465 }
466 }
467
468 fn deposit(
478 who: &AccountId,
479 value: Self::Balance,
480 precision: Precision,
481 ) -> Result<Debt<AccountId, Self>, DispatchError> {
482 let increase = Self::increase_balance(who, value, precision)?;
483 Self::done_deposit(who, increase);
484 Ok(Imbalance::<Self::Balance, Self::OnDropDebt, Self::OnDropCredit>::new(increase))
485 }
486
487 fn withdraw(
501 who: &AccountId,
502 value: Self::Balance,
503 precision: Precision,
504 preservation: Preservation,
505 force: Fortitude,
506 ) -> Result<Credit<AccountId, Self>, DispatchError> {
507 let decrease = Self::decrease_balance(who, value, precision, preservation, force)?;
508 Self::done_withdraw(who, decrease);
509 Ok(Imbalance::<Self::Balance, Self::OnDropCredit, Self::OnDropDebt>::new(decrease))
510 }
511
512 fn resolve(
519 who: &AccountId,
520 credit: Credit<AccountId, Self>,
521 ) -> Result<(), Credit<AccountId, Self>> {
522 let v = credit.peek();
523 let debt = match Self::deposit(who, v, Exact) {
524 Err(_) => return Err(credit),
525 Ok(d) => d,
526 };
527 let result = credit.offset(debt).try_drop();
528 debug_assert!(result.is_ok(), "ok deposit return must be equal to credit value; qed");
529 Ok(())
530 }
531
532 fn settle(
536 who: &AccountId,
537 debt: Debt<AccountId, Self>,
538 preservation: Preservation,
539 ) -> Result<Credit<AccountId, Self>, Debt<AccountId, Self>> {
540 let amount = debt.peek();
541 let credit = match Self::withdraw(who, amount, Exact, preservation, Polite) {
542 Err(_) => return Err(debt),
543 Ok(c) => c,
544 };
545
546 match credit.offset(debt) {
547 SameOrOther::None => Ok(Credit::<AccountId, Self>::zero()),
548 SameOrOther::Same(dust) => Ok(dust),
549 SameOrOther::Other(rest) => {
550 debug_assert!(false, "ok withdraw return must be at least debt value; qed");
551 Err(rest)
552 },
553 }
554 }
555
556 fn done_rescind(_amount: Self::Balance) {}
557 fn done_issue(_amount: Self::Balance) {}
558 fn done_deposit(_who: &AccountId, _amount: Self::Balance) {}
559 fn done_withdraw(_who: &AccountId, _amount: Self::Balance) {}
560}
561
562#[cfg(feature = "std")]
564impl<AccountId> Inspect<AccountId> for () {
565 type Balance = u32;
566 fn total_issuance() -> Self::Balance {
567 0
568 }
569 fn minimum_balance() -> Self::Balance {
570 0
571 }
572 fn total_balance(_: &AccountId) -> Self::Balance {
573 0
574 }
575 fn balance(_: &AccountId) -> Self::Balance {
576 0
577 }
578 fn reducible_balance(_: &AccountId, _: Preservation, _: Fortitude) -> Self::Balance {
579 0
580 }
581 fn can_deposit(_: &AccountId, _: Self::Balance, _: Provenance) -> DepositConsequence {
582 DepositConsequence::Success
583 }
584 fn can_withdraw(_: &AccountId, _: Self::Balance) -> WithdrawConsequence<Self::Balance> {
585 WithdrawConsequence::Success
586 }
587}
588
589#[cfg(feature = "std")]
591impl<AccountId> Unbalanced<AccountId> for () {
592 fn handle_dust(_: Dust<AccountId, Self>) {}
593 fn write_balance(
594 _: &AccountId,
595 _: Self::Balance,
596 ) -> Result<Option<Self::Balance>, DispatchError> {
597 Ok(None)
598 }
599 fn set_total_issuance(_: Self::Balance) {}
600}
601
602#[cfg(feature = "std")]
604impl<AccountId: Eq> Mutate<AccountId> for () {}
605
606#[cfg(test)]
607mod tests {
608 use super::*;
609 use core::cell::RefCell;
610
611 thread_local! {
612 static BALANCES: RefCell<[u64; 2]> = const { RefCell::new([0, 0]) };
613 }
614
615 const MIN_BALANCE: u64 = 10;
616
617 struct SweepingFungible;
622
623 impl Inspect<usize> for SweepingFungible {
624 type Balance = u64;
625 fn total_issuance() -> u64 {
626 BALANCES.with(|b| b.borrow().iter().sum())
627 }
628 fn minimum_balance() -> u64 {
629 MIN_BALANCE
630 }
631 fn total_balance(who: &usize) -> u64 {
632 Self::balance(who)
633 }
634 fn balance(who: &usize) -> u64 {
635 BALANCES.with(|b| b.borrow()[*who])
636 }
637 fn reducible_balance(who: &usize, _: Preservation, _: Fortitude) -> u64 {
638 Self::balance(who)
639 }
640 fn can_deposit(_: &usize, _: u64, _: Provenance) -> DepositConsequence {
641 DepositConsequence::Success
642 }
643 fn can_withdraw(_: &usize, _: u64) -> WithdrawConsequence<u64> {
644 WithdrawConsequence::Success
645 }
646 }
647
648 impl Unbalanced<usize> for SweepingFungible {
649 fn handle_dust(_: Dust<usize, Self>) {
650 unreachable!("the sweep is reported through `decrease_balance`, not trapped");
651 }
652 fn write_balance(_: &usize, _: u64) -> Result<Option<u64>, DispatchError> {
653 unimplemented!("`decrease_balance` and `increase_balance` are provided");
654 }
655 fn set_total_issuance(_: u64) {}
656 fn decrease_balance(
657 who: &usize,
658 amount: u64,
659 _: Precision,
660 _: Preservation,
661 _: Fortitude,
662 ) -> Result<u64, DispatchError> {
663 BALANCES.with(|b| {
664 let mut b = b.borrow_mut();
665 let remainder = b[*who].checked_sub(amount).ok_or(TokenError::FundsUnavailable)?;
666 let actual =
667 if !remainder.is_zero() && remainder < MIN_BALANCE { b[*who] } else { amount };
668 b[*who] -= actual;
669 Ok(actual)
670 })
671 }
672 fn increase_balance(who: &usize, amount: u64, _: Precision) -> Result<u64, DispatchError> {
673 BALANCES.with(|b| b.borrow_mut()[*who] += amount);
674 Ok(amount)
675 }
676 }
677
678 impl Mutate<usize> for SweepingFungible {}
679
680 #[test]
684 fn transfer_credits_the_amount_actually_debited() {
685 BALANCES.with(|b| *b.borrow_mut() = [100, 0]);
686
687 assert_eq!(
688 <SweepingFungible as Mutate<usize>>::transfer(&0, &1, 95, Preservation::Expendable),
689 Ok(100),
690 );
691
692 assert_eq!(<SweepingFungible as Inspect<usize>>::balance(&0), 0);
693 assert_eq!(<SweepingFungible as Inspect<usize>>::balance(&1), 100);
694 }
695
696 #[test]
698 fn transfer_without_a_sweep_moves_exactly_the_amount() {
699 BALANCES.with(|b| *b.borrow_mut() = [100, 0]);
700
701 assert_eq!(
702 <SweepingFungible as Mutate<usize>>::transfer(&0, &1, 90, Preservation::Expendable),
703 Ok(90),
704 );
705
706 assert_eq!(<SweepingFungible as Inspect<usize>>::balance(&0), 10);
707 assert_eq!(<SweepingFungible as Inspect<usize>>::balance(&1), 90);
708 }
709}