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
//!
98
//! ### Permissioned Functions
99
//!
100
//! * `force_create`: Creates a new asset class without taking any deposit.
101
//! * `force_set_metadata`: Set the metadata of an asset class.
102
//! * `force_clear_metadata`: Remove the metadata of an asset class.
103
//! * `force_asset_status`: Alter an asset class's attributes.
104
//! * `force_cancel_approval`: Rescind a previous approval.
105
//!
106
//! ### Privileged Functions
107
//!
108
//! * `destroy`: Destroys an entire asset class; called by the asset class's Owner.
109
//! * `mint`: Increases the asset balance of an account; called by the asset class's Issuer.
110
//! * `burn`: Decreases the asset balance of an account; called by the asset class's Admin.
111
//! * `force_transfer`: Transfers between arbitrary accounts; called by the asset class's Admin.
112
//! * `freeze`: Disallows further `transfer`s from an account; called by the asset class's Freezer.
113
//! * `thaw`: Allows further `transfer`s to and from an account; called by the asset class's Admin.
114
//! * `transfer_ownership`: Changes an asset class's Owner; called by the asset class's Owner.
115
//! * `set_team`: Changes an asset class's Admin, Freezer and Issuer; called by the asset class's
116
//!   Owner.
117
//! * `set_metadata`: Set the metadata of an asset class; called by the asset class's Owner.
118
//! * `clear_metadata`: Remove the metadata of an asset class; called by the asset class's Owner.
119
//! * `touch_other`: Create an asset account for specified account. Caller must place a deposit;
120
//!   called by the asset class's Freezer or Admin.
121
//! * `block`: Disallows further `transfer`s to and from an account; called by the asset class's
122
//!   Freezer.
123
//!
124
//! Please refer to the [`Call`] enum and its associated variants for documentation on each
125
//! function.
126
//!
127
//! ### Public Functions
128
//! <!-- Original author of descriptions: @gavofyork -->
129
//!
130
//! * `balance` - Get the asset `id` balance of `who`.
131
//! * `total_supply` - Get the total supply of an asset `id`.
132
//!
133
//! Please refer to the [`Pallet`] struct for details on publicly available functions.
134
//!
135
//! ### Callbacks
136
//!
137
//! Using `CallbackHandle` associated type, user can configure custom callback functions which are
138
//! executed when new asset is created or an existing asset is destroyed.
139
//!
140
//! ## Related Modules
141
//!
142
//! * [`System`](../frame_system/index.html)
143
//! * [`Support`](../frame_support/index.html)
144
//!
145
//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html
146

            
147
// This recursion limit is needed because we have too many benchmarks and benchmarking will fail if
148
// we add more without this limit.
149
#![recursion_limit = "1024"]
150
// Ensure we're `no_std` when compiling for Wasm.
151
#![cfg_attr(not(feature = "std"), no_std)]
152

            
153
#[cfg(feature = "runtime-benchmarks")]
154
pub mod benchmarking;
155
pub mod migration;
156
#[cfg(test)]
157
pub mod mock;
158
#[cfg(test)]
159
mod tests;
160
pub mod weights;
161

            
162
mod extra_mutator;
163
pub use extra_mutator::*;
164
mod functions;
165
mod impl_fungibles;
166
mod impl_stored_map;
167
mod types;
168
pub use types::*;
169

            
170
extern crate alloc;
171

            
172
use scale_info::TypeInfo;
173
use sp_runtime::{
174
	traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Saturating, StaticLookup, Zero},
175
	ArithmeticError, DispatchError, TokenError,
