diff --git a/substrate/frame/balances/src/impl_currency.rs b/substrate/frame/balances/src/impl_currency.rs index 1ac882ade70df..04f85eb21a8b9 100644 --- a/substrate/frame/balances/src/impl_currency.rs +++ b/substrate/frame/balances/src/impl_currency.rs @@ -25,7 +25,7 @@ use frame_support::{ ensure, pallet_prelude::DispatchResult, traits::{ - tokens::{fungible, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort}, + tokens::{fungible, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort, Preservation::{self, Preserve, Protect}}, Currency, DefensiveSaturating, ExistenceRequirement, ExistenceRequirement::AllowDeath, Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, @@ -487,6 +487,27 @@ where ) .unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero())) } + + fn transferrable_balance( + who: &T::AccountId, + preservation: Preservation, + ) -> Self::Balance { + let a = Self::account(who); + let mut untouchable = a.frozen; + // 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::::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::::can_dec_provider(who) + { + // ..then the ED needed.. + untouchable = untouchable.max(T::ExistentialDeposit::get()); + } + a.free.saturating_sub(untouchable) + } } impl, I: 'static> ReservableCurrency for Pallet diff --git a/substrate/frame/support/src/traits/tokens/currency.rs b/substrate/frame/support/src/traits/tokens/currency.rs index 0030e1261dac1..8958b679fcb5f 100644 --- a/substrate/frame/support/src/traits/tokens/currency.rs +++ b/substrate/frame/support/src/traits/tokens/currency.rs @@ -19,7 +19,7 @@ use super::{ imbalance::{Imbalance, SignedImbalance}, - misc::{Balance, ExistenceRequirement, WithdrawReasons}, + misc::{Balance, ExistenceRequirement, WithdrawReasons}, Preservation, }; use crate::{dispatch::DispatchResult, traits::Get}; use sp_runtime::{traits::MaybeSerializeDeserialize, DispatchError}; @@ -207,6 +207,17 @@ pub trait Currency { who: &AccountId, balance: Self::Balance, ) -> SignedImbalance; + + /// Get the maximum amount that `who` can withdraw/transfer successfully based on whether the + /// account should be kept alive (`preservation`) or whether we are willing to force the + /// reduction and potentially go below user-level restrictions on the minimum amount of the + /// account. + /// + /// Always less than or equal to `balance()`. + fn transferrable_balance( + who: &AccountId, + preservation: Preservation, + ) -> Self::Balance; } /// A non-const `Get` implementation parameterised by a `Currency` impl which provides the result @@ -313,4 +324,7 @@ impl Currency for () { ) -> SignedImbalance { SignedImbalance::Positive(()) } + fn transferrable_balance(_: &AccountId, _: Preservation) -> u32 { + 0 + } } diff --git a/substrate/frame/support/src/traits/tokens/currency/reservable.rs b/substrate/frame/support/src/traits/tokens/currency/reservable.rs index ff8b0c6eea838..0000966572a71 100644 --- a/substrate/frame/support/src/traits/tokens/currency/reservable.rs +++ b/substrate/frame/support/src/traits/tokens/currency/reservable.rs @@ -23,7 +23,7 @@ use sp_core::Get; use super::{super::misc::BalanceStatus, Currency}; use crate::{ dispatch::DispatchResult, - traits::{ExistenceRequirement, SignedImbalance, WithdrawReasons}, + traits::{ExistenceRequirement, SignedImbalance, WithdrawReasons, tokens::Preservation}, }; use sp_runtime::DispatchError; @@ -338,6 +338,12 @@ impl< ) -> SignedImbalance { NamedReservable::make_free_balance_be(who, balance) } + fn transferrable_balance( + who: &AccountId, + preservation: Preservation, + ) -> Self::Balance { + NamedReservable::transferrable_balance(who, preservation) + } } impl< NamedReservable: NamedReservableCurrency,