diff --git a/frame/generic-asset/src/lib.rs b/frame/generic-asset/src/lib.rs index 0a0209b20f175..3b509a683d705 100644 --- a/frame/generic-asset/src/lib.rs +++ b/frame/generic-asset/src/lib.rs @@ -95,9 +95,10 @@ //! This will emit the `Transferred` event. //! - `reserve`: Moves an amount from free balance to reserved balance. //! - `unreserve`: Move up to an amount from reserved balance to free balance. This function cannot fail. +//! - `mint_free`: Mint to an account's free balance. +//! - `burn_free`: Burn an account's free balance. //! - `slash`: Deduct up to an amount from the combined balance of `who`, preferring to deduct from the //! free balance. This function cannot fail. -//! - `reward`: Add up to an amount to the free balance of an account. //! - `slash_reserved`: Deduct up to an amount from reserved balance of an account. This function cannot fail. //! - `repatriate_reserved`: Move up to an amount from reserved balance of an account to free balance of another //! account. @@ -109,7 +110,7 @@ //! //! The following examples show how to use the Generic Asset module in your custom module. //! -//! ### Examples from the PRML +//! ### Examples from the frame module //! //! The Fees module uses the `Currency` trait to handle fee charge/refund, and its types inherit from `Currency`: //! @@ -162,7 +163,7 @@ use sp_runtime::traits::{ use sp_std::prelude::*; use sp_std::{cmp, result, fmt::Debug}; use frame_support::{ - decl_event, decl_module, decl_storage, ensure, dispatch, decl_error, + decl_event, decl_module, decl_storage, ensure, decl_error, traits::{ Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, ReservableCurrency, SignedImbalance, UpdateBalanceOutcome, WithdrawReason, WithdrawReasons, TryDrop, @@ -359,7 +360,7 @@ decl_module! { fn deposit_event() = default; /// Create a new kind of asset. - fn create(origin, options: AssetOptions) -> dispatch::DispatchResult { + fn create(origin, options: AssetOptions) -> DispatchResult { let origin = ensure_signed(origin)?; let id = Self::next_asset_id(); @@ -392,7 +393,7 @@ decl_module! { origin, #[compact] asset_id: T::AssetId, new_permission: PermissionLatest - ) -> dispatch::DispatchResult { + ) -> DispatchResult { let origin = ensure_signed(origin)?; let permissions: PermissionVersions = new_permission.into(); @@ -410,56 +411,20 @@ decl_module! { /// Mints an asset, increases its total issuance. /// The origin must have `mint` permissions. - fn mint(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) - -> dispatch::DispatchResult - { - let origin = ensure_signed(origin)?; - if Self::check_permission(&asset_id, &origin, &PermissionType::Mint) { - let original_free_balance = Self::free_balance(&asset_id, &to); - let current_total_issuance = >::get(asset_id); - let new_total_issuance = current_total_issuance.checked_add(&amount) - .ok_or(Error::::TotalMintingOverflow)?; - let value = original_free_balance.checked_add(&amount) - .ok_or(Error::::FreeMintingOverflow)?; - - >::insert(asset_id, new_total_issuance); - Self::set_free_balance(&asset_id, &to, value); - - Self::deposit_event(RawEvent::Minted(asset_id, to, amount)); - - Ok(()) - } else { - Err(Error::::NoMintPermission)? - } + fn mint(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::mint_free(&asset_id, &who, &to, &amount)?; + Self::deposit_event(RawEvent::Minted(asset_id, to, amount)); + Ok(()) } /// Burns an asset, decreases its total issuance. - /// /// The `origin` must have `burn` permissions. - fn burn(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) - -> dispatch::DispatchResult - { - let origin = ensure_signed(origin)?; - - if Self::check_permission(&asset_id, &origin, &PermissionType::Burn) { - let original_free_balance = Self::free_balance(&asset_id, &to); - - let current_total_issuance = >::get(asset_id); - let new_total_issuance = current_total_issuance.checked_sub(&amount) - .ok_or(Error::::TotalBurningUnderflow)?; - let value = original_free_balance.checked_sub(&amount) - .ok_or(Error::::FreeBurningUnderflow)?; - - >::insert(asset_id, new_total_issuance); - - Self::set_free_balance(&asset_id, &to, value); - - Self::deposit_event(RawEvent::Burned(asset_id, to, amount)); - - Ok(()) - } else { - Err(Error::::NoBurnPermission)? - } + fn burn(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> DispatchResult { + let who = ensure_signed(origin)?; + Self::burn_free(&asset_id, &who, &to, &amount)?; + Self::deposit_event(RawEvent::Burned(asset_id, to, amount)); + Ok(()) } /// Can be used to create reserved tokens. @@ -468,7 +433,7 @@ decl_module! { origin, asset_id: T::AssetId, options: AssetOptions - ) -> dispatch::DispatchResult { + ) -> DispatchResult { ensure_root(origin)?; Self::create_asset(Some(asset_id), None, options) } @@ -565,6 +530,53 @@ impl Module { >::get(asset_id, who) } + /// Mint to an account's free balance, without event + pub fn mint_free( + asset_id: &T::AssetId, + who: &T::AccountId, + to: &T::AccountId, + amount: &T::Balance, + ) -> DispatchResult { + if Self::check_permission(asset_id, who, &PermissionType::Mint) { + let original_free_balance = Self::free_balance(&asset_id, &to); + let current_total_issuance = >::get(asset_id); + let new_total_issuance = current_total_issuance.checked_add(&amount) + .ok_or(Error::::TotalMintingOverflow)?; + let value = original_free_balance.checked_add(&amount) + .ok_or(Error::::FreeMintingOverflow)?; + + >::insert(asset_id, new_total_issuance); + Self::set_free_balance(&asset_id, &to, value); + Ok(()) + } else { + Err(Error::::NoMintPermission)? + } + } + + /// Burn an account's free balance, without event + pub fn burn_free( + asset_id: &T::AssetId, + who: &T::AccountId, + to: &T::AccountId, + amount: &T::Balance, + ) -> DispatchResult { + if Self::check_permission(asset_id, who, &PermissionType::Burn) { + let original_free_balance = Self::free_balance(asset_id, to); + + let current_total_issuance = >::get(asset_id); + let new_total_issuance = current_total_issuance.checked_sub(amount) + .ok_or(Error::::TotalBurningUnderflow)?; + let value = original_free_balance.checked_sub(amount) + .ok_or(Error::::FreeBurningUnderflow)?; + + >::insert(asset_id, new_total_issuance); + Self::set_free_balance(asset_id, to, value); + Ok(()) + } else { + Err(Error::::NoBurnPermission)? + } + } + /// Creates an asset. /// /// # Arguments @@ -577,7 +589,7 @@ impl Module { asset_id: Option, from_account: Option, options: AssetOptions, - ) -> dispatch::DispatchResult { + ) -> DispatchResult { let asset_id = if let Some(asset_id) = asset_id { ensure!(!>::exists(&asset_id), Error::::IdAlreadyTaken); ensure!(asset_id < Self::next_asset_id(), Error::::IdUnavailable); @@ -610,7 +622,7 @@ impl Module { from: &T::AccountId, to: &T::AccountId, amount: T::Balance - ) -> dispatch::DispatchResult { + ) -> DispatchResult { let new_balance = Self::free_balance(asset_id, from) .checked_sub(&amount) .ok_or(Error::::InsufficientBalance)?; @@ -631,7 +643,7 @@ impl Module { from: &T::AccountId, to: &T::AccountId, amount: T::Balance, - ) -> dispatch::DispatchResult { + ) -> DispatchResult { Self::make_transfer(asset_id, from, to, amount)?; if from != to { @@ -646,7 +658,7 @@ impl Module { /// If the free balance is lower than `amount`, then no funds will be moved and an `Err` will /// be returned. This is different behavior than `unreserve`. pub fn reserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) - -> dispatch::DispatchResult + -> DispatchResult { // Do we need to consider that this is an atomic transaction? let original_reserve_balance = Self::reserved_balance(asset_id, who); @@ -683,7 +695,7 @@ impl Module { /// then `Some(remaining)` will be returned. Full completion is given by `None`. /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that /// the caller will do this. - fn slash(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { + pub fn slash(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { let free_balance = Self::free_balance(asset_id, who); let free_slash = sp_std::cmp::min(free_balance, amount); let new_free_balance = free_balance - free_slash; @@ -701,7 +713,7 @@ impl Module { /// is less than `amount`, then a non-zero second item will be returned. /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that /// the caller will do this. - fn slash_reserved(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { + pub fn slash_reserved(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { let original_reserve_balance = Self::reserved_balance(asset_id, who); let slash = sp_std::cmp::min(original_reserve_balance, amount); let new_reserve_balance = original_reserve_balance - slash; @@ -720,7 +732,7 @@ impl Module { /// the `remaining` would be returned, else `Zero::zero()`. /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that /// the caller will do this. - fn repatriate_reserved( + pub fn repatriate_reserved( asset_id: &T::AssetId, who: &T::AccountId, beneficiary: &T::AccountId, @@ -785,7 +797,7 @@ impl Module { _amount: T::Balance, reasons: WithdrawReasons, new_balance: T::Balance, - ) -> dispatch::DispatchResult { + ) -> DispatchResult { if asset_id != &Self::staking_asset_id() { return Ok(()); }