1#![recursion_limit = "1024"]
151#![cfg_attr(not(feature = "std"), no_std)]
153
154#[cfg(feature = "runtime-benchmarks")]
155pub mod benchmarking;
156pub mod migration;
157#[cfg(test)]
158pub mod mock;
159#[cfg(test)]
160mod tests;
161pub mod weights;
162
163mod extra_mutator;
164pub use extra_mutator::*;
165mod functions;
166mod impl_fungibles;
167mod impl_stored_map;
168mod types;
169pub use types::*;
170
171extern crate alloc;
172extern crate core;
173
174use scale_info::TypeInfo;
175use sp_runtime::{
176 traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero},
177 ArithmeticError, DispatchError, TokenError,
178};
179
180use alloc::vec::Vec;
181use core::{fmt::Debug, marker::PhantomData};
182use frame_support::{
183 dispatch::DispatchResult,
184 ensure,
185 pallet_prelude::DispatchResultWithPostInfo,
186 storage::KeyPrefixIterator,
187 traits::{
188 tokens::{
189 fungibles, DepositConsequence, Fortitude,
190 Preservation::{Expendable, Preserve},
191 WithdrawConsequence,
192 },
193 BalanceStatus::Reserved,
194 Currency, EnsureOriginWithArg, Incrementable, ReservableCurrency, StoredMap,
195 },
196};
197use frame_system::Config as SystemConfig;
198
199pub use pallet::*;
200pub use weights::WeightInfo;
201
202type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
203const LOG_TARGET: &str = "runtime::assets";
204
205pub trait AssetsCallback<AssetId, AccountId> {
207 fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> {
209 Ok(())
210 }
211
212 fn destroyed(_id: &AssetId) -> Result<(), ()> {
214 Ok(())
215 }
216}
217
218#[impl_trait_for_tuples::impl_for_tuples(10)]
219impl<AssetId, AccountId> AssetsCallback<AssetId, AccountId> for Tuple {
220 fn created(id: &AssetId, owner: &AccountId) -> Result<(), ()> {
221 for_tuples!( #( Tuple::created(id, owner)?; )* );
222 Ok(())
223 }
224
225 fn destroyed(id: &AssetId) -> Result<(), ()> {
226 for_tuples!( #( Tuple::destroyed(id)?; )* );
227 Ok(())
228 }
229}
230
231pub struct AutoIncAssetId<T, I = ()>(PhantomData<(T, I)>);
235impl<T: Config<I>, I> AssetsCallback<T::AssetId, T::AccountId> for AutoIncAssetId<T, I>
236where
237 T::AssetId: Incrementable,
238{
239 fn created(_: &T::AssetId, _: &T::AccountId) -> Result<(), ()> {
240 let Some(next_id) = NextAssetId::<T, I>::get() else {
241 return Ok(());
243 };
244 let next_id = next_id.increment().ok_or(())?;
245 NextAssetId::<T, I>::put(next_id);
246 Ok(())
247 }
248}
249
250#[frame_support::pallet]
251pub mod pallet {
252 use super::*;
253 use codec::HasCompact;
254 use frame_support::{
255 pallet_prelude::*,
256 traits::{tokens::ProvideAssetReserves, AccountTouch, ContainsPair},
257 };
258 use frame_system::pallet_prelude::*;
259
260 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
262
263 pub const MAX_RESERVES: u32 = 5;
265
266 #[pallet::pallet]
267 #[pallet::storage_version(STORAGE_VERSION)]
268 pub struct Pallet<T, I = ()>(_);
269
270 #[cfg(feature = "runtime-benchmarks")]
271 pub trait BenchmarkHelper<AssetIdParameter, ReserveIdParameter> {
272 fn create_asset_id_parameter(id: u32) -> AssetIdParameter;
273 fn create_reserve_id_parameter(id: u32) -> ReserveIdParameter;
274 }
275 #[cfg(feature = "runtime-benchmarks")]
276 impl<AssetIdParameter: From<u32>> BenchmarkHelper<AssetIdParameter, ()> for () {
277 fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
278 id.into()
279 }
280 fn create_reserve_id_parameter(_: u32) -> () {
281 ()
282 }
283 }
284
285 pub mod config_preludes {
287 use super::*;
288 use frame_support::derive_impl;
289 pub struct TestDefaultConfig;
290
291 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
292 impl frame_system::DefaultConfig for TestDefaultConfig {}
293
294 #[frame_support::register_default_impl(TestDefaultConfig)]
295 impl DefaultConfig for TestDefaultConfig {
296 #[inject_runtime_type]
297 type RuntimeEvent = ();
298 type Balance = u64;
299 type RemoveItemsLimit = ConstU32<5>;
300 type AssetId = u32;
301 type AssetIdParameter = u32;
302 type ReserveData = ();
303 type AssetDeposit = ConstUint<1>;
304 type AssetAccountDeposit = ConstUint<10>;
305 type MetadataDepositBase = ConstUint<1>;
306 type MetadataDepositPerByte = ConstUint<1>;
307 type ApprovalDeposit = ConstUint<1>;
308 type StringLimit = ConstU32<50>;
309 type Freezer = ();
310 type Holder = ();
311 type Extra = ();
312 type CallbackHandle = ();
313 type WeightInfo = ();
314 #[cfg(feature = "runtime-benchmarks")]
315 type BenchmarkHelper = ();
316 }
317 }
318
319 #[pallet::config(with_default)]
320 pub trait Config<I: 'static = ()>: frame_system::Config {
322 #[pallet::no_default_bounds]
324 #[allow(deprecated)]
325 type RuntimeEvent: From<Event<Self, I>>
326 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
327
328 type Balance: Member
330 + Parameter
331 + HasCompact<Type: DecodeWithMemTracking>
332 + AtLeast32BitUnsigned
333 + Default
334 + Copy
335 + MaybeSerializeDeserialize
336 + MaxEncodedLen
337 + TypeInfo;
338
339 #[pallet::constant]
343 type RemoveItemsLimit: Get<u32>;
344
345 type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
347
348 type AssetIdParameter: Parameter + From<Self::AssetId> + Into<Self::AssetId> + MaxEncodedLen;
356
357 type ReserveData: Debug + Parameter + MaybeSerializeDeserialize + MaxEncodedLen;
359
360 #[pallet::no_default]
362 type Currency: ReservableCurrency<Self::AccountId>;
363
364 #[pallet::no_default]
367 type CreateOrigin: EnsureOriginWithArg<
368 Self::RuntimeOrigin,
369 Self::AssetId,
370 Success = Self::AccountId,
371 >;
372
373 #[pallet::no_default]
376 type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
377
378 #[pallet::constant]
380 #[pallet::no_default_bounds]
381 type AssetDeposit: Get<DepositBalanceOf<Self, I>>;
382
383 #[pallet::constant]
386 #[pallet::no_default_bounds]
387 type AssetAccountDeposit: Get<DepositBalanceOf<Self, I>>;
388
389 #[pallet::constant]
391 #[pallet::no_default_bounds]
392 type MetadataDepositBase: Get<DepositBalanceOf<Self, I>>;
393
394 #[pallet::constant]
397 #[pallet::no_default_bounds]
398 type MetadataDepositPerByte: Get<DepositBalanceOf<Self, I>>;
399
400 #[pallet::constant]
402 #[pallet::no_default_bounds]
403 type ApprovalDeposit: Get<DepositBalanceOf<Self, I>>;
404
405 #[pallet::constant]
407 type StringLimit: Get<u32>;
408
409 type Freezer: FrozenBalance<Self::AssetId, Self::AccountId, Self::Balance>;
412
413 type Holder: BalanceOnHold<Self::AssetId, Self::AccountId, Self::Balance>;
416
417 type Extra: Member + Parameter + Default + MaxEncodedLen;
419
420 type CallbackHandle: AssetsCallback<Self::AssetId, Self::AccountId>;
427
428 type WeightInfo: WeightInfo;
430
431 #[cfg(feature = "runtime-benchmarks")]
433 type BenchmarkHelper: BenchmarkHelper<Self::AssetIdParameter, Self::ReserveData>;
434 }
435
436 #[pallet::storage]
437 pub type Asset<T: Config<I>, I: 'static = ()> = StorageMap<
439 _,
440 Blake2_128Concat,
441 T::AssetId,
442 AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
443 >;
444
445 #[pallet::storage]
446 pub type Account<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
448 _,
449 Blake2_128Concat,
450 T::AssetId,
451 Blake2_128Concat,
452 T::AccountId,
453 AssetAccountOf<T, I>,
454 >;
455
456 #[pallet::storage]
457 pub type Approvals<T: Config<I>, I: 'static = ()> = StorageNMap<
461 _,
462 (
463 NMapKey<Blake2_128Concat, T::AssetId>,
464 NMapKey<Blake2_128Concat, T::AccountId>, NMapKey<Blake2_128Concat, T::AccountId>, ),
467 Approval<T::Balance, DepositBalanceOf<T, I>>,
468 >;
469
470 #[pallet::storage]
471 pub type Metadata<T: Config<I>, I: 'static = ()> = StorageMap<
473 _,
474 Blake2_128Concat,
475 T::AssetId,
476 AssetMetadata<DepositBalanceOf<T, I>, BoundedVec<u8, T::StringLimit>>,
477 ValueQuery,
478 >;
479
480 #[pallet::storage]
482 pub type Reserves<T: Config<I>, I: 'static = ()> = StorageMap<
483 _,
484 Blake2_128Concat,
485 T::AssetId,
486 BoundedVec<T::ReserveData, ConstU32<MAX_RESERVES>>,
487 ValueQuery,
488 >;
489
490 #[pallet::storage]
500 pub type NextAssetId<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AssetId, OptionQuery>;
501
502 #[pallet::genesis_config]
503 #[derive(frame_support::DefaultNoBound)]
504 pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
505 pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>,
507 pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>,
509 pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>,
511 pub next_asset_id: Option<T::AssetId>,
518 pub reserves: Vec<(T::AssetId, Vec<T::ReserveData>)>,
520 }
521
522 #[pallet::genesis_build]
523 impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
524 fn build(&self) {
525 for (id, owner, is_sufficient, min_balance) in &self.assets {
526 assert!(!Asset::<T, I>::contains_key(id), "Asset id already in use");
527 assert!(!min_balance.is_zero(), "Min balance should not be zero");
528 Asset::<T, I>::insert(
529 id,
530 AssetDetails {
531 owner: owner.clone(),
532 issuer: owner.clone(),
533 admin: owner.clone(),
534 freezer: owner.clone(),
535 supply: Zero::zero(),
536 deposit: Zero::zero(),
537 min_balance: *min_balance,
538 is_sufficient: *is_sufficient,
539 accounts: 0,
540 sufficients: 0,
541 approvals: 0,
542 status: AssetStatus::Live,
543 },
544 );
545 }
546
547 for (id, name, symbol, decimals) in &self.metadata {
548 assert!(Asset::<T, I>::contains_key(id), "Asset does not exist");
549
550 let bounded_name: BoundedVec<u8, T::StringLimit> =
551 name.clone().try_into().expect("asset name is too long");
552 let bounded_symbol: BoundedVec<u8, T::StringLimit> =
553 symbol.clone().try_into().expect("asset symbol is too long");
554
555 let metadata = AssetMetadata {
556 deposit: Zero::zero(),
557 name: bounded_name,
558 symbol: bounded_symbol,
559 decimals: *decimals,
560 is_frozen: false,
561 };
562 Metadata::<T, I>::insert(id, metadata);
563 }
564
565 for (id, account_id, amount) in &self.accounts {
566 let result = <Pallet<T, I>>::increase_balance(
567 id.clone(),
568 account_id,
569 *amount,
570 |details| -> DispatchResult {
571 debug_assert!(
572 details.supply.checked_add(&amount).is_some(),
573 "checked in prep; qed"
574 );
575 details.supply = details.supply.saturating_add(*amount);
576 Ok(())
577 },
578 );
579 assert!(result.is_ok());
580 }
581
582 if let Some(next_asset_id) = &self.next_asset_id {
583 NextAssetId::<T, I>::put(next_asset_id);
584 }
585
586 for (id, reserves) in &self.reserves {
587 assert!(!Reserves::<T, I>::contains_key(id), "Asset id already in use");
588 let reserves = BoundedVec::try_from(reserves.clone()).expect("too many reserves");
589 Reserves::<T, I>::insert(id, reserves);
590 }
591 }
592 }
593
594 #[pallet::event]
595 #[pallet::generate_deposit(pub(super) fn deposit_event)]
596 pub enum Event<T: Config<I>, I: 'static = ()> {
597 Created { asset_id: T::AssetId, creator: T::AccountId, owner: T::AccountId },
599 Issued { asset_id: T::AssetId, owner: T::AccountId, amount: T::Balance },
601 Transferred {
603 asset_id: T::AssetId,
604 from: T::AccountId,
605 to: T::AccountId,
606 amount: T::Balance,
607 },
608 Burned { asset_id: T::AssetId, owner: T::AccountId, balance: T::Balance },
610 TeamChanged {
612 asset_id: T::AssetId,
613 issuer: T::AccountId,
614 admin: T::AccountId,
615 freezer: T::AccountId,
616 },
617 OwnerChanged { asset_id: T::AssetId, owner: T::AccountId },
619 Frozen { asset_id: T::AssetId, who: T::AccountId },
621 Thawed { asset_id: T::AssetId, who: T::AccountId },
623 AssetFrozen { asset_id: T::AssetId },
625 AssetThawed { asset_id: T::AssetId },
627 AccountsDestroyed { asset_id: T::AssetId, accounts_destroyed: u32, accounts_remaining: u32 },
629 ApprovalsDestroyed {
631 asset_id: T::AssetId,
632 approvals_destroyed: u32,
633 approvals_remaining: u32,
634 },
635 DestructionStarted { asset_id: T::AssetId },
637 Destroyed { asset_id: T::AssetId },
639 ForceCreated { asset_id: T::AssetId, owner: T::AccountId },
641 MetadataSet {
643 asset_id: T::AssetId,
644 name: Vec<u8>,
645 symbol: Vec<u8>,
646 decimals: u8,
647 is_frozen: bool,
648 },
649 MetadataCleared { asset_id: T::AssetId },
651 ApprovedTransfer {
653 asset_id: T::AssetId,
654 source: T::AccountId,
655 delegate: T::AccountId,
656 amount: T::Balance,
657 },
658 ApprovalCancelled { asset_id: T::AssetId, owner: T::AccountId, delegate: T::AccountId },
660 TransferredApproved {
663 asset_id: T::AssetId,
664 owner: T::AccountId,
665 delegate: T::AccountId,
666 destination: T::AccountId,
667 amount: T::Balance,
668 },
669 AssetStatusChanged { asset_id: T::AssetId },
671 AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance },
673 Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId },
675 Blocked { asset_id: T::AssetId, who: T::AccountId },
677 Deposited { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
679 Withdrawn { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
681 ReservesUpdated { asset_id: T::AssetId, reserves: Vec<T::ReserveData> },
683 ReservesRemoved { asset_id: T::AssetId },
685 }
686
687 #[pallet::error]
688 pub enum Error<T, I = ()> {
689 BalanceLow,
691 NoAccount,
693 NoPermission,
695 Unknown,
697 Frozen,
699 InUse,
701 BadWitness,
703 MinBalanceZero,
705 UnavailableConsumer,
709 BadMetadata,
711 Unapproved,
713 WouldDie,
715 AlreadyExists,
717 NoDeposit,
719 WouldBurn,
721 LiveAsset,
724 AssetNotLive,
726 IncorrectStatus,
728 NotFrozen,
730 CallbackFailed,
732 BadAssetId,
734 ContainsFreezes,
736 ContainsHolds,
738 TooManyReserves,
740 }
741
742 #[pallet::hooks]
743 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
744 #[cfg(feature = "try-runtime")]
745 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
746 Self::do_try_state()
747 }
748 }
749
750 #[pallet::call(weight(<T as Config<I>>::WeightInfo))]
751 impl<T: Config<I>, I: 'static> Pallet<T, I> {
752 #[pallet::call_index(0)]
772 pub fn create(
773 origin: OriginFor<T>,
774 id: T::AssetIdParameter,
775 admin: AccountIdLookupOf<T>,
776 min_balance: T::Balance,
777 ) -> DispatchResult {
778 let id: T::AssetId = id.into();
779 let owner = T::CreateOrigin::ensure_origin(origin, &id)?;
780 let admin = T::Lookup::lookup(admin)?;
781
782 ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
783 ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
784
785 if let Some(next_id) = NextAssetId::<T, I>::get() {
786 ensure!(id == next_id, Error::<T, I>::BadAssetId);
787 }
788
789 let deposit = T::AssetDeposit::get();
790 T::Currency::reserve(&owner, deposit)?;
791
792 Asset::<T, I>::insert(
793 id.clone(),
794 AssetDetails {
795 owner: owner.clone(),
796 issuer: admin.clone(),
797 admin: admin.clone(),
798 freezer: admin.clone(),
799 supply: Zero::zero(),
800 deposit,
801 min_balance,
802 is_sufficient: false,
803 accounts: 0,
804 sufficients: 0,
805 approvals: 0,
806 status: AssetStatus::Live,
807 },
808 );
809 ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
810 Self::deposit_event(Event::Created {
811 asset_id: id,
812 creator: owner.clone(),
813 owner: admin,
814 });
815
816 Ok(())
817 }
818
819 #[pallet::call_index(1)]
839 pub fn force_create(
840 origin: OriginFor<T>,
841 id: T::AssetIdParameter,
842 owner: AccountIdLookupOf<T>,
843 is_sufficient: bool,
844 #[pallet::compact] min_balance: T::Balance,
845 ) -> DispatchResult {
846 T::ForceOrigin::ensure_origin(origin)?;
847 let owner = T::Lookup::lookup(owner)?;
848 let id: T::AssetId = id.into();
849 Self::do_force_create(id, owner, is_sufficient, min_balance)
850 }
851
852 #[pallet::call_index(2)]
865 pub fn start_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
866 let maybe_check_owner = match T::ForceOrigin::try_origin(origin) {
867 Ok(_) => None,
868 Err(origin) => Some(ensure_signed(origin)?),
869 };
870 let id: T::AssetId = id.into();
871 Self::do_start_destroy(id, maybe_check_owner)
872 }
873
874 #[pallet::call_index(3)]
887 #[pallet::weight(T::WeightInfo::destroy_accounts(T::RemoveItemsLimit::get()))]
888 pub fn destroy_accounts(
889 origin: OriginFor<T>,
890 id: T::AssetIdParameter,
891 ) -> DispatchResultWithPostInfo {
892 ensure_signed(origin)?;
893 let id: T::AssetId = id.into();
894 let removed_accounts = Self::do_destroy_accounts(id, T::RemoveItemsLimit::get())?;
895 Ok(Some(T::WeightInfo::destroy_accounts(removed_accounts)).into())
896 }
897
898 #[pallet::call_index(4)]
911 #[pallet::weight(T::WeightInfo::destroy_approvals(T::RemoveItemsLimit::get()))]
912 pub fn destroy_approvals(
913 origin: OriginFor<T>,
914 id: T::AssetIdParameter,
915 ) -> DispatchResultWithPostInfo {
916 ensure_signed(origin)?;
917 let id: T::AssetId = id.into();
918 let removed_approvals = Self::do_destroy_approvals(id, T::RemoveItemsLimit::get())?;
919 Ok(Some(T::WeightInfo::destroy_approvals(removed_approvals)).into())
920 }
921
922 #[pallet::call_index(5)]
933 pub fn finish_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
934 ensure_signed(origin)?;
935 let id: T::AssetId = id.into();
936 Self::do_finish_destroy(id)
937 }
938
939 #[pallet::call_index(6)]
952 pub fn mint(
953 origin: OriginFor<T>,
954 id: T::AssetIdParameter,
955 beneficiary: AccountIdLookupOf<T>,
956 #[pallet::compact] amount: T::Balance,
957 ) -> DispatchResult {
958 let origin = ensure_signed(origin)?;
959 let beneficiary = T::Lookup::lookup(beneficiary)?;
960 let id: T::AssetId = id.into();
961 Self::do_mint(id, &beneficiary, amount, Some(origin))?;
962 Ok(())
963 }
964
965 #[pallet::call_index(7)]
981 pub fn burn(
982 origin: OriginFor<T>,
983 id: T::AssetIdParameter,
984 who: AccountIdLookupOf<T>,
985 #[pallet::compact] amount: T::Balance,
986 ) -> DispatchResult {
987 let origin = ensure_signed(origin)?;
988 let who = T::Lookup::lookup(who)?;
989 let id: T::AssetId = id.into();
990
991 let f = DebitFlags { keep_alive: false, best_effort: true };
992 Self::do_burn(id, &who, amount, Some(origin), f)?;
993 Ok(())
994 }
995
996 #[pallet::call_index(8)]
1015 pub fn transfer(
1016 origin: OriginFor<T>,
1017 id: T::AssetIdParameter,
1018 target: AccountIdLookupOf<T>,
1019 #[pallet::compact] amount: T::Balance,
1020 ) -> DispatchResult {
1021 let origin = ensure_signed(origin)?;
1022 let dest = T::Lookup::lookup(target)?;
1023 let id: T::AssetId = id.into();
1024
1025 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1026 Self::do_transfer(id, &origin, &dest, amount, None, f).map(|_| ())
1027 }
1028
1029 #[pallet::call_index(9)]
1048 pub fn transfer_keep_alive(
1049 origin: OriginFor<T>,
1050 id: T::AssetIdParameter,
1051 target: AccountIdLookupOf<T>,
1052 #[pallet::compact] amount: T::Balance,
1053 ) -> DispatchResult {
1054 let source = ensure_signed(origin)?;
1055 let dest = T::Lookup::lookup(target)?;
1056 let id: T::AssetId = id.into();
1057
1058 let f = TransferFlags { keep_alive: true, best_effort: false, burn_dust: false };
1059 Self::do_transfer(id, &source, &dest, amount, None, f).map(|_| ())
1060 }
1061
1062 #[pallet::call_index(10)]
1082 pub fn force_transfer(
1083 origin: OriginFor<T>,
1084 id: T::AssetIdParameter,
1085 source: AccountIdLookupOf<T>,
1086 dest: AccountIdLookupOf<T>,
1087 #[pallet::compact] amount: T::Balance,
1088 ) -> DispatchResult {
1089 let origin = ensure_signed(origin)?;
1090 let source = T::Lookup::lookup(source)?;
1091 let dest = T::Lookup::lookup(dest)?;
1092 let id: T::AssetId = id.into();
1093
1094 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1095 Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ())
1096 }
1097
1098 #[pallet::call_index(11)]
1111 pub fn freeze(
1112 origin: OriginFor<T>,
1113 id: T::AssetIdParameter,
1114 who: AccountIdLookupOf<T>,
1115 ) -> DispatchResult {
1116 let origin = ensure_signed(origin)?;
1117 let id: T::AssetId = id.into();
1118
1119 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1120 ensure!(
1121 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1122 Error::<T, I>::IncorrectStatus
1123 );
1124 ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1125 let who = T::Lookup::lookup(who)?;
1126
1127 Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1128 maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1129 AccountStatus::Frozen;
1130 Ok(())
1131 })?;
1132
1133 Self::deposit_event(Event::<T, I>::Frozen { asset_id: id, who });
1134 Ok(())
1135 }
1136
1137 #[pallet::call_index(12)]
1148 pub fn thaw(
1149 origin: OriginFor<T>,
1150 id: T::AssetIdParameter,
1151 who: AccountIdLookupOf<T>,
1152 ) -> DispatchResult {
1153 let origin = ensure_signed(origin)?;
1154 let id: T::AssetId = id.into();
1155
1156 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1157 ensure!(
1158 details.status == AssetStatus::Live || details.status == AssetStatus::Frozen,
1159 Error::<T, I>::IncorrectStatus
1160 );
1161 ensure!(origin == details.admin, Error::<T, I>::NoPermission);
1162 let who = T::Lookup::lookup(who)?;
1163
1164 Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1165 maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1166 AccountStatus::Liquid;
1167 Ok(())
1168 })?;
1169
1170 Self::deposit_event(Event::<T, I>::Thawed { asset_id: id, who });
1171 Ok(())
1172 }
1173
1174 #[pallet::call_index(13)]
1184 pub fn freeze_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1185 let origin = ensure_signed(origin)?;
1186 let id: T::AssetId = id.into();
1187
1188 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1189 let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1190 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1191 ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1192
1193 d.status = AssetStatus::Frozen;
1194
1195 Self::deposit_event(Event::<T, I>::AssetFrozen { asset_id: id });
1196 Ok(())
1197 })
1198 }
1199
1200 #[pallet::call_index(14)]
1210 pub fn thaw_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1211 let origin = ensure_signed(origin)?;
1212 let id: T::AssetId = id.into();
1213
1214 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1215 let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1216 ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1217 ensure!(d.status == AssetStatus::Frozen, Error::<T, I>::NotFrozen);
1218
1219 d.status = AssetStatus::Live;
1220
1221 Self::deposit_event(Event::<T, I>::AssetThawed { asset_id: id });
1222 Ok(())
1223 })
1224 }
1225
1226 #[pallet::call_index(15)]
1237 pub fn transfer_ownership(
1238 origin: OriginFor<T>,
1239 id: T::AssetIdParameter,
1240 owner: AccountIdLookupOf<T>,
1241 ) -> DispatchResult {
1242 let origin = ensure_signed(origin)?;
1243 let owner = T::Lookup::lookup(owner)?;
1244 let id: T::AssetId = id.into();
1245
1246 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1247 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1248 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1249 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1250 if details.owner == owner {
1251 return Ok(());
1252 }
1253
1254 let metadata_deposit = Metadata::<T, I>::get(&id).deposit;
1255 let deposit = details.deposit + metadata_deposit;
1256
1257 T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?;
1259
1260 details.owner = owner.clone();
1261
1262 Self::deposit_event(Event::OwnerChanged { asset_id: id, owner });
1263 Ok(())
1264 })
1265 }
1266
1267 #[pallet::call_index(16)]
1280 pub fn set_team(
1281 origin: OriginFor<T>,
1282 id: T::AssetIdParameter,
1283 issuer: AccountIdLookupOf<T>,
1284 admin: AccountIdLookupOf<T>,
1285 freezer: AccountIdLookupOf<T>,
1286 ) -> DispatchResult {
1287 let origin = ensure_signed(origin)?;
1288 let issuer = T::Lookup::lookup(issuer)?;
1289 let admin = T::Lookup::lookup(admin)?;
1290 let freezer = T::Lookup::lookup(freezer)?;
1291 let id: T::AssetId = id.into();
1292
1293 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1294 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1295 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1296 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1297
1298 details.issuer = issuer.clone();
1299 details.admin = admin.clone();
1300 details.freezer = freezer.clone();
1301
1302 Self::deposit_event(Event::TeamChanged { asset_id: id, issuer, admin, freezer });
1303 Ok(())
1304 })
1305 }
1306
1307 #[pallet::call_index(17)]
1324 #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))]
1325 pub fn set_metadata(
1326 origin: OriginFor<T>,
1327 id: T::AssetIdParameter,
1328 name: Vec<u8>,
1329 symbol: Vec<u8>,
1330 decimals: u8,
1331 ) -> DispatchResult {
1332 let origin = ensure_signed(origin)?;
1333 let id: T::AssetId = id.into();
1334 Self::do_set_metadata(id, &origin, name, symbol, decimals)
1335 }
1336
1337 #[pallet::call_index(18)]
1349 pub fn clear_metadata(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1350 let origin = ensure_signed(origin)?;
1351 let id: T::AssetId = id.into();
1352
1353 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1354 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1355 ensure!(origin == d.owner, Error::<T, I>::NoPermission);
1356
1357 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1358 let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1359 T::Currency::unreserve(&d.owner, deposit);
1360 Self::deposit_event(Event::MetadataCleared { asset_id: id });
1361 Ok(())
1362 })
1363 }
1364
1365 #[pallet::call_index(19)]
1380 #[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))]
1381 pub fn force_set_metadata(
1382 origin: OriginFor<T>,
1383 id: T::AssetIdParameter,
1384 name: Vec<u8>,
1385 symbol: Vec<u8>,
1386 decimals: u8,
1387 is_frozen: bool,
1388 ) -> DispatchResult {
1389 T::ForceOrigin::ensure_origin(origin)?;
1390 let id: T::AssetId = id.into();
1391
1392 let bounded_name: BoundedVec<u8, T::StringLimit> =
1393 name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1394
1395 let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1396 symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1397
1398 ensure!(Asset::<T, I>::contains_key(&id), Error::<T, I>::Unknown);
1399 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1400 let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1401 *metadata = Some(AssetMetadata {
1402 deposit,
1403 name: bounded_name,
1404 symbol: bounded_symbol,
1405 decimals,
1406 is_frozen,
1407 });
1408
1409 Self::deposit_event(Event::MetadataSet {
1410 asset_id: id,
1411 name,
1412 symbol,
1413 decimals,
1414 is_frozen,
1415 });
1416 Ok(())
1417 })
1418 }
1419
1420 #[pallet::call_index(20)]
1432 pub fn force_clear_metadata(
1433 origin: OriginFor<T>,
1434 id: T::AssetIdParameter,
1435 ) -> DispatchResult {
1436 T::ForceOrigin::ensure_origin(origin)?;
1437 let id: T::AssetId = id.into();
1438
1439 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1440 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1441 let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1442 T::Currency::unreserve(&d.owner, deposit);
1443 Self::deposit_event(Event::MetadataCleared { asset_id: id });
1444 Ok(())
1445 })
1446 }
1447
1448 #[pallet::call_index(21)]
1471 pub fn force_asset_status(
1472 origin: OriginFor<T>,
1473 id: T::AssetIdParameter,
1474 owner: AccountIdLookupOf<T>,
1475 issuer: AccountIdLookupOf<T>,
1476 admin: AccountIdLookupOf<T>,
1477 freezer: AccountIdLookupOf<T>,
1478 #[pallet::compact] min_balance: T::Balance,
1479 is_sufficient: bool,
1480 is_frozen: bool,
1481 ) -> DispatchResult {
1482 T::ForceOrigin::ensure_origin(origin)?;
1483 let id: T::AssetId = id.into();
1484
1485 Asset::<T, I>::try_mutate(id.clone(), |maybe_asset| {
1486 let mut asset = maybe_asset.take().ok_or(Error::<T, I>::Unknown)?;
1487 ensure!(asset.status != AssetStatus::Destroying, Error::<T, I>::AssetNotLive);
1488 asset.owner = T::Lookup::lookup(owner)?;
1489 asset.issuer = T::Lookup::lookup(issuer)?;
1490 asset.admin = T::Lookup::lookup(admin)?;
1491 asset.freezer = T::Lookup::lookup(freezer)?;
1492 asset.min_balance = min_balance;
1493 asset.is_sufficient = is_sufficient;
1494 if is_frozen {
1495 asset.status = AssetStatus::Frozen;
1496 } else {
1497 asset.status = AssetStatus::Live;
1498 }
1499 *maybe_asset = Some(asset);
1500
1501 Self::deposit_event(Event::AssetStatusChanged { asset_id: id });
1502 Ok(())
1503 })
1504 }
1505
1506 #[pallet::call_index(22)]
1527 pub fn approve_transfer(
1528 origin: OriginFor<T>,
1529 id: T::AssetIdParameter,
1530 delegate: AccountIdLookupOf<T>,
1531 #[pallet::compact] amount: T::Balance,
1532 ) -> DispatchResult {
1533 let owner = ensure_signed(origin)?;
1534 let delegate = T::Lookup::lookup(delegate)?;
1535 let id: T::AssetId = id.into();
1536 Self::do_approve_transfer(id, &owner, &delegate, amount)
1537 }
1538
1539 #[pallet::call_index(23)]
1553 pub fn cancel_approval(
1554 origin: OriginFor<T>,
1555 id: T::AssetIdParameter,
1556 delegate: AccountIdLookupOf<T>,
1557 ) -> DispatchResult {
1558 let owner = ensure_signed(origin)?;
1559 let delegate = T::Lookup::lookup(delegate)?;
1560 let id: T::AssetId = id.into();
1561 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1562 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1563
1564 let approval = Approvals::<T, I>::take((id.clone(), &owner, &delegate))
1565 .ok_or(Error::<T, I>::Unknown)?;
1566 T::Currency::unreserve(&owner, approval.deposit);
1567
1568 d.approvals.saturating_dec();
1569 Asset::<T, I>::insert(id.clone(), d);
1570
1571 Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate });
1572 Ok(())
1573 }
1574
1575 #[pallet::call_index(24)]
1589 pub fn force_cancel_approval(
1590 origin: OriginFor<T>,
1591 id: T::AssetIdParameter,
1592 owner: AccountIdLookupOf<T>,
1593 delegate: AccountIdLookupOf<T>,
1594 ) -> DispatchResult {
1595 let id: T::AssetId = id.into();
1596 let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1597 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1598 T::ForceOrigin::try_origin(origin)
1599 .map(|_| ())
1600 .or_else(|origin| -> DispatchResult {
1601 let origin = ensure_signed(origin)?;
1602 ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1603 Ok(())
1604 })?;
1605
1606 let owner = T::Lookup::lookup(owner)?;
1607 let delegate = T::Lookup::lookup(delegate)?;
1608
1609 let approval = Approvals::<T, I>::take((id.clone(), &owner, &delegate))
1610 .ok_or(Error::<T, I>::Unknown)?;
1611 T::Currency::unreserve(&owner, approval.deposit);
1612 d.approvals.saturating_dec();
1613 Asset::<T, I>::insert(id.clone(), d);
1614
1615 Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate });
1616 Ok(())
1617 }
1618
1619 #[pallet::call_index(25)]
1638 pub fn transfer_approved(
1639 origin: OriginFor<T>,
1640 id: T::AssetIdParameter,
1641 owner: AccountIdLookupOf<T>,
1642 destination: AccountIdLookupOf<T>,
1643 #[pallet::compact] amount: T::Balance,
1644 ) -> DispatchResult {
1645 let delegate = ensure_signed(origin)?;
1646 let owner = T::Lookup::lookup(owner)?;
1647 let destination = T::Lookup::lookup(destination)?;
1648 let id: T::AssetId = id.into();
1649 Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
1650 }
1651
1652 #[pallet::call_index(26)]
1662 #[pallet::weight(T::WeightInfo::touch())]
1663 pub fn touch(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1664 let who = ensure_signed(origin)?;
1665 let id: T::AssetId = id.into();
1666 Self::do_touch(id, who.clone(), who)
1667 }
1668
1669 #[pallet::call_index(27)]
1683 #[pallet::weight(T::WeightInfo::refund())]
1684 pub fn refund(
1685 origin: OriginFor<T>,
1686 id: T::AssetIdParameter,
1687 allow_burn: bool,
1688 ) -> DispatchResult {
1689 let id: T::AssetId = id.into();
1690 Self::do_refund(id, ensure_signed(origin)?, allow_burn)
1691 }
1692
1693 #[pallet::call_index(28)]
1706 pub fn set_min_balance(
1707 origin: OriginFor<T>,
1708 id: T::AssetIdParameter,
1709 min_balance: T::Balance,
1710 ) -> DispatchResult {
1711 let origin = ensure_signed(origin)?;
1712 let id: T::AssetId = id.into();
1713
1714 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1715 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1716
1717 let old_min_balance = details.min_balance;
1718 ensure!(!details.is_sufficient, Error::<T, I>::NoPermission);
1721
1722 ensure!(
1725 min_balance < old_min_balance || details.accounts == 0,
1726 Error::<T, I>::NoPermission
1727 );
1728
1729 details.min_balance = min_balance;
1730 Asset::<T, I>::insert(&id, details);
1731
1732 Self::deposit_event(Event::AssetMinBalanceChanged {
1733 asset_id: id,
1734 new_min_balance: min_balance,
1735 });
1736 Ok(())
1737 }
1738
1739 #[pallet::call_index(29)]
1751 #[pallet::weight(T::WeightInfo::touch_other())]
1752 pub fn touch_other(
1753 origin: OriginFor<T>,
1754 id: T::AssetIdParameter,
1755 who: AccountIdLookupOf<T>,
1756 ) -> DispatchResult {
1757 let origin = ensure_signed(origin)?;
1758 let who = T::Lookup::lookup(who)?;
1759 let id: T::AssetId = id.into();
1760 Self::do_touch(id, who, origin)
1761 }
1762
1763 #[pallet::call_index(30)]
1777 #[pallet::weight(T::WeightInfo::refund_other())]
1778 pub fn refund_other(
1779 origin: OriginFor<T>,
1780 id: T::AssetIdParameter,
1781 who: AccountIdLookupOf<T>,
1782 ) -> DispatchResult {
1783 let origin = ensure_signed(origin)?;
1784 let who = T::Lookup::lookup(who)?;
1785 let id: T::AssetId = id.into();
1786 Self::do_refund_other(id, &who, Some(origin))
1787 }
1788
1789 #[pallet::call_index(31)]
1800 pub fn block(
1801 origin: OriginFor<T>,
1802 id: T::AssetIdParameter,
1803 who: AccountIdLookupOf<T>,
1804 ) -> DispatchResult {
1805 let origin = ensure_signed(origin)?;
1806 let id: T::AssetId = id.into();
1807
1808 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1809 ensure!(
1810 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1811 Error::<T, I>::IncorrectStatus
1812 );
1813 ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1814 let who = T::Lookup::lookup(who)?;
1815
1816 Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1817 maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1818 AccountStatus::Blocked;
1819 Ok(())
1820 })?;
1821
1822 Self::deposit_event(Event::<T, I>::Blocked { asset_id: id, who });
1823 Ok(())
1824 }
1825
1826 #[pallet::call_index(32)]
1843 #[pallet::weight(T::WeightInfo::transfer_all())]
1844 pub fn transfer_all(
1845 origin: OriginFor<T>,
1846 id: T::AssetIdParameter,
1847 dest: AccountIdLookupOf<T>,
1848 keep_alive: bool,
1849 ) -> DispatchResult {
1850 let transactor = ensure_signed(origin)?;
1851 let keep_alive = if keep_alive { Preserve } else { Expendable };
1852 let reducible_balance = <Self as fungibles::Inspect<_>>::reducible_balance(
1853 id.clone().into(),
1854 &transactor,
1855 keep_alive,
1856 Fortitude::Polite,
1857 );
1858 let dest = T::Lookup::lookup(dest)?;
1859 <Self as fungibles::Mutate<_>>::transfer(
1860 id.into(),
1861 &transactor,
1862 &dest,
1863 reducible_balance,
1864 keep_alive,
1865 )?;
1866 Ok(())
1867 }
1868
1869 #[pallet::call_index(33)]
1879 #[pallet::weight(T::WeightInfo::set_reserves())]
1880 pub fn set_reserves(
1881 origin: OriginFor<T>,
1882 id: T::AssetIdParameter,
1883 reserves: Vec<T::ReserveData>,
1884 ) -> DispatchResult {
1885 let id: T::AssetId = id.into();
1886 let origin = ensure_signed(origin.clone())
1887 .or_else(|_| T::CreateOrigin::ensure_origin(origin, &id))?;
1888
1889 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1890 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1891
1892 Self::unchecked_update_reserves(id, reserves)?;
1893 Ok(())
1894 }
1895 }
1896
1897 #[pallet::view_functions]
1898 impl<T: Config<I>, I: 'static> Pallet<T, I> {
1899 pub fn asset_details(
1901 id: T::AssetId,
1902 ) -> Option<AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>> {
1903 Asset::<T, I>::get(id)
1904 }
1905
1906 pub fn balance_of(who: T::AccountId, id: T::AssetId) -> Option<<T as Config<I>>::Balance> {
1908 Account::<T, I>::get(id, who).map(|account| account.balance)
1909 }
1910
1911 pub fn get_metadata(
1913 id: T::AssetId,
1914 ) -> Option<AssetMetadata<DepositBalanceOf<T, I>, BoundedVec<u8, T::StringLimit>>> {
1915 Metadata::<T, I>::try_get(id).ok()
1916 }
1917
1918 pub fn get_reserves_data(id: T::AssetId) -> Vec<T::ReserveData> {
1920 Self::reserves(&id)
1921 }
1922 }
1923
1924 impl<T: Config<I>, I: 'static> AccountTouch<T::AssetId, T::AccountId> for Pallet<T, I> {
1927 type Balance = DepositBalanceOf<T, I>;
1928
1929 fn deposit_required(_: T::AssetId) -> Self::Balance {
1930 T::AssetAccountDeposit::get()
1931 }
1932
1933 fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
1934 match Asset::<T, I>::get(&asset) {
1935 Some(info) if info.is_sufficient => false,
1937 Some(_) if frame_system::Pallet::<T>::can_accrue_consumers(who, 2) => false,
1938 Some(_) => !Account::<T, I>::contains_key(asset, who),
1939 _ => true,
1940 }
1941 }
1942
1943 fn touch(
1944 asset: T::AssetId,
1945 who: &T::AccountId,
1946 depositor: &T::AccountId,
1947 ) -> DispatchResult {
1948 Self::do_touch(asset, who.clone(), depositor.clone())
1949 }
1950 }
1951
1952 impl<T: Config<I>, I: 'static> ContainsPair<T::AssetId, T::AccountId> for Pallet<T, I> {
1954 fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool {
1956 Account::<T, I>::contains_key(asset, who)
1957 }
1958 }
1959
1960 impl<T: Config<I>, I: 'static> ProvideAssetReserves<T::AssetId, T::ReserveData> for Pallet<T, I> {
1963 fn reserves(id: &T::AssetId) -> Vec<T::ReserveData> {
1965 Reserves::<T, I>::get(id).into_inner()
1966 }
1967 }
1968}
1969
1970#[cfg(any(feature = "try-runtime", test))]
1971impl<T: Config<I>, I: 'static> Pallet<T, I> {
1972 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
1973 for asset_id in Reserves::<T, I>::iter_keys() {
1974 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Reserves data found");
1975 }
1976
1977 for asset_id in Metadata::<T, I>::iter_keys() {
1978 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Metadata found");
1979 }
1980
1981 for (asset_id, _, _) in Approvals::<T, I>::iter_keys() {
1982 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Approval found");
1983 }
1984
1985 for (asset_id, _) in Account::<T, I>::iter_keys() {
1986 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Account found");
1987 }
1988
1989 for (asset_id, details) in Asset::<T, I>::iter() {
1990 if details.status == AssetStatus::Destroying {
1991 continue;
1992 }
1993
1994 let mut calculated_supply = T::Balance::zero();
1995 let mut calculated_accounts = 0u32;
1996 let mut calculated_sufficients = 0u32;
1997
1998 for (who, account) in Account::<T, I>::iter_prefix(&asset_id) {
1999 let held = T::Holder::balance_on_hold(asset_id.clone(), &who).unwrap_or_default();
2000 calculated_supply =
2001 calculated_supply.saturating_add(account.balance).saturating_add(held);
2002 calculated_accounts += 1;
2003
2004 if matches!(account.reason, ExistenceReason::Sufficient) {
2005 calculated_sufficients += 1;
2006 }
2007
2008 if account.balance < details.min_balance {
2009 ensure!(
2010 matches!(
2011 account.reason,
2012 ExistenceReason::DepositHeld(_) | ExistenceReason::DepositFrom(_, _)
2013 ),
2014 "Account below min_balance must have a deposit"
2015 );
2016 }
2017 }
2018
2019 ensure!(details.supply >= calculated_supply, "Asset supply mismatch");
2024 ensure!(details.accounts == calculated_accounts, "Asset account count mismatch");
2025 ensure!(
2026 details.sufficients == calculated_sufficients,
2027 "Asset sufficients count mismatch"
2028 );
2029
2030 let calculated_approvals = Approvals::<T, I>::iter_prefix((&asset_id,)).count() as u32;
2031
2032 if details.approvals != calculated_approvals {
2033 log::error!(
2034 "Asset {asset_id:?} approvals count mismatch: calculated {calculated_approvals} vs expected {}",
2035 details.approvals,
2036 );
2037
2038 return Err("Asset approvals count mismatch".into())
2039 }
2040 }
2041 Ok(())
2042 }
2043}
2044
2045sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);