referrerpolicy=no-referrer-when-downgrade

pallet_assets/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Assets Pallet
19//!
20//! A simple, secure module for dealing with sets of assets implementing
21//! [`fungible`](frame_support::traits::fungible) traits, via [`fungibles`] traits.
22//!
23//! The pallet makes heavy use of concepts such as Holds and Freezes from the
24//! [`frame_support::traits::fungible`] traits, therefore you should read and understand those docs
25//! as a prerequisite to understanding this pallet.
26//!
27//! See the [`frame_tokens`] reference docs for more information about the place of the
28//! Assets pallet in FRAME.
29//!
30//! ## Overview
31//!
32//! The Assets module provides functionality for asset management of fungible asset classes
33//! with a fixed supply, including:
34//!
35//! * Asset Issuance (Minting)
36//! * Asset Transferal
37//! * Asset Freezing
38//! * Asset Destruction (Burning)
39//! * Delegated Asset Transfers ("Approval API")
40//!
41//! To use it in your runtime, you need to implement the assets [`Config`].
42//!
43//! The supported dispatchable functions are documented in the [`Call`] enum.
44//!
45//! ### Terminology
46//!
47//! * **Admin**: An account ID uniquely privileged to be able to unfreeze (thaw) an account and its
48//!   assets, as well as forcibly transfer a particular class of assets between arbitrary accounts
49//!   and reduce the balance of a particular class of assets of arbitrary accounts.
50//! * **Asset issuance/minting**: The creation of a new asset, whose total supply will belong to the
51//!   account designated as the beneficiary of the asset. This is a privileged operation.
52//! * **Asset transfer**: The reduction of the balance of an asset of one account with the
53//!   corresponding increase in the balance of another.
54//! * **Asset destruction**: The process of reducing the balance of an asset of one account. This is
55//!   a privileged operation.
56//! * **Fungible asset**: An asset whose units are interchangeable.
57//! * **Issuer**: An account ID uniquely privileged to be able to mint a particular class of assets.
58//! * **Freezer**: An account ID uniquely privileged to be able to freeze an account from
59//!   transferring a particular class of assets.
60//! * **Freezing**: Removing the possibility of an unpermissioned transfer of an asset from a
61//!   particular account.
62//! * **Non-fungible asset**: An asset for which each unit has unique characteristics.
63//! * **Owner**: An account ID uniquely privileged to be able to destroy a particular asset class,
64//!   or to set the Issuer, Freezer, Reserves, or Admin of that asset class.
65//! * **Approval**: The act of allowing an account the permission to transfer some balance of asset
66//!   from the approving account into some third-party destination account.
67//! * **Sufficiency**: The idea of a minimum-balance of an asset being sufficient to allow the
68//!   account's existence on the system without requiring any other existential-deposit.
69//!
70//! ### Goals
71//!
72//! The assets system in Substrate is designed to make the following possible:
73//!
74//! * Issue new assets in a permissioned or permissionless way, if permissionless, then with a
75//!   deposit required.
76//! * Allow accounts to be delegated the ability to transfer assets without otherwise existing
77//!   on-chain (*approvals*).
78//! * Move assets between accounts.
79//! * Update an asset class's total supply.
80//! * Allow administrative activities by specially privileged accounts including freezing account
81//!   balances and minting/burning assets.
82//!
83//! ## Interface
84//!
85//! ### Permissionless Functions
86//!
87//! * `create`: Creates a new asset class, taking the required deposit.
88//! * `transfer`: Transfer sender's assets to another account.
89//! * `transfer_keep_alive`: Transfer sender's assets to another account, keeping the sender alive.
90//! * `approve_transfer`: Create or increase an delegated transfer.
91//! * `cancel_approval`: Rescind a previous approval.
92//! * `transfer_approved`: Transfer third-party's assets to another account.
93//! * `touch`: Create an asset account for non-provider assets. Caller must place a deposit.
94//! * `refund`: Return the deposit (if any) of the caller's asset account or a consumer reference
95//!   (if any) of the caller's account.
96//! * `refund_other`: Return the deposit (if any) of a specified asset account.
97//! * `touch_other`: Create an asset account for specified account. Caller must place a deposit.
98//!
99//! ### Permissioned Functions
100//!
101//! * `force_create`: Creates a new asset class without taking any deposit.
102//! * `force_set_metadata`: Set the metadata of an asset class.
103//! * `force_clear_metadata`: Remove the metadata of an asset class.
104//! * `force_asset_status`: Alter an asset class's attributes.
105//! * `force_cancel_approval`: Rescind a previous approval.
106//!
107//! ### Privileged Functions
108//!
109//! * `destroy`: Destroys an entire asset class; called by the asset class's Owner.
110//! * `mint`: Increases the asset balance of an account; called by the asset class's Issuer.
111//! * `burn`: Decreases the asset balance of an account; called by the asset class's Admin.
112//! * `force_transfer`: Transfers between arbitrary accounts; called by the asset class's Admin.
113//! * `freeze`: Disallows further `transfer`s from an account; called by the asset class's Freezer.
114//! * `thaw`: Allows further `transfer`s to and from an account; called by the asset class's Admin.
115//! * `transfer_ownership`: Changes an asset class's Owner; called by the asset class's Owner.
116//! * `set_team`: Changes an asset class's Admin, Freezer and Issuer; called by the asset class's
117//!   Owner.
118//! * `set_metadata`: Set the metadata of an asset class; called by the asset class's Owner.
119//! * `clear_metadata`: Remove the metadata of an asset class; called by the asset class's Owner.
120//! * `set_reserves`: Set the reserve information of an asset class; called by the asset class's
121//!   Owner.
122//! * `block`: Disallows further `transfer`s to and from an account; called by the asset class's
123//!   Freezer.
124//!
125//! Please refer to the [`Call`] enum and its associated variants for documentation on each
126//! function.
127//!
128//! ### Public Functions
129//! <!-- Original author of descriptions: @gavofyork -->
130//!
131//! * `balance` - Get the asset `id` balance of `who`.
132//! * `total_supply` - Get the total supply of an asset `id`.
133//!
134//! Please refer to the [`Pallet`] struct for details on publicly available functions.
135//!
136//! ### Callbacks
137//!
138//! Using `CallbackHandle` associated type, user can configure custom callback functions which are
139//! executed when new asset is created or an existing asset is destroyed.
140//!
141//! ## Related Modules
142//!
143//! * [`System`](../frame_system/index.html)
144//! * [`Support`](../frame_support/index.html)
145//!
146//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html
147
148// This recursion limit is needed because we have too many benchmarks and benchmarking will fail if
149// we add more without this limit.
150#![recursion_limit = "1024"]
151// Ensure we're `no_std` when compiling for Wasm.
152#![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
205/// Trait with callbacks that are executed after successful asset creation or destruction.
206pub trait AssetsCallback<AssetId, AccountId> {
207	/// Indicates that asset with `id` was successfully created by the `owner`
208	fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> {
209		Ok(())
210	}
211
212	/// Indicates that asset with `id` has just been destroyed
213	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
231/// Auto-increment the [`NextAssetId`] when an asset is created.
232///
233/// This has not effect if the [`NextAssetId`] value is not present.
234pub 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			// Auto increment for the asset id is not enabled.
242			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	/// The in-code storage version.
261	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
262
263	/// The maximum number of configurable reserve locations for one asset class.
264	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	/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
286	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	/// The module configuration trait.
321	pub trait Config<I: 'static = ()>: frame_system::Config {
322		/// The overarching event type.
323		#[pallet::no_default_bounds]
324		#[allow(deprecated)]
325		type RuntimeEvent: From<Event<Self, I>>
326			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
327
328		/// The units in which we record balances.
329		type Balance: Member
330			+ Parameter
331			+ HasCompact<Type: DecodeWithMemTracking>
332			+ AtLeast32BitUnsigned
333			+ Default
334			+ Copy
335			+ MaybeSerializeDeserialize
336			+ MaxEncodedLen
337			+ TypeInfo;
338
339		/// Max number of items to destroy per `destroy_accounts` and `destroy_approvals` call.
340		///
341		/// Must be configured to result in a weight that makes each call fit in a block.
342		#[pallet::constant]
343		type RemoveItemsLimit: Get<u32>;
344
345		/// Identifier for the class of asset.
346		type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
347
348		/// Wrapper around `Self::AssetId` to use in dispatchable call signatures. Allows the use
349		/// of compact encoding in instances of the pallet, which will prevent breaking changes
350		/// resulting from the removal of `HasCompact` from `Self::AssetId`.
351		///
352		/// This type includes the `From<Self::AssetId>` bound, since tightly coupled pallets may
353		/// want to convert an `AssetId` into a parameter for calling dispatchable functions
354		/// directly.
355		type AssetIdParameter: Parameter + From<Self::AssetId> + Into<Self::AssetId> + MaxEncodedLen;
356
357		/// Information about reserve locations for a class of asset.
358		type ReserveData: Debug + Parameter + MaybeSerializeDeserialize + MaxEncodedLen;
359
360		/// The currency mechanism.
361		#[pallet::no_default]
362		type Currency: ReservableCurrency<Self::AccountId>;
363
364		/// Standard asset class creation is only allowed if the origin attempting it and the
365		/// asset class are in this set.
366		#[pallet::no_default]
367		type CreateOrigin: EnsureOriginWithArg<
368			Self::RuntimeOrigin,
369			Self::AssetId,
370			Success = Self::AccountId,
371		>;
372
373		/// The origin which may forcibly create or destroy an asset or otherwise alter privileged
374		/// attributes.
375		#[pallet::no_default]
376		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
377
378		/// The basic amount of funds that must be reserved for an asset.
379		#[pallet::constant]
380		#[pallet::no_default_bounds]
381		type AssetDeposit: Get<DepositBalanceOf<Self, I>>;
382
383		/// The amount of funds that must be reserved for a non-provider asset account to be
384		/// maintained.
385		#[pallet::constant]
386		#[pallet::no_default_bounds]
387		type AssetAccountDeposit: Get<DepositBalanceOf<Self, I>>;
388
389		/// The basic amount of funds that must be reserved when adding metadata to your asset.
390		#[pallet::constant]
391		#[pallet::no_default_bounds]
392		type MetadataDepositBase: Get<DepositBalanceOf<Self, I>>;
393
394		/// The additional funds that must be reserved for the number of bytes you store in your
395		/// metadata.
396		#[pallet::constant]
397		#[pallet::no_default_bounds]
398		type MetadataDepositPerByte: Get<DepositBalanceOf<Self, I>>;
399
400		/// The amount of funds that must be reserved when creating a new approval.
401		#[pallet::constant]
402		#[pallet::no_default_bounds]
403		type ApprovalDeposit: Get<DepositBalanceOf<Self, I>>;
404
405		/// The maximum length of a name or symbol stored on-chain.
406		#[pallet::constant]
407		type StringLimit: Get<u32>;
408
409		/// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be
410		/// respected in all permissionless operations.
411		type Freezer: FrozenBalance<Self::AssetId, Self::AccountId, Self::Balance>;
412
413		/// A hook to inspect a per-asset, per-account balance that is held. This goes in
414		/// accordance with balance model.
415		type Holder: BalanceOnHold<Self::AssetId, Self::AccountId, Self::Balance>;
416
417		/// Additional data to be stored with an account's asset balance.
418		type Extra: Member + Parameter + Default + MaxEncodedLen;
419
420		/// Callback methods for asset state change (e.g. asset created or destroyed)
421		///
422		/// Types implementing the [`AssetsCallback`] can be chained when listed together as a
423		/// tuple.
424		/// The [`AutoIncAssetId`] callback, in conjunction with the [`NextAssetId`], can be
425		/// used to set up auto-incrementing asset IDs for this collection.
426		type CallbackHandle: AssetsCallback<Self::AssetId, Self::AccountId>;
427
428		/// Weight information for extrinsics in this pallet.
429		type WeightInfo: WeightInfo;
430
431		/// Helper trait for benchmarks.
432		#[cfg(feature = "runtime-benchmarks")]
433		type BenchmarkHelper: BenchmarkHelper<Self::AssetIdParameter, Self::ReserveData>;
434	}
435
436	#[pallet::storage]
437	/// Details of an asset.
438	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	/// The holdings of a specific account for a specific asset.
447	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	/// Approved balance transfers. First balance is the amount approved for transfer. Second
458	/// is the amount of `T::Currency` reserved for storing this.
459	/// First key is the asset ID, second key is the owner and third key is the delegate.
460	pub type Approvals<T: Config<I>, I: 'static = ()> = StorageNMap<
461		_,
462		(
463			NMapKey<Blake2_128Concat, T::AssetId>,
464			NMapKey<Blake2_128Concat, T::AccountId>, // owner
465			NMapKey<Blake2_128Concat, T::AccountId>, // delegate
466		),
467		Approval<T::Balance, DepositBalanceOf<T, I>>,
468	>;
469
470	#[pallet::storage]
471	/// Metadata of an asset.
472	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	/// Maps an asset to a list of its configured reserve information.
481	#[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	/// The asset ID enforced for the next asset creation, if any present. Otherwise, this storage
491	/// item has no effect.
492	///
493	/// This can be useful for setting up constraints for IDs of the new assets. For example, by
494	/// providing an initial [`NextAssetId`] and using the [`crate::AutoIncAssetId`] callback, an
495	/// auto-increment model can be applied to all new asset IDs.
496	///
497	/// The initial next asset ID can be set using the [`GenesisConfig`] or the
498	/// [SetNextAssetId](`migration::next_asset_id::SetNextAssetId`) migration.
499	#[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		/// Genesis assets: id, owner, is_sufficient, min_balance
506		pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>,
507		/// Genesis metadata: id, name, symbol, decimals
508		pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>,
509		/// Genesis accounts: id, account_id, balance
510		pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>,
511		/// Genesis [`NextAssetId`].
512		///
513		/// Refer to the [`NextAssetId`] item for more information.
514		///
515		/// This does not enforce the asset ID for the [assets](`GenesisConfig::assets`) within the
516		/// genesis config. It sets the [`NextAssetId`] after they have been created.
517		pub next_asset_id: Option<T::AssetId>,
518		/// Genesis assets and their reserves
519		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		/// Some asset class was created.
598		Created { asset_id: T::AssetId, creator: T::AccountId, owner: T::AccountId },
599		/// Some assets were issued.
600		Issued { asset_id: T::AssetId, owner: T::AccountId, amount: T::Balance },
601		/// Some assets were transferred.
602		Transferred {
603			asset_id: T::AssetId,
604			from: T::AccountId,
605			to: T::AccountId,
606			amount: T::Balance,
607		},
608		/// Some assets were destroyed.
609		Burned { asset_id: T::AssetId, owner: T::AccountId, balance: T::Balance },
610		/// The management team changed.
611		TeamChanged {
612			asset_id: T::AssetId,
613			issuer: T::AccountId,
614			admin: T::AccountId,
615			freezer: T::AccountId,
616		},
617		/// The owner changed.
618		OwnerChanged { asset_id: T::AssetId, owner: T::AccountId },
619		/// Some account `who` was frozen.
620		Frozen { asset_id: T::AssetId, who: T::AccountId },
621		/// Some account `who` was thawed.
622		Thawed { asset_id: T::AssetId, who: T::AccountId },
623		/// Some asset `asset_id` was frozen.
624		AssetFrozen { asset_id: T::AssetId },
625		/// Some asset `asset_id` was thawed.
626		AssetThawed { asset_id: T::AssetId },
627		/// Accounts were destroyed for given asset.
628		AccountsDestroyed { asset_id: T::AssetId, accounts_destroyed: u32, accounts_remaining: u32 },
629		/// Approvals were destroyed for given asset.
630		ApprovalsDestroyed {
631			asset_id: T::AssetId,
632			approvals_destroyed: u32,
633			approvals_remaining: u32,
634		},
635		/// An asset class is in the process of being destroyed.
636		DestructionStarted { asset_id: T::AssetId },
637		/// An asset class was destroyed.
638		Destroyed { asset_id: T::AssetId },
639		/// Some asset class was force-created.
640		ForceCreated { asset_id: T::AssetId, owner: T::AccountId },
641		/// New metadata has been set for an asset.
642		MetadataSet {
643			asset_id: T::AssetId,
644			name: Vec<u8>,
645			symbol: Vec<u8>,
646			decimals: u8,
647			is_frozen: bool,
648		},
649		/// Metadata has been cleared for an asset.
650		MetadataCleared { asset_id: T::AssetId },
651		/// (Additional) funds have been approved for transfer to a destination account.
652		ApprovedTransfer {
653			asset_id: T::AssetId,
654			source: T::AccountId,
655			delegate: T::AccountId,
656			amount: T::Balance,
657		},
658		/// An approval for account `delegate` was cancelled by `owner`.
659		ApprovalCancelled { asset_id: T::AssetId, owner: T::AccountId, delegate: T::AccountId },
660		/// An `amount` was transferred in its entirety from `owner` to `destination` by
661		/// the approved `delegate`.
662		TransferredApproved {
663			asset_id: T::AssetId,
664			owner: T::AccountId,
665			delegate: T::AccountId,
666			destination: T::AccountId,
667			amount: T::Balance,
668		},
669		/// An asset has had its attributes changed by the `Force` origin.
670		AssetStatusChanged { asset_id: T::AssetId },
671		/// The min_balance of an asset has been updated by the asset owner.
672		AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance },
673		/// Some account `who` was created with a deposit from `depositor`.
674		Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId },
675		/// Some account `who` was blocked.
676		Blocked { asset_id: T::AssetId, who: T::AccountId },
677		/// Some assets were deposited (e.g. for transaction fees).
678		Deposited { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
679		/// Some assets were withdrawn from the account (e.g. for transaction fees).
680		Withdrawn { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
681		/// Reserve information was set or updated for `asset_id`.
682		ReservesUpdated { asset_id: T::AssetId, reserves: Vec<T::ReserveData> },
683		/// Reserve information was removed for `asset_id`.
684		ReservesRemoved { asset_id: T::AssetId },
685		/// Some assets were issued as Credit (no owner yet).
686		IssuedCredit { asset_id: T::AssetId, amount: T::Balance },
687		/// Some assets Credit was destroyed.
688		BurnedCredit { asset_id: T::AssetId, amount: T::Balance },
689		/// Some assets were burned and a Debt was created.
690		IssuedDebt { asset_id: T::AssetId, amount: T::Balance },
691		/// Some assets Debt was destroyed (and assets issued).
692		BurnedDebt { asset_id: T::AssetId, amount: T::Balance },
693	}
694
695	#[pallet::error]
696	pub enum Error<T, I = ()> {
697		/// Account balance must be greater than or equal to the transfer amount.
698		BalanceLow,
699		/// The account to alter does not exist.
700		NoAccount,
701		/// The signing account has no permission to do the operation.
702		NoPermission,
703		/// The given asset ID is unknown.
704		Unknown,
705		/// The origin account is frozen.
706		Frozen,
707		/// The asset ID is already taken.
708		InUse,
709		/// Invalid witness data given.
710		BadWitness,
711		/// Minimum balance should be non-zero.
712		MinBalanceZero,
713		/// Unable to increment the consumer reference counters on the account. Either no provider
714		/// reference exists to allow a non-zero balance of a non-self-sufficient asset, or one
715		/// fewer then the maximum number of consumers has been reached.
716		UnavailableConsumer,
717		/// Invalid metadata given.
718		BadMetadata,
719		/// No approval exists that would allow the transfer.
720		Unapproved,
721		/// The source account would not survive the transfer and it needs to stay alive.
722		WouldDie,
723		/// The asset-account already exists.
724		AlreadyExists,
725		/// The asset-account doesn't have an associated deposit.
726		NoDeposit,
727		/// The operation would result in funds being burned.
728		WouldBurn,
729		/// The asset is a live asset and is actively being used. Usually emit for operations such
730		/// as `start_destroy` which require the asset to be in a destroying state.
731		LiveAsset,
732		/// The asset is not live, and likely being destroyed.
733		AssetNotLive,
734		/// The asset status is not the expected status.
735		IncorrectStatus,
736		/// The asset should be frozen before the given operation.
737		NotFrozen,
738		/// Callback action resulted in error
739		CallbackFailed,
740		/// The asset ID must be equal to the [`NextAssetId`].
741		BadAssetId,
742		/// The asset cannot be destroyed because some accounts for this asset contain freezes.
743		ContainsFreezes,
744		/// The asset cannot be destroyed because some accounts for this asset contain holds.
745		ContainsHolds,
746		/// Tried setting too many reserves.
747		TooManyReserves,
748		/// The asset deposit could not be fully moved due to a lock or freeze on the owner.
749		IncompleteDepositTransfer,
750	}
751
752	#[pallet::hooks]
753	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
754		#[cfg(feature = "try-runtime")]
755		fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
756			Self::do_try_state()
757		}
758	}
759
760	#[pallet::call(weight(<T as Config<I>>::WeightInfo))]
761	impl<T: Config<I>, I: 'static> Pallet<T, I> {
762		/// Issue a new class of fungible assets from a public origin.
763		///
764		/// This new asset class has no assets initially and its owner is the origin.
765		///
766		/// The origin must conform to the configured `CreateOrigin` and have sufficient funds free.
767		///
768		/// Funds of sender are reserved by `AssetDeposit`.
769		///
770		/// Parameters:
771		/// - `id`: The identifier of the new asset. This must not be currently in use to identify
772		/// an existing asset. If [`NextAssetId`] is set, then this must be equal to it.
773		/// - `admin`: The admin of this class of assets. The admin is the initial address of each
774		/// member of the asset class's admin team.
775		/// - `min_balance`: The minimum balance of this new asset that any single account must
776		/// have. If an account's balance is reduced below this, then it collapses to zero.
777		///
778		/// Emits `Created` event when successful.
779		///
780		/// Weight: `O(1)`
781		#[pallet::call_index(0)]
782		pub fn create(
783			origin: OriginFor<T>,
784			id: T::AssetIdParameter,
785			admin: AccountIdLookupOf<T>,
786			min_balance: T::Balance,
787		) -> DispatchResult {
788			let id: T::AssetId = id.into();
789			let owner = T::CreateOrigin::ensure_origin(origin, &id)?;
790			let admin = T::Lookup::lookup(admin)?;
791
792			ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
793			ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
794
795			if let Some(next_id) = NextAssetId::<T, I>::get() {
796				ensure!(id == next_id, Error::<T, I>::BadAssetId);
797			}
798
799			let deposit = T::AssetDeposit::get();
800			T::Currency::reserve(&owner, deposit)?;
801
802			Asset::<T, I>::insert(
803				id.clone(),
804				AssetDetails {
805					owner: owner.clone(),
806					issuer: admin.clone(),
807					admin: admin.clone(),
808					freezer: admin.clone(),
809					supply: Zero::zero(),
810					deposit,
811					min_balance,
812					is_sufficient: false,
813					accounts: 0,
814					sufficients: 0,
815					approvals: 0,
816					status: AssetStatus::Live,
817				},
818			);
819			ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
820			Self::deposit_event(Event::Created {
821				asset_id: id,
822				creator: owner.clone(),
823				owner: admin,
824			});
825
826			Ok(())
827		}
828
829		/// Issue a new class of fungible assets from a privileged origin.
830		///
831		/// This new asset class has no assets initially.
832		///
833		/// The origin must conform to `ForceOrigin`.
834		///
835		/// Unlike `create`, no funds are reserved.
836		///
837		/// - `id`: The identifier of the new asset. This must not be currently in use to identify
838		/// an existing asset. If [`NextAssetId`] is set, then this must be equal to it.
839		/// - `owner`: The owner of this class of assets. The owner has full superuser permissions
840		/// over this asset, but may later change and configure the permissions using
841		/// `transfer_ownership` and `set_team`.
842		/// - `min_balance`: The minimum balance of this new asset that any single account must
843		/// have. If an account's balance is reduced below this, then it collapses to zero.
844		///
845		/// Emits `ForceCreated` event when successful.
846		///
847		/// Weight: `O(1)`
848		#[pallet::call_index(1)]
849		pub fn force_create(
850			origin: OriginFor<T>,
851			id: T::AssetIdParameter,
852			owner: AccountIdLookupOf<T>,
853			is_sufficient: bool,
854			#[pallet::compact] min_balance: T::Balance,
855		) -> DispatchResult {
856			T::ForceOrigin::ensure_origin(origin)?;
857			let owner = T::Lookup::lookup(owner)?;
858			let id: T::AssetId = id.into();
859			Self::do_force_create(id, owner, is_sufficient, min_balance)
860		}
861
862		/// Start the process of destroying a fungible asset class.
863		///
864		/// `start_destroy` is the first in a series of extrinsics that should be called, to allow
865		/// destruction of an asset class.
866		///
867		/// The origin must conform to `ForceOrigin` or must be `Signed` by the asset's `owner`.
868		///
869		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
870		///   asset.
871		///
872		/// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
873		/// an account contains holds or freezes in place.
874		#[pallet::call_index(2)]
875		pub fn start_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
876			let maybe_check_owner = match T::ForceOrigin::try_origin(origin) {
877				Ok(_) => None,
878				Err(origin) => Some(ensure_signed(origin)?),
879			};
880			let id: T::AssetId = id.into();
881			Self::do_start_destroy(id, maybe_check_owner)
882		}
883
884		/// Destroy all accounts associated with a given asset.
885		///
886		/// `destroy_accounts` should only be called after `start_destroy` has been called, and the
887		/// asset is in a `Destroying` state.
888		///
889		/// Due to weight restrictions, this function may need to be called multiple times to fully
890		/// destroy all accounts. It will destroy `RemoveItemsLimit` accounts at a time.
891		///
892		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
893		///   asset.
894		///
895		/// Each call emits the `Event::DestroyedAccounts` event.
896		#[pallet::call_index(3)]
897		#[pallet::weight(T::WeightInfo::destroy_accounts(T::RemoveItemsLimit::get()))]
898		pub fn destroy_accounts(
899			origin: OriginFor<T>,
900			id: T::AssetIdParameter,
901		) -> DispatchResultWithPostInfo {
902			ensure_signed(origin)?;
903			let id: T::AssetId = id.into();
904			let removed_accounts = Self::do_destroy_accounts(id, T::RemoveItemsLimit::get())?;
905			Ok(Some(T::WeightInfo::destroy_accounts(removed_accounts)).into())
906		}
907
908		/// Destroy all approvals associated with a given asset up to the max (T::RemoveItemsLimit).
909		///
910		/// `destroy_approvals` should only be called after `start_destroy` has been called, and the
911		/// asset is in a `Destroying` state.
912		///
913		/// Due to weight restrictions, this function may need to be called multiple times to fully
914		/// destroy all approvals. It will destroy `RemoveItemsLimit` approvals at a time.
915		///
916		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
917		///   asset.
918		///
919		/// Each call emits the `Event::DestroyedApprovals` event.
920		#[pallet::call_index(4)]
921		#[pallet::weight(T::WeightInfo::destroy_approvals(T::RemoveItemsLimit::get()))]
922		pub fn destroy_approvals(
923			origin: OriginFor<T>,
924			id: T::AssetIdParameter,
925		) -> DispatchResultWithPostInfo {
926			ensure_signed(origin)?;
927			let id: T::AssetId = id.into();
928			let removed_approvals = Self::do_destroy_approvals(id, T::RemoveItemsLimit::get())?;
929			Ok(Some(T::WeightInfo::destroy_approvals(removed_approvals)).into())
930		}
931
932		/// Complete destroying asset and unreserve currency.
933		///
934		/// `finish_destroy` should only be called after `start_destroy` has been called, and the
935		/// asset is in a `Destroying` state. All accounts or approvals should be destroyed before
936		/// hand.
937		///
938		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
939		///   asset.
940		///
941		/// Each successful call emits the `Event::Destroyed` event.
942		#[pallet::call_index(5)]
943		pub fn finish_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
944			ensure_signed(origin)?;
945			let id: T::AssetId = id.into();
946			Self::do_finish_destroy(id)
947		}
948
949		/// Mint assets of a particular class.
950		///
951		/// The origin must be Signed and the sender must be the Issuer of the asset `id`.
952		///
953		/// - `id`: The identifier of the asset to have some amount minted.
954		/// - `beneficiary`: The account to be credited with the minted assets.
955		/// - `amount`: The amount of the asset to be minted.
956		///
957		/// Emits `Issued` event when successful.
958		///
959		/// Weight: `O(1)`
960		/// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`.
961		#[pallet::call_index(6)]
962		pub fn mint(
963			origin: OriginFor<T>,
964			id: T::AssetIdParameter,
965			beneficiary: AccountIdLookupOf<T>,
966			#[pallet::compact] amount: T::Balance,
967		) -> DispatchResult {
968			let origin = ensure_signed(origin)?;
969			let beneficiary = T::Lookup::lookup(beneficiary)?;
970			let id: T::AssetId = id.into();
971			Self::do_mint(id, &beneficiary, amount, Some(origin))?;
972			Ok(())
973		}
974
975		/// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`.
976		///
977		/// Origin must be Signed and the sender should be the Manager of the asset `id`.
978		///
979		/// Bails with `NoAccount` if the `who` is already dead.
980		///
981		/// - `id`: The identifier of the asset to have some amount burned.
982		/// - `who`: The account to be debited from.
983		/// - `amount`: The maximum amount by which `who`'s balance should be reduced.
984		///
985		/// Emits `Burned` with the actual amount burned. If this takes the balance to below the
986		/// minimum for the asset, then the amount burned is increased to take it to zero.
987		///
988		/// Weight: `O(1)`
989		/// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`.
990		#[pallet::call_index(7)]
991		pub fn burn(
992			origin: OriginFor<T>,
993			id: T::AssetIdParameter,
994			who: AccountIdLookupOf<T>,
995			#[pallet::compact] amount: T::Balance,
996		) -> DispatchResult {
997			let origin = ensure_signed(origin)?;
998			let who = T::Lookup::lookup(who)?;
999			let id: T::AssetId = id.into();
1000
1001			let f = DebitFlags { keep_alive: false, best_effort: true };
1002			Self::do_burn(id, &who, amount, Some(origin), f)?;
1003			Ok(())
1004		}
1005
1006		/// Move some assets from the sender account to another.
1007		///
1008		/// Origin must be Signed.
1009		///
1010		/// - `id`: The identifier of the asset to have some amount transferred.
1011		/// - `target`: The account to be credited.
1012		/// - `amount`: The amount by which the sender's balance of assets should be reduced and
1013		/// `target`'s balance increased. The amount actually transferred may be slightly greater in
1014		/// the case that the transfer would otherwise take the sender balance above zero but below
1015		/// the minimum balance. Must be greater than zero.
1016		///
1017		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1018		/// to below the minimum for the asset, then the amount transferred is increased to take it
1019		/// to zero.
1020		///
1021		/// Weight: `O(1)`
1022		/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
1023		/// `target`.
1024		#[pallet::call_index(8)]
1025		pub fn transfer(
1026			origin: OriginFor<T>,
1027			id: T::AssetIdParameter,
1028			target: AccountIdLookupOf<T>,
1029			#[pallet::compact] amount: T::Balance,
1030		) -> DispatchResult {
1031			let origin = ensure_signed(origin)?;
1032			let dest = T::Lookup::lookup(target)?;
1033			let id: T::AssetId = id.into();
1034
1035			let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1036			Self::do_transfer(id, &origin, &dest, amount, None, f).map(|_| ())
1037		}
1038
1039		/// Move some assets from the sender account to another, keeping the sender account alive.
1040		///
1041		/// Origin must be Signed.
1042		///
1043		/// - `id`: The identifier of the asset to have some amount transferred.
1044		/// - `target`: The account to be credited.
1045		/// - `amount`: The amount by which the sender's balance of assets should be reduced and
1046		/// `target`'s balance increased. The amount actually transferred may be slightly greater in
1047		/// the case that the transfer would otherwise take the sender balance above zero but below
1048		/// the minimum balance. Must be greater than zero.
1049		///
1050		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1051		/// to below the minimum for the asset, then the amount transferred is increased to take it
1052		/// to zero.
1053		///
1054		/// Weight: `O(1)`
1055		/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
1056		/// `target`.
1057		#[pallet::call_index(9)]
1058		pub fn transfer_keep_alive(
1059			origin: OriginFor<T>,
1060			id: T::AssetIdParameter,
1061			target: AccountIdLookupOf<T>,
1062			#[pallet::compact] amount: T::Balance,
1063		) -> DispatchResult {
1064			let source = ensure_signed(origin)?;
1065			let dest = T::Lookup::lookup(target)?;
1066			let id: T::AssetId = id.into();
1067
1068			let f = TransferFlags { keep_alive: true, best_effort: false, burn_dust: false };
1069			Self::do_transfer(id, &source, &dest, amount, None, f).map(|_| ())
1070		}
1071
1072		/// Move some assets from one account to another.
1073		///
1074		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
1075		///
1076		/// - `id`: The identifier of the asset to have some amount transferred.
1077		/// - `source`: The account to be debited.
1078		/// - `dest`: The account to be credited.
1079		/// - `amount`: The amount by which the `source`'s balance of assets should be reduced and
1080		/// `dest`'s balance increased. The amount actually transferred may be slightly greater in
1081		/// the case that the transfer would otherwise take the `source` balance above zero but
1082		/// below the minimum balance. Must be greater than zero.
1083		///
1084		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1085		/// to below the minimum for the asset, then the amount transferred is increased to take it
1086		/// to zero.
1087		///
1088		/// Weight: `O(1)`
1089		/// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of
1090		/// `dest`.
1091		#[pallet::call_index(10)]
1092		pub fn force_transfer(
1093			origin: OriginFor<T>,
1094			id: T::AssetIdParameter,
1095			source: AccountIdLookupOf<T>,
1096			dest: AccountIdLookupOf<T>,
1097			#[pallet::compact] amount: T::Balance,
1098		) -> DispatchResult {
1099			let origin = ensure_signed(origin)?;
1100			let source = T::Lookup::lookup(source)?;
1101			let dest = T::Lookup::lookup(dest)?;
1102			let id: T::AssetId = id.into();
1103
1104			let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1105			Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ())
1106		}
1107
1108		/// Disallow further unprivileged transfers of an asset `id` from an account `who`. `who`
1109		/// must already exist as an entry in `Account`s of the asset. If you want to freeze an
1110		/// account that does not have an entry, use `touch_other` first.
1111		///
1112		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1113		///
1114		/// - `id`: The identifier of the asset to be frozen.
1115		/// - `who`: The account to be frozen.
1116		///
1117		/// Emits `Frozen`.
1118		///
1119		/// Weight: `O(1)`
1120		#[pallet::call_index(11)]
1121		pub fn freeze(
1122			origin: OriginFor<T>,
1123			id: T::AssetIdParameter,
1124			who: AccountIdLookupOf<T>,
1125		) -> DispatchResult {
1126			let origin = ensure_signed(origin)?;
1127			let id: T::AssetId = id.into();
1128
1129			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1130			ensure!(
1131				d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1132				Error::<T, I>::IncorrectStatus
1133			);
1134			ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1135			let who = T::Lookup::lookup(who)?;
1136
1137			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1138				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1139					AccountStatus::Frozen;
1140				Ok(())
1141			})?;
1142
1143			Self::deposit_event(Event::<T, I>::Frozen { asset_id: id, who });
1144			Ok(())
1145		}
1146
1147		/// Allow unprivileged transfers to and from an account again.
1148		///
1149		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
1150		///
1151		/// - `id`: The identifier of the asset to be frozen.
1152		/// - `who`: The account to be unfrozen.
1153		///
1154		/// Emits `Thawed`.
1155		///
1156		/// Weight: `O(1)`
1157		#[pallet::call_index(12)]
1158		pub fn thaw(
1159			origin: OriginFor<T>,
1160			id: T::AssetIdParameter,
1161			who: AccountIdLookupOf<T>,
1162		) -> DispatchResult {
1163			let origin = ensure_signed(origin)?;
1164			let id: T::AssetId = id.into();
1165
1166			let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1167			ensure!(
1168				details.status == AssetStatus::Live || details.status == AssetStatus::Frozen,
1169				Error::<T, I>::IncorrectStatus
1170			);
1171			ensure!(origin == details.admin, Error::<T, I>::NoPermission);
1172			let who = T::Lookup::lookup(who)?;
1173
1174			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1175				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1176					AccountStatus::Liquid;
1177				Ok(())
1178			})?;
1179
1180			Self::deposit_event(Event::<T, I>::Thawed { asset_id: id, who });
1181			Ok(())
1182		}
1183
1184		/// Disallow further unprivileged transfers for the asset class.
1185		///
1186		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1187		///
1188		/// - `id`: The identifier of the asset to be frozen.
1189		///
1190		/// Emits `Frozen`.
1191		///
1192		/// Weight: `O(1)`
1193		#[pallet::call_index(13)]
1194		pub fn freeze_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1195			let origin = ensure_signed(origin)?;
1196			let id: T::AssetId = id.into();
1197
1198			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1199				let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1200				ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1201				ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1202
1203				d.status = AssetStatus::Frozen;
1204
1205				Self::deposit_event(Event::<T, I>::AssetFrozen { asset_id: id });
1206				Ok(())
1207			})
1208		}
1209
1210		/// Allow unprivileged transfers for the asset again.
1211		///
1212		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
1213		///
1214		/// - `id`: The identifier of the asset to be thawed.
1215		///
1216		/// Emits `Thawed`.
1217		///
1218		/// Weight: `O(1)`
1219		#[pallet::call_index(14)]
1220		pub fn thaw_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1221			let origin = ensure_signed(origin)?;
1222			let id: T::AssetId = id.into();
1223
1224			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1225				let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1226				ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1227				ensure!(d.status == AssetStatus::Frozen, Error::<T, I>::NotFrozen);
1228
1229				d.status = AssetStatus::Live;
1230
1231				Self::deposit_event(Event::<T, I>::AssetThawed { asset_id: id });
1232				Ok(())
1233			})
1234		}
1235
1236		/// Change the Owner of an asset.
1237		///
1238		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1239		///
1240		/// The asset (and metadata) deposit is moved from the current to the new owner. Fails
1241		/// with [`Error::IncompleteDepositTransfer`] if a lock or freeze on the current owner
1242		/// blocks the full transfer; clear it and retry.
1243		///
1244		/// - `id`: The identifier of the asset.
1245		/// - `owner`: The new Owner of this asset.
1246		///
1247		/// Emits `OwnerChanged`.
1248		///
1249		/// Weight: `O(1)`
1250		#[pallet::call_index(15)]
1251		pub fn transfer_ownership(
1252			origin: OriginFor<T>,
1253			id: T::AssetIdParameter,
1254			owner: AccountIdLookupOf<T>,
1255		) -> DispatchResult {
1256			let origin = ensure_signed(origin)?;
1257			let owner = T::Lookup::lookup(owner)?;
1258			let id: T::AssetId = id.into();
1259
1260			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1261				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1262				ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1263				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1264				if details.owner == owner {
1265					return Ok(());
1266				}
1267
1268				let metadata_deposit = Metadata::<T, I>::get(&id).deposit;
1269				let deposit = details.deposit + metadata_deposit;
1270
1271				// `repatriate_reserved` is best-effort: reject any partial move so the recorded
1272				// deposit stays in sync with what is actually reserved on the owner.
1273				let remaining =
1274					T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?;
1275				ensure!(remaining.is_zero(), Error::<T, I>::IncompleteDepositTransfer);
1276
1277				details.owner = owner.clone();
1278
1279				Self::deposit_event(Event::OwnerChanged { asset_id: id, owner });
1280				Ok(())
1281			})
1282		}
1283
1284		/// Change the Issuer, Admin and Freezer of an asset.
1285		///
1286		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1287		///
1288		/// - `id`: The identifier of the asset to be frozen.
1289		/// - `issuer`: The new Issuer of this asset.
1290		/// - `admin`: The new Admin of this asset.
1291		/// - `freezer`: The new Freezer of this asset.
1292		///
1293		/// Emits `TeamChanged`.
1294		///
1295		/// Weight: `O(1)`
1296		#[pallet::call_index(16)]
1297		pub fn set_team(
1298			origin: OriginFor<T>,
1299			id: T::AssetIdParameter,
1300			issuer: AccountIdLookupOf<T>,
1301			admin: AccountIdLookupOf<T>,
1302			freezer: AccountIdLookupOf<T>,
1303		) -> DispatchResult {
1304			let origin = ensure_signed(origin)?;
1305			let issuer = T::Lookup::lookup(issuer)?;
1306			let admin = T::Lookup::lookup(admin)?;
1307			let freezer = T::Lookup::lookup(freezer)?;
1308			let id: T::AssetId = id.into();
1309
1310			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1311				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1312				ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1313				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1314
1315				details.issuer = issuer.clone();
1316				details.admin = admin.clone();
1317				details.freezer = freezer.clone();
1318
1319				Self::deposit_event(Event::TeamChanged { asset_id: id, issuer, admin, freezer });
1320				Ok(())
1321			})
1322		}
1323
1324		/// Set the metadata for an asset.
1325		///
1326		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1327		///
1328		/// Funds of sender are reserved according to the formula:
1329		/// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into
1330		/// account any already reserved funds.
1331		///
1332		/// - `id`: The identifier of the asset to update.
1333		/// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
1334		/// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
1335		/// - `decimals`: The number of decimals this asset uses to represent one unit.
1336		///
1337		/// Emits `MetadataSet`.
1338		///
1339		/// Weight: `O(1)`
1340		#[pallet::call_index(17)]
1341		#[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))]
1342		pub fn set_metadata(
1343			origin: OriginFor<T>,
1344			id: T::AssetIdParameter,
1345			name: Vec<u8>,
1346			symbol: Vec<u8>,
1347			decimals: u8,
1348		) -> DispatchResult {
1349			let origin = ensure_signed(origin)?;
1350			let id: T::AssetId = id.into();
1351			Self::do_set_metadata(id, &origin, name, symbol, decimals)
1352		}
1353
1354		/// Clear the metadata for an asset.
1355		///
1356		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1357		///
1358		/// Any deposit is freed for the asset owner.
1359		///
1360		/// - `id`: The identifier of the asset to clear.
1361		///
1362		/// Emits `MetadataCleared`.
1363		///
1364		/// Weight: `O(1)`
1365		#[pallet::call_index(18)]
1366		pub fn clear_metadata(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1367			let origin = ensure_signed(origin)?;
1368			let id: T::AssetId = id.into();
1369
1370			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1371			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1372			ensure!(origin == d.owner, Error::<T, I>::NoPermission);
1373
1374			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1375				let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1376				T::Currency::unreserve(&d.owner, deposit);
1377				Self::deposit_event(Event::MetadataCleared { asset_id: id });
1378				Ok(())
1379			})
1380		}
1381
1382		/// Force the metadata for an asset to some value.
1383		///
1384		/// Origin must be ForceOrigin.
1385		///
1386		/// Any deposit is left alone.
1387		///
1388		/// - `id`: The identifier of the asset to update.
1389		/// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
1390		/// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
1391		/// - `decimals`: The number of decimals this asset uses to represent one unit.
1392		///
1393		/// Emits `MetadataSet`.
1394		///
1395		/// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively.
1396		#[pallet::call_index(19)]
1397		#[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))]
1398		pub fn force_set_metadata(
1399			origin: OriginFor<T>,
1400			id: T::AssetIdParameter,
1401			name: Vec<u8>,
1402			symbol: Vec<u8>,
1403			decimals: u8,
1404			is_frozen: bool,
1405		) -> DispatchResult {
1406			T::ForceOrigin::ensure_origin(origin)?;
1407			let id: T::AssetId = id.into();
1408
1409			let bounded_name: BoundedVec<u8, T::StringLimit> =
1410				name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1411
1412			let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1413				symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1414
1415			ensure!(Asset::<T, I>::contains_key(&id), Error::<T, I>::Unknown);
1416			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1417				let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1418				*metadata = Some(AssetMetadata {
1419					deposit,
1420					name: bounded_name,
1421					symbol: bounded_symbol,
1422					decimals,
1423					is_frozen,
1424				});
1425
1426				Self::deposit_event(Event::MetadataSet {
1427					asset_id: id,
1428					name,
1429					symbol,
1430					decimals,
1431					is_frozen,
1432				});
1433				Ok(())
1434			})
1435		}
1436
1437		/// Clear the metadata for an asset.
1438		///
1439		/// Origin must be ForceOrigin.
1440		///
1441		/// Any deposit is returned.
1442		///
1443		/// - `id`: The identifier of the asset to clear.
1444		///
1445		/// Emits `MetadataCleared`.
1446		///
1447		/// Weight: `O(1)`
1448		#[pallet::call_index(20)]
1449		pub fn force_clear_metadata(
1450			origin: OriginFor<T>,
1451			id: T::AssetIdParameter,
1452		) -> DispatchResult {
1453			T::ForceOrigin::ensure_origin(origin)?;
1454			let id: T::AssetId = id.into();
1455
1456			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1457			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1458				let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1459				T::Currency::unreserve(&d.owner, deposit);
1460				Self::deposit_event(Event::MetadataCleared { asset_id: id });
1461				Ok(())
1462			})
1463		}
1464
1465		/// Alter the attributes of a given asset.
1466		///
1467		/// Origin must be `ForceOrigin`.
1468		///
1469		/// - `id`: The identifier of the asset.
1470		/// - `owner`: The new Owner of this asset.
1471		/// - `issuer`: The new Issuer of this asset.
1472		/// - `admin`: The new Admin of this asset.
1473		/// - `freezer`: The new Freezer of this asset.
1474		/// - `min_balance`: The minimum balance of this new asset that any single account must
1475		/// have. If an account's balance is reduced below this, then it collapses to zero.
1476		/// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient
1477		/// value to account for the state bloat associated with its balance storage. If set to
1478		/// `true`, then non-zero balances may be stored without a `consumer` reference (and thus
1479		/// an ED in the Balances pallet or whatever else is used to control user-account state
1480		/// growth).
1481		/// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin
1482		/// instructions.
1483		///
1484		/// Emits `AssetStatusChanged` with the identity of the asset.
1485		///
1486		/// Weight: `O(1)`
1487		#[pallet::call_index(21)]
1488		pub fn force_asset_status(
1489			origin: OriginFor<T>,
1490			id: T::AssetIdParameter,
1491			owner: AccountIdLookupOf<T>,
1492			issuer: AccountIdLookupOf<T>,
1493			admin: AccountIdLookupOf<T>,
1494			freezer: AccountIdLookupOf<T>,
1495			#[pallet::compact] min_balance: T::Balance,
1496			is_sufficient: bool,
1497			is_frozen: bool,
1498		) -> DispatchResult {
1499			T::ForceOrigin::ensure_origin(origin)?;
1500			let id: T::AssetId = id.into();
1501
1502			Asset::<T, I>::try_mutate(id.clone(), |maybe_asset| {
1503				let mut asset = maybe_asset.take().ok_or(Error::<T, I>::Unknown)?;
1504				ensure!(asset.status != AssetStatus::Destroying, Error::<T, I>::AssetNotLive);
1505				asset.owner = T::Lookup::lookup(owner)?;
1506				asset.issuer = T::Lookup::lookup(issuer)?;
1507				asset.admin = T::Lookup::lookup(admin)?;
1508				asset.freezer = T::Lookup::lookup(freezer)?;
1509				asset.min_balance = min_balance;
1510				asset.is_sufficient = is_sufficient;
1511				if is_frozen {
1512					asset.status = AssetStatus::Frozen;
1513				} else {
1514					asset.status = AssetStatus::Live;
1515				}
1516				*maybe_asset = Some(asset);
1517
1518				Self::deposit_event(Event::AssetStatusChanged { asset_id: id });
1519				Ok(())
1520			})
1521		}
1522
1523		/// Approve an amount of asset for transfer by a delegated third-party account.
1524		///
1525		/// Origin must be Signed.
1526		///
1527		/// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account
1528		/// for the purpose of holding the approval. If some non-zero amount of assets is already
1529		/// approved from signing account to `delegate`, then it is topped up or unreserved to
1530		/// meet the right value.
1531		///
1532		/// NOTE: The signing account does not need to own `amount` of assets at the point of
1533		/// making this call.
1534		///
1535		/// - `id`: The identifier of the asset.
1536		/// - `delegate`: The account to delegate permission to transfer asset.
1537		/// - `amount`: The amount of asset that may be transferred by `delegate`. If there is
1538		/// already an approval in place, then this acts additively.
1539		///
1540		/// Emits `ApprovedTransfer` on success.
1541		///
1542		/// Weight: `O(1)`
1543		#[pallet::call_index(22)]
1544		pub fn approve_transfer(
1545			origin: OriginFor<T>,
1546			id: T::AssetIdParameter,
1547			delegate: AccountIdLookupOf<T>,
1548			#[pallet::compact] amount: T::Balance,
1549		) -> DispatchResult {
1550			let owner = ensure_signed(origin)?;
1551			let delegate = T::Lookup::lookup(delegate)?;
1552			let id: T::AssetId = id.into();
1553			Self::do_approve_transfer(id, &owner, &delegate, amount)
1554		}
1555
1556		/// Cancel all of some asset approved for delegated transfer by a third-party account.
1557		///
1558		/// Origin must be Signed and there must be an approval in place between signer and
1559		/// `delegate`.
1560		///
1561		/// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
1562		///
1563		/// - `id`: The identifier of the asset.
1564		/// - `delegate`: The account delegated permission to transfer asset.
1565		///
1566		/// Emits `ApprovalCancelled` on success.
1567		///
1568		/// Weight: `O(1)`
1569		#[pallet::call_index(23)]
1570		pub fn cancel_approval(
1571			origin: OriginFor<T>,
1572			id: T::AssetIdParameter,
1573			delegate: AccountIdLookupOf<T>,
1574		) -> DispatchResult {
1575			let owner = ensure_signed(origin)?;
1576			let delegate = T::Lookup::lookup(delegate)?;
1577			let id: T::AssetId = id.into();
1578			Self::do_cancel_approval(&id, &owner, &delegate)
1579		}
1580
1581		/// Cancel all of some asset approved for delegated transfer by a third-party account.
1582		///
1583		/// Origin must be either ForceOrigin or Signed origin with the signer being the Admin
1584		/// account of the asset `id`.
1585		///
1586		/// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
1587		///
1588		/// - `id`: The identifier of the asset.
1589		/// - `delegate`: The account delegated permission to transfer asset.
1590		///
1591		/// Emits `ApprovalCancelled` on success.
1592		///
1593		/// Weight: `O(1)`
1594		#[pallet::call_index(24)]
1595		pub fn force_cancel_approval(
1596			origin: OriginFor<T>,
1597			id: T::AssetIdParameter,
1598			owner: AccountIdLookupOf<T>,
1599			delegate: AccountIdLookupOf<T>,
1600		) -> DispatchResult {
1601			let id: T::AssetId = id.into();
1602			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1603			T::ForceOrigin::try_origin(origin)
1604				.map(|_| ())
1605				.or_else(|origin| -> DispatchResult {
1606					let origin = ensure_signed(origin)?;
1607					ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1608					Ok(())
1609				})?;
1610
1611			let owner = T::Lookup::lookup(owner)?;
1612			let delegate = T::Lookup::lookup(delegate)?;
1613			Self::do_cancel_approval(&id, &owner, &delegate)
1614		}
1615
1616		/// Transfer some asset balance from a previously delegated account to some third-party
1617		/// account.
1618		///
1619		/// Origin must be Signed and there must be an approval in place by the `owner` to the
1620		/// signer.
1621		///
1622		/// If the entire amount approved for transfer is transferred, then any deposit previously
1623		/// reserved by `approve_transfer` is unreserved.
1624		///
1625		/// - `id`: The identifier of the asset.
1626		/// - `owner`: The account which previously approved for a transfer of at least `amount` and
1627		/// from which the asset balance will be withdrawn.
1628		/// - `destination`: The account to which the asset balance of `amount` will be transferred.
1629		/// - `amount`: The amount of assets to transfer.
1630		///
1631		/// Emits `TransferredApproved` on success.
1632		///
1633		/// Weight: `O(1)`
1634		#[pallet::call_index(25)]
1635		pub fn transfer_approved(
1636			origin: OriginFor<T>,
1637			id: T::AssetIdParameter,
1638			owner: AccountIdLookupOf<T>,
1639			destination: AccountIdLookupOf<T>,
1640			#[pallet::compact] amount: T::Balance,
1641		) -> DispatchResult {
1642			let delegate = ensure_signed(origin)?;
1643			let owner = T::Lookup::lookup(owner)?;
1644			let destination = T::Lookup::lookup(destination)?;
1645			let id: T::AssetId = id.into();
1646			Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
1647		}
1648
1649		/// Create an asset account for non-provider assets.
1650		///
1651		/// A deposit will be taken from the signer account.
1652		///
1653		/// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
1654		///   to be taken.
1655		/// - `id`: The identifier of the asset for the account to be created.
1656		///
1657		/// Emits `Touched` event when successful.
1658		#[pallet::call_index(26)]
1659		#[pallet::weight(T::WeightInfo::touch())]
1660		pub fn touch(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1661			let who = ensure_signed(origin)?;
1662			let id: T::AssetId = id.into();
1663			Self::do_touch(id, who.clone(), who)
1664		}
1665
1666		/// Return the deposit (if any) of an asset account or a consumer reference (if any) of an
1667		/// account.
1668		///
1669		/// The origin must be Signed.
1670		///
1671		/// - `id`: The identifier of the asset for which the caller would like the deposit
1672		///   refunded.
1673		/// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund.
1674		///
1675		/// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
1676		/// the asset account contains holds or freezes in place.
1677		///
1678		/// Emits `Refunded` event when successful.
1679		#[pallet::call_index(27)]
1680		#[pallet::weight(T::WeightInfo::refund())]
1681		pub fn refund(
1682			origin: OriginFor<T>,
1683			id: T::AssetIdParameter,
1684			allow_burn: bool,
1685		) -> DispatchResult {
1686			let id: T::AssetId = id.into();
1687			Self::do_refund(id, ensure_signed(origin)?, allow_burn)
1688		}
1689
1690		/// Sets the minimum balance of an asset.
1691		///
1692		/// Only works if there aren't any accounts that are holding the asset or if
1693		/// the new value of `min_balance` is less than the old one.
1694		///
1695		/// Origin must be Signed and the sender has to be the Owner of the
1696		/// asset `id`.
1697		///
1698		/// - `id`: The identifier of the asset.
1699		/// - `min_balance`: The new value of `min_balance`.
1700		///
1701		/// Emits `AssetMinBalanceChanged` event when successful.
1702		#[pallet::call_index(28)]
1703		pub fn set_min_balance(
1704			origin: OriginFor<T>,
1705			id: T::AssetIdParameter,
1706			min_balance: T::Balance,
1707		) -> DispatchResult {
1708			let origin = ensure_signed(origin)?;
1709			let id: T::AssetId = id.into();
1710
1711			let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1712			ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1713
1714			let old_min_balance = details.min_balance;
1715			// If the asset is marked as sufficient it won't be allowed to
1716			// change the min_balance.
1717			ensure!(!details.is_sufficient, Error::<T, I>::NoPermission);
1718
1719			// Ensure that either the new min_balance is less than old
1720			// min_balance or there aren't any accounts holding the asset.
1721			ensure!(
1722				min_balance < old_min_balance || details.accounts == 0,
1723				Error::<T, I>::NoPermission
1724			);
1725
1726			details.min_balance = min_balance;
1727			Asset::<T, I>::insert(&id, details);
1728
1729			Self::deposit_event(Event::AssetMinBalanceChanged {
1730				asset_id: id,
1731				new_min_balance: min_balance,
1732			});
1733			Ok(())
1734		}
1735
1736		/// Create an asset account for `who`.
1737		///
1738		/// A deposit will be taken from the signer account.
1739		///
1740		/// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
1741		///   to be taken.
1742		/// - `id`: The identifier of the asset for the account to be created, the asset status must
1743		///   be live.
1744		/// - `who`: The account to be created.
1745		///
1746		/// Emits `Touched` event when successful.
1747		#[pallet::call_index(29)]
1748		#[pallet::weight(T::WeightInfo::touch_other())]
1749		pub fn touch_other(
1750			origin: OriginFor<T>,
1751			id: T::AssetIdParameter,
1752			who: AccountIdLookupOf<T>,
1753		) -> DispatchResult {
1754			let origin = ensure_signed(origin)?;
1755			let who = T::Lookup::lookup(who)?;
1756			let id: T::AssetId = id.into();
1757			Self::do_touch(id, who, origin)
1758		}
1759
1760		/// Return the deposit (if any) of a target asset account. Useful if you are the depositor.
1761		///
1762		/// The origin must be Signed and either the account owner, depositor, or asset `Admin`. In
1763		/// order to burn a non-zero balance of the asset, the caller must be the account and should
1764		/// use `refund`.
1765		///
1766		/// - `id`: The identifier of the asset for the account holding a deposit.
1767		/// - `who`: The account to refund.
1768		///
1769		/// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
1770		/// the asset account contains holds or freezes in place.
1771		///
1772		/// Emits `Refunded` event when successful.
1773		#[pallet::call_index(30)]
1774		#[pallet::weight(T::WeightInfo::refund_other())]
1775		pub fn refund_other(
1776			origin: OriginFor<T>,
1777			id: T::AssetIdParameter,
1778			who: AccountIdLookupOf<T>,
1779		) -> DispatchResult {
1780			let origin = ensure_signed(origin)?;
1781			let who = T::Lookup::lookup(who)?;
1782			let id: T::AssetId = id.into();
1783			Self::do_refund_other(id, &who, Some(origin))
1784		}
1785
1786		/// Disallow further unprivileged transfers of an asset `id` to and from an account `who`.
1787		///
1788		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1789		///
1790		/// - `id`: The identifier of the account's asset.
1791		/// - `who`: The account to be unblocked.
1792		///
1793		/// Emits `Blocked`.
1794		///
1795		/// Weight: `O(1)`
1796		#[pallet::call_index(31)]
1797		pub fn block(
1798			origin: OriginFor<T>,
1799			id: T::AssetIdParameter,
1800			who: AccountIdLookupOf<T>,
1801		) -> DispatchResult {
1802			let origin = ensure_signed(origin)?;
1803			let id: T::AssetId = id.into();
1804
1805			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1806			ensure!(
1807				d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1808				Error::<T, I>::IncorrectStatus
1809			);
1810			ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1811			let who = T::Lookup::lookup(who)?;
1812
1813			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1814				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1815					AccountStatus::Blocked;
1816				Ok(())
1817			})?;
1818
1819			Self::deposit_event(Event::<T, I>::Blocked { asset_id: id, who });
1820			Ok(())
1821		}
1822
1823		/// Transfer the entire transferable balance from the caller asset account.
1824		///
1825		/// NOTE: This function only attempts to transfer _transferable_ balances. This means that
1826		/// any held, frozen, or minimum balance (when `keep_alive` is `true`), will not be
1827		/// transferred by this function. To ensure that this function results in a killed account,
1828		/// you might need to prepare the account by removing any reference counters, storage
1829		/// deposits, etc...
1830		///
1831		/// The dispatch origin of this call must be Signed.
1832		///
1833		/// - `id`: The identifier of the asset for the account holding a deposit.
1834		/// - `dest`: The recipient of the transfer.
1835		/// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all
1836		///   of the funds the asset account has, causing the sender asset account to be killed
1837		///   (false), or transfer everything except at least the minimum balance, which will
1838		///   guarantee to keep the sender asset account alive (true).
1839		#[pallet::call_index(32)]
1840		#[pallet::weight(T::WeightInfo::transfer_all())]
1841		pub fn transfer_all(
1842			origin: OriginFor<T>,
1843			id: T::AssetIdParameter,
1844			dest: AccountIdLookupOf<T>,
1845			keep_alive: bool,
1846		) -> DispatchResult {
1847			let transactor = ensure_signed(origin)?;
1848			let keep_alive = if keep_alive { Preserve } else { Expendable };
1849			let reducible_balance = <Self as fungibles::Inspect<_>>::reducible_balance(
1850				id.clone().into(),
1851				&transactor,
1852				keep_alive,
1853				Fortitude::Polite,
1854			);
1855			let dest = T::Lookup::lookup(dest)?;
1856			<Self as fungibles::Mutate<_>>::transfer(
1857				id.into(),
1858				&transactor,
1859				&dest,
1860				reducible_balance,
1861				keep_alive,
1862			)?;
1863			Ok(())
1864		}
1865
1866		/// Sets the trusted reserve information of an asset.
1867		///
1868		/// Origin must be the Owner of the asset `id`. The origin must conform to the configured
1869		/// `CreateOrigin` or be the signed `owner` configured during asset creation.
1870		///
1871		/// - `id`: The identifier of the asset.
1872		/// - `reserves`: The full list of trusted reserves information.
1873		///
1874		/// Emits `AssetMinBalanceChanged` event when successful.
1875		#[pallet::call_index(33)]
1876		#[pallet::weight(T::WeightInfo::set_reserves(reserves.len() as u32))]
1877		pub fn set_reserves(
1878			origin: OriginFor<T>,
1879			id: T::AssetIdParameter,
1880			reserves: BoundedVec<T::ReserveData, ConstU32<MAX_RESERVES>>,
1881		) -> DispatchResult {
1882			let id: T::AssetId = id.into();
1883			let origin = ensure_signed(origin.clone())
1884				.or_else(|_| T::CreateOrigin::ensure_origin(origin, &id))?;
1885
1886			let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1887			ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1888
1889			Self::unchecked_update_reserves(id, reserves)?;
1890			Ok(())
1891		}
1892	}
1893
1894	#[pallet::view_functions]
1895	impl<T: Config<I>, I: 'static> Pallet<T, I> {
1896		/// Provide the asset details for asset `id`.
1897		pub fn asset_details(
1898			id: T::AssetId,
1899		) -> Option<AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>> {
1900			Asset::<T, I>::get(id)
1901		}
1902
1903		/// Provide the balance of `who` for asset `id`.
1904		pub fn balance_of(who: T::AccountId, id: T::AssetId) -> Option<<T as Config<I>>::Balance> {
1905			Account::<T, I>::get(id, who).map(|account| account.balance)
1906		}
1907
1908		/// Provide the configured metadata for asset `id`.
1909		pub fn get_metadata(
1910			id: T::AssetId,
1911		) -> Option<AssetMetadata<DepositBalanceOf<T, I>, BoundedVec<u8, T::StringLimit>>> {
1912			Metadata::<T, I>::try_get(id).ok()
1913		}
1914
1915		/// Provide the configured reserves data for asset `id`.
1916		pub fn get_reserves_data(id: T::AssetId) -> Vec<T::ReserveData> {
1917			Self::reserves(&id)
1918		}
1919	}
1920
1921	/// Implements [`AccountTouch`] trait.
1922	/// Note that a depositor can be any account, without any specific privilege.
1923	impl<T: Config<I>, I: 'static> AccountTouch<T::AssetId, T::AccountId> for Pallet<T, I> {
1924		type Balance = DepositBalanceOf<T, I>;
1925
1926		fn deposit_required(_: T::AssetId) -> Self::Balance {
1927			T::AssetAccountDeposit::get()
1928		}
1929
1930		fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
1931			match Asset::<T, I>::get(&asset) {
1932				// refer to the [`Self::new_account`] function for more details.
1933				Some(info) if info.is_sufficient => false,
1934				Some(_) if frame_system::Pallet::<T>::can_accrue_consumers(who, 2) => false,
1935				Some(_) => !Account::<T, I>::contains_key(asset, who),
1936				_ => true,
1937			}
1938		}
1939
1940		fn touch(
1941			asset: T::AssetId,
1942			who: &T::AccountId,
1943			depositor: &T::AccountId,
1944		) -> DispatchResult {
1945			Self::do_touch(asset, who.clone(), depositor.clone())
1946		}
1947	}
1948
1949	/// Implements [`ContainsPair`] trait for a pair of asset and account IDs.
1950	impl<T: Config<I>, I: 'static> ContainsPair<T::AssetId, T::AccountId> for Pallet<T, I> {
1951		/// Check if an account with the given asset ID and account address exists.
1952		fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool {
1953			Account::<T, I>::contains_key(asset, who)
1954		}
1955	}
1956
1957	/// Implements [`ProvideAssetReserves`] trait for getting the list of trusted reserves for a
1958	/// given asset.
1959	impl<T: Config<I>, I: 'static> ProvideAssetReserves<T::AssetId, T::ReserveData> for Pallet<T, I> {
1960		/// Provide the configured reserves for asset `id`.
1961		fn reserves(id: &T::AssetId) -> Vec<T::ReserveData> {
1962			Reserves::<T, I>::get(id).into_inner()
1963		}
1964	}
1965}
1966
1967#[cfg(any(feature = "try-runtime", test))]
1968impl<T: Config<I>, I: 'static> Pallet<T, I> {
1969	pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
1970		for asset_id in Reserves::<T, I>::iter_keys() {
1971			ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Reserves data found");
1972		}
1973
1974		for asset_id in Metadata::<T, I>::iter_keys() {
1975			ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Metadata found");
1976		}
1977
1978		for (asset_id, _, _) in Approvals::<T, I>::iter_keys() {
1979			ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Approval found");
1980		}
1981
1982		for (asset_id, _) in Account::<T, I>::iter_keys() {
1983			ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Account found");
1984		}
1985
1986		for (asset_id, details) in Asset::<T, I>::iter() {
1987			if details.status == AssetStatus::Destroying {
1988				continue;
1989			}
1990
1991			let mut calculated_supply = T::Balance::zero();
1992			let mut calculated_accounts = 0u32;
1993			let mut calculated_sufficients = 0u32;
1994
1995			for (who, account) in Account::<T, I>::iter_prefix(&asset_id) {
1996				let held = T::Holder::balance_on_hold(asset_id.clone(), &who).unwrap_or_default();
1997				calculated_supply =
1998					calculated_supply.saturating_add(account.balance).saturating_add(held);
1999				calculated_accounts += 1;
2000
2001				if matches!(account.reason, ExistenceReason::Sufficient) {
2002					calculated_sufficients += 1;
2003				}
2004
2005				let total_balance = account.balance.saturating_add(held);
2006				if total_balance < details.min_balance {
2007					if !matches!(
2008						account.reason,
2009						ExistenceReason::DepositHeld(_) | ExistenceReason::DepositFrom(_, _)
2010					) {
2011						log::warn!(
2012							"Account {who:?} for asset {asset_id:?} has total balance below min_balance but no deposit. Balance: {:?}, Held: {:?}, Min balance: {:?}, Reason: {:?}",
2013							account.balance,
2014							held,
2015							details.min_balance,
2016							account.reason,
2017						);
2018					}
2019				}
2020			}
2021
2022			// Using >= instead of == because the provided `do_refund` implementation
2023			// historically destroyed the account balance without decrementing the asset
2024			// supply. Although this has been fixed, existing on-chain state may still
2025			// contain overcounted supply from prior refunds.
2026			// TODO: add a migration to recalculate supply, then tighten this to `==`.
2027			ensure!(details.supply >= calculated_supply, "Asset supply mismatch");
2028			if details.accounts != calculated_accounts {
2029				// Legacy error in Kusama Asset Hub that needs to be cleaned up.
2030				log::error!(
2031					"Asset {asset_id:?} account count mismatch: calculated {calculated_accounts} vs expected {}",
2032					details.accounts,
2033				);
2034			}
2035			ensure!(
2036				details.sufficients == calculated_sufficients,
2037				"Asset sufficients count mismatch"
2038			);
2039
2040			let calculated_approvals = Approvals::<T, I>::iter_prefix((&asset_id,)).count() as u32;
2041
2042			if details.approvals != calculated_approvals {
2043				log::error!(
2044					"Asset {asset_id:?} approvals count mismatch: calculated {calculated_approvals} vs expected {}",
2045					details.approvals,
2046				);
2047
2048				return Err("Asset approvals count mismatch".into());
2049			}
2050		}
2051		Ok(())
2052	}
2053}
2054
2055sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);