176
};
177

            
178
use alloc::vec::Vec;
179
use core::marker::PhantomData;
180
use frame_support::{
181
	dispatch::DispatchResult,
182
	ensure,
183
	pallet_prelude::DispatchResultWithPostInfo,
184
	storage::KeyPrefixIterator,
185
	traits::{
186
		tokens::{fungibles, DepositConsequence, WithdrawConsequence},
187
		BalanceStatus::Reserved,
188
		Currency, EnsureOriginWithArg, Incrementable, ReservableCurrency, StoredMap,
189
	},
190
};
191
use frame_system::Config as SystemConfig;
192

            
193
pub use pallet::*;
194
pub use weights::WeightInfo;
195

            
196
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
197
const LOG_TARGET: &str = "runtime::assets";
198

            
199
/// Trait with callbacks that are executed after successful asset creation or destruction.
200
pub trait AssetsCallback<AssetId, AccountId> {
201
	/// Indicates that asset with `id` was successfully created by the `owner`
202
	fn created(_id: &AssetId, _owner: &AccountId) -> Result<(), ()> {
203
		Ok(())
204
	}
205

            
206
	/// Indicates that asset with `id` has just been destroyed
207
	fn destroyed(_id: &AssetId) -> Result<(), ()> {
208
		Ok(())
209
	}
210
}
211

            
212
#[impl_trait_for_tuples::impl_for_tuples(10)]
213
impl<AssetId, AccountId> AssetsCallback<AssetId, AccountId> for Tuple {
214
	fn created(id: &AssetId, owner: &AccountId) -> Result<(), ()> {
215
		for_tuples!( #( Tuple::created(id, owner)?; )* );
216
		Ok(())
217
	}
218

            
219
	fn destroyed(id: &AssetId) -> Result<(), ()> {
220
		for_tuples!( #( Tuple::destroyed(id)?; )* );
221
		Ok(())
222
	}
223
}
224

            
225
/// Auto-increment the [`NextAssetId`] when an asset is created.
226
///
227
/// This has not effect if the [`NextAssetId`] value is not present.
228
pub struct AutoIncAssetId<T, I = ()>(PhantomData<(T, I)>);
229
impl<T: Config<I>, I> AssetsCallback<T::AssetId, T::AccountId> for AutoIncAssetId<T, I>
230
where
231
	T::AssetId: Incrementable,
232
{
233
	fn created(_: &T::AssetId, _: &T::AccountId) -> Result<(), ()> {
234
		let Some(next_id) = NextAssetId::<T, I>::get() else {
235
			// Auto increment for the asset id is not enabled.
236
			return Ok(());
237
		};
238
		let next_id = next_id.increment().ok_or(())?;
239
		NextAssetId::<T, I>::put(next_id);
240
		Ok(())
241
	}
242
}
243

            
244
#[frame_support::pallet]
245
pub mod pallet {
246
	use super::*;
247
	use frame_support::{
248
		pallet_prelude::*,
249
		traits::{AccountTouch, ContainsPair},
250
	};
251
	use frame_system::pallet_prelude::*;
252

            
253
	/// The in-code storage version.
254
	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
255

            
256
	#[pallet::pallet]
257
	#[pallet::storage_version(STORAGE_VERSION)]
258
	pub struct Pallet<T, I = ()>(_);
259

            
260
	#[cfg(feature = "runtime-benchmarks")]
261
	pub trait BenchmarkHelper<AssetIdParameter> {
262
		fn create_asset_id_parameter(id: u32) -> AssetIdParameter;
263
	}
264
	#[cfg(feature = "runtime-benchmarks")]
265
	impl<AssetIdParameter: From<u32>> BenchmarkHelper<AssetIdParameter> for () {
266
		fn create_asset_id_parameter(id: u32) -> AssetIdParameter {
267
			id.into()
268
		}
269
	}
270

            
271
	/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
272
	pub mod config_preludes {
273
		use super::*;
274
		use frame_support::{derive_impl, traits::ConstU64};
275
		pub struct TestDefaultConfig;
276

            
277
		#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
278
		impl frame_system::DefaultConfig for TestDefaultConfig {}
279

            
280
		#[frame_support::register_default_impl(TestDefaultConfig)]
281
		impl DefaultConfig for TestDefaultConfig {
282
			#[inject_runtime_type]
283
			type RuntimeEvent = ();
284
			type Balance = u64;
285
			type RemoveItemsLimit = ConstU32<5>;
286
			type AssetId = u32;
287
			type AssetIdParameter = u32;
288
			type AssetDeposit = ConstU64<1>;
289
			type AssetAccountDeposit = ConstU64<10>;
290
			type MetadataDepositBase = ConstU64<1>;
291
			type MetadataDepositPerByte = ConstU64<1>;
292
			type ApprovalDeposit = ConstU64<1>;
293
			type StringLimit = ConstU32<50>;
294
			type Extra = ();
295
			type CallbackHandle = ();
296
			type WeightInfo = ();
297
			#[cfg(feature = "runtime-benchmarks")]
298
			type BenchmarkHelper = ();
299
		}
300
	}
301

            
302
	#[pallet::config(with_default)]
303
	/// The module configuration trait.
304
	pub trait Config<I: 'static = ()>: frame_system::Config {
305
		/// The overarching event type.
306
		#[pallet::no_default_bounds]
307
		type RuntimeEvent: From<Event<Self, I>>
308
			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
309

            
310
		/// The units in which we record balances.
311
		type Balance: Member
312
			+ Parameter
313
			+ AtLeast32BitUnsigned
314
			+ Default
315
			+ Copy
316
			+ MaybeSerializeDeserialize
317
			+ MaxEncodedLen
318
			+ TypeInfo;
319

            
320
		/// Max number of items to destroy per `destroy_accounts` and `destroy_approvals` call.
321
		///
322
		/// Must be configured to result in a weight that makes each call fit in a block.
323
		#[pallet::constant]
324
		type RemoveItemsLimit: Get<u32>;
325

            
326
		/// Identifier for the class of asset.
327
		type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
328

            
329
		/// Wrapper around `Self::AssetId` to use in dispatchable call signatures. Allows the use
330
		/// of compact encoding in instances of the pallet, which will prevent breaking changes
331
		/// resulting from the removal of `HasCompact` from `Self::AssetId`.
332
		///
333
		/// This type includes the `From<Self::AssetId>` bound, since tightly coupled pallets may
334
		/// want to convert an `AssetId` into a parameter for calling dispatchable functions
335
		/// directly.
336
		type AssetIdParameter: Parameter + From<Self::AssetId> + Into<Self::AssetId> + MaxEncodedLen;
337

            
338
		/// The currency mechanism.
339
		#[pallet::no_default]
340
		type Currency: ReservableCurrency<Self::AccountId>;
341

            
342
		/// Standard asset class creation is only allowed if the origin attempting it and the
343
		/// asset class are in this set.
344
		#[pallet::no_default]
345
		type CreateOrigin: EnsureOriginWithArg<
346
			Self::RuntimeOrigin,
347
			Self::AssetId,
348
			Success = Self::AccountId,
349
		>;
350

            
351
		/// The origin which may forcibly create or destroy an asset or otherwise alter privileged
352
		/// attributes.
353
		#[pallet::no_default]
354
		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
355

            
356
		/// The basic amount of funds that must be reserved for an asset.
357
		#[pallet::constant]
358
		#[pallet::no_default_bounds]
359
		type AssetDeposit: Get<DepositBalanceOf<Self, I>>;
360

            
361
		/// The amount of funds that must be reserved for a non-provider asset account to be
362
		/// maintained.
363
		#[pallet::constant]
364
		#[pallet::no_default_bounds]
365
		type AssetAccountDeposit: Get<DepositBalanceOf<Self, I>>;
366

            
367
		/// The basic amount of funds that must be reserved when adding metadata to your asset.
368
		#[pallet::constant]
369
		#[pallet::no_default_bounds]
370
		type MetadataDepositBase: Get<DepositBalanceOf<Self, I>>;
371

            
372
		/// The additional funds that must be reserved for the number of bytes you store in your
373
		/// metadata.
374
		#[pallet::constant]
375
		#[pallet::no_default_bounds]
376
		type MetadataDepositPerByte: Get<DepositBalanceOf<Self, I>>;
377

            
378
		/// The amount of funds that must be reserved when creating a new approval.
379
		#[pallet::constant]
380
		#[pallet::no_default_bounds]
381
		type ApprovalDeposit: Get<DepositBalanceOf<Self, I>>;
382

            
383
		/// The maximum length of a name or symbol stored on-chain.
384
		#[pallet::constant]
385
		type StringLimit: Get<u32>;
386

            
387
		/// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be
388
		/// respected in all permissionless operations.
389
		#[pallet::no_default]
390
		type Freezer: FrozenBalance<Self::AssetId, Self::AccountId, Self::Balance>;
391

            
392
		/// Additional data to be stored with an account's asset balance.
393
		type Extra: Member + Parameter + Default + MaxEncodedLen;
394

            
395
		/// Callback methods for asset state change (e.g. asset created or destroyed)
396
		///
397
		/// Types implementing the [`AssetsCallback`] can be chained when listed together as a
398
		/// tuple.
399
		/// The [`AutoIncAssetId`] callback, in conjunction with the [`NextAssetId`], can be
400
		/// used to set up auto-incrementing asset IDs for this collection.
401
		type CallbackHandle: AssetsCallback<Self::AssetId, Self::AccountId>;
402

            
403
		/// Weight information for extrinsics in this pallet.
404
		type WeightInfo: WeightInfo;
405

            
406
		/// Helper trait for benchmarks.
407
		#[cfg(feature = "runtime-benchmarks")]
408
		type BenchmarkHelper: BenchmarkHelper<Self::AssetIdParameter>;
409
	}
410

            
411
	#[pallet::storage]
412
	/// Details of an asset.
413
	pub(super) type Asset<T: Config<I>, I: 'static = ()> = StorageMap<
414
		_,
415
		Blake2_128Concat,
416
		T::AssetId,
417
		AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
418
	>;
419

            
420
	#[pallet::storage]
421
	/// The holdings of a specific account for a specific asset.
422
	pub(super) type Account<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
423
		_,
424
		Blake2_128Concat,
425
		T::AssetId,
426
		Blake2_128Concat,
427
		T::AccountId,
428
		AssetAccountOf<T, I>,
429
	>;
430

            
431
	#[pallet::storage]
432
	/// Approved balance transfers. First balance is the amount approved for transfer. Second
433
	/// is the amount of `T::Currency` reserved for storing this.
434
	/// First key is the asset ID, second key is the owner and third key is the delegate.
435
	pub(super) type Approvals<T: Config<I>, I: 'static = ()> = StorageNMap<
436
		_,
437
		(
438
			NMapKey<Blake2_128Concat, T::AssetId>,
439
			NMapKey<Blake2_128Concat, T::AccountId>, // owner
440
			NMapKey<Blake2_128Concat, T::AccountId>, // delegate
441
		),
442
		Approval<T::Balance, DepositBalanceOf<T, I>>,
443
	>;
444

            
445
	#[pallet::storage]
446
	/// Metadata of an asset.
447
	pub(super) type Metadata<T: Config<I>, I: 'static = ()> = StorageMap<
448
		_,
449
		Blake2_128Concat,
450
		T::AssetId,
451
		AssetMetadata<DepositBalanceOf<T, I>, BoundedVec<u8, T::StringLimit>>,
452
		ValueQuery,
453
	>;
454

            
455
	/// The asset ID enforced for the next asset creation, if any present. Otherwise, this storage
456
	/// item has no effect.
457
	///
458
	/// This can be useful for setting up constraints for IDs of the new assets. For example, by
459
	/// providing an initial [`NextAssetId`] and using the [`crate::AutoIncAssetId`] callback, an
460
	/// auto-increment model can be applied to all new asset IDs.
461
	///
462
	/// The initial next asset ID can be set using the [`GenesisConfig`] or the
463
	/// [SetNextAssetId](`migration::next_asset_id::SetNextAssetId`) migration.
464
	#[pallet::storage]
465
	pub type NextAssetId<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AssetId, OptionQuery>;
466

            
467
	#[pallet::genesis_config]
468
	#[derive(frame_support::DefaultNoBound)]
469
	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
470
		/// Genesis assets: id, owner, is_sufficient, min_balance
471
		pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>,
472
		/// Genesis metadata: id, name, symbol, decimals
473
		pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>,
474
		/// Genesis accounts: id, account_id, balance
475
		pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>,
476
		/// Genesis [`NextAssetId`].
477
		///
478
		/// Refer to the [`NextAssetId`] item for more information.
479
		///
480
		/// This does not enforce the asset ID for the [assets](`GenesisConfig::assets`) within the
481
		/// genesis config. It sets the [`NextAssetId`] after they have been created.
482
		pub next_asset_id: Option<T::AssetId>,
483
	}
484

            
485
	#[pallet::genesis_build]
486
	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
487
		fn build(&self) {
488
			for (id, owner, is_sufficient, min_balance) in &self.assets {
489
				assert!(!Asset::<T, I>::contains_key(id), "Asset id already in use");
490
				assert!(!min_balance.is_zero(), "Min balance should not be zero");
491
				Asset::<T, I>::insert(
492
					id,
493
					AssetDetails {
494
						owner: owner.clone(),
495
						issuer: owner.clone(),
496
						admin: owner.clone(),
497
						freezer: owner.clone(),
498
						supply: Zero::zero(),
499
						deposit: Zero::zero(),
500
						min_balance: *min_balance,
501
						is_sufficient: *is_sufficient,
502
						accounts: 0,
503
						sufficients: 0,
504
						approvals: 0,
505
						status: AssetStatus::Live,
506
					},
507
				);
508
			}
509

            
510
			for (id, name, symbol, decimals) in &self.metadata {
511
				assert!(Asset::<T, I>::contains_key(id), "Asset does not exist");
512

            
513
				let bounded_name: BoundedVec<u8, T::StringLimit> =
514
					name.clone().try_into().expect("asset name is too long");
515
				let bounded_symbol: BoundedVec<u8, T::StringLimit> =
516
					symbol.clone().try_into().expect("asset symbol is too long");
517

            
518
				let metadata = AssetMetadata {
519
					deposit: Zero::zero(),
520
					name: bounded_name,
521
					symbol: bounded_symbol,
522
					decimals: *decimals,
523
					is_frozen: false,
524
				};
525
				Metadata::<T, I>::insert(id, metadata);
526
			}
527

            
528
			for (id, account_id, amount) in &self.accounts {
529
				let result = <Pallet<T, I>>::increase_balance(
530
					id.clone(),
531
					account_id,
532
					*amount,
533
					|details| -> DispatchResult {
534
						debug_assert!(
535
							details.supply.checked_add(&amount).is_some(),
536
							"checked in prep; qed"
537
						);
538
						details.supply = details.supply.saturating_add(*amount);
539
						Ok(())
540
					},
541
				);
542
				assert!(result.is_ok());
543
			}
544

            
545
			if let Some(next_asset_id) = &self.next_asset_id {
546
				NextAssetId::<T, I>::put(next_asset_id);
547
			}
548
		}
549
	}
