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 }
809
810 #[pallet::hooks]
811 impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
812 #[cfg(feature = "try-runtime")]
813 fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
814 Self::do_try_state()
815 }
816 }
817
818 #[pallet::call(weight(<T as Config<I>>::WeightInfo))]
819 impl<T: Config<I>, I: 'static> Pallet<T, I> {
820 /// Issue a new class of fungible assets from a public origin.
821 ///
822 /// This new asset class has no assets initially and its owner is the origin.
823 ///
824 /// The origin must conform to the configured `CreateOrigin` and have sufficient funds free.
825 ///
826 /// Funds of sender are reserved by `AssetDeposit`.
827 ///
828 /// Parameters:
829 /// - `id`: The identifier of the new asset. This must not be currently in use to identify
830 /// an existing asset. If [`Config::AssetIdAllocator`] requires a specific id, this must
831 /// equal it.
832 /// - `admin`: The admin of this class of assets. The admin is the initial address of each
833 /// member of the asset class's admin team.
834 /// - `min_balance`: The minimum balance of this new asset that any single account must
835 /// have. If an account's balance is reduced below this, then it collapses to zero.
836 ///
837 /// Emits `Created` event when successful.
838 ///
839 /// Weight: `O(1)`
840 #[pallet::call_index(0)]
841 pub fn create(
842 origin: OriginFor<T>,
843 id: T::AssetIdParameter,
844 admin: AccountIdLookupOf<T>,
845 min_balance: T::Balance,
846 ) -> DispatchResult {
847 let id: T::AssetId = id.into();
848 let owner = T::CreateOrigin::ensure_origin(origin, &id)?;
849 let admin = T::Lookup::lookup(admin)?;
850
851 ensure!(!Asset::<T, I>::contains_key(&id), Error::<T, I>::InUse);
852 ensure!(!min_balance.is_zero(), Error::<T, I>::MinBalanceZero);
853
854 if let Some(next_id) = T::AssetIdAllocator::next() {
855 ensure!(id == next_id, Error::<T, I>::BadAssetId);
856 }
857
858 let deposit = T::AssetDeposit::get();
859 T::Currency::reserve(&owner, deposit)?;
860
861 Asset::<T, I>::insert(
862 id.clone(),
863 AssetDetails {
864 owner: owner.clone(),
865 issuer: admin.clone(),
866 admin: admin.clone(),
867 freezer: admin.clone(),
868 supply: Zero::zero(),
869 deposit,
870 min_balance,
871 is_sufficient: false,
872 accounts: 0,
873 sufficients: 0,
874 approvals: 0,
875 status: AssetStatus::Live,
876 },
877 );
878 ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed);
879 T::AssetIdAllocator::advance().map_err(|_| Error::<T, I>::AssetIdAllocationFailed)?;
880 Self::deposit_event(Event::Created {
881 asset_id: id,
882 creator: owner.clone(),
883 owner: admin,
884 });
885
886 Ok(())
887 }
888
889 /// Issue a new class of fungible assets from a privileged origin.
890 ///
891 /// This new asset class has no assets initially.
892 ///
893 /// The origin must conform to `ForceOrigin`.
894 ///
895 /// Unlike `create`, no funds are reserved.
896 ///
897 /// Unlike `create`, the `id` does not have to be the one required by
898 /// [`Config::AssetIdAllocator`]: a privileged origin may pick any `id`, which is then
899 /// marked as allocated.
900 ///
901 /// # Warning
902 ///
903 /// Forcing an arbitrary `id` is dangerous: the pallet only checks that `id` is not
904 /// *currently* in use, not that it was never used before. Reusing an id can corrupt state,
905 /// most severely for bridged assets, where a collision breaks the local/remote mapping.
906 ///
907 /// - `id`: The identifier of the new asset. This must not be currently in use to identify
908 /// an existing asset, and must never have been in use previously (see warning above).
909 /// - `owner`: The owner of this class of assets. The owner has full superuser permissions
910 /// over this asset, but may later change and configure the permissions using
911 /// `transfer_ownership` and `set_team`.
912 /// - `min_balance`: The minimum balance of this new asset that any single account must
913 /// have. If an account's balance is reduced below this, then it collapses to zero.
914 ///
915 /// Emits `ForceCreated` event when successful.
916 ///
917 /// Weight: `O(1)`
918 #[pallet::call_index(1)]
919 pub fn force_create(
920 origin: OriginFor<T>,
921 id: T::AssetIdParameter,
922 owner: AccountIdLookupOf<T>,
923 is_sufficient: bool,
924 #[pallet::compact] min_balance: T::Balance,
925 ) -> DispatchResult {
926 T::ForceOrigin::ensure_origin(origin)?;
927 let owner = T::Lookup::lookup(owner)?;
928 let id: T::AssetId = id.into();
929 Self::do_force_create(id, owner, is_sufficient, min_balance, false)
930 }
931
932 /// Start the process of destroying a fungible asset class.
933 ///
934 /// `start_destroy` is the first in a series of extrinsics that should be called, to allow
935 /// destruction of an asset class.
936 ///
937 /// The origin must conform to `ForceOrigin` or must be `Signed` by the asset's `owner`.
938 ///
939 /// - `id`: The identifier of the asset to be destroyed. This must identify an existing
940 /// asset.
941 ///
942 /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
943 /// an account contains holds or freezes in place.
944 #[pallet::call_index(2)]
945 pub fn start_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
946 let maybe_check_owner = match T::ForceOrigin::try_origin(origin) {
947 Ok(_) => None,
948 Err(origin) => Some(ensure_signed(origin)?),
949 };
950 let id: T::AssetId = id.into();
951 Self::do_start_destroy(id, maybe_check_owner)
952 }
953
954 /// Destroy all accounts associated with a given asset.
955 ///
956 /// `destroy_accounts` should only be called after `start_destroy` has been called, and the
957 /// asset is in a `Destroying` state.
958 ///
959 /// Due to weight restrictions, this function may need to be called multiple times to fully
960 /// destroy all accounts. It will destroy `RemoveItemsLimit` accounts at a time.
961 ///
962 /// - `id`: The identifier of the asset to be destroyed. This must identify an existing
963 /// asset.
964 ///
965 /// Each call emits the `Event::DestroyedAccounts` event.
966 #[pallet::call_index(3)]
967 #[pallet::weight(T::WeightInfo::destroy_accounts(T::RemoveItemsLimit::get()))]
968 pub fn destroy_accounts(
969 origin: OriginFor<T>,
970 id: T::AssetIdParameter,
971 ) -> DispatchResultWithPostInfo {
972 ensure_signed(origin)?;
973 let id: T::AssetId = id.into();
974 let removed_accounts = Self::do_destroy_accounts(id, T::RemoveItemsLimit::get())?;
975 Ok(Some(T::WeightInfo::destroy_accounts(removed_accounts)).into())
976 }
977
978 /// Destroy all approvals associated with a given asset up to the max (T::RemoveItemsLimit).
979 ///
980 /// `destroy_approvals` should only be called after `start_destroy` has been called, and the
981 /// asset is in a `Destroying` state.
982 ///
983 /// Due to weight restrictions, this function may need to be called multiple times to fully
984 /// destroy all approvals. It will destroy `RemoveItemsLimit` approvals at a time.
985 ///
986 /// - `id`: The identifier of the asset to be destroyed. This must identify an existing
987 /// asset.
988 ///
989 /// Each call emits the `Event::DestroyedApprovals` event.
990 #[pallet::call_index(4)]
991 #[pallet::weight(T::WeightInfo::destroy_approvals(T::RemoveItemsLimit::get()))]
992 pub fn destroy_approvals(
993 origin: OriginFor<T>,
994 id: T::AssetIdParameter,
995 ) -> DispatchResultWithPostInfo {
996 ensure_signed(origin)?;
997 let id: T::AssetId = id.into();
998 let removed_approvals = Self::do_destroy_approvals(id, T::RemoveItemsLimit::get())?;
999 Ok(Some(T::WeightInfo::destroy_approvals(removed_approvals)).into())
1000 }
1001
1002 /// Complete destroying asset and unreserve currency.
1003 ///
1004 /// `finish_destroy` should only be called after `start_destroy` has been called, and the
1005 /// asset is in a `Destroying` state. All accounts or approvals should be destroyed before
1006 /// hand.
1007 ///
1008 /// - `id`: The identifier of the asset to be destroyed. This must identify an existing
1009 /// asset.
1010 ///
1011 /// Each successful call emits the `Event::Destroyed` event.
1012 #[pallet::call_index(5)]
1013 pub fn finish_destroy(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1014 ensure_signed(origin)?;
1015 let id: T::AssetId = id.into();
1016 Self::do_finish_destroy(id)
1017 }
1018
1019 /// Mint assets of a particular class.
1020 ///
1021 /// The origin must be Signed and the sender must be the Issuer of the asset `id`.
1022 ///
1023 /// - `id`: The identifier of the asset to have some amount minted.
1024 /// - `beneficiary`: The account to be credited with the minted assets.
1025 /// - `amount`: The amount of the asset to be minted.
1026 ///
1027 /// Emits `Issued` event when successful.
1028 ///
1029 /// Weight: `O(1)`
1030 /// Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`.
1031 #[pallet::call_index(6)]
1032 pub fn mint(
1033 origin: OriginFor<T>,
1034 id: T::AssetIdParameter,
1035 beneficiary: AccountIdLookupOf<T>,
1036 #[pallet::compact] amount: T::Balance,
1037 ) -> DispatchResult {
1038 let origin = ensure_signed(origin)?;
1039 let beneficiary = T::Lookup::lookup(beneficiary)?;
1040 let id: T::AssetId = id.into();
1041 Self::do_mint(id, &beneficiary, amount, Some(origin))?;
1042 Ok(())
1043 }
1044
1045 /// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`.
1046 ///
1047 /// Origin must be Signed and the sender should be the Manager of the asset `id`.
1048 ///
1049 /// Bails with `NoAccount` if the `who` is already dead.
1050 ///
1051 /// - `id`: The identifier of the asset to have some amount burned.
1052 /// - `who`: The account to be debited from.
1053 /// - `amount`: The maximum amount by which `who`'s balance should be reduced.
1054 ///
1055 /// Emits `Burned` with the actual amount burned. If this takes the balance to below the
1056 /// minimum for the asset, then the amount burned is increased to take it to zero.
1057 ///
1058 /// Weight: `O(1)`
1059 /// Modes: Post-existence of `who`; Pre & post Zombie-status of `who`.
1060 #[pallet::call_index(7)]
1061 pub fn burn(
1062 origin: OriginFor<T>,
1063 id: T::AssetIdParameter,
1064 who: AccountIdLookupOf<T>,
1065 #[pallet::compact] amount: T::Balance,
1066 ) -> DispatchResult {
1067 let origin = ensure_signed(origin)?;
1068 let who = T::Lookup::lookup(who)?;
1069 let id: T::AssetId = id.into();
1070
1071 let f = DebitFlags { keep_alive: false, best_effort: true };
1072 Self::do_burn(id, &who, amount, Some(origin), f)?;
1073 Ok(())
1074 }
1075
1076 /// Move some assets from the sender account to another.
1077 ///
1078 /// Origin must be Signed.
1079 ///
1080 /// - `id`: The identifier of the asset to have some amount transferred.
1081 /// - `target`: The account to be credited.
1082 /// - `amount`: The amount by which the sender's balance of assets should be reduced and
1083 /// `target`'s balance increased. The amount actually transferred may be slightly greater in
1084 /// the case that the transfer would otherwise take the sender balance above zero but below
1085 /// the minimum balance. Must be greater than zero.
1086 ///
1087 /// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1088 /// to below the minimum for the asset, then the amount transferred is increased to take it
1089 /// to zero.
1090 ///
1091 /// Weight: `O(1)`
1092 /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
1093 /// `target`.
1094 #[pallet::call_index(8)]
1095 pub fn transfer(
1096 origin: OriginFor<T>,
1097 id: T::AssetIdParameter,
1098 target: AccountIdLookupOf<T>,
1099 #[pallet::compact] amount: T::Balance,
1100 ) -> DispatchResult {
1101 let origin = ensure_signed(origin)?;
1102 let dest = T::Lookup::lookup(target)?;
1103 let id: T::AssetId = id.into();
1104
1105 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1106 Self::do_transfer(id, &origin, &dest, amount, None, f).map(|_| ())
1107 }
1108
1109 /// Move some assets from the sender account to another, keeping the sender account alive.
1110 ///
1111 /// Origin must be Signed.
1112 ///
1113 /// - `id`: The identifier of the asset to have some amount transferred.
1114 /// - `target`: The account to be credited.
1115 /// - `amount`: The amount by which the sender's balance of assets should be reduced and
1116 /// `target`'s balance increased. The amount actually transferred may be slightly greater in
1117 /// the case that the transfer would otherwise take the sender balance above zero but below
1118 /// the minimum balance. Must be greater than zero.
1119 ///
1120 /// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1121 /// to below the minimum for the asset, then the amount transferred is increased to take it
1122 /// to zero.
1123 ///
1124 /// Weight: `O(1)`
1125 /// Modes: Pre-existence of `target`; Post-existence of sender; Account pre-existence of
1126 /// `target`.
1127 #[pallet::call_index(9)]
1128 pub fn transfer_keep_alive(
1129 origin: OriginFor<T>,
1130 id: T::AssetIdParameter,
1131 target: AccountIdLookupOf<T>,
1132 #[pallet::compact] amount: T::Balance,
1133 ) -> DispatchResult {
1134 let source = ensure_signed(origin)?;
1135 let dest = T::Lookup::lookup(target)?;
1136 let id: T::AssetId = id.into();
1137
1138 let f = TransferFlags { keep_alive: true, best_effort: false, burn_dust: false };
1139 Self::do_transfer(id, &source, &dest, amount, None, f).map(|_| ())
1140 }
1141
1142 /// Move some assets from one account to another.
1143 ///
1144 /// Origin must be Signed and the sender should be the Admin of the asset `id`.
1145 ///
1146 /// - `id`: The identifier of the asset to have some amount transferred.
1147 /// - `source`: The account to be debited.
1148 /// - `dest`: The account to be credited.
1149 /// - `amount`: The amount by which the `source`'s balance of assets should be reduced and
1150 /// `dest`'s balance increased. The amount actually transferred may be slightly greater in
1151 /// the case that the transfer would otherwise take the `source` balance above zero but
1152 /// below the minimum balance. Must be greater than zero.
1153 ///
1154 /// Emits `Transferred` with the actual amount transferred. If this takes the source balance
1155 /// to below the minimum for the asset, then the amount transferred is increased to take it
1156 /// to zero.
1157 ///
1158 /// Weight: `O(1)`
1159 /// Modes: Pre-existence of `dest`; Post-existence of `source`; Account pre-existence of
1160 /// `dest`.
1161 #[pallet::call_index(10)]
1162 pub fn force_transfer(
1163 origin: OriginFor<T>,
1164 id: T::AssetIdParameter,
1165 source: AccountIdLookupOf<T>,
1166 dest: AccountIdLookupOf<T>,
1167 #[pallet::compact] amount: T::Balance,
1168 ) -> DispatchResult {
1169 let origin = ensure_signed(origin)?;
1170 let source = T::Lookup::lookup(source)?;
1171 let dest = T::Lookup::lookup(dest)?;
1172 let id: T::AssetId = id.into();
1173
1174 let f = TransferFlags { keep_alive: false, best_effort: false, burn_dust: false };
1175 Self::do_transfer(id, &source, &dest, amount, Some(origin), f).map(|_| ())
1176 }
1177
1178 /// Disallow further unprivileged transfers of an asset `id` from an account `who`. `who`
1179 /// must already exist as an entry in `Account`s of the asset. If you want to freeze an
1180 /// account that does not have an entry, use `touch_other` first.
1181 ///
1182 /// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1183 ///
1184 /// - `id`: The identifier of the asset to be frozen.
1185 /// - `who`: The account to be frozen.
1186 ///
1187 /// Emits `Frozen`.
1188 ///
1189 /// Weight: `O(1)`
1190 #[pallet::call_index(11)]
1191 pub fn freeze(
1192 origin: OriginFor<T>,
1193 id: T::AssetIdParameter,
1194 who: AccountIdLookupOf<T>,
1195 ) -> DispatchResult {
1196 let origin = ensure_signed(origin)?;
1197 let id: T::AssetId = id.into();
1198
1199 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1200 ensure!(
1201 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1202 Error::<T, I>::IncorrectStatus
1203 );
1204 ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1205 let who = T::Lookup::lookup(who)?;
1206
1207 Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1208 maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1209 AccountStatus::Frozen;
1210 Ok(())
1211 })?;
1212
1213 Self::deposit_event(Event::<T, I>::Frozen { asset_id: id, who });
1214 Ok(())
1215 }
1216
1217 /// Allow unprivileged transfers to and from an account again.
1218 ///
1219 /// Origin must be Signed and the sender should be the Admin of the asset `id`.
1220 ///
1221 /// - `id`: The identifier of the asset to be frozen.
1222 /// - `who`: The account to be unfrozen.
1223 ///
1224 /// Emits `Thawed`.
1225 ///
1226 /// Weight: `O(1)`
1227 #[pallet::call_index(12)]
1228 pub fn thaw(
1229 origin: OriginFor<T>,
1230 id: T::AssetIdParameter,
1231 who: AccountIdLookupOf<T>,
1232 ) -> DispatchResult {
1233 let origin = ensure_signed(origin)?;
1234 let id: T::AssetId = id.into();
1235
1236 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1237 ensure!(
1238 details.status == AssetStatus::Live || details.status == AssetStatus::Frozen,
1239 Error::<T, I>::IncorrectStatus
1240 );
1241 ensure!(origin == details.admin, Error::<T, I>::NoPermission);
1242 let who = T::Lookup::lookup(who)?;
1243
1244 Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1245 maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1246 AccountStatus::Liquid;
1247 Ok(())
1248 })?;
1249
1250 Self::deposit_event(Event::<T, I>::Thawed { asset_id: id, who });
1251 Ok(())
1252 }
1253
1254 /// Disallow further unprivileged transfers for the asset class.
1255 ///
1256 /// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1257 ///
1258 /// - `id`: The identifier of the asset to be frozen.
1259 ///
1260 /// Emits `Frozen`.
1261 ///
1262 /// Weight: `O(1)`
1263 #[pallet::call_index(13)]
1264 pub fn freeze_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1265 let origin = ensure_signed(origin)?;
1266 let id: T::AssetId = id.into();
1267
1268 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1269 let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1270 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1271 ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1272
1273 d.status = AssetStatus::Frozen;
1274
1275 Self::deposit_event(Event::<T, I>::AssetFrozen { asset_id: id });
1276 Ok(())
1277 })
1278 }
1279
1280 /// Allow unprivileged transfers for the asset again.
1281 ///
1282 /// Origin must be Signed and the sender should be the Admin of the asset `id`.
1283 ///
1284 /// - `id`: The identifier of the asset to be thawed.
1285 ///
1286 /// Emits `Thawed`.
1287 ///
1288 /// Weight: `O(1)`
1289 #[pallet::call_index(14)]
1290 pub fn thaw_asset(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1291 let origin = ensure_signed(origin)?;
1292 let id: T::AssetId = id.into();
1293
1294 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1295 let d = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1296 ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1297 ensure!(d.status == AssetStatus::Frozen, Error::<T, I>::NotFrozen);
1298
1299 d.status = AssetStatus::Live;
1300
1301 Self::deposit_event(Event::<T, I>::AssetThawed { asset_id: id });
1302 Ok(())
1303 })
1304 }
1305
1306 /// Change the Owner of an asset.
1307 ///
1308 /// Origin must be Signed and the sender should be the Owner of the asset `id`.
1309 ///
1310 /// - `id`: The identifier of the asset.
1311 /// - `owner`: The new Owner of this asset.
1312 ///
1313 /// Emits `OwnerChanged`.
1314 ///
1315 /// Weight: `O(1)`
1316 #[pallet::call_index(15)]
1317 pub fn transfer_ownership(
1318 origin: OriginFor<T>,
1319 id: T::AssetIdParameter,
1320 owner: AccountIdLookupOf<T>,
1321 ) -> DispatchResult {
1322 let origin = ensure_signed(origin)?;
1323 let owner = T::Lookup::lookup(owner)?;
1324 let id: T::AssetId = id.into();
1325
1326 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1327 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1328 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1329 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1330 if details.owner == owner {
1331 return Ok(());
1332 }
1333
1334 let metadata_deposit = Metadata::<T, I>::get(&id).deposit;
1335 let deposit = details.deposit + metadata_deposit;
1336
1337 // Move the deposit to the new owner.
1338 T::Currency::repatriate_reserved(&details.owner, &owner, deposit, Reserved)?;
1339
1340 details.owner = owner.clone();
1341
1342 Self::deposit_event(Event::OwnerChanged { asset_id: id, owner });
1343 Ok(())
1344 })
1345 }
1346
1347 /// Change the Issuer, Admin and Freezer of an asset.
1348 ///
1349 /// Origin must be Signed and the sender should be the Owner of the asset `id`.
1350 ///
1351 /// - `id`: The identifier of the asset to be frozen.
1352 /// - `issuer`: The new Issuer of this asset.
1353 /// - `admin`: The new Admin of this asset.
1354 /// - `freezer`: The new Freezer of this asset.
1355 ///
1356 /// Emits `TeamChanged`.
1357 ///
1358 /// Weight: `O(1)`
1359 #[pallet::call_index(16)]
1360 pub fn set_team(
1361 origin: OriginFor<T>,
1362 id: T::AssetIdParameter,
1363 issuer: AccountIdLookupOf<T>,
1364 admin: AccountIdLookupOf<T>,
1365 freezer: AccountIdLookupOf<T>,
1366 ) -> DispatchResult {
1367 let origin = ensure_signed(origin)?;
1368 let issuer = T::Lookup::lookup(issuer)?;
1369 let admin = T::Lookup::lookup(admin)?;
1370 let freezer = T::Lookup::lookup(freezer)?;
1371 let id: T::AssetId = id.into();
1372
1373 Asset::<T, I>::try_mutate(id.clone(), |maybe_details| {
1374 let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
1375 ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1376 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1377
1378 details.issuer = issuer.clone();
1379 details.admin = admin.clone();
1380 details.freezer = freezer.clone();
1381
1382 Self::deposit_event(Event::TeamChanged { asset_id: id, issuer, admin, freezer });
1383 Ok(())
1384 })
1385 }
1386
1387 /// Set the metadata for an asset.
1388 ///
1389 /// Origin must be Signed and the sender should be the Owner of the asset `id`.
1390 ///
1391 /// Funds of sender are reserved according to the formula:
1392 /// `MetadataDepositBase + MetadataDepositPerByte * (name.len + symbol.len)` taking into
1393 /// account any already reserved funds.
1394 ///
1395 /// - `id`: The identifier of the asset to update.
1396 /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
1397 /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
1398 /// - `decimals`: The number of decimals this asset uses to represent one unit.
1399 ///
1400 /// Emits `MetadataSet`.
1401 ///
1402 /// Weight: `O(1)`
1403 #[pallet::call_index(17)]
1404 #[pallet::weight(T::WeightInfo::set_metadata(name.len() as u32, symbol.len() as u32))]
1405 pub fn set_metadata(
1406 origin: OriginFor<T>,
1407 id: T::AssetIdParameter,
1408 name: Vec<u8>,
1409 symbol: Vec<u8>,
1410 decimals: u8,
1411 ) -> DispatchResult {
1412 let origin = ensure_signed(origin)?;
1413 let id: T::AssetId = id.into();
1414 Self::do_set_metadata(id, &origin, name, symbol, decimals)
1415 }
1416
1417 /// Clear the metadata for an asset.
1418 ///
1419 /// Origin must be Signed and the sender should be the Owner of the asset `id`.
1420 ///
1421 /// Any deposit is freed for the asset owner.
1422 ///
1423 /// - `id`: The identifier of the asset to clear.
1424 ///
1425 /// Emits `MetadataCleared`.
1426 ///
1427 /// Weight: `O(1)`
1428 #[pallet::call_index(18)]
1429 pub fn clear_metadata(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1430 let origin = ensure_signed(origin)?;
1431 let id: T::AssetId = id.into();
1432
1433 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1434 ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive);
1435 ensure!(origin == d.owner, Error::<T, I>::NoPermission);
1436
1437 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1438 let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1439 T::Currency::unreserve(&d.owner, deposit);
1440 Self::deposit_event(Event::MetadataCleared { asset_id: id });
1441 Ok(())
1442 })
1443 }
1444
1445 /// Force the metadata for an asset to some value.
1446 ///
1447 /// Origin must be ForceOrigin.
1448 ///
1449 /// Any deposit is left alone.
1450 ///
1451 /// - `id`: The identifier of the asset to update.
1452 /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`.
1453 /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`.
1454 /// - `decimals`: The number of decimals this asset uses to represent one unit.
1455 ///
1456 /// Emits `MetadataSet`.
1457 ///
1458 /// Weight: `O(N + S)` where N and S are the length of the name and symbol respectively.
1459 #[pallet::call_index(19)]
1460 #[pallet::weight(T::WeightInfo::force_set_metadata(name.len() as u32, symbol.len() as u32))]
1461 pub fn force_set_metadata(
1462 origin: OriginFor<T>,
1463 id: T::AssetIdParameter,
1464 name: Vec<u8>,
1465 symbol: Vec<u8>,
1466 decimals: u8,
1467 is_frozen: bool,
1468 ) -> DispatchResult {
1469 T::ForceOrigin::ensure_origin(origin)?;
1470 let id: T::AssetId = id.into();
1471
1472 let bounded_name: BoundedVec<u8, T::StringLimit> =
1473 name.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1474
1475 let bounded_symbol: BoundedVec<u8, T::StringLimit> =
1476 symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?;
1477
1478 ensure!(Asset::<T, I>::contains_key(&id), Error::<T, I>::Unknown);
1479 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1480 let deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit);
1481 *metadata = Some(AssetMetadata {
1482 deposit,
1483 name: bounded_name,
1484 symbol: bounded_symbol,
1485 decimals,
1486 is_frozen,
1487 });
1488
1489 Self::deposit_event(Event::MetadataSet {
1490 asset_id: id,
1491 name,
1492 symbol,
1493 decimals,
1494 is_frozen,
1495 });
1496 Ok(())
1497 })
1498 }
1499
1500 /// Clear the metadata for an asset.
1501 ///
1502 /// Origin must be ForceOrigin.
1503 ///
1504 /// Any deposit is returned.
1505 ///
1506 /// - `id`: The identifier of the asset to clear.
1507 ///
1508 /// Emits `MetadataCleared`.
1509 ///
1510 /// Weight: `O(1)`
1511 #[pallet::call_index(20)]
1512 pub fn force_clear_metadata(
1513 origin: OriginFor<T>,
1514 id: T::AssetIdParameter,
1515 ) -> DispatchResult {
1516 T::ForceOrigin::ensure_origin(origin)?;
1517 let id: T::AssetId = id.into();
1518
1519 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1520 Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| {
1521 let deposit = metadata.take().ok_or(Error::<T, I>::Unknown)?.deposit;
1522 T::Currency::unreserve(&d.owner, deposit);
1523 Self::deposit_event(Event::MetadataCleared { asset_id: id });
1524 Ok(())
1525 })
1526 }
1527
1528 /// Alter the attributes of a given asset.
1529 ///
1530 /// Origin must be `ForceOrigin`.
1531 ///
1532 /// - `id`: The identifier of the asset.
1533 /// - `owner`: The new Owner of this asset.
1534 /// - `issuer`: The new Issuer of this asset.
1535 /// - `admin`: The new Admin of this asset.
1536 /// - `freezer`: The new Freezer of this asset.
1537 /// - `min_balance`: The minimum balance of this new asset that any single account must
1538 /// have. If an account's balance is reduced below this, then it collapses to zero.
1539 /// - `is_sufficient`: Whether a non-zero balance of this asset is deposit of sufficient
1540 /// value to account for the state bloat associated with its balance storage. If set to
1541 /// `true`, then non-zero balances may be stored without a `consumer` reference (and thus
1542 /// an ED in the Balances pallet or whatever else is used to control user-account state
1543 /// growth).
1544 /// - `is_frozen`: Whether this asset class is frozen except for permissioned/admin
1545 /// instructions.
1546 ///
1547 /// Emits `AssetStatusChanged` with the identity of the asset.
1548 ///
1549 /// Weight: `O(1)`
1550 #[pallet::call_index(21)]
1551 pub fn force_asset_status(
1552 origin: OriginFor<T>,
1553 id: T::AssetIdParameter,
1554 owner: AccountIdLookupOf<T>,
1555 issuer: AccountIdLookupOf<T>,
1556 admin: AccountIdLookupOf<T>,
1557 freezer: AccountIdLookupOf<T>,
1558 #[pallet::compact] min_balance: T::Balance,
1559 is_sufficient: bool,
1560 is_frozen: bool,
1561 ) -> DispatchResult {
1562 T::ForceOrigin::ensure_origin(origin)?;
1563 let id: T::AssetId = id.into();
1564
1565 Asset::<T, I>::try_mutate(id.clone(), |maybe_asset| {
1566 let mut asset = maybe_asset.take().ok_or(Error::<T, I>::Unknown)?;
1567 ensure!(asset.status != AssetStatus::Destroying, Error::<T, I>::AssetNotLive);
1568 asset.owner = T::Lookup::lookup(owner)?;
1569 asset.issuer = T::Lookup::lookup(issuer)?;
1570 asset.admin = T::Lookup::lookup(admin)?;
1571 asset.freezer = T::Lookup::lookup(freezer)?;
1572 asset.min_balance = min_balance;
1573 asset.is_sufficient = is_sufficient;
1574 if is_frozen {
1575 asset.status = AssetStatus::Frozen;
1576 } else {
1577 asset.status = AssetStatus::Live;
1578 }
1579 *maybe_asset = Some(asset);
1580
1581 Self::deposit_event(Event::AssetStatusChanged { asset_id: id });
1582 Ok(())
1583 })
1584 }
1585
1586 /// Approve an amount of asset for transfer by a delegated third-party account.
1587 ///
1588 /// Origin must be Signed.
1589 ///
1590 /// Ensures that `ApprovalDeposit` worth of `Currency` is reserved from signing account
1591 /// for the purpose of holding the approval. If some non-zero amount of assets is already
1592 /// approved from signing account to `delegate`, then it is topped up or unreserved to
1593 /// meet the right value.
1594 ///
1595 /// NOTE: The signing account does not need to own `amount` of assets at the point of
1596 /// making this call.
1597 ///
1598 /// - `id`: The identifier of the asset.
1599 /// - `delegate`: The account to delegate permission to transfer asset.
1600 /// - `amount`: The amount of asset that may be transferred by `delegate`. If there is
1601 /// already an approval in place, then this acts additively.
1602 ///
1603 /// Emits `ApprovedTransfer` on success.
1604 ///
1605 /// Weight: `O(1)`
1606 #[pallet::call_index(22)]
1607 pub fn approve_transfer(
1608 origin: OriginFor<T>,
1609 id: T::AssetIdParameter,
1610 delegate: AccountIdLookupOf<T>,
1611 #[pallet::compact] amount: T::Balance,
1612 ) -> DispatchResult {
1613 let owner = ensure_signed(origin)?;
1614 let delegate = T::Lookup::lookup(delegate)?;
1615 let id: T::AssetId = id.into();
1616 Self::do_approve_transfer(id, &owner, &delegate, amount)
1617 }
1618
1619 /// Cancel all of some asset approved for delegated transfer by a third-party account.
1620 ///
1621 /// Origin must be Signed and there must be an approval in place between signer and
1622 /// `delegate`.
1623 ///
1624 /// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
1625 ///
1626 /// - `id`: The identifier of the asset.
1627 /// - `delegate`: The account delegated permission to transfer asset.
1628 ///
1629 /// Emits `ApprovalCancelled` on success.
1630 ///
1631 /// Weight: `O(1)`
1632 #[pallet::call_index(23)]
1633 pub fn cancel_approval(
1634 origin: OriginFor<T>,
1635 id: T::AssetIdParameter,
1636 delegate: AccountIdLookupOf<T>,
1637 ) -> DispatchResult {
1638 let owner = ensure_signed(origin)?;
1639 let delegate = T::Lookup::lookup(delegate)?;
1640 let id: T::AssetId = id.into();
1641 Self::do_cancel_approval(&id, &owner, &delegate)
1642 }
1643
1644 /// Cancel all of some asset approved for delegated transfer by a third-party account.
1645 ///
1646 /// Origin must be either ForceOrigin or Signed origin with the signer being the Admin
1647 /// account of the asset `id`.
1648 ///
1649 /// Unreserves any deposit previously reserved by `approve_transfer` for the approval.
1650 ///
1651 /// - `id`: The identifier of the asset.
1652 /// - `delegate`: The account delegated permission to transfer asset.
1653 ///
1654 /// Emits `ApprovalCancelled` on success.
1655 ///
1656 /// Weight: `O(1)`
1657 #[pallet::call_index(24)]
1658 pub fn force_cancel_approval(
1659 origin: OriginFor<T>,
1660 id: T::AssetIdParameter,
1661 owner: AccountIdLookupOf<T>,
1662 delegate: AccountIdLookupOf<T>,
1663 ) -> DispatchResult {
1664 let id: T::AssetId = id.into();
1665 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1666 T::ForceOrigin::try_origin(origin)
1667 .map(|_| ())
1668 .or_else(|origin| -> DispatchResult {
1669 let origin = ensure_signed(origin)?;
1670 ensure!(origin == d.admin, Error::<T, I>::NoPermission);
1671 Ok(())
1672 })?;
1673
1674 let owner = T::Lookup::lookup(owner)?;
1675 let delegate = T::Lookup::lookup(delegate)?;
1676 Self::do_cancel_approval(&id, &owner, &delegate)
1677 }
1678
1679 /// Transfer some asset balance from a previously delegated account to some third-party
1680 /// account.
1681 ///
1682 /// Origin must be Signed and there must be an approval in place by the `owner` to the
1683 /// signer.
1684 ///
1685 /// If the entire amount approved for transfer is transferred, then any deposit previously
1686 /// reserved by `approve_transfer` is unreserved.
1687 ///
1688 /// - `id`: The identifier of the asset.
1689 /// - `owner`: The account which previously approved for a transfer of at least `amount` and
1690 /// from which the asset balance will be withdrawn.
1691 /// - `destination`: The account to which the asset balance of `amount` will be transferred.
1692 /// - `amount`: The amount of assets to transfer.
1693 ///
1694 /// Emits `TransferredApproved` on success.
1695 ///
1696 /// Weight: `O(1)`
1697 #[pallet::call_index(25)]
1698 pub fn transfer_approved(
1699 origin: OriginFor<T>,
1700 id: T::AssetIdParameter,
1701 owner: AccountIdLookupOf<T>,
1702 destination: AccountIdLookupOf<T>,
1703 #[pallet::compact] amount: T::Balance,
1704 ) -> DispatchResult {
1705 let delegate = ensure_signed(origin)?;
1706 let owner = T::Lookup::lookup(owner)?;
1707 let destination = T::Lookup::lookup(destination)?;
1708 let id: T::AssetId = id.into();
1709 Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
1710 }
1711
1712 /// Create an asset account for non-provider assets.
1713 ///
1714 /// A deposit will be taken from the signer account.
1715 ///
1716 /// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
1717 /// to be taken.
1718 /// - `id`: The identifier of the asset for the account to be created.
1719 ///
1720 /// Emits `Touched` event when successful.
1721 #[pallet::call_index(26)]
1722 #[pallet::weight(T::WeightInfo::touch())]
1723 pub fn touch(origin: OriginFor<T>, id: T::AssetIdParameter) -> DispatchResult {
1724 let who = ensure_signed(origin)?;
1725 let id: T::AssetId = id.into();
1726 Self::do_touch(id, who.clone(), who)
1727 }
1728
1729 /// Return the deposit (if any) of an asset account or a consumer reference (if any) of an
1730 /// account.
1731 ///
1732 /// The origin must be Signed.
1733 ///
1734 /// - `id`: The identifier of the asset for which the caller would like the deposit
1735 /// refunded.
1736 /// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund.
1737 ///
1738 /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
1739 /// the asset account contains holds or freezes in place.
1740 ///
1741 /// Emits `Refunded` event when successful.
1742 #[pallet::call_index(27)]
1743 #[pallet::weight(T::WeightInfo::refund())]
1744 pub fn refund(
1745 origin: OriginFor<T>,
1746 id: T::AssetIdParameter,
1747 allow_burn: bool,
1748 ) -> DispatchResult {
1749 let id: T::AssetId = id.into();
1750 Self::do_refund(id, ensure_signed(origin)?, allow_burn)
1751 }
1752
1753 /// Sets the minimum balance of an asset.
1754 ///
1755 /// Only works if there aren't any accounts that are holding the asset or if
1756 /// the new value of `min_balance` is less than the old one.
1757 ///
1758 /// Origin must be Signed and the sender has to be the Owner of the
1759 /// asset `id`.
1760 ///
1761 /// - `id`: The identifier of the asset.
1762 /// - `min_balance`: The new value of `min_balance`.
1763 ///
1764 /// Emits `AssetMinBalanceChanged` event when successful.
1765 #[pallet::call_index(28)]
1766 pub fn set_min_balance(
1767 origin: OriginFor<T>,
1768 id: T::AssetIdParameter,
1769 min_balance: T::Balance,
1770 ) -> DispatchResult {
1771 let origin = ensure_signed(origin)?;
1772 let id: T::AssetId = id.into();
1773
1774 let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1775 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1776
1777 let old_min_balance = details.min_balance;
1778 // If the asset is marked as sufficient it won't be allowed to
1779 // change the min_balance.
1780 ensure!(!details.is_sufficient, Error::<T, I>::NoPermission);
1781
1782 // Ensure that either the new min_balance is less than old
1783 // min_balance or there aren't any accounts holding the asset.
1784 ensure!(
1785 min_balance < old_min_balance || details.accounts == 0,
1786 Error::<T, I>::NoPermission
1787 );
1788
1789 details.min_balance = min_balance;
1790 Asset::<T, I>::insert(&id, details);
1791
1792 Self::deposit_event(Event::AssetMinBalanceChanged {
1793 asset_id: id,
1794 new_min_balance: min_balance,
1795 });
1796 Ok(())
1797 }
1798
1799 /// Create an asset account for `who`.
1800 ///
1801 /// A deposit will be taken from the signer account.
1802 ///
1803 /// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
1804 /// to be taken.
1805 /// - `id`: The identifier of the asset for the account to be created, the asset status must
1806 /// be live.
1807 /// - `who`: The account to be created.
1808 ///
1809 /// Emits `Touched` event when successful.
1810 #[pallet::call_index(29)]
1811 #[pallet::weight(T::WeightInfo::touch_other())]
1812 pub fn touch_other(
1813 origin: OriginFor<T>,
1814 id: T::AssetIdParameter,
1815 who: AccountIdLookupOf<T>,
1816 ) -> DispatchResult {
1817 let origin = ensure_signed(origin)?;
1818 let who = T::Lookup::lookup(who)?;
1819 let id: T::AssetId = id.into();
1820 Self::do_touch(id, who, origin)
1821 }
1822
1823 /// Return the deposit (if any) of a target asset account. Useful if you are the depositor.
1824 ///
1825 /// The origin must be Signed and either the account owner, depositor, or asset `Admin`. In
1826 /// order to burn a non-zero balance of the asset, the caller must be the account and should
1827 /// use `refund`.
1828 ///
1829 /// - `id`: The identifier of the asset for the account holding a deposit.
1830 /// - `who`: The account to refund.
1831 ///
1832 /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if
1833 /// the asset account contains holds or freezes in place.
1834 ///
1835 /// Emits `Refunded` event when successful.
1836 #[pallet::call_index(30)]
1837 #[pallet::weight(T::WeightInfo::refund_other())]
1838 pub fn refund_other(
1839 origin: OriginFor<T>,
1840 id: T::AssetIdParameter,
1841 who: AccountIdLookupOf<T>,
1842 ) -> DispatchResult {
1843 let origin = ensure_signed(origin)?;
1844 let who = T::Lookup::lookup(who)?;
1845 let id: T::AssetId = id.into();
1846 Self::do_refund_other(id, &who, Some(origin))
1847 }
1848
1849 /// Disallow further unprivileged transfers of an asset `id` to and from an account `who`.
1850 ///
1851 /// Origin must be Signed and the sender should be the Freezer of the asset `id`.
1852 ///
1853 /// - `id`: The identifier of the account's asset.
1854 /// - `who`: The account to be unblocked.
1855 ///
1856 /// Emits `Blocked`.
1857 ///
1858 /// Weight: `O(1)`
1859 #[pallet::call_index(31)]
1860 pub fn block(
1861 origin: OriginFor<T>,
1862 id: T::AssetIdParameter,
1863 who: AccountIdLookupOf<T>,
1864 ) -> DispatchResult {
1865 let origin = ensure_signed(origin)?;
1866 let id: T::AssetId = id.into();
1867
1868 let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1869 ensure!(
1870 d.status == AssetStatus::Live || d.status == AssetStatus::Frozen,
1871 Error::<T, I>::IncorrectStatus
1872 );
1873 ensure!(origin == d.freezer, Error::<T, I>::NoPermission);
1874 let who = T::Lookup::lookup(who)?;
1875
1876 Account::<T, I>::try_mutate(&id, &who, |maybe_account| -> DispatchResult {
1877 maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.status =
1878 AccountStatus::Blocked;
1879 Ok(())
1880 })?;
1881
1882 Self::deposit_event(Event::<T, I>::Blocked { asset_id: id, who });
1883 Ok(())
1884 }
1885
1886 /// Transfer the entire transferable balance from the caller asset account.
1887 ///
1888 /// NOTE: This function only attempts to transfer _transferable_ balances. This means that
1889 /// any held, frozen, or minimum balance (when `keep_alive` is `true`), will not be
1890 /// transferred by this function. To ensure that this function results in a killed account,
1891 /// you might need to prepare the account by removing any reference counters, storage
1892 /// deposits, etc...
1893 ///
1894 /// The dispatch origin of this call must be Signed.
1895 ///
1896 /// - `id`: The identifier of the asset for the account holding a deposit.
1897 /// - `dest`: The recipient of the transfer.
1898 /// - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all
1899 /// of the funds the asset account has, causing the sender asset account to be killed
1900 /// (false), or transfer everything except at least the minimum balance, which will
1901 /// guarantee to keep the sender asset account alive (true).
1902 #[pallet::call_index(32)]
1903 #[pallet::weight(T::WeightInfo::transfer_all())]
1904 pub fn transfer_all(
1905 origin: OriginFor<T>,
1906 id: T::AssetIdParameter,
1907 dest: AccountIdLookupOf<T>,
1908 keep_alive: bool,
1909 ) -> DispatchResult {
1910 let transactor = ensure_signed(origin)?;
1911 let keep_alive = if keep_alive { Preserve } else { Expendable };
1912 let reducible_balance = <Self as fungibles::Inspect<_>>::reducible_balance(
1913 id.clone().into(),
1914 &transactor,
1915 keep_alive,
1916 Fortitude::Polite,
1917 );
1918 let dest = T::Lookup::lookup(dest)?;
1919 <Self as fungibles::Mutate<_>>::transfer(
1920 id.into(),
1921 &transactor,
1922 &dest,
1923 reducible_balance,
1924 keep_alive,
1925 )?;
1926 Ok(())
1927 }
1928
1929 /// Sets the trusted reserve information of an asset.
1930 ///
1931 /// Origin must be the Owner of the asset `id`. The origin must conform to the configured
1932 /// `CreateOrigin` or be the signed `owner` configured during asset creation.
1933 ///
1934 /// - `id`: The identifier of the asset.
1935 /// - `reserves`: The full list of trusted reserves information.
1936 ///
1937 /// Emits `AssetMinBalanceChanged` event when successful.
1938 #[pallet::call_index(33)]
1939 #[pallet::weight(T::WeightInfo::set_reserves(reserves.len() as u32))]
1940 pub fn set_reserves(
1941 origin: OriginFor<T>,
1942 id: T::AssetIdParameter,
1943 reserves: BoundedVec<T::ReserveData, ConstU32<MAX_RESERVES>>,
1944 ) -> DispatchResult {
1945 let id: T::AssetId = id.into();
1946 let origin = ensure_signed(origin.clone())
1947 .or_else(|_| T::CreateOrigin::ensure_origin(origin, &id))?;
1948
1949 let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
1950 ensure!(origin == details.owner, Error::<T, I>::NoPermission);
1951
1952 Self::unchecked_update_reserves(id, reserves)?;
1953 Ok(())
1954 }
1955 }
1956
1957 #[pallet::view_functions]
1958 impl<T: Config<I>, I: 'static> Pallet<T, I> {
1959 /// Provide the asset details for asset `id`.
1960 pub fn asset_details(
1961 id: T::AssetId,
1962 ) -> Option<AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>> {
1963 Asset::<T, I>::get(id)
1964 }
1965
1966 /// Provide the balance of `who` for asset `id`.
1967 pub fn balance_of(who: T::AccountId, id: T::AssetId) -> Option<<T as Config<I>>::Balance> {
1968 Account::<T, I>::get(id, who).map(|account| account.balance)
1969 }
1970
1971 /// Provide the configured metadata for asset `id`.
1972 pub fn get_metadata(
1973 id: T::AssetId,
1974 ) -> Option<AssetMetadata<DepositBalanceOf<T, I>, BoundedVec<u8, T::StringLimit>>> {
1975 Metadata::<T, I>::try_get(id).ok()
1976 }
1977
1978 /// Provide the configured reserves data for asset `id`.
1979 pub fn get_reserves_data(id: T::AssetId) -> Vec<T::ReserveData> {
1980 Self::reserves(&id)
1981 }
1982 }
1983
1984 /// Implements [`AccountTouch`] trait.
1985 /// Note that a depositor can be any account, without any specific privilege.
1986 impl<T: Config<I>, I: 'static> AccountTouch<T::AssetId, T::AccountId> for Pallet<T, I> {
1987 type Balance = DepositBalanceOf<T, I>;
1988
1989 fn deposit_required(_: T::AssetId) -> Self::Balance {
1990 T::AssetAccountDeposit::get()
1991 }
1992
1993 fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
1994 match Asset::<T, I>::get(&asset) {
1995 // refer to the [`Self::new_account`] function for more details.
1996 Some(info) if info.is_sufficient => false,
1997 Some(_) if frame_system::Pallet::<T>::can_accrue_consumers(who, 2) => false,
1998 Some(_) => !Account::<T, I>::contains_key(asset, who),
1999 _ => true,
2000 }
2001 }
2002
2003 fn touch(
2004 asset: T::AssetId,
2005 who: &T::AccountId,
2006 depositor: &T::AccountId,
2007 ) -> DispatchResult {
2008 Self::do_touch(asset, who.clone(), depositor.clone())
2009 }
2010 }
2011
2012 /// Implements [`ContainsPair`] trait for a pair of asset and account IDs.
2013 impl<T: Config<I>, I: 'static> ContainsPair<T::AssetId, T::AccountId> for Pallet<T, I> {
2014 /// Check if an account with the given asset ID and account address exists.
2015 fn contains(asset: &T::AssetId, who: &T::AccountId) -> bool {
2016 Account::<T, I>::contains_key(asset, who)
2017 }
2018 }
2019
2020 /// Implements [`ProvideAssetReserves`] trait for getting the list of trusted reserves for a
2021 /// given asset.
2022 impl<T: Config<I>, I: 'static> ProvideAssetReserves<T::AssetId, T::ReserveData> for Pallet<T, I> {
2023 /// Provide the configured reserves for asset `id`.
2024 fn reserves(id: &T::AssetId) -> Vec<T::ReserveData> {
2025 Reserves::<T, I>::get(id).into_inner()
2026 }
2027 }
2028}
2029
2030#[cfg(any(feature = "try-runtime", test))]
2031impl<T: Config<I>, I: 'static> Pallet<T, I> {
2032 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
2033 for asset_id in Reserves::<T, I>::iter_keys() {
2034 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Reserves data found");
2035 }
2036
2037 for asset_id in Metadata::<T, I>::iter_keys() {
2038 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Metadata found");
2039 }
2040
2041 for (asset_id, _, _) in Approvals::<T, I>::iter_keys() {
2042 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Approval found");
2043 }
2044
2045 for (asset_id, _) in Account::<T, I>::iter_keys() {
2046 ensure!(Asset::<T, I>::contains_key(asset_id.clone()), "Orphaned Account found");
2047 }
2048
2049 for (asset_id, details) in Asset::<T, I>::iter() {
2050 if details.status == AssetStatus::Destroying {
2051 continue;
2052 }
2053
2054 let mut calculated_supply = T::Balance::zero();
2055 let mut calculated_accounts = 0u32;
2056 let mut calculated_sufficients = 0u32;
2057
2058 for (who, account) in Account::<T, I>::iter_prefix(&asset_id) {
2059 let held = T::Holder::balance_on_hold(asset_id.clone(), &who).unwrap_or_default();
2060 calculated_supply =
2061 calculated_supply.saturating_add(account.balance).saturating_add(held);
2062 calculated_accounts += 1;
2063
2064 if matches!(account.reason, ExistenceReason::Sufficient) {
2065 calculated_sufficients += 1;
2066 }
2067
2068 let total_balance = account.balance.saturating_add(held);
2069 if total_balance < details.min_balance {
2070 if !matches!(
2071 account.reason,
2072 ExistenceReason::DepositHeld(_) | ExistenceReason::DepositFrom(_, _)
2073 ) {
2074 log::warn!(
2075 "Account {who:?} for asset {asset_id:?} has total balance below min_balance but no deposit. Balance: {:?}, Held: {:?}, Min balance: {:?}, Reason: {:?}",
2076 account.balance,
2077 held,
2078 details.min_balance,
2079 account.reason,
2080 );
2081 }
2082 }
2083 }
2084
2085 // Using >= instead of == because the provided `do_refund` implementation
2086 // historically destroyed the account balance without decrementing the asset
2087 // supply. Although this has been fixed, existing on-chain state may still
2088 // contain overcounted supply from prior refunds.
2089 // TODO: add a migration to recalculate supply, then tighten this to `==`.
2090 ensure!(details.supply >= calculated_supply, "Asset supply mismatch");
2091 if details.accounts != calculated_accounts {
2092 // Legacy error in Kusama Asset Hub that needs to be cleaned up.
2093 log::error!(
2094 "Asset {asset_id:?} account count mismatch: calculated {calculated_accounts} vs expected {}",
2095 details.accounts,
2096 );
2097 }
2098 ensure!(
2099 details.sufficients == calculated_sufficients,
2100 "Asset sufficients count mismatch"
2101 );
2102
2103 let calculated_approvals = Approvals::<T, I>::iter_prefix((&asset_id,)).count() as u32;
2104
2105 if details.approvals != calculated_approvals {
2106 log::error!(
2107 "Asset {asset_id:?} approvals count mismatch: calculated {calculated_approvals} vs expected {}",
2108 details.approvals,
2109 );
2110
2111 return Err("Asset approvals count mismatch".into());
2112 }
2113 }
2114 Ok(())
2115 }
2116}
2117
2118sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $);