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