referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/fungibles/
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::fungibles`] doc for more information about fungibles traits.
22
23use super::*;
24use crate::traits::{
25	fungible,
26	misc::{SameOrOther, TryDrop},
27	tokens::{
28		imbalance::{Imbalance as ImbalanceT, TryMerge},
29		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<AssetId, Balance> {
39	fn handle(asset: AssetId, amount: Balance);
40}
41
42/// An imbalance in the system, representing a divergence of recorded token supply from the sum of
43/// the balances of all accounts. This is `must_use` in order to ensure it gets handled (placing
44/// into an account, settling from an account or altering the supply).
45///
46/// Importantly, it has a special `Drop` impl, and cannot be created outside of this module.
47#[must_use]
48#[derive(EqNoBound, PartialEqNoBound, RuntimeDebugNoBound)]
49pub struct Imbalance<
50	A: AssetId,
51	B: Balance,
52	OnDrop: HandleImbalanceDrop<A, B>,
53	OppositeOnDrop: HandleImbalanceDrop<A, B>,
54> {
55	asset: A,
56	amount: B,
57	_phantom: PhantomData<(OnDrop, OppositeOnDrop)>,
58}
59
60impl<
61		A: AssetId,
62		B: Balance,
63		OnDrop: HandleImbalanceDrop<A, B>,
64		OppositeOnDrop: HandleImbalanceDrop<A, B>,
65	> Drop for Imbalance<A, B, OnDrop, OppositeOnDrop>
66{
67	fn drop(&mut self) {
68		if !self.amount.is_zero() {
69			OnDrop::handle(self.asset.clone(), self.amount)
70		}
71	}
72}
73
74impl<
75		A: AssetId,
76		B: Balance,
77		OnDrop: HandleImbalanceDrop<A, B>,
78		OppositeOnDrop: HandleImbalanceDrop<A, B>,
79	> TryDrop for Imbalance<A, B, OnDrop, OppositeOnDrop>
80{
81	/// Drop an instance cleanly. Only works if its value represents "no-operation".
82	fn try_drop(self) -> Result<(), Self> {
83		self.drop_zero()
84	}
85}
86
87impl<
88		A: AssetId,
89		B: Balance,
90		OnDrop: HandleImbalanceDrop<A, B>,
91		OppositeOnDrop: HandleImbalanceDrop<A, B>,
92	> Imbalance<A, B, OnDrop, OppositeOnDrop>
93{
94	pub fn zero(asset: A) -> Self {
95		Self { asset, amount: Zero::zero(), _phantom: PhantomData }
96	}
97
98	pub(crate) fn new(asset: A, amount: B) -> Self {
99		Self { asset, amount, _phantom: PhantomData }
100	}
101
102	/// Forget the imbalance without invoking the on-drop handler.
103	pub(crate) fn forget(imbalance: Self) {
104		core::mem::forget(imbalance);
105	}
106
107	pub fn drop_zero(self) -> Result<(), Self> {
108		if self.amount.is_zero() {
109			core::mem::forget(self);
110			Ok(())
111		} else {
112			Err(self)
113		}
114	}
115
116	pub fn split(self, amount: B) -> (Self, Self) {
117		let first = self.amount.min(amount);
118		let second = self.amount - first;
119		let asset = self.asset.clone();
120		core::mem::forget(self);
121		(Imbalance::new(asset.clone(), first), Imbalance::new(asset, second))
122	}
123
124	/// Mutate `self` by extracting a new instance with at most `amount` value, reducing `self`
125	/// accordingly.
126	pub fn extract(&mut self, amount: B) -> Self {
127		let new = self.amount.min(amount);
128		self.amount = self.amount - new;
129		Imbalance::new(self.asset.clone(), new)
130	}
131
132	pub fn merge(mut self, other: Self) -> Result<Self, (Self, Self)> {
133		if self.asset == other.asset {
134			self.amount = self.amount.saturating_add(other.amount);
135			core::mem::forget(other);
136			Ok(self)
137		} else {
138			Err((self, other))
139		}
140	}
141	pub fn subsume(&mut self, other: Self) -> Result<(), Self> {
142		if self.asset == other.asset {
143			self.amount = self.amount.saturating_add(other.amount);
144			core::mem::forget(other);
145			Ok(())
146		} else {
147			Err(other)
148		}
149	}
150	pub fn offset(
151		self,
152		other: Imbalance<A, B, OppositeOnDrop, OnDrop>,
153	) -> Result<
154		SameOrOther<Self, Imbalance<A, B, OppositeOnDrop, OnDrop>>,
155		(Self, Imbalance<A, B, OppositeOnDrop, OnDrop>),
156	> {
157		if self.asset == other.asset {
158			let (a, b) = (self.amount, other.amount);
159			let asset = self.asset.clone();
160			core::mem::forget((self, other));
161
162			if a == b {
163				Ok(SameOrOther::None)
164			} else if a > b {
165				Ok(SameOrOther::Same(Imbalance::new(asset, a - b)))
166			} else {
167				Ok(SameOrOther::Other(Imbalance::<A, B, OppositeOnDrop, OnDrop>::new(asset, b - a)))
168			}
169		} else {
170			Err((self, other))
171		}
172	}
173	pub fn peek(&self) -> B {
174		self.amount
175	}
176
177	pub fn asset(&self) -> A {
178		self.asset.clone()
179	}
180}
181
182impl<
183		A: AssetId,
184		B: Balance,
185		OnDrop: HandleImbalanceDrop<A, B>,
186		OppositeOnDrop: HandleImbalanceDrop<A, B>,
187	> TryMerge for Imbalance<A, B, OnDrop, OppositeOnDrop>
188{
189	fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
190		self.merge(other)
191	}
192}
193
194/// Converts a `fungible` `imbalance` instance to an instance of a `fungibles` imbalance type using
195/// a specified `asset`.
196///
197/// This function facilitates imbalance conversions within the implementations of
198/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
199/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
200/// within the current crate.
201pub(crate) fn from_fungible<
202	A: AssetId,
203	B: Balance,
204	OnDropIn: fungible::HandleImbalanceDrop<B>,
205	OppositeIn: fungible::HandleImbalanceDrop<B>,
206	OnDropOut: HandleImbalanceDrop<A, B>,
207	OppositeOut: HandleImbalanceDrop<A, B>,
208>(
209	imbalance: fungible::Imbalance<B, OnDropIn, OppositeIn>,
210	asset: A,
211) -> Imbalance<A, B, OnDropOut, OppositeOut> {
212	let new = Imbalance::new(asset, imbalance.peek());
213	fungible::Imbalance::forget(imbalance);
214	new
215}
216
217/// Converts a `fungibles` `imbalance` instance of one type to another using a specified `asset`.
218///
219/// This function facilitates imbalance conversions within the implementations of
220/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
221/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
222/// within the current crate.
223pub(crate) fn from_fungibles<
224	A: AssetId,
225	B: Balance,
226	OnDropIn: HandleImbalanceDrop<A, B>,
227	OppositeIn: HandleImbalanceDrop<A, B>,
228	AssetOut: AssetId,
229	OnDropOut: HandleImbalanceDrop<AssetOut, B>,
230	OppositeOut: HandleImbalanceDrop<AssetOut, B>,
231>(
232	imbalance: Imbalance<A, B, OnDropIn, OppositeIn>,
233	asset: AssetOut,
234) -> Imbalance<AssetOut, B, OnDropOut, OppositeOut> {
235	let new = Imbalance::new(asset, imbalance.peek());
236	Imbalance::forget(imbalance);
237	new
238}
239
240/// Imbalance implying that the total_issuance value is less than the sum of all account balances.
241pub type Debt<AccountId, B> = Imbalance<
242	<B as Inspect<AccountId>>::AssetId,
243	<B as Inspect<AccountId>>::Balance,
244	// This will generally be implemented by increasing the total_issuance value.
245	<B as Balanced<AccountId>>::OnDropDebt,
246	<B as Balanced<AccountId>>::OnDropCredit,
247>;
248
249/// Imbalance implying that the total_issuance value is greater than the sum of all account
250/// balances.
251pub type Credit<AccountId, B> = Imbalance<
252	<B as Inspect<AccountId>>::AssetId,
253	<B as Inspect<AccountId>>::Balance,
254	// This will generally be implemented by decreasing the total_issuance value.
255	<B as Balanced<AccountId>>::OnDropCredit,
256	<B as Balanced<AccountId>>::OnDropDebt,
257>;