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