550

            
551
	#[pallet::event]
552
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
553
	pub enum Event<T: Config<I>, I: 'static = ()> {
554
		/// Some asset class was created.
555
		Created { asset_id: T::AssetId, creator: T::AccountId, owner: T::AccountId },
556
		/// Some assets were issued.
557
		Issued { asset_id: T::AssetId, owner: T::AccountId, amount: T::Balance },
558
		/// Some assets were transferred.
559
		Transferred {
560
			asset_id: T::AssetId,
561
			from: T::AccountId,
562
			to: T::AccountId,
563
			amount: T::Balance,
564
		},
565
		/// Some assets were destroyed.
566
		Burned { asset_id: T::AssetId, owner: T::AccountId, balance: T::Balance },
567
		/// The management team changed.
568
		TeamChanged {
569
			asset_id: T::AssetId,
570
			issuer: T::AccountId,
571
			admin: T::AccountId,
572
			freezer: T::AccountId,
573
		},
574
		/// The owner changed.
575
		OwnerChanged { asset_id: T::AssetId, owner: T::AccountId },
576
		/// Some account `who` was frozen.
577
		Frozen { asset_id: T::AssetId, who: T::AccountId },
578
		/// Some account `who` was thawed.
579
		Thawed { asset_id: T::AssetId, who: T::AccountId },
580
		/// Some asset `asset_id` was frozen.
581
		AssetFrozen { asset_id: T::AssetId },
582
		/// Some asset `asset_id` was thawed.
583
		AssetThawed { asset_id: T::AssetId },
584
		/// Accounts were destroyed for given asset.
585
		AccountsDestroyed { asset_id: T::AssetId, accounts_destroyed: u32, accounts_remaining: u32 },
586
		/// Approvals were destroyed for given asset.
587
		ApprovalsDestroyed {
588
			asset_id: T::AssetId,
589
			approvals_destroyed: u32,
590
			approvals_remaining: u32,
591
		},
592
		/// An asset class is in the process of being destroyed.
593
		DestructionStarted { asset_id: T::AssetId },
594
		/// An asset class was destroyed.
595
		Destroyed { asset_id: T::AssetId },
596
		/// Some asset class was force-created.
597
		ForceCreated { asset_id: T::AssetId, owner: T::AccountId },
598
		/// New metadata has been set for an asset.
599
		MetadataSet {
600
			asset_id: T::AssetId,
601
			name: Vec<u8>,
602
			symbol: Vec<u8>,
603
			decimals: u8,
604
			is_frozen: bool,
605
		},
606
		/// Metadata has been cleared for an asset.
607
		MetadataCleared { asset_id: T::AssetId },
608
		/// (Additional) funds have been approved for transfer to a destination account.
609
		ApprovedTransfer {
610
			asset_id: T::AssetId,
611
			source: T::AccountId,
612
			delegate: T::AccountId,
613
			amount: T::Balance,
614
		},
615
		/// An approval for account `delegate` was cancelled by `owner`.
616
		ApprovalCancelled { asset_id: T::AssetId, owner: T::AccountId, delegate: T::AccountId },
617
		/// An `amount` was transferred in its entirety from `owner` to `destination` by
618
		/// the approved `delegate`.
619
		TransferredApproved {
620
			asset_id: T::AssetId,
621
			owner: T::AccountId,
622
			delegate: T::AccountId,
623
			destination: T::AccountId,
624
			amount: T::Balance,
625
		},
