From 5b61ed24a847395038c6c12c53104b36cb56c1d5 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Thu, 10 Jun 2021 10:19:44 +0200 Subject: [PATCH 1/8] add BalanceConversion trait and implementation --- frame/assets/src/lib.rs | 6 +---- frame/assets/src/tests.rs | 17 +++++++++++- frame/assets/src/types.rs | 55 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 333dbad836462..e8d7e7141e2c0 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -147,7 +147,7 @@ use sp_runtime::{ } }; use codec::HasCompact; -use frame_support::{ensure, dispatch::{DispatchError, DispatchResult}}; +use frame_support::pallet_prelude::*; use frame_support::traits::{Currency, ReservableCurrency, BalanceStatus::Reserved, StoredMap}; use frame_support::traits::tokens::{WithdrawConsequence, DepositConsequence, fungibles}; use frame_system::Config as SystemConfig; @@ -157,10 +157,6 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { - use frame_support::{ - dispatch::DispatchResult, - pallet_prelude::*, - }; use frame_system::pallet_prelude::*; use super::*; diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index 6bef5b962de74..dfa00e788b688 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -19,7 +19,7 @@ use super::*; use crate::{Error, mock::*}; -use sp_runtime::TokenError; +use sp_runtime::{TokenError, traits::ConvertInto}; use frame_support::{assert_ok, assert_noop, traits::Currency}; use pallet_balances::Error as BalancesError; @@ -625,3 +625,18 @@ fn force_asset_status_should_work(){ assert_eq!(Assets::total_supply(0), 200); }); } + +#[test] +fn balance_conversion_should_work() { + new_test_ext().execute_with(|| { + let id = 42; + assert_ok!(Assets::force_create(Origin::root(), id, 1, true, 10)); + let not_sufficient = 23; + assert_ok!(Assets::force_create(Origin::root(), not_sufficient, 1, false, 10)); + + assert_eq!(BalanceToAssetBalance::::to_asset_balance(100, 1234), Err(ConversionError::AssetMissing)); + assert_eq!(BalanceToAssetBalance::::to_asset_balance(100, not_sufficient), Err(ConversionError::AssetNotSufficient)); + // 10 / 1 == 10 -> the conversion should 10x the value + assert_eq!(BalanceToAssetBalance::::to_asset_balance(100, id), Ok(100 * 10)); + }); +} diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index afd6b536cf18a..8a38d06070d51 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -20,6 +20,10 @@ use super::*; use frame_support::pallet_prelude::*; +use frame_support::traits::fungible; +use sp_runtime::{FixedPointNumber, FixedPointOperand, FixedU128}; +use sp_runtime::traits::Convert; + pub(super) type DepositBalanceOf = <>::Currency as Currency<::AccountId>>::Balance; @@ -177,3 +181,54 @@ impl From for DebitFlags { } } } + +/// Converts a balance value into an asset balance. +pub trait BalanceConversion { + fn to_asset_balance(balance: InBalance, asset_id: AssetId) -> Result; +} + +/// Possible errors when converting between external and asset balances. +#[derive(Eq, PartialEq, Copy, Clone, RuntimeDebug, Encode, Decode)] +pub enum ConversionError { + /// The external minimum balance must not be zero. + MinBalanceZero, + /// The asset is not present in storage. + AssetMissing, + /// The asset is not sufficient and thus does not have a reliable `min_balance` so it cannot be converted. + AssetNotSufficient, +} + +type AccountIdOf = ::AccountId; +type AssetIdOf = >::AssetId; +type AssetBalanceOf = >::Balance; +type BalanceOf = >>::Balance; + +/// Converts a balance value into an asset balance based on the ratio between the fungible's +/// minimum balance and the minimum asset balance. +pub struct BalanceToAssetBalance(PhantomData<(F, T, CON, I)>); +impl BalanceConversion, AssetIdOf, AssetBalanceOf> for BalanceToAssetBalance +where + F: fungible::Inspect>, + T: Config, + I: 'static, + CON: Convert, AssetBalanceOf>, + BalanceOf: FixedPointOperand + Zero, + AssetBalanceOf: FixedPointOperand + Zero, +{ + /// Convert the given balance value into an asset balance based on the ratio between the fungible's + /// minimum balance and the minimum asset balance. + /// + /// Will return `Err` if the asset is not found, not sufficient or the fungible's minimum balance is zero. + fn to_asset_balance(balance: BalanceOf, asset_id: AssetIdOf) -> Result, ConversionError> { + let asset = Asset::::get(asset_id).ok_or(ConversionError::AssetMissing)?; + // only sufficient assets have a min balance with reliable value + ensure!(asset.is_sufficient, ConversionError::AssetNotSufficient); + let min_balance = CON::convert(F::minimum_balance()); + // make sure we don't divide by zero + ensure!(!min_balance.is_zero(), ConversionError::MinBalanceZero); + let balance = CON::convert(balance); + // balance * asset.min_balance / min_balance + Ok(FixedU128::saturating_from_rational(asset.min_balance, min_balance) + .saturating_mul_int(balance)) + } +} From e158d4f0bb72dbcef0b5aa44f3d5ad24f59a81c2 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Thu, 10 Jun 2021 14:53:17 +0200 Subject: [PATCH 2/8] derive some useful traits on Imbalance --- frame/support/src/traits/tokens/fungibles/imbalance.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs index ecc415cb568bd..d8ac8a8cc6b87 100644 --- a/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -37,6 +37,7 @@ pub trait HandleImbalanceDrop { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] +#[derive(Debug, Eq, PartialEq)] pub struct Imbalance< A: AssetId, B: Balance, From 8ecdfef3de5deb89fbcb8b8f59e943cf9337bb71 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Fri, 11 Jun 2021 09:55:22 +0200 Subject: [PATCH 3/8] Update frame/support/src/traits/tokens/fungibles/imbalance.rs Co-authored-by: Xiliang Chen --- frame/support/src/traits/tokens/fungibles/imbalance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs index d8ac8a8cc6b87..7c6f6198a522e 100644 --- a/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -37,7 +37,7 @@ pub trait HandleImbalanceDrop { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] -#[derive(Debug, Eq, PartialEq)] +#[derive(RuntimeDebug, Eq, PartialEq)] pub struct Imbalance< A: AssetId, B: Balance, From bbd731dcac9243595f68b9f87679e445b91c333c Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Fri, 11 Jun 2021 10:32:29 +0200 Subject: [PATCH 4/8] make BalanceConversion error type configurable --- frame/assets/src/types.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index 8a38d06070d51..b71972669c97e 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -184,7 +184,8 @@ impl From for DebitFlags { /// Converts a balance value into an asset balance. pub trait BalanceConversion { - fn to_asset_balance(balance: InBalance, asset_id: AssetId) -> Result; + type Error; + fn to_asset_balance(balance: InBalance, asset_id: AssetId) -> Result; } /// Possible errors when converting between external and asset balances. @@ -215,6 +216,8 @@ where BalanceOf: FixedPointOperand + Zero, AssetBalanceOf: FixedPointOperand + Zero, { + type Error = ConversionError; + /// Convert the given balance value into an asset balance based on the ratio between the fungible's /// minimum balance and the minimum asset balance. /// From 12acd48ef7c1e51b40e89d42f478e2823f21ee80 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Fri, 11 Jun 2021 10:42:14 +0200 Subject: [PATCH 5/8] add RuntimeDebug import and derive traits on other Imbalance --- frame/support/src/traits/tokens/fungible/imbalance.rs | 3 ++- frame/support/src/traits/tokens/fungibles/imbalance.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frame/support/src/traits/tokens/fungible/imbalance.rs b/frame/support/src/traits/tokens/fungible/imbalance.rs index c084fa97fbec0..ab3694359ce9c 100644 --- a/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -20,7 +20,7 @@ use super::*; use sp_std::marker::PhantomData; -use sp_runtime::traits::Zero; +use sp_runtime::{RuntimeDebug, traits::Zero}; use super::misc::Balance; use super::balanced::Balanced; use crate::traits::misc::{TryDrop, SameOrOther}; @@ -39,6 +39,7 @@ pub trait HandleImbalanceDrop { /// /// Importantly, it has a special `Drop` impl, and cannot be created outside of this module. #[must_use] +#[derive(RuntimeDebug, Eq, PartialEq)] pub struct Imbalance< B: Balance, OnDrop: HandleImbalanceDrop, diff --git a/frame/support/src/traits/tokens/fungibles/imbalance.rs b/frame/support/src/traits/tokens/fungibles/imbalance.rs index 7c6f6198a522e..9ecdeac1d4f06 100644 --- a/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -20,7 +20,7 @@ use super::*; use sp_std::marker::PhantomData; -use sp_runtime::traits::Zero; +use sp_runtime::{RuntimeDebug, traits::Zero}; use super::fungibles::{AssetId, Balance}; use super::balanced::Balanced; use crate::traits::misc::{TryDrop, SameOrOther}; From a3681dcf2f95549a32d5a9510955b47a58383f69 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Fri, 11 Jun 2021 11:44:29 +0200 Subject: [PATCH 6/8] formatting --- frame/assets/src/tests.rs | 15 ++++++++++++--- frame/assets/src/types.rs | 8 ++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index dfa00e788b688..96a5160d5e0a7 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -634,9 +634,18 @@ fn balance_conversion_should_work() { let not_sufficient = 23; assert_ok!(Assets::force_create(Origin::root(), not_sufficient, 1, false, 10)); - assert_eq!(BalanceToAssetBalance::::to_asset_balance(100, 1234), Err(ConversionError::AssetMissing)); - assert_eq!(BalanceToAssetBalance::::to_asset_balance(100, not_sufficient), Err(ConversionError::AssetNotSufficient)); + assert_eq!( + BalanceToAssetBalance::::to_asset_balance(100, 1234), + Err(ConversionError::AssetMissing) + ); + assert_eq!( + BalanceToAssetBalance::::to_asset_balance(100, not_sufficient), + Err(ConversionError::AssetNotSufficient) + ); // 10 / 1 == 10 -> the conversion should 10x the value - assert_eq!(BalanceToAssetBalance::::to_asset_balance(100, id), Ok(100 * 10)); + assert_eq!( + BalanceToAssetBalance::::to_asset_balance(100, id), + Ok(100 * 10) + ); }); } diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index b71972669c97e..7e9eceeaa3e67 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -207,7 +207,8 @@ type BalanceOf = >>::Balance; /// Converts a balance value into an asset balance based on the ratio between the fungible's /// minimum balance and the minimum asset balance. pub struct BalanceToAssetBalance(PhantomData<(F, T, CON, I)>); -impl BalanceConversion, AssetIdOf, AssetBalanceOf> for BalanceToAssetBalance +impl BalanceConversion, AssetIdOf, AssetBalanceOf> +for BalanceToAssetBalance where F: fungible::Inspect>, T: Config, @@ -222,7 +223,10 @@ where /// minimum balance and the minimum asset balance. /// /// Will return `Err` if the asset is not found, not sufficient or the fungible's minimum balance is zero. - fn to_asset_balance(balance: BalanceOf, asset_id: AssetIdOf) -> Result, ConversionError> { + fn to_asset_balance( + balance: BalanceOf, + asset_id: AssetIdOf, + ) -> Result, ConversionError> { let asset = Asset::::get(asset_id).ok_or(ConversionError::AssetMissing)?; // only sufficient assets have a min balance with reliable value ensure!(asset.is_sufficient, ConversionError::AssetNotSufficient); From 9c77db4e0ede115112a5ba84cd243dd8a1fa2337 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 12 Jul 2021 14:39:48 +0200 Subject: [PATCH 7/8] move BalanceConversion trait to frame-support --- frame/assets/src/types.rs | 11 ++++------- frame/support/src/traits/tokens.rs | 3 ++- frame/support/src/traits/tokens/misc.rs | 6 ++++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frame/assets/src/types.rs b/frame/assets/src/types.rs index 7e9eceeaa3e67..478905eb68a3b 100644 --- a/frame/assets/src/types.rs +++ b/frame/assets/src/types.rs @@ -20,7 +20,7 @@ use super::*; use frame_support::pallet_prelude::*; -use frame_support::traits::fungible; +use frame_support::traits::{fungible, tokens::BalanceConversion}; use sp_runtime::{FixedPointNumber, FixedPointOperand, FixedU128}; use sp_runtime::traits::Convert; @@ -182,12 +182,6 @@ impl From for DebitFlags { } } -/// Converts a balance value into an asset balance. -pub trait BalanceConversion { - type Error; - fn to_asset_balance(balance: InBalance, asset_id: AssetId) -> Result; -} - /// Possible errors when converting between external and asset balances. #[derive(Eq, PartialEq, Copy, Clone, RuntimeDebug, Encode, Decode)] pub enum ConversionError { @@ -199,9 +193,12 @@ pub enum ConversionError { AssetNotSufficient, } +// Type alias for `frame_system`'s account id. type AccountIdOf = ::AccountId; +// This pallet's asset id and balance type. type AssetIdOf = >::AssetId; type AssetBalanceOf = >::Balance; +// Generic fungible balance type. type BalanceOf = >>::Balance; /// Converts a balance value into an asset balance based on the ratio between the fungible's diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index ac316b82b03e5..faf8ebfd306ce 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -25,6 +25,7 @@ pub mod nonfungible; pub mod nonfungibles; mod misc; pub use misc::{ - WithdrawConsequence, DepositConsequence, ExistenceRequirement, BalanceStatus, WithdrawReasons, + BalanceConversion, BalanceStatus, DepositConsequence, + ExistenceRequirement, WithdrawConsequence, WithdrawReasons, }; pub use imbalance::Imbalance; diff --git a/frame/support/src/traits/tokens/misc.rs b/frame/support/src/traits/tokens/misc.rs index 0c55ac79182cf..97c111798caaa 100644 --- a/frame/support/src/traits/tokens/misc.rs +++ b/frame/support/src/traits/tokens/misc.rs @@ -167,3 +167,9 @@ impl AssetId for T {} /// Simple amalgamation trait to collect together properties for a Balance under one roof. pub trait Balance: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug {} impl Balance for T {} + +/// Converts a balance value into an asset balance. +pub trait BalanceConversion { + type Error; + fn to_asset_balance(balance: InBalance, asset_id: AssetId) -> Result; +} From ef2f4b97d6702f3dfde1ec0f228c6b919982b6b8 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Tue, 20 Jul 2021 12:08:25 +0200 Subject: [PATCH 8/8] add necessary trait import --- frame/assets/src/tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index 47ae72b2568ae..6f52d7619df78 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -703,6 +703,8 @@ fn force_asset_status_should_work(){ #[test] fn balance_conversion_should_work() { new_test_ext().execute_with(|| { + use frame_support::traits::tokens::BalanceConversion; + let id = 42; assert_ok!(Assets::force_create(Origin::root(), id, 1, true, 10)); let not_sufficient = 23;