mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-07 01:28:07 +00:00
8778ca7dc8
* Rename: Phase 1. * Unify codec. * Fixing: Phase 2 * Fixing: Phase 3. * Fixing: Phase 4. * Fixing: Phase 5. * Fixing: Phase 6. * Fixing: Phase 7. * Fixing: Phase 8. Tests * Fixing: Phase 9. Tests!!! * Fixing: Phase 10. Moar tests! * Finally done! * More fixes. * Rename primitives:: to sp_core:: * Apply renames in finality-grandpa. * Fix benches. * Fix benches 2. * Revert node-template. * Fix frame-system in our modules.
1256 lines
43 KiB
Rust
1256 lines
43 KiB
Rust
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
|
// This file is part of Substrate.
|
|
|
|
// Substrate is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Substrate is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! # Balances Module
|
|
//!
|
|
//! The Balances module provides functionality for handling accounts and balances.
|
|
//!
|
|
//! - [`balances::Trait`](./trait.Trait.html)
|
|
//! - [`Call`](./enum.Call.html)
|
|
//! - [`Module`](./struct.Module.html)
|
|
//!
|
|
//! ## Overview
|
|
//!
|
|
//! The Balances module provides functions for:
|
|
//!
|
|
//! - Getting and setting free balances.
|
|
//! - Retrieving total, reserved and unreserved balances.
|
|
//! - Repatriating a reserved balance to a beneficiary account that exists.
|
|
//! - Transferring a balance between accounts (when not reserved).
|
|
//! - Slashing an account balance.
|
|
//! - Account creation and removal.
|
|
//! - Managing total issuance.
|
|
//! - Setting and managing locks.
|
|
//!
|
|
//! ### Terminology
|
|
//!
|
|
//! - **Existential Deposit:** The minimum balance required to create or keep an account open. This prevents
|
|
//! "dust accounts" from filling storage.
|
|
//! - **Total Issuance:** The total number of units in existence in a system.
|
|
//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its balance is set
|
|
//! to zero.
|
|
//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only
|
|
//! balance that matters for most operations. When this balance falls below the existential
|
|
//! deposit, most functionality of the account is removed. When both it and the reserved balance
|
|
//! are deleted, then the account is said to be dead.
|
|
//!
|
|
//! No account should ever have a free balance that is strictly between 0 and the existential
|
|
//! deposit (exclusive). If this ever happens, it indicates either a bug in this module or an
|
|
//! erroneous raw mutation of storage.
|
|
//!
|
|
//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended.
|
|
//! Reserved balance can still be slashed, but only after all the free balance has been slashed.
|
|
//! If the reserved balance falls below the existential deposit, it and any related functionality
|
|
//! will be deleted. When both it and the free balance are deleted, then the account is said to
|
|
//! be dead.
|
|
//!
|
|
//! No account should ever have a reserved balance that is strictly between 0 and the existential
|
|
//! deposit (exclusive). If this ever happens, it indicates either a bug in this module or an
|
|
//! erroneous raw mutation of storage.
|
|
//!
|
|
//! - **Imbalance:** A condition when some funds were credited or debited without equal and opposite accounting
|
|
//! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will
|
|
//! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is
|
|
//! simply dropped, it should automatically maintain any book-keeping such as total issuance.)
|
|
//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple
|
|
//! locks always operate over the same funds, so they "overlay" rather than "stack".
|
|
//! - **Vesting:** Similar to a lock, this is another, but independent, liquidity restriction that reduces linearly
|
|
//! over time.
|
|
//!
|
|
//! ### Implementations
|
|
//!
|
|
//! The Balances module provides implementations for the following traits. If these traits provide the functionality
|
|
//! that you need, then you can avoid coupling with the Balances module.
|
|
//!
|
|
//! - [`Currency`](../frame_support/traits/trait.Currency.html): Functions for dealing with a
|
|
//! fungible assets system.
|
|
//! - [`ReservableCurrency`](../frame_support/traits/trait.ReservableCurrency.html):
|
|
//! Functions for dealing with assets that can be reserved from an account.
|
|
//! - [`LockableCurrency`](../frame_support/traits/trait.LockableCurrency.html): Functions for
|
|
//! dealing with accounts that allow liquidity restrictions.
|
|
//! - [`Imbalance`](../frame_support/traits/trait.Imbalance.html): Functions for handling
|
|
//! imbalances between total issuance in the system and account balances. Must be used when a function
|
|
//! creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee).
|
|
//! - [`IsDeadAccount`](../frame_system/trait.IsDeadAccount.html): Determiner to say whether a
|
|
//! given account is unused.
|
|
//!
|
|
//! ## Interface
|
|
//!
|
|
//! ### Dispatchable Functions
|
|
//!
|
|
//! - `transfer` - Transfer some liquid free balance to another account.
|
|
//! - `set_balance` - Set the balances of a given account. The origin of this call must be root.
|
|
//!
|
|
//! ### Public Functions
|
|
//!
|
|
//! - `vesting_balance` - Get the amount that is currently being vested and cannot be transferred out of this account.
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! The following examples show how to use the Balances module in your custom module.
|
|
//!
|
|
//! ### Examples from the SRML
|
|
//!
|
|
//! The Contract module uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`:
|
|
//!
|
|
//! ```
|
|
//! use frame_support::traits::Currency;
|
|
//! # pub trait Trait: frame_system::Trait {
|
|
//! # type Currency: Currency<Self::AccountId>;
|
|
//! # }
|
|
//!
|
|
//! pub type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
|
//! pub type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
|
|
//!
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! The Staking module uses the `LockableCurrency` trait to lock a stash account's funds:
|
|
//!
|
|
//! ```
|
|
//! use frame_support::traits::{WithdrawReasons, LockableCurrency};
|
|
//! use sp_runtime::traits::Bounded;
|
|
//! pub trait Trait: frame_system::Trait {
|
|
//! type Currency: LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>;
|
|
//! }
|
|
//! # struct StakingLedger<T: Trait> {
|
|
//! # stash: <T as frame_system::Trait>::AccountId,
|
|
//! # total: <<T as Trait>::Currency as frame_support::traits::Currency<<T as frame_system::Trait>::AccountId>>::Balance,
|
|
//! # phantom: std::marker::PhantomData<T>,
|
|
//! # }
|
|
//! # const STAKING_ID: [u8; 8] = *b"staking ";
|
|
//!
|
|
//! fn update_ledger<T: Trait>(
|
|
//! controller: &T::AccountId,
|
|
//! ledger: &StakingLedger<T>
|
|
//! ) {
|
|
//! T::Currency::set_lock(
|
|
//! STAKING_ID,
|
|
//! &ledger.stash,
|
|
//! ledger.total,
|
|
//! T::BlockNumber::max_value(),
|
|
//! WithdrawReasons::all()
|
|
//! );
|
|
//! // <Ledger<T>>::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here.
|
|
//! }
|
|
//! # fn main() {}
|
|
//! ```
|
|
//!
|
|
//! ## Genesis config
|
|
//!
|
|
//! The Balances module depends on the [`GenesisConfig`](./struct.GenesisConfig.html).
|
|
//!
|
|
//! ## Assumptions
|
|
//!
|
|
//! * Total issued balanced of all accounts should be less than `Trait::Balance::max_value()`.
|
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
use sp_std::prelude::*;
|
|
use sp_std::{cmp, result, mem, fmt::Debug};
|
|
use codec::{Codec, Encode, Decode};
|
|
use frame_support::{
|
|
StorageValue, Parameter, decl_event, decl_storage, decl_module,
|
|
traits::{
|
|
UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnUnbalanced, TryDrop,
|
|
WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement,
|
|
Imbalance, SignedImbalance, ReservableCurrency, Get, VestingCurrency,
|
|
},
|
|
weights::SimpleDispatchInfo,
|
|
dispatch::Result,
|
|
};
|
|
use sp_runtime::{
|
|
RuntimeDebug,
|
|
traits::{
|
|
Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDeserialize,
|
|
Saturating, Bounded,
|
|
},
|
|
};
|
|
use frame_system::{self as system, IsDeadAccount, OnNewAccount, ensure_signed, ensure_root};
|
|
|
|
#[cfg(test)]
|
|
mod mock;
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
pub use self::imbalances::{PositiveImbalance, NegativeImbalance};
|
|
|
|
pub trait Subtrait<I: Instance = DefaultInstance>: frame_system::Trait {
|
|
/// The balance of an account.
|
|
type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy +
|
|
MaybeSerializeDeserialize + Debug + From<Self::BlockNumber>;
|
|
|
|
/// A function that is invoked when the free-balance has fallen below the existential deposit and
|
|
/// has been reduced to zero.
|
|
///
|
|
/// Gives a chance to clean up resources associated with the given account.
|
|
type OnFreeBalanceZero: OnFreeBalanceZero<Self::AccountId>;
|
|
|
|
/// Handler for when a new account is created.
|
|
type OnNewAccount: OnNewAccount<Self::AccountId>;
|
|
|
|
/// The minimum amount required to keep an account open.
|
|
type ExistentialDeposit: Get<Self::Balance>;
|
|
|
|
/// The fee required to make a transfer.
|
|
type TransferFee: Get<Self::Balance>;
|
|
|
|
/// The fee required to create an account.
|
|
type CreationFee: Get<Self::Balance>;
|
|
}
|
|
|
|
pub trait Trait<I: Instance = DefaultInstance>: frame_system::Trait {
|
|
/// The balance of an account.
|
|
type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy +
|
|
MaybeSerializeDeserialize + Debug + From<Self::BlockNumber>;
|
|
|
|
/// A function that is invoked when the free-balance has fallen below the existential deposit and
|
|
/// has been reduced to zero.
|
|
///
|
|
/// Gives a chance to clean up resources associated with the given account.
|
|
type OnFreeBalanceZero: OnFreeBalanceZero<Self::AccountId>;
|
|
|
|
/// Handler for when a new account is created.
|
|
type OnNewAccount: OnNewAccount<Self::AccountId>;
|
|
|
|
/// Handler for the unbalanced reduction when taking fees associated with balance
|
|
/// transfer (which may also include account creation).
|
|
type TransferPayment: OnUnbalanced<NegativeImbalance<Self, I>>;
|
|
|
|
/// Handler for the unbalanced reduction when removing a dust account.
|
|
type DustRemoval: OnUnbalanced<NegativeImbalance<Self, I>>;
|
|
|
|
/// The overarching event type.
|
|
type Event: From<Event<Self, I>> + Into<<Self as frame_system::Trait>::Event>;
|
|
|
|
/// The minimum amount required to keep an account open.
|
|
type ExistentialDeposit: Get<Self::Balance>;
|
|
|
|
/// The fee required to make a transfer.
|
|
type TransferFee: Get<Self::Balance>;
|
|
|
|
/// The fee required to create an account.
|
|
type CreationFee: Get<Self::Balance>;
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> Subtrait<I> for T {
|
|
type Balance = T::Balance;
|
|
type OnFreeBalanceZero = T::OnFreeBalanceZero;
|
|
type OnNewAccount = T::OnNewAccount;
|
|
type ExistentialDeposit = T::ExistentialDeposit;
|
|
type TransferFee = T::TransferFee;
|
|
type CreationFee = T::CreationFee;
|
|
}
|
|
|
|
decl_event!(
|
|
pub enum Event<T, I: Instance = DefaultInstance> where
|
|
<T as frame_system::Trait>::AccountId,
|
|
<T as Trait<I>>::Balance
|
|
{
|
|
/// A new account was created.
|
|
NewAccount(AccountId, Balance),
|
|
/// An account was reaped.
|
|
ReapedAccount(AccountId),
|
|
/// Transfer succeeded (from, to, value, fees).
|
|
Transfer(AccountId, AccountId, Balance, Balance),
|
|
}
|
|
);
|
|
|
|
/// Struct to encode the vesting schedule of an individual account.
|
|
#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
pub struct VestingSchedule<Balance, BlockNumber> {
|
|
/// Locked amount at genesis.
|
|
pub locked: Balance,
|
|
/// Amount that gets unlocked every block after `starting_block`.
|
|
pub per_block: Balance,
|
|
/// Starting block for unlocking(vesting).
|
|
pub starting_block: BlockNumber,
|
|
}
|
|
|
|
impl<Balance: SimpleArithmetic + Copy, BlockNumber: SimpleArithmetic + Copy> VestingSchedule<Balance, BlockNumber> {
|
|
/// Amount locked at block `n`.
|
|
pub fn locked_at(&self, n: BlockNumber) -> Balance
|
|
where Balance: From<BlockNumber>
|
|
{
|
|
// Number of blocks that count toward vesting
|
|
// Saturating to 0 when n < starting_block
|
|
let vested_block_count = n.saturating_sub(self.starting_block);
|
|
// Return amount that is still locked in vesting
|
|
if let Some(x) = Balance::from(vested_block_count).checked_mul(&self.per_block) {
|
|
self.locked.max(x) - x
|
|
} else {
|
|
Zero::zero()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
|
|
pub struct BalanceLock<Balance, BlockNumber> {
|
|
pub id: LockIdentifier,
|
|
pub amount: Balance,
|
|
pub until: BlockNumber,
|
|
pub reasons: WithdrawReasons,
|
|
}
|
|
|
|
decl_storage! {
|
|
trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Balances {
|
|
/// The total units issued in the system.
|
|
pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig<T, I>| {
|
|
config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n)
|
|
}): T::Balance;
|
|
|
|
/// Information regarding the vesting of a given account.
|
|
pub Vesting get(fn vesting) build(|config: &GenesisConfig<T, I>| {
|
|
// Generate initial vesting configuration
|
|
// * who - Account which we are generating vesting configuration for
|
|
// * begin - Block when the account will start to vest
|
|
// * length - Number of blocks from `begin` until fully vested
|
|
// * liquid - Number of units which can be spent before vesting begins
|
|
config.vesting.iter().filter_map(|&(ref who, begin, length, liquid)| {
|
|
let length = <T::Balance as From<T::BlockNumber>>::from(length);
|
|
|
|
config.balances.iter()
|
|
.find(|&&(ref w, _)| w == who)
|
|
.map(|&(_, balance)| {
|
|
// Total genesis `balance` minus `liquid` equals funds locked for vesting
|
|
let locked = balance.saturating_sub(liquid);
|
|
// Number of units unlocked per block after `begin`
|
|
let per_block = locked / length.max(sp_runtime::traits::One::one());
|
|
|
|
(who.clone(), VestingSchedule {
|
|
locked: locked,
|
|
per_block: per_block,
|
|
starting_block: begin
|
|
})
|
|
})
|
|
}).collect::<Vec<_>>()
|
|
}): map T::AccountId => Option<VestingSchedule<T::Balance, T::BlockNumber>>;
|
|
|
|
/// The 'free' balance of a given account.
|
|
///
|
|
/// This is the only balance that matters in terms of most operations on tokens. It
|
|
/// alone is used to determine the balance when in the contract execution environment. When this
|
|
/// balance falls below the value of `ExistentialDeposit`, then the 'current account' is
|
|
/// deleted: specifically `FreeBalance`. Further, the `OnFreeBalanceZero` callback
|
|
/// is invoked, giving a chance to external modules to clean up data associated with
|
|
/// the deleted account.
|
|
///
|
|
/// `frame_system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
|
|
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
|
pub FreeBalance get(fn free_balance)
|
|
build(|config: &GenesisConfig<T, I>| config.balances.clone()):
|
|
map T::AccountId => T::Balance;
|
|
|
|
/// The amount of the balance of a given account that is externally reserved; this can still get
|
|
/// slashed, but gets slashed last of all.
|
|
///
|
|
/// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
|
|
/// that are still 'owned' by the account holder, but which are suspendable.
|
|
///
|
|
/// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'
|
|
/// is deleted: specifically, `ReservedBalance`.
|
|
///
|
|
/// `frame_system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
|
|
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.)
|
|
pub ReservedBalance get(fn reserved_balance): map T::AccountId => T::Balance;
|
|
|
|
/// Any liquidity locks on some account balances.
|
|
pub Locks get(fn locks): map T::AccountId => Vec<BalanceLock<T::Balance, T::BlockNumber>>;
|
|
}
|
|
add_extra_genesis {
|
|
config(balances): Vec<(T::AccountId, T::Balance)>;
|
|
config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, T::Balance)>;
|
|
// ^^ begin, length, amount liquid at genesis
|
|
build(|config: &GenesisConfig<T, I>| {
|
|
for (_, balance) in &config.balances {
|
|
assert!(
|
|
*balance >= <T as Trait<I>>::ExistentialDeposit::get(),
|
|
"the balance of any account should always be more than existential deposit.",
|
|
)
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
decl_module! {
|
|
pub struct Module<T: Trait<I>, I: Instance = DefaultInstance> for enum Call where origin: T::Origin {
|
|
/// The minimum amount required to keep an account open.
|
|
const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get();
|
|
|
|
/// The fee required to make a transfer.
|
|
const TransferFee: T::Balance = T::TransferFee::get();
|
|
|
|
/// The fee required to create an account.
|
|
const CreationFee: T::Balance = T::CreationFee::get();
|
|
|
|
fn deposit_event() = default;
|
|
|
|
/// Transfer some liquid free balance to another account.
|
|
///
|
|
/// `transfer` will set the `FreeBalance` of the sender and receiver.
|
|
/// It will decrease the total issuance of the system by the `TransferFee`.
|
|
/// If the sender's account is below the existential deposit as a result
|
|
/// of the transfer, the account will be reaped.
|
|
///
|
|
/// The dispatch origin for this call must be `Signed` by the transactor.
|
|
///
|
|
/// # <weight>
|
|
/// - Dependent on arguments but not critical, given proper implementations for
|
|
/// input config types. See related functions below.
|
|
/// - It contains a limited number of reads and writes internally and no complex computation.
|
|
///
|
|
/// Related functions:
|
|
///
|
|
/// - `ensure_can_withdraw` is always called internally but has a bounded complexity.
|
|
/// - Transferring balances to accounts that did not exist before will cause
|
|
/// `T::OnNewAccount::on_new_account` to be called.
|
|
/// - Removing enough funds from an account will trigger
|
|
/// `T::DustRemoval::on_unbalanced` and `T::OnFreeBalanceZero::on_free_balance_zero`.
|
|
/// - `transfer_keep_alive` works the same way as `transfer`, but has an additional
|
|
/// check that the transfer will not kill the origin account.
|
|
///
|
|
/// # </weight>
|
|
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
|
|
pub fn transfer(
|
|
origin,
|
|
dest: <T::Lookup as StaticLookup>::Source,
|
|
#[compact] value: T::Balance
|
|
) {
|
|
let transactor = ensure_signed(origin)?;
|
|
let dest = T::Lookup::lookup(dest)?;
|
|
<Self as Currency<_>>::transfer(&transactor, &dest, value, ExistenceRequirement::AllowDeath)?;
|
|
}
|
|
|
|
/// Set the balances of a given account.
|
|
///
|
|
/// This will alter `FreeBalance` and `ReservedBalance` in storage. it will
|
|
/// also decrease the total issuance of the system (`TotalIssuance`).
|
|
/// If the new free or reserved balance is below the existential deposit,
|
|
/// it will reset the account nonce (`frame_system::AccountNonce`).
|
|
///
|
|
/// The dispatch origin for this call is `root`.
|
|
///
|
|
/// # <weight>
|
|
/// - Independent of the arguments.
|
|
/// - Contains a limited number of reads and writes.
|
|
/// # </weight>
|
|
#[weight = SimpleDispatchInfo::FixedOperational(50_000)]
|
|
fn set_balance(
|
|
origin,
|
|
who: <T::Lookup as StaticLookup>::Source,
|
|
#[compact] new_free: T::Balance,
|
|
#[compact] new_reserved: T::Balance
|
|
) {
|
|
ensure_root(origin)?;
|
|
let who = T::Lookup::lookup(who)?;
|
|
|
|
let current_free = <FreeBalance<T, I>>::get(&who);
|
|
if new_free > current_free {
|
|
mem::drop(PositiveImbalance::<T, I>::new(new_free - current_free));
|
|
} else if new_free < current_free {
|
|
mem::drop(NegativeImbalance::<T, I>::new(current_free - new_free));
|
|
}
|
|
Self::set_free_balance(&who, new_free);
|
|
|
|
let current_reserved = <ReservedBalance<T, I>>::get(&who);
|
|
if new_reserved > current_reserved {
|
|
mem::drop(PositiveImbalance::<T, I>::new(new_reserved - current_reserved));
|
|
} else if new_reserved < current_reserved {
|
|
mem::drop(NegativeImbalance::<T, I>::new(current_reserved - new_reserved));
|
|
}
|
|
Self::set_reserved_balance(&who, new_reserved);
|
|
}
|
|
|
|
/// Exactly as `transfer`, except the origin must be root and the source account may be
|
|
/// specified.
|
|
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
|
|
pub fn force_transfer(
|
|
origin,
|
|
source: <T::Lookup as StaticLookup>::Source,
|
|
dest: <T::Lookup as StaticLookup>::Source,
|
|
#[compact] value: T::Balance
|
|
) {
|
|
ensure_root(origin)?;
|
|
let source = T::Lookup::lookup(source)?;
|
|
let dest = T::Lookup::lookup(dest)?;
|
|
<Self as Currency<_>>::transfer(&source, &dest, value, ExistenceRequirement::AllowDeath)?;
|
|
}
|
|
|
|
/// Same as the [`transfer`] call, but with a check that the transfer will not kill the
|
|
/// origin account.
|
|
///
|
|
/// 99% of the time you want [`transfer`] instead.
|
|
///
|
|
/// [`transfer`]: struct.Module.html#method.transfer
|
|
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
|
|
pub fn transfer_keep_alive(
|
|
origin,
|
|
dest: <T::Lookup as StaticLookup>::Source,
|
|
#[compact] value: T::Balance
|
|
) {
|
|
let transactor = ensure_signed(origin)?;
|
|
let dest = T::Lookup::lookup(dest)?;
|
|
<Self as Currency<_>>::transfer(&transactor, &dest, value, ExistenceRequirement::KeepAlive)?;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> Module<T, I> {
|
|
// PRIVATE MUTABLES
|
|
|
|
/// Set the reserved balance of an account to some new value. Will enforce `ExistentialDeposit`
|
|
/// law, annulling the account as needed.
|
|
///
|
|
/// Doesn't do any preparatory work for creating a new account, so should only be used when it
|
|
/// is known that the account already exists.
|
|
///
|
|
/// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that
|
|
/// the caller will do this.
|
|
fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
|
if balance < T::ExistentialDeposit::get() {
|
|
<ReservedBalance<T, I>>::insert(who, balance);
|
|
Self::on_reserved_too_low(who);
|
|
UpdateBalanceOutcome::AccountKilled
|
|
} else {
|
|
<ReservedBalance<T, I>>::insert(who, balance);
|
|
UpdateBalanceOutcome::Updated
|
|
}
|
|
}
|
|
|
|
/// Set the free balance of an account to some new value. Will enforce `ExistentialDeposit`
|
|
/// law, annulling the account as needed.
|
|
///
|
|
/// Doesn't do any preparatory work for creating a new account, so should only be used when it
|
|
/// is known that the account already exists.
|
|
///
|
|
/// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that
|
|
/// the caller will do this.
|
|
fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
|
|
// Commented out for now - but consider it instructive.
|
|
// assert!(!Self::total_balance(who).is_zero());
|
|
// assert!(Self::free_balance(who) > T::ExistentialDeposit::get());
|
|
if balance < T::ExistentialDeposit::get() {
|
|
<FreeBalance<T, I>>::insert(who, balance);
|
|
Self::on_free_too_low(who);
|
|
UpdateBalanceOutcome::AccountKilled
|
|
} else {
|
|
<FreeBalance<T, I>>::insert(who, balance);
|
|
UpdateBalanceOutcome::Updated
|
|
}
|
|
}
|
|
|
|
/// Register a new account (with existential balance).
|
|
///
|
|
/// This just calls appropriate hooks. It doesn't (necessarily) make any state changes.
|
|
fn new_account(who: &T::AccountId, balance: T::Balance) {
|
|
T::OnNewAccount::on_new_account(&who);
|
|
Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone()));
|
|
}
|
|
|
|
/// Unregister an account.
|
|
///
|
|
/// This just removes the nonce and leaves an event.
|
|
fn reap_account(who: &T::AccountId) {
|
|
<frame_system::AccountNonce<T>>::remove(who);
|
|
Self::deposit_event(RawEvent::ReapedAccount(who.clone()));
|
|
}
|
|
|
|
/// Account's free balance has dropped below existential deposit. Kill its
|
|
/// free side and the account completely if its reserved size is already dead.
|
|
///
|
|
/// Will maintain total issuance.
|
|
fn on_free_too_low(who: &T::AccountId) {
|
|
let dust = <FreeBalance<T, I>>::take(who);
|
|
<Locks<T, I>>::remove(who);
|
|
|
|
// underflow should never happen, but if it does, there's not much we can do about it.
|
|
if !dust.is_zero() {
|
|
T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust));
|
|
}
|
|
|
|
T::OnFreeBalanceZero::on_free_balance_zero(who);
|
|
|
|
if Self::reserved_balance(who).is_zero() {
|
|
Self::reap_account(who);
|
|
}
|
|
}
|
|
|
|
/// Account's reserved balance has dropped below existential deposit. Kill its
|
|
/// reserved side and the account completely if its free size is already dead.
|
|
///
|
|
/// Will maintain total issuance.
|
|
fn on_reserved_too_low(who: &T::AccountId) {
|
|
let dust = <ReservedBalance<T, I>>::take(who);
|
|
|
|
// underflow should never happen, but it if does, there's nothing to be done here.
|
|
if !dust.is_zero() {
|
|
T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust));
|
|
}
|
|
|
|
if Self::free_balance(who).is_zero() {
|
|
Self::reap_account(who);
|
|
}
|
|
}
|
|
}
|
|
|
|
// wrapping these imbalances in a private module is necessary to ensure absolute privacy
|
|
// of the inner member.
|
|
mod imbalances {
|
|
use super::{
|
|
result, Subtrait, DefaultInstance, Imbalance, Trait, Zero, Instance, Saturating,
|
|
StorageValue, TryDrop,
|
|
};
|
|
use sp_std::mem;
|
|
|
|
/// 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]
|
|
pub struct PositiveImbalance<T: Subtrait<I>, I: Instance=DefaultInstance>(T::Balance);
|
|
|
|
impl<T: Subtrait<I>, I: Instance> 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]
|
|
pub struct NegativeImbalance<T: Subtrait<I>, I: Instance=DefaultInstance>(T::Balance);
|
|
|
|
impl<T: Subtrait<I>, I: Instance> NegativeImbalance<T, I> {
|
|
/// Create a new negative imbalance from a balance.
|
|
pub fn new(amount: T::Balance) -> Self {
|
|
NegativeImbalance(amount)
|
|
}
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> TryDrop for PositiveImbalance<T, I> {
|
|
fn try_drop(self) -> result::Result<(), Self> {
|
|
self.drop_zero()
|
|
}
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> 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) -> result::Result<Self, Self::Opposite> {
|
|
let (a, b) = (self.0, other.0);
|
|
mem::forget((self, other));
|
|
|
|
if a >= b {
|
|
Ok(Self(a - b))
|
|
} else {
|
|
Err(NegativeImbalance::new(b - a))
|
|
}
|
|
}
|
|
fn peek(&self) -> T::Balance {
|
|
self.0.clone()
|
|
}
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> TryDrop for NegativeImbalance<T, I> {
|
|
fn try_drop(self) -> result::Result<(), Self> {
|
|
self.drop_zero()
|
|
}
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> Imbalance<T::Balance> for NegativeImbalance<T, I> {
|
|
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) -> result::Result<Self, Self::Opposite> {
|
|
let (a, b) = (self.0, other.0);
|
|
mem::forget((self, other));
|
|
|
|
if a >= b {
|
|
Ok(Self(a - b))
|
|
} else {
|
|
Err(PositiveImbalance::new(b - a))
|
|
}
|
|
}
|
|
fn peek(&self) -> T::Balance {
|
|
self.0.clone()
|
|
}
|
|
}
|
|
|
|
impl<T: Subtrait<I>, I: Instance> Drop for PositiveImbalance<T, I> {
|
|
/// Basic drop handler will just square up the total issuance.
|
|
fn drop(&mut self) {
|
|
<super::TotalIssuance<super::ElevatedTrait<T, I>, I>>::mutate(
|
|
|v| *v = v.saturating_add(self.0)
|
|
);
|
|
}
|
|
}
|
|
|
|
impl<T: Subtrait<I>, I: Instance> Drop for NegativeImbalance<T, I> {
|
|
/// Basic drop handler will just square up the total issuance.
|
|
fn drop(&mut self) {
|
|
<super::TotalIssuance<super::ElevatedTrait<T, I>, I>>::mutate(
|
|
|v| *v = v.saturating_sub(self.0)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: #2052
|
|
// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by`
|
|
// using only the Subtrait (which defines only the types that are not dependent
|
|
// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a
|
|
// circular dependency with Trait having some types be dependent on PositiveImbalance<Trait>
|
|
// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus
|
|
// its type declaration).
|
|
// This works as long as `increase_total_issuance_by` doesn't use the Imbalance
|
|
// types (basically for charging fees).
|
|
// This should eventually be refactored so that the three type items that do
|
|
// depend on the Imbalance type (TransferPayment, DustRemoval)
|
|
// are placed in their own SRML module.
|
|
struct ElevatedTrait<T: Subtrait<I>, I: Instance>(T, I);
|
|
impl<T: Subtrait<I>, I: Instance> Clone for ElevatedTrait<T, I> {
|
|
fn clone(&self) -> Self { unimplemented!() }
|
|
}
|
|
impl<T: Subtrait<I>, I: Instance> PartialEq for ElevatedTrait<T, I> {
|
|
fn eq(&self, _: &Self) -> bool { unimplemented!() }
|
|
}
|
|
impl<T: Subtrait<I>, I: Instance> Eq for ElevatedTrait<T, I> {}
|
|
impl<T: Subtrait<I>, I: Instance> frame_system::Trait for ElevatedTrait<T, I> {
|
|
type Origin = T::Origin;
|
|
type Call = T::Call;
|
|
type Index = T::Index;
|
|
type BlockNumber = T::BlockNumber;
|
|
type Hash = T::Hash;
|
|
type Hashing = T::Hashing;
|
|
type AccountId = T::AccountId;
|
|
type Lookup = T::Lookup;
|
|
type Header = T::Header;
|
|
type Event = ();
|
|
type BlockHashCount = T::BlockHashCount;
|
|
type MaximumBlockWeight = T::MaximumBlockWeight;
|
|
type MaximumBlockLength = T::MaximumBlockLength;
|
|
type AvailableBlockRatio = T::AvailableBlockRatio;
|
|
type Version = T::Version;
|
|
}
|
|
impl<T: Subtrait<I>, I: Instance> Trait<I> for ElevatedTrait<T, I> {
|
|
type Balance = T::Balance;
|
|
type OnFreeBalanceZero = T::OnFreeBalanceZero;
|
|
type OnNewAccount = T::OnNewAccount;
|
|
type Event = ();
|
|
type TransferPayment = ();
|
|
type DustRemoval = ();
|
|
type ExistentialDeposit = T::ExistentialDeposit;
|
|
type TransferFee = T::TransferFee;
|
|
type CreationFee = T::CreationFee;
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<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 {
|
|
Self::free_balance(who) + Self::reserved_balance(who)
|
|
}
|
|
|
|
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
|
|
Self::free_balance(who) >= value
|
|
}
|
|
|
|
fn total_issuance() -> Self::Balance {
|
|
<TotalIssuance<T, I>>::get()
|
|
}
|
|
|
|
fn minimum_balance() -> Self::Balance {
|
|
T::ExistentialDeposit::get()
|
|
}
|
|
|
|
fn free_balance(who: &T::AccountId) -> Self::Balance {
|
|
<FreeBalance<T, I>>::get(who)
|
|
}
|
|
|
|
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
|
|
<TotalIssuance<T, I>>::mutate(|issued| {
|
|
*issued = issued.checked_sub(&amount).unwrap_or_else(|| {
|
|
amount = *issued;
|
|
Zero::zero()
|
|
});
|
|
});
|
|
PositiveImbalance::new(amount)
|
|
}
|
|
|
|
fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
|
|
<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)
|
|
}
|
|
|
|
// # <weight>
|
|
// Despite iterating over a list of locks, they are limited by the number of
|
|
// lock IDs, which means the number of runtime modules that intend to use and create locks.
|
|
// # </weight>
|
|
fn ensure_can_withdraw(
|
|
who: &T::AccountId,
|
|
_amount: T::Balance,
|
|
reasons: WithdrawReasons,
|
|
new_balance: T::Balance,
|
|
) -> Result {
|
|
if reasons.intersects(WithdrawReason::Reserve | WithdrawReason::Transfer)
|
|
&& Self::vesting_balance(who) > new_balance
|
|
{
|
|
return Err("vesting balance too high to send value");
|
|
}
|
|
let locks = Self::locks(who);
|
|
if locks.is_empty() {
|
|
return Ok(())
|
|
}
|
|
|
|
let now = <frame_system::Module<T>>::block_number();
|
|
if locks.into_iter()
|
|
.all(|l|
|
|
now >= l.until
|
|
|| new_balance >= l.amount
|
|
|| !l.reasons.intersects(reasons)
|
|
)
|
|
{
|
|
Ok(())
|
|
} else {
|
|
Err("account liquidity restrictions prevent withdrawal")
|
|
}
|
|
}
|
|
|
|
fn transfer(
|
|
transactor: &T::AccountId,
|
|
dest: &T::AccountId,
|
|
value: Self::Balance,
|
|
existence_requirement: ExistenceRequirement,
|
|
) -> Result {
|
|
let from_balance = Self::free_balance(transactor);
|
|
let to_balance = Self::free_balance(dest);
|
|
let would_create = to_balance.is_zero();
|
|
let fee = if would_create { T::CreationFee::get() } else { T::TransferFee::get() };
|
|
let liability = match value.checked_add(&fee) {
|
|
Some(l) => l,
|
|
None => return Err("got overflow after adding a fee to value"),
|
|
};
|
|
|
|
let new_from_balance = match from_balance.checked_sub(&liability) {
|
|
None => return Err("balance too low to send value"),
|
|
Some(b) => b,
|
|
};
|
|
if would_create && value < T::ExistentialDeposit::get() {
|
|
return Err("value too low to create account");
|
|
}
|
|
Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer.into(), new_from_balance)?;
|
|
|
|
// NOTE: total stake being stored in the same type means that this could never overflow
|
|
// but better to be safe than sorry.
|
|
let new_to_balance = match to_balance.checked_add(&value) {
|
|
Some(b) => b,
|
|
None => return Err("destination balance too high to receive value"),
|
|
};
|
|
|
|
if transactor != dest {
|
|
if existence_requirement == ExistenceRequirement::KeepAlive {
|
|
if new_from_balance < Self::minimum_balance() {
|
|
return Err("transfer would kill account");
|
|
}
|
|
}
|
|
|
|
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(())
|
|
}
|
|
|
|
fn withdraw(
|
|
who: &T::AccountId,
|
|
value: Self::Balance,
|
|
reasons: WithdrawReasons,
|
|
liveness: ExistenceRequirement,
|
|
) -> result::Result<Self::NegativeImbalance, &'static str> {
|
|
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()
|
|
{
|
|
return Err("payment would kill account")
|
|
}
|
|
Self::ensure_can_withdraw(who, value, reasons, new_balance)?;
|
|
Self::set_free_balance(who, new_balance);
|
|
Ok(NegativeImbalance::new(value))
|
|
} else {
|
|
Err("too few free funds in account")
|
|
}
|
|
}
|
|
|
|
fn slash(
|
|
who: &T::AccountId,
|
|
value: Self::Balance
|
|
) -> (Self::NegativeImbalance, Self::Balance) {
|
|
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)
|
|
} else {
|
|
(NegativeImbalance::new(value), Zero::zero())
|
|
}
|
|
}
|
|
|
|
fn deposit_into_existing(
|
|
who: &T::AccountId,
|
|
value: Self::Balance
|
|
) -> result::Result<Self::PositiveImbalance, &'static str> {
|
|
if Self::total_balance(who).is_zero() {
|
|
return Err("beneficiary account must pre-exist");
|
|
}
|
|
Self::set_free_balance(who, Self::free_balance(who) + value);
|
|
Ok(PositiveImbalance::new(value))
|
|
}
|
|
|
|
fn deposit_creating(
|
|
who: &T::AccountId,
|
|
value: Self::Balance,
|
|
) -> Self::PositiveImbalance {
|
|
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))
|
|
} else {
|
|
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
|
|
{
|
|
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
|
|
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)
|
|
}
|
|
|
|
fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> {
|
|
let b = Self::free_balance(who);
|
|
if b < value {
|
|
return Err("not enough free funds")
|
|
}
|
|
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);
|
|
Ok(())
|
|
}
|
|
|
|
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
|
|
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);
|
|
value - actual
|
|
}
|
|
|
|
fn slash_reserved(
|
|
who: &T::AccountId,
|
|
value: Self::Balance
|
|
) -> (Self::NegativeImbalance, Self::Balance) {
|
|
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)
|
|
}
|
|
|
|
fn repatriate_reserved(
|
|
slashed: &T::AccountId,
|
|
beneficiary: &T::AccountId,
|
|
value: Self::Balance,
|
|
) -> result::Result<Self::Balance, &'static str> {
|
|
if Self::total_balance(beneficiary).is_zero() {
|
|
return Err("beneficiary account must pre-exist");
|
|
}
|
|
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);
|
|
Ok(value - slash)
|
|
}
|
|
}
|
|
|
|
impl<T: Trait<I>, I: Instance> LockableCurrency<T::AccountId> for Module<T, I>
|
|
where
|
|
T::Balance: MaybeSerializeDeserialize + Debug
|
|
{
|
|
type Moment = T::BlockNumber;
|
|
|
|
fn set_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()
|
|
} 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.
|
|
fn add_vesting_schedule(
|
|
who: &T::AccountId,
|
|
locked: T::Balance,
|
|
per_block: T::Balance,
|
|
starting_block: T::BlockNumber
|
|
) -> Result {
|
|
if <Vesting<T, I>>::exists(who) {
|
|
return Err("A vesting schedule already exists for this account.");
|
|
}
|
|
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>
|
|
where
|
|
T::Balance: MaybeSerializeDeserialize + Debug
|
|
{
|
|
fn is_dead_account(who: &T::AccountId) -> bool {
|
|
Self::total_balance(who).is_zero()
|
|
}
|
|
}
|