Newer
Older
}
fn minimum_balance() -> Self::Balance {
T::ExistentialDeposit::get()
}
fn balance(who: &T::AccountId) -> Self::Balance {
Self::account(who).total()
}
fn reducible_balance(who: &T::AccountId, keep_alive: bool) -> Self::Balance {
let a = Self::account(who);
// Liquid balance is what is neither reserved nor locked/frozen.
let liquid = a.free.saturating_sub(a.fee_frozen.max(a.misc_frozen));
if frame_system::Pallet::<T>::can_dec_provider(who) && !keep_alive {
liquid
} else {
// `must_remain_to_exist` is the part of liquid balance which must remain to keep total over
// ED.
let must_remain_to_exist = T::ExistentialDeposit::get().saturating_sub(a.total() - liquid);
liquid.saturating_sub(must_remain_to_exist)
}
}
fn can_deposit(who: &T::AccountId, amount: Self::Balance) -> DepositConsequence {
Self::deposit_consequence(who, amount, &Self::account(who))
}
fn can_withdraw(who: &T::AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance> {
Self::withdraw_consequence(who, amount, &Self::account(who))
}
}
impl<T: Config<I>, I: 'static> fungible::Mutate<T::AccountId> for Pallet<T, I> {
fn mint_into(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() { return Ok(()) }
Self::try_mutate_account(who, |account, _is_new| -> DispatchResult {
Self::deposit_consequence(who, amount, &account).into_result()?;
account.free += amount;
Ok(())
})?;
TotalIssuance::<T, I>::mutate(|t| *t += amount);
Ok(())
}
fn burn_from(who: &T::AccountId, amount: Self::Balance) -> Result<Self::Balance, DispatchError> {
if amount.is_zero() { return Ok(Self::Balance::zero()); }
let actual = Self::try_mutate_account(who, |account, _is_new| -> Result<T::Balance, DispatchError> {
let extra = Self::withdraw_consequence(who, amount, &account).into_result()?;
let actual = amount + extra;
account.free -= actual;
Ok(actual)
})?;
TotalIssuance::<T, I>::mutate(|t| *t -= actual);
Ok(actual)
}
}
impl<T: Config<I>, I: 'static> fungible::Transfer<T::AccountId> for Pallet<T, I> {
fn transfer(
source: &T::AccountId,
dest: &T::AccountId,
amount: T::Balance,
) -> Result<T::Balance, DispatchError> {
let er = if keep_alive { KeepAlive } else { AllowDeath };
<Self as Currency::<T::AccountId>>::transfer(source, dest, amount, er)
.map(|_| amount)
}
}
impl<T: Config<I>, I: 'static> fungible::Unbalanced<T::AccountId> for Pallet<T, I> {
fn set_balance(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
Self::mutate_account(who, |account| account.free = amount)?;
Ok(())
}
fn set_total_issuance(amount: Self::Balance) {
TotalIssuance::<T, I>::mutate(|t| *t = amount);
}
}
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
impl<T: Config<I>, I: 'static> fungible::InspectHold<T::AccountId> for Pallet<T, I> {
fn balance_on_hold(who: &T::AccountId) -> T::Balance {
Self::account(who).reserved
}
fn can_hold(who: &T::AccountId, amount: T::Balance) -> bool {
let a = Self::account(who);
let min_balance = T::ExistentialDeposit::get().max(a.frozen(Reasons::All));
if a.reserved.checked_add(&amount).is_none() { return false }
// We require it to be min_balance + amount to ensure that the full reserved funds may be
// slashed without compromising locked funds or destroying the account.
let required_free = match min_balance.checked_add(&amount) {
Some(x) => x,
None => return false,
};
a.free >= required_free
}
}
impl<T: Config<I>, I: 'static> fungible::MutateHold<T::AccountId> for Pallet<T, I> {
fn hold(who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
if amount.is_zero() { return Ok(()) }
ensure!(Self::can_reserve(who, amount), Error::<T, I>::InsufficientBalance);
Self::mutate_account(who, |a| {
a.free -= amount;
a.reserved += amount;
})?;
Ok(())
}
fn release(who: &T::AccountId, amount: Self::Balance, best_effort: bool)
-> Result<T::Balance, DispatchError>
{
if amount.is_zero() { return Ok(amount) }
// Done on a best-effort basis.
Self::try_mutate_account(who, |a, _| {
let new_free = a.free.saturating_add(amount.min(a.reserved));
let actual = new_free - a.free;
ensure!(best_effort || actual == amount, Error::<T, I>::InsufficientBalance);
// ^^^ Guaranteed to be <= amount and <= a.reserved
a.free = new_free;
a.reserved = a.reserved.saturating_sub(actual.clone());
Ok(actual)
})
}
fn transfer_held(
source: &T::AccountId,
dest: &T::AccountId,
amount: Self::Balance,
best_effort: bool,
on_hold: bool,
) -> Result<Self::Balance, DispatchError> {
let status = if on_hold { Status::Reserved } else { Status::Free };
Self::do_transfer_reserved(source, dest, amount, best_effort, status)
}
}
// wrapping these imbalances in a private module is necessary to ensure absolute privacy
// of the inner member.
mod imbalances {
use super::{
result, Imbalance, Config, Zero, Saturating,
TryDrop, RuntimeDebug,
use frame_support::traits::SameOrOther;
/// Opaque, move-only struct with private fields that serves as a token denoting that
/// funds have been created without any equal and opposite accounting.
#[must_use]
#[derive(RuntimeDebug, PartialEq, Eq)]
pub struct PositiveImbalance<T: Config<I>, I: 'static = ()>(T::Balance);
impl<T: Config<I>, I: 'static> PositiveImbalance<T, I> {
/// Create a new positive imbalance from a balance.
pub fn new(amount: T::Balance) -> Self {
PositiveImbalance(amount)
}
}
/// Opaque, move-only struct with private fields that serves as a token denoting that
/// funds have been destroyed without any equal and opposite accounting.
#[must_use]
#[derive(RuntimeDebug, PartialEq, Eq)]
pub struct NegativeImbalance<T: Config<I>, I: 'static = ()>(T::Balance);
impl<T: Config<I>, I: 'static> NegativeImbalance<T, I> {
/// Create a new negative imbalance from a balance.
pub fn new(amount: T::Balance) -> Self {
NegativeImbalance(amount)
impl<T: Config<I>, I: 'static> TryDrop for PositiveImbalance<T, I> {
fn try_drop(self) -> result::Result<(), Self> {
self.drop_zero()
}
}
impl<T: Config<I>, I: 'static> Default for PositiveImbalance<T, I> {
fn default() -> Self {
Self::zero()
}
}
impl<T: Config<I>, I: 'static> Imbalance<T::Balance> for PositiveImbalance<T, I> {
type Opposite = NegativeImbalance<T, I>;
fn zero() -> Self {
Self(Zero::zero())
fn drop_zero(self) -> result::Result<(), Self> {
if self.0.is_zero() {
Ok(())
} else {
Err(self)
}
}
fn split(self, amount: T::Balance) -> (Self, Self) {
let first = self.0.min(amount);
let second = self.0 - first;
mem::forget(self);
(Self(first), Self(second))
}
fn merge(mut self, other: Self) -> Self {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
self
}
fn subsume(&mut self, other: Self) {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
}
fn offset(self, other: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
let (a, b) = (self.0, other.0);
mem::forget((self, other));
if a > b {
SameOrOther::Same(Self(a - b))
} else if b > a {
SameOrOther::Other(NegativeImbalance::new(b - a))
SameOrOther::None
}
}
fn peek(&self) -> T::Balance {
self.0.clone()
impl<T: Config<I>, I: 'static> TryDrop for NegativeImbalance<T, I> {
fn try_drop(self) -> result::Result<(), Self> {
self.drop_zero()
}
}
impl<T: Config<I>, I: 'static> Default for NegativeImbalance<T, I> {
fn default() -> Self {
Self::zero()
}
}
impl<T: Config<I>, I: 'static> Imbalance<T::Balance> for NegativeImbalance<T, I> {
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
type Opposite = PositiveImbalance<T, I>;
fn zero() -> Self {
Self(Zero::zero())
}
fn drop_zero(self) -> result::Result<(), Self> {
if self.0.is_zero() {
Ok(())
} else {
Err(self)
}
}
fn split(self, amount: T::Balance) -> (Self, Self) {
let first = self.0.min(amount);
let second = self.0 - first;
mem::forget(self);
(Self(first), Self(second))
}
fn merge(mut self, other: Self) -> Self {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
self
}
fn subsume(&mut self, other: Self) {
self.0 = self.0.saturating_add(other.0);
mem::forget(other);
}
fn offset(self, other: Self::Opposite) -> SameOrOther<Self, Self::Opposite> {
let (a, b) = (self.0, other.0);
mem::forget((self, other));
if a > b {
SameOrOther::Same(Self(a - b))
} else if b > a {
SameOrOther::Other(PositiveImbalance::new(b - a))
SameOrOther::None
}
}
fn peek(&self) -> T::Balance {
self.0.clone()
}
impl<T: Config<I>, I: 'static> Drop for PositiveImbalance<T, I> {
/// Basic drop handler will just square up the total issuance.
fn drop(&mut self) {
<super::TotalIssuance<T, I>>::mutate(
|v| *v = v.saturating_add(self.0)
);
impl<T: Config<I>, I: 'static> Drop for NegativeImbalance<T, I> {
/// Basic drop handler will just square up the total issuance.
fn drop(&mut self) {
<super::TotalIssuance<T, I>>::mutate(
|v| *v = v.saturating_sub(self.0)
);
}
impl<T: Config<I>, I: 'static> Currency<T::AccountId> for Pallet<T, I> where
T::Balance: MaybeSerializeDeserialize + Debug
{
type Balance = T::Balance;
type PositiveImbalance = PositiveImbalance<T, I>;
type NegativeImbalance = NegativeImbalance<T, I>;
fn total_balance(who: &T::AccountId) -> Self::Balance {
// Check if `value` amount of free balance can be slashed from `who`.
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
if value.is_zero() { return true }
Self::free_balance(who) >= value
}
fn total_issuance() -> Self::Balance {
}
fn minimum_balance() -> Self::Balance {
// Burn funds from the total issuance, returning a positive imbalance for the amount burned.
// Is a no-op if amount to be burned is zero.
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
if amount.is_zero() { return PositiveImbalance::zero() }
<TotalIssuance<T, I>>::mutate(|issued| {
*issued = issued.checked_sub(&amount).unwrap_or_else(|| {
amount = *issued;
Zero::zero()
PositiveImbalance::new(amount)
}
// Create new funds into the total issuance, returning a negative imbalance
// for the amount issued.
// Is a no-op if amount to be issued it zero.
fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
if amount.is_zero() { return NegativeImbalance::zero() }
<TotalIssuance<T, I>>::mutate(|issued|
*issued = issued.checked_add(&amount).unwrap_or_else(|| {
amount = Self::Balance::max_value() - *issued;
Self::Balance::max_value()
})
);
NegativeImbalance::new(amount)
}
fn free_balance(who: &T::AccountId) -> Self::Balance {
Self::account(who).free
}
// Ensure that an account can withdraw from their free balance given any existing withdrawal
// restrictions like locks and vesting balance.
// Is a no-op if amount to be withdrawn is zero.
//
// # <weight>
// Despite iterating over a list of locks, they are limited by the number of
// lock IDs, which means the number of runtime pallets that intend to use and create locks.
// # </weight>
fn ensure_can_withdraw(
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
new_balance: T::Balance,
if amount.is_zero() { return Ok(()) }
let min_balance = Self::account(who).frozen(reasons.into());
ensure!(new_balance >= min_balance, Error::<T, I>::LiquidityRestrictions);
Ok(())
// Transfer some free balance from `transactor` to `dest`, respecting existence requirements.
// Is a no-op if value to be transferred is zero or the `transactor` is the same as `dest`.
fn transfer(
transactor: &T::AccountId,
dest: &T::AccountId,
value: Self::Balance,
existence_requirement: ExistenceRequirement,
if value.is_zero() || transactor == dest { return Ok(()) }
Self::try_mutate_account_with_dust(
dest,
|to_account, _| -> Result<DustCleaner<T, I>, DispatchError> {
Self::try_mutate_account_with_dust(
|from_account, _| -> DispatchResult {
from_account.free = from_account.free.checked_sub(&value)
.ok_or(Error::<T, I>::InsufficientBalance)?;
// NOTE: total stake being stored in the same type means that this could never overflow
// but better to be safe than sorry.
to_account.free = to_account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
let ed = T::ExistentialDeposit::get();
ensure!(to_account.total() >= ed, Error::<T, I>::ExistentialDeposit);
Self::ensure_can_withdraw(
transactor,
value,
WithdrawReasons::TRANSFER,
from_account.free,
).map_err(|_| Error::<T, I>::LiquidityRestrictions)?;
// TODO: This is over-conservative. There may now be other providers, and this pallet
// may not even be a provider.
let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
let allow_death = allow_death && !system::Pallet::<T>::is_provider_required(transactor);
ensure!(allow_death || from_account.total() >= ed, Error::<T, I>::KeepAlive);
Ok(())
}
).map(|(_, maybe_dust_cleaner)| maybe_dust_cleaner)
}
)?;
Self::deposit_event(Event::Transfer(transactor.clone(), dest.clone(), value));
Ok(())
}
/// Slash a target account `who`, returning the negative imbalance created and any left over
/// amount that could not be slashed.
///
/// Is a no-op if `value` to be slashed is zero or the account does not exist.
///
/// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn
/// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having
/// to draw from reserved funds, however we err on the side of punishment if things are inconsistent
/// or `can_slash` wasn't used appropriately.
fn slash(
who: &T::AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
if Self::total_balance(&who).is_zero() { return (NegativeImbalance::zero(), value) }
for attempt in 0..2 {
match Self::try_mutate_account(who,
|account, _is_new| -> Result<(Self::NegativeImbalance, Self::Balance), StoredMapError> {
// Best value is the most amount we can slash following liveness rules.
let best_value = match attempt {
// First attempt we try to slash the full amount, and see if liveness issues happen.
0 => value,
// If acting as a critical provider (i.e. first attempt failed), then slash
// as much as possible while leaving at least at ED.
_ => value.min((account.free + account.reserved).saturating_sub(T::ExistentialDeposit::get())),
};
let free_slash = cmp::min(account.free, best_value);
account.free -= free_slash; // Safe because of above check
let remaining_slash = best_value - free_slash; // Safe because of above check
if !remaining_slash.is_zero() {
// If we have remaining slash, take it from reserved balance.
let reserved_slash = cmp::min(account.reserved, remaining_slash);
account.reserved -= reserved_slash; // Safe because of above check
Ok((
NegativeImbalance::new(free_slash + reserved_slash),
value - free_slash - reserved_slash, // Safe because value is gt or eq total slashed
))
} else {
// Else we are done!
Ok((
NegativeImbalance::new(free_slash),
value - free_slash, // Safe because value is gt or eq to total slashed
))
}
}
) {
Ok(r) => return r,
Err(_) => (),
}
// Should never get here. But we'll be defensive anyway.
(Self::NegativeImbalance::zero(), value)
/// Deposit some `value` into the free balance of an existing target account `who`.
///
/// Is a no-op if the `value` to be deposited is zero.
fn deposit_into_existing(
who: &T::AccountId,
value: Self::Balance
) -> Result<Self::PositiveImbalance, DispatchError> {
if value.is_zero() { return Ok(PositiveImbalance::zero()) }
Self::try_mutate_account(who, |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
ensure!(!is_new, Error::<T, I>::DeadAccount);
account.free = account.free.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
/// Deposit some `value` into the free balance of `who`, possibly creating a new account.
///
/// This function is a no-op if:
/// - the `value` to be deposited is zero; or
/// - the `value` to be deposited is less than the required ED and the account does not yet exist; or
/// - the deposit would necessitate the account to exist and there are no provider references; or
/// - `value` is so large it would cause the balance of `who` to overflow.
fn deposit_creating(
who: &T::AccountId,
value: Self::Balance,
) -> Self::PositiveImbalance {
if value.is_zero() { return Self::PositiveImbalance::zero() }
let r = Self::try_mutate_account(who, |account, is_new| -> Result<Self::PositiveImbalance, DispatchError> {
ensure!(value >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
// defensive only: overflow should never happen, however in case it does, then this
// operation is a no-op.
account.free = match account.free.checked_add(&value) {
Some(x) => x,
None => return Ok(Self::PositiveImbalance::zero()),
};
}).unwrap_or_else(|_| Self::PositiveImbalance::zero());
r
/// Withdraw some free balance from an account, respecting existence requirements.
///
/// Is a no-op if value to be withdrawn is zero.
fn withdraw(
who: &T::AccountId,
value: Self::Balance,
reasons: WithdrawReasons,
liveness: ExistenceRequirement,
) -> result::Result<Self::NegativeImbalance, DispatchError> {
if value.is_zero() { return Ok(NegativeImbalance::zero()); }
Self::try_mutate_account(who, |account, _|
-> Result<Self::NegativeImbalance, DispatchError>
{
let new_free_account = account.free.checked_sub(&value)
.ok_or(Error::<T, I>::InsufficientBalance)?;
// bail if we need to keep the account alive and this would kill it.
let ed = T::ExistentialDeposit::get();
let would_be_dead = new_free_account + account.reserved < ed;
let would_kill = would_be_dead && account.free + account.reserved >= ed;
ensure!(liveness == AllowDeath || !would_kill, Error::<T, I>::KeepAlive);
Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
account.free = new_free_account;
Ok(NegativeImbalance::new(value))
})
/// Force the new free balance of a target account `who` to some new value `balance`.
fn make_free_balance_be(who: &T::AccountId, value: Self::Balance)
-> SignedImbalance<Self::Balance, Self::PositiveImbalance>
{
Self::try_mutate_account(who, |account, is_new|
-> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, DispatchError>
let total = value.saturating_add(account.reserved);
// If we're attempting to set an existing account to less than ED, then
// bypass the entire operation. It's a no-op if you follow it through, but
// since this is an instance where we might account for a negative imbalance
// (in the dust cleaner of set_account) before we account for its actual
// equal and opposite cause (returned as an Imbalance), then in the
// instance that there's no other accounts on the system at all, we might
// underflow the issuance and our arithmetic will be off.
ensure!(total >= ed || !is_new, Error::<T, I>::ExistentialDeposit);
let imbalance = if account.free <= value {
SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
} else {
SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
};
account.free = value;
Ok(imbalance)
}).unwrap_or_else(|_| SignedImbalance::Positive(Self::PositiveImbalance::zero()))
impl<T: Config<I>, I: 'static> ReservableCurrency<T::AccountId> for Pallet<T, I> where
T::Balance: MaybeSerializeDeserialize + Debug
/// Check if `who` can reserve `value` from their free balance.
///
/// Always `true` if value to be reserved is zero.
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
if value.is_zero() { return true }
.checked_sub(&value)
.map_or(false, |new_balance|
Self::ensure_can_withdraw(who, value, WithdrawReasons::RESERVE, new_balance).is_ok()
)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
/// Move `value` from the free balance from `who` to their reserved balance.
///
/// Is a no-op if value to be reserved is zero.
fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
if value.is_zero() { return Ok(()) }
Self::try_mutate_account(who, |account, _| -> DispatchResult {
account.free = account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
account.reserved = account.reserved.checked_add(&value).ok_or(ArithmeticError::Overflow)?;
Self::ensure_can_withdraw(&who, value.clone(), WithdrawReasons::RESERVE, account.free)
})?;
Self::deposit_event(Event::Reserved(who.clone(), value));
/// Unreserve some funds, returning any amount that was unable to be unreserved.
///
/// Is a no-op if the value to be unreserved is zero or the account does not exist.
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if value.is_zero() { return Zero::zero() }
if Self::total_balance(&who).is_zero() { return value }
let actual = match Self::mutate_account(who, |account| {
let actual = cmp::min(account.reserved, value);
account.reserved -= actual;
// defensive only: this can never fail since total issuance which is at least free+reserved
account.free = account.free.saturating_add(actual);
}) {
Ok(x) => x,
Err(_) => {
// This should never happen since we don't alter the total amount in the account.
// If it ever does, then we should fail gracefully though, indicating that nothing
// could be done.
return value
}
};
Self::deposit_event(Event::Unreserved(who.clone(), actual.clone()));
value - actual
/// Slash from reserved balance, returning the negative imbalance created,
/// and any amount that was unable to be slashed.
///
/// Is a no-op if the value to be slashed is zero or the account does not exist.
fn slash_reserved(
who: &T::AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
if Self::total_balance(&who).is_zero() { return (NegativeImbalance::zero(), value) }
// NOTE: `mutate_account` may fail if it attempts to reduce the balance to the point that an
// account is attempted to be illegally destroyed.
for attempt in 0..2 {
match Self::mutate_account(who, |account| {
let best_value = match attempt {
0 => value,
// If acting as a critical provider (i.e. first attempt failed), then ensure
// slash leaves at least the ED.
_ => value.min((account.free + account.reserved).saturating_sub(T::ExistentialDeposit::get())),
};
let actual = cmp::min(account.reserved, best_value);
account.reserved -= actual;
// underflow should never happen, but it if does, there's nothing to be done here.
(NegativeImbalance::new(actual), value - actual)
}) {
Ok(r) => return r,
Err(_) => (),
}
}
// Should never get here as we ensure that ED is left in the second attempt.
// In case we do, though, then we fail gracefully.
(Self::NegativeImbalance::zero(), value)
/// Move the reserved balance of one account into the balance of another, according to `status`.
/// Is a no-op if:
/// - the value to be moved is zero; or
/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
fn repatriate_reserved(
slashed: &T::AccountId,
beneficiary: &T::AccountId,
status: Status,
) -> Result<Self::Balance, DispatchError> {
let actual = Self::do_transfer_reserved(slashed, beneficiary, value, true, status)?;
Ok(value.saturating_sub(actual))
impl<T: Config<I>, I: 'static> NamedReservableCurrency<T::AccountId> for Pallet<T, I> where
T::Balance: MaybeSerializeDeserialize + Debug
{
type ReserveIdentifier = T::ReserveIdentifier;
fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &T::AccountId) -> Self::Balance {
let reserves = Self::reserves(who);
reserves
.binary_search_by_key(id, |data| data.id)
.map(|index| reserves[index].amount)
.unwrap_or_default()
}
/// Move `value` from the free balance from `who` to a named reserve balance.
///
/// Is a no-op if value to be reserved is zero.
fn reserve_named(id: &Self::ReserveIdentifier, who: &T::AccountId, value: Self::Balance) -> DispatchResult {
if value.is_zero() { return Ok(()) }
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
Reserves::<T, I>::try_mutate(who, |reserves| -> DispatchResult {
match reserves.binary_search_by_key(id, |data| data.id) {
Ok(index) => {
// this add can't overflow but just to be defensive.
reserves[index].amount = reserves[index].amount.saturating_add(value);
},
Err(index) => {
reserves.try_insert(index, ReserveData {
id: id.clone(),
amount: value
}).map_err(|_| Error::<T, I>::TooManyReserves)?;
},
};
<Self as ReservableCurrency<_>>::reserve(who, value)?;
Ok(())
})
}
/// Unreserve some funds, returning any amount that was unable to be unreserved.
///
/// Is a no-op if the value to be unreserved is zero.
fn unreserve_named(id: &Self::ReserveIdentifier, who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if value.is_zero() { return Zero::zero() }
Reserves::<T, I>::mutate_exists(who, |maybe_reserves| -> Self::Balance {
if let Some(reserves) = maybe_reserves.as_mut() {
match reserves.binary_search_by_key(id, |data| data.id) {
Ok(index) => {
let to_change = cmp::min(reserves[index].amount, value);
let remain = <Self as ReservableCurrency<_>>::unreserve(who, to_change);
// remain should always be zero but just to be defensive here
let actual = to_change.saturating_sub(remain);
// `actual <= to_change` and `to_change <= amount`; qed;
reserves[index].amount -= actual;
if reserves[index].amount.is_zero() {
if reserves.len() == 1 {
// no more named reserves
*maybe_reserves = None;
} else {
// remove this named reserve
reserves.remove(index);
}
}
value - actual
},
Err(_) => {
value
},
}
} else {
value
}
})
}
/// Slash from reserved balance, returning the negative imbalance created,
/// and any amount that was unable to be slashed.
///
/// Is a no-op if the value to be slashed is zero.
fn slash_reserved_named(
id: &Self::ReserveIdentifier,
who: &T::AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
Reserves::<T, I>::mutate(who, |reserves| -> (Self::NegativeImbalance, Self::Balance) {
match reserves.binary_search_by_key(id, |data| data.id) {
Ok(index) => {
let to_change = cmp::min(reserves[index].amount, value);
let (imb, remain) = <Self as ReservableCurrency<_>>::slash_reserved(who, to_change);
// remain should always be zero but just to be defensive here
let actual = to_change.saturating_sub(remain);
// `actual <= to_change` and `to_change <= amount`; qed;
reserves[index].amount -= actual;
(imb, value - actual)
},
Err(_) => {
(NegativeImbalance::zero(), value)
},
}
})
}
/// Move the reserved balance of one account into the balance of another, according to `status`.
/// If `status` is `Reserved`, the balance will be reserved with given `id`.
///
/// Is a no-op if:
/// - the value to be moved is zero; or
/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
fn repatriate_reserved_named(
id: &Self::ReserveIdentifier,
slashed: &T::AccountId,
beneficiary: &T::AccountId,
value: Self::Balance,
status: Status,
) -> Result<Self::Balance, DispatchError> {
if value.is_zero() { return Ok(Zero::zero()) }
if slashed == beneficiary {
return match status {
Status::Free => Ok(Self::unreserve_named(id, slashed, value)),
Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance_named(id, slashed))),
};
}
Reserves::<T, I>::try_mutate(slashed, |reserves| -> Result<Self::Balance, DispatchError> {
match reserves.binary_search_by_key(id, |data| data.id) {
Ok(index) => {
let to_change = cmp::min(reserves[index].amount, value);
let actual = if status == Status::Reserved {
// make it the reserved under same identifier
Reserves::<T, I>::try_mutate(beneficiary, |reserves| -> Result<T::Balance, DispatchError> {
match reserves.binary_search_by_key(id, |data| data.id) {
Ok(index) => {
let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(slashed, beneficiary, to_change, status)?;
// remain should always be zero but just to be defensive here
let actual = to_change.saturating_sub(remain);
// this add can't overflow but just to be defensive.
reserves[index].amount = reserves[index].amount.saturating_add(actual);
Ok(actual)
},
Err(index) => {
let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(slashed, beneficiary, to_change, status)?;
// remain should always be zero but just to be defensive here
let actual = to_change.saturating_sub(remain);
reserves.try_insert(index, ReserveData {
id: id.clone(),
amount: actual
}).map_err(|_| Error::<T, I>::TooManyReserves)?;
Ok(actual)
},
}
})?
} else {
let remain = <Self as ReservableCurrency<_>>::repatriate_reserved(slashed, beneficiary, to_change, status)?;
// remain should always be zero but just to be defensive here
to_change.saturating_sub(remain)
};
// `actual <= to_change` and `to_change <= amount`; qed;
reserves[index].amount -= actual;
Ok(value - actual)
},
Err(_) => {
Ok(value)
},
}
})
}
}
impl<T: Config<I>, I: 'static> LockableCurrency<T::AccountId> for Pallet<T, I>
T::Balance: MaybeSerializeDeserialize + Debug
{
type Moment = T::BlockNumber;
type MaxLocks = T::MaxLocks;
// Set a lock on the balance of `who`.
// Is a no-op if lock amount is zero or `reasons` `is_none()`.
fn set_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
) {
if amount.is_zero() || reasons.is_empty() { return }
let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
let mut locks = Self::locks(who).into_iter()
.filter_map(|l| if l.id == id { new_lock.take() } else { Some(l) })
.collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
// Extend a lock on the balance of `who`.
// Is a no-op if lock amount is zero or `reasons` `is_none()`.
fn extend_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
reasons: WithdrawReasons,
) {
if amount.is_zero() || reasons.is_empty() { return }
let mut new_lock = Some(BalanceLock { id, amount, reasons: reasons.into() });
let mut locks = Self::locks(who).into_iter().filter_map(|l|
if l.id == id {
new_lock.take().map(|nl| {
BalanceLock {
id: l.id,
amount: l.amount.max(nl.amount),
reasons: l.reasons | nl.reasons,
}
})
} else {
}).collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
}
fn remove_lock(
id: LockIdentifier,
who: &T::AccountId,
) {
let mut locks = Self::locks(who);
locks.retain(|l| l.id != id);
Self::update_locks(who, &locks[..]);