Skip to content

Commit

Permalink
Tokens in FRAME Docs (#2802)
Browse files Browse the repository at this point in the history
Closes paritytech/polkadot-sdk-docs#70

WIP PR for an overview of how to develop tokens in FRAME. 

- [x] Tokens in Substrate Ref Doc
  - High-level overview of the token-related logic in FRAME
- Improve docs with better explanation of how holds, freezes, ed, free
balance, etc, all work
- [x] Update `pallet_balances` docs
  - Clearly mark what is deprecated (currency)
- [x] Write fungible trait docs
- [x] Evaluate and if required update `pallet_assets`, `pallet_uniques`,
`pallet_nfts` docs
- [x] Absorb #2683
- [x] Audit individual trait method docs, and improve if possible

Feel free to suggest additional TODOs for this PR in the comments

---------

Co-authored-by: Bill Laboon <laboon@users.noreply.github.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Sebastian Kunert <skunert49@gmail.com>
  • Loading branch information
5 people authored Mar 31, 2024
1 parent 0d93248 commit 4125706
Show file tree
Hide file tree
Showing 28 changed files with 377 additions and 83 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,16 @@ pallet-aura = { path = "../../substrate/frame/aura", default-features = false }
pallet-timestamp = { path = "../../substrate/frame/timestamp" }
pallet-balances = { path = "../../substrate/frame/balances" }
pallet-assets = { path = "../../substrate/frame/assets" }
pallet-preimage = { path = "../../substrate/frame/preimage" }
pallet-transaction-payment = { path = "../../substrate/frame/transaction-payment" }
pallet-utility = { path = "../../substrate/frame/utility" }
pallet-multisig = { path = "../../substrate/frame/multisig" }
pallet-proxy = { path = "../../substrate/frame/proxy" }
pallet-authorship = { path = "../../substrate/frame/authorship" }
pallet-collective = { path = "../../substrate/frame/collective" }
pallet-democracy = { path = "../../substrate/frame/democracy" }
pallet-uniques = { path = "../../substrate/frame/uniques" }
pallet-nfts = { path = "../../substrate/frame/nfts" }
pallet-scheduler = { path = "../../substrate/frame/scheduler" }

# Primitives
Expand Down
8 changes: 0 additions & 8 deletions docs/sdk/src/reference_docs/frame_currency.rs

This file was deleted.

2 changes: 1 addition & 1 deletion docs/sdk/src/reference_docs/frame_pallet_coupling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
//! For example, all pallets in `polkadot-sdk` that needed to work with currencies could have been
//! tightly coupled with [`pallet_balances`]. But, `polkadot-sdk` also provides [`pallet_assets`]
//! (and more implementations by the community), therefore all pallets use traits to loosely couple
//! with balances or assets pallet. More on this in [`crate::reference_docs::frame_currency`].
//! with balances or assets pallet. More on this in [`crate::reference_docs::frame_tokens`].
//!
//! ## Further References
//!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@
//!
//! TODO: Link to multi block migration example/s once PR is merged (<https://github.com/paritytech/polkadot-sdk/pull/2119>).
//!
//! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion
//! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade
//! [`StorageVersion`]: frame_support::traits::StorageVersion
//! [`set_code`]: frame_system::Call::set_code
Expand Down
131 changes: 131 additions & 0 deletions docs/sdk/src/reference_docs/frame_tokens.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// This file is part of polkadot-sdk.
//
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # FRAME Tokens
//!
//! This reference doc serves as a high-level overview of the token-related logic in FRAME, and
//! how to properly apply it to your use case.
//!
//! On completion of reading this doc, you should have a good understanding of:
//! - The distinction between token traits and trait implementations in FRAME, and why this
//! distinction is helpful
//! - Token-related traits avaliable in FRAME
//! - Token-related trait implementations in FRAME
//! - How to choose the right trait or trait implementation for your use case
//! - Where to go next
//!
//! ## Getting Started
//!
//! The most ubiquitous way to add a token to a FRAME runtime is [`pallet_balances`]. Read
//! more about pallets [here](crate::polkadot_sdk::frame_runtime#pallets).
//!
//! You may then write custom pallets that interact with [`pallet_balances`]. The fastest way to
//! get started with that is by
//! [tightly coupling](crate::reference_docs::frame_pallet_coupling#tight-coupling-pallets) your
//! custom pallet to [`pallet_balances`].
//!
//! However, to keep pallets flexible and modular, it is often prefered to
//! [loosely couple](crate::reference_docs::frame_pallet_coupling#loosely--coupling-pallets).
//!
//! To achieve loose coupling,
//! we separate token logic into traits and trait implementations.
//!
//! ## Traits and Trait Implementations
//!
//! Broadly speaking, token logic in FRAME can be divided into two categories: traits and
//! trait implementations.
//!
//! **Traits** define common interfaces that types of tokens should implement. For example, the
//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`) trait specifies an interface
//! for *inspecting* token state such as the total issuance of the token, the balance of individual
//! accounts, etc.
//!
//! **Trait implementations** are concrete implementations of these traits. For example, one of the
//! many traits [`pallet_balances`] implements is
//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`)*. It provides the concrete way
//! of inspecting the total issuance, balance of accounts, etc. There can be many implementations of
//! the same traits.
//!
//! The distinction between traits and trait implementations is helpful because it allows pallets
//! and other logic to be generic over their dependencies, avoiding tight coupling.
//!
//! To illustrate this with an example let's consider [`pallet_preimage`]. This pallet takes a
//! deposit in exchange for storing a preimage for later use. A naive implementation of the
//! pallet may use [`pallet_balances`] in a tightly coupled manner, directly calling methods
//! on the pallet to reserve and unreserve deposits. This approach works well,
//! until someone has a use case requiring that an asset from a different pallet such as
//! [`pallet_assets`] is used for the deposit. Rather than tightly couple [`pallet_preimage`] to
//! [`pallet_balances`], [`pallet_assets`], and every other token-handling pallet a user
//! could possibly specify, [`pallet_preimage`] does not specify a concrete pallet as a dependency
//! but instead accepts any dependency which implements the
//! [`currency::ReservableCurrency`](`frame_support::traits::tokens::currency::ReservableCurrency`)
//! trait, namely via its [`Config::Currency`](`pallet_preimage::pallet::Config::Currency`)
//! associated type. This allows [`pallet_preimage`] to support any arbitrary pallet implementing
//! this trait, without needing any knowledge of what those pallets may be or requiring changes to
//! support new pallets which may be written in the future.
//!
//! Read more about coupling, and the benefits of loose coupling
//! [here](crate::reference_docs::frame_pallet_coupling).
//!
//! ##### *Rust Advanced Tip
//!
//! The knowledge that [`pallet_balances`] implements
//! [`fungible::Inspect`](`frame_support::traits::fungible::Inspect`) is not some arcane knowledge
//! that you have to know by heart or memorize. One can simply look at the list of the implementors
//! of any trait in the Rust Doc to find all implementors (e.g.
//! <https://paritytech.github.io/polkadot-sdk/master/frame/traits/tokens/fungible/trait.Mutate.html#implementors>),
//! or use the `rust-analyzer` `Implementations` action.
//!
//! ## Fungible Token Traits in FRAME
//!
//! The [`fungible`](`frame_support::traits::fungible`) crate contains the latest set of FRAME
//! fungible token traits, and is recommended to use for all new logic requiring a fungible token.
//! See the crate documentation for more info about these fungible traits.
//!
//! [`fungibles`](`frame_support::traits::fungibles`) provides very similar functionality to
//! [`fungible`](`frame_support::traits::fungible`), except it supports managing multiple tokens.
//!
//! You may notice the trait [`Currency`](`frame_support::traits::Currency`) with similar
//! functionality is also used in the codebase, however this trait is deprecated and existing logic
//! is in the process of being migrated to [`fungible`](`frame_support::traits::fungible`) ([tracking issue](https://github.com/paritytech/polkadot-sdk/issues/226)).
//!
//! ## Fungible Token Trait Implementations in FRAME
//!
//! [`pallet_balances`] implements [`fungible`](`frame_support::traits::fungible`), and is the most
//! commonly used fungible implementation in FRAME. Most of the time, it's used for managing the
//! native token of the blockchain network it's used in.
//!
//! [`pallet_assets`] implements [`fungibles`](`frame_support::traits::fungibles`), and is another
//! popular fungible token implementation. It supports the creation and management of multiple
//! assets in a single crate, making it a good choice when a network requires more assets in
//! addition to its native token.
//!
//! ## Non-Fungible Tokens in FRAME
//!
//! [`pallet_nfts`] is recommended to use for all NFT use cases in FRAME.
//! See the crate documentation for more info about this pallet.
//!
//! [`pallet_uniques`] is deprecated and should not be used.
//!
//!
//! # What Next?
//!
//! - If you are interested in implementing a single fungible token, continue reading the
//! [`fungible`](`frame_support::traits::fungible`) and [`pallet_balances`] docs.
//! - If you are interested in implementing a set of fungible tokens, continue reading the
//! [`fungibles`](`frame_support::traits::fungibles`) trait and [`pallet_assets`] docs.
//! - If you are interested in implementing an NFT, continue reading the [`pallet_nfts`] docs.
6 changes: 3 additions & 3 deletions docs/sdk/src/reference_docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,16 @@ pub mod metadata;
/// Learn about how frame-system handles `account-ids`, nonces, consumers and providers.
pub mod frame_system_accounts;

/// Learn about the currency-related abstractions provided in FRAME.
pub mod frame_currency;

/// Advice for configuring your development environment for Substrate development.
pub mod development_environment_advice;

/// Learn about benchmarking and weight.
// TODO: @shawntabrizi @ggwpez https://github.com/paritytech/polkadot-sdk-docs/issues/50
pub mod frame_benchmarking_weight;

/// Learn about the token-related logic in FRAME and how to apply it to your use case.
pub mod frame_tokens;

/// Learn about chain specification file and the genesis state of the blockchain.
// TODO: @michalkucharczyk https://github.com/paritytech/polkadot-sdk-docs/issues/51
pub mod chain_spec_genesis;
Expand Down
13 changes: 12 additions & 1 deletion substrate/frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@

//! # Assets Pallet
//!
//! A simple, secure module for dealing with fungible assets.
//! A simple, secure module for dealing with sets of assets implementing
//! [`fungible`](frame_support::traits::fungible) traits, via
//! [`fungibles`](frame_support::traits::fungibles) traits.
//!
//! The pallet makes heavy use of concepts such as Holds and Freezes from the
//! [`frame_support::traits::fungible`] traits, therefore you should read and understand those docs
//! as a prerequisite to understanding this pallet.
//!
//! See the [`frame_tokens`] reference docs for more information about the place of the
//! Assets pallet in FRAME.
//!
//! ## Overview
//!
Expand Down Expand Up @@ -133,6 +142,8 @@
//!
//! * [`System`](../frame_system/index.html)
//! * [`Support`](../frame_support/index.html)
//!
//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html

// This recursion limit is needed because we have too many benchmarks and benchmarking will fail if
// we add more without this limit.
Expand Down
83 changes: 39 additions & 44 deletions substrate/frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@

//! # Balances Pallet
//!
//! The Balances pallet provides functionality for handling accounts and balances.
//! The Balances pallet provides functionality for handling accounts and balances for a single
//! token.
//!
//! - [`Config`]
//! - [`Call`]
//! - [`Pallet`]
//! It makes heavy use of concepts such as Holds and Freezes from the
//! [`frame_support::traits::fungible`] traits, therefore you should read and understand those docs
//! as a prerequisite to understanding this pallet.
//!
//! Also see the [`frame_tokens`] reference docs for higher level information regarding the
//! place of this palet in FRAME.
//!
//! ## Overview
//!
Expand All @@ -38,42 +42,30 @@
//!
//! ### Terminology
//!
//! - **Existential Deposit:** The minimum balance required to create or keep an account open. This
//! prevents "dust accounts" from filling storage. When the free plus the reserved balance (i.e.
//! the total balance) fall below this, then the account is said to be dead; and it loses its
//! functionality as well as any prior history and all information on it is removed from the
//! chain's state. No account should ever have a total balance that is strictly between 0 and the
//! existential deposit (exclusive). If this ever happens, it indicates either a bug in this
//! pallet or an erroneous raw mutation of 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 total balance has become zero (or, strictly speaking, less than the Existential Deposit).
//!
//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only
//! balance that matters for most operations.
//! its total balance has become less than the Existential Deposit.
//!
//! - **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.
//!
//! - **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.)
//! ### Implementations
//!
//! - **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".
//! The Balances pallet provides implementations for the following [`fungible`] traits. If these
//! traits provide the functionality that you need, then you should avoid tight coupling with the
//! Balances pallet.
//!
//! ### Implementations
//! - [`fungible::Inspect`]
//! - [`fungible::Mutate`]
//! - [`fungible::Unbalanced`]
//! - [`fungible::Balanced`]
//! - [`fungible::BalancedHold`]
//! - [`fungible::InspectHold`]
//! - [`fungible::MutateHold`]
//! - [`fungible::InspectFreeze`]
//! - [`fungible::MutateFreeze`]
//! - [`fungible::Imbalance`]
//!
//! The Balances pallet provides implementations for the following traits. If these traits provide
//! the functionality that you need, then you can avoid coupling with the Balances pallet.
//! It also implements the following [`Currency`] related traits, however they are deprecated and
//! will eventually be removed.
//!
//! - [`Currency`]: Functions for dealing with a
//! fungible assets system.
//! - [`Currency`]: Functions for dealing with a fungible assets system.
//! - [`ReservableCurrency`]
//! - [`NamedReservableCurrency`](frame_support::traits::NamedReservableCurrency):
//! Functions for dealing with assets that can be reserved from an account.
Expand All @@ -83,14 +75,6 @@
//! 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).
//!
//! ## Interface
//!
//! ### Dispatchable Functions
//!
//! - `transfer_allow_death` - Transfer some liquid free balance to another account.
//! - `force_set_balance` - Set the balances of a given account. The origin of this call must be
//! root.
//!
//! ## Usage
//!
//! The following examples show how to use the Balances pallet in your custom pallet.
Expand Down Expand Up @@ -151,8 +135,11 @@
//! * Total issued balanced of all accounts should be less than `Config::Balance::max_value()`.
//! * Existential Deposit is set to a value greater than zero.
//!
//! Note, you may find the Balances pallet still functions with an ED of zero in some circumstances,
//! however this is not a configuration which is generally supported, nor will it be.
//! Note, you may find the Balances pallet still functions with an ED of zero when the
//! `insecure_zero_ed` cargo feature is enabled. However this is not a configuration which is
//! generally supported, nor will it be.
//!
//! [`frame_tokens`]: ../polkadot_sdk_docs/reference_docs/frame_tokens/index.html

#![cfg_attr(not(feature = "std"), no_std)]
mod benchmarking;
Expand Down Expand Up @@ -308,10 +295,14 @@ pub mod pallet {

/// The maximum number of locks that should exist on an account.
/// Not strictly enforced, but used for weight estimation.
///
/// Use of locks is deprecated in favour of freezes. See `https://github.com/paritytech/substrate/pull/12951/`
#[pallet::constant]
type MaxLocks: Get<u32>;

/// The maximum number of named reserves that can exist on an account.
///
/// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/`
#[pallet::constant]
type MaxReserves: Get<u32>;

Expand Down Expand Up @@ -455,6 +446,8 @@ pub mod pallet {

/// Any liquidity locks on some account balances.
/// NOTE: Should only be accessed when setting, changing and freeing a lock.
///
/// Use of locks is deprecated in favour of freezes. See `https://github.com/paritytech/substrate/pull/12951/`
#[pallet::storage]
#[pallet::getter(fn locks)]
pub type Locks<T: Config<I>, I: 'static = ()> = StorageMap<
Expand All @@ -466,6 +459,8 @@ pub mod pallet {
>;

/// Named reserves on some account balances.
///
/// Use of reserves is deprecated in favour of holds. See `https://github.com/paritytech/substrate/pull/12951/`
#[pallet::storage]
#[pallet::getter(fn reserves)]
pub type Reserves<T: Config<I>, I: 'static = ()> = StorageMap<
Expand Down
3 changes: 3 additions & 0 deletions substrate/frame/support/src/traits/tokens/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
// limitations under the License.

//! The Currency trait and associated types.
//!
//! Note Currency and related traits are deprecated, instead
//! [`fungible`](frame_support::traits::fungible) traits should be used.

use super::{
imbalance::{Imbalance, SignedImbalance},
Expand Down
Loading

0 comments on commit 4125706

Please sign in to comment.