626
		/// An asset has had its attributes changed by the `Force` origin.
627
		AssetStatusChanged { asset_id: T::AssetId },
628
		/// The min_balance of an asset has been updated by the asset owner.
629
		AssetMinBalanceChanged { asset_id: T::AssetId, new_min_balance: T::Balance },
630
		/// Some account `who` was created with a deposit from `depositor`.
631
		Touched { asset_id: T::AssetId, who: T::AccountId, depositor: T::AccountId },
632
		/// Some account `who` was blocked.
633
		Blocked { asset_id: T::AssetId, who: T::AccountId },
634
		/// Some assets were deposited (e.g. for transaction fees).
635
		Deposited { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
636
		/// Some assets were withdrawn from the account (e.g. for transaction fees).
637
		Withdrawn { asset_id: T::AssetId, who: T::AccountId, amount: T::Balance },
638
	}
639

            
640
	#[pallet::error]
641
	pub enum Error<T, I = ()> {
642
		/// Account balance must be greater than or equal to the transfer amount.
643
		BalanceLow,
644
		/// The account to alter does not exist.
645
		NoAccount,
646
		/// The signing account has no permission to do the operation.
647
		NoPermission,
648
		/// The given asset ID is unknown.
649
		Unknown,
650
		/// The origin account is frozen.
651
		Frozen,
652
		/// The asset ID is already taken.
653
		InUse,
654
		/// Invalid witness data given.
655
		BadWitness,
656
		/// Minimum balance should be non-zero.
657
		MinBalanceZero,
658
		/// Unable to increment the consumer reference counters on the account. Either no provider
659
		/// reference exists to allow a non-zero balance of a non-self-sufficient asset, or one
660
		/// fewer then the maximum number of consumers has been reached.
661
		UnavailableConsumer,
662
		/// Invalid metadata given.
663
		BadMetadata,
664
		/// No approval exists that would allow the transfer.
665
		Unapproved,
666
		/// The source account would not survive the transfer and it needs to stay alive.
667
		WouldDie,
668
		/// The asset-account already exists.
669
		AlreadyExists,
670
		/// The asset-account doesn't have an associated deposit.
671
		NoDeposit,
672
		/// The operation would result in funds being burned.
673
		WouldBurn,
674
		/// The asset is a live asset and is actively being used. Usually emit for operations such
675
		/// as `start_destroy` which require the asset to be in a destroying state.
676
		LiveAsset,
677
		/// The asset is not live, and likely being destroyed.
678
		AssetNotLive,
679
		/// The asset status is not the expected status.
680
		IncorrectStatus,
681
		/// The asset should be frozen before the given operation.
682
		NotFrozen,
683
		/// Callback action resulted in error
684
		CallbackFailed,
685
		/// The asset ID must be equal to the [`NextAssetId`].
686
		BadAssetId,
687
	}
688

            
689
	#[pallet::call(weight(<T as Config<I>>::WeightInfo))]
690
	impl<T: Config<I>, I: 'static> Pallet<T, I> {
691
		/// Issue a new class of fungible assets from a public origin.
692
		///
693
		/// This new asset class has no assets initially and its owner is the origin.
694
		///
695
		/// The origin must conform to the configured `CreateOrigin` and have sufficient funds free.
696
		///
697
		/// Funds of sender are reserved by `AssetDeposit`.
698
		///
699
		/// Parameters:
700
		/// - `id`: The identifier of the new asset. This must not be currently in use to identify
701
		/// an existing asset. If [`NextAssetId`] is set, then this must be equal to it.
702
		/// - `admin`: The admin of this class of assets. The admin is the initial address of each
703
		/// member of the asset class's admin team.
704
		/// - `min_balance`: The minimum balance of this new asset that any single account must
705
		/// have. If an account's balance is reduced below this, then it collapses to zero.
706
		///
707
		/// Emits `Created` event when successful.
708
		///
709
		/// Weight: `O(1)`
710
		#[pallet::call_index(0)]
711
		pub fn create(
712
			origin: OriginFor<T>,
713
			id: T::AssetIdParameter,
714
			admin: AccountIdLookupOf<T>,
715
			min_balance: T::Balance,
716
		) -> DispatchResult {
717
			let id: T::AssetId = id.into();
718
			let owner = T::CreateOrigin::ensure_origin(origin, &id)?;
719
			let admin = T::Lookup::lookup(admin)?;
720

            
721
			ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
722
			ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
723

            
724
			if let Some(next_id) = NextAssetId::<T, I>::get() {
725
				ensure!(id == next_id, Error::<T, I>::BadAssetId);
726
			}
727

            
728
			let deposit = T::AssetDeposit::get();
729
			T::Currency::reserve(&owner, deposit)?;
730

            
731
			Asset::<T, I>::insert(
732
				id.clone(),
733
				AssetDetails {
734
					owner: owner.clone(),
735
					issuer: admin.clone(),
736
					admin: admin.clone(),
737
					freezer: admin.clone(),
738
					supply: Zero::zero(),
739
					deposit,
740
					min_balance,
741
					is_sufficient: false,
742
					accounts: 0,
743
					sufficients: 0,
744
					approvals: 0,
745
					status: AssetStatus::Live,
746
				},
747
			);
748
			ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
749
			Self::deposit_event(Event::Created {
750
				asset_id: id,
751
				creator: owner.clone(),
752
				owner: admin,
753
			});
754

            
755
			Ok(())
756
		}
757

            
758
		/// Issue a new class of fungible assets from a privileged origin.
759
		///
760
		/// This new asset class has no assets initially.
761
		///
762
		/// The origin must conform to `ForceOrigin`.
763
		///
764
		/// Unlike `create`, no funds are reserved.
765
		///
766
		/// - `id`: The identifier of the new asset. This must not be currently in use to identify
767
		/// an existing asset. If [`NextAssetId`] is set, then this must be equal to it.
768
		/// - `owner`: The owner of this class of assets. The owner has full superuser permissions
769
		/// over this asset, but may later change and configure the permissions using
770
		/// `transfer_ownership` and `set_team`.
771
		/// - `min_balance`: The minimum balance of this new asset that any single account must
772
		/// have. If an account's balance is reduced below this, then it collapses to zero.
773
		///
774
		/// Emits `ForceCreated` event when successful.
775
		///
776
		/// Weight: `O(1)`
777
		#[pallet::call_index(1)]
778
		pub fn force_create(
779
			origin: OriginFor<T>,
780
			id: T::AssetIdParameter,
781
			owner: AccountIdLookupOf<T>,
782
			is_sufficient: bool,
783
			#[pallet::compact] min_balance: T::Balance,
784
		) -> DispatchResult {
785
			T::ForceOrigin::ensure_origin(origin)?;
786
			let owner = T::Lookup::lookup(owner)?;
787
			let id: T::AssetId = id.into();
788
			Self::do_force_create(id, owner, is_sufficient, min_balance)
789
		}
790

            
791
		/// Start the process of destroying a fungible asset class.
792
		///
793
		/// `start_destroy` is the first in a series of extrinsics that should be called, to allow
794
		/// destruction of an asset class.
795
		///
796
		/// The origin must conform to `ForceOrigin` or must be `Signed` by the asset's `owner`.
797
		///
798
		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
799
		///   asset.
800
		///
801
		/// The asset class must be frozen before calling `start_destroy`.
802
		#[pallet::call_index(2)]
803
		pub fn start_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
