referrerpolicy=no-referrer-when-downgrade

pallet_staking/
asset.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Contains all the interactions with [`Config::Currency`] to manipulate the underlying staking
19//! asset.
20
21use crate::{BalanceOf, Config, HoldReason, NegativeImbalanceOf, PositiveImbalanceOf};
22use frame_support::traits::{
23	fungible::{
24		hold::{Balanced as FunHoldBalanced, Inspect as FunHoldInspect, Mutate as FunHoldMutate},
25		Balanced, Inspect as FunInspect,
26	},
27	tokens::{Fortitude, Precision, Preservation},
28};
29use sp_runtime::{DispatchResult, Saturating};
30
31/// Existential deposit for the chain.
32pub fn existential_deposit<T: Config>() -> BalanceOf<T> {
33	T::Currency::minimum_balance()
34}
35
36/// Total issuance of the chain.
37pub fn total_issuance<T: Config>() -> BalanceOf<T> {
38	T::Currency::total_issuance()
39}
40
41/// Total balance of `who`. Includes both free and staked.
42pub fn total_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
43	T::Currency::total_balance(who)
44}
45
46/// Stakeable balance of `who`.
47///
48/// This includes balance free to stake along with any balance that is already staked.
49pub fn stakeable_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
50	free_to_stake::<T>(who).saturating_add(staked::<T>(who))
51}
52
53/// Balance of `who` that is currently at stake.
54///
55/// The staked amount is on hold and cannot be transferred out of `who`s account.
56pub fn staked<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
57	T::Currency::balance_on_hold(&HoldReason::Staking.into(), who)
58}
59
60/// Balance of who that can be staked additionally.
61///
62/// Does not include the current stake.
63pub fn free_to_stake<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
64	// since we want to be able to use frozen funds for staking, we force the reduction.
65	T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Force)
66}
67
68/// Set balance that can be staked for `who`.
69///
70/// If `Value` is lower than the current staked balance, the difference is unlocked.
71///
72/// Should only be used with test.
73#[cfg(any(test, feature = "runtime-benchmarks"))]
74pub fn set_stakeable_balance<T: Config>(who: &T::AccountId, value: BalanceOf<T>) {
75	use frame_support::traits::fungible::Mutate;
76
77	// minimum free balance (non-staked) required to keep the account alive.
78	let ed = existential_deposit::<T>();
79	// currently on stake
80	let staked_balance = staked::<T>(who);
81
82	// if new value is greater than staked balance, mint some free balance.
83	if value > staked_balance {
84		let _ = T::Currency::set_balance(who, value - staked_balance + ed);
85	} else {
86		// else reduce the staked balance.
87		update_stake::<T>(who, value).expect("can remove from what is staked");
88		// burn all free, only leaving ED.
89		let _ = T::Currency::set_balance(who, ed);
90	}
91
92	// ensure new stakeable balance same as desired `value`.
93	assert_eq!(stakeable_balance::<T>(who), value);
94}
95
96/// Update `amount` at stake for `who`.
97///
98/// Overwrites the existing stake amount. If passed amount is lower than the existing stake, the
99/// difference is unlocked.
100pub fn update_stake<T: Config>(who: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {
101	T::Currency::set_on_hold(&HoldReason::Staking.into(), who, amount)
102}
103
104/// Release all staked amount to `who`.
105///
106/// Fails if there are consumers left on `who` that restricts it from being reaped.
107pub fn kill_stake<T: Config>(who: &T::AccountId) -> DispatchResult {
108	T::Currency::release_all(&HoldReason::Staking.into(), who, Precision::BestEffort).map(|_| ())
109}
110
111/// Slash the value from `who`.
112///
113/// A negative imbalance is returned which can be resolved to deposit the slashed value.
114pub fn slash<T: Config>(
115	who: &T::AccountId,
116	value: BalanceOf<T>,
117) -> (NegativeImbalanceOf<T>, BalanceOf<T>) {
118	T::Currency::slash(&HoldReason::Staking.into(), who, value)
119}
120
121/// Mint `value` into an existing account `who`.
122///
123/// This does not increase the total issuance.
124pub fn mint_into_existing<T: Config>(
125	who: &T::AccountId,
126	value: BalanceOf<T>,
127) -> Option<PositiveImbalanceOf<T>> {
128	// since the account already exists, we mint exact value even if value is below ED.
129	T::Currency::deposit(who, value, Precision::Exact).ok()
130}
131
132/// Mint `value` and create account for `who` if it does not exist.
133///
134/// If value is below existential deposit, the account is not created.
135///
136/// Note: This does not increase the total issuance.
137pub fn mint_creating<T: Config>(who: &T::AccountId, value: BalanceOf<T>) -> PositiveImbalanceOf<T> {
138	T::Currency::deposit(who, value, Precision::BestEffort).unwrap_or_default()
139}
140
141/// Deposit newly issued or slashed `value` into `who`.
142pub fn deposit_slashed<T: Config>(who: &T::AccountId, value: NegativeImbalanceOf<T>) {
143	let _ = T::Currency::resolve(who, value);
144}
145
146/// Issue `value` increasing total issuance.
147///
148/// Creates a negative imbalance.
149pub fn issue<T: Config>(value: BalanceOf<T>) -> NegativeImbalanceOf<T> {
150	T::Currency::issue(value)
151}
152
153/// Burn the amount from the total issuance.
154#[cfg(feature = "runtime-benchmarks")]
155pub fn burn<T: Config>(amount: BalanceOf<T>) -> PositiveImbalanceOf<T> {
156	T::Currency::rescind(amount)
157}