Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Documentation for balances module #1943

Merged
merged 35 commits into from
Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
74bcfcb
comment updates
joepetrowski Mar 6, 2019
a544d4a
Merge branch 'master' into joe-balances-docs
joepetrowski Mar 6, 2019
1cc5721
added rustdoc and readme
joepetrowski Mar 6, 2019
e4a3bf7
clarified LockableCurrency trait
joepetrowski Mar 6, 2019
3e4b5e4
Currency trait rustdocs
joepetrowski Mar 7, 2019
077cde2
fixed typo
joepetrowski Mar 7, 2019
dd16fd6
fixed suggestions round 1
joepetrowski Mar 8, 2019
ceeb172
UpdateBalanceOutcome docs (open for discussion)
joepetrowski Mar 8, 2019
05894ab
rm description of enum, consolidation, rm ReclaimRebate
joepetrowski Mar 9, 2019
a654735
type clarification, examples overhaul, adoc formatting
joepetrowski Mar 10, 2019
02110b2
adoc to md
joepetrowski Mar 11, 2019
53680af
format change for rustdoc
joepetrowski Mar 12, 2019
f1b62a4
update links and fix typos
joepetrowski Mar 12, 2019
499c239
typos and links
joepetrowski Mar 13, 2019
cf750f8
Merge remote-tracking branch 'origin/master' into joe-balances-docs
joepetrowski Mar 13, 2019
2cf4009
updates according to comments
joepetrowski Mar 13, 2019
d863ff6
new example
joepetrowski Mar 14, 2019
5e28748
small clarifications
joepetrowski Mar 14, 2019
c8ec45a
trait implementation section
joepetrowski Mar 14, 2019
36b1689
missing ```
joepetrowski Mar 14, 2019
3c157e4
small changes, ready for review
joepetrowski Mar 14, 2019
a0ef9b7
line width update
joepetrowski Mar 15, 2019
2d28117
Merge branch 'master' into joe-balances-docs
joepetrowski Mar 18, 2019
df3ced9
Merge branch 'master' into joe-balances-docs
joepetrowski Mar 21, 2019
e42f6b6
small tweaks
joepetrowski Mar 25, 2019
e000871
Update srml/balances/src/lib.rs
gui1117 Mar 25, 2019
faccdbf
Update srml/balances/src/lib.rs
gui1117 Mar 25, 2019
513b377
Update srml/balances/src/lib.rs
gui1117 Mar 25, 2019
ce29349
Update srml/balances/src/lib.rs
gui1117 Mar 25, 2019
fa9f215
Update lib.rs
joepetrowski Mar 25, 2019
28a1d33
address review by thiolliere
joepetrowski Mar 25, 2019
aa43b56
remove common warning
joepetrowski Mar 26, 2019
191ecc0
Merge remote-tracking branch 'origin/master' into joe-balances-docs
gavofyork Mar 26, 2019
665fa6b
Update docs
gavofyork Mar 26, 2019
5f391ae
updated srml example
joepetrowski Mar 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions srml/balances/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Balances Module

The balances module provides the functionality for handling balances.

## Overview

The balances module provides functions for:

- Getting and setting free balance
- Retrieving total, reserved, and unreserved balances
- Repatriating a reserved balance to a beneficiary account that exists
- Transfering a balance between accounts (when not reserved)
- Slashing an account balance
- Account removal
- Lookup of an index to reclaim an account
- Increasing or decreasing total stake
- Setting and removing locks on chains that implement `LockableCurrency`

The dispatchable function `transfer` ensures that the sender has signed the transaction. When using the publicly exposed functions in the implementation, you will need to do these checks in your runtime, as many functions will affect storage without ensuring, for example, that the sender is the signer.

## Public Interface

### Types

- Balance
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
- AccountId

### Dispatchable Functions
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Add link to rust docs (https://github.com/paritytech/substrate-developer-hub/issues/24)
- `transfer` - Transfer some liquid free balance to another staker.
- `set_balance` - Set the balances of a given account. Only dispatchable by a user with root priviledges.

## Usage

The following example shows how to use the balances module in your custom module.

1 Import the `balances` module and derive your module configuration trait with the balances trait.

```
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
use balances;

