From 6d654a336be25ed6ab4171db52162e858deb41c3 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Mon, 21 Sep 2020 11:16:50 +0200 Subject: [PATCH] Remove generic asset palet (#7156) --- substrate/Cargo.lock | 14 - substrate/Cargo.toml | 1 - substrate/frame/generic-asset/Cargo.toml | 35 - substrate/frame/generic-asset/README.md | 131 -- substrate/frame/generic-asset/src/lib.rs | 1369 -------------------- substrate/frame/generic-asset/src/mock.rs | 152 --- substrate/frame/generic-asset/src/tests.rs | 1215 ----------------- 7 files changed, 2917 deletions(-) delete mode 100644 substrate/frame/generic-asset/Cargo.toml delete mode 100644 substrate/frame/generic-asset/README.md delete mode 100644 substrate/frame/generic-asset/src/lib.rs delete mode 100644 substrate/frame/generic-asset/src/mock.rs delete mode 100644 substrate/frame/generic-asset/src/tests.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 2cfe702399..f8c6bab59d 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -4541,20 +4541,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-generic-asset" -version = "2.0.0-rc6" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-grandpa" version = "2.0.0-rc6" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 8f483234df..e69a95f1e4 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -79,7 +79,6 @@ members = [ "frame/example-offchain-worker", "frame/executive", "frame/finality-tracker", - "frame/generic-asset", "frame/grandpa", "frame/identity", "frame/im-online", diff --git a/substrate/frame/generic-asset/Cargo.toml b/substrate/frame/generic-asset/Cargo.toml deleted file mode 100644 index 9dfc769915..0000000000 --- a/substrate/frame/generic-asset/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "pallet-generic-asset" -version = "2.0.0-rc6" -authors = ["Centrality Developers "] -edition = "2018" -license = "Apache-2.0" -homepage = "https://substrate.dev" -repository = "https://github.com/paritytech/substrate/" -description = "FRAME pallet for generic asset management" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } - -[dev-dependencies] -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } - -[features] -default = ["std"] -std =[ - "serde/std", - "codec/std", - "sp-std/std", - "sp-runtime/std", - "frame-support/std", - "frame-system/std", -] diff --git a/substrate/frame/generic-asset/README.md b/substrate/frame/generic-asset/README.md deleted file mode 100644 index ab82be54b2..0000000000 --- a/substrate/frame/generic-asset/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# Generic Asset Module - -The Generic Asset module provides functionality for handling accounts and asset balances. - -## Overview - -The Generic Asset module provides functions for: - -- Creating a new kind of asset. -- Setting permissions of an asset. -- Getting and setting free balances. -- Retrieving total, reserved and unreserved balances. -- Repatriating a reserved balance to a beneficiary account. -- Transferring a balance between accounts (when not reserved). -- Slashing an account balance. -- Managing total issuance. -- Setting and managing locks. - -### Terminology - -- **Staking Asset:** The asset for staking, to participate as Validators in the network. -- **Spending Asset:** The asset for payment, such as paying transfer fees, gas fees, etc. -- **Permissions:** A set of rules for a kind of asset, defining the allowed operations to the asset, and which -accounts are allowed to possess it. -- **Total Issuance:** The total number of units in existence in a system. -- **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters -for most operations. When this balance falls below the existential deposit, most functionality of the account is -removed. When both it and the reserved balance are deleted, then the account is said to be dead. -- **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance -can still be slashed, but only after all the free balance has been slashed. If the reserved balance falls below the -existential deposit then it and any related functionality will be deleted. When both it and the free balance are -deleted, then the account is said to be dead. -- **Imbalance:** A condition when some assets were credited or debited without equal and opposite accounting -(i.e. a difference between total issuance and account balances). Functions that result in an imbalance will -return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is -simply dropped, it should automatically maintain any book-keeping such as total issuance.) -- **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple -locks always operate over the same funds, so they "overlay" rather than "stack". - -### Implementations - -The Generic Asset module provides `AssetCurrency`, which implements the following traits. If these traits provide -the functionality that you need, you can avoid coupling with the Generic Asset module. - -- `Currency`: Functions for dealing with a fungible assets system. -- `ReservableCurrency`: Functions for dealing with assets that can be reserved from an account. -- `LockableCurrency`: Functions for dealing with accounts that allow liquidity restrictions. -- `Imbalance`: Functions for handling imbalances between total issuance in the system and account balances. -Must be used when a function creates new assets (e.g. a reward) or destroys some assets (e.g. a system fee). - -The Generic Asset module provides two types of `AssetCurrency` as follows. - -- `StakingAssetCurrency`: Currency for staking. -- `SpendingAssetCurrency`: Currency for payments such as transfer fee, gas fee. - -## Interface - -### Dispatchable Functions - -- `create`: Create a new kind of asset. -- `transfer`: Transfer some liquid free balance to another account. -- `update_permission`: Updates permission for a given `asset_id` and an account. The origin of this call -must have update permissions. -- `mint`: Mint an asset, increases its total issuance. The origin of this call must have mint permissions. -- `burn`: Burn an asset, decreases its total issuance. The origin of this call must have burn permissions. -- `create_reserved`: Create a new kind of reserved asset. The origin of this call must be root. - -### Public Functions - -- `total_balance`: Get an account's total balance of an asset kind. -- `free_balance`: Get an account's free balance of an asset kind. -- `reserved_balance`: Get an account's reserved balance of an asset kind. -- `create_asset`: Creates an asset. -- `make_transfer`: Transfer some liquid free balance from one account to another. -This will not emit the `Transferred` event. -- `make_transfer_with_event`: Transfer some liquid free balance from one account to another. -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. -- `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. -- `check_permission`: Check permission to perform burn, mint or update. -- `ensure_can_withdraw`: Check if the account is able to make a withdrawal of the given amount - for the given reason. - -### Usage - -The following examples show how to use the Generic Asset Pallet in your custom pallet. - -### Examples from the FRAME pallet - -The Fees Pallet uses the `Currency` trait to handle fee charge/refund, and its types inherit from `Currency`: - -```rust -use frame_support::{ - dispatch, - traits::{Currency, ExistenceRequirement, WithdrawReason}, -}; -type AssetOf = <::Currency as Currency<::AccountId>>::Balance; - -fn charge_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { - // ... - T::Currency::withdraw( - transactor, - amount, - WithdrawReason::TransactionPayment.into(), - ExistenceRequirement::KeepAlive, - )?; - // ... - Ok(()) -} - -fn refund_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { - // ... - T::Currency::deposit_into_existing(transactor, amount)?; - // ... - Ok(()) -} - -``` - -## Genesis config - -The Generic Asset Pallet depends on the [`GenesisConfig`](./struct.GenesisConfig.html). - -License: Apache-2.0 \ No newline at end of file diff --git a/substrate/frame/generic-asset/src/lib.rs b/substrate/frame/generic-asset/src/lib.rs deleted file mode 100644 index 6c3683312d..0000000000 --- a/substrate/frame/generic-asset/src/lib.rs +++ /dev/null @@ -1,1369 +0,0 @@ -// Copyright 2019-2020 -// by Centrality Investments Ltd. -// and Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! # Generic Asset Module -//! -//! The Generic Asset module provides functionality for handling accounts and asset balances. -//! -//! ## Overview -//! -//! The Generic Asset module provides functions for: -//! -//! - Creating a new kind of asset. -//! - Setting permissions of an asset. -//! - Getting and setting free balances. -//! - Retrieving total, reserved and unreserved balances. -//! - Repatriating a reserved balance to a beneficiary account. -//! - Transferring a balance between accounts (when not reserved). -//! - Slashing an account balance. -//! - Managing total issuance. -//! - Setting and managing locks. -//! -//! ### Terminology -//! -//! - **Staking Asset:** The asset for staking, to participate as Validators in the network. -//! - **Spending Asset:** The asset for payment, such as paying transfer fees, gas fees, etc. -//! - **Permissions:** A set of rules for a kind of asset, defining the allowed operations to the asset, and which -//! accounts are allowed to possess it. -//! - **Total Issuance:** The total number of units in existence in a system. -//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters -//! for most operations. When this balance falls below the existential deposit, most functionality of the account is -//! removed. When both it and the reserved balance are deleted, then the account is said to be dead. -//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance -//! can still be slashed, but only after all the free balance has been slashed. If the reserved balance falls below the -//! existential deposit then it and any related functionality will be deleted. When both it and the free balance are -//! deleted, then the account is said to be dead. -//! - **Imbalance:** A condition when some assets were credited or debited without equal and opposite accounting -//! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will -//! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is -//! simply dropped, it should automatically maintain any book-keeping such as total issuance.) -//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple -//! locks always operate over the same funds, so they "overlay" rather than "stack". -//! -//! ### Implementations -//! -//! The Generic Asset module provides `AssetCurrency`, which implements the following traits. If these traits provide -//! the functionality that you need, you can avoid coupling with the Generic Asset module. -//! -//! - `Currency`: Functions for dealing with a fungible assets system. -//! - `ReservableCurrency`: Functions for dealing with assets that can be reserved from an account. -//! - `LockableCurrency`: Functions for dealing with accounts that allow liquidity restrictions. -//! - `Imbalance`: Functions for handling imbalances between total issuance in the system and account balances. -//! Must be used when a function creates new assets (e.g. a reward) or destroys some assets (e.g. a system fee). -//! -//! The Generic Asset module provides two types of `AssetCurrency` as follows. -//! -//! - `StakingAssetCurrency`: Currency for staking. -//! - `SpendingAssetCurrency`: Currency for payments such as transfer fee, gas fee. -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! - `create`: Create a new kind of asset. -//! - `transfer`: Transfer some liquid free balance to another account. -//! - `update_permission`: Updates permission for a given `asset_id` and an account. The origin of this call -//! must have update permissions. -//! - `mint`: Mint an asset, increases its total issuance. The origin of this call must have mint permissions. -//! - `burn`: Burn an asset, decreases its total issuance. The origin of this call must have burn permissions. -//! - `create_reserved`: Create a new kind of reserved asset. The origin of this call must be root. -//! -//! ### Public Functions -//! -//! - `total_balance`: Get an account's total balance of an asset kind. -//! - `free_balance`: Get an account's free balance of an asset kind. -//! - `reserved_balance`: Get an account's reserved balance of an asset kind. -//! - `create_asset`: Creates an asset. -//! - `make_transfer`: Transfer some liquid free balance from one account to another. -//! This will not emit the `Transferred` event. -//! - `make_transfer_with_event`: Transfer some liquid free balance from one account to another. -//! 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. -//! - `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. -//! - `check_permission`: Check permission to perform burn, mint or update. -//! - `ensure_can_withdraw`: Check if the account is able to make a withdrawal of the given amount -//! for the given reason. -//! -//! ### Usage -//! -//! The following examples show how to use the Generic Asset Pallet in your custom pallet. -//! -//! ### Examples from the FRAME pallet -//! -//! The Fees Pallet uses the `Currency` trait to handle fee charge/refund, and its types inherit from `Currency`: -//! -//! ``` -//! use frame_support::{ -//! dispatch, -//! traits::{Currency, ExistenceRequirement, WithdrawReason}, -//! }; -//! # pub trait Trait: frame_system::Trait { -//! # type Currency: Currency; -//! # } -//! type AssetOf = <::Currency as Currency<::AccountId>>::Balance; -//! -//! fn charge_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { -//! // ... -//! T::Currency::withdraw( -//! transactor, -//! amount, -//! WithdrawReason::TransactionPayment.into(), -//! ExistenceRequirement::KeepAlive, -//! )?; -//! // ... -//! Ok(()) -//! } -//! -//! fn refund_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { -//! // ... -//! T::Currency::deposit_into_existing(transactor, amount)?; -//! // ... -//! Ok(()) -//! } -//! -//! # fn main() {} -//! ``` -//! -//! ## Genesis config -//! -//! The Generic Asset Pallet depends on the [`GenesisConfig`](./struct.GenesisConfig.html). - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, HasCompact, Input, Output, Error as CodecError}; - -use sp_runtime::{RuntimeDebug, DispatchResult, DispatchError}; -use sp_runtime::traits::{ - CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Saturating, AtLeast32Bit, - Zero, Bounded, AtLeast32BitUnsigned -}; - -use sp_std::prelude::*; -use sp_std::{cmp, result, fmt::Debug}; -use frame_support::{ - decl_event, decl_module, decl_storage, ensure, decl_error, - traits::{ - Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, - ReservableCurrency, SignedImbalance, WithdrawReason, WithdrawReasons, TryDrop, - BalanceStatus, - }, - Parameter, StorageMap, -}; -use frame_system::{ensure_signed, ensure_root}; - -mod mock; -mod tests; - -pub use self::imbalances::{NegativeImbalance, PositiveImbalance}; - -pub trait Trait: frame_system::Trait { - type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + Debug + - MaybeSerializeDeserialize; - type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy; - type Event: From> + Into<::Event>; -} - -pub trait Subtrait: frame_system::Trait { - type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + Debug + - MaybeSerializeDeserialize; - type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy; -} - -impl Subtrait for T { - type Balance = T::Balance; - type AssetId = T::AssetId; -} - -/// Asset creation options. -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -pub struct AssetOptions { - /// Initial issuance of this asset. All deposit to the creator of the asset. - #[codec(compact)] - pub initial_issuance: Balance, - /// Which accounts are allowed to possess this asset. - pub permissions: PermissionLatest, -} - -/// Owner of an asset. -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -pub enum Owner { - /// No owner. - None, - /// Owned by an AccountId - Address(AccountId), -} - -impl Default for Owner { - fn default() -> Self { - Owner::None - } -} - -/// Asset permissions -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -pub struct PermissionsV1 { - /// Who have permission to update asset permission - pub update: Owner, - /// Who have permission to mint new asset - pub mint: Owner, - /// Who have permission to burn asset - pub burn: Owner, -} - -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -#[repr(u8)] -enum PermissionVersionNumber { - V1 = 0, -} - -/// Versioned asset permission -#[derive(Clone, PartialEq, Eq, RuntimeDebug)] -pub enum PermissionVersions { - V1(PermissionsV1), -} - -/// Asset permission types -pub enum PermissionType { - /// Permission to burn asset permission - Burn, - /// Permission to mint new asset - Mint, - /// Permission to update asset - Update, -} - -/// Alias to latest asset permissions -pub type PermissionLatest = PermissionsV1; - -impl Default for PermissionVersions { - fn default() -> Self { - PermissionVersions::V1(Default::default()) - } -} - -impl Encode for PermissionVersions { - fn encode_to(&self, dest: &mut T) { - match self { - PermissionVersions::V1(payload) => { - dest.push(&PermissionVersionNumber::V1); - dest.push(payload); - }, - } - } -} - -impl codec::EncodeLike for PermissionVersions {} - -impl Decode for PermissionVersions { - fn decode(input: &mut I) -> core::result::Result { - let version = PermissionVersionNumber::decode(input)?; - Ok( - match version { - PermissionVersionNumber::V1 => PermissionVersions::V1(Decode::decode(input)?) - } - ) - } -} - -impl Default for PermissionsV1 { - fn default() -> Self { - PermissionsV1 { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - } - } -} - -impl Into> for PermissionVersions { - fn into(self) -> PermissionLatest { - match self { - PermissionVersions::V1(v1) => v1, - } - } -} - -/// Converts the latest permission to other version. -impl Into> for PermissionLatest { - fn into(self) -> PermissionVersions { - PermissionVersions::V1(self) - } -} - -decl_error! { - /// Error for the generic-asset module. - pub enum Error for Module { - /// No new assets id available. - NoIdAvailable, - /// Cannot transfer zero amount. - ZeroAmount, - /// The origin does not have enough permission to update permissions. - NoUpdatePermission, - /// The origin does not have permission to mint an asset. - NoMintPermission, - /// The origin does not have permission to burn an asset. - NoBurnPermission, - /// Total issuance got overflowed after minting. - TotalMintingOverflow, - /// Free balance got overflowed after minting. - FreeMintingOverflow, - /// Total issuance got underflowed after burning. - TotalBurningUnderflow, - /// Free balance got underflowed after burning. - FreeBurningUnderflow, - /// Asset id is already taken. - IdAlreadyTaken, - /// Asset id not available. - IdUnavailable, - /// The balance is too low to send amount. - InsufficientBalance, - /// The account liquidity restrictions prevent withdrawal. - LiquidityRestrictions, - } -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - type Error = Error; - - fn deposit_event() = default; - - /// Create a new kind of asset. - #[weight = 0] - fn create(origin, options: AssetOptions) -> DispatchResult { - let origin = ensure_signed(origin)?; - Self::create_asset(None, Some(origin), options) - } - - /// Transfer some liquid free balance to another account. - #[weight = 0] - pub fn transfer(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, #[compact] amount: T::Balance) { - let origin = ensure_signed(origin)?; - ensure!(!amount.is_zero(), Error::::ZeroAmount); - Self::make_transfer_with_event(&asset_id, &origin, &to, amount)?; - } - - /// Updates permission for a given `asset_id` and an account. - /// - /// The `origin` must have `update` permission. - #[weight = 0] - fn update_permission( - origin, - #[compact] asset_id: T::AssetId, - new_permission: PermissionLatest - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - - let permissions: PermissionVersions = new_permission.into(); - - if Self::check_permission(&asset_id, &origin, &PermissionType::Update) { - >::insert(asset_id, &permissions); - - Self::deposit_event(RawEvent::PermissionUpdated(asset_id, permissions.into())); - - Ok(()) - } else { - Err(Error::::NoUpdatePermission)? - } - } - - /// Mints an asset, increases its total issuance. - /// The origin must have `mint` permissions. - #[weight = 0] - 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. - #[weight = 0] - 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. - /// Requires Root call. - #[weight = 0] - fn create_reserved( - origin, - asset_id: T::AssetId, - options: AssetOptions - ) -> DispatchResult { - ensure_root(origin)?; - Self::create_asset(Some(asset_id), None, options) - } - } -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct BalanceLock { - pub id: LockIdentifier, - pub amount: Balance, - pub reasons: WithdrawReasons, -} - -decl_storage! { - trait Store for Module as GenericAsset { - /// Total issuance of a given asset. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { - let issuance = config.initial_balance * (config.endowed_accounts.len() as u32).into(); - config.assets.iter().map(|id| (id.clone(), issuance)).collect::>() - }): map hasher(twox_64_concat) T::AssetId => T::Balance; - - /// The free balance of a given asset under an account. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub FreeBalance: - double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance; - - /// The reserved balance of a given asset under an account. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub ReservedBalance: - double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance; - - /// Next available ID for user-created asset. - pub NextAssetId get(fn next_asset_id) config(): T::AssetId; - - /// Permission options for a given asset. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub Permissions get(fn get_permission): - map hasher(twox_64_concat) T::AssetId => PermissionVersions; - - /// Any liquidity locks on some account balances. - pub Locks get(fn locks): - map hasher(blake2_128_concat) T::AccountId => Vec>; - - /// The identity of the asset which is the one that is designated for the chain's staking system. - pub StakingAssetId get(fn staking_asset_id) config(): T::AssetId; - - /// The identity of the asset which is the one that is designated for paying the chain's transaction fee. - pub SpendingAssetId get(fn spending_asset_id) config(): T::AssetId; - } - add_extra_genesis { - config(assets): Vec; - config(initial_balance): T::Balance; - config(endowed_accounts): Vec; - - build(|config: &GenesisConfig| { - config.assets.iter().for_each(|asset_id| { - config.endowed_accounts.iter().for_each(|account_id| { - >::insert(asset_id, account_id, &config.initial_balance); - }); - }); - }); - } -} - -decl_event!( - pub enum Event where - ::AccountId, - ::Balance, - ::AssetId, - AssetOptions = AssetOptions<::Balance, ::AccountId> - { - /// Asset created. \[asset_id, creator, asset_options\] - Created(AssetId, AccountId, AssetOptions), - /// Asset transfer succeeded. \[asset_id, from, to, amount\] - Transferred(AssetId, AccountId, AccountId, Balance), - /// Asset permission updated. \[asset_id, new_permissions\] - PermissionUpdated(AssetId, PermissionLatest), - /// New asset minted. \[asset_id, account, amount\] - Minted(AssetId, AccountId, Balance), - /// Asset burned. \[asset_id, account, amount\] - Burned(AssetId, AccountId, Balance), - } -); - -impl Module { - // PUBLIC IMMUTABLES - - /// Get an account's total balance of an asset kind. - pub fn total_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { - Self::free_balance(asset_id, who) + Self::reserved_balance(asset_id, who) - } - - /// Get an account's free balance of an asset kind. - pub fn free_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { - >::get(asset_id, who) - } - - /// Get an account's reserved balance of an asset kind. - pub fn reserved_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { - >::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 - /// * `asset_id`: An ID of a reserved asset. - /// If not provided, a user-generated asset will be created with the next available ID. - /// * `from_account`: The initiator account of this call - /// * `asset_options`: Asset creation options. - /// - pub fn create_asset( - asset_id: Option, - from_account: Option, - options: AssetOptions, - ) -> DispatchResult { - let asset_id = if let Some(asset_id) = asset_id { - ensure!(!>::contains_key(&asset_id), Error::::IdAlreadyTaken); - ensure!(asset_id < Self::next_asset_id(), Error::::IdUnavailable); - asset_id - } else { - let asset_id = Self::next_asset_id(); - let next_id = asset_id - .checked_add(&One::one()) - .ok_or(Error::::NoIdAvailable)?; - >::put(next_id); - asset_id - }; - - let account_id = from_account.unwrap_or_default(); - let permissions: PermissionVersions = options.permissions.clone().into(); - - >::insert(asset_id, &options.initial_issuance); - >::insert(&asset_id, &account_id, &options.initial_issuance); - >::insert(&asset_id, permissions); - - Self::deposit_event(RawEvent::Created(asset_id, account_id, options)); - - Ok(()) - } - - /// Transfer some liquid free balance from one account to another. - /// This will not emit the `Transferred` event. - pub fn make_transfer( - asset_id: &T::AssetId, - from: &T::AccountId, - to: &T::AccountId, - amount: T::Balance - ) -> DispatchResult { - let new_balance = Self::free_balance(asset_id, from) - .checked_sub(&amount) - .ok_or(Error::::InsufficientBalance)?; - Self::ensure_can_withdraw(asset_id, from, amount, WithdrawReason::Transfer.into(), new_balance)?; - - if from != to { - >::mutate(asset_id, from, |balance| *balance -= amount); - >::mutate(asset_id, to, |balance| *balance += amount); - } - - Ok(()) - } - - /// Transfer some liquid free balance from one account to another. - /// This will emit the `Transferred` event. - pub fn make_transfer_with_event( - asset_id: &T::AssetId, - from: &T::AccountId, - to: &T::AccountId, - amount: T::Balance, - ) -> DispatchResult { - Self::make_transfer(asset_id, from, to, amount)?; - - if from != to { - Self::deposit_event(RawEvent::Transferred(*asset_id, from.clone(), to.clone(), amount)); - } - - Ok(()) - } - - /// Move `amount` from free balance to reserved balance. - /// - /// 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) - -> DispatchResult - { - // Do we need to consider that this is an atomic transaction? - let original_reserve_balance = Self::reserved_balance(asset_id, who); - let original_free_balance = Self::free_balance(asset_id, who); - if original_free_balance < amount { - Err(Error::::InsufficientBalance)? - } - let new_reserve_balance = original_reserve_balance + amount; - Self::set_reserved_balance(asset_id, who, new_reserve_balance); - let new_free_balance = original_free_balance - amount; - Self::set_free_balance(asset_id, who, new_free_balance); - Ok(()) - } - - /// Moves up to `amount` from reserved balance to free balance. This function cannot fail. - /// - /// As many assets up to `amount` will be moved as possible. If the reserve balance of `who` - /// is less than `amount`, then the remaining amount will be returned. - /// NOTE: This is different behavior than `reserve`. - pub fn unreserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> T::Balance { - let b = Self::reserved_balance(asset_id, who); - let actual = sp_std::cmp::min(b, amount); - let original_free_balance = Self::free_balance(asset_id, who); - let new_free_balance = original_free_balance + actual; - Self::set_free_balance(asset_id, who, new_free_balance); - Self::set_reserved_balance(asset_id, who, b - actual); - amount - actual - } - - /// Deduct up to `amount` from the combined balance of `who`, preferring to deduct from the - /// free balance. This function cannot fail. - /// - /// As much funds up to `amount` will be deducted as possible. If this is less than `amount` - /// 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. - 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; - Self::set_free_balance(asset_id, who, new_free_balance); - if free_slash < amount { - Self::slash_reserved(asset_id, who, amount - free_slash) - } else { - None - } - } - - /// Deducts up to `amount` from reserved balance of `who`. This function cannot fail. - /// - /// As much funds up to `amount` will be deducted as possible. If the reserve balance of `who` - /// 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. - 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; - Self::set_reserved_balance(asset_id, who, new_reserve_balance); - if amount == slash { - None - } else { - Some(amount - slash) - } - } - - /// Move up to `amount` from reserved balance of account `who` to balance of account - /// `beneficiary`, either free or reserved depending on `status`. - /// - /// As much funds up to `amount` will be moved as possible. If this is less than `amount`, then - /// 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. - pub fn repatriate_reserved( - asset_id: &T::AssetId, - who: &T::AccountId, - beneficiary: &T::AccountId, - amount: T::Balance, - status: BalanceStatus, - ) -> T::Balance { - let b = Self::reserved_balance(asset_id, who); - let slash = sp_std::cmp::min(b, amount); - - match status { - BalanceStatus::Free => { - let original_free_balance = Self::free_balance(asset_id, beneficiary); - let new_free_balance = original_free_balance + slash; - Self::set_free_balance(asset_id, beneficiary, new_free_balance); - } - BalanceStatus::Reserved => { - let original_reserved_balance = Self::reserved_balance(asset_id, beneficiary); - let new_reserved_balance = original_reserved_balance + slash; - Self::set_reserved_balance(asset_id, beneficiary, new_reserved_balance); - } - } - - let new_reserve_balance = b - slash; - Self::set_reserved_balance(asset_id, who, new_reserve_balance); - amount - slash - } - - /// Check permission to perform burn, mint or update. - /// - /// # Arguments - /// * `asset_id`: A `T::AssetId` type that contains the `asset_id`, which has the permission embedded. - /// * `who`: A `T::AccountId` type that contains the `account_id` for which to check permissions. - /// * `what`: The permission to check. - /// - pub fn check_permission(asset_id: &T::AssetId, who: &T::AccountId, what: &PermissionType) -> bool { - let permission_versions: PermissionVersions = Self::get_permission(asset_id); - let permission = permission_versions.into(); - - match (what, permission) { - ( - PermissionType::Burn, - PermissionLatest { - burn: Owner::Address(account), - .. - }, - ) => account == *who, - ( - PermissionType::Mint, - PermissionLatest { - mint: Owner::Address(account), - .. - }, - ) => account == *who, - ( - PermissionType::Update, - PermissionLatest { - update: Owner::Address(account), - .. - }, - ) => account == *who, - _ => false, - } - } - - /// Return `Ok` iff the account is able to make a withdrawal of the given amount - /// for the given reason. - /// - /// `Err(...)` with the reason why not otherwise. - pub fn ensure_can_withdraw( - asset_id: &T::AssetId, - who: &T::AccountId, - _amount: T::Balance, - reasons: WithdrawReasons, - new_balance: T::Balance, - ) -> DispatchResult { - if asset_id != &Self::staking_asset_id() { - return Ok(()); - } - - let locks = Self::locks(who); - if locks.is_empty() { - return Ok(()); - } - if Self::locks(who) - .into_iter().all(|l| new_balance >= l.amount || !l.reasons.intersects(reasons)) - { - Ok(()) - } else { - Err(Error::::LiquidityRestrictions)? - } - } - - // PRIVATE MUTABLES - - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn set_reserved_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) { - >::insert(asset_id, who, &balance); - } - - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn set_free_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) { - >::insert(asset_id, who, &balance); - } - - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - let mut new_lock = Some(BalanceLock { - id, - amount, - reasons, - }); - let mut locks = >::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - new_lock.take() - } else { - Some(l) - } - }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - >::insert(who, locks); - } - - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - let mut new_lock = Some(BalanceLock { - id, - amount, - reasons, - }); - let mut locks = >::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - new_lock.take().map(|nl| BalanceLock { - id: l.id, - amount: l.amount.max(nl.amount), - reasons: l.reasons | nl.reasons, - }) - } else { - Some(l) - } - }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - >::insert(who, locks); - } - - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - let mut locks = >::locks(who); - locks.retain(|l| l.id != id); - >::insert(who, locks); - } -} - -pub trait AssetIdProvider { - type AssetId; - fn asset_id() -> Self::AssetId; -} - -// wrapping these imbalances in a private module is necessary to ensure absolute privacy -// of the inner member. -mod imbalances { - use super::{ - result, AssetIdProvider, Imbalance, Saturating, StorageMap, Subtrait, Zero, TryDrop - }; - use sp_std::mem; - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been created without any equal and opposite accounting. - #[must_use] - pub struct PositiveImbalance>( - T::Balance, - sp_std::marker::PhantomData, - ); - impl PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - pub fn new(amount: T::Balance) -> Self { - PositiveImbalance(amount, Default::default()) - } - } - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been destroyed without any equal and opposite accounting. - #[must_use] - pub struct NegativeImbalance>( - T::Balance, - sp_std::marker::PhantomData, - ); - impl NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - pub fn new(amount: T::Balance) -> Self { - NegativeImbalance(amount, Default::default()) - } - } - - impl TryDrop for PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl Imbalance for PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - type Opposite = NegativeImbalance; - - fn zero() -> Self { - Self::new(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self::new(first), Self::new(second)) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> result::Result { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - if a >= b { - Ok(Self::new(a - b)) - } else { - Err(NegativeImbalance::new(b - a)) - } - } - fn peek(&self) -> T::Balance { - self.0.clone() - } - } - - impl TryDrop for NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl Imbalance for NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - type Opposite = PositiveImbalance; - - fn zero() -> Self { - Self::new(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self::new(first), Self::new(second)) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> result::Result { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - if a >= b { - Ok(Self::new(a - b)) - } else { - Err(PositiveImbalance::new(b - a)) - } - } - fn peek(&self) -> T::Balance { - self.0.clone() - } - } - - impl Drop for PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >>::mutate(&U::asset_id(), |v| *v = v.saturating_add(self.0)); - } - } - - impl Drop for NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >>::mutate(&U::asset_id(), |v| *v = v.saturating_sub(self.0)); - } - } -} - -// TODO: #2052 -// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by` -// using only the Subtrait (which defines only the types that are not dependent -// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a -// circular dependency with Trait having some types be dependent on PositiveImbalance -// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus -// its type declaration). -// This works as long as `increase_total_issuance_by` doesn't use the Imbalance -// types (basically for charging fees). -// This should eventually be refactored so that the two type items that do -// depend on the Imbalance type (TransactionPayment, DustRemoval) -// are placed in their own pallet. -struct ElevatedTrait(T); -impl Clone for ElevatedTrait { - fn clone(&self) -> Self { - unimplemented!() - } -} -impl PartialEq for ElevatedTrait { - fn eq(&self, _: &Self) -> bool { - unimplemented!() - } -} -impl Eq for ElevatedTrait {} -impl frame_system::Trait for ElevatedTrait { - type BaseCallFilter = T::BaseCallFilter; - type Origin = T::Origin; - type Call = T::Call; - type Index = T::Index; - type BlockNumber = T::BlockNumber; - type Hash = T::Hash; - type Hashing = T::Hashing; - type AccountId = T::AccountId; - type Lookup = T::Lookup; - type Header = T::Header; - type Event = (); - type BlockHashCount = T::BlockHashCount; - type MaximumBlockWeight = T::MaximumBlockWeight; - type DbWeight = (); - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); - type MaximumExtrinsicWeight = T::MaximumBlockWeight; - type MaximumBlockLength = T::MaximumBlockLength; - type AvailableBlockRatio = T::AvailableBlockRatio; - type Version = T::Version; - type ModuleToIndex = (); - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); -} -impl Trait for ElevatedTrait { - type Balance = T::Balance; - type AssetId = T::AssetId; - type Event = (); -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct AssetCurrency(sp_std::marker::PhantomData, sp_std::marker::PhantomData); - -impl Currency for AssetCurrency -where - T: Trait, - U: AssetIdProvider, -{ - type Balance = T::Balance; - type PositiveImbalance = PositiveImbalance; - type NegativeImbalance = NegativeImbalance; - - fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::free_balance(&who) + Self::reserved_balance(&who) - } - - fn free_balance(who: &T::AccountId) -> Self::Balance { - >::free_balance(&U::asset_id(), &who) - } - - /// Returns the total staking asset issuance - fn total_issuance() -> Self::Balance { - >::total_issuance(U::asset_id()) - } - - fn minimum_balance() -> Self::Balance { - Zero::zero() - } - - fn transfer( - transactor: &T::AccountId, - dest: &T::AccountId, - value: Self::Balance, - _: ExistenceRequirement, // no existential deposit policy for generic asset - ) -> DispatchResult { - >::make_transfer(&U::asset_id(), transactor, dest, value) - } - - fn ensure_can_withdraw( - who: &T::AccountId, - amount: Self::Balance, - reasons: WithdrawReasons, - new_balance: Self::Balance, - ) -> DispatchResult { - >::ensure_can_withdraw(&U::asset_id(), who, amount, reasons, new_balance) - } - - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - _: ExistenceRequirement, // no existential deposit policy for generic asset - ) -> result::Result { - let new_balance = Self::free_balance(who) - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; - Self::ensure_can_withdraw(who, value, reasons, new_balance)?; - >::set_free_balance(&U::asset_id(), who, new_balance); - Ok(NegativeImbalance::new(value)) - } - - fn deposit_into_existing( - who: &T::AccountId, - value: Self::Balance, - ) -> result::Result { - // No existential deposit rule and creation fee in GA. `deposit_into_existing` is same with `deposit_creating`. - Ok(Self::deposit_creating(who, value)) - } - - fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - let imbalance = Self::make_free_balance_be(who, Self::free_balance(who) + value); - if let SignedImbalance::Positive(p) = imbalance { - p - } else { - // Impossible, but be defensive. - Self::PositiveImbalance::zero() - } - } - - fn make_free_balance_be( - who: &T::AccountId, - balance: Self::Balance, - ) -> SignedImbalance { - let original = >::free_balance(&U::asset_id(), who); - let imbalance = if original <= balance { - SignedImbalance::Positive(PositiveImbalance::new(balance - original)) - } else { - SignedImbalance::Negative(NegativeImbalance::new(original - balance)) - }; - >::set_free_balance(&U::asset_id(), who, balance); - imbalance - } - - fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { - >::free_balance(&U::asset_id(), &who) >= value - } - - fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - let remaining = >::slash(&U::asset_id(), who, value); - if let Some(r) = remaining { - (NegativeImbalance::new(value - r), r) - } else { - (NegativeImbalance::new(value), Zero::zero()) - } - } - - fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { - >::mutate(&U::asset_id(), |issued| - issued.checked_sub(&amount).unwrap_or_else(|| { - amount = *issued; - Zero::zero() - }) - ); - PositiveImbalance::new(amount) - } - - fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { - >::mutate(&U::asset_id(), |issued| - *issued = issued.checked_add(&amount).unwrap_or_else(|| { - amount = Self::Balance::max_value() - *issued; - Self::Balance::max_value() - }) - ); - NegativeImbalance::new(amount) - } -} - -impl ReservableCurrency for AssetCurrency -where - T: Trait, - U: AssetIdProvider, -{ - fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { - Self::free_balance(who) - .checked_sub(&value) - .map_or(false, |new_balance| - >::ensure_can_withdraw( - &U::asset_id(), who, value, WithdrawReason::Reserve.into(), new_balance - ).is_ok() - ) - } - - fn reserved_balance(who: &T::AccountId) -> Self::Balance { - >::reserved_balance(&U::asset_id(), &who) - } - - fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { - >::reserve(&U::asset_id(), who, value) - } - - fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { - >::unreserve(&U::asset_id(), who, value) - } - - fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - let b = Self::reserved_balance(&who.clone()); - let slash = cmp::min(b, value); - - >::set_reserved_balance(&U::asset_id(), who, b - slash); - (NegativeImbalance::new(slash), value - slash) - } - - fn repatriate_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: BalanceStatus, - ) -> result::Result { - Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value, status)) - } -} - -pub struct StakingAssetIdProvider(sp_std::marker::PhantomData); - -impl AssetIdProvider for StakingAssetIdProvider { - type AssetId = T::AssetId; - fn asset_id() -> Self::AssetId { - >::staking_asset_id() - } -} - -pub struct SpendingAssetIdProvider(sp_std::marker::PhantomData); - -impl AssetIdProvider for SpendingAssetIdProvider { - type AssetId = T::AssetId; - fn asset_id() -> Self::AssetId { - >::spending_asset_id() - } -} - -impl LockableCurrency for AssetCurrency> -where - T: Trait, - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Moment = T::BlockNumber; - - type MaxLocks = (); - - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - >::set_lock(id, who, amount, reasons) - } - - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - >::extend_lock(id, who, amount, reasons) - } - - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - >::remove_lock(id, who) - } -} - -pub type StakingAssetCurrency = AssetCurrency>; -pub type SpendingAssetCurrency = AssetCurrency>; diff --git a/substrate/frame/generic-asset/src/mock.rs b/substrate/frame/generic-asset/src/mock.rs deleted file mode 100644 index 8c0a06a156..0000000000 --- a/substrate/frame/generic-asset/src/mock.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2019-2020 -// by Centrality Investments Ltd. -// and Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Mocks for the module. - -#![cfg(test)] - -use sp_runtime::{ - Perbill, - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; -use sp_core::H256; -use frame_support::{parameter_types, impl_outer_event, impl_outer_origin, weights::Weight}; - -use super::*; - -impl_outer_origin! { - pub enum Origin for Test where system = frame_system {} -} - -#[derive(Clone, Eq, PartialEq)] -pub struct Test; -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); -} -impl frame_system::Trait for Test { - type BaseCallFilter = (); - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Call = (); - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = TestEvent; - type MaximumBlockWeight = MaximumBlockWeight; - type DbWeight = (); - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); - type MaximumExtrinsicWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type BlockHashCount = BlockHashCount; - type Version = (); - type ModuleToIndex = (); - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); -} - -impl Trait for Test { - type Balance = u64; - type AssetId = u32; - type Event = TestEvent; -} - -mod generic_asset { - pub use crate::Event; -} - -use frame_system as system; -impl_outer_event! { - pub enum TestEvent for Test { - system, - generic_asset, - } -} - -pub type GenericAsset = Module; - -pub type System = frame_system::Module; - -pub struct ExtBuilder { - asset_id: u32, - next_asset_id: u32, - accounts: Vec, - initial_balance: u64, -} - -// Returns default values for genesis config -impl Default for ExtBuilder { - fn default() -> Self { - Self { - asset_id: 0, - next_asset_id: 1000, - accounts: vec![0], - initial_balance: 0, - } - } -} - -impl ExtBuilder { - // Sets free balance to genesis config - pub fn free_balance(mut self, free_balance: (u32, u64, u64)) -> Self { - self.asset_id = free_balance.0; - self.accounts = vec![free_balance.1]; - self.initial_balance = free_balance.2; - self - } - - pub fn next_asset_id(mut self, asset_id: u32) -> Self { - self.next_asset_id = asset_id; - self - } - - // builds genesis config - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - GenesisConfig:: { - assets: vec![self.asset_id], - endowed_accounts: self.accounts, - initial_balance: self.initial_balance, - next_asset_id: self.next_asset_id, - staking_asset_id: 16000, - spending_asset_id: 16001, - }.assimilate_storage(&mut t).unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default() - .build_storage::() - .unwrap() - .into() -} diff --git a/substrate/frame/generic-asset/src/tests.rs b/substrate/frame/generic-asset/src/tests.rs deleted file mode 100644 index a094f69ba1..0000000000 --- a/substrate/frame/generic-asset/src/tests.rs +++ /dev/null @@ -1,1215 +0,0 @@ -// Copyright 2019-2020 -// by Centrality Investments Ltd. -// and Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Tests for the module. - -#![cfg(test)] - -use super::*; -use crate::mock::{new_test_ext, ExtBuilder, GenericAsset, Origin, System, Test, TestEvent}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn issuing_asset_units_to_issuer_should_work() { - let balance = 100; - - ExtBuilder::default().free_balance((16000, 1, 100)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - - let expected_balance = balance; - - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: balance, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&16000, &1), expected_balance); - }); -} - -#[test] -fn issuing_with_next_asset_id_overflow_should_not_work() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - NextAssetId::::put(u32::max_value()); - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_noop!( - GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 1, - permissions: default_permission - } - ), - Error::::NoIdAvailable - ); - assert_eq!(GenericAsset::next_asset_id(), u32::max_value()); - }); -} - -#[test] -fn querying_total_supply_should_work() { - let asset_id = 1000; - - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 50)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 50); - assert_ok!(GenericAsset::transfer(Origin::signed(2), asset_id, 3, 31)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 19); - assert_eq!(GenericAsset::free_balance(&asset_id, &3), 31); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 1)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - }); -} - -// Given -// - The next asset id as `asset_id` = 1000. -// - AssetOptions with all permissions. -// - GenesisStore has sufficient free balance. -// -// When -// - Create an asset from `origin` as 1. -// Then -// - free_balance of next asset id = 100. -// -// When -// - After transferring 40 from account 1 to account 2. -// Then -// - Origin account's `free_balance` = 60. -// - account 2's `free_balance` = 40. -#[test] -fn transferring_amount_should_work() { - let asset_id = 1000; - let free_balance = 100; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: free_balance, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), free_balance); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 40)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 60); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 40); - }); -} - -// Given -// - The next asset id as `asset_id` = 1000. -// - AssetOptions with all permissions. -// - GenesisStore has sufficient free balance. -// -// When -// - Create an asset from `origin` as 1. -// Then -// - free_balance of next asset id = 100. -// -// When -// - After transferring 40 from account 1 to account 2. -// Then -// - Origin account's `free_balance` = 60. -// - account 2's `free_balance` = 40. -#[test] -fn transferring_amount_should_fail_when_transferring_more_than_free_balance() { - let asset_id = 1000; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 2000), - Error::::InsufficientBalance - ); - }); -} - -#[test] -fn transferring_less_than_one_unit_should_not_work() { - let asset_id = 1000; - - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 0), - Error::::ZeroAmount - ); - }); -} - -// Given -// - Next asset id as `asset_id` = 1000. -// - Sufficient free balance. -// - initial balance = 100. -// When -// - After performing a self transfer from account 1 to 1. -// Then -// - Should not throw any errors. -// - Free balance after self transfer should equal to the free balance before self transfer. -#[test] -fn self_transfer_should_fail() { - let asset_id = 1000; - let balance = 100; - - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: balance, - permissions: default_permission - } - )); - - let initial_free_balance = GenericAsset::free_balance(&asset_id, &1); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 10)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), initial_free_balance); - }); -} - -#[test] -fn transferring_more_units_than_total_supply_should_not_work() { - let asset_id = 1000; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 101), - Error::::InsufficientBalance - ); - }); -} - -// Ensures it uses fake money for staking asset id. -#[test] -fn staking_asset_id_should_return_0() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(GenericAsset::staking_asset_id(), 16000); - }); -} - -// Ensures it uses fake money for spending asset id. -#[test] -fn spending_asset_id_should_return_10() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(GenericAsset::spending_asset_id(), 16001); - }); -} - -// Given -// -Â Free balance is 0 and the reserved balance is 0. -// Then -// -Â total_balance should return 0 -#[test] -fn total_balance_should_be_zero() { - new_test_ext().execute_with(|| { - assert_eq!(GenericAsset::total_balance(&0, &0), 0); - }); -} - -// Given -// -Â Free balance is 0 and the reserved balance > 0. -// When -// - After calling total_balance. -// Then -// -Â total_balance should equals to reserved balance. -#[test] -fn total_balance_should_be_equal_to_account_balance() { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::total_balance(&1000, &1), 100); - }); -} - -// Given -// - An account presents with AccountId = 1 -// -Â free_balance > 0. -// - reserved_balance = 50. -// When -// - After calling free_balance. -// Then -// -Â free_balance should return 50. -#[test] -fn free_balance_should_only_return_account_free_balance() { - ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 70); - assert_eq!(GenericAsset::free_balance(&1, &0), 50); - }); -} - -// Given -// - An account presents with AccountId = 1. -// -Â Free balance > 0 and the reserved balance > 0. -// When -// - After calling total_balance. -// Then -// -Â total_balance should equals to account balance + free balance. -#[test] -fn total_balance_should_be_equal_to_sum_of_account_balance_and_free_balance() { - ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 70); - assert_eq!(GenericAsset::total_balance(&1, &0), 120); - }); -} - -// Given -// -Â free_balance > 0. -// - reserved_balance = 70. -// When -// - After calling reserved_balance. -// Then -// - reserved_balance should return 70. -#[test] -fn reserved_balance_should_only_return_account_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 70); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); - }); -} - -// Given -// - A valid account presents. -// - Initial reserved_balance = 0 -// When -// - After calls set_reserved_balance -// Then -// - Should persists the amount as reserved_balance. -// - reserved_balance = amount -#[test] -fn set_reserved_balance_should_add_balance_as_reserved() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 50); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 50); - }); -} - -// Given -// - A valid account presents. -// - Initial free_balance = 100. -// When -// - After calling set_free_balance. -// Then -// - Should persists the amount as free_balance. -// - New free_balance should replace older free_balance. -#[test] -fn set_free_balance_should_add_amount_as_free_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_free_balance(&1, &0, 50); - assert_eq!(GenericAsset::free_balance(&1, &0), 50); - }); -} - -// Given -// - free_balance is greater than the account balance. -// - free_balance = 100 -// - reserved_balance = 0 -// - reserve amount = 70 -// When -// - After calling reserve -// Then -// - Funds should be removed from the account. -// - new free_balance = original free_balance - reserved amount -// - new reserved_balance = original free balance + reserved amount -#[test] -fn reserve_should_moves_amount_from_balance_to_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - assert_ok!(GenericAsset::reserve(&1, &0, 70)); - assert_eq!(GenericAsset::free_balance(&1, &0), 30); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); - }); -} - -// Given -// - Free balance is lower than the account balance. -// - free_balance = 100 -// - reserved_balance = 0 -// - reserve amount = 120 -// When -// - After calling reverse function. -// Then -// - Funds should not be removed from the account. -// - Should throw an error. -#[test] -fn reserve_should_not_moves_amount_from_balance_to_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - assert_noop!(GenericAsset::reserve(&1, &0, 120), Error::::InsufficientBalance); - assert_eq!(GenericAsset::free_balance(&1, &0), 100); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); - }); -} - -// Given -// - unreserved_amount > reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 120. -// When -// - After calling unreserve function. -// Then -// - unreserved should return 20. -#[test] -fn unreserve_should_return_subtracted_value_from_unreserved_amount_by_actual_account_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::unreserve(&1, &0, 120), 20); - }); -} - -// Given -// - unreserved_amount < reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 50. -// When -// - After calling unreserve function. -// Then -// - unreserved should return None. -#[test] -fn unreserve_should_return_none() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::unreserve(&1, &0, 50), 0); - }); -} - -// Given -// - unreserved_amount > reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 120. -// When -// - After calling unreserve function. -// Then -// - free_balance should be 200. -#[test] -fn unreserve_should_increase_free_balance_by_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - GenericAsset::unreserve(&1, &0, 120); - assert_eq!(GenericAsset::free_balance(&1, &0), 200); - }); -} - -// Given -// - unreserved_amount > reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 120. -// When -// - After calling unreserve function. -// Then -// - reserved_balance should be 0. -#[test] -fn unreserve_should_deduct_reserved_balance_by_reserved_amount() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_free_balance(&1, &0, 100); - GenericAsset::unreserve(&1, &0, 120); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); - }); -} - -// Given -// - slash amount < free_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - slash amount = 70. -// When -// - After calling slash function. -// Then -// - slash should return None. -#[test] -fn slash_should_return_slash_reserved_amount() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::slash(&1, &0, 70), None); - }); -} - -// Given -// - slashed_amount > reserved_balance. -// When -// - After calling slashed_reverse function. -// Then -// - Should return slashed_reserved - reserved_balance. -#[test] -fn slash_reserved_should_deducts_up_to_amount_from_reserved_balance() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::slash_reserved(&1, &0, 150), Some(50)); - }); -} - -// Given -// - slashed_amount equals to reserved_amount. -// When -// - After calling slashed_reverse function. -// Then -// - Should return None. -#[test] -fn slash_reserved_should_return_none() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::slash_reserved(&1, &0, 100), None); - }); -} - -// Given -// - reserved_balance = 100. -// - repatriate_reserved_amount > reserved_balance. -// When -// - After calling repatriate_reserved. -// Then -// - Should not return None. -#[test] -fn repatriate_reserved_return_amount_subtracted_by_slash_amount() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130, BalanceStatus::Free), 30); - }); -} - -// Given -// - reserved_balance = 100. -// - repatriate_reserved_amount > reserved_balance. -// When -// - After calling repatriate_reserved. -// Then -// - Should return None. -#[test] -fn repatriate_reserved_return_none() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90, BalanceStatus::Free), 0); - }); -} - -// Given -// - An asset with all permissions -// When -// - After calling `create_reserved` function. -// Then -// - Should create a new reserved asset. -#[test] -fn create_reserved_should_create_a_default_account_with_the_balance_given() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - let options = AssetOptions { - initial_issuance: 500, - permissions: default_permission, - }; - - let expected_total_issuance = 500; - let created_asset_id = 9; - let created_account_id = 0; - - assert_ok!(GenericAsset::create_reserved(Origin::root(), created_asset_id, options)); - - // Tests for side effects. - assert_eq!(>::get(created_asset_id), expected_total_issuance); - assert_eq!( - >::get(&created_asset_id, &created_account_id), - expected_total_issuance - ); - }); -} - -// Given -// - Origin is signed -// - Origin does not have minting permission -// When -// - After calling mint function -// Then -// - Should throw a permission error -#[test] -fn mint_should_throw_permission_error() { - ExtBuilder::default().build().execute_with(|| { - let origin = 1; - let asset_id = 4; - let to_account = 2; - let amount = 100; - - assert_noop!( - GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount), - Error::::NoMintPermission, - ); - }); -} - -// Given -// - Origin is signed. -// - Origin has permissions. -// When -// - After calling mint function -// Then -// - Should increase `to` free_balance. -// - Should not change `origins` free_balance. -#[test] -fn mint_should_increase_asset() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let to_account = 2; - let amount = 500; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); - assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), amount); - - // Origin's free_balance should not change. - assert_eq!(GenericAsset::free_balance(&asset_id, &origin), initial_issuance); - }); -} - -// Given -// - Origin is signed. -// - Origin does not have burning permission. -// When -// - After calling burn function. -// Then -// - Should throw a permission error. -#[test] -fn burn_should_throw_permission_error() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 4; - let to_account = 2; - let amount = 10; - - assert_noop!( - GenericAsset::burn(Origin::signed(origin), asset_id, to_account, amount), - Error::::NoBurnPermission, - ); - }); -} - -// Given -// - Origin is signed. -// - Origin has permissions. -// When -// - After calling burn function -// Then -// - Should decrease `to`'s free_balance. -// - Should not change `origin`'s free_balance. -#[test] -fn burn_should_burn_an_asset() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let to_account = 2; - let amount = 1000; - let initial_issuance = 100; - let burn_amount = 400; - let expected_amount = 600; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); - - assert_ok!(GenericAsset::burn( - Origin::signed(origin), - asset_id, - to_account, - burn_amount - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), expected_amount); - }); -} - -// Given -// - `default_permission` with all privileges. -// - All permissions for origin. -// When -// - After executing create function and check_permission function. -// Then -// - The account origin should have burn, mint and update permissions. -#[test] -fn check_permission_should_return_correct_permission() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - }, - )); - - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update)); - }); -} - -// Given -// - `default_permission` with no privileges. -// - No permissions for origin. -// When -// - After executing create function and check_permission function. -// Then -// - The account origin should not have burn, mint and update permissions. -#[test] -fn check_permission_should_return_false_for_no_permission() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update)); - }); -} - -// Given -// - `default_permission` only with update. -// When -// - After executing update_permission function. -// Then -// - The account origin should not have the burn permission. -// - The account origin should have update and mint permissions. -#[test] -fn update_permission_should_change_permission() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::None, - burn: Owner::None, - }; - - let new_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::None, - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - assert_ok!(GenericAsset::update_permission( - Origin::signed(origin), - asset_id, - new_permission, - )); - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); - }); -} - -// Given -// - `default_permission` without any permissions. -// When -// - After executing update_permission function. -// Then -// - Should throw an error stating "Origin does not have enough permission to update permissions." -#[test] -fn update_permission_should_throw_error_when_lack_of_permissions() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - }; - - let new_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::None, - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - }, - )); - - assert_noop!( - GenericAsset::update_permission(Origin::signed(origin), asset_id, new_permission), - Error::::NoUpdatePermission, - ); - }); -} - -// Given -// - `asset_id` provided. -// - `from_account` is present. -// - All permissions for origin. -// When -// - After calling create_asset. -// Then -// - Should create a reserved token with provided id. -// - NextAssetId doesn't change. -// - TotalIssuance must equal to initial issuance. -// - FreeBalance must equal to initial issuance for the given account. -// - Permissions must have burn, mint and updatePermission for the given asset_id. -#[test] -fn create_asset_works_with_given_asset_id_and_from_account() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = Some(1); - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - let expected_permission = PermissionVersions::V1(default_permission.clone()); - let asset_id = 9; - let initial_issuance = 100; - - assert_ok!(GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission.clone() - } - )); - - // Test for side effects. - assert_eq!(>::get(), 10); - assert_eq!(>::get(asset_id), initial_issuance); - assert_eq!(>::get(&asset_id, &origin), initial_issuance); - assert_eq!(>::get(&asset_id), expected_permission); - }); -} - -// Given -// - `asset_id` is an id for user generated assets. -// - Whatever other params. -// Then -// - `create_asset` should not work. -#[test] -fn create_asset_with_non_reserved_asset_id_should_not_work() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = Some(1); - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let asset_id = 11; - let initial_issuance = 100; - - assert_noop!( - GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance, - permissions: default_permission.clone() - } - ), - Error::::IdUnavailable, - ); - }); -} - -// Given -// - `asset_id` is for reserved assets, but already taken. -// - Whatever other params. -// Then -// - `create_asset` should not work. -#[test] -fn create_asset_with_a_taken_asset_id_should_not_work() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = Some(1); - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let asset_id = 9; - let initial_issuance = 100; - - assert_ok!(GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance, - permissions: default_permission.clone() - } - )); - assert_noop!( - GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance, - permissions: default_permission.clone() - } - ), - Error::::IdAlreadyTaken, - ); - }); -} - -// Given -// - `asset_id` provided. -// - `from_account` is None. -// - All permissions for origin. -// When -// - After calling create_asset. -// Then -// - Should create a reserved token. -#[test] -fn create_asset_should_create_a_reserved_asset_when_from_account_is_none() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = None; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let created_account_id = 0; - let asset_id = 9; - let initial_issuance = 100; - - assert_ok!(GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - // Test for a side effect. - assert_eq!( - >::get(&asset_id, &created_account_id), - initial_issuance - ); - }); -} - -// Given -// - `asset_id` not provided. -// - `from_account` is None. -// - All permissions for origin. -// When -// - After calling create_asset. -// Then -// - Should create a user token. -// - `NextAssetId`'s get should return a new value. -// - Should not create a `reserved_asset`. -#[test] -fn create_asset_should_create_a_user_asset() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = None; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let created_account_id = 0; - let reserved_asset_id = 100000; - let initial_issuance = 100; - let created_user_asset_id = 10; - - assert_ok!(GenericAsset::create_asset( - None, - from_account, - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - // Test for side effects. - assert_eq!(>::get(&reserved_asset_id, &created_account_id), 0); - assert_eq!( - >::get(&created_user_asset_id, &created_account_id), - initial_issuance - ); - assert_eq!(>::get(created_user_asset_id), initial_issuance); - }); -} - -#[test] -fn update_permission_should_raise_event() { - // Arrange - let staking_asset_id = 16000; - let asset_id = 1000; - let origin = 1; - let initial_balance = 1000; - let permissions = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build() - .execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: 0, - permissions: permissions.clone(), - } - )); - - // Act - assert_ok!(GenericAsset::update_permission( - Origin::signed(origin), - asset_id, - permissions.clone() - )); - - let expected_event = TestEvent::generic_asset( - RawEvent::PermissionUpdated(asset_id, permissions.clone()), - ); - // Assert - assert!(System::events().iter().any(|record| record.event == expected_event)); - }, - ); -} - -#[test] -fn mint_should_raise_event() { - // Arrange - let staking_asset_id = 16000; - let asset_id = 1000; - let origin = 1; - let initial_balance = 1000; - let permissions = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - let to = 2; - let amount = 100; - - ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build() - .execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: 0, - permissions: permissions.clone(), - }, - )); - - // Act - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to, amount)); - - let expected_event = TestEvent::generic_asset(RawEvent::Minted(asset_id, to, amount)); - - // Assert - assert!(System::events().iter().any(|record| record.event == expected_event)); - }, - ); -} - -#[test] -fn burn_should_raise_event() { - // Arrange - let staking_asset_id = 16000; - let asset_id = 1000; - let origin = 1; - let initial_balance = 1000; - let permissions = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - let amount = 100; - - ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build() - .execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: amount, - permissions: permissions.clone(), - }, - )); - - // Act - assert_ok!(GenericAsset::burn(Origin::signed(origin), asset_id, origin, amount)); - - let expected_event = TestEvent::generic_asset(RawEvent::Burned(asset_id, origin, amount)); - - // Assert - assert!(System::events().iter().any(|record| record.event == expected_event)); - }, - ); -}