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 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//! * `block`: Disallows further `transfer`s to and from an account; called by the asset class's
121//!   Freezer.
122//!
123//! Please refer to the [`Call`] enum and its associated variants for documentation on each
124//! function.
125//!
126//! ### Public Functions
127//! <!-- Original author of descriptions: @gavofyork -->
128//!
129//! * `balance` - Get the asset `id` balance of `who`.
130//! * `total_supply` - Get the total supply of an asset `id`.
131//!
132//! Please refer to the [`Pallet`] struct for details on publicly available functions.
133//!
134//! ### Callbacks
135//!
136//! Using `CallbackHandle` associated type, user can configure custom callback functions which are
137//! executed when new asset is created or an existing asset is destroyed.
138//!
139//! ## Related Modules
140//!
141//! * [`System`](../frame_system/index.html)
142//! * [`Support`](../frame_support/index.html)
143//!
144//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html
145
146// This recursion limit is needed because we have too many benchmarks and benchmarking will fail if
147// we add more without this limit.
148#![recursion_limit = "1024"]
149// Ensure we're `no_std` when compiling for Wasm.
150#![cfg_attr(not(feature = "std"), no_std)]
151
152#[cfg(feature = "runtime-benchmarks")]
153pub mod benchmarking;
154pub mod migration;
155#[cfg(test)]
156pub mod mock;
157#[cfg(test)]
158mod tests;
159pub mod weights;
160
161mod extra_mutator;
162pub use extra_mutator::*;
163mod functions;
164mod impl_fungibles;
165mod impl_stored_map;
166mod types;
167pub use types::*;
168
169extern crate alloc;
170
171use scale_info::TypeInfo;
172use sp_runtime::{
173	traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero},
174	ArithmeticError, DispatchError, TokenError,
175};
176
177use alloc::vec::Vec;
178use core::marker::PhantomData;
179use frame_support::{
180	dispatch::DispatchResult,
181	ensure,
182	pallet_prelude::DispatchResultWithPostInfo,
183	storage::KeyPrefixIterator,
184	traits::{
185		tokens::{
186			fungibles, DepositConsequence, Fortitude,
187			Preservation::{Expendable, Preserve},
188			WithdrawConsequence,
189		},
190		BalanceStatus::Reserved,
191		Currency, EnsureOriginWithArg, Incrementable, ReservableCurrency, StoredMap,
192	},
193};
194use frame_system::Config as SystemConfig;
195
196pub use pallet::*;
197pub use weights::WeightInfo;
198
199type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
200const LOG_TARGET: &str = "runtime::assets";
201
202/// Trait with callbacks that are executed after successful asset creation or destruction.
203pub trait AssetsCallback<AssetId, AccountId> {
204	/// Indicates that asset with `id` was successfully created by the `owner`
205	fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> {
206		Ok(())
207	}
208
209	/// Indicates that asset with `id` has just been destroyed
210	fn destroyed(_id: &AssetId) -> Result<(), ()> {
211		Ok(())
212	}
213}
214
215#[impl_trait_for_tuples::impl_for_tuples(10)]
216impl<AssetId, AccountId> AssetsCallback<AssetId, AccountId> for Tuple {
217	fn created(id: &AssetId, owner: &AccountId) -> Result<(), ()> {
218		for_tuples!( #( Tuple::created(id, owner)?; )* );
219		Ok(())
220	}
221
222	fn destroyed(id: &AssetId) -> Result<(), ()> {
223		for_tuples!( #( Tuple::destroyed(id)?; )* );
224		Ok(())
225	}
226}
227
228/// Auto-increment the [`NextAssetId`] when an asset is created.
229///
230/// This has not effect if the [`NextAssetId`] value is not present.
231pub struct AutoIncAssetId<T, I = ()>(PhantomData<(T, I)>);
232impl<T: Config<I>, I> AssetsCallback<T::AssetId, T::AccountId> for AutoIncAssetId<T, I>
233where
234	T::AssetId: Incrementable,
235{
236	fn created(_: &T::AssetId, _: &T::AccountId) -> Result<(), ()> {
237		let Some(next_id) = NextAssetId::<T, I>::get() else {
238			// Auto increment for the asset id is not enabled.
239			return Ok(());
240		};
241		let next_id = next_id.increment().ok_or(())?;
242		NextAssetId::<T, I>::put(next_id);
243		Ok(())
244	}
245}
246
247#[frame_support::pallet]
248pub mod pallet {
249	use super::*;
250	use codec::HasCompact;
251	use frame_support::{
252		pallet_prelude::*,
253		traits::{AccountTouch, ContainsPair},
254	};
255	use frame_system::pallet_prelude::*;
256
257	/// The in-code storage version.
258	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
259
260	#[pallet::pallet]
261	#[pallet::storage_version(STORAGE_VERSION)]
262	pub struct Pallet<T, I = ()>(_);
263
264	#[cfg(feature = "runtime-benchmarks")]
265	pub trait BenchmarkHelper<AssetIdParameter> {
266		fn create_asset_id_parameter(id: u32) -> AssetIdParameter;
267	}
268	#[cfg(feature = "runtime-benchmarks")]
269	impl<AssetIdParameter: From<u32>> BenchmarkHelper<AssetIdParameter> for () {
270		fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
271			id.into()
272		}
273	}
274
275	/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
276	pub mod config_preludes {
277		use super::*;
278		use frame_support::derive_impl;
279		pub struct TestDefaultConfig;
280
281		#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
282		impl frame_system::DefaultConfig for TestDefaultConfig {}
283
284		#[frame_support::register_default_impl(TestDefaultConfig)]
285		impl DefaultConfig for TestDefaultConfig {
286			#[inject_runtime_type]
287			type RuntimeEvent = ();
288			type Balance = u64;
289			type RemoveItemsLimit = ConstU32<5>;
290			type AssetId = u32;
291			type AssetIdParameter = u32;
292			type AssetDeposit = ConstUint<1>;
293			type AssetAccountDeposit = ConstUint<10>;
294			type MetadataDepositBase = ConstUint<1>;
295			type MetadataDepositPerByte = ConstUint<1>;
296			type ApprovalDeposit = ConstUint<1>;
297			type StringLimit = ConstU32<50>;
298			type Freezer = ();
299			type Holder = ();
300			type Extra = ();
301			type CallbackHandle = ();
302			type WeightInfo = ();
303			#[cfg(feature = "runtime-benchmarks")]
304			type BenchmarkHelper = ();
305		}
306	}
307
308	#[pallet::config(with_default)]
309	/// The module configuration trait.
310	pub trait Config<I: 'static = ()>: frame_system::Config {
311		/// The overarching event type.
312		#[pallet::no_default_bounds]
313		#[allow(deprecated)]
314		type RuntimeEvent: From<Event<Self, I>>
315			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
316
317		/// The units in which we record balances.
318		type Balance: Member
319			+ Parameter
320			+ HasCompact<Type: DecodeWithMemTracking>
321			+ AtLeast32BitUnsigned
322			+ Default
323			+ Copy
324			+ MaybeSerializeDeserialize
325			+ MaxEncodedLen
326			+ TypeInfo;
327
328		/// Max number of items to destroy per `destroy_accounts` and `destroy_approvals` call.
329		///
330		/// Must be configured to result in a weight that makes each call fit in a block.
331		#[pallet::constant]
332		type RemoveItemsLimit: Get<u32>;
333
334		/// Identifier for the class of asset.
335		type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
336
337		/// Wrapper around `Self::AssetId` to use in dispatchable call signatures. Allows the use
338		/// of compact encoding in instances of the pallet, which will prevent breaking changes
339		/// resulting from the removal of `HasCompact` from `Self::AssetId`.
340		///
341		/// This type includes the `From<Self::AssetId>` bound, since tightly coupled pallets may
342		/// want to convert an `AssetId` into a parameter for calling dispatchable functions
343		/// directly.
344		type AssetIdParameter: Parameter + From<Self::AssetId> + Into<Self::AssetId> + MaxEncodedLen;
345
346		/// The currency mechanism.
347		#[pallet::no_default]
348		type Currency: ReservableCurrency<Self::AccountId>;
349
350		/// Standard asset class creation is only allowed if the origin attempting it and the
351		/// asset class are in this set.
352		#[pallet::no_default]
353		type CreateOrigin: EnsureOriginWithArg<
354			Self::RuntimeOrigin,
355			Self::AssetId,
356			Success = Self::AccountId,
357		>;
358
359		/// The origin which may forcibly create or destroy an asset or otherwise alter privileged
360		/// attributes.
361		#[pallet::no_default]
362		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
363
364		/// The basic amount of funds that must be reserved for an asset.
365		#[pallet::constant]
366		#[pallet::no_default_bounds]
367		type AssetDeposit: Get<DepositBalanceOf<Self, I>>;
368
369		/// The amount of funds that must be reserved for a non-provider asset account to be
370		/// maintained.
371		#[pallet::constant]
372		#[pallet::no_default_bounds]
373		type AssetAccountDeposit: Get<DepositBalanceOf<Self, I>>;
374
375		/// The basic amount of funds that must be reserved when adding metadata to your asset.
376		#[pallet::constant]
377		#[pallet::no_default_bounds]
378		type MetadataDepositBase: Get<DepositBalanceOf<Self, I>>;
379
380		/// The additional funds that must be reserved for the number of bytes you store in your
381		/// metadata.
382		#[pallet::constant]
383		#[pallet::no_default_bounds]
384		type MetadataDepositPerByte: Get<DepositBalanceOf<Self, I>>;
385
386		/// The amount of funds that must be reserved when creating a new approval.
387		#[pallet::constant]
388		#[pallet::no_default_bounds]
389		type ApprovalDeposit: Get<DepositBalanceOf<Self, I>>;
390
391		/// The maximum length of a name or symbol stored on-chain.
392		#[pallet::constant]
393		type StringLimit: Get<u32>;
394
395		/// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be
396		/// respected in all permissionless operations.
397		type Freezer: FrozenBalance<Self::AssetId, Self::AccountId, Self::Balance>;
398
399		/// A hook to inspect a per-asset, per-account balance that is held. This goes in
400		/// accordance with balance model.
401		type Holder: BalanceOnHold<Self::AssetId, Self::AccountId, Self::Balance>;
402
403		/// Additional data to be stored with an account's asset balance.
404		type Extra: Member + Parameter + Default + MaxEncodedLen;
405
406		/// Callback methods for asset state change (e.g. asset created or destroyed)
407		///
408		/// Types implementing the [`AssetsCallback`] can be chained when listed together as a
409		/// tuple.
410		/// The [`AutoIncAssetId`] callback, in conjunction with the [`NextAssetId`], can be
411		/// used to set up auto-incrementing asset IDs for this collection.
412		type CallbackHandle: AssetsCallback<Self::AssetId, Self::AccountId>;
413
414		/// Weight information for extrinsics in this pallet.
415		type WeightInfo: WeightInfo;
416
417		/// Helper trait for benchmarks.
418		#[cfg(feature = "runtime-benchmarks")]
419		type BenchmarkHelper: BenchmarkHelper<Self::AssetIdParameter>;
420	}
421
422	#[pallet::storage]
423	/// Details of an asset.
424	pub type Asset<T: Config<I>, I: 'static = ()> = StorageMap<
425		_,
426		Blake2_128Concat,
427		T::AssetId,
428		AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
429	>;
430
431	#[pallet::storage]
432	/// The holdings of a specific account for a specific asset.
433	pub type Account<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
434		_,
435		Blake2_128Concat,
436		T::AssetId,
437		Blake2_128Concat,
438		T::AccountId,
439		AssetAccountOf<T, I>,
440	>;
441
442	#[pallet::storage]
443	/// Approved balance transfers. First balance is the amount approved for transfer. Second
444	/// is the amount of `T::Currency` reserved for storing this.
445	/// First key is the asset ID, second key is the owner and third key is the delegate.
446	pub type Approvals<T: Config<I>, I: 'static = ()> = StorageNMap<
447		_,
448		(
449			NMapKey<Blake2_128Concat, T::AssetId>,
450			NMapKey<Blake2_128Concat, T::AccountId>, // owner
451			NMapKey<Blake2_128Concat, T::AccountId>, // delegate
452		),
453		Approval<T::Balance, DepositBalanceOf<T, I>>,
454	>;
455
456	#[pallet::storage]
457	/// Metadata of an asset.
458	pub type Metadata<T: Config<I>, I: 'static = ()> = StorageMap<
459		_,
460		Blake2_128Concat,
461		T::AssetId,
462		AssetMetadata<DepositBalanceOf<T, I>, BoundedVec<u8, T::StringLimit>>,
463		ValueQuery,
464	>;
465
466	/// The asset ID enforced for the next asset creation, if any present. Otherwise, this storage
467	/// item has no effect.
468	///
469	/// This can be useful for setting up constraints for IDs of the new assets. For example, by
470	/// providing an initial [`NextAssetId`] and using the [`crate::AutoIncAssetId`] callback, an
471	/// auto-increment model can be applied to all new asset IDs.
472	///
473	/// The initial next asset ID can be set using the [`GenesisConfig`] or the
474	/// [SetNextAssetId](`migration::next_asset_id::SetNextAssetId`) migration.
475	#[pallet::storage]
476	pub type NextAssetId<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AssetId, OptionQuery>;
477
478	#[pallet::genesis_config]
479	#[derive(frame_support::DefaultNoBound)]
480	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
481		/// Genesis assets: id, owner, is_sufficient, min_balance
482		pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>,
483		/// Genesis metadata: id, name, symbol, decimals
484		pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>,
485		/// Genesis accounts: id, account_id, balance
486		pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>,
487		/// Genesis [`NextAssetId`].
488		///
489		/// Refer to the [`NextAssetId`] item for more information.
490		///
491		/// This does not enforce the asset ID for the [assets](`GenesisConfig::assets`) within the
492		/// genesis config. It sets the [`NextAssetId`] after they have been created.
493		pub next_asset_id: Option<T::AssetId>,
494	}
495
496	#[pallet::genesis_build]
497	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
498		fn build(&self) {
499			for (id, owner, is_sufficient, min_balance) in &self.assets {
500				assert!(!Asset::<T, I>::contains_key(id), "Asset id already in use");
501				assert!(!min_balance.is_zero(), "Min balance should not be zero");
502				Asset::<T, I>::insert(
503					id,
504					AssetDetails {
505						owner: owner.clone(),
506						issuer: owner.clone(),
507						admin: owner.clone(),
508						freezer: owner.clone(),
509						supply: Zero::zero(),
510						deposit: Zero::zero(),
511						min_balance: *min_balance,
512						is_sufficient: *is_sufficient,
513						accounts: 0,
514						sufficients: 0,
515						approvals: 0,
516						status: AssetStatus::Live,
517					},
518				);
519			}
520
521			for (id, name, symbol, decimals) in &self.metadata {
522				assert!(Asset::<T, I>::contains_key(id), "Asset does not exist");
523
524				let bounded_name: BoundedVec<u8, T::StringLimit> =
525					name.clone().try_into().expect("asset name is too long");
526				let bounded_symbol: BoundedVec<u8, T::StringLimit> =
527					symbol.clone().try_into().expect("asset symbol is too long");
528
529				let metadata = AssetMetadata {
530					deposit: Zero::zero(),
531					name: bounded_name,
532					symbol: bounded_symbol,
533					decimals: *decimals,
534					is_frozen: false,
535				};
536				Metadata::<T, I>::insert(id, metadata);
537			}
538
539			for (id, account_id, amount) in &self.accounts {
540				let result = <Pallet<T, I>>::increase_balance(
541					id.clone(),
542					account_id,
543					*amount,
544					|details| -> DispatchResult {
545						debug_assert!(
546							details.supply.checked_add(&amount).is_some(),
547							"checked in prep; qed"
548						);
549						details.supply = details.supply.saturating_add(*amount);
550						Ok(())
551					},
552				);
553				assert!(result.is_ok());
554			}
555
556			if let Some(next_asset_id) = &self.next_asset_id {
557				NextAssetId::<T, I>::put(next_asset_id);
558			}
559		}
560	}
561
562	#[pallet::event]
563	#[pallet::generate_deposit(pub(super) fn deposit_event)]
564	pub enum Event<T: Config<I>, I: 'static = ()> {
565		/// Some asset class was created.
566		Created { asset_id: T::AssetId, creator: T::AccountId, owner: T::AccountId },
567		/// Some assets were issued.
568		Issued { asset_id: T::AssetId, owner: T::AccountId, amount: T::Balance },
569		/// Some assets were transferred.
570		Transferred {
571			asset_id: T::AssetId,
572			from: T::AccountId,
573			to: T::AccountId,
574			amount: T::Balance,
575		},
576		/// Some assets were destroyed.
577		Burned { asset_id: T::AssetId, owner: T::AccountId, balance: T::Balance },
578		/// The management team changed.
579		TeamChanged {
580			asset_id: T::AssetId,
581			issuer: T::AccountId,
582			admin: T::AccountId,
583			freezer: T::AccountId,
584		},
585		/// The owner changed.
586		OwnerChanged { asset_id: T::AssetId, owner: T::AccountId },
587		/// Some account `who` was frozen.
588		Frozen { asset_id: T::AssetId, who: T::AccountId },
589		/// Some account `who` was thawed.
590		Thawed { asset_id: T::AssetId, who: T::AccountId },
591		/// Some asset `asset_id` was frozen.
592		AssetFrozen { asset_id: T::AssetId },
593		/// Some asset `asset_id` was thawed.
594		AssetThawed { asset_id: T::AssetId },
595		/// Accounts were destroyed for given asset.
596		AccountsDestroyed { asset_id: T::AssetId, accounts_destroyed: u32, accounts_remaining: u32 },
597		/// Approvals were destroyed for given asset.
598		ApprovalsDestroyed {
599			asset_id: T::AssetId,
600			approvals_destroyed: u32,
601			approvals_remaining: u32,
602		},
603		/// An asset class is in the process of being destroyed.
604		DestructionStarted { asset_id: T::AssetId },
605		/// An asset class was destroyed.
606		Destroyed { asset_id: T::AssetId },
607		/// Some asset class was force-created.
608		ForceCreated { asset_id: T::AssetId, owner: T::AccountId },
609		/// New metadata has been set for an asset.
610		MetadataSet {
611			asset_id: T::AssetId,
612			name: Vec<u8>,
613			symbol: Vec<u8>,
614			decimals: u8,
615			is_frozen: bool,
616		},
617		/// Metadata has been cleared for an asset.
618		MetadataCleared { asset_id: T::AssetId },
619		/// (Additional) funds have been approved for transfer to a destination account.
620		ApprovedTransfer {
621			asset_id: T::AssetId,
622			source: T::AccountId,
623			delegate: T::AccountId,
624			amount: T::Balance,
625		},
626		/// An approval for account `delegate` was cancelled by `owner`.
627		ApprovalCancelled { asset_id: T::AssetId, owner: T::AccountId, delegate: T::AccountId },
628		/// An `amount` was transferred in its entirety from `owner` to `destination` by
629		/// the approved `delegate`.
630		TransferredApproved {
631			asset_id: T::AssetId,
632			owner: T::AccountId,
633			delegate: T::AccountId,
634			destination: T::AccountId,
635			amount: T::Balance,
636		},
637		/// An asset has had its attributes changed by the `Force` origin.
638		AssetStatusChanged { asset_id: T::AssetId },
639		/// The min_balance of an asset has been updated by the asset owner.
640		AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance },
641		/// Some account `who` was created with a deposit from `depositor`.
642		Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId },
643		/// Some account `who` was blocked.
644		Blocked { asset_id: T::AssetId, who: T::AccountId },
645		/// Some assets were deposited (e.g. for transaction fees).
646		Deposited { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
647		/// Some assets were withdrawn from the account (e.g. for transaction fees).
648		Withdrawn { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
649	}
650
651	#[pallet::error]
652	pub enum Error<T, I = ()> {
653		/// Account balance must be greater than or equal to the transfer amount.
654		BalanceLow,
655		/// The account to alter does not exist.
656		NoAccount,
657		/// The signing account has no permission to do the operation.
658		NoPermission,
659		/// The given asset ID is unknown.
660		Unknown,
661		/// The origin account is frozen.
662		Frozen,
663		/// The asset ID is already taken.
664		InUse,
665		/// Invalid witness data given.
666		BadWitness,
667		/// Minimum balance should be non-zero.
668		MinBalanceZero,
669		/// Unable to increment the consumer reference counters on the account. Either no provider
670		/// reference exists to allow a non-zero balance of a non-self-sufficient asset, or one
671		/// fewer then the maximum number of consumers has been reached.
672		UnavailableConsumer,
673		/// Invalid metadata given.
674		BadMetadata,
675		/// No approval exists that would allow the transfer.
676		Unapproved,
677		/// The source account would not survive the transfer and it needs to stay alive.
678		WouldDie,
679		/// The asset-account already exists.
680		AlreadyExists,
681		/// The asset-account doesn't have an associated deposit.
682		NoDeposit,
683		/// The operation would result in funds being burned.
684		WouldBurn,
685		/// The asset is a live asset and is actively being used. Usually emit for operations such
686		/// as `start_destroy` which require the asset to be in a destroying state.
687		LiveAsset,
688		/// The asset is not live, and likely being destroyed.
689		AssetNotLive,
690		/// The asset status is not the expected status.
691		IncorrectStatus,
692		/// The asset should be frozen before the given operation.
693		NotFrozen,
694		/// Callback action resulted in error
695		CallbackFailed,
696		/// The asset ID must be equal to the [`NextAssetId`].
697		BadAssetId,
698		/// The asset cannot be destroyed because some accounts for this asset contain freezes.
699		ContainsFreezes,
700		/// The asset cannot be destroyed because some accounts for this asset contain holds.
701		ContainsHolds,
702	}
703
704	#[pallet::call(weight(<T as Config<I>>::WeightInfo))]
705	impl<T: Config<I>, I: 'static> Pallet<T, I> {
706		/// Issue a new class of fungible assets from a public origin.
707		///
708		/// This new asset class has no assets initially and its owner is the origin.
709		///
710		/// The origin must conform to the configured `CreateOrigin` and have sufficient funds free.
711		///
712		/// Funds of sender are reserved by `AssetDeposit`.
713		///
714		/// Parameters:
715		/// - `id`: The identifier of the new asset. This must not be currently in use to identify
716		/// an existing asset. If [`NextAssetId`] is set, then this must be equal to it.
717		/// - `admin`: The admin of this class of assets. The admin is the initial address of each
718		/// member of the asset class's admin team.
719		/// - `min_balance`: The minimum balance of this new asset that any single account must
720		/// have. If an account's balance is reduced below this, then it collapses to zero.
721		///
722		/// Emits `Created` event when successful.
723		///
724		/// Weight: `O(1)`
725		#[pallet::call_index(0)]
726		pub fn create(
727			origin: OriginFor<T>,
728			id: T::AssetIdParameter,
729			admin: AccountIdLookupOf<T>,
730			min_balance: T::Balance,
731		) -> DispatchResult {
732			let id: T::AssetId = id.into();
733			let owner = T::CreateOrigin::ensure_origin(origin, &id)?;
734			let admin = T::Lookup::lookup(admin)?;
735
736			ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
737			ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
738
739			if let Some(next_id) = NextAssetId::<T, I>::get() {
740				ensure!(id == next_id, Error::<T, I>::BadAssetId);
741			}
742
743			let deposit = T::AssetDeposit::get();
744			T::Currency::reserve(&owner, deposit)?;
745
746			Asset::<T, I>::insert(
747				id.clone(),
748				AssetDetails {
749					owner: owner.clone(),
750					issuer: admin.clone(),
751					admin: admin.clone(),
752					freezer: admin.clone(),
753					supply: Zero::zero(),
754					deposit,
755					min_balance,
756					is_sufficient: false,
757					accounts: 0,
758					sufficients: 0,
759					approvals: 0,
760					status: AssetStatus::Live,
761				},
762			);
763			ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
764			Self::deposit_event(Event::Created {
765				asset_id: id,
766				creator: owner.clone(),
767				owner: admin,
768			});
769
770			Ok(())
771		}
772
773		/// Issue a new class of fungible assets from a privileged origin.
774		///
775		/// This new asset class has no assets initially.
776		///
777		/// The origin must conform to `ForceOrigin`.
778		///
779		/// Unlike `create`, no funds are reserved.
780		///
781		/// - `id`: The identifier of the new asset. This must not be currently in use to identify
782		/// an existing asset. If [`NextAssetId`] is set, then this must be equal to it.
783		/// - `owner`: The owner of this class of assets. The owner has full superuser permissions
784		/// over this asset, but may later change and configure the permissions using
785		/// `transfer_ownership` and `set_team`.
786		/// - `min_balance`: The minimum balance of this new asset that any single account must
787		/// have. If an account's balance is reduced below this, then it collapses to zero.
788		///
789		/// Emits `ForceCreated` event when successful.
790		///
791		/// Weight: `O(1)`
792		#[pallet::call_index(1)]
793		pub fn force_create(
794			origin: OriginFor<T>,
795			id: T::AssetIdParameter,
796			owner: AccountIdLookupOf<T>,
797			is_sufficient: bool,
798			#[pallet::compact] min_balance: T::Balance,
799		) -> DispatchResult {
800			T::ForceOrigin::ensure_origin(origin)?;
801			let owner = T::Lookup::lookup(owner)?;
802			let id: T::AssetId = id.into();
803			Self::do_force_create(id, owner, is_sufficient, min_balance)
804		}
805
806		/// Start the process of destroying a fungible asset class.
807		///
808		/// `start_destroy` is the first in a series of extrinsics that should be called, to allow
809		/// destruction of an asset class.
810		///
811		/// The origin must conform to `ForceOrigin` or must be `Signed` by the asset's `owner`.
812		///
813		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
814		///   asset.
815		///
816		/// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
817		/// an account contains holds or freezes in place.
818		#[pallet::call_index(2)]
819		pub fn start_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
820			let maybe_check_owner = match T::ForceOrigin::try_origin(origin) {
821				Ok(_) => None,
822				Err(origin) => Some(ensure_signed(origin)?),
823			};
824			let id: T::AssetId = id.into();
825			Self::do_start_destroy(id, maybe_check_owner)
826		}
827
828		/// Destroy all accounts associated with a given asset.
829		///
830		/// `destroy_accounts` should only be called after `start_destroy` has been called, and the
831		/// asset is in a `Destroying` state.
832		///
833		/// Due to weight restrictions, this function may need to be called multiple times to fully
834		/// destroy all accounts. It will destroy `RemoveItemsLimit` accounts at a time.
835		///
836		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
837		///   asset.
838		///
839		/// Each call emits the `Event::DestroyedAccounts` event.
840		#[pallet::call_index(3)]
841		#[pallet::weight(T::WeightInfo::destroy_accounts(T::RemoveItemsLimit::get()))]
842		pub fn destroy_accounts(
843			origin: OriginFor<T>,
844			id: T::AssetIdParameter,
845		) -> DispatchResultWithPostInfo {
846			ensure_signed(origin)?;
847			let id: T::AssetId = id.into();
848			let removed_accounts = Self::do_destroy_accounts(id, T::RemoveItemsLimit::get())?;
849			Ok(Some(T::WeightInfo::destroy_accounts(removed_accounts)).into())
850		}
851
852		/// Destroy all approvals associated with a given asset up to the max (T::RemoveItemsLimit).
853		///
854		/// `destroy_approvals` should only be called after `start_destroy` has been called, and the
855		/// asset is in a `Destroying` state.
856		///
857		/// Due to weight restrictions, this function may need to be called multiple times to fully
858		/// destroy all approvals. It will destroy `RemoveItemsLimit` approvals at a time.
859		///
860		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
861		///   asset.
862		///
863		/// Each call emits the `Event::DestroyedApprovals` event.
864		#[pallet::call_index(4)]
865		#[pallet::weight(T::WeightInfo::destroy_approvals(T::RemoveItemsLimit::get()))]
866		pub fn destroy_approvals(
867			origin: OriginFor<T>,
868			id: T::AssetIdParameter,
869		) -> DispatchResultWithPostInfo {
870			ensure_signed(origin)?;
871			let id: T::AssetId = id.into();
872			let removed_approvals = Self::do_destroy_approvals(id, T::RemoveItemsLimit::get())?;
873			Ok(Some(T::WeightInfo::destroy_approvals(removed_approvals)).into())
874		}
875
876		/// Complete destroying asset and unreserve currency.
877		///
878		/// `finish_destroy` should only be called after `start_destroy` has been called, and the
879		/// asset is in a `Destroying` state. All accounts or approvals should be destroyed before
880		/// hand.
881		///
882		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
883		///   asset.
884		///
885		/// Each successful call emits the `Event::Destroyed` event.
886		#[pallet::call_index(5)]
887		pub fn finish_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
888			ensure_signed(origin)?;
889			let id: T::AssetId = id.into();
890			Self::do_finish_destroy(id)
891		}
892
893		/// Mint assets of a particular class.
894		///
895		/// The origin must be Signed and the sender must be the Issuer of the asset `id`.
896		///
897		/// - `id`: The identifier of the asset to have some amount minted.
898		/// - `beneficiary`: The account to be credited with the minted assets.
899		/// - `amount`: The amount of the asset to be minted.
900		///
901		/// Emits `Issued` event when successful.
902		///
903		/// Weight: `O(1)`
904		/// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`.
905		#[pallet::call_index(6)]
906		pub fn mint(
907			origin: OriginFor<T>,
908			id: T::AssetIdParameter,
909			beneficiary: AccountIdLookupOf<T>,
910			#[pallet::compact] amount: T::Balance,
911		) -> DispatchResult {
912			let origin = ensure_signed(origin)?;
913			let beneficiary = T::Lookup::lookup(beneficiary)?;
914			let id: T::AssetId = id.into();
915			Self::do_mint(id, &beneficiary, amount, Some(origin))?;
916			Ok(())
917		}
918
919		/// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`.
920		///
921		/// Origin must be Signed and the sender should be the Manager of the asset `id`.
922		///
923		/// Bails with `NoAccount` if the `who` is already dead.
924		///
925		/// - `id`: The identifier of the asset to have some amount burned.
926		/// - `who`: The account to be debited from.
927		/// - `amount`: The maximum amount by which `who`'s balance should be reduced.
928		///
929		/// Emits `Burned` with the actual amount burned. If this takes the balance to below the
930		/// minimum for the asset, then the amount burned is increased to take it to zero.
931		///
932		/// Weight: `O(1)`
933		/// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`.
934		#[pallet::call_index(7)]
935		pub fn burn(
936			origin: OriginFor<T>,
937			id: T::AssetIdParameter,
938			who: AccountIdLookupOf<T>,
939			#[pallet::compact] amount: T::Balance,
940		) -> DispatchResult {
941			let origin = ensure_signed(origin)?;
942			let who = T::Lookup::lookup(who)?;
943			let id: T::AssetId = id.into();
944
945			let f = DebitFlags { keep_alive: false, best_effort: true };
946			Self::do_burn(id, &who, amount, Some(origin), f)?;
947			Ok(())
948		}
949
950		/// Move some assets from the sender account to another.
951		///
952		/// Origin must be Signed.
953		///
954		/// - `id`: The identifier of the asset to have some amount transferred.
955		/// - `target`: The account to be credited.
956		/// - `amount`: The amount by which the sender's balance of assets should be reduced and
957		/// `target`'s balance increased. The amount actually transferred may be slightly greater in
958		/// the case that the transfer would otherwise take the sender balance above zero but below
959		/// the minimum balance. Must be greater than zero.
960		///
961		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
962		/// to below the minimum for the asset, then the amount transferred is increased to take it
963		/// to zero.
964		///
965		/// Weight: `O(1)`
966		/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
967		/// `target`.
968		#[pallet::call_index(8)]
969		pub fn transfer(
970			origin: OriginFor<T>,
971			id: T::AssetIdParameter,
972			target: AccountIdLookupOf<T>,
973			#[pallet::compact] amount: T::Balance,
974		) -> DispatchResult {
975			let origin = ensure_signed(origin)?;
976			let dest = T::Lookup::lookup(target)?;
977			let id: T::AssetId = id.into();
978
979			let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
980			Self::do_transfer(id, &origin, &dest, amount, None, f).map(|_| ())
981		}
982
983		/// Move some assets from the sender account to another, keeping the sender account alive.
984		///
985		/// Origin must be Signed.
986		///
987		/// - `id`: The identifier of the asset to have some amount transferred.
988		/// - `target`: The account to be credited.
989		/// - `amount`: The amount by which the sender's balance of assets should be reduced and
990		/// `target`'s balance increased. The amount actually transferred may be slightly greater in
991		/// the case that the transfer would otherwise take the sender balance above zero but below
992		/// the minimum balance. Must be greater than zero.
993		///
994		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
995		/// to below the minimum for the asset, then the amount transferred is increased to take it
996		/// to zero.
997		///
998		/// Weight: `O(1)`
999		/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
1000		/// `target`.
1001		#[pallet::call_index(9)]
1002		pub fn transfer_keep_alive(
1003			origin: OriginFor<T>,
1004			id: T::AssetIdParameter,
1005			target: AccountIdLookupOf<T>,
1006			#[pallet::compact] amount: T::Balance,
1007		) -> DispatchResult {
1008			let source = ensure_signed(origin)?;
1009			let dest = T::Lookup::lookup(target)?;
1010			let id: T::AssetId = id.into();
1011
1012			let f = TransferFlags { keep_alive: true, best_effort: false, burn_dust: false };
1013			Self::do_transfer(id, &source, &dest, amount, None, f).map(|_| ())
1014		}
1015
1016		/// Move some assets from one account to another.
1017		///
1018		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
1019		///
1020		/// - `id`: The identifier of the asset to have some amount transferred.
1021		/// - `source`: The account to be debited.
1022		/// - `dest`: The account to be credited.
1023		/// - `amount`: The amount by which the `source`'s balance of assets should be reduced and
1024		/// `dest`'s balance increased. The amount actually transferred may be slightly greater in
1025		/// the case that the transfer would otherwise take the `source` balance above zero but
1026		/// below the minimum balance. Must be greater than zero.
1027		///
1028		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1029		/// to below the minimum for the asset, then the amount transferred is increased to take it
1030		/// to zero.
1031		///
1032		/// Weight: `O(1)`
1033		/// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of
1034		/// `dest`.
1035		#[pallet::call_index(10)]
1036		pub fn force_transfer(
1037			origin: OriginFor<T>,
1038			id: T::AssetIdParameter,
1039			source: AccountIdLookupOf<T>,
1040			dest: AccountIdLookupOf<T>,
1041			#[pallet::compact] amount: T::Balance,
1042		) -> DispatchResult {
1043			let origin = ensure_signed(origin)?;
1044			let source = T::Lookup::lookup(source)?;
1045			let dest = T::Lookup::lookup(dest)?;
1046			let id: T::AssetId = id.into();
1047
1048			let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1049			Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ())
1050		}
1051
1052		/// Disallow further unprivileged transfers of an asset `id` from an account `who`. `who`
1053		/// must already exist as an entry in `Account`s of the asset. If you want to freeze an
1054		/// account that does not have an entry, use `touch_other` first.
1055		///
1056		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1057		///
1058		/// - `id`: The identifier of the asset to be frozen.
1059		/// - `who`: The account to be frozen.
1060		///
1061		/// Emits `Frozen`.
1062		///
1063		/// Weight: `O(1)`
1064		#[pallet::call_index(11)]
1065		pub fn freeze(
1066			origin: OriginFor<T>,
1067			id: T::AssetIdParameter,
1068			who: AccountIdLookupOf<T>,
1069		) -> DispatchResult {
1070			let origin = ensure_signed(origin)?;
1071			let id: T::AssetId = id.into();
1072
1073			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1074			ensure!(
1075				d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1076				Error::<T, I>::IncorrectStatus
1077			);
1078			ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1079			let who = T::Lookup::lookup(who)?;
1080
1081			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1082				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1083					AccountStatus::Frozen;
1084				Ok(())
1085			})?;
1086
1087			Self::deposit_event(Event::<T, I>::Frozen { asset_id: id, who });
1088			Ok(())
1089		}
1090
1091		/// Allow unprivileged transfers to and from an account again.
1092		///
1093		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
1094		///
1095		/// - `id`: The identifier of the asset to be frozen.
1096		/// - `who`: The account to be unfrozen.
1097		///
1098		/// Emits `Thawed`.
1099		///
1100		/// Weight: `O(1)`
1101		#[pallet::call_index(12)]
1102		pub fn thaw(
1103			origin: OriginFor<T>,
1104			id: T::AssetIdParameter,
1105			who: AccountIdLookupOf<T>,
1106		) -> DispatchResult {
1107			let origin = ensure_signed(origin)?;
1108			let id: T::AssetId = id.into();
1109
1110			let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1111			ensure!(
1112				details.status == AssetStatus::Live || details.status == AssetStatus::Frozen,
1113				Error::<T, I>::IncorrectStatus
1114			);
1115			ensure!(origin == details.admin, Error::<T, I>::NoPermission);
1116			let who = T::Lookup::lookup(who)?;
1117
1118			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1119				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1120					AccountStatus::Liquid;
1121				Ok(())
1122			})?;
1123
1124			Self::deposit_event(Event::<T, I>::Thawed { asset_id: id, who });
1125			Ok(())
1126		}
1127
1128		/// Disallow further unprivileged transfers for the asset class.
1129		///
1130		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1131		///
1132		/// - `id`: The identifier of the asset to be frozen.
1133		///
1134		/// Emits `Frozen`.
1135		///
1136		/// Weight: `O(1)`
1137		#[pallet::call_index(13)]
1138		pub fn freeze_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1139			let origin = ensure_signed(origin)?;
1140			let id: T::AssetId = id.into();
1141
1142			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1143				let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1144				ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1145				ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1146
1147				d.status = AssetStatus::Frozen;
1148
1149				Self::deposit_event(Event::<T, I>::AssetFrozen { asset_id: id });
1150				Ok(())
1151			})
1152		}
1153
1154		/// Allow unprivileged transfers for the asset again.
1155		///
1156		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
1157		///
1158		/// - `id`: The identifier of the asset to be thawed.
1159		///
1160		/// Emits `Thawed`.
1161		///
1162		/// Weight: `O(1)`
1163		#[pallet::call_index(14)]
1164		pub fn thaw_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1165			let origin = ensure_signed(origin)?;
1166			let id: T::AssetId = id.into();
1167
1168			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1169				let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1170				ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1171				ensure!(d.status == AssetStatus::Frozen, Error::<T, I>::NotFrozen);
1172
1173				d.status = AssetStatus::Live;
1174
1175				Self::deposit_event(Event::<T, I>::AssetThawed { asset_id: id });
1176				Ok(())
1177			})
1178		}
1179
1180		/// Change the Owner of an asset.
1181		///
1182		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1183		///
1184		/// - `id`: The identifier of the asset.
1185		/// - `owner`: The new Owner of this asset.
1186		///
1187		/// Emits `OwnerChanged`.
1188		///
1189		/// Weight: `O(1)`
1190		#[pallet::call_index(15)]
1191		pub fn transfer_ownership(
1192			origin: OriginFor<T>,
1193			id: T::AssetIdParameter,
1194			owner: AccountIdLookupOf<T>,
1195		) -> DispatchResult {
1196			let origin = ensure_signed(origin)?;
1197			let owner = T::Lookup::lookup(owner)?;
1198			let id: T::AssetId = id.into();
1199
1200			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1201				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1202				ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1203				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1204				if details.owner == owner {
1205					return Ok(());
1206				}
1207
1208				let metadata_deposit = Metadata::<T, I>::get(&id).deposit;
1209				let deposit = details.deposit + metadata_deposit;
1210
1211				// Move the deposit to the new owner.
1212				T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?;
1213
1214				details.owner = owner.clone();
1215
1216				Self::deposit_event(Event::OwnerChanged { asset_id: id, owner });
1217				Ok(())
1218			})
1219		}
1220
1221		/// Change the Issuer, Admin and Freezer of an asset.
1222		///
1223		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1224		///
1225		/// - `id`: The identifier of the asset to be frozen.
1226		/// - `issuer`: The new Issuer of this asset.
1227		/// - `admin`: The new Admin of this asset.
1228		/// - `freezer`: The new Freezer of this asset.
1229		///
1230		/// Emits `TeamChanged`.
1231		///
1232		/// Weight: `O(1)`
1233		#[pallet::call_index(16)]
1234		pub fn set_team(
1235			origin: OriginFor<T>,
1236			id: T::AssetIdParameter,
1237			issuer: AccountIdLookupOf<T>,
1238			admin: AccountIdLookupOf<T>,
1239			freezer: AccountIdLookupOf<T>,
1240		) -> DispatchResult {
1241			let origin = ensure_signed(origin)?;
1242			let issuer = T::Lookup::lookup(issuer)?;
1243			let admin = T::Lookup::lookup(admin)?;
1244			let freezer = T::Lookup::lookup(freezer)?;
1245			let id: T::AssetId = id.into();
1246
1247			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1248				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1249				ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1250				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1251
1252				details.issuer = issuer.clone();
1253				details.admin = admin.clone();
1254				details.freezer = freezer.clone();
1255
1256				Self::deposit_event(Event::TeamChanged { asset_id: id, issuer, admin, freezer });
1257				Ok(())
1258			})
1259		}
1260
1261		/// Set the metadata for an asset.
1262		///
1263		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1264		///
1265		/// Funds of sender are reserved according to the formula:
1266		/// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into
1267		/// account any already reserved funds.
1268		///
1269		/// - `id`: The identifier of the asset to update.
1270		/// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
1271		/// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
1272		/// - `decimals`: The number of decimals this asset uses to represent one unit.
1273		///
1274		/// Emits `MetadataSet`.
1275		///
1276		/// Weight: `O(1)`
1277		#[pallet::call_index(17)]
1278		#[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))]
1279		pub fn set_metadata(
1280			origin: OriginFor<T>,
1281			id: T::AssetIdParameter,
1282			name: Vec<u8>,
1283			symbol: Vec<u8>,
1284			decimals: u8,
1285		) -> DispatchResult {
1286			let origin = ensure_signed(origin)?;
1287			let id: T::AssetId = id.into();
1288			Self::do_set_metadata(id, &origin, name, symbol, decimals)
1289		}
1290
1291		/// Clear the metadata for an asset.
1292		///
1293		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
1294		///
1295		/// Any deposit is freed for the asset owner.
1296		///
1297		/// - `id`: The identifier of the asset to clear.
1298		///
1299		/// Emits `MetadataCleared`.
1300		///
1301		/// Weight: `O(1)`
1302		#[pallet::call_index(18)]
1303		pub fn clear_metadata(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1304			let origin = ensure_signed(origin)?;
1305			let id: T::AssetId = id.into();
1306
1307			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1308			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1309			ensure!(origin == d.owner, Error::<T, I>::NoPermission);
1310
1311			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1312				let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1313				T::Currency::unreserve(&d.owner, deposit);
1314				Self::deposit_event(Event::MetadataCleared { asset_id: id });
1315				Ok(())
1316			})
1317		}
1318
1319		/// Force the metadata for an asset to some value.
1320		///
1321		/// Origin must be ForceOrigin.
1322		///
1323		/// Any deposit is left alone.
1324		///
1325		/// - `id`: The identifier of the asset to update.
1326		/// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
1327		/// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
1328		/// - `decimals`: The number of decimals this asset uses to represent one unit.
1329		///
1330		/// Emits `MetadataSet`.
1331		///
1332		/// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively.
1333		#[pallet::call_index(19)]
1334		#[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))]
1335		pub fn force_set_metadata(
1336			origin: OriginFor<T>,
1337			id: T::AssetIdParameter,
1338			name: Vec<u8>,
1339			symbol: Vec<u8>,
1340			decimals: u8,
1341			is_frozen: bool,
1342		) -> DispatchResult {
1343			T::ForceOrigin::ensure_origin(origin)?;
1344			let id: T::AssetId = id.into();
1345
1346			let bounded_name: BoundedVec<u8, T::StringLimit> =
1347				name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1348
1349			let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1350				symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1351
1352			ensure!(Asset::<T, I>::contains_key(&id), Error::<T, I>::Unknown);
1353			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1354				let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1355				*metadata = Some(AssetMetadata {
1356					deposit,
1357					name: bounded_name,
1358					symbol: bounded_symbol,
1359					decimals,
1360					is_frozen,
1361				});
1362
1363				Self::deposit_event(Event::MetadataSet {
1364					asset_id: id,
1365					name,
1366					symbol,
1367					decimals,
1368					is_frozen,
1369				});
1370				Ok(())
1371			})
1372		}
1373
1374		/// Clear the metadata for an asset.
1375		///
1376		/// Origin must be ForceOrigin.
1377		///
1378		/// Any deposit is returned.
1379		///
1380		/// - `id`: The identifier of the asset to clear.
1381		///
1382		/// Emits `MetadataCleared`.
1383		///
1384		/// Weight: `O(1)`
1385		#[pallet::call_index(20)]
1386		pub fn force_clear_metadata(
1387			origin: OriginFor<T>,
1388			id: T::AssetIdParameter,
1389		) -> DispatchResult {
1390			T::ForceOrigin::ensure_origin(origin)?;
1391			let id: T::AssetId = id.into();
1392
1393			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1394			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1395				let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1396				T::Currency::unreserve(&d.owner, deposit);
1397				Self::deposit_event(Event::MetadataCleared { asset_id: id });
1398				Ok(())
1399			})
1400		}
1401
1402		/// Alter the attributes of a given asset.
1403		///
1404		/// Origin must be `ForceOrigin`.
1405		///
1406		/// - `id`: The identifier of the asset.
1407		/// - `owner`: The new Owner of this asset.
1408		/// - `issuer`: The new Issuer of this asset.
1409		/// - `admin`: The new Admin of this asset.
1410		/// - `freezer`: The new Freezer of this asset.
1411		/// - `min_balance`: The minimum balance of this new asset that any single account must
1412		/// have. If an account's balance is reduced below this, then it collapses to zero.
1413		/// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient
1414		/// value to account for the state bloat associated with its balance storage. If set to
1415		/// `true`, then non-zero balances may be stored without a `consumer` reference (and thus
1416		/// an ED in the Balances pallet or whatever else is used to control user-account state
1417		/// growth).
1418		/// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin
1419		/// instructions.
1420		///
1421		/// Emits `AssetStatusChanged` with the identity of the asset.
1422		///
1423		/// Weight: `O(1)`
1424		#[pallet::call_index(21)]
1425		pub fn force_asset_status(
1426			origin: OriginFor<T>,
1427			id: T::AssetIdParameter,
1428			owner: AccountIdLookupOf<T>,
1429			issuer: AccountIdLookupOf<T>,
1430			admin: AccountIdLookupOf<T>,
1431			freezer: AccountIdLookupOf<T>,
1432			#[pallet::compact] min_balance: T::Balance,
1433			is_sufficient: bool,
1434			is_frozen: bool,
1435		) -> DispatchResult {
1436			T::ForceOrigin::ensure_origin(origin)?;
1437			let id: T::AssetId = id.into();
1438
1439			Asset::<T, I>::try_mutate(id.clone(), |maybe_asset| {
1440				let mut asset = maybe_asset.take().ok_or(Error::<T, I>::Unknown)?;
1441				ensure!(asset.status != AssetStatus::Destroying, Error::<T, I>::AssetNotLive);
1442				asset.owner = T::Lookup::lookup(owner)?;
1443				asset.issuer = T::Lookup::lookup(issuer)?;
1444				asset.admin = T::Lookup::lookup(admin)?;
1445				asset.freezer = T::Lookup::lookup(freezer)?;
1446				asset.min_balance = min_balance;
1447				asset.is_sufficient = is_sufficient;
1448				if is_frozen {
1449					asset.status = AssetStatus::Frozen;
1450				} else {
1451					asset.status = AssetStatus::Live;
1452				}
1453				*maybe_asset = Some(asset);
1454
1455				Self::deposit_event(Event::AssetStatusChanged { asset_id: id });
1456				Ok(())
1457			})
1458		}
1459
1460		/// Approve an amount of asset for transfer by a delegated third-party account.
1461		///
1462		/// Origin must be Signed.
1463		///
1464		/// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account
1465		/// for the purpose of holding the approval. If some non-zero amount of assets is already
1466		/// approved from signing account to `delegate`, then it is topped up or unreserved to
1467		/// meet the right value.
1468		///
1469		/// NOTE: The signing account does not need to own `amount` of assets at the point of
1470		/// making this call.
1471		///
1472		/// - `id`: The identifier of the asset.
1473		/// - `delegate`: The account to delegate permission to transfer asset.
1474		/// - `amount`: The amount of asset that may be transferred by `delegate`. If there is
1475		/// already an approval in place, then this acts additively.
1476		///
1477		/// Emits `ApprovedTransfer` on success.
1478		///
1479		/// Weight: `O(1)`
1480		#[pallet::call_index(22)]
1481		pub fn approve_transfer(
1482			origin: OriginFor<T>,
1483			id: T::AssetIdParameter,
1484			delegate: AccountIdLookupOf<T>,
1485			#[pallet::compact] amount: T::Balance,
1486		) -> DispatchResult {
1487			let owner = ensure_signed(origin)?;
1488			let delegate = T::Lookup::lookup(delegate)?;
1489			let id: T::AssetId = id.into();
1490			Self::do_approve_transfer(id, &owner, &delegate, amount)
1491		}
1492
1493		/// Cancel all of some asset approved for delegated transfer by a third-party account.
1494		///
1495		/// Origin must be Signed and there must be an approval in place between signer and
1496		/// `delegate`.
1497		///
1498		/// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
1499		///
1500		/// - `id`: The identifier of the asset.
1501		/// - `delegate`: The account delegated permission to transfer asset.
1502		///
1503		/// Emits `ApprovalCancelled` on success.
1504		///
1505		/// Weight: `O(1)`
1506		#[pallet::call_index(23)]
1507		pub fn cancel_approval(
1508			origin: OriginFor<T>,
1509			id: T::AssetIdParameter,
1510			delegate: AccountIdLookupOf<T>,
1511		) -> DispatchResult {
1512			let owner = ensure_signed(origin)?;
1513			let delegate = T::Lookup::lookup(delegate)?;
1514			let id: T::AssetId = id.into();
1515			let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1516			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1517
1518			let approval = Approvals::<T, I>::take((id.clone(), &owner, &delegate))
1519				.ok_or(Error::<T, I>::Unknown)?;
1520			T::Currency::unreserve(&owner, approval.deposit);
1521
1522			d.approvals.saturating_dec();
1523			Asset::<T, I>::insert(id.clone(), d);
1524
1525			Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate });
1526			Ok(())
1527		}
1528
1529		/// Cancel all of some asset approved for delegated transfer by a third-party account.
1530		///
1531		/// Origin must be either ForceOrigin or Signed origin with the signer being the Admin
1532		/// account of the asset `id`.
1533		///
1534		/// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
1535		///
1536		/// - `id`: The identifier of the asset.
1537		/// - `delegate`: The account delegated permission to transfer asset.
1538		///
1539		/// Emits `ApprovalCancelled` on success.
1540		///
1541		/// Weight: `O(1)`
1542		#[pallet::call_index(24)]
1543		pub fn force_cancel_approval(
1544			origin: OriginFor<T>,
1545			id: T::AssetIdParameter,
1546			owner: AccountIdLookupOf<T>,
1547			delegate: AccountIdLookupOf<T>,
1548		) -> DispatchResult {
1549			let id: T::AssetId = id.into();
1550			let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1551			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1552			T::ForceOrigin::try_origin(origin)
1553				.map(|_| ())
1554				.or_else(|origin| -> DispatchResult {
1555					let origin = ensure_signed(origin)?;
1556					ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1557					Ok(())
1558				})?;
1559
1560			let owner = T::Lookup::lookup(owner)?;
1561			let delegate = T::Lookup::lookup(delegate)?;
1562
1563			let approval = Approvals::<T, I>::take((id.clone(), &owner, &delegate))
1564				.ok_or(Error::<T, I>::Unknown)?;
1565			T::Currency::unreserve(&owner, approval.deposit);
1566			d.approvals.saturating_dec();
1567			Asset::<T, I>::insert(id.clone(), d);
1568
1569			Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate });
1570			Ok(())
1571		}
1572
1573		/// Transfer some asset balance from a previously delegated account to some third-party
1574		/// account.
1575		///
1576		/// Origin must be Signed and there must be an approval in place by the `owner` to the
1577		/// signer.
1578		///
1579		/// If the entire amount approved for transfer is transferred, then any deposit previously
1580		/// reserved by `approve_transfer` is unreserved.
1581		///
1582		/// - `id`: The identifier of the asset.
1583		/// - `owner`: The account which previously approved for a transfer of at least `amount` and
1584		/// from which the asset balance will be withdrawn.
1585		/// - `destination`: The account to which the asset balance of `amount` will be transferred.
1586		/// - `amount`: The amount of assets to transfer.
1587		///
1588		/// Emits `TransferredApproved` on success.
1589		///
1590		/// Weight: `O(1)`
1591		#[pallet::call_index(25)]
1592		pub fn transfer_approved(
1593			origin: OriginFor<T>,
1594			id: T::AssetIdParameter,
1595			owner: AccountIdLookupOf<T>,
1596			destination: AccountIdLookupOf<T>,
1597			#[pallet::compact] amount: T::Balance,
1598		) -> DispatchResult {
1599			let delegate = ensure_signed(origin)?;
1600			let owner = T::Lookup::lookup(owner)?;
1601			let destination = T::Lookup::lookup(destination)?;
1602			let id: T::AssetId = id.into();
1603			Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
1604		}
1605
1606		/// Create an asset account for non-provider assets.
1607		///
1608		/// A deposit will be taken from the signer account.
1609		///
1610		/// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
1611		///   to be taken.
1612		/// - `id`: The identifier of the asset for the account to be created.
1613		///
1614		/// Emits `Touched` event when successful.
1615		#[pallet::call_index(26)]
1616		#[pallet::weight(T::WeightInfo::touch())]
1617		pub fn touch(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1618			let who = ensure_signed(origin)?;
1619			let id: T::AssetId = id.into();
1620			Self::do_touch(id, who.clone(), who)
1621		}
1622
1623		/// Return the deposit (if any) of an asset account or a consumer reference (if any) of an
1624		/// account.
1625		///
1626		/// The origin must be Signed.
1627		///
1628		/// - `id`: The identifier of the asset for which the caller would like the deposit
1629		///   refunded.
1630		/// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund.
1631		///
1632		/// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
1633		/// the asset account contains holds or freezes in place.
1634		///
1635		/// Emits `Refunded` event when successful.
1636		#[pallet::call_index(27)]
1637		#[pallet::weight(T::WeightInfo::refund())]
1638		pub fn refund(
1639			origin: OriginFor<T>,
1640			id: T::AssetIdParameter,
1641			allow_burn: bool,
1642		) -> DispatchResult {
1643			let id: T::AssetId = id.into();
1644			Self::do_refund(id, ensure_signed(origin)?, allow_burn)
1645		}
1646
1647		/// Sets the minimum balance of an asset.
1648		///
1649		/// Only works if there aren't any accounts that are holding the asset or if
1650		/// the new value of `min_balance` is less than the old one.
1651		///
1652		/// Origin must be Signed and the sender has to be the Owner of the
1653		/// asset `id`.
1654		///
1655		/// - `id`: The identifier of the asset.
1656		/// - `min_balance`: The new value of `min_balance`.
1657		///
1658		/// Emits `AssetMinBalanceChanged` event when successful.
1659		#[pallet::call_index(28)]
1660		pub fn set_min_balance(
1661			origin: OriginFor<T>,
1662			id: T::AssetIdParameter,
1663			min_balance: T::Balance,
1664		) -> DispatchResult {
1665			let origin = ensure_signed(origin)?;
1666			let id: T::AssetId = id.into();
1667
1668			let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1669			ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1670
1671			let old_min_balance = details.min_balance;
1672			// If the asset is marked as sufficient it won't be allowed to
1673			// change the min_balance.
1674			ensure!(!details.is_sufficient, Error::<T, I>::NoPermission);
1675
1676			// Ensure that either the new min_balance is less than old
1677			// min_balance or there aren't any accounts holding the asset.
1678			ensure!(
1679				min_balance < old_min_balance || details.accounts == 0,
1680				Error::<T, I>::NoPermission
1681			);
1682
1683			details.min_balance = min_balance;
1684			Asset::<T, I>::insert(&id, details);
1685
1686			Self::deposit_event(Event::AssetMinBalanceChanged {
1687				asset_id: id,
1688				new_min_balance: min_balance,
1689			});
1690			Ok(())
1691		}
1692
1693		/// Create an asset account for `who`.
1694		///
1695		/// A deposit will be taken from the signer account.
1696		///
1697		/// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
1698		///   to be taken.
1699		/// - `id`: The identifier of the asset for the account to be created, the asset status must
1700		///   be live.
1701		/// - `who`: The account to be created.
1702		///
1703		/// Emits `Touched` event when successful.
1704		#[pallet::call_index(29)]
1705		#[pallet::weight(T::WeightInfo::touch_other())]
1706		pub fn touch_other(
1707			origin: OriginFor<T>,
1708			id: T::AssetIdParameter,
1709			who: AccountIdLookupOf<T>,
1710		) -> DispatchResult {
1711			let origin = ensure_signed(origin)?;
1712			let who = T::Lookup::lookup(who)?;
1713			let id: T::AssetId = id.into();
1714			Self::do_touch(id, who, origin)
1715		}
1716
1717		/// Return the deposit (if any) of a target asset account. Useful if you are the depositor.
1718		///
1719		/// The origin must be Signed and either the account owner, depositor, or asset `Admin`. In
1720		/// order to burn a non-zero balance of the asset, the caller must be the account and should
1721		/// use `refund`.
1722		///
1723		/// - `id`: The identifier of the asset for the account holding a deposit.
1724		/// - `who`: The account to refund.
1725		///
1726		/// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
1727		/// the asset account contains holds or freezes in place.
1728		///
1729		/// Emits `Refunded` event when successful.
1730		#[pallet::call_index(30)]
1731		#[pallet::weight(T::WeightInfo::refund_other())]
1732		pub fn refund_other(
1733			origin: OriginFor<T>,
1734			id: T::AssetIdParameter,
1735			who: AccountIdLookupOf<T>,
1736		) -> DispatchResult {
1737			let origin = ensure_signed(origin)?;
1738			let who = T::Lookup::lookup(who)?;
1739			let id: T::AssetId = id.into();
1740			Self::do_refund_other(id, &who, Some(origin))
1741		}
1742
1743		/// Disallow further unprivileged transfers of an asset `id` to and from an account `who`.
1744		///
1745		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1746		///
1747		/// - `id`: The identifier of the account's asset.
1748		/// - `who`: The account to be unblocked.
1749		///
1750		/// Emits `Blocked`.
1751		///
1752		/// Weight: `O(1)`
1753		#[pallet::call_index(31)]
1754		pub fn block(
1755			origin: OriginFor<T>,
1756			id: T::AssetIdParameter,
1757			who: AccountIdLookupOf<T>,
1758		) -> DispatchResult {
1759			let origin = ensure_signed(origin)?;
1760			let id: T::AssetId = id.into();
1761
1762			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1763			ensure!(
1764				d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1765				Error::<T, I>::IncorrectStatus
1766			);
1767			ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1768			let who = T::Lookup::lookup(who)?;
1769
1770			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1771				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1772					AccountStatus::Blocked;
1773				Ok(())
1774			})?;
1775
1776			Self::deposit_event(Event::<T, I>::Blocked { asset_id: id, who });
1777			Ok(())
1778		}
1779
1780		/// Transfer the entire transferable balance from the caller asset account.
1781		///
1782		/// NOTE: This function only attempts to transfer _transferable_ balances. This means that
1783		/// any held, frozen, or minimum balance (when `keep_alive` is `true`), will not be
1784		/// transferred by this function. To ensure that this function results in a killed account,
1785		/// you might need to prepare the account by removing any reference counters, storage
1786		/// deposits, etc...
1787		///
1788		/// The dispatch origin of this call must be Signed.
1789		///
1790		/// - `id`: The identifier of the asset for the account holding a deposit.
1791		/// - `dest`: The recipient of the transfer.
1792		/// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all
1793		///   of the funds the asset account has, causing the sender asset account to be killed
1794		///   (false), or transfer everything except at least the minimum balance, which will
1795		///   guarantee to keep the sender asset account alive (true).
1796		#[pallet::call_index(32)]
1797		#[pallet::weight(T::WeightInfo::transfer_all())]
1798		pub fn transfer_all(
1799			origin: OriginFor<T>,
1800			id: T::AssetIdParameter,
1801			dest: AccountIdLookupOf<T>,
1802			keep_alive: bool,
1803		) -> DispatchResult {
1804			let transactor = ensure_signed(origin)?;
1805			let keep_alive = if keep_alive { Preserve } else { Expendable };
1806			let reducible_balance = <Self as fungibles::Inspect<_>>::reducible_balance(
1807				id.clone().into(),
1808				&transactor,
1809				keep_alive,
1810				Fortitude::Polite,
1811			);
1812			let dest = T::Lookup::lookup(dest)?;
1813			<Self as fungibles::Mutate<_>>::transfer(
1814				id.into(),
1815				&transactor,
1816				&dest,
1817				reducible_balance,
1818				keep_alive,
1819			)?;
1820			Ok(())
1821		}
1822	}
1823
1824	/// Implements [`AccountTouch`] trait.
1825	/// Note that a depositor can be any account, without any specific privilege.
1826	impl<T: Config<I>, I: 'static> AccountTouch<T::AssetId, T::AccountId> for Pallet<T, I> {
1827		type Balance = DepositBalanceOf<T, I>;
1828
1829		fn deposit_required(_: T::AssetId) -> Self::Balance {
1830			T::AssetAccountDeposit::get()
1831		}
1832
1833		fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
1834			match Asset::<T, I>::get(&asset) {
1835				// refer to the [`Self::new_account`] function for more details.
1836				Some(info) if info.is_sufficient => false,
1837				Some(_) if frame_system::Pallet::<T>::can_accrue_consumers(who, 2) => false,
1838				Some(_) => !Account::<T, I>::contains_key(asset, who),
1839				_ => true,
1840			}
1841		}
1842
1843		fn touch(
1844			asset: T::AssetId,
1845			who: &T::AccountId,
1846			depositor: &T::AccountId,
1847		) -> DispatchResult {
1848			Self::do_touch(asset, who.clone(), depositor.clone())
1849		}
1850	}
1851
1852	/// Implements [`ContainsPair`] trait for a pair of asset and account IDs.
1853	impl<T: Config<I>, I: 'static> ContainsPair<T::AssetId, T::AccountId> for Pallet<T, I> {
1854		/// Check if an account with the given asset ID and account address exists.
1855		fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool {
1856			Account::<T, I>::contains_key(asset, who)
1857		}
1858	}
1859}
1860
1861sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);