804
			let maybe_check_owner = match T::ForceOrigin::try_origin(origin) {
805
				Ok(_) => None,
806
				Err(origin) => Some(ensure_signed(origin)?),
807
			};
808
			let id: T::AssetId = id.into();
809
			Self::do_start_destroy(id, maybe_check_owner)
810
		}
811

            
812
		/// Destroy all accounts associated with a given asset.
813
		///
814
		/// `destroy_accounts` should only be called after `start_destroy` has been called, and the
815
		/// asset is in a `Destroying` state.
816
		///
817
		/// Due to weight restrictions, this function may need to be called multiple times to fully
818
		/// destroy all accounts. It will destroy `RemoveItemsLimit` accounts at a time.
819
		///
820
		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
821
		///   asset.
822
		///
823
		/// Each call emits the `Event::DestroyedAccounts` event.
824
		#[pallet::call_index(3)]
825
		#[pallet::weight(T::WeightInfo::destroy_accounts(T::RemoveItemsLimit::get()))]
826
		pub fn destroy_accounts(
827
			origin: OriginFor<T>,
828
			id: T::AssetIdParameter,
829
		) -> DispatchResultWithPostInfo {
830
			let _ = ensure_signed(origin)?;
831
			let id: T::AssetId = id.into();
832
			let removed_accounts = Self::do_destroy_accounts(id, T::RemoveItemsLimit::get())?;
833
			Ok(Some(T::WeightInfo::destroy_accounts(removed_accounts)).into())
834
		}
835

            
836
		/// Destroy all approvals associated with a given asset up to the max (T::RemoveItemsLimit).
837
		///
838
		/// `destroy_approvals` should only be called after `start_destroy` has been called, and the
839
		/// asset is in a `Destroying` state.
840
		///
841
		/// Due to weight restrictions, this function may need to be called multiple times to fully
842
		/// destroy all approvals. It will destroy `RemoveItemsLimit` approvals at a time.
843
		///
844
		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
845
		///   asset.
846
		///
847
		/// Each call emits the `Event::DestroyedApprovals` event.
848
		#[pallet::call_index(4)]
849
		#[pallet::weight(T::WeightInfo::destroy_approvals(T::RemoveItemsLimit::get()))]
850
		pub fn destroy_approvals(
851
			origin: OriginFor<T>,
852
			id: T::AssetIdParameter,
853
		) -> DispatchResultWithPostInfo {
854
			let _ = ensure_signed(origin)?;
855
			let id: T::AssetId = id.into();
856
			let removed_approvals = Self::do_destroy_approvals(id, T::RemoveItemsLimit::get())?;
857
			Ok(Some(T::WeightInfo::destroy_approvals(removed_approvals)).into())
858
		}
859

            
860
		/// Complete destroying asset and unreserve currency.
861
		///
862
		/// `finish_destroy` should only be called after `start_destroy` has been called, and the
863
		/// asset is in a `Destroying` state. All accounts or approvals should be destroyed before
864
		/// hand.
865
		///
866
		/// - `id`: The identifier of the asset to be destroyed. This must identify an existing
867
		///   asset.
868
		///
869
		/// Each successful call emits the `Event::Destroyed` event.
870
		#[pallet::call_index(5)]
871
		pub fn finish_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
872
			let _ = ensure_signed(origin)?;
873
			let id: T::AssetId = id.into();
874
			Self::do_finish_destroy(id)
875
		}
876

            
877
		/// Mint assets of a particular class.
878
		///
879
		/// The origin must be Signed and the sender must be the Issuer of the asset `id`.
880
		///
881
		/// - `id`: The identifier of the asset to have some amount minted.
882
		/// - `beneficiary`: The account to be credited with the minted assets.
883
		/// - `amount`: The amount of the asset to be minted.
884
		///
885
		/// Emits `Issued` event when successful.
886
		///
887
		/// Weight: `O(1)`
888
		/// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`.
889
		#[pallet::call_index(6)]
890
		pub fn mint(
891
			origin: OriginFor<T>,
892
			id: T::AssetIdParameter,
893
			beneficiary: AccountIdLookupOf<T>,
894
			#[pallet::compact] amount: T::Balance,
895
		) -> DispatchResult {
896
			let origin = ensure_signed(origin)?;
897
			let beneficiary = T::Lookup::lookup(beneficiary)?;
898
			let id: T::AssetId = id.into();
899
			Self::do_mint(id, &beneficiary, amount, Some(origin))?;
900
			Ok(())
901
		}
902

            
903
		/// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`.
904
		///
905
		/// Origin must be Signed and the sender should be the Manager of the asset `id`.
906
		///
907
		/// Bails with `NoAccount` if the `who` is already dead.
908
		///
909
		/// - `id`: The identifier of the asset to have some amount burned.
910
		/// - `who`: The account to be debited from.
911
		/// - `amount`: The maximum amount by which `who`'s balance should be reduced.
912
		///
913
		/// Emits `Burned` with the actual amount burned. If this takes the balance to below the
914
		/// minimum for the asset, then the amount burned is increased to take it to zero.
915
		///
916
		/// Weight: `O(1)`
917
		/// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`.
918
		#[pallet::call_index(7)]
919
		pub fn burn(
920
			origin: OriginFor<T>,
921
			id: T::AssetIdParameter,
922
			who: AccountIdLookupOf<T>,
923
			#[pallet::compact] amount: T::Balance,
924
		) -> DispatchResult {
925
			let origin = ensure_signed(origin)?;
926
			let who = T::Lookup::lookup(who)?;
927
			let id: T::AssetId = id.into();
928

            
929
			let f = DebitFlags { keep_alive: false, best_effort: true };
930
			let _ = Self::do_burn(id, &who, amount, Some(origin), f)?;
931
			Ok(())
932
		}
933

            
934
		/// Move some assets from the sender account to another.
935
		///
936
		/// Origin must be Signed.
937
		///
938
		/// - `id`: The identifier of the asset to have some amount transferred.
939
		/// - `target`: The account to be credited.
940
		/// - `amount`: The amount by which the sender's balance of assets should be reduced and
941
		/// `target`'s balance increased. The amount actually transferred may be slightly greater in
942
		/// the case that the transfer would otherwise take the sender balance above zero but below
943
		/// the minimum balance. Must be greater than zero.
944
		///
945
		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
946
		/// to below the minimum for the asset, then the amount transferred is increased to take it
947
		/// to zero.
948
		///
949
		/// Weight: `O(1)`
950
		/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
951
		/// `target`.
952
		#[pallet::call_index(8)]
953
		pub fn transfer(
954
			origin: OriginFor<T>,
955
			id: T::AssetIdParameter,
956
			target: AccountIdLookupOf<T>,
957
			#[pallet::compact] amount: T::Balance,
958
		) -> DispatchResult {
959
			let origin = ensure_signed(origin)?;
960
			let dest = T::Lookup::lookup(target)?;
961
			let id: T::AssetId = id.into();
962

            
963
			let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
964
			Self::do_transfer(id, &origin, &dest, amount, None, f).map(|_| ())
965
		}
966

            
967
		/// Move some assets from the sender account to another, keeping the sender account alive.
968
		///
969
		/// Origin must be Signed.
970
		///
971
		/// - `id`: The identifier of the asset to have some amount transferred.
972
		/// - `target`: The account to be credited.
973
		/// - `amount`: The amount by which the sender's balance of assets should be reduced and
974
		/// `target`'s balance increased. The amount actually transferred may be slightly greater in
