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