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