1use super::*;
21use alloc::vec;
22use frame_support::{defensive, traits::Get, BoundedVec};
23
24#[must_use]
25pub(super) enum DeadConsequence {
26 Remove,
27 Keep,
28}
29
30use DeadConsequence::*;
31
32impl<T: Config<I>, I: 'static> Pallet<T, I> {
34 pub fn adjust_extra(
38 id: T::AssetId,
39 who: impl core::borrow::Borrow<T::AccountId>,
40 ) -> Option<ExtraMutator<T, I>> {
41 ExtraMutator::maybe_new(id, who)
42 }
43
44 pub fn balance(id: T::AssetId, who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
46 Self::maybe_balance(id, who).unwrap_or_default()
47 }
48
49 pub fn maybe_balance(
51 id: T::AssetId,
52 who: impl core::borrow::Borrow<T::AccountId>,
53 ) -> Option<T::Balance> {
54 Account::<T, I>::get(id, who.borrow()).map(|a| a.balance)
55 }
56
57 pub fn total_supply(id: T::AssetId) -> T::Balance {
59 Self::maybe_total_supply(id).unwrap_or_default()
60 }
61
62 pub fn maybe_total_supply(id: T::AssetId) -> Option<T::Balance> {
64 Asset::<T, I>::get(id).map(|x| x.supply)
65 }
66
67 pub(super) fn new_account(
68 who: &T::AccountId,
69 d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
70 maybe_deposit: Option<(&T::AccountId, DepositBalanceOf<T, I>)>,
71 ) -> Result<ExistenceReasonOf<T, I>, DispatchError> {
72 let accounts = d.accounts.checked_add(1).ok_or(ArithmeticError::Overflow)?;
73 let reason = if let Some((depositor, deposit)) = maybe_deposit {
74 if depositor == who {
75 ExistenceReason::DepositHeld(deposit)
76 } else {
77 ExistenceReason::DepositFrom(depositor.clone(), deposit)
78 }
79 } else if d.is_sufficient {
80 frame_system::Pallet::<T>::inc_sufficients(who);
81 d.sufficients.saturating_inc();
82 ExistenceReason::Sufficient
83 } else {
84 frame_system::Pallet::<T>::inc_consumers(who)
85 .map_err(|_| Error::<T, I>::UnavailableConsumer)?;
86 if !frame_system::Pallet::<T>::can_inc_consumer(who) {
89 frame_system::Pallet::<T>::dec_consumers(who);
90 return Err(Error::<T, I>::UnavailableConsumer.into())
91 }
92 ExistenceReason::Consumer
93 };
94 d.accounts = accounts;
95 Ok(reason)
96 }
97
98 pub(super) fn ensure_account_can_die(id: T::AssetId, who: &T::AccountId) -> DispatchResult {
99 ensure!(
100 T::Holder::balance_on_hold(id.clone(), who).is_none(),
101 Error::<T, I>::ContainsHolds
102 );
103 ensure!(T::Freezer::frozen_balance(id, who).is_none(), Error::<T, I>::ContainsFreezes);
104 Ok(())
105 }
106
107 pub(super) fn dead_account(
108 who: &T::AccountId,
109 d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
110 reason: &ExistenceReasonOf<T, I>,
111 force: bool,
112 ) -> DeadConsequence {
113 use ExistenceReason::*;
114
115 match *reason {
116 Consumer => frame_system::Pallet::<T>::dec_consumers(who),
117 Sufficient => {
118 d.sufficients = d.sufficients.saturating_sub(1);
119 frame_system::Pallet::<T>::dec_sufficients(who);
120 },
121 DepositRefunded => {},
122 DepositHeld(_) | DepositFrom(..) if !force => return Keep,
123 DepositHeld(_) | DepositFrom(..) => {},
124 }
125 d.accounts = d.accounts.saturating_sub(1);
126 Remove
127 }
128
129 pub(super) fn can_increase(
137 id: T::AssetId,
138 who: &T::AccountId,
139 amount: T::Balance,
140 increase_supply: bool,
141 ) -> DepositConsequence {
142 let details = match Asset::<T, I>::get(&id) {
143 Some(details) => details,
144 None => return DepositConsequence::UnknownAsset,
145 };
146 if details.status == AssetStatus::Destroying {
147 return DepositConsequence::UnknownAsset
148 }
149 if increase_supply && details.supply.checked_add(&amount).is_none() {
150 return DepositConsequence::Overflow
151 }
152 if let Some(account) = Account::<T, I>::get(id, who) {
153 if account.status.is_blocked() {
154 return DepositConsequence::Blocked
155 }
156 if account.balance.checked_add(&amount).is_none() {
157 return DepositConsequence::Overflow
158 }
159 } else {
160 if amount < details.min_balance {
161 return DepositConsequence::BelowMinimum
162 }
163 if !details.is_sufficient && !frame_system::Pallet::<T>::can_accrue_consumers(who, 2) {
164 return DepositConsequence::CannotCreate
165 }
166 if details.is_sufficient && details.sufficients.checked_add(1).is_none() {
167 return DepositConsequence::Overflow
168 }
169 }
170
171 DepositConsequence::Success
172 }
173
174 pub(super) fn can_decrease(
176 id: T::AssetId,
177 who: &T::AccountId,
178 amount: T::Balance,
179 keep_alive: bool,
180 ) -> WithdrawConsequence<T::Balance> {
181 use WithdrawConsequence::*;
182 let details = match Asset::<T, I>::get(&id) {
183 Some(details) => details,
184 None => return UnknownAsset,
185 };
186 if details.supply.checked_sub(&amount).is_none() {
187 return Underflow
188 }
189 if details.status == AssetStatus::Frozen {
190 return Frozen
191 }
192 if details.status == AssetStatus::Destroying {
193 return UnknownAsset
194 }
195 if amount.is_zero() {
196 return Success
197 }
198 let account = match Account::<T, I>::get(&id, who) {
199 Some(a) => a,
200 None => return BalanceLow,
201 };
202 if account.status.is_frozen() {
203 return Frozen
204 }
205 if let Some(rest) = account.balance.checked_sub(&amount) {
206 match (
207 T::Holder::balance_on_hold(id.clone(), who),
208 T::Freezer::frozen_balance(id.clone(), who),
209 ) {
210 (None, None) =>
211 if rest < details.min_balance {
212 if keep_alive {
213 WouldDie
214 } else {
215 ReducedToZero(rest)
216 }
217 } else {
218 Success
219 },
220 (maybe_held, maybe_frozen) => {
221 let frozen = maybe_frozen.unwrap_or_default();
222 let held = maybe_held.unwrap_or_default();
223
224 let untouchable = frozen.saturating_sub(held).max(details.min_balance);
227 if rest < untouchable {
228 if !frozen.is_zero() {
229 Frozen
230 } else {
231 WouldDie
232 }
233 } else {
234 Success
235 }
236 },
237 }
238 } else {
239 BalanceLow
240 }
241 }
242
243 pub(super) fn reducible_balance(
246 id: T::AssetId,
247 who: &T::AccountId,
248 keep_alive: bool,
249 ) -> Result<T::Balance, DispatchError> {
250 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
251 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
252
253 let account = Account::<T, I>::get(&id, who).ok_or(Error::<T, I>::NoAccount)?;
254 ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
255
256 let untouchable = match (
257 T::Holder::balance_on_hold(id.clone(), who),
258 T::Freezer::frozen_balance(id.clone(), who),
259 keep_alive,
260 ) {
261 (None, None, true) => details.min_balance,
262 (None, None, false) => Zero::zero(),
263 (maybe_held, maybe_frozen, _) => {
264 let held = maybe_held.unwrap_or_default();
265 let frozen = maybe_frozen.unwrap_or_default();
266 frozen.saturating_sub(held).max(details.min_balance)
267 },
268 };
269 let amount = account.balance.saturating_sub(untouchable);
270
271 Ok(amount.min(details.supply))
272 }
273
274 pub(super) fn prep_debit(
290 id: T::AssetId,
291 target: &T::AccountId,
292 amount: T::Balance,
293 f: DebitFlags,
294 ) -> Result<T::Balance, DispatchError> {
295 let actual = Self::reducible_balance(id.clone(), target, f.keep_alive)?.min(amount);
296 ensure!(f.best_effort || actual >= amount, Error::<T, I>::BalanceLow);
297
298 let conseq = Self::can_decrease(id, target, actual, f.keep_alive);
299 let actual = match conseq.into_result(f.keep_alive) {
300 Ok(dust) => actual.saturating_add(dust), Err(e) => {
302 debug_assert!(false, "passed from reducible_balance; qed");
303 return Err(e)
304 },
305 };
306
307 Ok(actual)
308 }
309
310 pub(super) fn prep_credit(
326 id: T::AssetId,
327 dest: &T::AccountId,
328 amount: T::Balance,
329 debit: T::Balance,
330 burn_dust: bool,
331 ) -> Result<(T::Balance, Option<T::Balance>), DispatchError> {
332 let (credit, maybe_burn) = match (burn_dust, debit.checked_sub(&amount)) {
333 (true, Some(dust)) => (amount, Some(dust)),
334 _ => (debit, None),
335 };
336 Self::can_increase(id, dest, credit, false).into_result()?;
337 Ok((credit, maybe_burn))
338 }
339
340 pub(super) fn do_touch(
345 id: T::AssetId,
346 who: T::AccountId,
347 depositor: T::AccountId,
348 check_depositor: bool,
349 ) -> DispatchResult {
350 ensure!(!Account::<T, I>::contains_key(&id, &who), Error::<T, I>::AlreadyExists);
351 let deposit = T::AssetAccountDeposit::get();
352 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
353 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
354 ensure!(
355 !check_depositor || &depositor == &details.admin || &depositor == &details.freezer,
356 Error::<T, I>::NoPermission
357 );
358 let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?;
359 T::Currency::reserve(&depositor, deposit)?;
360 Asset::<T, I>::insert(&id, details);
361 Account::<T, I>::insert(
362 &id,
363 &who,
364 AssetAccountOf::<T, I> {
365 balance: Zero::zero(),
366 status: AccountStatus::Liquid,
367 reason,
368 extra: T::Extra::default(),
369 },
370 );
371 Self::deposit_event(Event::Touched { asset_id: id, who, depositor });
372 Ok(())
373 }
374
375 pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult {
378 use AssetStatus::*;
379 use ExistenceReason::*;
380
381 let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
382 ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::<T, I>::NoDeposit);
383 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
384 ensure!(matches!(details.status, Live | Frozen), Error::<T, I>::IncorrectStatus);
385 ensure!(account.balance.is_zero() || allow_burn, Error::<T, I>::WouldBurn);
386 Self::ensure_account_can_die(id.clone(), &who)?;
387
388 if let Some(deposit) = account.reason.take_deposit() {
389 T::Currency::unreserve(&who, deposit);
390 }
391
392 if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
393 Account::<T, I>::remove(&id, &who);
394 } else {
395 debug_assert!(false, "refund did not result in dead account?!");
396 Account::<T, I>::insert(id, &who, account);
398 return Ok(())
399 }
400
401 Asset::<T, I>::insert(&id, details);
402 T::Freezer::died(id.clone(), &who);
404 T::Holder::died(id, &who);
405 Ok(())
406 }
407
408 pub(super) fn do_refund_other(
413 id: T::AssetId,
414 who: &T::AccountId,
415 maybe_check_caller: Option<T::AccountId>,
416 ) -> DispatchResult {
417 let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
418 let (depositor, deposit) =
419 account.reason.take_deposit_from().ok_or(Error::<T, I>::NoDeposit)?;
420 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
421 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
422 ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
423 if let Some(caller) = maybe_check_caller {
424 ensure!(caller == depositor || caller == details.admin, Error::<T, I>::NoPermission);
425 }
426 ensure!(account.balance.is_zero(), Error::<T, I>::WouldBurn);
427 Self::ensure_account_can_die(id.clone(), who)?;
428
429 T::Currency::unreserve(&depositor, deposit);
430
431 if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
432 Account::<T, I>::remove(&id, &who);
433 } else {
434 debug_assert!(false, "refund did not result in dead account?!");
435 Account::<T, I>::insert(&id, &who, account);
437 return Ok(())
438 }
439 Asset::<T, I>::insert(&id, details);
440 T::Freezer::died(id.clone(), who);
442 T::Holder::died(id, &who);
443 return Ok(())
444 }
445
446 pub(super) fn do_mint(
452 id: T::AssetId,
453 beneficiary: &T::AccountId,
454 amount: T::Balance,
455 maybe_check_issuer: Option<T::AccountId>,
456 ) -> DispatchResult {
457 Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult {
458 if let Some(check_issuer) = maybe_check_issuer {
459 ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission);
460 }
461 debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed");
462
463 details.supply = details.supply.saturating_add(amount);
464
465 Ok(())
466 })?;
467
468 Self::deposit_event(Event::Issued { asset_id: id, owner: beneficiary.clone(), amount });
469
470 Ok(())
471 }
472
473 pub(super) fn increase_balance(
480 id: T::AssetId,
481 beneficiary: &T::AccountId,
482 amount: T::Balance,
483 check: impl FnOnce(
484 &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
485 ) -> DispatchResult,
486 ) -> DispatchResult {
487 if amount.is_zero() {
488 return Ok(())
489 }
490
491 Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?;
492 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
493 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
494 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
495 check(details)?;
496
497 Account::<T, I>::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult {
498 match maybe_account {
499 Some(ref mut account) => {
500 account.balance.saturating_accrue(amount);
501 },
502 maybe_account @ None => {
503 ensure!(amount >= details.min_balance, TokenError::BelowMinimum);
506 *maybe_account = Some(AssetAccountOf::<T, I> {
507 balance: amount,
508 reason: Self::new_account(beneficiary, details, None)?,
509 status: AccountStatus::Liquid,
510 extra: T::Extra::default(),
511 });
512 },
513 }
514 Ok(())
515 })?;
516 Ok(())
517 })?;
518 Ok(())
519 }
520
521 pub(super) fn do_burn(
529 id: T::AssetId,
530 target: &T::AccountId,
531 amount: T::Balance,
532 maybe_check_admin: Option<T::AccountId>,
533 f: DebitFlags,
534 ) -> Result<T::Balance, DispatchError> {
535 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
536 ensure!(
537 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
538 Error::<T, I>::IncorrectStatus
539 );
540
541 let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| {
542 if let Some(check_admin) = maybe_check_admin {
544 ensure!(check_admin == details.admin, Error::<T, I>::NoPermission);
545 }
546
547 debug_assert!(details.supply >= actual, "checked in prep; qed");
548 details.supply = details.supply.saturating_sub(actual);
549
550 Ok(())
551 })?;
552 Self::deposit_event(Event::Burned { asset_id: id, owner: target.clone(), balance: actual });
553 Ok(actual)
554 }
555
556 pub(super) fn decrease_balance(
565 id: T::AssetId,
566 target: &T::AccountId,
567 amount: T::Balance,
568 f: DebitFlags,
569 check: impl FnOnce(
570 T::Balance,
571 &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
572 ) -> DispatchResult,
573 ) -> Result<T::Balance, DispatchError> {
574 if amount.is_zero() {
575 return Ok(amount)
576 }
577
578 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
579 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
580
581 let actual = Self::prep_debit(id.clone(), target, amount, f)?;
582 let mut target_died: Option<DeadConsequence> = None;
583
584 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
585 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
586 check(actual, details)?;
587
588 Account::<T, I>::try_mutate(&id, target, |maybe_account| -> DispatchResult {
589 let mut account = maybe_account.take().ok_or(Error::<T, I>::NoAccount)?;
590 debug_assert!(account.balance >= actual, "checked in prep; qed");
591
592 account.balance = account.balance.saturating_sub(actual);
594 if account.balance < details.min_balance {
595 debug_assert!(account.balance.is_zero(), "checked in prep; qed");
596 Self::ensure_account_can_die(id.clone(), target)?;
597 target_died = Some(Self::dead_account(target, details, &account.reason, false));
598 if let Some(Remove) = target_died {
599 return Ok(())
600 }
601 };
602 *maybe_account = Some(account);
603 Ok(())
604 })?;
605
606 Ok(())
607 })?;
608
609 if let Some(Remove) = target_died {
611 T::Freezer::died(id.clone(), target);
612 T::Holder::died(id, target);
613 }
614 Ok(actual)
615 }
616
617 pub(super) fn do_transfer(
626 id: T::AssetId,
627 source: &T::AccountId,
628 dest: &T::AccountId,
629 amount: T::Balance,
630 maybe_need_admin: Option<T::AccountId>,
631 f: TransferFlags,
632 ) -> Result<T::Balance, DispatchError> {
633 let (balance, died) =
634 Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?;
635 if let Some(Remove) = died {
636 T::Freezer::died(id.clone(), source);
637 T::Holder::died(id, source);
638 }
639 Ok(balance)
640 }
641
642 fn transfer_and_die(
645 id: T::AssetId,
646 source: &T::AccountId,
647 dest: &T::AccountId,
648 amount: T::Balance,
649 maybe_need_admin: Option<T::AccountId>,
650 f: TransferFlags,
651 ) -> Result<(T::Balance, Option<DeadConsequence>), DispatchError> {
652 if amount.is_zero() {
654 return Ok((amount, None))
655 }
656 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
657 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
658
659 let debit = Self::prep_debit(id.clone(), source, amount, f.into())?;
661 let (credit, maybe_burn) = Self::prep_credit(id.clone(), dest, amount, debit, f.burn_dust)?;
662
663 let mut source_account =
664 Account::<T, I>::get(&id, &source).ok_or(Error::<T, I>::NoAccount)?;
665 let mut source_died: Option<DeadConsequence> = None;
666
667 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
668 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
669
670 if let Some(need_admin) = maybe_need_admin {
672 ensure!(need_admin == details.admin, Error::<T, I>::NoPermission);
673 }
674
675 if source == dest {
677 return Ok(())
678 }
679
680 if let Some(burn) = maybe_burn {
682 debug_assert!(details.supply >= burn, "checked in prep; qed");
685 details.supply = details.supply.saturating_sub(burn);
686 }
687
688 debug_assert!(source_account.balance >= debit, "checked in prep; qed");
690 source_account.balance = source_account.balance.saturating_sub(debit);
691
692 if source_account.balance < details.min_balance {
694 debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
695 Self::ensure_account_can_die(id.clone(), source)?;
696 }
697
698 Account::<T, I>::try_mutate(&id, &dest, |maybe_account| -> DispatchResult {
699 match maybe_account {
700 Some(ref mut account) => {
701 debug_assert!(
704 account.balance.checked_add(&credit).is_some(),
705 "checked in prep; qed"
706 );
707 account.balance.saturating_accrue(credit);
708 },
709 maybe_account @ None => {
710 *maybe_account = Some(AssetAccountOf::<T, I> {
711 balance: credit,
712 status: AccountStatus::Liquid,
713 reason: Self::new_account(dest, details, None)?,
714 extra: T::Extra::default(),
715 });
716 },
717 }
718 Ok(())
719 })?;
720
721 if source_account.balance < details.min_balance {
723 debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
724 source_died =
725 Some(Self::dead_account(source, details, &source_account.reason, false));
726 if let Some(Remove) = source_died {
727 Account::<T, I>::remove(&id, &source);
728 return Ok(())
729 }
730 }
731 Account::<T, I>::insert(&id, &source, &source_account);
732 Ok(())
733 })?;
734
735 Self::deposit_event(Event::Transferred {
736 asset_id: id,
737 from: source.clone(),
738 to: dest.clone(),
739 amount: credit,
740 });
741 Ok((credit, source_died))
742 }
743
744 pub(super) fn do_force_create(
753 id: T::AssetId,
754 owner: T::AccountId,
755 is_sufficient: bool,
756 min_balance: T::Balance,
757 ) -> DispatchResult {
758 ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
759 ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
760 if let Some(next_id) = NextAssetId::<T, I>::get() {
761 ensure!(id == next_id, Error::<T, I>::BadAssetId);
762 }
763
764 Asset::<T, I>::insert(
765 &id,
766 AssetDetails {
767 owner: owner.clone(),
768 issuer: owner.clone(),
769 admin: owner.clone(),
770 freezer: owner.clone(),
771 supply: Zero::zero(),
772 deposit: Zero::zero(),
773 min_balance,
774 is_sufficient,
775 accounts: 0,
776 sufficients: 0,
777 approvals: 0,
778 status: AssetStatus::Live,
779 },
780 );
781 ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
782 Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() });
783 Ok(())
784 }
785
786 pub(super) fn do_start_destroy(
789 id: T::AssetId,
790 maybe_check_owner: Option<T::AccountId>,
791 ) -> DispatchResult {
792 Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
793 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
794 if let Some(check_owner) = maybe_check_owner {
795 ensure!(details.owner == check_owner, Error::<T, I>::NoPermission);
796 }
797
798 ensure!(!T::Holder::contains_holds(id.clone()), Error::<T, I>::ContainsHolds);
799 ensure!(!T::Freezer::contains_freezes(id.clone()), Error::<T, I>::ContainsFreezes);
800
801 details.status = AssetStatus::Destroying;
802
803 Self::deposit_event(Event::DestructionStarted { asset_id: id });
804 Ok(())
805 })
806 }
807
808 pub(super) fn do_destroy_accounts(
813 id: T::AssetId,
814 max_items: u32,
815 ) -> Result<u32, DispatchError> {
816 let mut dead_accounts: Vec<T::AccountId> = vec![];
817 let mut remaining_accounts = 0;
818 Asset::<T, I>::try_mutate_exists(&id, |maybe_details| -> Result<(), DispatchError> {
819 let mut details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
820 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
822
823 for (i, (who, mut v)) in Account::<T, I>::iter_prefix(&id).enumerate() {
824 if Self::ensure_account_can_die(id.clone(), &who).is_err() {
825 continue
826 }
827 if let Some((depositor, deposit)) = v.reason.take_deposit_from() {
829 T::Currency::unreserve(&depositor, deposit);
830 } else if let Some(deposit) = v.reason.take_deposit() {
831 T::Currency::unreserve(&who, deposit);
832 }
833 if let Remove = Self::dead_account(&who, &mut details, &v.reason, false) {
834 Account::<T, I>::remove(&id, &who);
835 dead_accounts.push(who);
836 } else {
837 Account::<T, I>::insert(&id, &who, v);
839 defensive!("destroy did not result in dead account?!");
840 }
841 if i + 1 >= (max_items as usize) {
842 break
843 }
844 }
845 remaining_accounts = details.accounts;
846 Ok(())
847 })?;
848
849 for who in &dead_accounts {
850 T::Freezer::died(id.clone(), &who);
851 T::Holder::died(id.clone(), &who);
852 }
853
854 Self::deposit_event(Event::AccountsDestroyed {
855 asset_id: id,
856 accounts_destroyed: dead_accounts.len() as u32,
857 accounts_remaining: remaining_accounts as u32,
858 });
859 Ok(dead_accounts.len() as u32)
860 }
861
862 pub(super) fn do_destroy_approvals(
867 id: T::AssetId,
868 max_items: u32,
869 ) -> Result<u32, DispatchError> {
870 let mut removed_approvals = 0;
871 Asset::<T, I>::try_mutate_exists(
872 id.clone(),
873 |maybe_details| -> Result<(), DispatchError> {
874 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
875
876 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
878
879 for ((owner, _), approval) in Approvals::<T, I>::drain_prefix((id.clone(),)) {
880 T::Currency::unreserve(&owner, approval.deposit);
881 removed_approvals = removed_approvals.saturating_add(1);
882 details.approvals = details.approvals.saturating_sub(1);
883 if removed_approvals >= max_items {
884 break
885 }
886 }
887 Self::deposit_event(Event::ApprovalsDestroyed {
888 asset_id: id,
889 approvals_destroyed: removed_approvals as u32,
890 approvals_remaining: details.approvals as u32,
891 });
892 Ok(())
893 },
894 )?;
895 Ok(removed_approvals)
896 }
897
898 pub(super) fn do_finish_destroy(id: T::AssetId) -> DispatchResult {
902 Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
903 let details = maybe_details.take().ok_or(Error::<T, I>::Unknown)?;
904 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
905 ensure!(details.accounts == 0, Error::<T, I>::InUse);
906 ensure!(details.approvals == 0, Error::<T, I>::InUse);
907 ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::<T, I>::CallbackFailed);
908
909 let metadata = Metadata::<T, I>::take(&id);
910 T::Currency::unreserve(
911 &details.owner,
912 details.deposit.saturating_add(metadata.deposit),
913 );
914 Self::deposit_event(Event::Destroyed { asset_id: id });
915
916 Ok(())
917 })
918 }
919
920 pub(super) fn do_approve_transfer(
925 id: T::AssetId,
926 owner: &T::AccountId,
927 delegate: &T::AccountId,
928 amount: T::Balance,
929 ) -> DispatchResult {
930 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
931 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
932 Approvals::<T, I>::try_mutate(
933 (id.clone(), &owner, &delegate),
934 |maybe_approved| -> DispatchResult {
935 let mut approved = match maybe_approved.take() {
936 Some(a) => a,
938 None => {
940 d.approvals.saturating_inc();
941 Default::default()
942 },
943 };
944 let deposit_required = T::ApprovalDeposit::get();
945 if approved.deposit < deposit_required {
946 T::Currency::reserve(owner, deposit_required - approved.deposit)?;
947 approved.deposit = deposit_required;
948 }
949 approved.amount = approved.amount.saturating_add(amount);
950 *maybe_approved = Some(approved);
951 Ok(())
952 },
953 )?;
954 Asset::<T, I>::insert(&id, d);
955 Self::deposit_event(Event::ApprovedTransfer {
956 asset_id: id,
957 source: owner.clone(),
958 delegate: delegate.clone(),
959 amount,
960 });
961
962 Ok(())
963 }
964
965 pub(super) fn do_transfer_approved(
973 id: T::AssetId,
974 owner: &T::AccountId,
975 delegate: &T::AccountId,
976 destination: &T::AccountId,
977 amount: T::Balance,
978 ) -> DispatchResult {
979 let mut owner_died: Option<DeadConsequence> = None;
980
981 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
982 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
983
984 Approvals::<T, I>::try_mutate_exists(
985 (id.clone(), &owner, delegate),
986 |maybe_approved| -> DispatchResult {
987 let mut approved = maybe_approved.take().ok_or(Error::<T, I>::Unapproved)?;
988 let remaining =
989 approved.amount.checked_sub(&amount).ok_or(Error::<T, I>::Unapproved)?;
990
991 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
992 owner_died =
993 Self::transfer_and_die(id.clone(), owner, destination, amount, None, f)?.1;
994
995 if remaining.is_zero() {
996 T::Currency::unreserve(owner, approved.deposit);
997 Asset::<T, I>::mutate(id.clone(), |maybe_details| {
998 if let Some(details) = maybe_details {
999 details.approvals.saturating_dec();
1000 }
1001 });
1002 } else {
1003 approved.amount = remaining;
1004 *maybe_approved = Some(approved);
1005 }
1006 Ok(())
1007 },
1008 )?;
1009
1010 if let Some(Remove) = owner_died {
1012 T::Freezer::died(id.clone(), owner);
1013 T::Holder::died(id, owner);
1014 }
1015 Ok(())
1016 }
1017
1018 pub(super) fn do_set_metadata(
1020 id: T::AssetId,
1021 from: &T::AccountId,
1022 name: Vec<u8>,
1023 symbol: Vec<u8>,
1024 decimals: u8,
1025 ) -> DispatchResult {
1026 let bounded_name: BoundedVec<u8, T::StringLimit> =
1027 name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1028 let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1029 symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1030
1031 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1032 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1033 ensure!(from == &d.owner, Error::<T, I>::NoPermission);
1034
1035 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1036 ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::<T, I>::NoPermission);
1037
1038 let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1039 let new_deposit = Self::calc_metadata_deposit(&name, &symbol);
1040
1041 if new_deposit > old_deposit {
1042 T::Currency::reserve(from, new_deposit - old_deposit)?;
1043 } else {
1044 T::Currency::unreserve(from, old_deposit - new_deposit);
1045 }
1046
1047 *metadata = Some(AssetMetadata {
1048 deposit: new_deposit,
1049 name: bounded_name,
1050 symbol: bounded_symbol,
1051 decimals,
1052 is_frozen: false,
1053 });
1054
1055 Self::deposit_event(Event::MetadataSet {
1056 asset_id: id,
1057 name,
1058 symbol,
1059 decimals,
1060 is_frozen: false,
1061 });
1062 Ok(())
1063 })
1064 }
1065
1066 pub(super) fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalanceOf<T, I> {
1068 T::MetadataDepositPerByte::get()
1069 .saturating_mul(((name.len() + symbol.len()) as u32).into())
1070 .saturating_add(T::MetadataDepositBase::get())
1071 }
1072
1073 pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
1075 Asset::<T, I>::iter_keys()
1076 .filter_map(|id| {
1077 Self::maybe_balance(id.clone(), account.clone()).map(|balance| (id, balance))
1078 })
1079 .collect::<Vec<_>>()
1080 }
1081
1082 pub(crate) fn do_reset_team(
1091 id: T::AssetId,
1092 owner: T::AccountId,
1093 admin: T::AccountId,
1094 issuer: T::AccountId,
1095 freezer: T::AccountId,
1096 ) -> DispatchResult {
1097 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1098 d.owner = owner;
1099 d.admin = admin;
1100 d.issuer = issuer;
1101 d.freezer = freezer;
1102 Asset::<T, I>::insert(&id, d);
1103 Ok(())
1104 }
1105}