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