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(
342 id: T::AssetId,
343 who: T::AccountId,
344 depositor: T::AccountId,
345 ) -> DispatchResult {
346 ensure!(!Account::<T, I>::contains_key(&id, &who), Error::<T, I>::AlreadyExists);
347 let deposit = T::AssetAccountDeposit::get();
348 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
349 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
350 let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?;
351 T::Currency::reserve(&depositor, deposit)?;
352 Asset::<T, I>::insert(&id, details);
353 Account::<T, I>::insert(
354 &id,
355 &who,
356 AssetAccountOf::<T, I> {
357 balance: Zero::zero(),
358 status: AccountStatus::Liquid,
359 reason,
360 extra: T::Extra::default(),
361 },
362 );
363 Self::deposit_event(Event::Touched { asset_id: id, who, depositor });
364 Ok(())
365 }
366
367 pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult {
370 use AssetStatus::*;
371 use ExistenceReason::*;
372
373 let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
374 ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::<T, I>::NoDeposit);
375 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
376 ensure!(matches!(details.status, Live | Frozen), Error::<T, I>::IncorrectStatus);
377 ensure!(account.balance.is_zero() || allow_burn, Error::<T, I>::WouldBurn);
378 Self::ensure_account_can_die(id.clone(), &who)?;
379
380 if let Some(deposit) = account.reason.take_deposit() {
381 T::Currency::unreserve(&who, deposit);
382 }
383
384 if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
385 Account::<T, I>::remove(&id, &who);
386 } else {
387 debug_assert!(false, "refund did not result in dead account?!");
388 Account::<T, I>::insert(id, &who, account);
390 return Ok(())
391 }
392
393 Asset::<T, I>::insert(&id, details);
394 T::Freezer::died(id.clone(), &who);
396 T::Holder::died(id, &who);
397 Ok(())
398 }
399
400 pub(super) fn do_refund_other(
405 id: T::AssetId,
406 who: &T::AccountId,
407 maybe_check_caller: Option<T::AccountId>,
408 ) -> DispatchResult {
409 let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
410 let (depositor, deposit) =
411 account.reason.take_deposit_from().ok_or(Error::<T, I>::NoDeposit)?;
412 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
413 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
414 ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
415 if let Some(caller) = maybe_check_caller {
416 ensure!(caller == depositor || caller == details.admin, Error::<T, I>::NoPermission);
417 }
418 ensure!(account.balance.is_zero(), Error::<T, I>::WouldBurn);
419 Self::ensure_account_can_die(id.clone(), who)?;
420
421 T::Currency::unreserve(&depositor, deposit);
422
423 if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
424 Account::<T, I>::remove(&id, &who);
425 } else {
426 debug_assert!(false, "refund did not result in dead account?!");
427 Account::<T, I>::insert(&id, &who, account);
429 return Ok(())
430 }
431 Asset::<T, I>::insert(&id, details);
432 T::Freezer::died(id.clone(), who);
434 T::Holder::died(id, &who);
435 return Ok(())
436 }
437
438 pub(super) fn do_mint(
444 id: T::AssetId,
445 beneficiary: &T::AccountId,
446 amount: T::Balance,
447 maybe_check_issuer: Option<T::AccountId>,
448 ) -> DispatchResult {
449 Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult {
450 if let Some(check_issuer) = maybe_check_issuer {
451 ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission);
452 }
453 debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed");
454
455 details.supply = details.supply.saturating_add(amount);
456
457 Ok(())
458 })?;
459
460 Self::deposit_event(Event::Issued { asset_id: id, owner: beneficiary.clone(), amount });
461
462 Ok(())
463 }
464
465 pub(super) fn increase_balance(
472 id: T::AssetId,
473 beneficiary: &T::AccountId,
474 amount: T::Balance,
475 check: impl FnOnce(
476 &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
477 ) -> DispatchResult,
478 ) -> DispatchResult {
479 if amount.is_zero() {
480 return Ok(())
481 }
482
483 Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?;
484 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
485 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
486 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
487 check(details)?;
488
489 Account::<T, I>::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult {
490 match maybe_account {
491 Some(ref mut account) => {
492 account.balance.saturating_accrue(amount);
493 },
494 maybe_account @ None => {
495 ensure!(amount >= details.min_balance, TokenError::BelowMinimum);
498 *maybe_account = Some(AssetAccountOf::<T, I> {
499 balance: amount,
500 reason: Self::new_account(beneficiary, details, None)?,
501 status: AccountStatus::Liquid,
502 extra: T::Extra::default(),
503 });
504 },
505 }
506 Ok(())
507 })?;
508 Ok(())
509 })?;
510 Ok(())
511 }
512
513 pub(super) fn do_burn(
521 id: T::AssetId,
522 target: &T::AccountId,
523 amount: T::Balance,
524 maybe_check_admin: Option<T::AccountId>,
525 f: DebitFlags,
526 ) -> Result<T::Balance, DispatchError> {
527 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
528 ensure!(
529 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
530 Error::<T, I>::IncorrectStatus
531 );
532
533 let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| {
534 if let Some(check_admin) = maybe_check_admin {
536 ensure!(check_admin == details.admin, Error::<T, I>::NoPermission);
537 }
538
539 debug_assert!(details.supply >= actual, "checked in prep; qed");
540 details.supply = details.supply.saturating_sub(actual);
541
542 Ok(())
543 })?;
544 Self::deposit_event(Event::Burned { asset_id: id, owner: target.clone(), balance: actual });
545 Ok(actual)
546 }
547
548 pub(super) fn decrease_balance(
557 id: T::AssetId,
558 target: &T::AccountId,
559 amount: T::Balance,
560 f: DebitFlags,
561 check: impl FnOnce(
562 T::Balance,
563 &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
564 ) -> DispatchResult,
565 ) -> Result<T::Balance, DispatchError> {
566 if amount.is_zero() {
567 return Ok(amount)
568 }
569
570 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
571 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
572
573 let actual = Self::prep_debit(id.clone(), target, amount, f)?;
574 let mut target_died: Option<DeadConsequence> = None;
575
576 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
577 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
578 check(actual, details)?;
579
580 Account::<T, I>::try_mutate(&id, target, |maybe_account| -> DispatchResult {
581 let mut account = maybe_account.take().ok_or(Error::<T, I>::NoAccount)?;
582 debug_assert!(account.balance >= actual, "checked in prep; qed");
583
584 account.balance = account.balance.saturating_sub(actual);
586 if account.balance < details.min_balance {
587 debug_assert!(account.balance.is_zero(), "checked in prep; qed");
588 Self::ensure_account_can_die(id.clone(), target)?;
589 target_died = Some(Self::dead_account(target, details, &account.reason, false));
590 if let Some(Remove) = target_died {
591 return Ok(())
592 }
593 };
594 *maybe_account = Some(account);
595 Ok(())
596 })?;
597
598 Ok(())
599 })?;
600
601 if let Some(Remove) = target_died {
603 T::Freezer::died(id.clone(), target);
604 T::Holder::died(id, target);
605 }
606 Ok(actual)
607 }
608
609 pub fn do_transfer(
618 id: T::AssetId,
619 source: &T::AccountId,
620 dest: &T::AccountId,
621 amount: T::Balance,
622 maybe_need_admin: Option<T::AccountId>,
623 f: TransferFlags,
624 ) -> Result<T::Balance, DispatchError> {
625 let (balance, died) =
626 Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?;
627 if let Some(Remove) = died {
628 T::Freezer::died(id.clone(), source);
629 T::Holder::died(id, source);
630 }
631 Ok(balance)
632 }
633
634 fn transfer_and_die(
637 id: T::AssetId,
638 source: &T::AccountId,
639 dest: &T::AccountId,
640 amount: T::Balance,
641 maybe_need_admin: Option<T::AccountId>,
642 f: TransferFlags,
643 ) -> Result<(T::Balance, Option<DeadConsequence>), DispatchError> {
644 if amount.is_zero() {
646 return Ok((amount, None))
647 }
648 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
649 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
650
651 let debit = Self::prep_debit(id.clone(), source, amount, f.into())?;
653 let (credit, maybe_burn) = Self::prep_credit(id.clone(), dest, amount, debit, f.burn_dust)?;
654
655 let mut source_account =
656 Account::<T, I>::get(&id, &source).ok_or(Error::<T, I>::NoAccount)?;
657 let mut source_died: Option<DeadConsequence> = None;
658
659 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
660 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
661
662 if let Some(need_admin) = maybe_need_admin {
664 ensure!(need_admin == details.admin, Error::<T, I>::NoPermission);
665 }
666
667 if source == dest {
669 return Ok(())
670 }
671
672 if let Some(burn) = maybe_burn {
674 debug_assert!(details.supply >= burn, "checked in prep; qed");
677 details.supply = details.supply.saturating_sub(burn);
678 }
679
680 debug_assert!(source_account.balance >= debit, "checked in prep; qed");
682 source_account.balance = source_account.balance.saturating_sub(debit);
683
684 if source_account.balance < details.min_balance {
686 debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
687 Self::ensure_account_can_die(id.clone(), source)?;
688 }
689
690 Account::<T, I>::try_mutate(&id, &dest, |maybe_account| -> DispatchResult {
691 match maybe_account {
692 Some(ref mut account) => {
693 debug_assert!(
696 account.balance.checked_add(&credit).is_some(),
697 "checked in prep; qed"
698 );
699 account.balance.saturating_accrue(credit);
700 },
701 maybe_account @ None => {
702 *maybe_account = Some(AssetAccountOf::<T, I> {
703 balance: credit,
704 status: AccountStatus::Liquid,
705 reason: Self::new_account(dest, details, None)?,
706 extra: T::Extra::default(),
707 });
708 },
709 }
710 Ok(())
711 })?;
712
713 if source_account.balance < details.min_balance {
715 debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
716 source_died =
717 Some(Self::dead_account(source, details, &source_account.reason, false));
718 if let Some(Remove) = source_died {
719 Account::<T, I>::remove(&id, &source);
720 return Ok(())
721 }
722 }
723 Account::<T, I>::insert(&id, &source, &source_account);
724 Ok(())
725 })?;
726
727 Self::deposit_event(Event::Transferred {
728 asset_id: id,
729 from: source.clone(),
730 to: dest.clone(),
731 amount: credit,
732 });
733 Ok((credit, source_died))
734 }
735
736 pub(super) fn do_force_create(
745 id: T::AssetId,
746 owner: T::AccountId,
747 is_sufficient: bool,
748 min_balance: T::Balance,
749 ) -> DispatchResult {
750 ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
751 ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
752 if let Some(next_id) = NextAssetId::<T, I>::get() {
753 ensure!(id == next_id, Error::<T, I>::BadAssetId);
754 }
755
756 Asset::<T, I>::insert(
757 &id,
758 AssetDetails {
759 owner: owner.clone(),
760 issuer: owner.clone(),
761 admin: owner.clone(),
762 freezer: owner.clone(),
763 supply: Zero::zero(),
764 deposit: Zero::zero(),
765 min_balance,
766 is_sufficient,
767 accounts: 0,
768 sufficients: 0,
769 approvals: 0,
770 status: AssetStatus::Live,
771 },
772 );
773 ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
774 Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() });
775 Ok(())
776 }
777
778 pub(super) fn do_start_destroy(
781 id: T::AssetId,
782 maybe_check_owner: Option<T::AccountId>,
783 ) -> DispatchResult {
784 Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
785 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
786 if let Some(check_owner) = maybe_check_owner {
787 ensure!(details.owner == check_owner, Error::<T, I>::NoPermission);
788 }
789
790 ensure!(!T::Holder::contains_holds(id.clone()), Error::<T, I>::ContainsHolds);
791 ensure!(!T::Freezer::contains_freezes(id.clone()), Error::<T, I>::ContainsFreezes);
792
793 details.status = AssetStatus::Destroying;
794
795 Self::deposit_event(Event::DestructionStarted { asset_id: id });
796 Ok(())
797 })
798 }
799
800 pub(super) fn do_destroy_accounts(
805 id: T::AssetId,
806 max_items: u32,
807 ) -> Result<u32, DispatchError> {
808 let mut dead_accounts: Vec<T::AccountId> = vec![];
809 let mut remaining_accounts = 0;
810 Asset::<T, I>::try_mutate_exists(&id, |maybe_details| -> Result<(), DispatchError> {
811 let mut details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
812 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
814
815 for (i, (who, mut v)) in Account::<T, I>::iter_prefix(&id).enumerate() {
816 if Self::ensure_account_can_die(id.clone(), &who).is_err() {
817 continue
818 }
819 if let Some((depositor, deposit)) = v.reason.take_deposit_from() {
821 T::Currency::unreserve(&depositor, deposit);
822 } else if let Some(deposit) = v.reason.take_deposit() {
823 T::Currency::unreserve(&who, deposit);
824 }
825 if let Remove = Self::dead_account(&who, &mut details, &v.reason, false) {
826 Account::<T, I>::remove(&id, &who);
827 dead_accounts.push(who);
828 } else {
829 Account::<T, I>::insert(&id, &who, v);
831 defensive!("destroy did not result in dead account?!");
832 }
833 if i + 1 >= (max_items as usize) {
834 break
835 }
836 }
837 remaining_accounts = details.accounts;
838 Ok(())
839 })?;
840
841 for who in &dead_accounts {
842 T::Freezer::died(id.clone(), &who);
843 T::Holder::died(id.clone(), &who);
844 }
845
846 Self::deposit_event(Event::AccountsDestroyed {
847 asset_id: id,
848 accounts_destroyed: dead_accounts.len() as u32,
849 accounts_remaining: remaining_accounts as u32,
850 });
851 Ok(dead_accounts.len() as u32)
852 }
853
854 pub(super) fn do_destroy_approvals(
859 id: T::AssetId,
860 max_items: u32,
861 ) -> Result<u32, DispatchError> {
862 let mut removed_approvals = 0;
863 Asset::<T, I>::try_mutate_exists(
864 id.clone(),
865 |maybe_details| -> Result<(), DispatchError> {
866 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
867
868 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
870
871 for ((owner, _), approval) in Approvals::<T, I>::drain_prefix((id.clone(),)) {
872 T::Currency::unreserve(&owner, approval.deposit);
873 removed_approvals = removed_approvals.saturating_add(1);
874 details.approvals = details.approvals.saturating_sub(1);
875 if removed_approvals >= max_items {
876 break
877 }
878 }
879 Self::deposit_event(Event::ApprovalsDestroyed {
880 asset_id: id,
881 approvals_destroyed: removed_approvals as u32,
882 approvals_remaining: details.approvals as u32,
883 });
884 Ok(())
885 },
886 )?;
887 Ok(removed_approvals)
888 }
889
890 pub(super) fn do_finish_destroy(id: T::AssetId) -> DispatchResult {
894 Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
895 let details = maybe_details.take().ok_or(Error::<T, I>::Unknown)?;
896 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
897 ensure!(details.accounts == 0, Error::<T, I>::InUse);
898 ensure!(details.approvals == 0, Error::<T, I>::InUse);
899 ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::<T, I>::CallbackFailed);
900
901 let metadata = Metadata::<T, I>::take(&id);
902 T::Currency::unreserve(
903 &details.owner,
904 details.deposit.saturating_add(metadata.deposit),
905 );
906 Reserves::<T, I>::remove(&id);
907 Self::deposit_event(Event::Destroyed { asset_id: id });
908
909 Ok(())
910 })
911 }
912
913 pub fn do_approve_transfer(
918 id: T::AssetId,
919 owner: &T::AccountId,
920 delegate: &T::AccountId,
921 amount: T::Balance,
922 ) -> DispatchResult {
923 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
924 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
925 Approvals::<T, I>::try_mutate(
926 (id.clone(), &owner, &delegate),
927 |maybe_approved| -> DispatchResult {
928 let mut approved = match maybe_approved.take() {
929 Some(a) => a,
931 None => {
933 d.approvals.saturating_inc();
934 Default::default()
935 },
936 };
937 let deposit_required = T::ApprovalDeposit::get();
938 if approved.deposit < deposit_required {
939 T::Currency::reserve(owner, deposit_required - approved.deposit)?;
940 approved.deposit = deposit_required;
941 }
942 approved.amount = approved.amount.saturating_add(amount);
943 *maybe_approved = Some(approved);
944 Ok(())
945 },
946 )?;
947 Asset::<T, I>::insert(&id, d);
948 Self::deposit_event(Event::ApprovedTransfer {
949 asset_id: id,
950 source: owner.clone(),
951 delegate: delegate.clone(),
952 amount,
953 });
954
955 Ok(())
956 }
957
958 pub fn do_transfer_approved(
966 id: T::AssetId,
967 owner: &T::AccountId,
968 delegate: &T::AccountId,
969 destination: &T::AccountId,
970 amount: T::Balance,
971 ) -> DispatchResult {
972 let mut owner_died: Option<DeadConsequence> = None;
973
974 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
975 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
976
977 Approvals::<T, I>::try_mutate_exists(
978 (id.clone(), &owner, delegate),
979 |maybe_approved| -> DispatchResult {
980 let mut approved = maybe_approved.take().ok_or(Error::<T, I>::Unapproved)?;
981 let remaining =
982 approved.amount.checked_sub(&amount).ok_or(Error::<T, I>::Unapproved)?;
983
984 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
985 owner_died =
986 Self::transfer_and_die(id.clone(), owner, destination, amount, None, f)?.1;
987
988 if remaining.is_zero() {
989 T::Currency::unreserve(owner, approved.deposit);
990 Asset::<T, I>::mutate(id.clone(), |maybe_details| {
991 if let Some(details) = maybe_details {
992 details.approvals.saturating_dec();
993 }
994 });
995 } else {
996 approved.amount = remaining;
997 *maybe_approved = Some(approved);
998 }
999 Ok(())
1000 },
1001 )?;
1002
1003 if let Some(Remove) = owner_died {
1005 T::Freezer::died(id.clone(), owner);
1006 T::Holder::died(id, owner);
1007 }
1008 Ok(())
1009 }
1010
1011 pub(super) fn do_set_metadata(
1013 id: T::AssetId,
1014 from: &T::AccountId,
1015 name: Vec<u8>,
1016 symbol: Vec<u8>,
1017 decimals: u8,
1018 ) -> DispatchResult {
1019 let bounded_name: BoundedVec<u8, T::StringLimit> =
1020 name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1021 let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1022 symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1023
1024 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1025 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1026 ensure!(from == &d.owner, Error::<T, I>::NoPermission);
1027
1028 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1029 ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::<T, I>::NoPermission);
1030
1031 let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1032 let new_deposit = Self::calc_metadata_deposit(&name, &symbol);
1033
1034 if new_deposit > old_deposit {
1035 T::Currency::reserve(from, new_deposit - old_deposit)?;
1036 } else {
1037 T::Currency::unreserve(from, old_deposit - new_deposit);
1038 }
1039
1040 *metadata = Some(AssetMetadata {
1041 deposit: new_deposit,
1042 name: bounded_name,
1043 symbol: bounded_symbol,
1044 decimals,
1045 is_frozen: false,
1046 });
1047
1048 Self::deposit_event(Event::MetadataSet {
1049 asset_id: id,
1050 name,
1051 symbol,
1052 decimals,
1053 is_frozen: false,
1054 });
1055 Ok(())
1056 })
1057 }
1058
1059 pub(super) fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalanceOf<T, I> {
1061 T::MetadataDepositPerByte::get()
1062 .saturating_mul(((name.len() + symbol.len()) as u32).into())
1063 .saturating_add(T::MetadataDepositBase::get())
1064 }
1065
1066 pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
1068 Asset::<T, I>::iter_keys()
1069 .filter_map(|id| {
1070 Self::maybe_balance(id.clone(), account.clone()).map(|balance| (id, balance))
1071 })
1072 .collect::<Vec<_>>()
1073 }
1074
1075 pub(crate) fn do_reset_team(
1084 id: T::AssetId,
1085 owner: T::AccountId,
1086 admin: T::AccountId,
1087 issuer: T::AccountId,
1088 freezer: T::AccountId,
1089 ) -> DispatchResult {
1090 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1091 d.owner = owner;
1092 d.admin = admin;
1093 d.issuer = issuer;
1094 d.freezer = freezer;
1095 Asset::<T, I>::insert(&id, d);
1096 Ok(())
1097 }
1098
1099 pub fn unchecked_update_reserves(
1102 id: T::AssetId,
1103 reserves: Vec<T::ReserveData>,
1104 ) -> Result<(), Error<T, I>> {
1105 if reserves.is_empty() {
1106 Reserves::<T, I>::remove(&id);
1107 Self::deposit_event(Event::ReservesRemoved { asset_id: id });
1108 } else {
1109 let bounded_reserves =
1110 reserves.clone().try_into().map_err(|_| Error::<T, I>::TooManyReserves)?;
1111 Reserves::<T, I>::set(&id, bounded_reserves);
1112 Self::deposit_event(Event::ReservesUpdated { asset_id: id, reserves });
1113 }
1114 Ok(())
1115 }
1116}