1use 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
41pub trait HandleImbalanceDrop<AssetId, Balance> {
44 fn handle(asset: AssetId, amount: Balance);
45}
46
47#[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 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 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 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
249pub(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
272pub(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
295pub type Debt<AccountId, B> = Imbalance<
297 <B as Inspect<AccountId>>::AssetId,
298 <B as Inspect<AccountId>>::Balance,
299 <B as Balanced<AccountId>>::OnDropDebt,
301 <B as Balanced<AccountId>>::OnDropCredit,
302>;
303
304pub type Credit<AccountId, B> = Imbalance<
307 <B as Inspect<AccountId>>::AssetId,
308 <B as Inspect<AccountId>>::Balance,
309 <B as Balanced<AccountId>>::OnDropCredit,
311 <B as Balanced<AccountId>>::OnDropDebt,
312>;