pub trait Trait: balances::Trait { }
```

2 In your module, call the balance module:

```
// Get existential deposit
let ed = Balances::existential_deposit();

// Get transfer fee for the network
let tf = Balances::transfer_fee();
```

3 Include a balance transfer in a block (example from the `executor` module tests):
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved

```
let mut t = new_test_ext(COMPACT_CODE, false);
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
let block1 = construct_block(
&mut t,
1,
GENESIS_HASH.into(),
vec![
CheckedExtrinsic {
signed: None,
function: Call::Timestamp(timestamp::Call::set(42)),
},
CheckedExtrinsic {
signed: Some((alice(), 0)),
function: Call::Balances(balances::Call::transfer(bob().into(), 69)),
},
]
);
```

## Dependencies

The balances module depends on the `system` and `srml_support` modules as well as Substrate Core libraries and the Rust standard library.

### Genesis config

Configuration is in `<your-node-name>/src/chain_spec.rs`. The following storage items are configurable:

- `TotalIssuance`
- `ExistentialDeposit`
- `TransferFee`
- `CreationFee`
- `Vesting`
- `FreeBalance`
63 changes: 40 additions & 23 deletions srml/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Balances: Handles setting and retrieval of free balance,
//! retrieving total balance, reserve and unreserve balance,
//! retrieving total, reserved, and unreserved balances,
//! repatriating a reserved balance to a beneficiary account that exists,
//! transfering a balance between accounts (when not reserved),
//! slashing an account balance, account removal, rewards,
//! lookup of an index to reclaim an account (when not balance not reserved),
//! lookup of an index to reclaim an account (when balance not reserved),
//! increasing total stake.

#![cfg_attr(not(feature = "std"), no_std)]
Expand Down Expand Up @@ -108,11 +108,11 @@ pub struct BalanceLock<Balance, BlockNumber> {

decl_storage! {
trait Store for Module<T: Trait> as Balances {
/// The total amount of stake on the system.
/// The total amount of stake in the system.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
pub TotalIssuance get(total_issuance) build(|config: &GenesisConfig<T>| {
config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n)
}): T::Balance;
/// The minimum amount allowed to keep an account open.
/// The minimum amount required to keep an account open.
pub ExistentialDeposit get(existential_deposit) config(): T::Balance;
/// The fee required to make a transfer.
pub TransferFee get(transfer_fee) config(): T::Balance;
Expand Down Expand Up @@ -143,11 +143,11 @@ decl_storage! {

/// The 'free' balance of a given account.
///
/// This is the only balance that matters in terms of most operations on tokens. It is
/// alone used to determine the balance when in the contract execution environment. When this
/// 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`. Furthermore, `OnFreeBalanceZero` callback
/// is invoked, giving a chance to external modules to cleanup data associated with
/// is invoked, giving a chance to external modules to clean up data associated with
/// the deleted account.
///
/// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
Expand Down Expand Up @@ -182,6 +182,10 @@ decl_module! {
fn deposit_event<T>() = default;

/// Transfer some liquid free balance to another staker.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
/// `transfer` will set the `FreeBalance` of the sender and receiver.
/// It will decrease the total stake of the system by the `TransferFee`.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
/// If the sender's account is below the existential deposit as a result
/// of the transfer, the account will be reaped.
pub fn transfer(
origin,
dest: <T::Lookup as StaticLookup>::Source,
Expand All @@ -192,7 +196,11 @@ decl_module! {
Self::make_transfer(&transactor, &dest, value)?;
}

/// Set the balances of a given account.
/// Set the balances of a given account. Only dispatchable by root.
/// This will alter `FreeBalance` and `ReservedBalance` in storage.
/// If the new free or reserved balance is below the existential deposit,
/// it will also decrease the total stake of the system (`TotalIssuance`)
/// and reset the account nonce (`system::AccountNonce`).
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
fn set_balance(
who: <T::Lookup as StaticLookup>::Source,
#[compact] free: T::Balance,
Expand All @@ -217,10 +225,13 @@ impl<T: Trait> Module<T> {
}
}

/// Set the free balance of an account to some new value.
/// Set the reserved balance of an account to some new value. Will enforce `ExistentialDeposit`
/// law, annulling the account as needed.
///
/// Will enforce ExistentialDeposit law, anulling the account as needed.
/// In that case it will return `AccountKilled`.
/// 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to have a standardized preconditions documentation section, many method here have preconditions that have to be meet in order to ensure consistency.

///
/// Returns either `Updated` or `AccountKilled`.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
if balance < Self::existential_deposit() {
<ReservedBalance<T>>::insert(who, balance);
Expand All @@ -232,15 +243,15 @@ impl<T: Trait> Module<T> {
}
}

/// Set the free balance of an account to some new value. Will enforce ExistentialDeposit
/// law anulling the account as needed.
/// 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.
///
/// Returns if the account was successfully updated or update has led to killing of the account.
/// Returns either `Updated` or `AccountKilled`.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
// Commented out for no - but consider it instructive.
// Commented out for now - but consider it instructive.
// assert!(!Self::total_balance(who).is_zero());
if balance < Self::existential_deposit() {
<FreeBalance<T>>::insert(who, balance);
Expand All @@ -252,13 +263,12 @@ impl<T: Trait> Module<T> {
}
}

/// Set the free balance on an account to some new value.
///
/// Same as [`set_free_balance`], but will create a new account.
/// Set the free balance on an account to some new value. Will enforce `ExistentialDeposit`
/// law, annulling the account as needed.
///
/// Returns if the account was successfully updated or update has led to killing of the account.
/// Same as `set_free_balance`, but will create a new account.
///
/// [`set_free_balance`]: #method.set_free_balance
/// Returns either `Updated` or `AccountKilled`.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
pub fn set_free_balance_creating(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome {
let ed = <Module<T>>::existential_deposit();
// If the balance is too low, then the account is reaped.
Expand All @@ -284,7 +294,12 @@ impl<T: Trait> Module<T> {
}
}

/// Transfer some liquid free balance to another staker.
/// Transfer some liquid free balance from one account to another.
///
/// Enforces `ExistentialDeposit` law, reaping the sender's account if it's balance is
/// too low as a result of the transfer.
///
/// Will create a new account for the destination if the account does not exist.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
pub fn make_transfer(transactor: &T::AccountId, dest: &T::AccountId, value: T::Balance) -> Result {
let from_balance = Self::free_balance(transactor);
let to_balance = Self::free_balance(dest);
Expand Down Expand Up @@ -327,6 +342,7 @@ impl<T: Trait> Module<T> {
Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone()));
}

/// Remove an account whose balance is below the existential deposit.
fn reap_account(who: &T::AccountId) {
<system::AccountNonce<T>>::remove(who);
Self::deposit_event(RawEvent::ReapedAccount(who.clone()));
Expand Down Expand Up @@ -355,13 +371,14 @@ impl<T: Trait> Module<T> {
}
}

/// Increase TotalIssuance by Value.
/// Increase `TotalIssuance` by `value`.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
pub fn increase_total_stake_by(value: T::Balance) {
if let Some(v) = <Module<T>>::total_issuance().checked_add(&value) {
<TotalIssuance<T>>::put(v);
}
}
/// Decrease TotalIssuance by Value.

/// Decrease `TotalIssuance` by `value`.
pub fn decrease_total_stake_by(value: T::Balance) {
if let Some(v) = <Module<T>>::total_issuance().checked_sub(&value) {
<TotalIssuance<T>>::put(v);
Expand Down
45 changes: 33 additions & 12 deletions srml/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub trait Currency<AccountId> {
/// alone 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`. Furthermore, `OnFreeBalanceZero` callback
/// is invoked, giving a chance to external modules to cleanup data associated with
/// is invoked, giving a chance to external modules to clean up data associated with
/// the deleted account.
///
/// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
Expand All @@ -123,7 +123,8 @@ pub trait Currency<AccountId> {
/// free balance. This function cannot fail.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
/// then `Some(remaining)` will be returned. If all of `value` is deducted, the function
/// will return `None`.
fn slash(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;

/// Adds up to `value` to the free balance of `who`.
Expand All @@ -135,7 +136,8 @@ pub trait Currency<AccountId> {
///
/// If `who` doesn't exist, it is created
///
/// Returns if the account was successfully updated or update has led to killing of the account.
/// Returns `Updated` if the account was successfully updated
/// or `AccountKilled` if the update has led to killing the account.
///
/// NOTE: This assumes that the total stake remains unchanged after this operation.
fn increase_free_balance_creating(who: &AccountId, value: Self::Balance) -> UpdateBalanceOutcome;
Expand All @@ -146,17 +148,24 @@ pub trait Currency<AccountId> {
/// be returned to notify of this. This is different behaviour to `unreserve`.
fn reserve(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>;

/// Moves up to `value` from reserved balance to balance. This function cannot fail.
/// Moves up to `value` from reserved balance to free balance. This function cannot fail.
///
/// As much funds up to `value` will be moved as possible. If the reserve balance of `who`
/// is less than `value`, then `Some(remaining)` will be returned. If all of `value` is
/// moved, the function will return `None`.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
/// NOTE: This is different to `reserve`.
///
/// NOTE: If the remaining reserved balance is less than `ExistentialDeposit`, it will
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
/// invoke `on_reserved_too_low` and could reap the account.
fn unreserve(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;

/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
/// Deducts up to `value` from reserved balance of `who` and the total stake of the system.
/// This function cannot fail.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
/// As much funds up to `value` will be deducted as possible. If the reserve balance of `who`
/// is less than `value`, then `Some(remaining)` will be returned. If all of `value` is
/// moved, the function will return `None`.
fn slash_reserved(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;

/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
Expand All @@ -181,7 +190,12 @@ pub trait LockableCurrency<AccountId>: Currency<AccountId> {
/// The quantity used to denote time; usually just a `BlockNumber`.
type Moment;

/// Introduce a new lock or change an existing one.
/// Create a new `BalanceLock` struct on account `who`.
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
///
/// If the new lock is valid (i.e. not already expired), it will push the struct to
/// the `Locks` vec in storage. Note that you can lock more funds than a user has.
///
/// If the lock `id` already exists, this will update it.
fn set_lock(
id: LockIdentifier,
who: &AccountId,
Expand All @@ -190,8 +204,15 @@ pub trait LockableCurrency<AccountId>: Currency<AccountId> {
reasons: WithdrawReasons,
);

/// Change any existing lock so that it becomes strictly less liquid in all
/// respects to the given parameters.
/// Changes a `BalanceLock` (selected by `id`) so that it becomes less liquid in all
joepetrowski marked this conversation as resolved.
Show resolved Hide resolved
/// parameters or creates a new one if it does not exist.
///
/// Calling `extend_lock` on an existing lock `id` differs from `set_lock` in that it
/// applies the most severe constraints of the two, while `set_lock` replaces the lock
/// with the new parameters. As in, `extend_lock` will set:
/// - maximum `amount`
/// - farthest duration (`until`)
/// - bitwise mask of all `reasons`
fn extend_lock(
id: LockIdentifier,
who: &AccountId,
Expand Down