Insufficient asset quota and deposits (#10382)

* Allow asset accounts to exist by deposit

* Place limit on consumers (and therefore freebie asset accounts)

* Maximum number of assets

* Fixes

* Fixes

* Formatting

* Docs

* Formatting

* Destroyed assets are properly tidied

* Update frame/assets/src/types.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Docs

* Docs

* Formatting

* Docs

* Docs

* Fixes

* Fixes

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
This commit is contained in:
Gavin Wood
2021-12-09 13:22:54 +01:00
committed by GitHub
parent 73bc2f420e
commit 3dd8f83a3a
84 changed files with 513 additions and 154 deletions
@@ -51,6 +51,7 @@ impl system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_template::Config for Test {
@@ -26,7 +26,7 @@ use sp_version::RuntimeVersion;
// A few exports that help ease life for downstream crates.
pub use frame_support::{
construct_runtime, parameter_types,
traits::{KeyOwnerProofSystem, Randomness, StorageInfo},
traits::{ConstU32, KeyOwnerProofSystem, Randomness, StorageInfo},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
IdentityFee, Weight,
@@ -191,6 +191,7 @@ impl frame_system::Config for Runtime {
type SS58Prefix = SS58Prefix;
/// The set code logic, just the default since we're not a parachain.
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_randomness_collective_flip::Config for Runtime {}
+5 -2
View File
@@ -26,8 +26,9 @@ use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
construct_runtime, parameter_types,
traits::{
ConstU32, Currency, EnsureOneOf, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter,
KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote,
ConstU128, ConstU32, Currency, EnsureOneOf, EqualPrivilegeOnly, Everything, Imbalance,
InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced,
U128CurrencyToVote,
},
weights::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
@@ -219,6 +220,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = frame_system::weights::SubstrateWeight<Runtime>;
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_randomness_collective_flip::Config for Runtime {}
@@ -1195,6 +1197,7 @@ impl pallet_assets::Config for Runtime {
type Currency = Balances;
type ForceOrigin = EnsureRoot<AccountId>;
type AssetDeposit = AssetDeposit;
type AssetAccountDeposit = ConstU128<DOLLARS>;
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type ApprovalDeposit = ApprovalDeposit;
+9 -16
View File
@@ -62,11 +62,11 @@ impl<T: Config<I>, I: 'static> ExtraMutator<T, I> {
id: T::AssetId,
who: impl sp_std::borrow::Borrow<T::AccountId>,
) -> Option<ExtraMutator<T, I>> {
if Account::<T, I>::contains_key(id, who.borrow()) {
if let Some(a) = Account::<T, I>::get(id, who.borrow()) {
Some(ExtraMutator::<T, I> {
id,
who: who.borrow().clone(),
original: Account::<T, I>::get(id, who.borrow()).extra,
original: a.extra,
pending: None,
})
} else {
@@ -77,13 +77,8 @@ impl<T: Config<I>, I: 'static> ExtraMutator<T, I> {
/// Commit any changes to storage.
pub fn commit(&mut self) -> Result<(), ()> {
if let Some(extra) = self.pending.take() {
Account::<T, I>::try_mutate_exists(self.id, self.who.borrow(), |maybe_account| {
if let Some(ref mut account) = maybe_account {
account.extra = extra;
Ok(())
} else {
Err(())
}
Account::<T, I>::try_mutate(self.id, self.who.borrow(), |maybe_account| {
maybe_account.as_mut().ok_or(()).map(|account| account.extra = extra)
})
} else {
Ok(())
@@ -93,13 +88,11 @@ impl<T: Config<I>, I: 'static> ExtraMutator<T, I> {
/// Revert any changes, even those already committed by `self` and drop self.
pub fn revert(mut self) -> Result<(), ()> {
self.pending = None;
Account::<T, I>::try_mutate_exists(self.id, self.who.borrow(), |maybe_account| {
if let Some(ref mut account) = maybe_account {
account.extra = self.original.clone();
Ok(())
} else {
Err(())
}
Account::<T, I>::try_mutate(self.id, self.who.borrow(), |maybe_account| {
maybe_account
.as_mut()
.ok_or(())
.map(|account| account.extra = self.original.clone())
})
}
}
+154 -54
View File
@@ -20,6 +20,14 @@
use super::*;
use frame_support::{traits::Get, BoundedVec};
#[must_use]
pub(super) enum DeadConsequence {
Remove,
Keep,
}
use DeadConsequence::*;
// The main implementation block for the module.
impl<T: Config<I>, I: 'static> Pallet<T, I> {
// Public immutables
@@ -32,9 +40,17 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
ExtraMutator::maybe_new(id, who)
}
/// Get the asset `id` balance of `who`.
/// Get the asset `id` balance of `who`, or zero if the asset-account doesn't exist.
pub fn balance(id: T::AssetId, who: impl sp_std::borrow::Borrow<T::AccountId>) -> T::Balance {
Account::<T, I>::get(id, who.borrow()).balance
Self::maybe_balance(id, who).unwrap_or_default()
}
/// Get the asset `id` balance of `who` if the asset-account exists.
pub fn maybe_balance(
id: T::AssetId,
who: impl sp_std::borrow::Borrow<T::AccountId>,
) -> Option<T::Balance> {
Account::<T, I>::get(id, who.borrow()).map(|a| a.balance)
}
/// Get the total supply of an asset `id`.
@@ -45,34 +61,44 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
pub(super) fn new_account(
who: &T::AccountId,
d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
) -> Result<bool, DispatchError> {
maybe_deposit: Option<DepositBalanceOf<T, I>>,
) -> Result<ExistenceReason<DepositBalanceOf<T, I>>, DispatchError> {
let accounts = d.accounts.checked_add(1).ok_or(ArithmeticError::Overflow)?;
let is_sufficient = if d.is_sufficient {
let reason = if let Some(deposit) = maybe_deposit {
ExistenceReason::DepositHeld(deposit)
} else if d.is_sufficient {
frame_system::Pallet::<T>::inc_sufficients(who);
d.sufficients += 1;
true
ExistenceReason::Sufficient
} else {
frame_system::Pallet::<T>::inc_consumers(who).map_err(|_| Error::<T, I>::NoProvider)?;
false
ExistenceReason::Consumer
};
d.accounts = accounts;
Ok(is_sufficient)
Ok(reason)
}
pub(super) fn dead_account(
what: T::AssetId,
who: &T::AccountId,
d: &mut AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
sufficient: bool,
) {
if sufficient {
d.sufficients = d.sufficients.saturating_sub(1);
frame_system::Pallet::<T>::dec_sufficients(who);
} else {
frame_system::Pallet::<T>::dec_consumers(who);
reason: &ExistenceReason<DepositBalanceOf<T, I>>,
force: bool,
) -> DeadConsequence {
let mut result = Remove;
match *reason {
ExistenceReason::Consumer => frame_system::Pallet::<T>::dec_consumers(who),
ExistenceReason::Sufficient => {
d.sufficients = d.sufficients.saturating_sub(1);
frame_system::Pallet::<T>::dec_sufficients(who);
},
ExistenceReason::DepositRefunded => {},
ExistenceReason::DepositHeld(_) if !force => return Keep,
ExistenceReason::DepositHeld(_) => result = Keep,
}
d.accounts = d.accounts.saturating_sub(1);
T::Freezer::died(what, who)
T::Freezer::died(what, who);
result
}
pub(super) fn can_increase(
@@ -87,15 +113,15 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
if details.supply.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}
let account = Account::<T, I>::get(id, who);
if account.balance.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}
if account.balance.is_zero() {
if let Some(balance) = Self::maybe_balance(id, who) {
if balance.checked_add(&amount).is_none() {
return DepositConsequence::Overflow
}
} else {
if amount < details.min_balance {
return DepositConsequence::BelowMinimum
}
if !details.is_sufficient && frame_system::Pallet::<T>::providers(who) == 0 {
if !details.is_sufficient && !frame_system::Pallet::<T>::can_inc_consumer(who) {
return DepositConsequence::CannotCreate
}
if details.is_sufficient && details.sufficients.checked_add(1).is_none() {
@@ -124,7 +150,13 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
if details.is_frozen {
return Frozen
}
let account = Account::<T, I>::get(id, who);
if amount.is_zero() {
return Success
}
let account = match Account::<T, I>::get(id, who) {
Some(a) => a,
None => return NoFunds,
};
if account.is_frozen {
return Frozen
}
@@ -165,7 +197,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
let details = Asset::<T, I>::get(id).ok_or_else(|| Error::<T, I>::Unknown)?;
ensure!(!details.is_frozen, Error::<T, I>::Frozen);
let account = Account::<T, I>::get(id, who);
let account = Account::<T, I>::get(id, who).ok_or(Error::<T, I>::NoAccount)?;
ensure!(!account.is_frozen, Error::<T, I>::Frozen);
let amount = if let Some(frozen) = T::Freezer::frozen_balance(id, who) {
@@ -253,6 +285,48 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Ok((credit, maybe_burn))
}
/// Creates a account for `who` to hold asset `id` with a zero balance and takes a deposit.
pub(super) fn do_touch(id: T::AssetId, who: T::AccountId) -> DispatchResult {
ensure!(!Account::<T, I>::contains_key(id, &who), Error::<T, I>::AlreadyExists);
let deposit = T::AssetAccountDeposit::get();
let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
let reason = Self::new_account(&who, &mut details, Some(deposit))?;
T::Currency::reserve(&who, deposit)?;
Asset::<T, I>::insert(&id, details);
Account::<T, I>::insert(
id,
&who,
AssetAccountOf::<T, I> {
balance: Zero::zero(),
is_frozen: false,
reason,
extra: T::Extra::default(),
},
);
Ok(())
}
/// Returns a deposit, destroying an asset-account.
pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult {
let mut account = Account::<T, I>::get(id, &who).ok_or(Error::<T, I>::NoDeposit)?;
let deposit = account.reason.take_deposit().ok_or(Error::<T, I>::NoDeposit)?;
let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?;
ensure!(account.balance.is_zero() || allow_burn, Error::<T, I>::WouldBurn);
ensure!(!details.is_frozen, Error::<T, I>::Frozen);
ensure!(!account.is_frozen, Error::<T, I>::Frozen);
T::Currency::unreserve(&who, deposit);
if let Remove = Self::dead_account(id, &who, &mut details, &account.reason, false) {
Account::<T, I>::remove(id, &who);
} else {
debug_assert!(false, "refund did not result in dead account?!");
}
Asset::<T, I>::insert(&id, details);
Ok(())
}
/// Increases the asset `id` balance of `beneficiary` by `amount`.
///
/// This alters the registered supply of the asset and emits an event.
@@ -307,13 +381,22 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
check(details)?;
Account::<T, I>::try_mutate(id, beneficiary, |t| -> DispatchResult {
let new_balance = t.balance.saturating_add(amount);
ensure!(new_balance >= details.min_balance, TokenError::BelowMinimum);
if t.balance.is_zero() {
t.sufficient = Self::new_account(beneficiary, details)?;
Account::<T, I>::try_mutate(id, beneficiary, |maybe_account| -> DispatchResult {
match maybe_account {
Some(ref mut account) => {
account.balance.saturating_accrue(amount);
},
maybe_account @ None => {
// Note this should never fail as it's already checked by `can_increase`.
ensure!(amount >= details.min_balance, TokenError::BelowMinimum);
*maybe_account = Some(AssetAccountOf::<T, I> {
balance: amount,
reason: Self::new_account(beneficiary, details, None)?,
is_frozen: false,
extra: T::Extra::default(),
});
},
}
t.balance = new_balance;
Ok(())
})?;
Ok(())
@@ -375,23 +458,25 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
let actual = Self::prep_debit(id, target, amount, f)?;
Asset::<T, I>::try_mutate(id, |maybe_details| -> DispatchResult {
let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
let mut details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
check(actual, details)?;
Account::<T, I>::try_mutate_exists(id, target, |maybe_account| -> DispatchResult {
let mut account = maybe_account.take().unwrap_or_default();
Account::<T, I>::try_mutate(id, target, |maybe_account| -> DispatchResult {
let mut account = maybe_account.take().ok_or(Error::<T, I>::NoAccount)?;
debug_assert!(account.balance >= actual, "checked in prep; qed");
// Make the debit.
account.balance = account.balance.saturating_sub(actual);
*maybe_account = if account.balance < details.min_balance {
if account.balance < details.min_balance {
debug_assert!(account.balance.is_zero(), "checked in prep; qed");
Self::dead_account(id, target, details, account.sufficient);
None
} else {
Some(account)
if let Remove =
Self::dead_account(id, target, &mut details, &account.reason, false)
{
return Ok(())
}
};
*maybe_account = Some(account);
Ok(())
})?;
@@ -432,7 +517,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
let debit = Self::prep_debit(id, &source, amount, f.into())?;
let (credit, maybe_burn) = Self::prep_credit(id, &dest, amount, debit, f.burn_dust)?;
let mut source_account = Account::<T, I>::get(id, &source);
let mut source_account =
Account::<T, I>::get(id, &source).ok_or(Error::<T, I>::NoAccount)?;
Asset::<T, I>::try_mutate(id, |maybe_details| -> DispatchResult {
let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?;
@@ -459,29 +545,40 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
debug_assert!(source_account.balance >= debit, "checked in prep; qed");
source_account.balance = source_account.balance.saturating_sub(debit);
Account::<T, I>::try_mutate(id, &dest, |a| -> DispatchResult {
// Calculate new balance; this will not saturate since it's already checked in prep.
debug_assert!(a.balance.checked_add(&credit).is_some(), "checked in prep; qed");
let new_balance = a.balance.saturating_add(credit);
// Create a new account if there wasn't one already.
if a.balance.is_zero() {
a.sufficient = Self::new_account(&dest, details)?;
Account::<T, I>::try_mutate(id, &dest, |maybe_account| -> DispatchResult {
match maybe_account {
Some(ref mut account) => {
// Calculate new balance; this will not saturate since it's already checked
// in prep.
debug_assert!(
account.balance.checked_add(&credit).is_some(),
"checked in prep; qed"
);
account.balance.saturating_accrue(credit);
},
maybe_account @ None => {
*maybe_account = Some(AssetAccountOf::<T, I> {
balance: credit,
is_frozen: false,
reason: Self::new_account(&dest, details, None)?,
extra: T::Extra::default(),
});
},
}
a.balance = new_balance;
Ok(())
})?;
// Remove source account if it's now dead.
if source_account.balance < details.min_balance {
debug_assert!(source_account.balance.is_zero(), "checked in prep; qed");
Self::dead_account(id, &source, details, source_account.sufficient);
Account::<T, I>::remove(id, &source);
} else {
Account::<T, I>::insert(id, &source, &source_account)
if let Remove =
Self::dead_account(id, &source, details, &source_account.reason, false)
{
Account::<T, I>::remove(id, &source);
return Ok(())
}
}
Account::<T, I>::insert(id, &source, &source_account);
Ok(())
})?;
@@ -554,7 +651,10 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
ensure!(details.approvals <= witness.approvals, Error::<T, I>::BadWitness);
for (who, v) in Account::<T, I>::drain_prefix(id) {
Self::dead_account(id, &who, &mut details, v.sufficient);
// We have to force this as it's destroying the entire asset class.
// This could mean that some accounts now have irreversibly reserved
// funds.
let _ = Self::dead_account(id, &who, &mut details, &v.reason, true);
}
debug_assert_eq!(details.accounts, 0);
debug_assert_eq!(details.sufficients, 0);
@@ -22,11 +22,7 @@ use super::*;
impl<T: Config<I>, I: 'static> StoredMap<(T::AssetId, T::AccountId), T::Extra> for Pallet<T, I> {
fn get(id_who: &(T::AssetId, T::AccountId)) -> T::Extra {
let &(id, ref who) = id_who;
if Account::<T, I>::contains_key(id, who) {
Account::<T, I>::get(id, who).extra
} else {
Default::default()
}
Account::<T, I>::get(id, who).map(|a| a.extra).unwrap_or_default()
}
fn try_mutate_exists<R, E: From<DispatchError>>(
@@ -34,13 +30,13 @@ impl<T: Config<I>, I: 'static> StoredMap<(T::AssetId, T::AccountId), T::Extra> f
f: impl FnOnce(&mut Option<T::Extra>) -> Result<R, E>,
) -> Result<R, E> {
let &(id, ref who) = id_who;
let mut maybe_extra = Some(Account::<T, I>::get(id, who).extra);
let mut maybe_extra = Account::<T, I>::get(id, who).map(|a| a.extra);
let r = f(&mut maybe_extra)?;
// They want to write some value or delete it.
// If the account existed and they want to write a value, then we write.
// If the account didn't exist and they want to delete it, then we let it pass.
// Otherwise, we fail.
Account::<T, I>::try_mutate_exists(id, who, |maybe_account| {
Account::<T, I>::try_mutate(id, who, |maybe_account| {
if let Some(extra) = maybe_extra {
// They want to write a value. Let this happen only if the account actually exists.
if let Some(ref mut account) = maybe_account {
+56 -10
View File
@@ -212,6 +212,11 @@ pub mod pallet {
#[pallet::constant]
type AssetDeposit: Get<DepositBalanceOf<Self, I>>;
/// The amount of funds that must be reserved for a non-provider asset account to be
/// maintained.
#[pallet::constant]
type AssetAccountDeposit: Get<DepositBalanceOf<Self, I>>;
/// The basic amount of funds that must be reserved when adding metadata to your asset.
#[pallet::constant]
type MetadataDepositBase: Get<DepositBalanceOf<Self, I>>;
@@ -250,15 +255,15 @@ pub mod pallet {
>;
#[pallet::storage]
/// The number of units of assets held by any given account.
/// The holdings of a specific account for a specific asset.
pub(super) type Account<T: Config<I>, I: 'static = ()> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AssetId,
Blake2_128Concat,
T::AccountId,
AssetBalance<T::Balance, T::Extra>,
ValueQuery,
AssetAccountOf<T, I>,
OptionQuery,
GetDefault,
ConstU32<300_000>,
>;
@@ -448,8 +453,8 @@ pub mod pallet {
pub enum Error<T, I = ()> {
/// Account balance must be greater than or equal to the transfer amount.
BalanceLow,
/// Balance should be non-zero.
BalanceZero,
/// The account to alter does not exist.
NoAccount,
/// The signing account has no permission to do the operation.
NoPermission,
/// The given asset ID is unknown.
@@ -471,6 +476,12 @@ pub mod pallet {
Unapproved,
/// The source account would not survive the transfer and it needs to stay alive.
WouldDie,
/// The asset-account already exists.
AlreadyExists,
/// The asset-account doesn't have an associated deposit.
NoDeposit,
/// The operation would result in funds being burned.
WouldBurn,
}
#[pallet::call]
@@ -633,7 +644,7 @@ pub mod pallet {
///
/// Origin must be Signed and the sender should be the Manager of the asset `id`.
///
/// Bails with `BalanceZero` if the `who` is already dead.
/// Bails with `NoAccount` if the `who` is already dead.
///
/// - `id`: The identifier of the asset to have some amount burned.
/// - `who`: The account to be debited from.
@@ -779,9 +790,11 @@ pub mod pallet {
let d = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(&origin == &d.freezer, Error::<T, I>::NoPermission);
let who = T::Lookup::lookup(who)?;
ensure!(Account::<T, I>::contains_key(id, &who), Error::<T, I>::BalanceZero);
Account::<T, I>::mutate(id, &who, |a| a.is_frozen = true);
Account::<T, I>::try_mutate(id, &who, |maybe_account| -> DispatchResult {
maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.is_frozen = true;
Ok(())
})?;
Self::deposit_event(Event::<T, I>::Frozen { asset_id: id, who });
Ok(())
@@ -808,9 +821,11 @@ pub mod pallet {
let details = Asset::<T, I>::get(id).ok_or(Error::<T, I>::Unknown)?;
ensure!(&origin == &details.admin, Error::<T, I>::NoPermission);
let who = T::Lookup::lookup(who)?;
ensure!(Account::<T, I>::contains_key(id, &who), Error::<T, I>::BalanceZero);
Account::<T, I>::mutate(id, &who, |a| a.is_frozen = false);
Account::<T, I>::try_mutate(id, &who, |maybe_account| -> DispatchResult {
maybe_account.as_mut().ok_or(Error::<T, I>::NoAccount)?.is_frozen = false;
Ok(())
})?;
Self::deposit_event(Event::<T, I>::Thawed { asset_id: id, who });
Ok(())
@@ -1274,5 +1289,36 @@ pub mod pallet {
let destination = T::Lookup::lookup(destination)?;
Self::do_transfer_approved(id, &owner, &delegate, &destination, amount)
}
/// Create an asset account for non-provider assets.
///
/// A deposit will be taken from the signer account.
///
/// - `origin`: Must be Signed; the signer account must have sufficient funds for a deposit
/// to be taken.
/// - `id`: The identifier of the asset for the account to be created.
///
/// Emits `Touched` event when successful.
#[pallet::weight(T::WeightInfo::mint())]
pub fn touch(origin: OriginFor<T>, #[pallet::compact] id: T::AssetId) -> DispatchResult {
Self::do_touch(id, ensure_signed(origin)?)
}
/// Return the deposit (if any) of an asset account.
///
/// The origin must be Signed.
///
/// - `id`: The identifier of the asset for the account to be created.
/// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund.
///
/// Emits `Refunded` event when successful.
#[pallet::weight(T::WeightInfo::mint())]
pub fn refund(
origin: OriginFor<T>,
#[pallet::compact] id: T::AssetId,
allow_burn: bool,
) -> DispatchResult {
Self::do_refund(id, ensure_signed(origin)?, allow_burn)
}
}
}
+13 -23
View File
@@ -20,7 +20,10 @@
use super::*;
use crate as pallet_assets;
use frame_support::{construct_runtime, parameter_types, traits::GenesisBuild};
use frame_support::{
construct_runtime,
traits::{ConstU32, ConstU64, GenesisBuild},
};
use sp_core::H256;
use sp_runtime::{
testing::Header,
@@ -42,9 +45,6 @@ construct_runtime!(
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
}
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
@@ -59,7 +59,7 @@ impl frame_system::Config for Test {
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type BlockHashCount = ConstU64<250>;
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
@@ -69,17 +69,14 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
type MaxConsumers = ConstU32<2>;
}
impl pallet_balances::Config for Test {
type Balance = u64;
type DustRemoval = ();
type Event = Event;
type ExistentialDeposit = ExistentialDeposit;
type ExistentialDeposit = ConstU64<1>;
type AccountStore = System;
type WeightInfo = ();
type MaxLocks = ();
@@ -87,25 +84,18 @@ impl pallet_balances::Config for Test {
type ReserveIdentifier = [u8; 8];
}
parameter_types! {
pub const AssetDeposit: u64 = 1;
pub const ApprovalDeposit: u64 = 1;
pub const StringLimit: u32 = 50;
pub const MetadataDepositBase: u64 = 1;
pub const MetadataDepositPerByte: u64 = 1;
}
impl Config for Test {
type Event = Event;
type Balance = u64;
type AssetId = u32;
type Currency = Balances;
type ForceOrigin = frame_system::EnsureRoot<u64>;
type AssetDeposit = AssetDeposit;
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type ApprovalDeposit = ApprovalDeposit;
type StringLimit = StringLimit;
type AssetDeposit = ConstU64<1>;
type AssetAccountDeposit = ConstU64<10>;
type MetadataDepositBase = ConstU64<1>;
type MetadataDepositPerByte = ConstU64<1>;
type ApprovalDeposit = ConstU64<1>;
type StringLimit = ConstU32<50>;
type Freezer = TestFreezer;
type WeightInfo = ();
type Extra = ();
+97 -6
View File
@@ -34,6 +34,97 @@ fn basic_minting_should_work() {
});
}
#[test]
fn minting_too_many_insufficient_assets_fails() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1));
assert_ok!(Assets::force_create(Origin::root(), 1, 1, false, 1));
assert_ok!(Assets::force_create(Origin::root(), 2, 1, false, 1));
Balances::make_free_balance_be(&1, 100);
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
assert_ok!(Assets::mint(Origin::signed(1), 1, 1, 100));
assert_noop!(Assets::mint(Origin::signed(1), 2, 1, 100), TokenError::CannotCreate);
Balances::make_free_balance_be(&2, 1);
assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 100));
assert_ok!(Assets::mint(Origin::signed(1), 2, 1, 100));
});
}
#[test]
fn minting_insufficient_asset_with_deposit_should_work_when_consumers_exhausted() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1));
assert_ok!(Assets::force_create(Origin::root(), 1, 1, false, 1));
assert_ok!(Assets::force_create(Origin::root(), 2, 1, false, 1));
Balances::make_free_balance_be(&1, 100);
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
assert_ok!(Assets::mint(Origin::signed(1), 1, 1, 100));
assert_noop!(Assets::mint(Origin::signed(1), 2, 1, 100), TokenError::CannotCreate);
assert_ok!(Assets::touch(Origin::signed(1), 2));
assert_eq!(Balances::reserved_balance(&1), 10);
assert_ok!(Assets::mint(Origin::signed(1), 2, 1, 100));
});
}
#[test]
fn minting_insufficient_assets_with_deposit_without_consumer_should_work() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1));
assert_noop!(Assets::mint(Origin::signed(1), 0, 1, 100), TokenError::CannotCreate);
Balances::make_free_balance_be(&1, 100);
assert_ok!(Assets::touch(Origin::signed(1), 0));
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
assert_eq!(Balances::reserved_balance(&1), 10);
assert_eq!(System::consumers(&1), 0);
});
}
#[test]
fn refunding_asset_deposit_with_burn_should_work() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1));
Balances::make_free_balance_be(&1, 100);
assert_ok!(Assets::touch(Origin::signed(1), 0));
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
assert_ok!(Assets::refund(Origin::signed(1), 0, true));
assert_eq!(Balances::reserved_balance(&1), 0);
assert_eq!(Assets::balance(1, 0), 0);
});
}
#[test]
fn refunding_asset_deposit_with_burn_disallowed_should_fail() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1));
Balances::make_free_balance_be(&1, 100);
assert_ok!(Assets::touch(Origin::signed(1), 0));
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
assert_noop!(Assets::refund(Origin::signed(1), 0, false), Error::<Test>::WouldBurn);
});
}
#[test]
fn refunding_asset_deposit_without_burn_should_work() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, false, 1));
assert_noop!(Assets::mint(Origin::signed(1), 0, 1, 100), TokenError::CannotCreate);
Balances::make_free_balance_be(&1, 100);
assert_ok!(Assets::touch(Origin::signed(1), 0));
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
Balances::make_free_balance_be(&2, 100);
assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 100));
assert_eq!(Assets::balance(0, 2), 100);
assert_eq!(Assets::balance(0, 1), 0);
assert_eq!(Balances::reserved_balance(&1), 10);
assert_ok!(Assets::refund(Origin::signed(1), 0, false));
assert_eq!(Balances::reserved_balance(&1), 0);
assert_eq!(Assets::balance(1, 0), 0);
});
}
#[test]
fn approval_lifecycle_works() {
new_test_ext().execute_with(|| {
@@ -299,17 +390,17 @@ fn min_balance_should_work() {
// When deducting from an account to below minimum, it should be reaped.
assert_ok!(Assets::transfer(Origin::signed(1), 0, 2, 91));
assert!(Assets::balance(0, 1).is_zero());
assert!(Assets::maybe_balance(0, 1).is_none());
assert_eq!(Assets::balance(0, 2), 100);
assert_eq!(Asset::<Test>::get(0).unwrap().accounts, 1);
assert_ok!(Assets::force_transfer(Origin::signed(1), 0, 2, 1, 91));
assert!(Assets::balance(0, 2).is_zero());
assert!(Assets::maybe_balance(0, 2).is_none());
assert_eq!(Assets::balance(0, 1), 100);
assert_eq!(Asset::<Test>::get(0).unwrap().accounts, 1);
assert_ok!(Assets::burn(Origin::signed(1), 0, 1, 91));
assert!(Assets::balance(0, 1).is_zero());
assert!(Assets::maybe_balance(0, 1).is_none());
assert_eq!(Asset::<Test>::get(0).unwrap().accounts, 0);
});
}
@@ -488,7 +579,7 @@ fn transferring_amount_more_than_available_balance_should_not_work() {
assert_eq!(Assets::balance(0, 2), 50);
assert_ok!(Assets::burn(Origin::signed(1), 0, 1, u64::MAX));
assert_eq!(Assets::balance(0, 1), 0);
assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), Error::<Test>::BalanceLow);
assert_noop!(Assets::transfer(Origin::signed(1), 0, 1, 50), Error::<Test>::NoAccount);
assert_noop!(Assets::transfer(Origin::signed(2), 0, 1, 51), Error::<Test>::BalanceLow);
});
}
@@ -536,7 +627,7 @@ fn burning_asset_balance_with_zero_balance_does_nothing() {
assert_ok!(Assets::force_create(Origin::root(), 0, 1, true, 1));
assert_ok!(Assets::mint(Origin::signed(1), 0, 1, 100));
assert_eq!(Assets::balance(0, 2), 0);
assert_ok!(Assets::burn(Origin::signed(1), 0, 2, u64::MAX));
assert_noop!(Assets::burn(Origin::signed(1), 0, 2, u64::MAX), Error::<Test>::NoAccount);
assert_eq!(Assets::balance(0, 2), 0);
assert_eq!(Assets::total_supply(0), 100);
});
@@ -688,7 +779,7 @@ fn force_metadata_should_work() {
);
// string length limit check
let limit = StringLimit::get() as usize;
let limit = 50usize;
assert_noop!(
Assets::force_set_metadata(
Origin::root(),
+47 -9
View File
@@ -26,6 +26,8 @@ use sp_runtime::{traits::Convert, FixedPointNumber, FixedPointOperand, FixedU128
pub(super) type DepositBalanceOf<T, I = ()> =
<<T as Config<I>>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
pub(super) type AssetAccountOf<T, I> =
AssetAccount<<T as Config<I>>::Balance, DepositBalanceOf<T, I>, <T as Config<I>>::Extra>;
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetDetails<Balance, AccountId, DepositBalance> {
@@ -76,14 +78,47 @@ pub struct Approval<Balance, DepositBalance> {
pub(super) deposit: DepositBalance,
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, MaxEncodedLen, TypeInfo)]
pub struct AssetBalance<Balance, Extra> {
#[test]
fn ensure_bool_decodes_to_consumer_or_sufficient() {
assert_eq!(false.encode(), ExistenceReason::<()>::Consumer.encode());
assert_eq!(true.encode(), ExistenceReason::<()>::Sufficient.encode());
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum ExistenceReason<Balance> {
#[codec(index = 0)]
Consumer,
#[codec(index = 1)]
Sufficient,
#[codec(index = 2)]
DepositHeld(Balance),
#[codec(index = 3)]
DepositRefunded,
}
impl<Balance> ExistenceReason<Balance> {
pub(crate) fn take_deposit(&mut self) -> Option<Balance> {
if !matches!(self, ExistenceReason::DepositHeld(_)) {
return None
}
if let ExistenceReason::DepositHeld(deposit) =
sp_std::mem::replace(self, ExistenceReason::DepositRefunded)
{
return Some(deposit)
} else {
return None
}
}
}
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub struct AssetAccount<Balance, DepositBalance, Extra> {
/// The balance.
pub(super) balance: Balance,
/// Whether the account is frozen.
pub(super) is_frozen: bool,
/// `true` if this balance gave the account a self-sufficient reference.
pub(super) sufficient: bool,
/// The reason for the existence of the account.
pub(super) reason: ExistenceReason<DepositBalance>,
/// Additional "sidecar" data, in case some other pallet wants to use this storage item.
pub(super) extra: Extra,
}
@@ -124,12 +159,15 @@ pub struct DestroyWitness {
pub trait FrozenBalance<AssetId, AccountId, Balance> {
/// Return the frozen balance.
///
/// Under normal behaviour, the account balance should not go below the sum of this (if `Some`)
/// and the asset's minimum balance.
/// But the account balance can be below this sum (e.g. if less than the sum has been
/// transfered to the account).
/// Generally, the balance of every account must be at least the sum of this (if `Some`) and
/// the asset's `minimum_balance` (the latter since there may be complications to destroying an
/// asset's account completely).
///
/// In special case (privileged intervention) the account balance can go below the sum.
/// Under normal behaviour, the account balance should not go below the sum of this (if `Some`)
/// and the asset's minimum balance. However, the account balance may reasonably begin below
/// this sum (e.g. if less than the sum had ever been transfered into the account).
///
/// In special cases (privileged intervention) the account balance may also go below the sum.
///
/// If `None` is returned, then nothing special is enforced.
fn frozen_balance(asset: AssetId, who: &AccountId) -> Option<Balance>;
+1
View File
@@ -54,6 +54,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
+1
View File
@@ -78,6 +78,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_timestamp::Config for Test {
@@ -260,6 +260,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
pub struct TestSessionHandler;
+1
View File
@@ -458,6 +458,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -96,6 +96,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl<C> frame_system::offchain::SendTransactionTypes<C> for Test
+1
View File
@@ -68,6 +68,7 @@ impl frame_system::Config for Runtime {
type OnKilledAccount = ();
type SystemWeightInfo = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -73,6 +73,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const TransactionByteFee: u64 = 1;
@@ -75,6 +75,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const TransactionByteFee: u64 = 1;
@@ -77,6 +77,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const TransactionByteFee: u64 = 1;
+1
View File
@@ -86,6 +86,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -84,6 +84,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_beefy::Config for Test {
@@ -195,6 +195,7 @@ pub mod mock {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl super::Config for Test {}
@@ -108,6 +108,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -110,6 +110,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -84,6 +84,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
@@ -86,6 +86,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
+1
View File
@@ -115,6 +115,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl Config<Instance1> for Test {
type Origin = Origin;
+1
View File
@@ -206,6 +206,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_randomness_collective_flip::Config for Test {}
impl pallet_balances::Config for Test {
+1
View File
@@ -105,6 +105,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block;
@@ -218,6 +218,7 @@ impl frame_system::Config for Runtime {
type OnKilledAccount = ();
type SystemWeightInfo = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
@@ -145,6 +145,7 @@ mod tests {
type OnKilledAccount = ();
type SystemWeightInfo = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl Config for Runtime {
@@ -1150,6 +1150,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -60,6 +60,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -79,6 +79,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
@@ -77,6 +77,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
type Extrinsic = TestXt<Call, ()>;
@@ -68,6 +68,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -767,6 +767,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
type Balance = u64;
+1
View File
@@ -74,6 +74,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -98,6 +98,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl<C> frame_system::offchain::SendTransactionTypes<C> for Test
+1
View File
@@ -75,6 +75,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
+1
View File
@@ -139,6 +139,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -69,6 +69,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -79,6 +79,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -560,6 +560,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
ord_parameter_types! {
pub const One: u64 = 1;
@@ -69,6 +69,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl Config for Test {
+1
View File
@@ -74,6 +74,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
+1
View File
@@ -297,6 +297,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
@@ -71,6 +71,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
ord_parameter_types! {
@@ -63,6 +63,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: Balance = 10;
+1
View File
@@ -112,6 +112,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl Config for Runtime {
+1
View File
@@ -78,6 +78,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
@@ -219,6 +219,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_randomness_collective_flip::Config for Test {}
+1
View File
@@ -75,6 +75,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -932,6 +932,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl logger::Config for Test {
type Event = Event;
+1
View File
@@ -81,6 +81,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_balances::Config for Test {
@@ -68,6 +68,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: Balance = 10;
+1
View File
@@ -264,6 +264,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_timestamp::Config for Test {
+1
View File
@@ -89,6 +89,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_balances::Config for Test {
+1
View File
@@ -150,6 +150,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_balances::Config for Test {
type MaxLocks = MaxLocks;
+1
View File
@@ -144,6 +144,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
// Implement the logger module's `Config` on the Test runtime.
+4 -3
View File
@@ -50,9 +50,10 @@ pub use filter::{ClearFilterGuard, FilterStack, FilterStackGuard, InstanceFilter
mod misc;
pub use misc::{
Backing, ConstU32, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, ExecuteBlock,
ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType, Len,
OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, TryDrop,
Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16,
ConstU32, ConstU64, ConstU8, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee,
ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType,
Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, SameOrOther, Time, TryDrop,
UnixTime, WrapperKeepOpaque, WrapperOpaque,
};
+25 -12
View File
@@ -60,20 +60,33 @@ impl<T: Default> Get<T> for GetDefault {
}
}
/// Implement `Get<u32>` and `Get<Option<u32>>` using the given const.
pub struct ConstU32<const T: u32>;
impl<const T: u32> Get<u32> for ConstU32<T> {
fn get() -> u32 {
T
}
macro_rules! impl_const_get {
($name:ident, $t:ty) => {
pub struct $name<const T: $t>;
impl<const T: $t> Get<$t> for $name<T> {
fn get() -> $t {
T
}
}
impl<const T: $t> Get<Option<$t>> for $name<T> {
fn get() -> Option<$t> {
Some(T)
}
}
};
}
impl<const T: u32> Get<Option<u32>> for ConstU32<T> {
fn get() -> Option<u32> {
Some(T)
}
}
impl_const_get!(ConstBool, bool);
impl_const_get!(ConstU8, u8);
impl_const_get!(ConstU16, u16);
impl_const_get!(ConstU32, u32);
impl_const_get!(ConstU64, u64);
impl_const_get!(ConstU128, u128);
impl_const_get!(ConstI8, i8);
impl_const_get!(ConstI16, i16);
impl_const_get!(ConstI32, i32);
impl_const_get!(ConstI64, i64);
impl_const_get!(ConstI128, i128);
/// A type for which some values make sense to be able to drop without further consideration.
pub trait TryDrop: Sized {
@@ -17,14 +17,17 @@ scale-info = { version = "1.0", default-features = false, features = ["derive"]
sp-core = { version = "4.0.0", default-features = false, path = "../../../../primitives/core" }
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/runtime" }
sp-version = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/version" }
support = { package = "frame-support", version = "4.0.0-dev", default-features = false, path = "../../" }
system = { package = "frame-system", version = "4.0.0-dev", default-features = false, path = "../../../system" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" }
[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"support/std",
"system/std",
"sp-core/std",
"sp-runtime/std",
"sp-version/std",
"frame-support/std",
"frame-system/std",
]
@@ -22,13 +22,13 @@
//! This crate tests that `construct_runtime!` expands the pallet parts
//! correctly even when frame-support is renamed in Cargo.toml
use frame_support::{construct_runtime, parameter_types};
use sp_core::{sr25519, H256};
use sp_runtime::{
create_runtime_str, generic,
traits::{BlakeTwo256, IdentityLookup, Verify},
};
use sp_version::RuntimeVersion;
use support::{construct_runtime, parameter_types};
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("frame-support-test-compile-pass"),
@@ -51,8 +51,8 @@ parameter_types! {
pub const SS58Prefix: u8 = 0;
}
impl system::Config for Runtime {
type BaseCallFilter = support::traits::Everything;
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type Index = u128;
@@ -73,6 +73,7 @@ impl system::Config for Runtime {
type OnNewAccount = ();
type OnKilledAccount = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
}
@@ -87,6 +88,6 @@ construct_runtime!(
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system,
System: frame_system,
}
);
@@ -566,6 +566,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet::Config for Runtime {
type Event = Event;
@@ -246,6 +246,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet::Config for Runtime {
type Event = Event;
@@ -226,6 +226,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet::Config for Runtime {
type Event = Event;
@@ -274,6 +274,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet::Config for Runtime {
type Event = Event;
@@ -152,6 +152,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_test::Trait for Runtime {
+1
View File
@@ -92,6 +92,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl module::Config for Runtime {
@@ -62,6 +62,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl crate::Config for Test {}
+12 -3
View File
@@ -309,6 +309,10 @@ pub mod pallet {
/// It's unlikely that this needs to be customized, unless you are writing a parachain using
/// `Cumulus`, where the actual code change is deferred.
type OnSetCode: SetCode<Self>;
/// The maximum number of consumers allowed on a single account.
#[pallet::constant]
type MaxConsumers: Get<RefCount>;
}
#[pallet::pallet]
@@ -1107,8 +1111,12 @@ impl<T: Config> Pallet<T> {
pub fn inc_consumers(who: &T::AccountId) -> Result<(), DispatchError> {
Account::<T>::try_mutate(who, |a| {
if a.providers > 0 {
a.consumers = a.consumers.saturating_add(1);
Ok(())
if a.consumers < T::MaxConsumers::get() {
a.consumers = a.consumers.saturating_add(1);
Ok(())
} else {
Err(DispatchError::TooManyConsumers)
}
} else {
Err(DispatchError::NoProviders)
}
@@ -1148,7 +1156,8 @@ impl<T: Config> Pallet<T> {
/// True if the account has at least one provider reference.
pub fn can_inc_consumer(who: &T::AccountId) -> bool {
Account::<T>::get(who).providers > 0
let a = Account::<T>::get(who);
a.providers > 0 && a.consumers < T::MaxConsumers::get()
}
/// Deposits an event into this block's event record.
+1
View File
@@ -111,6 +111,7 @@ impl Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
pub type SysEvent = frame_system::Event<Test>;
+1
View File
@@ -372,6 +372,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const MinimumPeriod: u64 = 5;
+1
View File
@@ -83,6 +83,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
@@ -112,6 +112,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -169,6 +170,7 @@ impl pallet_assets::Config for Runtime {
type Currency = Balances;
type ForceOrigin = EnsureRoot<AccountId>;
type AssetDeposit = AssetDeposit;
type AssetAccountDeposit = frame_support::traits::ConstU64<2>;
type MetadataDepositBase = MetadataDeposit;
type MetadataDepositPerByte = MetadataDeposit;
type ApprovalDeposit = MetadataDeposit;
@@ -865,6 +865,7 @@ mod tests {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
@@ -77,6 +77,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -79,6 +79,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
+1
View File
@@ -69,6 +69,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
+1
View File
@@ -127,6 +127,7 @@ impl frame_system::Config for Test {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
+1
View File
@@ -64,6 +64,7 @@ impl frame_system::Config for Test {
type OnKilledAccount = ();
type OnNewAccount = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
type Origin = Origin;
type PalletInfo = PalletInfo;
type SS58Prefix = ();
+4
View File
@@ -494,6 +494,8 @@ pub enum DispatchError {
ConsumerRemaining,
/// There are no providers so the account cannot be created.
NoProviders,
/// There are too many consumers so the account cannot be created.
TooManyConsumers,
/// An error to do with tokens.
Token(TokenError),
/// An arithmetic error.
@@ -629,6 +631,7 @@ impl From<DispatchError> for &'static str {
DispatchError::Module { message, .. } => message.unwrap_or("Unknown module error"),
DispatchError::ConsumerRemaining => "Consumer remaining",
DispatchError::NoProviders => "No providers",
DispatchError::TooManyConsumers => "Too many consumers",
DispatchError::Token(e) => e.into(),
DispatchError::Arithmetic(e) => e.into(),
}
@@ -660,6 +663,7 @@ impl traits::Printable for DispatchError {
},
Self::ConsumerRemaining => "Consumer remaining".print(),
Self::NoProviders => "No providers".print(),
Self::TooManyConsumers => "Too many consumers".print(),
Self::Token(e) => {
"Token error: ".print();
<&'static str>::from(*e).print();
+1
View File
@@ -591,6 +591,7 @@ impl frame_system::Config for Runtime {
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_timestamp::Config for Runtime {