Deprecate Currency; introduce holds and freezing into fungible traits (#12951)

* First reworking of fungibles API

* New API and docs

* More fungible::* API improvements

* New ref-counting logic for old API

* Missing files

* Fixes

* Use the new transfer logic

* Use fungibles for the dispatchables

* Use shelve/restore names

* Locking works with total balance.

* repotting and removal

* Separate Holds from Reserves

* Introduce freezes

* Missing files

* Tests for freezing

* Fix hold+freeze combo

* More tests

* Fee-free dispatchable for upgrading accounts

* Benchmarks and a few fixes

* Another test

* Docs and refactor to avoid blanket impls

* Repot

* Fit out ItemOf fully

* Add events to Balanced traits

* Introduced events into Hold traits

* Fix Assets pallet tests

* Assets benchmarks pass

* Missing files and fixes

* Fixes

* Fixes

* Benchmarks fixes

* Fix balance benchmarks

* Formatting

* Expose fungible sub modules

* Move NIS to fungible API

* Fix broken impl and add test

* Fix tests

* API for `transfer_and_hold`

* Use composite APIs

* Formatting

* Upgraded event

* Fixes

* Fixes

* Fixes

* Fixes

* Repot tests and some fixed

* Fix some bits

* Fix dust tests

* Rename `set_balance`

- `Balances::set_balance` becomes `Balances::force_set_balance`
- `Unbalanced::set_balance` becomes `Unbalances::write_balance`

* becomes

* Move dust handling to fungibles API

* Formatting

* Fixes and more refactoring

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Fixes

* Use reducible_balance for better correctness on fees

* Reducing hold to zero should remove entry.

* Add test

* Docs

* Update frame/support/src/traits/tokens/fungibles/hold.rs

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>

* Update frame/support/src/traits/tokens/fungibles/regular.rs

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>

* Update frame/support/src/traits/tokens/fungible/hold.rs

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>

* Update frame/support/src/traits/tokens/fungible/regular.rs

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>

* Docs

* Docs

* Docs

* Fix NIS benchmarks

* Doc comment

* Remove post_mutation

* Fix some tests

* Fix some grumbles

* Enumify bool args to fungible(s) functions

* Fix up assets and balances

* Formatting

* Fix contracts

* Fix tests & benchmarks build

* Typify minted boolean arg

* Typify on_hold boolean arg; renames

* Fix numerous tests

* Fix dependency issue

* Privatize dangerous API mutate_account

* Fix contracts (@alext - please check this commit)

* Remove println

* Fix tests for contracts

* Fix broken rename

* Fix broken rename

* Fix broken rename

* Docs

* Update frame/support/src/traits/tokens/fungible/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* remove from_ref_time

* Update frame/executive/src/lib.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/executive/src/lib.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Reenable test

* Update frame/support/src/traits/tokens/fungibles/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungible/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungible/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungible/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/currency.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/lottery/src/tests.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungible/mod.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungible/regular.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungibles/freeze.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungible/regular.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungibles/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungibles/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Update frame/support/src/traits/tokens/fungibles/hold.rs

Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>

* Rename UnwantedRemoval to UnwantedAccountRemoval

* Docs

* Formatting

* Update frame/balances/src/lib.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* Update primitives/runtime/src/lib.rs

Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>

* handle_raw_dust oes nothing

* Formatting

* Fixes

* Grumble

* Fixes

* Add test

* Add test

* Tests for reducible_balance

* Fixes

* Fix Salary

* Fixes

* Disable broken test

* Disable nicely

* Fixes

* Fixes

* Fixes

* Rename some events

* Fix nomination pools breakage

* Add compatibility stub for transfer tx

* Reinstate a safely compatible version of Balances set_balance

* Fixes

* Grumble

* Update frame/nis/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_balances

* disable flakey tests

* Update frame/balances/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Grumbles

* Grumble

---------

Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com>
Co-authored-by: Alexander Theißen <alex.theissen@me.com>
Co-authored-by: Anthony Alaribe <anthonyalaribe@gmail.com>
Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: command-bot <>
This commit is contained in:
Gavin Wood
2023-03-18 14:47:55 +00:00
committed by GitHub
parent c699876ab8
commit 5d81f23f8f
129 changed files with 8370 additions and 6164 deletions
+65 -41
View File
@@ -21,7 +21,9 @@
use super::*;
use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller, BenchmarkError};
use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get};
use frame_support::traits::{
fungible::Inspect as FunInspect, nonfungible::Inspect, EnsureOrigin, Get,
};
use frame_system::RawOrigin;
use sp_arithmetic::Perquintill;
use sp_runtime::{
@@ -35,7 +37,7 @@ use crate::Pallet as Nis;
const SEED: u32 = 0;
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
<<T as Config>::Currency as FunInspect<<T as frame_system::Config>::AccountId>>::Balance;
fn fill_queues<T: Config>() -> Result<(), DispatchError> {
// filling queues involves filling the first queue entirely and placing a single item in all
@@ -45,10 +47,7 @@ fn fill_queues<T: Config>() -> Result<(), DispatchError> {
let bids = T::MaxQueueLen::get();
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(
&caller,
T::MinBid::get() * BalanceOf::<T>::from(queues + bids),
);
T::Currency::set_balance(&caller, T::MinBid::get() * BalanceOf::<T>::from(queues + bids));
for _ in 0..bids {
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
@@ -63,7 +62,9 @@ benchmarks! {
place_bid {
let l in 0..(T::MaxQueueLen::get() - 1);
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
let ed = T::Currency::minimum_balance();
let bid = T::MinBid::get();
T::Currency::set_balance(&caller, (ed + bid) * BalanceOf::<T>::from(l + 1) + bid);
for i in 0..l {
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
}
@@ -75,7 +76,10 @@ benchmarks! {
place_bid_max {
let caller: T::AccountId = whitelisted_caller();
let origin = RawOrigin::Signed(caller.clone());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
let ed = T::Currency::minimum_balance();
let bid = T::MinBid::get();
let ql = T::MaxQueueLen::get();
T::Currency::set_balance(&caller, (ed + bid) * BalanceOf::<T>::from(ql + 1) + bid);
for i in 0..T::MaxQueueLen::get() {
Nis::<T>::place_bid(origin.clone().into(), T::MinBid::get(), 1)?;
}
@@ -90,7 +94,9 @@ benchmarks! {
retract_bid {
let l in 1..T::MaxQueueLen::get();
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
let ed = T::Currency::minimum_balance();
let bid = T::MinBid::get();
T::Currency::set_balance(&caller, (ed + bid) * BalanceOf::<T>::from(l + 1) + bid);
for i in 0..l {
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
}
@@ -104,50 +110,41 @@ benchmarks! {
T::FundOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let caller: T::AccountId = whitelisted_caller();
let bid = T::MinBid::get().max(One::one());
T::Currency::make_free_balance_be(&caller, bid);
let ed = T::Currency::minimum_balance();
T::Currency::set_balance(&caller, ed + bid);
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::process_queues(Perquintill::one(), 1, 1, &mut WeightCounter::unlimited());
Nis::<T>::communify(RawOrigin::Signed(caller.clone()).into(), 0)?;
let original = T::Currency::free_balance(&Nis::<T>::account_id());
T::Currency::make_free_balance_be(&Nis::<T>::account_id(), BalanceOf::<T>::min_value());
let original = T::Currency::balance(&Nis::<T>::account_id());
T::Currency::set_balance(&Nis::<T>::account_id(), BalanceOf::<T>::min_value());
}: _<T::RuntimeOrigin>(origin)
verify {
// Must fund at least 99.999% of the required amount.
let missing = Perquintill::from_rational(
T::Currency::free_balance(&Nis::<T>::account_id()), original).left_from_one();
T::Currency::balance(&Nis::<T>::account_id()), original).left_from_one();
assert!(missing <= Perquintill::one() / 100_000);
}
thaw_private {
communify {
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::<T>::from(3u32));
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
let bid = T::MinBid::get().max(One::one()) * 100u32.into();
let ed = T::Currency::minimum_balance();
T::Currency::set_balance(&caller, ed + bid + bid);
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited());
Receipts::<T>::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() });
}: _(RawOrigin::Signed(caller.clone()), 0, None)
verify {
assert!(Receipts::<T>::get(0).is_none());
}
thaw_communal {
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::<T>::from(3u32));
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
Nis::<T>::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited());
Receipts::<T>::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() });
Nis::<T>::communify(RawOrigin::Signed(caller.clone()).into(), 0)?;
}: _(RawOrigin::Signed(caller.clone()), 0)
verify {
assert!(Receipts::<T>::get(0).is_none());
assert_eq!(Nis::<T>::owner(&0), None);
}
privatize {
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::<T>::from(3u32));
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
let bid = T::MinBid::get().max(One::one());
let ed = T::Currency::minimum_balance();
T::Currency::set_balance(&caller, ed + bid + bid);
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited());
Nis::<T>::communify(RawOrigin::Signed(caller.clone()).into(), 0)?;
}: _(RawOrigin::Signed(caller.clone()), 0)
@@ -155,15 +152,39 @@ benchmarks! {
assert_eq!(Nis::<T>::owner(&0), Some(caller));
}
communify {
thaw_private {
let whale: T::AccountId = account("whale", 0, SEED);
let caller: T::AccountId = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::<T>::from(3u32));
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?;
let bid = T::MinBid::get().max(One::one());
let ed = T::Currency::minimum_balance();
T::Currency::set_balance(&caller, ed + bid + bid);
// Ensure we don't get throttled.
T::Currency::set_balance(&whale, T::ThawThrottle::get().0.saturating_reciprocal_mul_ceil(T::Currency::balance(&caller)));
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited());
frame_system::Pallet::<T>::set_block_number(Receipts::<T>::get(0).unwrap().expiry);
}: _(RawOrigin::Signed(caller.clone()), 0, None)
verify {
assert!(Receipts::<T>::get(0).is_none());
}
thaw_communal {
let whale: T::AccountId = account("whale", 0, SEED);
let caller: T::AccountId = whitelisted_caller();
let bid = T::MinBid::get().max(One::one());
let ed = T::Currency::minimum_balance();
T::Currency::set_balance(&caller, ed + bid + bid);
// Ensure we don't get throttled.
T::Currency::set_balance(&whale, T::ThawThrottle::get().0.saturating_reciprocal_mul_ceil(T::Currency::balance(&caller)));
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?;
Nis::<T>::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited());
frame_system::Pallet::<T>::set_block_number(Receipts::<T>::get(0).unwrap().expiry);
Nis::<T>::communify(RawOrigin::Signed(caller.clone()).into(), 0)?;
}: _(RawOrigin::Signed(caller.clone()), 0)
verify {
assert_eq!(Nis::<T>::owner(&0), None);
assert!(Receipts::<T>::get(0).is_none());
}
process_queues {
@@ -197,6 +218,9 @@ benchmarks! {
process_bid {
let who = account::<T::AccountId>("bidder", 0, SEED);
let min_bid = T::MinBid::get().max(One::one());
let ed = T::Currency::minimum_balance();
T::Currency::set_balance(&who, ed + min_bid);
let bid = Bid {
amount: T::MinBid::get(),
who,
@@ -216,5 +240,5 @@ benchmarks! {
)
}
impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext(), crate::mock::Test);
impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext_empty(), crate::mock::Test);
}
+121 -119
View File
@@ -71,21 +71,21 @@
//!
//! ## Terms
//!
//! - *Effective total issuance*: The total issuance of balances in the system, including all claims
//! of all outstanding receipts but excluding `IgnoredIssuance`.
//! - *Effective total issuance*: The total issuance of balances in the system, equal to the active
//! issuance plus the value of all outstanding receipts, less `IgnoredIssuance`.
#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::{
dispatch::{DispatchError, DispatchResult},
traits::fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate},
use frame_support::traits::{
fungible::{self, Inspect as FunInspect, Mutate as FunMutate},
tokens::{DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence},
};
pub use pallet::*;
use sp_arithmetic::{traits::Unsigned, RationalArg};
use sp_core::TypedGet;
use sp_runtime::{
traits::{Convert, ConvertBack},
Perquintill,
DispatchError, Perquintill,
};
mod benchmarking;
@@ -117,7 +117,7 @@ where
}
pub struct NoCounterpart<T>(sp_std::marker::PhantomData<T>);
impl<T> FungibleInspect<T> for NoCounterpart<T> {
impl<T> FunInspect<T> for NoCounterpart<T> {
type Balance = u32;
fn total_issuance() -> u32 {
0
@@ -125,34 +125,30 @@ impl<T> FungibleInspect<T> for NoCounterpart<T> {
fn minimum_balance() -> u32 {
0
}
fn balance(_who: &T) -> u32 {
fn balance(_: &T) -> u32 {
0
}
fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 {
fn total_balance(_: &T) -> u32 {
0
}
fn can_deposit(
_who: &T,
_amount: u32,
_mint: bool,
) -> frame_support::traits::tokens::DepositConsequence {
frame_support::traits::tokens::DepositConsequence::Success
fn reducible_balance(_: &T, _: Preservation, _: Fortitude) -> u32 {
0
}
fn can_withdraw(
_who: &T,
_amount: u32,
) -> frame_support::traits::tokens::WithdrawConsequence<u32> {
frame_support::traits::tokens::WithdrawConsequence::Success
fn can_deposit(_: &T, _: u32, _: Provenance) -> DepositConsequence {
DepositConsequence::Success
}
fn can_withdraw(_: &T, _: u32) -> WithdrawConsequence<u32> {
WithdrawConsequence::Success
}
}
impl<T> FungibleMutate<T> for NoCounterpart<T> {
fn mint_into(_who: &T, _amount: u32) -> DispatchResult {
Ok(())
}
fn burn_from(_who: &T, _amount: u32) -> Result<u32, DispatchError> {
Ok(0)
impl<T> fungible::Unbalanced<T> for NoCounterpart<T> {
fn handle_dust(_: fungible::Dust<T, Self>) {}
fn write_balance(_: &T, _: Self::Balance) -> Result<Option<Self::Balance>, DispatchError> {
Ok(None)
}
fn set_total_issuance(_: Self::Balance) {}
}
impl<T> FunMutate<T> for NoCounterpart<T> {}
impl<T> Convert<Perquintill, u32> for NoCounterpart<T> {
fn convert(_: Perquintill) -> u32 {
0
@@ -161,15 +157,24 @@ impl<T> Convert<Perquintill, u32> for NoCounterpart<T> {
#[frame_support::pallet]
pub mod pallet {
use super::{FungibleInspect, FungibleMutate};
use super::{FunInspect, FunMutate};
pub use crate::weights::WeightInfo;
use frame_support::{
pallet_prelude::*,
traits::{
nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer},
Currency, Defensive, DefensiveSaturating,
ExistenceRequirement::AllowDeath,
NamedReservableCurrency, OnUnbalanced,
fungible::{
self,
hold::{Inspect as FunHoldInspect, Mutate as FunHoldMutate},
Balanced as FunBalanced,
},
nonfungible::{Inspect as NftInspect, Transfer as NftTransfer},
tokens::{
Fortitude::Polite,
Precision::{BestEffort, Exact},
Preservation::Expendable,
Restriction::{Free, OnHold},
},
Defensive, DefensiveSaturating, OnUnbalanced,
},
PalletId,
};
@@ -177,15 +182,14 @@ pub mod pallet {
use sp_arithmetic::{PerThing, Perquintill};
use sp_runtime::{
traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero},
TokenError,
Rounding, TokenError,
};
use sp_std::prelude::*;
type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency<
<T as frame_system::Config>::AccountId,
>>::PositiveImbalance;
<<T as Config>::Currency as FunInspect<<T as frame_system::Config>::AccountId>>::Balance;
type DebtOf<T> =
fungible::Debt<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
type ReceiptRecordOf<T> = ReceiptRecord<
<T as frame_system::Config>::AccountId,
<T as frame_system::Config>::BlockNumber,
@@ -209,7 +213,16 @@ pub mod pallet {
type PalletId: Get<PalletId>;
/// Currency type that this works on.
type Currency: NamedReservableCurrency<Self::AccountId, Balance = Self::CurrencyBalance>;
type Currency: FunInspect<Self::AccountId, Balance = Self::CurrencyBalance>
+ FunMutate<Self::AccountId>
+ FunBalanced<Self::AccountId>
+ FunHoldInspect<Self::AccountId>
+ FunHoldMutate<Self::AccountId>;
/// The identifier of the hold reason.
#[pallet::constant]
type HoldReason: Get<<Self::Currency as FunHoldInspect<Self::AccountId>>::Reason>;
/// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
/// `From<u64>`.
@@ -231,7 +244,7 @@ pub mod pallet {
type IgnoredIssuance: Get<BalanceOf<Self>>;
/// The accounting system for the fungible counterpart tokens.
type Counterpart: FungibleMutate<Self::AccountId>;
type Counterpart: FunMutate<Self::AccountId>;
/// The system to convert an overall proportion of issuance into a number of fungible
/// counterpart tokens.
@@ -239,12 +252,12 @@ pub mod pallet {
/// In general it's best to use `WithMaximumOf`.
type CounterpartAmount: ConvertBack<
Perquintill,
<Self::Counterpart as FungibleInspect<Self::AccountId>>::Balance,
<Self::Counterpart as FunInspect<Self::AccountId>>::Balance,
>;
/// Unbalanced handler to account for funds created (in case of a higher total issuance over
/// freezing period).
type Deficit: OnUnbalanced<PositiveImbalanceOf<Self>>;
type Deficit: OnUnbalanced<DebtOf<Self>>;
/// The target sum of all receipts' proportions.
type Target: Get<Perquintill>;
@@ -301,12 +314,6 @@ pub mod pallet {
/// The maximum proportion which may be thawed and the period over which it is reset.
#[pallet::constant]
type ThawThrottle: Get<(Perquintill, Self::BlockNumber)>;
/// The name for the reserve ID.
#[pallet::constant]
type ReserveId: Get<
<Self::Currency as NamedReservableCurrency<Self::AccountId>>::ReserveIdentifier,
>;
}
#[pallet::pallet]
@@ -348,7 +355,7 @@ pub mod pallet {
///
/// `issuance - frozen + proportion * issuance`
///
/// where `issuance = total_issuance - IgnoredIssuance`
/// where `issuance = active_issuance - IgnoredIssuance`
#[derive(
Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen,
)]
@@ -473,6 +480,9 @@ pub mod pallet {
AlreadyCommunal,
/// The receipt is already private.
AlreadyPrivate,
Release1,
Release2,
Tah,
}
pub(crate) struct WeightCounter {
@@ -554,16 +564,17 @@ pub mod pallet {
|q| -> Result<(u32, BalanceOf<T>), DispatchError> {
let queue_full = q.len() == T::MaxQueueLen::get() as usize;
ensure!(!queue_full || q[0].amount < amount, Error::<T>::BidTooLow);
T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?;
T::Currency::hold(&T::HoldReason::get(), &who, amount)?;
// queue is <Ordered: Lowest ... Highest><Fifo: Last ... First>
let mut bid = Bid { amount, who: who.clone() };
let net = if queue_full {
sp_std::mem::swap(&mut q[0], &mut bid);
let _ = T::Currency::unreserve_named(
&T::ReserveId::get(),
let _ = T::Currency::release(
&T::HoldReason::get(),
&bid.who,
bid.amount,
BestEffort,
);
Self::deposit_event(Event::<T>::BidDropped {
who: bid.who,
@@ -615,19 +626,21 @@ pub mod pallet {
ensure!(queue_index < queue_count, Error::<T>::DurationTooBig);
let bid = Bid { amount, who };
let new_len = Queues::<T>::try_mutate(duration, |q| -> Result<u32, DispatchError> {
let pos = q.iter().position(|i| i == &bid).ok_or(Error::<T>::UnknownBid)?;
q.remove(pos);
Ok(q.len() as u32)
})?;
let mut queue = Queues::<T>::get(duration);
let pos = queue.iter().position(|i| i == &bid).ok_or(Error::<T>::UnknownBid)?;
queue.remove(pos);
let new_len = queue.len() as u32;
T::Currency::release(&T::HoldReason::get(), &bid.who, bid.amount, BestEffort)?;
Queues::<T>::insert(duration, queue);
QueueTotals::<T>::mutate(|qs| {
qs.bounded_resize(queue_count, (0, Zero::zero()));
qs[queue_index].0 = new_len;
qs[queue_index].1.saturating_reduce(bid.amount);
});
T::Currency::unreserve_named(&T::ReserveId::get(), &bid.who, bid.amount);
Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration });
Ok(())
@@ -645,7 +658,7 @@ pub mod pallet {
let issuance = Self::issuance_with(&our_account, &summary);
let deficit = issuance.required.saturating_sub(issuance.holdings);
ensure!(!deficit.is_zero(), Error::<T>::AlreadyFunded);
T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit));
T::Deficit::on_unbalanced(T::Currency::deposit(&our_account, deficit, Exact)?);
Self::deposit_event(Event::<T>::Funded { deficit });
Ok(())
}
@@ -702,6 +715,7 @@ pub mod pallet {
// Multiply the proportion it is by the total issued.
let our_account = Self::account_id();
let effective_issuance = Self::issuance_with(&our_account, &summary).effective;
// let amount = proportion.mul_ceil(effective_issuance);
let amount = proportion * effective_issuance;
receipt.proportion.saturating_reduce(proportion);
@@ -710,46 +724,40 @@ pub mod pallet {
let dropped = receipt.proportion.is_zero();
if amount > on_hold {
T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold);
T::Currency::release(&T::HoldReason::get(), &who, on_hold, Exact)
.map_err(|_| Error::<T>::Release1)?;
let deficit = amount - on_hold;
// Try to transfer deficit from pot to receipt owner.
summary.receipts_on_hold.saturating_reduce(on_hold);
on_hold = Zero::zero();
T::Currency::transfer(&our_account, &who, deficit, AllowDeath)
T::Currency::transfer(&our_account, &who, deficit, Expendable)
.map_err(|_| Error::<T>::Unfunded)?;
} else {
T::Currency::unreserve_named(&T::ReserveId::get(), &who, amount);
on_hold.saturating_reduce(amount);
summary.receipts_on_hold.saturating_reduce(amount);
if dropped && !on_hold.is_zero() {
// Reclaim any remainder:
// Transfer `excess` to the pot if we have now fully compensated for the
// receipt.
//
// This will legitimately fail if there is no pot account in existance.
// There's nothing we can do about this so we just swallow the error.
// This code is not ideal and could fail in the second phase leaving
// the system in an invalid state. It can be fixed properly with the
// new API in https://github.com/paritytech/substrate/pull/12951
//
// Below is what it should look like then:
// let _ = T::Currency::repatriate_reserved_named(
// &T::ReserveId::get(),
// &who,
// &our_account,
// excess,
// BalanceStatus::Free,
// ).defensive();
T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold);
// It could theoretically be locked, so really we should be using a more
// forceful variant. But the alternative `repatriate_reserved_named` will
// fail if the destination account doesn't exist. This should be fixed when
// we move to the `fungible::*` traits, which should include a force
// transfer function to transfer the reserved balance into free balance in
// the destination regardless of locks and create it if it doesn't exist.
let _ = T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath);
// Transfer excess of `on_hold` to the pot if we have now fully compensated for
// the receipt.
T::Currency::transfer_on_hold(
&T::HoldReason::get(),
&who,
&our_account,
on_hold,
Exact,
Free,
Polite,
)
.map(|_| ())
// We ignore this error as it just means the amount we're trying to deposit is
// dust and the beneficiary account doesn't exist.
.or_else(
|e| if e == TokenError::CannotCreate.into() { Ok(()) } else { Err(e) },
)?;
summary.receipts_on_hold.saturating_reduce(on_hold);
}
T::Currency::release(&T::HoldReason::get(), &who, amount, Exact)
.map_err(|_| Error::<T>::Release2)?;
}
if dropped {
@@ -797,7 +805,8 @@ pub mod pallet {
summary.thawed.saturating_accrue(receipt.proportion);
ensure!(summary.thawed <= throttle, Error::<T>::Throttled);
T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?;
let cp_amount = T::CounterpartAmount::convert(receipt.proportion);
T::Counterpart::burn_from(&who, cp_amount, Exact, Polite)?;
// Multiply the proportion it is by the total issued.
let our_account = Self::account_id();
@@ -807,7 +816,7 @@ pub mod pallet {
summary.proportion_owed.saturating_reduce(receipt.proportion);
// Try to transfer amount owed from pot to receipt owner.
T::Currency::transfer(&our_account, &who, amount, AllowDeath)
T::Currency::transfer(&our_account, &who, amount, Expendable)
.map_err(|_| Error::<T>::Unfunded)?;
Receipts::<T>::remove(index);
@@ -840,11 +849,10 @@ pub mod pallet {
ensure!(owner == who, Error::<T>::NotOwner);
// Unreserve and transfer the funds to the pot.
T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold);
// Transfer `excess` to the pot if we have now fully compensated for the receipt.
T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath)
let reason = T::HoldReason::get();
let us = Self::account_id();
T::Currency::transfer_on_hold(&reason, &who, &us, on_hold, Exact, Free, Polite)
.map_err(|_| Error::<T>::Unfunded)?;
// TODO #12951: ^^^ The above should be done in a single operation `transfer_on_hold`.
// Record that we've moved the amount reserved.
let mut summary: SummaryRecordOf<T> = Summary::<T>::get();
@@ -881,16 +889,20 @@ pub mod pallet {
let effective_issuance = Self::issuance_with(&our_account, &summary).effective;
let max_amount = receipt.proportion * effective_issuance;
// Avoid trying to place more in the account's reserve than we have available in the pot
let amount = max_amount.min(T::Currency::free_balance(&our_account));
let amount = max_amount.min(T::Currency::balance(&our_account));
// Burn fungible counterparts.
T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?;
T::Counterpart::burn_from(
&who,
T::CounterpartAmount::convert(receipt.proportion),
Exact,
Polite,
)?;
// Transfer the funds from the pot to the owner and reserve
T::Currency::transfer(&Self::account_id(), &who, amount, AllowDeath)
.map_err(|_| Error::<T>::Unfunded)?;
T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?;
// TODO: ^^^ The above should be done in a single operation `transfer_and_hold`.
let reason = T::HoldReason::get();
let us = Self::account_id();
T::Currency::transfer_and_hold(&reason, &us, &who, amount, Exact, Expendable, Polite)?;
// Record that we've moved the amount reserved.
summary.receipts_on_hold.saturating_accrue(amount);
@@ -920,7 +932,7 @@ pub mod pallet {
pub required: Balance,
}
impl<T: Config> NonfungibleInspect<T::AccountId> for Pallet<T> {
impl<T: Config> NftInspect<T::AccountId> for Pallet<T> {
type ItemId = ReceiptIndex;
fn owner(item: &ReceiptIndex) -> Option<T::AccountId> {
@@ -939,31 +951,19 @@ pub mod pallet {
}
}
impl<T: Config> NonfungibleTransfer<T::AccountId> for Pallet<T> {
fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult {
impl<T: Config> NftTransfer<T::AccountId> for Pallet<T> {
fn transfer(index: &ReceiptIndex, dest: &T::AccountId) -> DispatchResult {
let mut item = Receipts::<T>::get(index).ok_or(TokenError::UnknownAsset)?;
let (owner, on_hold) = item.owner.take().ok_or(Error::<T>::AlreadyCommunal)?;
// TODO: This should all be replaced by a single call `transfer_held`.
let shortfall = T::Currency::unreserve_named(&T::ReserveId::get(), &owner, on_hold);
if !shortfall.is_zero() {
let _ =
T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold - shortfall);
return Err(TokenError::NoFunds.into())
}
if let Err(e) = T::Currency::transfer(&owner, destination, on_hold, AllowDeath) {
let _ = T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold);
return Err(e)
}
// This can never fail, and if it somehow does, then we can't handle this gracefully.
let _ =
T::Currency::reserve_named(&T::ReserveId::get(), destination, on_hold).defensive();
let reason = T::HoldReason::get();
T::Currency::transfer_on_hold(&reason, &owner, dest, on_hold, Exact, OnHold, Polite)?;
item.owner = Some((destination.clone(), on_hold));
item.owner = Some((dest.clone(), on_hold));
Receipts::<T>::insert(&index, &item);
Pallet::<T>::deposit_event(Event::<T>::Transferred {
from: owner,
to: destination.clone(),
to: dest.clone(),
index: *index,
});
Ok(())
@@ -997,9 +997,9 @@ pub mod pallet {
summary: &SummaryRecordOf<T>,
) -> IssuanceInfo<BalanceOf<T>> {
let total_issuance =
T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
T::Currency::active_issuance().saturating_sub(T::IgnoredIssuance::get());
let holdings =
T::Currency::free_balance(our_account).saturating_add(summary.receipts_on_hold);
T::Currency::balance(our_account).saturating_add(summary.receipts_on_hold);
let other = total_issuance.saturating_sub(holdings);
let effective =
summary.proportion_owed.left_from_one().saturating_reciprocal_mul(other);
@@ -1136,7 +1136,9 @@ pub mod pallet {
// Now to activate the bid...
let n = amount;
let d = issuance.effective;
let proportion = Perquintill::from_rational(n, d);
let proportion =
Perquintill::from_rational_with_rounding(n, d, Rounding::NearestPrefDown)
.defensive_unwrap_or_default();
let who = bid.who;
let index = summary.index;
summary.proportion_owed.defensive_saturating_accrue(proportion);
+39 -11
View File
@@ -19,13 +19,18 @@
use crate::{self as pallet_nis, Perquintill, WithMaximumOf};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
ord_parameter_types, parameter_types,
traits::{ConstU16, ConstU32, ConstU64, Currency, OnFinalize, OnInitialize, StorageMapShim},
traits::{
fungible::Inspect, ConstU16, ConstU32, ConstU64, Everything, OnFinalize, OnInitialize,
StorageMapShim,
},
weights::Weight,
PalletId,
};
use pallet_balances::{Instance1, Instance2};
use scale_info::TypeInfo;
use sp_core::{ConstU128, H256};
use sp_runtime::{
testing::Header,
@@ -35,6 +40,8 @@ use sp_runtime::{
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
pub type Balance = u64;
// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test where
@@ -50,7 +57,7 @@ frame_support::construct_runtime!(
);
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BaseCallFilter = Everything;
type BlockWeights = ();
type BlockLength = ();
type RuntimeOrigin = RuntimeOrigin;
@@ -67,35 +74,45 @@ impl frame_system::Config for Test {
type DbWeight = ();
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ConstU16<42>;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
type MaxConsumers = ConstU32<16>;
}
impl pallet_balances::Config<Instance1> for Test {
type Balance = u64;
type Balance = Balance;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = frame_support::traits::ConstU64<1>;
type ExistentialDeposit = ConstU64<1>;
type AccountStore = System;
type WeightInfo = ();
type MaxLocks = ();
type MaxReserves = ConstU32<1>;
type ReserveIdentifier = [u8; 8];
type FreezeIdentifier = ();
type MaxFreezes = ();
type HoldIdentifier = HoldIdentifier;
type MaxHolds = ConstU32<1>;
}
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo,
)]
pub enum HoldIdentifier {
Nis,
}
impl pallet_balances::Config<Instance2> for Test {
type Balance = u128;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = frame_support::traits::ConstU128<1>;
type ExistentialDeposit = ConstU128<1>;
type AccountStore = StorageMapShim<
pallet_balances::Account<Test, Instance2>,
frame_system::Provider<Test>,
u64,
pallet_balances::AccountData<u128>,
>;
@@ -103,16 +120,20 @@ impl pallet_balances::Config<Instance2> for Test {
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type FreezeIdentifier = ();
type MaxFreezes = ();
type HoldIdentifier = ();
type MaxHolds = ();
}
parameter_types! {
pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored.
pub IgnoredIssuance: Balance = Balances::total_balance(&0); // Account zero is ignored.
pub const NisPalletId: PalletId = PalletId(*b"py/nis ");
pub static Target: Perquintill = Perquintill::zero();
pub const MinReceipt: Perquintill = Perquintill::from_percent(1);
pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5);
pub static MaxIntakeWeight: Weight = Weight::from_parts(2_000_000_000_000, 0);
pub const ReserveId: [u8; 8] = *b"py/nis ";
pub const HoldReason: HoldIdentifier = HoldIdentifier::Nis;
}
ord_parameter_types! {
@@ -140,7 +161,7 @@ impl pallet_nis::Config for Test {
type MaxIntakeWeight = MaxIntakeWeight;
type MinReceipt = MinReceipt;
type ThawThrottle = ThawThrottle;
type ReserveId = ReserveId;
type HoldReason = HoldReason;
}
// This function basically just builds a genesis storage key/value store according to
@@ -155,6 +176,13 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
t.into()
}
// This function basically just builds a genesis storage key/value store according to
// our desired mockup, but without any balances.
#[cfg(feature = "runtime-benchmarks")]
pub fn new_test_ext_empty() -> sp_io::TestExternalities {
frame_system::GenesisConfig::default().build_storage::<Test>().unwrap().into()
}
pub fn run_to_block(n: u64) {
while System::block_number() < n {
Nis::on_finalize(System::block_number());
+66 -44
View File
@@ -22,19 +22,22 @@ use crate::{mock::*, Error};
use frame_support::{
assert_noop, assert_ok,
traits::{
fungible::{hold::Inspect as InspectHold, Inspect as FunInspect, Mutate as FunMutate},
nonfungible::{Inspect, Transfer},
Currency,
tokens::{Fortitude::Force, Precision::Exact},
},
};
use pallet_balances::{Error as BalancesError, Instance1};
use sp_arithmetic::Perquintill;
use sp_runtime::{Saturating, TokenError};
use sp_runtime::{
Saturating,
TokenError::{self, FundsUnavailable},
};
fn pot() -> u64 {
fn pot() -> Balance {
Balances::free_balance(&Nis::account_id())
}
fn holdings() -> u64 {
fn holdings() -> Balance {
Nis::issuance().holdings
}
@@ -42,8 +45,8 @@ fn signed(who: u64) -> RuntimeOrigin {
RuntimeOrigin::signed(who)
}
fn enlarge(amount: u64, max_bids: u32) {
let summary: SummaryRecord<u64, u64> = Summary::<Test>::get();
fn enlarge(amount: Balance, max_bids: u32) {
let summary: SummaryRecord<u64, Balance> = Summary::<Test>::get();
let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective);
let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed);
Nis::process_queues(target, u32::max_value(), max_bids, &mut WeightCounter::unlimited());
@@ -75,10 +78,7 @@ fn place_bid_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_noop!(Nis::place_bid(signed(1), 1, 2), Error::<Test>::AmountTooSmall);
assert_noop!(
Nis::place_bid(signed(1), 101, 2),
BalancesError::<Test, Instance1>::InsufficientBalance
);
assert_noop!(Nis::place_bid(signed(1), 101, 2), FundsUnavailable);
assert_noop!(Nis::place_bid(signed(1), 10, 4), Error::<Test>::DurationTooBig);
assert_ok!(Nis::place_bid(signed(1), 10, 2));
assert_eq!(Balances::reserved_balance(1), 10);
@@ -451,16 +451,16 @@ fn communify_works() {
assert_noop!(Nis::thaw_communal(signed(1), 1), Error::<Test>::UnknownReceipt);
// Transfer some of the fungibles away.
assert_ok!(NisBalances::transfer(signed(1), 2, 100_000));
assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 100_000));
assert_eq!(NisBalances::free_balance(&1), 2_000_000);
assert_eq!(NisBalances::free_balance(&2), 100_000);
// Communal thawing with the correct index is not possible now.
assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds);
assert_noop!(Nis::thaw_communal(signed(2), 0), TokenError::NoFunds);
assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::FundsUnavailable);
assert_noop!(Nis::thaw_communal(signed(2), 0), TokenError::FundsUnavailable);
// Transfer the rest to 2...
assert_ok!(NisBalances::transfer(signed(1), 2, 2_000_000));
assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 2_000_000));
assert_eq!(NisBalances::free_balance(&1), 0);
assert_eq!(NisBalances::free_balance(&2), 2_100_000);
@@ -487,8 +487,8 @@ fn privatize_works() {
assert_ok!(Nis::communify(signed(1), 0));
// Transfer the fungibles to #2
assert_ok!(NisBalances::transfer(signed(1), 2, 2_100_000));
assert_noop!(Nis::privatize(signed(1), 0), TokenError::NoFunds);
assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 2_100_000));
assert_noop!(Nis::privatize(signed(1), 0), TokenError::FundsUnavailable);
// Privatize
assert_ok!(Nis::privatize(signed(2), 0));
@@ -513,16 +513,16 @@ fn privatize_and_thaw_with_another_receipt_works() {
assert_ok!(Nis::communify(signed(2), 1));
// Transfer half of fungibles to #3 from each of #1 and #2, and the other half from #2 to #4
assert_ok!(NisBalances::transfer(signed(1), 3, 1_050_000));
assert_ok!(NisBalances::transfer(signed(2), 3, 1_050_000));
assert_ok!(NisBalances::transfer(signed(2), 4, 1_050_000));
assert_ok!(NisBalances::transfer_allow_death(signed(1), 3, 1_050_000));
assert_ok!(NisBalances::transfer_allow_death(signed(2), 3, 1_050_000));
assert_ok!(NisBalances::transfer_allow_death(signed(2), 4, 1_050_000));
// #3 privatizes, partially thaws, then re-communifies with #0, then transfers the fungibles
// to #2
assert_ok!(Nis::privatize(signed(3), 0));
assert_ok!(Nis::thaw_private(signed(3), 0, Some(Perquintill::from_percent(5))));
assert_ok!(Nis::communify(signed(3), 0));
assert_ok!(NisBalances::transfer(signed(3), 1, 1_050_000));
assert_ok!(NisBalances::transfer_allow_death(signed(3), 1, 1_050_000));
// #1 now has enough to thaw using receipt 1
assert_ok!(Nis::thaw_communal(signed(1), 1));
@@ -536,17 +536,21 @@ fn privatize_and_thaw_with_another_receipt_works() {
fn communal_thaw_when_issuance_higher_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
assert_ok!(Nis::place_bid(signed(1), 100, 1));
enlarge(100, 1);
assert_eq!(Balances::total_balance(&1), 101);
assert_ok!(Nis::communify(signed(1), 0));
assert_eq!(Balances::total_balance_on_hold(&1), 0);
assert_eq!(Balances::total_balance(&1), 1);
assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m)
assert_eq!(NisBalances::free_balance(1), 5_250_000); // (12.5% of 21m)
// Everybody else's balances goes up by 50%
Balances::make_free_balance_be(&2, 150);
Balances::make_free_balance_be(&3, 150);
Balances::make_free_balance_be(&4, 150);
assert_ok!(Balances::mint_into(&2, 50));
assert_ok!(Balances::mint_into(&3, 50));
assert_ok!(Balances::mint_into(&4, 50));
run_to_block(4);
@@ -556,16 +560,20 @@ fn communal_thaw_when_issuance_higher_works() {
assert_ok!(Nis::fund_deficit(signed(1)));
// Transfer counterparts away...
assert_ok!(NisBalances::transfer(signed(1), 2, 250_000));
assert_ok!(NisBalances::transfer_allow_death(signed(1), 2, 125_000));
// ...and it's not thawable.
assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds);
assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::FundsUnavailable);
// Transfer counterparts back...
assert_ok!(NisBalances::transfer(signed(2), 1, 250_000));
assert_ok!(NisBalances::transfer_allow_death(signed(2), 1, 125_000));
// ...and it is.
assert_ok!(Nis::thaw_communal(signed(1), 0));
assert_eq!(Balances::total_balance(&1), 151);
assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1));
assert_eq!(Balances::total_balance(&1), 150);
assert_eq!(Balances::free_balance(1), 150);
assert_eq!(Balances::total_balance_on_hold(&1), 0);
assert_eq!(Balances::reserved_balance(1), 0);
});
}
@@ -574,13 +582,14 @@ fn communal_thaw_when_issuance_higher_works() {
fn private_thaw_when_issuance_higher_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
assert_ok!(Nis::place_bid(signed(1), 100, 1));
enlarge(100, 1);
// Everybody else's balances goes up by 50%
Balances::make_free_balance_be(&2, 150);
Balances::make_free_balance_be(&3, 150);
Balances::make_free_balance_be(&4, 150);
assert_ok!(Balances::mint_into(&2, 50));
assert_ok!(Balances::mint_into(&3, 50));
assert_ok!(Balances::mint_into(&4, 50));
run_to_block(4);
@@ -591,6 +600,7 @@ fn private_thaw_when_issuance_higher_works() {
assert_ok!(Nis::thaw_private(signed(1), 0, None));
assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1));
assert_eq!(Balances::free_balance(1), 150);
assert_eq!(Balances::reserved_balance(1), 0);
});
@@ -601,15 +611,16 @@ fn thaw_with_ignored_issuance_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
// Give account zero some balance.
Balances::make_free_balance_be(&0, 200);
assert_ok!(Balances::mint_into(&0, 200));
assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
assert_ok!(Nis::place_bid(signed(1), 100, 1));
enlarge(100, 1);
// Account zero transfers 50 into everyone else's accounts.
assert_ok!(Balances::transfer(signed(0), 2, 50));
assert_ok!(Balances::transfer(signed(0), 3, 50));
assert_ok!(Balances::transfer(signed(0), 4, 50));
assert_ok!(Balances::transfer_allow_death(signed(0), 2, 50));
assert_ok!(Balances::transfer_allow_death(signed(0), 3, 50));
assert_ok!(Balances::transfer_allow_death(signed(0), 4, 50));
run_to_block(4);
// Unfunded initially...
@@ -620,6 +631,7 @@ fn thaw_with_ignored_issuance_works() {
assert_ok!(Nis::thaw_private(signed(1), 0, None));
// Account zero changes have been ignored.
assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1));
assert_eq!(Balances::free_balance(1), 150);
assert_eq!(Balances::reserved_balance(1), 0);
});
@@ -629,17 +641,19 @@ fn thaw_with_ignored_issuance_works() {
fn thaw_when_issuance_lower_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
assert_ok!(Nis::place_bid(signed(1), 100, 1));
enlarge(100, 1);
// Everybody else's balances goes down by 25%
Balances::make_free_balance_be(&2, 75);
Balances::make_free_balance_be(&3, 75);
Balances::make_free_balance_be(&4, 75);
assert_ok!(Balances::burn_from(&2, 25, Exact, Force));
assert_ok!(Balances::burn_from(&3, 25, Exact, Force));
assert_ok!(Balances::burn_from(&4, 25, Exact, Force));
run_to_block(4);
assert_ok!(Nis::thaw_private(signed(1), 0, None));
assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1));
assert_eq!(Balances::free_balance(1), 75);
assert_eq!(Balances::reserved_balance(1), 0);
});
@@ -649,15 +663,16 @@ fn thaw_when_issuance_lower_works() {
fn multiple_thaws_works() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Balances::transfer_allow_death(signed(3), 1, 1));
assert_ok!(Nis::place_bid(signed(1), 40, 1));
assert_ok!(Nis::place_bid(signed(1), 60, 1));
assert_ok!(Nis::place_bid(signed(2), 50, 1));
enlarge(200, 3);
// Double everyone's free balances.
Balances::make_free_balance_be(&2, 100);
Balances::make_free_balance_be(&3, 200);
Balances::make_free_balance_be(&4, 200);
assert_ok!(Balances::mint_into(&2, 50));
assert_ok!(Balances::mint_into(&3, 100));
assert_ok!(Balances::mint_into(&4, 100));
assert_ok!(Nis::fund_deficit(signed(1)));
run_to_block(4);
@@ -667,8 +682,11 @@ fn multiple_thaws_works() {
run_to_block(5);
assert_ok!(Nis::thaw_private(signed(2), 2, None));
assert_ok!(Balances::transfer_allow_death(signed(1), 3, 1));
assert_eq!(Balances::free_balance(1), 200);
assert_eq!(Balances::free_balance(2), 200);
assert_eq!(Balances::total_balance(&1), 200);
assert_eq!(Balances::total_balance(&2), 200);
});
}
@@ -676,15 +694,16 @@ fn multiple_thaws_works() {
fn multiple_thaws_works_in_alternative_thaw_order() {
new_test_ext().execute_with(|| {
run_to_block(1);
assert_ok!(Balances::transfer_allow_death(signed(3), 1, 1));
assert_ok!(Nis::place_bid(signed(1), 40, 1));
assert_ok!(Nis::place_bid(signed(1), 60, 1));
assert_ok!(Nis::place_bid(signed(2), 50, 1));
enlarge(200, 3);
// Double everyone's free balances.
Balances::make_free_balance_be(&2, 100);
Balances::make_free_balance_be(&3, 200);
Balances::make_free_balance_be(&4, 200);
assert_ok!(Balances::mint_into(&2, 50));
assert_ok!(Balances::mint_into(&3, 100));
assert_ok!(Balances::mint_into(&4, 100));
assert_ok!(Nis::fund_deficit(signed(1)));
run_to_block(4);
@@ -695,8 +714,11 @@ fn multiple_thaws_works_in_alternative_thaw_order() {
run_to_block(5);
assert_ok!(Nis::thaw_private(signed(1), 1, None));
assert_ok!(Balances::transfer_allow_death(signed(1), 3, 1));
assert_eq!(Balances::free_balance(1), 200);
assert_eq!(Balances::free_balance(2), 200);
assert_eq!(Balances::total_balance(&1), 200);
assert_eq!(Balances::total_balance(&2), 200);
});
}