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