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