mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 05:21:03 +00:00
5d81f23f8f
* 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 <>
359 lines
12 KiB
Rust
359 lines
12 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 2017-2022 Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Implementation of `fungible` traits for Balances pallet.
|
|
use super::*;
|
|
use frame_support::traits::tokens::{
|
|
Fortitude,
|
|
Preservation::{self, Preserve, Protect},
|
|
Provenance::{self, Minted},
|
|
};
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::Inspect<T::AccountId> for Pallet<T, I> {
|
|
type Balance = T::Balance;
|
|
|
|
fn total_issuance() -> Self::Balance {
|
|
TotalIssuance::<T, I>::get()
|
|
}
|
|
fn active_issuance() -> Self::Balance {
|
|
TotalIssuance::<T, I>::get().saturating_sub(InactiveIssuance::<T, I>::get())
|
|
}
|
|
fn minimum_balance() -> Self::Balance {
|
|
T::ExistentialDeposit::get()
|
|
}
|
|
fn total_balance(who: &T::AccountId) -> Self::Balance {
|
|
Self::account(who).total()
|
|
}
|
|
fn balance(who: &T::AccountId) -> Self::Balance {
|
|
Self::account(who).free
|
|
}
|
|
fn reducible_balance(
|
|
who: &T::AccountId,
|
|
preservation: Preservation,
|
|
force: Fortitude,
|
|
) -> Self::Balance {
|
|
let a = Self::account(who);
|
|
let mut untouchable = Zero::zero();
|
|
if force == Polite {
|
|
// Frozen balance applies to total. Anything on hold therefore gets discounted from the
|
|
// limit given by the freezes.
|
|
untouchable = a.frozen.saturating_sub(a.reserved);
|
|
}
|
|
// If we want to keep our provider ref..
|
|
if preservation == Preserve
|
|
// ..or we don't want the account to die and our provider ref is needed for it to live..
|
|
|| preservation == Protect && !a.free.is_zero() &&
|
|
frame_system::Pallet::<T>::providers(who) == 1
|
|
// ..or we don't care about the account dying but our provider ref is required..
|
|
|| preservation == Expendable && !a.free.is_zero() &&
|
|
!frame_system::Pallet::<T>::can_dec_provider(who)
|
|
{
|
|
// ..then the ED needed..
|
|
untouchable = untouchable.max(T::ExistentialDeposit::get());
|
|
}
|
|
// Liquid balance is what is neither on hold nor frozen/required for provider.
|
|
a.free.saturating_sub(untouchable)
|
|
}
|
|
fn can_deposit(
|
|
who: &T::AccountId,
|
|
amount: Self::Balance,
|
|
provenance: Provenance,
|
|
) -> DepositConsequence {
|
|
if amount.is_zero() {
|
|
return DepositConsequence::Success
|
|
}
|
|
|
|
if provenance == Minted && TotalIssuance::<T, I>::get().checked_add(&amount).is_none() {
|
|
return DepositConsequence::Overflow
|
|
}
|
|
|
|
let account = Self::account(who);
|
|
let new_free = match account.free.checked_add(&amount) {
|
|
None => return DepositConsequence::Overflow,
|
|
Some(x) if x < T::ExistentialDeposit::get() => return DepositConsequence::BelowMinimum,
|
|
Some(x) => x,
|
|
};
|
|
|
|
match account.reserved.checked_add(&new_free) {
|
|
Some(_) => {},
|
|
None => return DepositConsequence::Overflow,
|
|
};
|
|
|
|
// NOTE: We assume that we are a provider, so don't need to do any checks in the
|
|
// case of account creation.
|
|
|
|
DepositConsequence::Success
|
|
}
|
|
fn can_withdraw(
|
|
who: &T::AccountId,
|
|
amount: Self::Balance,
|
|
) -> WithdrawConsequence<Self::Balance> {
|
|
if amount.is_zero() {
|
|
return WithdrawConsequence::Success
|
|
}
|
|
|
|
if TotalIssuance::<T, I>::get().checked_sub(&amount).is_none() {
|
|
return WithdrawConsequence::Underflow
|
|
}
|
|
|
|
let account = Self::account(who);
|
|
let new_free_balance = match account.free.checked_sub(&amount) {
|
|
Some(x) => x,
|
|
None => return WithdrawConsequence::BalanceLow,
|
|
};
|
|
|
|
let liquid = Self::reducible_balance(who, Expendable, Polite);
|
|
if amount > liquid {
|
|
return WithdrawConsequence::Frozen
|
|
}
|
|
|
|
// Provider restriction - total account balance cannot be reduced to zero if it cannot
|
|
// sustain the loss of a provider reference.
|
|
// NOTE: This assumes that the pallet is a provider (which is true). Is this ever changes,
|
|
// then this will need to adapt accordingly.
|
|
let ed = T::ExistentialDeposit::get();
|
|
let success = if new_free_balance < ed {
|
|
if frame_system::Pallet::<T>::can_dec_provider(who) {
|
|
WithdrawConsequence::ReducedToZero(new_free_balance)
|
|
} else {
|
|
return WithdrawConsequence::WouldDie
|
|
}
|
|
} else {
|
|
WithdrawConsequence::Success
|
|
};
|
|
|
|
let new_total_balance = new_free_balance.saturating_add(account.reserved);
|
|
|
|
// Eventual free funds must be no less than the frozen balance.
|
|
if new_total_balance < account.frozen {
|
|
return WithdrawConsequence::Frozen
|
|
}
|
|
|
|
success
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::Unbalanced<T::AccountId> for Pallet<T, I> {
|
|
fn handle_dust(dust: fungible::Dust<T::AccountId, Self>) {
|
|
T::DustRemoval::on_unbalanced(dust.into_credit());
|
|
}
|
|
fn write_balance(
|
|
who: &T::AccountId,
|
|
amount: Self::Balance,
|
|
) -> Result<Option<Self::Balance>, DispatchError> {
|
|
let max_reduction =
|
|
<Self as fungible::Inspect<_>>::reducible_balance(who, Expendable, Force);
|
|
let (result, maybe_dust) = Self::mutate_account(who, |account| -> DispatchResult {
|
|
// Make sure the reduction (if there is one) is no more than the maximum allowed.
|
|
let reduction = account.free.saturating_sub(amount);
|
|
ensure!(reduction <= max_reduction, Error::<T, I>::InsufficientBalance);
|
|
|
|
account.free = amount;
|
|
Ok(())
|
|
})?;
|
|
result?;
|
|
Ok(maybe_dust)
|
|
}
|
|
|
|
fn set_total_issuance(amount: Self::Balance) {
|
|
TotalIssuance::<T, I>::mutate(|t| *t = amount);
|
|
}
|
|
|
|
fn deactivate(amount: Self::Balance) {
|
|
InactiveIssuance::<T, I>::mutate(|b| b.saturating_accrue(amount));
|
|
}
|
|
|
|
fn reactivate(amount: Self::Balance) {
|
|
InactiveIssuance::<T, I>::mutate(|b| b.saturating_reduce(amount));
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::Mutate<T::AccountId> for Pallet<T, I> {
|
|
fn done_mint_into(who: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Minted { who: who.clone(), amount });
|
|
}
|
|
fn done_burn_from(who: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Burned { who: who.clone(), amount });
|
|
}
|
|
fn done_shelve(who: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Suspended { who: who.clone(), amount });
|
|
}
|
|
fn done_restore(who: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Restored { who: who.clone(), amount });
|
|
}
|
|
fn done_transfer(source: &T::AccountId, dest: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Transfer {
|
|
from: source.clone(),
|
|
to: dest.clone(),
|
|
amount,
|
|
});
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::MutateHold<T::AccountId> for Pallet<T, I> {}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::InspectHold<T::AccountId> for Pallet<T, I> {
|
|
type Reason = T::HoldIdentifier;
|
|
|
|
fn total_balance_on_hold(who: &T::AccountId) -> T::Balance {
|
|
Self::account(who).reserved
|
|
}
|
|
fn reducible_total_balance_on_hold(who: &T::AccountId, force: Fortitude) -> Self::Balance {
|
|
// The total balance must never drop below the freeze requirements if we're not forcing:
|
|
let a = Self::account(who);
|
|
let unavailable = if force == Force {
|
|
Self::Balance::zero()
|
|
} else {
|
|
// The freeze lock applies to the total balance, so we can discount the free balance
|
|
// from the amount which the total reserved balance must provide to satisfy it.
|
|
a.frozen.saturating_sub(a.free)
|
|
};
|
|
a.reserved.saturating_sub(unavailable)
|
|
}
|
|
fn balance_on_hold(reason: &Self::Reason, who: &T::AccountId) -> T::Balance {
|
|
Holds::<T, I>::get(who)
|
|
.iter()
|
|
.find(|x| &x.id == reason)
|
|
.map_or_else(Zero::zero, |x| x.amount)
|
|
}
|
|
fn hold_available(reason: &Self::Reason, who: &T::AccountId) -> bool {
|
|
if frame_system::Pallet::<T>::providers(who) == 0 {
|
|
return false
|
|
}
|
|
let holds = Holds::<T, I>::get(who);
|
|
if holds.is_full() && !holds.iter().any(|x| &x.id == reason) {
|
|
return false
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::UnbalancedHold<T::AccountId> for Pallet<T, I> {
|
|
fn set_balance_on_hold(
|
|
reason: &Self::Reason,
|
|
who: &T::AccountId,
|
|
amount: Self::Balance,
|
|
) -> DispatchResult {
|
|
let mut new_account = Self::account(who);
|
|
let mut holds = Holds::<T, I>::get(who);
|
|
let mut increase = true;
|
|
let mut delta = amount;
|
|
|
|
if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) {
|
|
delta = item.amount.max(amount) - item.amount.min(amount);
|
|
increase = amount > item.amount;
|
|
item.amount = amount;
|
|
holds.retain(|x| !x.amount.is_zero());
|
|
} else {
|
|
if !amount.is_zero() {
|
|
holds
|
|
.try_push(IdAmount { id: *reason, amount })
|
|
.map_err(|_| Error::<T, I>::TooManyHolds)?;
|
|
}
|
|
}
|
|
|
|
new_account.reserved = if increase {
|
|
new_account.reserved.checked_add(&delta).ok_or(ArithmeticError::Overflow)?
|
|
} else {
|
|
new_account.reserved.checked_sub(&delta).ok_or(ArithmeticError::Underflow)?
|
|
};
|
|
|
|
let (result, maybe_dust) = Self::try_mutate_account(who, |a, _| -> DispatchResult {
|
|
*a = new_account;
|
|
Ok(())
|
|
})?;
|
|
debug_assert!(
|
|
maybe_dust.is_none(),
|
|
"Does not alter main balance; dust only happens when it is altered; qed"
|
|
);
|
|
Holds::<T, I>::insert(who, holds);
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::InspectFreeze<T::AccountId> for Pallet<T, I> {
|
|
type Id = T::FreezeIdentifier;
|
|
|
|
fn balance_frozen(id: &Self::Id, who: &T::AccountId) -> Self::Balance {
|
|
let locks = Freezes::<T, I>::get(who);
|
|
locks.into_iter().find(|l| &l.id == id).map_or(Zero::zero(), |l| l.amount)
|
|
}
|
|
|
|
fn can_freeze(id: &Self::Id, who: &T::AccountId) -> bool {
|
|
let l = Freezes::<T, I>::get(who);
|
|
!l.is_full() || l.iter().any(|x| &x.id == id)
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::MutateFreeze<T::AccountId> for Pallet<T, I> {
|
|
fn set_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
|
|
if amount.is_zero() {
|
|
return Self::thaw(id, who)
|
|
}
|
|
let mut locks = Freezes::<T, I>::get(who);
|
|
if let Some(i) = locks.iter_mut().find(|x| &x.id == id) {
|
|
i.amount = amount;
|
|
} else {
|
|
locks
|
|
.try_push(IdAmount { id: *id, amount })
|
|
.map_err(|_| Error::<T, I>::TooManyFreezes)?;
|
|
}
|
|
Self::update_freezes(who, locks.as_bounded_slice())
|
|
}
|
|
|
|
fn extend_freeze(id: &Self::Id, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
|
|
if amount.is_zero() {
|
|
return Ok(())
|
|
}
|
|
let mut locks = Freezes::<T, I>::get(who);
|
|
if let Some(i) = locks.iter_mut().find(|x| &x.id == id) {
|
|
i.amount = i.amount.max(amount);
|
|
} else {
|
|
locks
|
|
.try_push(IdAmount { id: *id, amount })
|
|
.map_err(|_| Error::<T, I>::TooManyFreezes)?;
|
|
}
|
|
Self::update_freezes(who, locks.as_bounded_slice())
|
|
}
|
|
|
|
fn thaw(id: &Self::Id, who: &T::AccountId) -> DispatchResult {
|
|
let mut locks = Freezes::<T, I>::get(who);
|
|
locks.retain(|l| &l.id != id);
|
|
Self::update_freezes(who, locks.as_bounded_slice())
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::Balanced<T::AccountId> for Pallet<T, I> {
|
|
type OnDropCredit = fungible::DecreaseIssuance<T::AccountId, Self>;
|
|
type OnDropDebt = fungible::IncreaseIssuance<T::AccountId, Self>;
|
|
|
|
fn done_deposit(who: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Deposit { who: who.clone(), amount });
|
|
}
|
|
fn done_withdraw(who: &T::AccountId, amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Withdraw { who: who.clone(), amount });
|
|
}
|
|
fn done_issue(amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Issued { amount });
|
|
}
|
|
fn done_rescind(amount: Self::Balance) {
|
|
Self::deposit_event(Event::<T, I>::Rescinded { amount });
|
|
}
|
|
}
|
|
|
|
impl<T: Config<I>, I: 'static> fungible::BalancedHold<T::AccountId> for Pallet<T, I> {}
|