Newer
Older
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> {
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
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(
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
|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(Error::<T, I>::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()) }
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
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(Error::<T, I>::Overflow)?;
Ok(PositiveImbalance::new(value))
})
/// 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(Error::<T, I>::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()) }
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
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> {
if value.is_zero() { return Ok(Zero::zero()) }
return match status {
Status::Free => Ok(Self::unreserve(slashed, value)),
Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))),
};
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
let ((actual, _maybe_one_dust), _maybe_other_dust) = Self::try_mutate_account_with_dust(
beneficiary,
|to_account, is_new| -> Result<(Self::Balance, DustCleaner<T, I>), DispatchError> {
ensure!(!is_new, Error::<T, I>::DeadAccount);
Self::try_mutate_account_with_dust(
slashed,
|from_account, _| -> Result<Self::Balance, DispatchError> {
let actual = cmp::min(from_account.reserved, value);
match status {
Status::Free => to_account.free = to_account.free
.checked_add(&actual)
.ok_or(Error::<T, I>::Overflow)?,
Status::Reserved => to_account.reserved = to_account.reserved
.checked_add(&actual)
.ok_or(Error::<T, I>::Overflow)?,
}
from_account.reserved -= actual;
Ok(actual)
}
)
}
)?;
Self::deposit_event(Event::ReserveRepatriated(slashed.clone(), beneficiary.clone(), actual, status));
Ok(value - actual)
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[..]);