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::{
29			Imbalance as ImbalanceT, ImbalanceAccounting, TryMerge, UnsafeConstructorDestructor,
30			UnsafeManualAccounting,
31		},
32		AssetId, Balance,
33	},
34};
35use alloc::boxed::Box;
36use core::marker::PhantomData;
37use frame_support_procedural::{DebugNoBound, EqNoBound, PartialEqNoBound};
38use sp_arithmetic::traits::SaturatedConversion;
39use sp_runtime::traits::Zero;
40
41/// Handler for when an imbalance gets dropped. This could handle either a credit (negative) or
42/// debt (positive) imbalance.
43pub trait HandleImbalanceDrop<AssetId, Balance> {
44	fn handle(asset: AssetId, amount: 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(EqNoBound, PartialEqNoBound, DebugNoBound)]
54pub struct Imbalance<
55	A: AssetId,
56	B: Balance,
57	OnDrop: HandleImbalanceDrop<A, B>,
58	OppositeOnDrop: HandleImbalanceDrop<A, B>,
59> {
60	asset: A,
61	amount: B,
62	_phantom: PhantomData<(OnDrop, OppositeOnDrop)>,
63}
64
65impl<
66		A: AssetId,
67		B: Balance,
68		OnDrop: HandleImbalanceDrop<A, B>,
69		OppositeOnDrop: HandleImbalanceDrop<A, B>,
70	> Drop for Imbalance<A, B, OnDrop, OppositeOnDrop>
71{
72	fn drop(&mut self) {
73		if !self.amount.is_zero() {
74			OnDrop::handle(self.asset.clone(), self.amount)
75		}
76	}
77}
78
79impl<
80		A: AssetId,
81		B: Balance,
82		OnDrop: HandleImbalanceDrop<A, B>,
83		OppositeOnDrop: HandleImbalanceDrop<A, B>,
84	> TryDrop for Imbalance<A, 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<
93		A: AssetId,
94		B: Balance,
95		OnDrop: HandleImbalanceDrop<A, B>,
96		OppositeOnDrop: HandleImbalanceDrop<A, B>,
97	> Imbalance<A, B, OnDrop, OppositeOnDrop>
98{
99	pub fn zero(asset: A) -> Self {
100		Self { asset, amount: Zero::zero(), _phantom: PhantomData }
101	}
102
103	pub(crate) fn new(asset: A, amount: B) -> Self {
104		Self { asset, 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	pub fn drop_zero(self) -> Result<(), Self> {
113		if self.amount.is_zero() {
114			core::mem::forget(self);
115			Ok(())
116		} else {
117			Err(self)
118		}
119	}
120
121	pub fn split(self, amount: B) -> (Self, Self) {
122		let first = self.amount.min(amount);
123		let second = self.amount - first;
124		let asset = self.asset.clone();
125		core::mem::forget(self);
126		(Imbalance::new(asset.clone(), first), Imbalance::new(asset, second))
127	}
128
129	/// Mutate `self` by extracting a new instance with at most `amount` value, reducing `self`
130	/// accordingly.
131	pub fn extract(&mut self, amount: B) -> Self {
132		let new = self.amount.min(amount);
133		self.amount = self.amount - new;
134		Imbalance::new(self.asset.clone(), new)
135	}
136
137	pub fn merge(mut self, other: Self) -> Result<Self, (Self, Self)> {
138		if self.asset == other.asset {
139			self.amount = self.amount.saturating_add(other.amount);
140			core::mem::forget(other);
141			Ok(self)
142		} else {
143			Err((self, other))
144		}
145	}
146	pub fn subsume(&mut self, other: Self) -> Result<(), Self> {
147		if self.asset == other.asset {
148			self.amount = self.amount.saturating_add(other.amount);
149			core::mem::forget(other);
150			Ok(())
151		} else {
152			Err(other)
153		}
154	}
155	pub fn offset(
156		self,
157		other: Imbalance<A, B, OppositeOnDrop, OnDrop>,
158	) -> Result<
159		SameOrOther<Self, Imbalance<A, B, OppositeOnDrop, OnDrop>>,
160		(Self, Imbalance<A, B, OppositeOnDrop, OnDrop>),
161	> {
162		if self.asset == other.asset {
163			let (a, b) = (self.amount, other.amount);
164			let asset = self.asset.clone();
165			core::mem::forget((self, other));
166
167			if a == b {
168				Ok(SameOrOther::None)
169			} else if a > b {
170				Ok(SameOrOther::Same(Imbalance::new(asset, a - b)))
171			} else {
172				Ok(SameOrOther::Other(Imbalance::<A, B, OppositeOnDrop, OnDrop>::new(asset, b - a)))
173			}
174		} else {
175			Err((self, other))
176		}
177	}
178	pub fn peek(&self) -> B {
179		self.amount
180	}
181
182	pub fn asset(&self) -> A {
183		self.asset.clone()
184	}
185}
186
187impl<
188		A: AssetId,
189		B: Balance,
190		OnDrop: HandleImbalanceDrop<A, B>,
191		OppositeOnDrop: HandleImbalanceDrop<A, B>,
192	> TryMerge for Imbalance<A, B, OnDrop, OppositeOnDrop>
193{
194	fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> {
195		self.merge(other)
196	}
197}
198
199impl<
200		A: AssetId + 'static,
201		B: Balance + 'static,
202		OnDrop: HandleImbalanceDrop<A, B> + 'static,
203		OppositeOnDrop: HandleImbalanceDrop<A, B> + 'static,
204	> UnsafeConstructorDestructor<u128> for Imbalance<A, B, OnDrop, OppositeOnDrop>
205{
206	fn unsafe_clone(&self) -> Box<dyn ImbalanceAccounting<u128>> {
207		let clone = Self {
208			asset: self.asset.clone(),
209			amount: self.amount,
210			_phantom: PhantomData::default(),
211		};
212		Box::new(clone)
213	}
214	fn forget_imbalance(&mut self) -> u128 {
215		let amount = self.amount.saturated_into();
216		self.amount = 0u128.saturated_into();
217		amount
218	}
219}
220
221impl<
222		A: AssetId + 'static,
223		B: Balance + 'static,
224		OnDrop: HandleImbalanceDrop<A, B> + 'static,
225		OppositeOnDrop: HandleImbalanceDrop<A, B> + 'static,
226	> UnsafeManualAccounting<u128> for Imbalance<A, B, OnDrop, OppositeOnDrop>
227{
228	fn saturating_subsume(&mut self, mut other: Box<dyn ImbalanceAccounting<u128>>) {
229		let amount = other.forget_imbalance();
230		self.amount = self.amount.saturating_add(amount.saturated_into());
231	}
232}
233
234impl<
235		A: AssetId + 'static,
236		B: Balance + 'static,
237		OnDrop: HandleImbalanceDrop<A, B> + 'static,
238		OppositeOnDrop: HandleImbalanceDrop<A, B> + 'static,
239	> ImbalanceAccounting<u128> for Imbalance<A, B, OnDrop, OppositeOnDrop>
240{
241	fn amount(&self) -> u128 {
242		self.peek().saturated_into()
243	}
244	fn saturating_take(&mut self, amount: u128) -> Box<dyn ImbalanceAccounting<u128>> {
245		Box::new(self.extract(amount.saturated_into()))
246	}
247}
248
249/// Converts a `fungible` `imbalance` instance to an instance of a `fungibles` imbalance type using
250/// a specified `asset`.
251///
252/// This function facilitates imbalance conversions within the implementations of
253/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
254/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
255/// within the current crate.
256pub(crate) fn from_fungible<
257	A: AssetId,
258	B: Balance,
259	OnDropIn: fungible::HandleImbalanceDrop<B>,
260	OppositeIn: fungible::HandleImbalanceDrop<B>,
261	OnDropOut: HandleImbalanceDrop<A, B>,
262	OppositeOut: HandleImbalanceDrop<A, B>,
263>(
264	imbalance: fungible::Imbalance<B, OnDropIn, OppositeIn>,
265	asset: A,
266) -> Imbalance<A, B, OnDropOut, OppositeOut> {
267	let new = Imbalance::new(asset, imbalance.peek());
268	fungible::Imbalance::forget(imbalance);
269	new
270}
271
272/// Converts a `fungibles` `imbalance` instance of one type to another using a specified `asset`.
273///
274/// This function facilitates imbalance conversions within the implementations of
275/// [`frame_support::traits::fungibles::UnionOf`], [`frame_support::traits::fungible::UnionOf`], and
276/// [`frame_support::traits::fungible::ItemOf`] adapters. It is intended only for internal use
277/// within the current crate.
278pub(crate) fn from_fungibles<
279	A: AssetId,
280	B: Balance,
281	OnDropIn: HandleImbalanceDrop<A, B>,
282	OppositeIn: HandleImbalanceDrop<A, B>,
283	AssetOut: AssetId,
284	OnDropOut: HandleImbalanceDrop<AssetOut, B>,
285	OppositeOut: HandleImbalanceDrop<AssetOut, B>,
286>(
287	imbalance: Imbalance<A, B, OnDropIn, OppositeIn>,
288	asset: AssetOut,
289) -> Imbalance<AssetOut, B, OnDropOut, OppositeOut> {
290	let new = Imbalance::new(asset, imbalance.peek());
291	Imbalance::forget(imbalance);
292	new
293}
294
295/// Imbalance implying that the total_issuance value is less than the sum of all account balances.
296pub type Debt<AccountId, B> = Imbalance<
297	<B as Inspect<AccountId>>::AssetId,
298	<B as Inspect<AccountId>>::Balance,
299	// This will generally be implemented by increasing the total_issuance value.
300	<B as Balanced<AccountId>>::OnDropDebt,
301	<B as Balanced<AccountId>>::OnDropCredit,
302>;
303
304/// Imbalance implying that the total_issuance value is greater than the sum of all account
305/// balances.
306pub type Credit<AccountId, B> = Imbalance<
307	<B as Inspect<AccountId>>::AssetId,
308	<B as Inspect<AccountId>>::Balance,
309	// This will generally be implemented by decreasing the total_issuance value.
310	<B as Balanced<AccountId>>::OnDropCredit,
311	<B as Balanced<AccountId>>::OnDropDebt,
312>;