1use super::*;
21use alloc::vec;
22use frame_support::{defensive, traits::Get, BoundedVec};
23use sp_runtime::traits::ConstU32;
24
25#[must_use]
26pub(super) enum DeadConsequence {
27 Remove,
28 Keep,
29}
30
31use DeadConsequence::*;
32
33impl<T: Config<I>, I: 'static> Pallet<T, I> {
35 pub fn adjust_extra(
39 id: T::AssetId,
40 who: impl core::borrow::Borrow<T::AccountId>,
41 ) -> Option<ExtraMutator<T, I>> {
42 ExtraMutator::maybe_new(id, who)
43 }
44
45 pub fn balance(id: T::AssetId, who: impl core::borrow::Borrow<T::AccountId>) -> T::Balance {
47 Self::maybe_balance(id, who).unwrap_or_default()
48 }
49
50 pub fn maybe_balance(
52 id: T::AssetId,
53 who: impl core::borrow::Borrow<T::AccountId>,
54 ) -> Option<T::Balance> {
55 Account::<T, I>::get(id, who.borrow()).map(|a| a.balance)
56 }
57
58 pub fn total_supply(id: T::AssetId) -> T::Balance {
60 Self::maybe_total_supply(id).unwrap_or_default()
61 }
62
63 pub fn maybe_total_supply(id: T::AssetId) -> Option<T::Balance> {
65 Asset::<T, I>::get(id).map(|x| x.supply)
66 }
67
68 pub(super) fn new_account(
69 who: &T::AccountId,
70 d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
71 maybe_deposit: Option<(&T::AccountId, DepositBalanceOf<T, I>)>,
72 ) -> Result<ExistenceReasonOf<T, I>, DispatchError> {
73 let accounts = d.accounts.checked_add(1).ok_or(ArithmeticError::Overflow)?;
74 let reason = if let Some((depositor, deposit)) = maybe_deposit {
75 if depositor == who {
76 ExistenceReason::DepositHeld(deposit)
77 } else {
78 ExistenceReason::DepositFrom(depositor.clone(), deposit)
79 }
80 } else if d.is_sufficient {
81 frame_system::Pallet::<T>::inc_sufficients(who);
82 d.sufficients.saturating_inc();
83 ExistenceReason::Sufficient
84 } else {
85 frame_system::Pallet::<T>::inc_consumers(who)
86 .map_err(|_| Error::<T, I>::UnavailableConsumer)?;
87 if !frame_system::Pallet::<T>::can_inc_consumer(who) {
90 frame_system::Pallet::<T>::dec_consumers(who);
91 return Err(Error::<T, I>::UnavailableConsumer.into());
92 }
93 ExistenceReason::Consumer
94 };
95 d.accounts = accounts;
96 Ok(reason)
97 }
98
99 pub(super) fn ensure_account_can_die(id: T::AssetId, who: &T::AccountId) -> DispatchResult {
100 ensure!(
101 T::Holder::balance_on_hold(id.clone(), who).is_none(),
102 Error::<T, I>::ContainsHolds
103 );
104 ensure!(T::Freezer::frozen_balance(id, who).is_none(), Error::<T, I>::ContainsFreezes);
105 Ok(())
106 }
107
108 pub(super) fn dead_account(
109 who: &T::AccountId,
110 d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
111 reason: &ExistenceReasonOf<T, I>,
112 force: bool,
113 ) -> DeadConsequence {
114 use ExistenceReason::*;
115
116 match *reason {
117 Consumer => frame_system::Pallet::<T>::dec_consumers(who),
118 Sufficient => {
119 d.sufficients = d.sufficients.saturating_sub(1);
120 frame_system::Pallet::<T>::dec_sufficients(who);
121 },
122 DepositRefunded => {},
123 DepositHeld(_) | DepositFrom(..) if !force => return Keep,
124 DepositHeld(_) | DepositFrom(..) => {},
125 }
126 d.accounts = d.accounts.saturating_sub(1);
127 Remove
128 }
129
130 pub(super) fn can_increase(
138 id: T::AssetId,
139 who: &T::AccountId,
140 amount: T::Balance,
141 increase_supply: bool,
142 ) -> DepositConsequence {
143 let details = match Asset::<T, I>::get(&id) {
144 Some(details) => details,
145 None => return DepositConsequence::UnknownAsset,
146 };
147 if details.status == AssetStatus::Destroying {
148 return DepositConsequence::UnknownAsset;
149 }
150 if increase_supply && details.supply.checked_add(&amount).is_none() {
151 return DepositConsequence::Overflow;
152 }
153 if let Some(account) = Account::<T, I>::get(id, who) {
154 if account.status.is_blocked() {
155 return DepositConsequence::Blocked;
156 }
157 if account.balance.checked_add(&amount).is_none() {
158 return DepositConsequence::Overflow;
159 }
160 } else {
161 if amount < details.min_balance {
162 return DepositConsequence::BelowMinimum;
163 }
164 if !details.is_sufficient && !frame_system::Pallet::<T>::can_accrue_consumers(who, 2) {
165 return DepositConsequence::CannotCreate;
166 }
167 if details.is_sufficient && details.sufficients.checked_add(1).is_none() {
168 return DepositConsequence::Overflow;
169 }
170 }
171
172 DepositConsequence::Success
173 }
174
175 pub(super) fn can_decrease(
177 id: T::AssetId,
178 who: &T::AccountId,
179 amount: T::Balance,
180 keep_alive: bool,
181 ) -> WithdrawConsequence<T::Balance> {
182 use WithdrawConsequence::*;
183 let details = match Asset::<T, I>::get(&id) {
184 Some(details) => details,
185 None => return UnknownAsset,
186 };
187 if details.supply.checked_sub(&amount).is_none() {
188 return Underflow;
189 }
190 if details.status == AssetStatus::Frozen {
191 return Frozen;
192 }
193 if details.status == AssetStatus::Destroying {
194 return UnknownAsset;
195 }
196 if amount.is_zero() {
197 return Success;
198 }
199 let account = match Account::<T, I>::get(&id, who) {
200 Some(a) => a,
201 None => return BalanceLow,
202 };
203 if account.status.is_frozen() {
204 return Frozen;
205 }
206 if let Some(rest) = account.balance.checked_sub(&amount) {
207 match (
208 T::Holder::balance_on_hold(id.clone(), who),
209 T::Freezer::frozen_balance(id.clone(), who),
210 ) {
211 (None, None) => {
212 if rest < details.min_balance {
213 if keep_alive {
214 WouldDie
215 } else {
216 ReducedToZero(rest)
217 }
218 } else {
219 Success
220 }
221 },
222 (maybe_held, maybe_frozen) => {
223 let frozen = maybe_frozen.unwrap_or_default();
224 let held = maybe_held.unwrap_or_default();
225
226 let untouchable = frozen.saturating_sub(held).max(details.min_balance);
229 if rest < untouchable {
230 if !frozen.is_zero() {
231 Frozen
232 } else {
233 WouldDie
234 }
235 } else {
236 Success
237 }
238 },
239 }
240 } else {
241 BalanceLow
242 }
243 }
244
245 pub(super) fn reducible_balance(
248 id: T::AssetId,
249 who: &T::AccountId,
250 keep_alive: bool,
251 ) -> Result<T::Balance, DispatchError> {
252 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
253 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
254
255 let account = Account::<T, I>::get(&id, who).ok_or(Error::<T, I>::NoAccount)?;
256 ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
257
258 let untouchable = match (
259 T::Holder::balance_on_hold(id.clone(), who),
260 T::Freezer::frozen_balance(id.clone(), who),
261 keep_alive,
262 ) {
263 (None, None, true) => details.min_balance,
264 (None, None, false) => Zero::zero(),
265 (maybe_held, maybe_frozen, _) => {
266 let held = maybe_held.unwrap_or_default();
267 let frozen = maybe_frozen.unwrap_or_default();
268 frozen.saturating_sub(held).max(details.min_balance)
269 },
270 };
271 let amount = account.balance.saturating_sub(untouchable);
272
273 Ok(amount.min(details.supply))
274 }
275
276 pub(super) fn prep_debit(
292 id: T::AssetId,
293 target: &T::AccountId,
294 amount: T::Balance,
295 f: DebitFlags,
296 ) -> Result<T::Balance, DispatchError> {
297 let actual = Self::reducible_balance(id.clone(), target, f.keep_alive)?.min(amount);
298 ensure!(f.best_effort || actual >= amount, Error::<T, I>::BalanceLow);
299
300 let conseq = Self::can_decrease(id, target, actual, f.keep_alive);
301 let actual = match conseq.into_result(f.keep_alive) {
302 Ok(dust) => actual.saturating_add(dust), Err(e) => {
304 debug_assert!(false, "passed from reducible_balance; qed");
305 return Err(e);
306 },
307 };
308
309 Ok(actual)
310 }
311
312 pub(super) fn prep_credit(
328 id: T::AssetId,
329 dest: &T::AccountId,
330 amount: T::Balance,
331 debit: T::Balance,
332 burn_dust: bool,
333 ) -> Result<(T::Balance, Option<T::Balance>), DispatchError> {
334 let (credit, maybe_burn) = match (burn_dust, debit.checked_sub(&amount)) {
335 (true, Some(dust)) if !dust.is_zero() => (amount, Some(dust)),
336 _ => (debit, None),
337 };
338 Self::can_increase(id, dest, credit, false).into_result()?;
339 Ok((credit, maybe_burn))
340 }
341
342 pub(super) fn do_touch(
344 id: T::AssetId,
345 who: T::AccountId,
346 depositor: T::AccountId,
347 ) -> DispatchResult {
348 ensure!(!Account::<T, I>::contains_key(&id, &who), Error::<T, I>::AlreadyExists);
349 let deposit = T::AssetAccountDeposit::get();
350 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
351 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
352 let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?;
353 T::Currency::reserve(&depositor, deposit)?;
354 Asset::<T, I>::insert(&id, details);
355 Account::<T, I>::insert(
356 &id,
357 &who,
358 AssetAccountOf::<T, I> {
359 balance: Zero::zero(),
360 status: AccountStatus::Liquid,
361 reason,
362 extra: T::Extra::default(),
363 },
364 );
365 Self::deposit_event(Event::Touched { asset_id: id, who, depositor });
366 Ok(())
367 }
368
369 pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult {
372 use AssetStatus::*;
373 use ExistenceReason::*;
374
375 let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
376 ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::<T, I>::NoDeposit);
377 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
378 ensure!(matches!(details.status, Live | Frozen), Error::<T, I>::IncorrectStatus);
379 ensure!(account.balance.is_zero() || allow_burn, Error::<T, I>::WouldBurn);
380 Self::ensure_account_can_die(id.clone(), &who)?;
381
382 if let Some(deposit) = account.reason.take_deposit() {
383 T::Currency::unreserve(&who, deposit);
384 }
385
386 if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
387 if !account.balance.is_zero() {
388 debug_assert!(details.supply >= account.balance, "supply < balance; qed");
389 details.supply = details.supply.saturating_sub(account.balance);
390 }
391 Account::<T, I>::remove(&id, &who);
392 } else {
393 debug_assert!(false, "refund did not result in dead account?!");
394 Account::<T, I>::insert(id, &who, account);
396 return Ok(());
397 }
398
399 Asset::<T, I>::insert(&id, details);
400
401 if !account.balance.is_zero() {
402 Self::deposit_event(Event::Burned {
403 asset_id: id.clone(),
404 owner: who.clone(),
405 balance: account.balance,
406 });
407 T::CallbackHandle::burned(&id, &who, account.balance);
408 }
409
410 T::Freezer::died(id.clone(), &who);
412 T::Holder::died(id, &who);
413 Ok(())
414 }
415
416 pub(super) fn do_refund_other(
421 id: T::AssetId,
422 who: &T::AccountId,
423 maybe_check_caller: Option<T::AccountId>,
424 ) -> DispatchResult {
425 let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?;
426 let (depositor, deposit) =
427 account.reason.take_deposit_from().ok_or(Error::<T, I>::NoDeposit)?;
428 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
429 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
430 ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen);
431 if let Some(caller) = maybe_check_caller {
432 ensure!(caller == depositor || caller == details.admin, Error::<T, I>::NoPermission);
433 }
434 ensure!(account.balance.is_zero(), Error::<T, I>::WouldBurn);
435 Self::ensure_account_can_die(id.clone(), who)?;
436
437 T::Currency::unreserve(&depositor, deposit);
438
439 if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) {
440 Account::<T, I>::remove(&id, &who);
441 } else {
442 debug_assert!(false, "refund did not result in dead account?!");
443 Account::<T, I>::insert(&id, &who, account);
445 return Ok(());
446 }
447 Asset::<T, I>::insert(&id, details);
448 T::Freezer::died(id.clone(), who);
450 T::Holder::died(id, &who);
451 return Ok(());
452 }
453
454 pub(super) fn do_mint(
460 id: T::AssetId,
461 beneficiary: &T::AccountId,
462 amount: T::Balance,
463 maybe_check_issuer: Option<T::AccountId>,
464 ) -> DispatchResult {
465 Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult {
466 if let Some(check_issuer) = maybe_check_issuer {
467 ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission);
468 }
469 debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed");
470
471 details.supply = details.supply.saturating_add(amount);
472
473 Ok(())
474 })?;
475
476 Self::deposit_event(Event::Issued {
477 asset_id: id.clone(),
478 owner: beneficiary.clone(),
479 amount,
480 });
481 T::CallbackHandle::issued(&id, beneficiary, amount);
482
483 Ok(())
484 }
485
486 pub(super) fn increase_balance(
493 id: T::AssetId,
494 beneficiary: &T::AccountId,
495 amount: T::Balance,
496 check: impl FnOnce(
497 &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
498 ) -> DispatchResult,
499 ) -> DispatchResult {
500 if amount.is_zero() {
501 return Ok(());
502 }
503
504 Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?;
505 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
506 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
507 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
508 check(details)?;
509
510 Account::<T, I>::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult {
511 match maybe_account {
512 Some(ref mut account) => {
513 account.balance.saturating_accrue(amount);
514 },
515 maybe_account @ None => {
516 ensure!(amount >= details.min_balance, TokenError::BelowMinimum);
519 *maybe_account = Some(AssetAccountOf::<T, I> {
520 balance: amount,
521 reason: Self::new_account(beneficiary, details, None)?,
522 status: AccountStatus::Liquid,
523 extra: T::Extra::default(),
524 });
525 },
526 }
527 Ok(())
528 })?;
529 Ok(())
530 })?;
531 Ok(())
532 }
533
534 pub(super) fn do_burn(
542 id: T::AssetId,
543 target: &T::AccountId,
544 amount: T::Balance,
545 maybe_check_admin: Option<T::AccountId>,
546 f: DebitFlags,
547 ) -> Result<T::Balance, DispatchError> {
548 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
549 ensure!(
550 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
551 Error::<T, I>::IncorrectStatus
552 );
553
554 let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| {
555 if let Some(check_admin) = maybe_check_admin {
557 ensure!(check_admin == details.admin, Error::<T, I>::NoPermission);
558 }
559
560 debug_assert!(details.supply >= actual, "checked in prep; qed");
561 details.supply = details.supply.saturating_sub(actual);
562
563 Ok(())
564 })?;
565 Self::deposit_event(Event::Burned {
566 asset_id: id.clone(),
567 owner: target.clone(),
568 balance: actual,
569 });
570 T::CallbackHandle::burned(&id, target, actual);
571 Ok(actual)
572 }
573
574 pub(super) fn decrease_balance(
583 id: T::AssetId,
584 target: &T::AccountId,
585 amount: T::Balance,
586 f: DebitFlags,
587 check: impl FnOnce(
588 T::Balance,
589 &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
590 ) -> DispatchResult,
591 ) -> Result<T::Balance, DispatchError> {
592 if amount.is_zero() {
593 return Ok(amount);
594 }
595
596 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
597 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
598
599 let actual = Self::prep_debit(id.clone(), target, amount, f)?;
600 let mut target_died: Option<DeadConsequence> = None;
601
602 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
603 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
604 check(actual, details)?;
605
606 Account::<T, I>::try_mutate(&id, target, |maybe_account| -> DispatchResult {
607 let mut account = maybe_account.take().ok_or(Error::<T, I>::NoAccount)?;
608 debug_assert!(account.balance >= actual, "checked in prep; qed");
609
610 account.balance = account.balance.saturating_sub(actual);
612 if account.balance < details.min_balance {
613 debug_assert!(account.balance.is_zero(), "checked in prep; qed");
614 Self::ensure_account_can_die(id.clone(), target)?;
615 target_died = Some(Self::dead_account(target, details, &account.reason, false));
616 if let Some(Remove) = target_died {
617 return Ok(());
618 }
619 };
620 *maybe_account = Some(account);
621 Ok(())
622 })?;
623
624 Ok(())
625 })?;
626
627 if let Some(Remove) = target_died {
629 T::Freezer::died(id.clone(), target);
630 T::Holder::died(id, target);
631 }
632 Ok(actual)
633 }
634
635 pub fn do_transfer(
644 id: T::AssetId,
645 source: &T::AccountId,
646 dest: &T::AccountId,
647 amount: T::Balance,
648 maybe_need_admin: Option<T::AccountId>,
649 f: TransferFlags,
650 ) -> Result<T::Balance, DispatchError> {
651 let (balance, died) =
652 Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f, |_| {
653 Ok(())
654 })?;
655 if let Some(Remove) = died {
656 T::Freezer::died(id.clone(), source);
657 T::Holder::died(id, source);
658 }
659 Ok(balance)
660 }
661
662 fn transfer_and_die(
668 id: T::AssetId,
669 source: &T::AccountId,
670 dest: &T::AccountId,
671 amount: T::Balance,
672 maybe_need_admin: Option<T::AccountId>,
673 f: TransferFlags,
674 check: impl FnOnce(T::Balance) -> DispatchResult,
675 ) -> Result<(T::Balance, Option<DeadConsequence>), DispatchError> {
676 if amount.is_zero() {
678 return Ok((amount, None));
679 }
680 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
681 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
682
683 let debit = Self::prep_debit(id.clone(), source, amount, f.into())?;
685 let debit = if source == dest { amount } else { debit };
687 check(debit)?;
688 let (credit, maybe_burn) = Self::prep_credit(id.clone(), dest, amount, debit, f.burn_dust)?;
689
690 let mut source_account =
691 Account::<T, I>::get(&id, &source).ok_or(Error::<T, I>::NoAccount)?;
692 let mut source_died: Option<DeadConsequence> = None;
693
694 Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult {
695 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
696
697 if let Some(need_admin) = maybe_need_admin {
699 ensure!(need_admin == details.admin, Error::<T, I>::NoPermission);
700 }
701
702 if source == dest {
704 return Ok(());
705 }
706
707 if let Some(burn) = maybe_burn {
709 debug_assert!(details.supply >= burn, "checked in prep; qed");
712 details.supply = details.supply.saturating_sub(burn);
713 }
714
715 debug_assert!(source_account.balance >= debit, "checked in prep; qed");
717 source_account.balance = source_account.balance.saturating_sub(debit);
718
719 if source_account.balance < details.min_balance {
721 debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
722 Self::ensure_account_can_die(id.clone(), source)?;
723 }
724
725 Account::<T, I>::try_mutate(&id, &dest, |maybe_account| -> DispatchResult {
726 match maybe_account {
727 Some(ref mut account) => {
728 debug_assert!(
731 account.balance.checked_add(&credit).is_some(),
732 "checked in prep; qed"
733 );
734 account.balance.saturating_accrue(credit);
735 },
736 maybe_account @ None => {
737 *maybe_account = Some(AssetAccountOf::<T, I> {
738 balance: credit,
739 status: AccountStatus::Liquid,
740 reason: Self::new_account(dest, details, None)?,
741 extra: T::Extra::default(),
742 });
743 },
744 }
745 Ok(())
746 })?;
747
748 if source_account.balance < details.min_balance {
750 debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
751 source_died =
752 Some(Self::dead_account(source, details, &source_account.reason, false));
753 if let Some(Remove) = source_died {
754 Account::<T, I>::remove(&id, &source);
755 return Ok(());
756 }
757 }
758 Account::<T, I>::insert(&id, &source, &source_account);
759 Ok(())
760 })?;
761
762 Self::deposit_event(Event::Transferred {
763 asset_id: id.clone(),
764 from: source.clone(),
765 to: dest.clone(),
766 amount: credit,
767 });
768 T::CallbackHandle::transferred(&id, source, dest, credit);
769 if let Some(burn) = maybe_burn {
770 T::CallbackHandle::burned(&id, source, burn);
771 }
772 Ok((credit, source_died))
773 }
774
775 pub(super) fn do_force_create(
786 id: T::AssetId,
787 owner: T::AccountId,
788 is_sufficient: bool,
789 min_balance: T::Balance,
790 enforce_allocator: bool,
791 ) -> DispatchResult {
792 ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
793 ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
794 if enforce_allocator {
795 if let Some(next_id) = T::AssetIdAllocator::next() {
796 ensure!(id == next_id, Error::<T, I>::BadAssetId);
797 }
798 }
799
800 Asset::<T, I>::insert(
801 &id,
802 AssetDetails {
803 owner: owner.clone(),
804 issuer: owner.clone(),
805 admin: owner.clone(),
806 freezer: owner.clone(),
807 supply: Zero::zero(),
808 deposit: Zero::zero(),
809 min_balance,
810 is_sufficient,
811 accounts: 0,
812 sufficients: 0,
813 approvals: 0,
814 status: AssetStatus::Live,
815 },
816 );
817 ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
818 T::AssetIdAllocator::advance_from(&id)
819 .map_err(|_| Error::<T, I>::AssetIdAllocationFailed)?;
820 Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() });
821 Ok(())
822 }
823
824 pub(super) fn do_start_destroy(
827 id: T::AssetId,
828 maybe_check_owner: Option<T::AccountId>,
829 ) -> DispatchResult {
830 Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
831 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
832 if let Some(check_owner) = maybe_check_owner {
833 ensure!(details.owner == check_owner, Error::<T, I>::NoPermission);
834 }
835
836 ensure!(!T::Holder::contains_holds(id.clone()), Error::<T, I>::ContainsHolds);
837 ensure!(!T::Freezer::contains_freezes(id.clone()), Error::<T, I>::ContainsFreezes);
838
839 details.status = AssetStatus::Destroying;
840
841 Self::deposit_event(Event::DestructionStarted { asset_id: id });
842 Ok(())
843 })
844 }
845
846 pub(super) fn do_destroy_accounts(
851 id: T::AssetId,
852 max_items: u32,
853 ) -> Result<u32, DispatchError> {
854 let mut dead_accounts: Vec<T::AccountId> = vec![];
855 let mut remaining_accounts = 0;
856 Asset::<T, I>::try_mutate_exists(&id, |maybe_details| -> Result<(), DispatchError> {
857 let mut details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
858 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
860
861 for (i, (who, mut v)) in Account::<T, I>::iter_prefix(&id).enumerate() {
862 if Self::ensure_account_can_die(id.clone(), &who).is_err() {
863 continue;
864 }
865 if let Some((depositor, deposit)) = v.reason.take_deposit_from() {
867 T::Currency::unreserve(&depositor, deposit);
868 } else if let Some(deposit) = v.reason.take_deposit() {
869 T::Currency::unreserve(&who, deposit);
870 }
871 if let Remove = Self::dead_account(&who, &mut details, &v.reason, false) {
872 Account::<T, I>::remove(&id, &who);
873 dead_accounts.push(who);
874 } else {
875 Account::<T, I>::insert(&id, &who, v);
877 defensive!("destroy did not result in dead account?!");
878 }
879 if i + 1 >= (max_items as usize) {
880 break;
881 }
882 }
883 remaining_accounts = details.accounts;
884 Ok(())
885 })?;
886
887 for who in &dead_accounts {
888 T::Freezer::died(id.clone(), &who);
889 T::Holder::died(id.clone(), &who);
890 }
891
892 Self::deposit_event(Event::AccountsDestroyed {
893 asset_id: id,
894 accounts_destroyed: dead_accounts.len() as u32,
895 accounts_remaining: remaining_accounts as u32,
896 });
897 Ok(dead_accounts.len() as u32)
898 }
899
900 pub(super) fn do_destroy_approvals(
905 id: T::AssetId,
906 max_items: u32,
907 ) -> Result<u32, DispatchError> {
908 let mut removed_approvals = 0;
909 Asset::<T, I>::try_mutate_exists(
910 id.clone(),
911 |maybe_details| -> Result<(), DispatchError> {
912 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
913
914 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
916
917 for ((owner, _), approval) in Approvals::<T, I>::drain_prefix((id.clone(),)) {
918 T::Currency::unreserve(&owner, approval.deposit);
919 removed_approvals = removed_approvals.saturating_add(1);
920 details.approvals = details.approvals.saturating_sub(1);
921 if removed_approvals >= max_items {
922 break;
923 }
924 }
925 Self::deposit_event(Event::ApprovalsDestroyed {
926 asset_id: id,
927 approvals_destroyed: removed_approvals as u32,
928 approvals_remaining: details.approvals as u32,
929 });
930 Ok(())
931 },
932 )?;
933 Ok(removed_approvals)
934 }
935
936 pub(super) fn do_finish_destroy(id: T::AssetId) -> DispatchResult {
940 Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> {
941 let details = maybe_details.take().ok_or(Error::<T, I>::Unknown)?;
942 ensure!(details.status == AssetStatus::Destroying, Error::<T, I>::IncorrectStatus);
943 ensure!(details.accounts == 0, Error::<T, I>::InUse);
944 ensure!(details.approvals == 0, Error::<T, I>::InUse);
945 ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::<T, I>::CallbackFailed);
946
947 let metadata = Metadata::<T, I>::take(&id);
948 T::Currency::unreserve(
949 &details.owner,
950 details.deposit.saturating_add(metadata.deposit),
951 );
952 Reserves::<T, I>::remove(&id);
953 Self::deposit_event(Event::Destroyed { asset_id: id });
954
955 Ok(())
956 })
957 }
958
959 pub fn do_approve_transfer(
964 id: T::AssetId,
965 owner: &T::AccountId,
966 delegate: &T::AccountId,
967 amount: T::Balance,
968 ) -> DispatchResult {
969 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
970 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
971 Approvals::<T, I>::try_mutate(
972 (id.clone(), &owner, &delegate),
973 |maybe_approved| -> DispatchResult {
974 let mut approved = match maybe_approved.take() {
975 Some(a) => a,
977 None => {
979 d.approvals.saturating_inc();
980 Default::default()
981 },
982 };
983 let deposit_required = T::ApprovalDeposit::get();
984 if approved.deposit < deposit_required {
985 T::Currency::reserve(owner, deposit_required - approved.deposit)?;
986 approved.deposit = deposit_required;
987 }
988 approved.amount = approved.amount.saturating_add(amount);
989 *maybe_approved = Some(approved);
990 Ok(())
991 },
992 )?;
993 Asset::<T, I>::insert(&id, d);
994 Self::deposit_event(Event::ApprovedTransfer {
995 asset_id: id,
996 source: owner.clone(),
997 delegate: delegate.clone(),
998 amount,
999 });
1000
1001 Ok(())
1002 }
1003
1004 pub fn do_cancel_approval(
1008 id: &T::AssetId,
1009 owner: &T::AccountId,
1010 delegate: &T::AccountId,
1011 ) -> DispatchResult {
1012 let mut asset_details = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
1013 ensure!(asset_details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1014
1015 let approval =
1016 Approvals::<T, I>::take((id.clone(), owner, delegate)).ok_or(Error::<T, I>::Unknown)?;
1017 T::Currency::unreserve(owner, approval.deposit);
1018
1019 asset_details.approvals.saturating_dec();
1020 Asset::<T, I>::insert(id, asset_details);
1021
1022 Self::deposit_event(Event::ApprovalCancelled {
1023 asset_id: id.clone(),
1024 owner: owner.clone(),
1025 delegate: delegate.clone(),
1026 });
1027 Ok(())
1028 }
1029
1030 pub fn do_transfer_approved(
1040 id: T::AssetId,
1041 owner: &T::AccountId,
1042 delegate: &T::AccountId,
1043 destination: &T::AccountId,
1044 amount: T::Balance,
1045 ) -> DispatchResult {
1046 let mut owner_died: Option<DeadConsequence> = None;
1047
1048 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1049 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1050
1051 Approvals::<T, I>::try_mutate_exists(
1052 (id.clone(), &owner, delegate),
1053 |maybe_approved| -> DispatchResult {
1054 let mut approved = maybe_approved.take().ok_or(Error::<T, I>::Unapproved)?;
1055 ensure!(approved.amount >= amount, Error::<T, I>::Unapproved);
1057
1058 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1059 let mut remaining = approved.amount;
1060 owner_died = Self::transfer_and_die(
1061 id.clone(),
1062 owner,
1063 destination,
1064 amount,
1065 None,
1066 f,
1067 |debit| {
1068 remaining =
1069 approved.amount.checked_sub(&debit).ok_or(Error::<T, I>::Unapproved)?;
1070 Ok(())
1071 },
1072 )?
1073 .1;
1074
1075 if remaining.is_zero() {
1076 T::Currency::unreserve(owner, approved.deposit);
1077 Asset::<T, I>::mutate(id.clone(), |maybe_details| {
1078 if let Some(details) = maybe_details {
1079 details.approvals.saturating_dec();
1080 }
1081 });
1082 } else {
1083 approved.amount = remaining;
1084 *maybe_approved = Some(approved);
1085 }
1086 Ok(())
1087 },
1088 )?;
1089
1090 if let Some(Remove) = owner_died {
1092 T::Freezer::died(id.clone(), owner);
1093 T::Holder::died(id, owner);
1094 }
1095 Ok(())
1096 }
1097
1098 pub(super) fn do_set_metadata(
1100 id: T::AssetId,
1101 from: &T::AccountId,
1102 name: Vec<u8>,
1103 symbol: Vec<u8>,
1104 decimals: u8,
1105 ) -> DispatchResult {
1106 let bounded_name: BoundedVec<u8, T::StringLimit> =
1107 name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1108 let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1109 symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1110
1111 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1112 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1113 ensure!(from == &d.owner, Error::<T, I>::NoPermission);
1114
1115 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1116 ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::<T, I>::NoPermission);
1117
1118 let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1119 let new_deposit = Self::calc_metadata_deposit(&name, &symbol);
1120
1121 if new_deposit > old_deposit {
1122 T::Currency::reserve(from, new_deposit - old_deposit)?;
1123 } else {
1124 T::Currency::unreserve(from, old_deposit - new_deposit);
1125 }
1126
1127 *metadata = Some(AssetMetadata {
1128 deposit: new_deposit,
1129 name: bounded_name,
1130 symbol: bounded_symbol,
1131 decimals,
1132 is_frozen: false,
1133 });
1134
1135 Self::deposit_event(Event::MetadataSet {
1136 asset_id: id,
1137 name,
1138 symbol,
1139 decimals,
1140 is_frozen: false,
1141 });
1142 Ok(())
1143 })
1144 }
1145
1146 pub(super) fn calc_metadata_deposit(name: &[u8], symbol: &[u8]) -> DepositBalanceOf<T, I> {
1148 T::MetadataDepositPerByte::get()
1149 .saturating_mul(((name.len() + symbol.len()) as u32).into())
1150 .saturating_add(T::MetadataDepositBase::get())
1151 }
1152
1153 pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
1155 Asset::<T, I>::iter_keys()
1156 .filter_map(|id| {
1157 Self::maybe_balance(id.clone(), account.clone()).map(|balance| (id, balance))
1158 })
1159 .collect::<Vec<_>>()
1160 }
1161
1162 pub(crate) fn do_reset_team(
1171 id: T::AssetId,
1172 owner: T::AccountId,
1173 admin: T::AccountId,
1174 issuer: T::AccountId,
1175 freezer: T::AccountId,
1176 ) -> DispatchResult {
1177 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1178 d.owner = owner;
1179 d.admin = admin;
1180 d.issuer = issuer;
1181 d.freezer = freezer;
1182 Asset::<T, I>::insert(&id, d);
1183 Ok(())
1184 }
1185
1186 pub fn unchecked_update_reserves(
1189 id: T::AssetId,
1190 reserves: BoundedVec<T::ReserveData, ConstU32<MAX_RESERVES>>,
1191 ) -> Result<(), Error<T, I>> {
1192 if reserves.is_empty() {
1193 Reserves::<T, I>::remove(&id);
1194 Self::deposit_event(Event::ReservesRemoved { asset_id: id });
1195 } else {
1196 let reserves_vec = reserves.clone().into_inner();
1197 Reserves::<T, I>::set(&id, reserves);
1198 Self::deposit_event(Event::ReservesUpdated { asset_id: id, reserves: reserves_vec });
1199 }
1200 Ok(())
1201 }
1202}