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