Newer
Older
Err(Error::<T, I>::KeepAlive)?
Self::set_free_balance(transactor, new_from_balance);
if !<FreeBalance<T, I>>::exists(dest) {
Self::new_account(dest, new_to_balance);
}
// Emit transfer event.
Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee));
// Take action on the set_free_balance call.
// This will emit events that _resulted_ from the transfer.
Self::set_free_balance(dest, new_to_balance);
T::TransferPayment::on_unbalanced(NegativeImbalance::new(fee));
}
Ok(())
}
// 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()); }
let old_balance = Self::free_balance(who);
if let Some(new_balance) = old_balance.checked_sub(&value) {
// if we need to keep the account alive...
if liveness == ExistenceRequirement::KeepAlive
// ...and it would be dead afterwards...
&& new_balance < T::ExistentialDeposit::get()
// ...yet is was alive before
&& old_balance >= T::ExistentialDeposit::get()
{
Err(Error::<T, I>::KeepAlive)?
Self::ensure_can_withdraw(who, value, reasons, new_balance)?;
Self::set_free_balance(who, new_balance);
Ok(NegativeImbalance::new(value))
Err(Error::<T, I>::InsufficientBalance)?
// Slash an account, 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.
fn slash(
who: &T::AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
let free_balance = Self::free_balance(who);
let free_slash = cmp::min(free_balance, value);
Self::set_free_balance(who, free_balance - free_slash);
let remaining_slash = value - free_slash;
// 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.
if !remaining_slash.is_zero() {
let reserved_balance = Self::reserved_balance(who);
let reserved_slash = cmp::min(reserved_balance, remaining_slash);
Self::set_reserved_balance(who, reserved_balance - reserved_slash);
(NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash)
(NegativeImbalance::new(value), Zero::zero())
// Deposit some `value` into the free balance of an existing account.
// Is a no-op if the value to be deposited is zero.
fn deposit_into_existing(
who: &T::AccountId,
value: Self::Balance
) -> result::Result<Self::PositiveImbalance, DispatchError> {
if value.is_zero() { return Ok(PositiveImbalance::zero()) }
if Self::total_balance(who).is_zero() {
Err(Error::<T, I>::DeadAccount)?
}
Self::set_free_balance(who, Self::free_balance(who) + value);
Ok(PositiveImbalance::new(value))
// Deposit some `value` into the free balance of `who`, possibly creating a new account.
// Is a no-op if the value to be deposited is zero.
fn deposit_creating(
who: &T::AccountId,
value: Self::Balance,
) -> Self::PositiveImbalance {
if value.is_zero() { return Self::PositiveImbalance::zero() }
let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value);
if let SignedImbalance::Positive(p) = imbalance {
p
} else {
// Impossible, but be defensive.
Self::PositiveImbalance::zero()
}
}
fn make_free_balance_be(who: &T::AccountId, balance: Self::Balance) -> (
SignedImbalance<Self::Balance, Self::PositiveImbalance>,
UpdateBalanceOutcome
) {
let original = Self::free_balance(who);
if balance < T::ExistentialDeposit::get() && original.is_zero() {
// 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_free_balance) 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.
return (
SignedImbalance::Positive(Self::PositiveImbalance::zero()),
UpdateBalanceOutcome::AccountKilled,
)
}
let imbalance = if original <= balance {
SignedImbalance::Positive(PositiveImbalance::new(balance - original))
SignedImbalance::Negative(NegativeImbalance::new(original - balance))
};
// If the balance is too low, then the account is reaped.
// NOTE: There are two balances for every account: `reserved_balance` and
// `free_balance`. This contract subsystem only cares about the latter: whenever
// the term "balance" is used *here* it should be assumed to mean "free balance"
// in the rest of the module.
// Free balance can never be less than ED. If that happens, it gets reduced to zero
// and the account information relevant to this subsystem is deleted (i.e. the
// account is reaped).
let outcome = if balance < T::ExistentialDeposit::get() {
Self::set_free_balance(who, balance);
UpdateBalanceOutcome::AccountKilled
} else {
if !<FreeBalance<T, I>>::exists(who) {
Self::new_account(&who, balance);
}
Self::set_free_balance(who, balance);
UpdateBalanceOutcome::Updated
};
(imbalance, outcome)
}
impl<T: Trait<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I>
where
T::Balance: MaybeSerializeDeserialize + Debug
// Check if `who` can reserve `value` from their free balance.
// Is a no-op if value to be reserved is zero.
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
if value.is_zero() { return true }
Self::free_balance(who)
.checked_sub(&value)
.map_or(false, |new_balance|
Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), new_balance).is_ok()
)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
<ReservedBalance<T, I>>::get(who)
}
// 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) -> result::Result<(), DispatchError> {
if value.is_zero() { return Ok(()) }
let b = Self::free_balance(who);
if b < value {
Err(Error::<T, I>::InsufficientBalance)?
let new_balance = b - value;
Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), new_balance)?;
Self::set_reserved_balance(who, Self::reserved_balance(who) + value);
Self::set_free_balance(who, new_balance);
// 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(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
if value.is_zero() { return Zero::zero() }
let b = Self::reserved_balance(who);
let actual = cmp::min(b, value);
Self::set_free_balance(who, Self::free_balance(who) + actual);
Self::set_reserved_balance(who, b - 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.
fn slash_reserved(
who: &T::AccountId,
value: Self::Balance
) -> (Self::NegativeImbalance, Self::Balance) {
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
let b = Self::reserved_balance(who);
let slash = cmp::min(b, value);
// underflow should never happen, but it if does, there's nothing to be done here.
Self::set_reserved_balance(who, b - slash);
(NegativeImbalance::new(slash), value - slash)
// Move the reserved balance of one account into the free balance of another.
// Is a no-op if the value to be moved is zero.
fn repatriate_reserved(
slashed: &T::AccountId,
beneficiary: &T::AccountId,
) -> result::Result<Self::Balance, DispatchError> {
if value.is_zero() { return Ok (Zero::zero()) }
if Self::total_balance(beneficiary).is_zero() {
Err(Error::<T, I>::DeadAccount)?
}
let b = Self::reserved_balance(slashed);
let slash = cmp::min(b, value);
Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash);
Self::set_reserved_balance(slashed, b - slash);
impl<T: Trait<I>, I: Instance> LockableCurrency<T::AccountId> for Module<T, I>
T::Balance: MaybeSerializeDeserialize + Debug
{
type Moment = T::BlockNumber;
// Set a lock on the balance of `who`.
// Is a no-op if lock amount is zero.
fn set_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
until: T::BlockNumber,
reasons: WithdrawReasons,
) {
if amount.is_zero() { return }
let now = <frame_system::Module<T>>::block_number();
let mut new_lock = Some(BalanceLock { id, amount, until, reasons });
let mut locks = Self::locks(who).into_iter().filter_map(|l|
if l.id == id {
new_lock.take()
} else if l.until > now {
Some(l)
} else {
None
}).collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
<Locks<T, I>>::insert(who, locks);
}
fn extend_lock(
id: LockIdentifier,
who: &T::AccountId,
amount: T::Balance,
until: T::BlockNumber,
reasons: WithdrawReasons,
) {
let now = <frame_system::Module<T>>::block_number();
let mut new_lock = Some(BalanceLock { id, amount, until, reasons });
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),
until: l.until.max(nl.until),
reasons: l.reasons | nl.reasons,
}
})
} else if l.until > now {
Some(l)
} else {
None
}).collect::<Vec<_>>();
if let Some(lock) = new_lock {
locks.push(lock)
}
<Locks<T, I>>::insert(who, locks);
}
fn remove_lock(
id: LockIdentifier,
who: &T::AccountId,
) {
let now = <frame_system::Module<T>>::block_number();
let locks = Self::locks(who).into_iter().filter_map(|l|
if l.until > now && l.id != id {
Some(l)
} else {
None
}).collect::<Vec<_>>();
<Locks<T, I>>::insert(who, locks);
}
}
impl<T: Trait<I>, I: Instance> VestingCurrency<T::AccountId> for Module<T, I>
where
T::Balance: MaybeSerializeDeserialize + Debug
{
type Moment = T::BlockNumber;
/// Get the amount that is currently being vested and cannot be transferred out of this account.
fn vesting_balance(who: &T::AccountId) -> T::Balance {
if let Some(v) = Self::vesting(who) {
Self::free_balance(who)
.min(v.locked_at(<frame_system::Module<T>>::block_number()))
} else {
Zero::zero()
}
}
/// Adds a vesting schedule to a given account.
///
/// If there already exists a vesting schedule for the given account, an `Err` is returned
/// and nothing is updated.
/// Is a no-op if the amount to be vested is zero.
fn add_vesting_schedule(
who: &T::AccountId,
locked: T::Balance,
per_block: T::Balance,
starting_block: T::BlockNumber
if locked.is_zero() { return Ok(()) }
if <Vesting<T, I>>::exists(who) {
Err(Error::<T, I>::ExistingVestingSchedule)?
}
let vesting_schedule = VestingSchedule {
locked,
per_block,
starting_block
};
<Vesting<T, I>>::insert(who, vesting_schedule);
Ok(())
}
/// Remove a vesting schedule for a given account.
fn remove_vesting_schedule(who: &T::AccountId) {
<Vesting<T, I>>::remove(who);
}
}
impl<T: Trait<I>, I: Instance> IsDeadAccount<T::AccountId> for Module<T, I>
T::Balance: MaybeSerializeDeserialize + Debug