975
		/// the case that the transfer would otherwise take the sender balance above zero but below
976
		/// the minimum balance. Must be greater than zero.
977
		///
978
		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
979
		/// to below the minimum for the asset, then the amount transferred is increased to take it
980
		/// to zero.
981
		///
982
		/// Weight: `O(1)`
983
		/// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
984
		/// `target`.
985
		#[pallet::call_index(9)]
986
		pub fn transfer_keep_alive(
987
			origin: OriginFor<T>,
988
			id: T::AssetIdParameter,
989
			target: AccountIdLookupOf<T>,
990
			#[pallet::compact] amount: T::Balance,
991
		) -> DispatchResult {
992
			let source = ensure_signed(origin)?;
993
			let dest = T::Lookup::lookup(target)?;
994
			let id: T::AssetId = id.into();
995

            
996
			let f = TransferFlags { keep_alive: true, best_effort: false, burn_dust: false };
997
			Self::do_transfer(id, &source, &dest, amount, None, f).map(|_| ())
998
		}
999

            
		/// Move some assets from one account to another.
		///
		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
		///
		/// - `id`: The identifier of the asset to have some amount transferred.
		/// - `source`: The account to be debited.
		/// - `dest`: The account to be credited.
		/// - `amount`: The amount by which the `source`'s balance of assets should be reduced and
		/// `dest`'s balance increased. The amount actually transferred may be slightly greater in
		/// the case that the transfer would otherwise take the `source` balance above zero but
		/// below the minimum balance. Must be greater than zero.
		///
		/// Emits `Transferred` with the actual amount transferred. If this takes the source balance
		/// to below the minimum for the asset, then the amount transferred is increased to take it
		/// to zero.
		///
		/// Weight: `O(1)`
		/// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of
		/// `dest`.
		#[pallet::call_index(10)]
		pub fn force_transfer(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			source: AccountIdLookupOf<T>,
			dest: AccountIdLookupOf<T>,
			#[pallet::compact] amount: T::Balance,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let source = T::Lookup::lookup(source)?;
			let dest = T::Lookup::lookup(dest)?;
			let id: T::AssetId = id.into();
			let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
			Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ())
		}
		/// Disallow further unprivileged transfers of an asset `id` from an account `who`. `who`
		/// must already exist as an entry in `Account`s of the asset. If you want to freeze an
		/// account that does not have an entry, use `touch_other` first.
		///
		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
		///
		/// - `id`: The identifier of the asset to be frozen.
		/// - `who`: The account to be frozen.
		///
		/// Emits `Frozen`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(11)]
		pub fn freeze(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			who: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(
				d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
				Error::<T, I>::IncorrectStatus
			);
			ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
			let who = T::Lookup::lookup(who)?;
			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
					AccountStatus::Frozen;
				Ok(())
			})?;
			Self::deposit_event(Event::<T, I>::Frozen { asset_id: id, who });
			Ok(())
		}
		/// Allow unprivileged transfers to and from an account again.
		///
		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
		///
		/// - `id`: The identifier of the asset to be frozen.
		/// - `who`: The account to be unfrozen.
		///
		/// Emits `Thawed`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(12)]
		pub fn thaw(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			who: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(
				details.status == AssetStatus::Live || details.status == AssetStatus::Frozen,
				Error::<T, I>::IncorrectStatus
			);
			ensure!(origin == details.admin, Error::<T, I>::NoPermission);
			let who = T::Lookup::lookup(who)?;
			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
					AccountStatus::Liquid;
				Ok(())
			})?;
			Self::deposit_event(Event::<T, I>::Thawed { asset_id: id, who });
			Ok(())
		}
		/// Disallow further unprivileged transfers for the asset class.
		///
		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
		///
		/// - `id`: The identifier of the asset to be frozen.
		///
		/// Emits `Frozen`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(13)]
		pub fn freeze_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
				let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
				ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
				ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
				d.status = AssetStatus::Frozen;
				Self::deposit_event(Event::<T, I>::AssetFrozen { asset_id: id });
				Ok(())
			})
		}
		/// Allow unprivileged transfers for the asset again.
		///
		/// Origin must be Signed and the sender should be the Admin of the asset `id`.
		///
		/// - `id`: The identifier of the asset to be thawed.
		///
		/// Emits `Thawed`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(14)]
		pub fn thaw_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
				let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
				ensure!(origin == d.admin, Error::<T, I>::NoPermission);
				ensure!(d.status == AssetStatus::Frozen, Error::<T, I>::NotFrozen);
				d.status = AssetStatus::Live;
				Self::deposit_event(Event::<T, I>::AssetThawed { asset_id: id });
				Ok(())
			})
		}
		/// Change the Owner of an asset.
		///
		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
		///
		/// - `id`: The identifier of the asset.
		/// - `owner`: The new Owner of this asset.
		///
		/// Emits `OwnerChanged`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(15)]
		pub fn transfer_ownership(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			owner: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let owner = T::Lookup::lookup(owner)?;
			let id: T::AssetId = id.into();
			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
				ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
				if details.owner == owner {
					return Ok(())
				}
				let metadata_deposit = Metadata::<T, I>::get(&id).deposit;
				let deposit = details.deposit + metadata_deposit;
				// Move the deposit to the new owner.
				T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?;
				details.owner = owner.clone();
				Self::deposit_event(Event::OwnerChanged { asset_id: id, owner });
				Ok(())
			})
		}
		/// Change the Issuer, Admin and Freezer of an asset.
		///
		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
		///
		/// - `id`: The identifier of the asset to be frozen.
		/// - `issuer`: The new Issuer of this asset.
		/// - `admin`: The new Admin of this asset.
		/// - `freezer`: The new Freezer of this asset.
		///
		/// Emits `TeamChanged`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(16)]
		pub fn set_team(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			issuer: AccountIdLookupOf<T>,
			admin: AccountIdLookupOf<T>,
			freezer: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let issuer = T::Lookup::lookup(issuer)?;
			let admin = T::Lookup::lookup(admin)?;
			let freezer = T::Lookup::lookup(freezer)?;
			let id: T::AssetId = id.into();
			Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
				let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
				ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
				ensure!(origin == details.owner, Error::<T, I>::NoPermission);
				details.issuer = issuer.clone();
				details.admin = admin.clone();
				details.freezer = freezer.clone();
				Self::deposit_event(Event::TeamChanged { asset_id: id, issuer, admin, freezer });
				Ok(())
			})
		}
		/// Set the metadata for an asset.
		///
		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
		///
		/// Funds of sender are reserved according to the formula:
		/// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into
		/// account any already reserved funds.
		///
		/// - `id`: The identifier of the asset to update.
		/// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
		/// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
		/// - `decimals`: The number of decimals this asset uses to represent one unit.
		///
		/// Emits `MetadataSet`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(17)]
		#[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))]
		pub fn set_metadata(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			name: Vec<u8>,
			symbol: Vec<u8>,
			decimals: u8,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			Self::do_set_metadata(id, &origin, name, symbol, decimals)
		}
		/// Clear the metadata for an asset.
		///
		/// Origin must be Signed and the sender should be the Owner of the asset `id`.
		///
		/// Any deposit is freed for the asset owner.
		///
		/// - `id`: The identifier of the asset to clear.
		///
		/// Emits `MetadataCleared`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(18)]
		pub fn clear_metadata(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
			ensure!(origin == d.owner, Error::<T, I>::NoPermission);
			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
				let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
				T::Currency::unreserve(&d.owner, deposit);
				Self::deposit_event(Event::MetadataCleared { asset_id: id });
				Ok(())
			})
		}
		/// Force the metadata for an asset to some value.
		///
		/// Origin must be ForceOrigin.
		///
		/// Any deposit is left alone.
		///
		/// - `id`: The identifier of the asset to update.
		/// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
		/// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
		/// - `decimals`: The number of decimals this asset uses to represent one unit.
		///
		/// Emits `MetadataSet`.
		///
		/// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively.
		#[pallet::call_index(19)]
		#[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))]
		pub fn force_set_metadata(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			name: Vec<u8>,
			symbol: Vec<u8>,
			decimals: u8,
			is_frozen: bool,
		) -> DispatchResult {
			T::ForceOrigin::ensure_origin(origin)?;
			let id: T::AssetId = id.into();
			let bounded_name: BoundedVec<u8, T::StringLimit> =
				name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
			let bounded_symbol: BoundedVec<u8, T::StringLimit> =
				symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
			ensure!(Asset::<T, I>::contains_key(&id), Error::<T, I>::Unknown);
			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
				let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
				*metadata = Some(AssetMetadata {
					deposit,
					name: bounded_name,
					symbol: bounded_symbol,
					decimals,
					is_frozen,
				});
				Self::deposit_event(Event::MetadataSet {
					asset_id: id,
					name,
					symbol,
					decimals,
					is_frozen,
				});
				Ok(())
			})
		}
		/// Clear the metadata for an asset.
		///
		/// Origin must be ForceOrigin.
		///
		/// Any deposit is returned.
		///
		/// - `id`: The identifier of the asset to clear.
		///
		/// Emits `MetadataCleared`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(20)]
		pub fn force_clear_metadata(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
		) -> DispatchResult {
			T::ForceOrigin::ensure_origin(origin)?;
			let id: T::AssetId = id.into();
			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
				let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
				T::Currency::unreserve(&d.owner, deposit);
				Self::deposit_event(Event::MetadataCleared { asset_id: id });
				Ok(())
			})
		}
		/// Alter the attributes of a given asset.
		///
		/// Origin must be `ForceOrigin`.
		///
		/// - `id`: The identifier of the asset.
		/// - `owner`: The new Owner of this asset.
		/// - `issuer`: The new Issuer of this asset.
		/// - `admin`: The new Admin of this asset.
		/// - `freezer`: The new Freezer of this asset.
		/// - `min_balance`: The minimum balance of this new asset that any single account must
		/// have. If an account's balance is reduced below this, then it collapses to zero.
		/// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient
		/// value to account for the state bloat associated with its balance storage. If set to
		/// `true`, then non-zero balances may be stored without a `consumer` reference (and thus
		/// an ED in the Balances pallet or whatever else is used to control user-account state
		/// growth).
		/// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin
		/// instructions.
		///
		/// Emits `AssetStatusChanged` with the identity of the asset.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(21)]
		pub fn force_asset_status(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			owner: AccountIdLookupOf<T>,
			issuer: AccountIdLookupOf<T>,
			admin: AccountIdLookupOf<T>,
			freezer: AccountIdLookupOf<T>,
			#[pallet::compact] min_balance: T::Balance,
			is_sufficient: bool,
			is_frozen: bool,
		) -> DispatchResult {
			T::ForceOrigin::ensure_origin(origin)?;
			let id: T::AssetId = id.into();
			Asset::<T, I>::try_mutate(id.clone(), |maybe_asset| {
				let mut asset = maybe_asset.take().ok_or(Error::<T, I>::Unknown)?;
				ensure!(asset.status != AssetStatus::Destroying, Error::<T, I>::AssetNotLive);
				asset.owner = T::Lookup::lookup(owner)?;
				asset.issuer = T::Lookup::lookup(issuer)?;
				asset.admin = T::Lookup::lookup(admin)?;
				asset.freezer = T::Lookup::lookup(freezer)?;
				asset.min_balance = min_balance;
				asset.is_sufficient = is_sufficient;
				if is_frozen {
					asset.status = AssetStatus::Frozen;
				} else {
					asset.status = AssetStatus::Live;
				}
				*maybe_asset = Some(asset);
				Self::deposit_event(Event::AssetStatusChanged { asset_id: id });
				Ok(())
			})
		}
		/// Approve an amount of asset for transfer by a delegated third-party account.
		///
		/// Origin must be Signed.
		///
		/// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account
		/// for the purpose of holding the approval. If some non-zero amount of assets is already
		/// approved from signing account to `delegate`, then it is topped up or unreserved to
		/// meet the right value.
		///
		/// NOTE: The signing account does not need to own `amount` of assets at the point of
		/// making this call.
		///
		/// - `id`: The identifier of the asset.
		/// - `delegate`: The account to delegate permission to transfer asset.
		/// - `amount`: The amount of asset that may be transferred by `delegate`. If there is
		/// already an approval in place, then this acts additively.
		///
		/// Emits `ApprovedTransfer` on success.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(22)]
		pub fn approve_transfer(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			delegate: AccountIdLookupOf<T>,
			#[pallet::compact] amount: T::Balance,
		) -> DispatchResult {
			let owner = ensure_signed(origin)?;
			let delegate = T::Lookup::lookup(delegate)?;
			let id: T::AssetId = id.into();
			Self::do_approve_transfer(id, &owner, &delegate, amount)
		}
		/// Cancel all of some asset approved for delegated transfer by a third-party account.
		///
		/// Origin must be Signed and there must be an approval in place between signer and
		/// `delegate`.
		///
		/// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
		///
		/// - `id`: The identifier of the asset.
		/// - `delegate`: The account delegated permission to transfer asset.
		///
		/// Emits `ApprovalCancelled` on success.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(23)]
		pub fn cancel_approval(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			delegate: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let owner = ensure_signed(origin)?;
			let delegate = T::Lookup::lookup(delegate)?;
			let id: T::AssetId = id.into();
			let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
			let approval = Approvals::<T, I>::take((id.clone(), &owner, &delegate))
				.ok_or(Error::<T, I>::Unknown)?;
			T::Currency::unreserve(&owner, approval.deposit);
			d.approvals.saturating_dec();
			Asset::<T, I>::insert(id.clone(), d);
			Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate });
			Ok(())
		}
		/// Cancel all of some asset approved for delegated transfer by a third-party account.
		///
		/// Origin must be either ForceOrigin or Signed origin with the signer being the Admin
		/// account of the asset `id`.
		///
		/// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
		///
		/// - `id`: The identifier of the asset.
		/// - `delegate`: The account delegated permission to transfer asset.
		///
		/// Emits `ApprovalCancelled` on success.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(24)]
		pub fn force_cancel_approval(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			owner: AccountIdLookupOf<T>,
			delegate: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let id: T::AssetId = id.into();
			let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
			T::ForceOrigin::try_origin(origin)
				.map(|_| ())
				.or_else(|origin| -> DispatchResult {
					let origin = ensure_signed(origin)?;
					ensure!(origin == d.admin, Error::<T, I>::NoPermission);
					Ok(())
				})?;
			let owner = T::Lookup::lookup(owner)?;
			let delegate = T::Lookup::lookup(delegate)?;
			let approval = Approvals::<T, I>::take((id.clone(), &owner, &delegate))
				.ok_or(Error::<T, I>::Unknown)?;
			T::Currency::unreserve(&owner, approval.deposit);
			d.approvals.saturating_dec();
			Asset::<T, I>::insert(id.clone(), d);
			Self::deposit_event(Event::ApprovalCancelled { asset_id: id, owner, delegate });
			Ok(())
		}
		/// Transfer some asset balance from a previously delegated account to some third-party
		/// account.
		///
		/// Origin must be Signed and there must be an approval in place by the `owner` to the
		/// signer.
		///
		/// If the entire amount approved for transfer is transferred, then any deposit previously
		/// reserved by `approve_transfer` is unreserved.
		///
		/// - `id`: The identifier of the asset.
		/// - `owner`: The account which previously approved for a transfer of at least `amount` and
		/// from which the asset balance will be withdrawn.
		/// - `destination`: The account to which the asset balance of `amount` will be transferred.
		/// - `amount`: The amount of assets to transfer.
		///
		/// Emits `TransferredApproved` on success.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(25)]
		pub fn transfer_approved(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			owner: AccountIdLookupOf<T>,
			destination: AccountIdLookupOf<T>,
			#[pallet::compact] amount: T::Balance,
		) -> DispatchResult {
			let delegate = ensure_signed(origin)?;
			let owner = T::Lookup::lookup(owner)?;
			let destination = T::Lookup::lookup(destination)?;
			let id: T::AssetId = id.into();
			Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
		}
		/// Create an asset account for non-provider assets.
		///
		/// A deposit will be taken from the signer account.
		///
		/// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
		///   to be taken.
		/// - `id`: The identifier of the asset for the account to be created.
		///
		/// Emits `Touched` event when successful.
		#[pallet::call_index(26)]
		#[pallet::weight(T::WeightInfo::touch())]
		pub fn touch(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
			let who = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			Self::do_touch(id, who.clone(), who, false)
		}
		/// Return the deposit (if any) of an asset account or a consumer reference (if any) of an
		/// account.
		///
		/// The origin must be Signed.
		///
		/// - `id`: The identifier of the asset for which the caller would like the deposit
		///   refunded.
		/// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund.
		///
		/// Emits `Refunded` event when successful.
		#[pallet::call_index(27)]
		#[pallet::weight(T::WeightInfo::refund())]
		pub fn refund(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			allow_burn: bool,
		) -> DispatchResult {
			let id: T::AssetId = id.into();
			Self::do_refund(id, ensure_signed(origin)?, allow_burn)
		}
		/// Sets the minimum balance of an asset.
		///
		/// Only works if there aren't any accounts that are holding the asset or if
		/// the new value of `min_balance` is less than the old one.
		///
		/// Origin must be Signed and the sender has to be the Owner of the
		/// asset `id`.
		///
		/// - `id`: The identifier of the asset.
		/// - `min_balance`: The new value of `min_balance`.
		///
		/// Emits `AssetMinBalanceChanged` event when successful.
		#[pallet::call_index(28)]
		pub fn set_min_balance(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			min_balance: T::Balance,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(origin == details.owner, Error::<T, I>::NoPermission);
			let old_min_balance = details.min_balance;
			// If the asset is marked as sufficient it won't be allowed to
			// change the min_balance.
			ensure!(!details.is_sufficient, Error::<T, I>::NoPermission);
			// Ensure that either the new min_balance is less than old
			// min_balance or there aren't any accounts holding the asset.
			ensure!(
				min_balance < old_min_balance || details.accounts == 0,
				Error::<T, I>::NoPermission
			);
			details.min_balance = min_balance;
			Asset::<T, I>::insert(&id, details);
			Self::deposit_event(Event::AssetMinBalanceChanged {
				asset_id: id,
				new_min_balance: min_balance,
			});
			Ok(())
		}
		/// Create an asset account for `who`.
		///
		/// A deposit will be taken from the signer account.
		///
		/// - `origin`: Must be Signed by `Freezer` or `Admin` of the asset `id`; the signer account
		///   must have sufficient funds for a deposit to be taken.
		/// - `id`: The identifier of the asset for the account to be created.
		/// - `who`: The account to be created.
		///
		/// Emits `Touched` event when successful.
		#[pallet::call_index(29)]
		#[pallet::weight(T::WeightInfo::touch_other())]
		pub fn touch_other(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			who: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let who = T::Lookup::lookup(who)?;
			let id: T::AssetId = id.into();
			Self::do_touch(id, who, origin, true)
		}
		/// Return the deposit (if any) of a target asset account. Useful if you are the depositor.
		///
		/// The origin must be Signed and either the account owner, depositor, or asset `Admin`. In
		/// order to burn a non-zero balance of the asset, the caller must be the account and should
		/// use `refund`.
		///
		/// - `id`: The identifier of the asset for the account holding a deposit.
		/// - `who`: The account to refund.
		///
		/// Emits `Refunded` event when successful.
		#[pallet::call_index(30)]
		#[pallet::weight(T::WeightInfo::refund_other())]
		pub fn refund_other(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			who: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let who = T::Lookup::lookup(who)?;
			let id: T::AssetId = id.into();
			Self::do_refund_other(id, &who, Some(origin))
		}
		/// Disallow further unprivileged transfers of an asset `id` to and from an account `who`.
		///
		/// Origin must be Signed and the sender should be the Freezer of the asset `id`.
		///
		/// - `id`: The identifier of the account's asset.
		/// - `who`: The account to be unblocked.
		///
		/// Emits `Blocked`.
		///
		/// Weight: `O(1)`
		#[pallet::call_index(31)]
		pub fn block(
			origin: OriginFor<T>,
			id: T::AssetIdParameter,
			who: AccountIdLookupOf<T>,
		) -> DispatchResult {
			let origin = ensure_signed(origin)?;
			let id: T::AssetId = id.into();
			let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
			ensure!(
				d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
				Error::<T, I>::IncorrectStatus
			);
			ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
			let who = T::Lookup::lookup(who)?;
			Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
				maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
					AccountStatus::Blocked;
				Ok(())
			})?;
			Self::deposit_event(Event::<T, I>::Blocked { asset_id: id, who });
			Ok(())
		}
	}
	/// Implements [`AccountTouch`] trait.
	/// Note that a depositor can be any account, without any specific privilege.
	/// This implementation is supposed to be used only for creation of system accounts.
	impl<T: Config<I>, I: 'static> AccountTouch<T::AssetId, T::AccountId> for Pallet<T, I> {
		type Balance = DepositBalanceOf<T, I>;
		fn deposit_required(_: T::AssetId) -> Self::Balance {
			T::AssetAccountDeposit::get()
		}
		fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
			match Asset::<T, I>::get(&asset) {
				// refer to the [`Self::new_account`] function for more details.
				Some(info) if info.is_sufficient => false,
				Some(_) if frame_system::Pallet::<T>::can_accrue_consumers(who, 2) => false,
				Some(_) => !Account::<T, I>::contains_key(asset, who),
				_ => true,
			}
		}
		fn touch(
			asset: T::AssetId,
			who: &T::AccountId,
			depositor: &T::AccountId,
		) -> DispatchResult {
			Self::do_touch(asset, who.clone(), depositor.clone(), false)
		}
	}
	/// Implements [`ContainsPair`] trait for a pair of asset and account IDs.
	impl<T: Config<I>, I: 'static> ContainsPair<T::AssetId, T::AccountId> for Pallet<T, I> {
		/// Check if an account with the given asset ID and account address exists.
		fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool {
			Account::<T, I>::contains_key(asset, who)
		}
	}
}
sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);