referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/fungible/
imbalance.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//! The imbalance type and its associates, which handles keeps everything adding up properly with
19//! unbalanced operations.
20//!
21//! See the [`crate::traits::fungible`] doc for more information about fungible traits.
22
23use super::{super::Imbalance as ImbalanceT, Balanced, *};
24use crate::traits::{
25	fungibles,
26	misc::{SameOrOther, TryDrop},
27	tokens::{imbalance::TryMerge, AssetId, Balance},
28};
29use core::marker::PhantomData;
30use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound};
31use sp_runtime::traits::Zero;
32
33/// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or
34/// debt (positive) imbalance.
35pub trait HandleImbalanceDrop<Balance> {
36	/// Some something with the imbalance's value which is being dropped.
37	fn handle(amount: Balance);
38}
39
40impl<Balance> HandleImbalanceDrop<Balance> for () {
41	fn handle(_: Balance) {}
42}
43
44/// An imbalance in the system, representing a divergence of recorded token supply from the sum of
45/// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing
46/// into an account, settling from an account or altering the supply).
47///
48/// Importantly, it has a special `Drop` impl, and cannot be created outside of this module.
49#[must_use]
50#[derive(EqNoBound, PartialEqNoBound, RuntimeDebugNoBound)]
51pub struct Imbalance<
52	B: Balance,
53	OnDrop: HandleImbalanceDrop<B>,
54	OppositeOnDrop: HandleImbalanceDrop<B>,
55> {
56	amount: B,
57	_phantom: PhantomData<(OnDrop, OppositeOnDrop)>,
58}
59
60impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>> Drop
61	for Imbalance<B, OnDrop, OppositeOnDrop>
62{
63	fn drop(&mut self) {
64		if !self.amount.is_zero() {
65			OnDrop::handle(self.amount)
66		}
67	}
68}
69
70impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>> TryDrop
71	for Imbalance<B, OnDrop, OppositeOnDrop>
72{
73	/// Drop an instance cleanly. Only works if its value represents "no-operation".
74	fn try_drop(self) -> Result<(), Self> {
75		self.drop_zero()
76	}
77}
78
79impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>> Default
80	for Imbalance<B, OnDrop, OppositeOnDrop>
81{
82	fn default() -> Self {
83		Self::zero()
84	}
85}
86
87impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>>
88	Imbalance<B, OnDrop, OppositeOnDrop>
89{
90	pub(crate) fn new(amount: B) -> Self {
91		Self { amount, _phantom: PhantomData }
92	}
93
94	/// Forget the imbalance without invoking the on-drop handler.
95	pub(crate) fn forget(imbalance: Self) {
96		core::mem::forget(imbalance);
97	}
98}
99
100impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>>
101	ImbalanceT<B> for Imbalance<B, OnDrop, OppositeOnDrop>
102{
103	type Opposite = Imbalance<B, OppositeOnDrop, OnDrop>;
104
105	fn zero() -> Self {
106		Self { amount: Zero::zero(), _phantom: PhantomData }
107	}
108
109	fn drop_zero(self) -> Result<(), Self> {
110		if self.amount.is_zero() {
111			core::mem::forget(self);
112			Ok(())
113		} else {
114			Err(self)
115		}
116	}
117
118	fn split(self, amount: B) -> (Self, Self) {
119		let first = self.amount.min(amount);
120		let second = self.amount - first;
121		core::mem::forget(self);
122		(Imbalance::new(first), Imbalance::new(second))
123	}
124
125	fn extract(&mut self, amount: B) -> Self {
126		let new = self.amount.min(amount);
127		self.amount = self.amount - new;
128		Imbalance::new(new)
129	}
130
131	fn merge(mut self, other: Self) -> Self {
132		self.amount = self.amount.saturating_add(other.amount);
133		core::mem::forget(other);
134		self
135	}
136	fn subsume(&mut self, other: Self) {
137		self.amount = self.amount.saturating_add(other.amount);
138		core::mem::forget(other);
139	}
140	fn offset(
141		self,
142		other: Imbalance<B, OppositeOnDrop, OnDrop>,
143	) -> SameOrOther<Self, Imbalance<B, OppositeOnDrop, OnDrop>> {
144		let (a, b) = (self.amount, other.amount);
145		core::mem::forget((self, other));
146
147		if a == b {
148			SameOrOther::None
149		} else if a > b {
150			SameOrOther::Same(Imbalance::new(a - b))
151		} else {
152			SameOrOther::Other(Imbalance::<B, OppositeOnDrop, OnDrop>::new(b - a))
153		}
154	}
155	fn peek(&self) -> B {
156		self.amount
157	}
158}
159
160impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>> TryMerge
161	for Imbalance<B, OnDrop, OppositeOnDrop>
162{
163	fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
164		Ok(self.merge(other))
165	}
166}
167
168/// Converts a `fungibles` `imbalance` instance to an instance of a `fungible` imbalance type.
169///
170/// This function facilitates imbalance conversions within the implementations of
171/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
172/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
173/// within the current crate.
174pub(crate) fn from_fungibles<
175	A: AssetId,
176	B: Balance,
177	OnDropIn: fungibles::HandleImbalanceDrop<A, B>,
178	OppositeIn: fungibles::HandleImbalanceDrop<A, B>,
179	OnDropOut: HandleImbalanceDrop<B>,
180	OppositeOut: HandleImbalanceDrop<B>,
181>(
182	imbalance: fungibles::Imbalance<A, B, OnDropIn, OppositeIn>,
183) -> Imbalance<B, OnDropOut, OppositeOut> {
184	let new = Imbalance::new(imbalance.peek());
185	fungibles::Imbalance::forget(imbalance);
186	new
187}
188
189/// Imbalance implying that the total_issuance value is less than the sum of all account balances.
190pub type Debt<AccountId, B> = Imbalance<
191	<B as Inspect<AccountId>>::Balance,
192	// This will generally be implemented by increasing the total_issuance value.
193	<B as Balanced<AccountId>>::OnDropDebt,
194	<B as Balanced<AccountId>>::OnDropCredit,
195>;
196
197/// Imbalance implying that the total_issuance value is greater than the sum of all account
198/// balances.
199pub type Credit<AccountId, B> = Imbalance<
200	<B as Inspect<AccountId>>::Balance,
201	// This will generally be implemented by decreasing the total_issuance value.
202	<B as Balanced<AccountId>>::OnDropCredit,
203	<B as Balanced<AccountId>>::OnDropDebt,
204>;