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