frame_support/traits/tokens/fungible/
union_of.rs

1// This file is part of Substrate.
2
3// Copyright (Criterion) 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//! Types to combine some `fungible::*` and `fungibles::*` implementations into one union
19//! `fungibles::*` implementation.
20//!
21//! See the [`crate::traits::fungible`] doc for more information about fungible traits.
22
23use codec::{Decode, Encode, MaxEncodedLen};
24use core::cmp::Ordering;
25use frame_support::traits::{
26	fungible::imbalance,
27	tokens::{
28		fungible, fungibles, AssetId, DepositConsequence, Fortitude, Precision, Preservation,
29		Provenance, Restriction, WithdrawConsequence,
30	},
31	AccountTouch,
32};
33use scale_info::TypeInfo;
34use sp_runtime::{
35	traits::Convert,
36	DispatchError, DispatchResult, Either,
37	Either::{Left, Right},
38	RuntimeDebug,
39};
40
41/// The `NativeOrWithId` enum classifies an asset as either `Native` to the current chain or as an
42/// asset with a specific ID.
43#[derive(Decode, Encode, Default, MaxEncodedLen, TypeInfo, Clone, RuntimeDebug, Eq)]
44pub enum NativeOrWithId<AssetId>
45where
46	AssetId: Ord,
47{
48	/// Represents the native asset of the current chain.
49	///
50	/// E.g., DOT for the Polkadot Asset Hub.
51	#[default]
52	Native,
53	/// Represents an asset identified by its underlying `AssetId`.
54	WithId(AssetId),
55}
56impl<AssetId: Ord> From<AssetId> for NativeOrWithId<AssetId> {
57	fn from(asset: AssetId) -> Self {
58		Self::WithId(asset)
59	}
60}
61impl<AssetId: Ord> Ord for NativeOrWithId<AssetId> {
62	fn cmp(&self, other: &Self) -> Ordering {
63		match (self, other) {
64			(Self::Native, Self::Native) => Ordering::Equal,
65			(Self::Native, Self::WithId(_)) => Ordering::Less,
66			(Self::WithId(_), Self::Native) => Ordering::Greater,
67			(Self::WithId(id1), Self::WithId(id2)) => <AssetId as Ord>::cmp(id1, id2),
68		}
69	}
70}
71impl<AssetId: Ord> PartialOrd for NativeOrWithId<AssetId> {
72	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
73		Some(<Self as Ord>::cmp(self, other))
74	}
75}
76impl<AssetId: Ord> PartialEq for NativeOrWithId<AssetId> {
77	fn eq(&self, other: &Self) -> bool {
78		self.cmp(other) == Ordering::Equal
79	}
80}
81
82/// Criterion for [`UnionOf`] where a set for [`NativeOrWithId::Native`] asset located from the left
83/// and for [`NativeOrWithId::WithId`] from the right.
84pub struct NativeFromLeft;
85impl<AssetId: Ord> Convert<NativeOrWithId<AssetId>, Either<(), AssetId>> for NativeFromLeft {
86	fn convert(asset: NativeOrWithId<AssetId>) -> Either<(), AssetId> {
87		match asset {
88			NativeOrWithId::Native => Either::Left(()),
89			NativeOrWithId::WithId(id) => Either::Right(id),
90		}
91	}
92}
93
94/// Type to combine some `fungible::*` and `fungibles::*` implementations into one union
95/// `fungibles::*` implementation.
96///
97/// ### Parameters:
98/// - `Left` is `fungible::*` implementation that is incorporated into the resulting union.
99/// - `Right` is `fungibles::*` implementation that is incorporated into the resulting union.
100/// - `Criterion` determines whether the `AssetKind` belongs to the `Left` or `Right` set.
101/// - `AssetKind` is a superset type encompassing asset kinds from `Left` and `Right` sets.
102/// - `AccountId` is an account identifier type.
103pub struct UnionOf<Left, Right, Criterion, AssetKind, AccountId>(
104	core::marker::PhantomData<(Left, Right, Criterion, AssetKind, AccountId)>,
105);
106
107impl<
108		Left: fungible::Inspect<AccountId>,
109		Right: fungibles::Inspect<AccountId, Balance = Left::Balance>,
110		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
111		AssetKind: AssetId,
112		AccountId,
113	> fungibles::Inspect<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
114{
115	type AssetId = AssetKind;
116	type Balance = Left::Balance;
117
118	fn total_issuance(asset: Self::AssetId) -> Self::Balance {
119		match Criterion::convert(asset) {
120			Left(()) => <Left as fungible::Inspect<AccountId>>::total_issuance(),
121			Right(a) => <Right as fungibles::Inspect<AccountId>>::total_issuance(a),
122		}
123	}
124	fn active_issuance(asset: Self::AssetId) -> Self::Balance {
125		match Criterion::convert(asset) {
126			Left(()) => <Left as fungible::Inspect<AccountId>>::active_issuance(),
127			Right(a) => <Right as fungibles::Inspect<AccountId>>::active_issuance(a),
128		}
129	}
130	fn minimum_balance(asset: Self::AssetId) -> Self::Balance {
131		match Criterion::convert(asset) {
132			Left(()) => <Left as fungible::Inspect<AccountId>>::minimum_balance(),
133			Right(a) => <Right as fungibles::Inspect<AccountId>>::minimum_balance(a),
134		}
135	}
136	fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
137		match Criterion::convert(asset) {
138			Left(()) => <Left as fungible::Inspect<AccountId>>::balance(who),
139			Right(a) => <Right as fungibles::Inspect<AccountId>>::balance(a, who),
140		}
141	}
142	fn total_balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
143		match Criterion::convert(asset) {
144			Left(()) => <Left as fungible::Inspect<AccountId>>::total_balance(who),
145			Right(a) => <Right as fungibles::Inspect<AccountId>>::total_balance(a, who),
146		}
147	}
148	fn reducible_balance(
149		asset: Self::AssetId,
150		who: &AccountId,
151		preservation: Preservation,
152		force: Fortitude,
153	) -> Self::Balance {
154		match Criterion::convert(asset) {
155			Left(()) =>
156				<Left as fungible::Inspect<AccountId>>::reducible_balance(who, preservation, force),
157			Right(a) => <Right as fungibles::Inspect<AccountId>>::reducible_balance(
158				a,
159				who,
160				preservation,
161				force,
162			),
163		}
164	}
165	fn can_deposit(
166		asset: Self::AssetId,
167		who: &AccountId,
168		amount: Self::Balance,
169		provenance: Provenance,
170	) -> DepositConsequence {
171		match Criterion::convert(asset) {
172			Left(()) =>
173				<Left as fungible::Inspect<AccountId>>::can_deposit(who, amount, provenance),
174			Right(a) =>
175				<Right as fungibles::Inspect<AccountId>>::can_deposit(a, who, amount, provenance),
176		}
177	}
178	fn can_withdraw(
179		asset: Self::AssetId,
180		who: &AccountId,
181		amount: Self::Balance,
182	) -> WithdrawConsequence<Self::Balance> {
183		match Criterion::convert(asset) {
184			Left(()) => <Left as fungible::Inspect<AccountId>>::can_withdraw(who, amount),
185			Right(a) => <Right as fungibles::Inspect<AccountId>>::can_withdraw(a, who, amount),
186		}
187	}
188	fn asset_exists(asset: Self::AssetId) -> bool {
189		match Criterion::convert(asset) {
190			Left(()) => true,
191			Right(a) => <Right as fungibles::Inspect<AccountId>>::asset_exists(a),
192		}
193	}
194}
195
196impl<
197		Left: fungible::InspectHold<AccountId>,
198		Right: fungibles::InspectHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
199		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
200		AssetKind: AssetId,
201		AccountId,
202	> fungibles::InspectHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
203{
204	type Reason = Left::Reason;
205
206	fn reducible_total_balance_on_hold(
207		asset: Self::AssetId,
208		who: &AccountId,
209		force: Fortitude,
210	) -> Self::Balance {
211		match Criterion::convert(asset) {
212			Left(()) =>
213				<Left as fungible::InspectHold<AccountId>>::reducible_total_balance_on_hold(
214					who, force,
215				),
216			Right(a) =>
217				<Right as fungibles::InspectHold<AccountId>>::reducible_total_balance_on_hold(
218					a, who, force,
219				),
220		}
221	}
222	fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool {
223		match Criterion::convert(asset) {
224			Left(()) => <Left as fungible::InspectHold<AccountId>>::hold_available(reason, who),
225			Right(a) =>
226				<Right as fungibles::InspectHold<AccountId>>::hold_available(a, reason, who),
227		}
228	}
229	fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
230		match Criterion::convert(asset) {
231			Left(()) => <Left as fungible::InspectHold<AccountId>>::total_balance_on_hold(who),
232			Right(a) => <Right as fungibles::InspectHold<AccountId>>::total_balance_on_hold(a, who),
233		}
234	}
235	fn balance_on_hold(
236		asset: Self::AssetId,
237		reason: &Self::Reason,
238		who: &AccountId,
239	) -> Self::Balance {
240		match Criterion::convert(asset) {
241			Left(()) => <Left as fungible::InspectHold<AccountId>>::balance_on_hold(reason, who),
242			Right(a) =>
243				<Right as fungibles::InspectHold<AccountId>>::balance_on_hold(a, reason, who),
244		}
245	}
246	fn can_hold(
247		asset: Self::AssetId,
248		reason: &Self::Reason,
249		who: &AccountId,
250		amount: Self::Balance,
251	) -> bool {
252		match Criterion::convert(asset) {
253			Left(()) => <Left as fungible::InspectHold<AccountId>>::can_hold(reason, who, amount),
254			Right(a) =>
255				<Right as fungibles::InspectHold<AccountId>>::can_hold(a, reason, who, amount),
256		}
257	}
258}
259
260impl<
261		Left: fungible::InspectFreeze<AccountId>,
262		Right: fungibles::InspectFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
263		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
264		AssetKind: AssetId,
265		AccountId,
266	> fungibles::InspectFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
267{
268	type Id = Left::Id;
269	fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance {
270		match Criterion::convert(asset) {
271			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::balance_frozen(id, who),
272			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_frozen(a, id, who),
273		}
274	}
275	fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
276		match Criterion::convert(asset) {
277			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::balance_freezable(who),
278			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_freezable(a, who),
279		}
280	}
281	fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool {
282		match Criterion::convert(asset) {
283			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::can_freeze(id, who),
284			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::can_freeze(a, id, who),
285		}
286	}
287}
288
289impl<
290		Left: fungible::Unbalanced<AccountId>,
291		Right: fungibles::Unbalanced<AccountId, Balance = Left::Balance>,
292		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
293		AssetKind: AssetId,
294		AccountId,
295	> fungibles::Unbalanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
296{
297	fn handle_dust(dust: fungibles::Dust<AccountId, Self>)
298	where
299		Self: Sized,
300	{
301		match Criterion::convert(dust.0) {
302			Left(()) =>
303				<Left as fungible::Unbalanced<AccountId>>::handle_dust(fungible::Dust(dust.1)),
304			Right(a) =>
305				<Right as fungibles::Unbalanced<AccountId>>::handle_dust(fungibles::Dust(a, dust.1)),
306		}
307	}
308	fn write_balance(
309		asset: Self::AssetId,
310		who: &AccountId,
311		amount: Self::Balance,
312	) -> Result<Option<Self::Balance>, DispatchError> {
313		match Criterion::convert(asset) {
314			Left(()) => <Left as fungible::Unbalanced<AccountId>>::write_balance(who, amount),
315			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::write_balance(a, who, amount),
316		}
317	}
318	fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) -> () {
319		match Criterion::convert(asset) {
320			Left(()) => <Left as fungible::Unbalanced<AccountId>>::set_total_issuance(amount),
321			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::set_total_issuance(a, amount),
322		}
323	}
324	fn decrease_balance(
325		asset: Self::AssetId,
326		who: &AccountId,
327		amount: Self::Balance,
328		precision: Precision,
329		preservation: Preservation,
330		force: Fortitude,
331	) -> Result<Self::Balance, DispatchError> {
332		match Criterion::convert(asset) {
333			Left(()) => <Left as fungible::Unbalanced<AccountId>>::decrease_balance(
334				who,
335				amount,
336				precision,
337				preservation,
338				force,
339			),
340			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::decrease_balance(
341				a,
342				who,
343				amount,
344				precision,
345				preservation,
346				force,
347			),
348		}
349	}
350	fn increase_balance(
351		asset: Self::AssetId,
352		who: &AccountId,
353		amount: Self::Balance,
354		precision: Precision,
355	) -> Result<Self::Balance, DispatchError> {
356		match Criterion::convert(asset) {
357			Left(()) =>
358				<Left as fungible::Unbalanced<AccountId>>::increase_balance(who, amount, precision),
359			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::increase_balance(
360				a, who, amount, precision,
361			),
362		}
363	}
364}
365
366impl<
367		Left: fungible::UnbalancedHold<AccountId>,
368		Right: fungibles::UnbalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
369		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
370		AssetKind: AssetId,
371		AccountId,
372	> fungibles::UnbalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
373{
374	fn set_balance_on_hold(
375		asset: Self::AssetId,
376		reason: &Self::Reason,
377		who: &AccountId,
378		amount: Self::Balance,
379	) -> DispatchResult {
380		match Criterion::convert(asset) {
381			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::set_balance_on_hold(
382				reason, who, amount,
383			),
384			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::set_balance_on_hold(
385				a, reason, who, amount,
386			),
387		}
388	}
389	fn decrease_balance_on_hold(
390		asset: Self::AssetId,
391		reason: &Self::Reason,
392		who: &AccountId,
393		amount: Self::Balance,
394		precision: Precision,
395	) -> Result<Self::Balance, DispatchError> {
396		match Criterion::convert(asset) {
397			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
398				reason, who, amount, precision,
399			),
400			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
401				a, reason, who, amount, precision,
402			),
403		}
404	}
405	fn increase_balance_on_hold(
406		asset: Self::AssetId,
407		reason: &Self::Reason,
408		who: &AccountId,
409		amount: Self::Balance,
410		precision: Precision,
411	) -> Result<Self::Balance, DispatchError> {
412		match Criterion::convert(asset) {
413			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::increase_balance_on_hold(
414				reason, who, amount, precision,
415			),
416			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::increase_balance_on_hold(
417				a, reason, who, amount, precision,
418			),
419		}
420	}
421}
422
423impl<
424		Left: fungible::Mutate<AccountId>,
425		Right: fungibles::Mutate<AccountId, Balance = Left::Balance>,
426		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
427		AssetKind: AssetId,
428		AccountId: Eq,
429	> fungibles::Mutate<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
430{
431	fn mint_into(
432		asset: Self::AssetId,
433		who: &AccountId,
434		amount: Self::Balance,
435	) -> Result<Self::Balance, DispatchError> {
436		match Criterion::convert(asset) {
437			Left(()) => <Left as fungible::Mutate<AccountId>>::mint_into(who, amount),
438			Right(a) => <Right as fungibles::Mutate<AccountId>>::mint_into(a, who, amount),
439		}
440	}
441	fn burn_from(
442		asset: Self::AssetId,
443		who: &AccountId,
444		amount: Self::Balance,
445		preservation: Preservation,
446		precision: Precision,
447		force: Fortitude,
448	) -> Result<Self::Balance, DispatchError> {
449		match Criterion::convert(asset) {
450			Left(()) => <Left as fungible::Mutate<AccountId>>::burn_from(
451				who,
452				amount,
453				preservation,
454				precision,
455				force,
456			),
457			Right(a) => <Right as fungibles::Mutate<AccountId>>::burn_from(
458				a,
459				who,
460				amount,
461				preservation,
462				precision,
463				force,
464			),
465		}
466	}
467	fn shelve(
468		asset: Self::AssetId,
469		who: &AccountId,
470		amount: Self::Balance,
471	) -> Result<Self::Balance, DispatchError> {
472		match Criterion::convert(asset) {
473			Left(()) => <Left as fungible::Mutate<AccountId>>::shelve(who, amount),
474			Right(a) => <Right as fungibles::Mutate<AccountId>>::shelve(a, who, amount),
475		}
476	}
477	fn restore(
478		asset: Self::AssetId,
479		who: &AccountId,
480		amount: Self::Balance,
481	) -> Result<Self::Balance, DispatchError> {
482		match Criterion::convert(asset) {
483			Left(()) => <Left as fungible::Mutate<AccountId>>::restore(who, amount),
484			Right(a) => <Right as fungibles::Mutate<AccountId>>::restore(a, who, amount),
485		}
486	}
487	fn transfer(
488		asset: Self::AssetId,
489		source: &AccountId,
490		dest: &AccountId,
491		amount: Self::Balance,
492		preservation: Preservation,
493	) -> Result<Self::Balance, DispatchError> {
494		match Criterion::convert(asset) {
495			Left(()) =>
496				<Left as fungible::Mutate<AccountId>>::transfer(source, dest, amount, preservation),
497			Right(a) => <Right as fungibles::Mutate<AccountId>>::transfer(
498				a,
499				source,
500				dest,
501				amount,
502				preservation,
503			),
504		}
505	}
506
507	fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance {
508		match Criterion::convert(asset) {
509			Left(()) => <Left as fungible::Mutate<AccountId>>::set_balance(who, amount),
510			Right(a) => <Right as fungibles::Mutate<AccountId>>::set_balance(a, who, amount),
511		}
512	}
513}
514
515impl<
516		Left: fungible::MutateHold<AccountId>,
517		Right: fungibles::MutateHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
518		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
519		AssetKind: AssetId,
520		AccountId,
521	> fungibles::MutateHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
522{
523	fn hold(
524		asset: Self::AssetId,
525		reason: &Self::Reason,
526		who: &AccountId,
527		amount: Self::Balance,
528	) -> DispatchResult {
529		match Criterion::convert(asset) {
530			Left(()) => <Left as fungible::MutateHold<AccountId>>::hold(reason, who, amount),
531			Right(a) => <Right as fungibles::MutateHold<AccountId>>::hold(a, reason, who, amount),
532		}
533	}
534	fn release(
535		asset: Self::AssetId,
536		reason: &Self::Reason,
537		who: &AccountId,
538		amount: Self::Balance,
539		precision: Precision,
540	) -> Result<Self::Balance, DispatchError> {
541		match Criterion::convert(asset) {
542			Left(()) =>
543				<Left as fungible::MutateHold<AccountId>>::release(reason, who, amount, precision),
544			Right(a) => <Right as fungibles::MutateHold<AccountId>>::release(
545				a, reason, who, amount, precision,
546			),
547		}
548	}
549	fn burn_held(
550		asset: Self::AssetId,
551		reason: &Self::Reason,
552		who: &AccountId,
553		amount: Self::Balance,
554		precision: Precision,
555		force: Fortitude,
556	) -> Result<Self::Balance, DispatchError> {
557		match Criterion::convert(asset) {
558			Left(()) => <Left as fungible::MutateHold<AccountId>>::burn_held(
559				reason, who, amount, precision, force,
560			),
561			Right(a) => <Right as fungibles::MutateHold<AccountId>>::burn_held(
562				a, reason, who, amount, precision, force,
563			),
564		}
565	}
566	fn transfer_on_hold(
567		asset: Self::AssetId,
568		reason: &Self::Reason,
569		source: &AccountId,
570		dest: &AccountId,
571		amount: Self::Balance,
572		precision: Precision,
573		mode: Restriction,
574		force: Fortitude,
575	) -> Result<Self::Balance, DispatchError> {
576		match Criterion::convert(asset) {
577			Left(()) => <Left as fungible::MutateHold<AccountId>>::transfer_on_hold(
578				reason, source, dest, amount, precision, mode, force,
579			),
580			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_on_hold(
581				a, reason, source, dest, amount, precision, mode, force,
582			),
583		}
584	}
585	fn transfer_and_hold(
586		asset: Self::AssetId,
587		reason: &Self::Reason,
588		source: &AccountId,
589		dest: &AccountId,
590		amount: Self::Balance,
591		precision: Precision,
592		preservation: Preservation,
593		force: Fortitude,
594	) -> Result<Self::Balance, DispatchError> {
595		match Criterion::convert(asset) {
596			Left(()) => <Left as fungible::MutateHold<AccountId>>::transfer_and_hold(
597				reason,
598				source,
599				dest,
600				amount,
601				precision,
602				preservation,
603				force,
604			),
605			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_and_hold(
606				a,
607				reason,
608				source,
609				dest,
610				amount,
611				precision,
612				preservation,
613				force,
614			),
615		}
616	}
617}
618
619impl<
620		Left: fungible::MutateFreeze<AccountId>,
621		Right: fungibles::MutateFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
622		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
623		AssetKind: AssetId,
624		AccountId,
625	> fungibles::MutateFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
626{
627	fn set_freeze(
628		asset: Self::AssetId,
629		id: &Self::Id,
630		who: &AccountId,
631		amount: Self::Balance,
632	) -> DispatchResult {
633		match Criterion::convert(asset) {
634			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::set_freeze(id, who, amount),
635			Right(a) =>
636				<Right as fungibles::MutateFreeze<AccountId>>::set_freeze(a, id, who, amount),
637		}
638	}
639	fn extend_freeze(
640		asset: Self::AssetId,
641		id: &Self::Id,
642		who: &AccountId,
643		amount: Self::Balance,
644	) -> DispatchResult {
645		match Criterion::convert(asset) {
646			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::extend_freeze(id, who, amount),
647			Right(a) =>
648				<Right as fungibles::MutateFreeze<AccountId>>::extend_freeze(a, id, who, amount),
649		}
650	}
651	fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult {
652		match Criterion::convert(asset) {
653			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::thaw(id, who),
654			Right(a) => <Right as fungibles::MutateFreeze<AccountId>>::thaw(a, id, who),
655		}
656	}
657}
658
659pub struct ConvertImbalanceDropHandler<
660	Left,
661	Right,
662	Criterion,
663	AssetKind,
664	Balance,
665	AssetId,
666	AccountId,
667>(core::marker::PhantomData<(Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId)>);
668
669impl<
670		Left: fungible::HandleImbalanceDrop<Balance>,
671		Right: fungibles::HandleImbalanceDrop<AssetId, Balance>,
672		Criterion: Convert<AssetKind, Either<(), AssetId>>,
673		AssetKind,
674		Balance,
675		AssetId,
676		AccountId,
677	> fungibles::HandleImbalanceDrop<AssetKind, Balance>
678	for ConvertImbalanceDropHandler<Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId>
679{
680	fn handle(asset: AssetKind, amount: Balance) {
681		match Criterion::convert(asset) {
682			Left(()) => Left::handle(amount),
683			Right(a) => Right::handle(a, amount),
684		}
685	}
686}
687
688impl<
689		Left: fungible::Balanced<AccountId>,
690		Right: fungibles::Balanced<AccountId, Balance = Left::Balance>,
691		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
692		AssetKind: AssetId,
693		AccountId,
694	> fungibles::Balanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
695{
696	type OnDropDebt = ConvertImbalanceDropHandler<
697		Left::OnDropDebt,
698		Right::OnDropDebt,
699		Criterion,
700		AssetKind,
701		Left::Balance,
702		Right::AssetId,
703		AccountId,
704	>;
705	type OnDropCredit = ConvertImbalanceDropHandler<
706		Left::OnDropCredit,
707		Right::OnDropCredit,
708		Criterion,
709		AssetKind,
710		Left::Balance,
711		Right::AssetId,
712		AccountId,
713	>;
714
715	fn deposit(
716		asset: Self::AssetId,
717		who: &AccountId,
718		value: Self::Balance,
719		precision: Precision,
720	) -> Result<fungibles::Debt<AccountId, Self>, DispatchError> {
721		match Criterion::convert(asset.clone()) {
722			Left(()) => <Left as fungible::Balanced<AccountId>>::deposit(who, value, precision)
723				.map(|d| fungibles::imbalance::from_fungible(d, asset)),
724			Right(a) =>
725				<Right as fungibles::Balanced<AccountId>>::deposit(a, who, value, precision)
726					.map(|d| fungibles::imbalance::from_fungibles(d, asset)),
727		}
728	}
729	fn issue(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Credit<AccountId, Self> {
730		match Criterion::convert(asset.clone()) {
731			Left(()) => {
732				let credit = <Left as fungible::Balanced<AccountId>>::issue(amount);
733				fungibles::imbalance::from_fungible(credit, asset)
734			},
735			Right(a) => {
736				let credit = <Right as fungibles::Balanced<AccountId>>::issue(a, amount);
737				fungibles::imbalance::from_fungibles(credit, asset)
738			},
739		}
740	}
741	fn pair(
742		asset: Self::AssetId,
743		amount: Self::Balance,
744	) -> Result<(fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>), DispatchError>
745	{
746		match Criterion::convert(asset.clone()) {
747			Left(()) => {
748				let (a, b) = <Left as fungible::Balanced<AccountId>>::pair(amount)?;
749				Ok((
750					fungibles::imbalance::from_fungible(a, asset.clone()),
751					fungibles::imbalance::from_fungible(b, asset),
752				))
753			},
754			Right(a) => {
755				let (a, b) = <Right as fungibles::Balanced<AccountId>>::pair(a, amount)?;
756				Ok((
757					fungibles::imbalance::from_fungibles(a, asset.clone()),
758					fungibles::imbalance::from_fungibles(b, asset),
759				))
760			},
761		}
762	}
763	fn rescind(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Debt<AccountId, Self> {
764		match Criterion::convert(asset.clone()) {
765			Left(()) => {
766				let debt = <Left as fungible::Balanced<AccountId>>::rescind(amount);
767				fungibles::imbalance::from_fungible(debt, asset)
768			},
769			Right(a) => {
770				let debt = <Right as fungibles::Balanced<AccountId>>::rescind(a, amount);
771				fungibles::imbalance::from_fungibles(debt, asset)
772			},
773		}
774	}
775	fn resolve(
776		who: &AccountId,
777		credit: fungibles::Credit<AccountId, Self>,
778	) -> Result<(), fungibles::Credit<AccountId, Self>> {
779		let asset = credit.asset();
780		match Criterion::convert(asset.clone()) {
781			Left(()) => {
782				let credit = imbalance::from_fungibles(credit);
783				<Left as fungible::Balanced<AccountId>>::resolve(who, credit)
784					.map_err(|credit| fungibles::imbalance::from_fungible(credit, asset))
785			},
786			Right(a) => {
787				let credit = fungibles::imbalance::from_fungibles(credit, a);
788				<Right as fungibles::Balanced<AccountId>>::resolve(who, credit)
789					.map_err(|credit| fungibles::imbalance::from_fungibles(credit, asset))
790			},
791		}
792	}
793	fn settle(
794		who: &AccountId,
795		debt: fungibles::Debt<AccountId, Self>,
796		preservation: Preservation,
797	) -> Result<fungibles::Credit<AccountId, Self>, fungibles::Debt<AccountId, Self>> {
798		let asset = debt.asset();
799		match Criterion::convert(asset.clone()) {
800			Left(()) => {
801				let debt = imbalance::from_fungibles(debt);
802				match <Left as fungible::Balanced<AccountId>>::settle(who, debt, preservation) {
803					Ok(c) => Ok(fungibles::imbalance::from_fungible(c, asset)),
804					Err(d) => Err(fungibles::imbalance::from_fungible(d, asset)),
805				}
806			},
807			Right(a) => {
808				let debt = fungibles::imbalance::from_fungibles(debt, a);
809				match <Right as fungibles::Balanced<AccountId>>::settle(who, debt, preservation) {
810					Ok(c) => Ok(fungibles::imbalance::from_fungibles(c, asset)),
811					Err(d) => Err(fungibles::imbalance::from_fungibles(d, asset)),
812				}
813			},
814		}
815	}
816	fn withdraw(
817		asset: Self::AssetId,
818		who: &AccountId,
819		value: Self::Balance,
820		precision: Precision,
821		preservation: Preservation,
822		force: Fortitude,
823	) -> Result<fungibles::Credit<AccountId, Self>, DispatchError> {
824		match Criterion::convert(asset.clone()) {
825			Left(()) => <Left as fungible::Balanced<AccountId>>::withdraw(
826				who,
827				value,
828				precision,
829				preservation,
830				force,
831			)
832			.map(|c| fungibles::imbalance::from_fungible(c, asset)),
833			Right(a) => <Right as fungibles::Balanced<AccountId>>::withdraw(
834				a,
835				who,
836				value,
837				precision,
838				preservation,
839				force,
840			)
841			.map(|c| fungibles::imbalance::from_fungibles(c, asset)),
842		}
843	}
844}
845
846impl<
847		Left: fungible::BalancedHold<AccountId>,
848		Right: fungibles::BalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
849		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
850		AssetKind: AssetId,
851		AccountId,
852	> fungibles::BalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
853{
854	fn slash(
855		asset: Self::AssetId,
856		reason: &Self::Reason,
857		who: &AccountId,
858		amount: Self::Balance,
859	) -> (fungibles::Credit<AccountId, Self>, Self::Balance) {
860		match Criterion::convert(asset.clone()) {
861			Left(()) => {
862				let (credit, amount) =
863					<Left as fungible::BalancedHold<AccountId>>::slash(reason, who, amount);
864				(fungibles::imbalance::from_fungible(credit, asset), amount)
865			},
866			Right(a) => {
867				let (credit, amount) =
868					<Right as fungibles::BalancedHold<AccountId>>::slash(a, reason, who, amount);
869				(fungibles::imbalance::from_fungibles(credit, asset), amount)
870			},
871		}
872	}
873}
874
875impl<
876		Left: fungible::Inspect<AccountId>,
877		Right: fungibles::Inspect<AccountId, Balance = Left::Balance> + fungibles::Create<AccountId>,
878		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
879		AssetKind: AssetId,
880		AccountId,
881	> fungibles::Create<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
882{
883	fn create(
884		asset: AssetKind,
885		admin: AccountId,
886		is_sufficient: bool,
887		min_balance: Self::Balance,
888	) -> DispatchResult {
889		match Criterion::convert(asset) {
890			// no-op for `Left` since `Create` trait is not defined within `fungible::*`.
891			Left(()) => Ok(()),
892			Right(a) => <Right as fungibles::Create<AccountId>>::create(
893				a,
894				admin,
895				is_sufficient,
896				min_balance,
897			),
898		}
899	}
900}
901
902impl<
903		Left: fungible::Inspect<AccountId>
904			+ AccountTouch<(), AccountId, Balance = <Left as fungible::Inspect<AccountId>>::Balance>,
905		Right: fungibles::Inspect<AccountId>
906			+ AccountTouch<
907				Right::AssetId,
908				AccountId,
909				Balance = <Left as fungible::Inspect<AccountId>>::Balance,
910			>,
911		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
912		AssetKind: AssetId,
913		AccountId,
914	> AccountTouch<AssetKind, AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
915{
916	type Balance = <Left as fungible::Inspect<AccountId>>::Balance;
917
918	fn deposit_required(asset: AssetKind) -> Self::Balance {
919		match Criterion::convert(asset) {
920			Left(()) => <Left as AccountTouch<(), AccountId>>::deposit_required(()),
921			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::deposit_required(a),
922		}
923	}
924
925	fn should_touch(asset: AssetKind, who: &AccountId) -> bool {
926		match Criterion::convert(asset) {
927			Left(()) => <Left as AccountTouch<(), AccountId>>::should_touch((), who),
928			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::should_touch(a, who),
929		}
930	}
931
932	fn touch(asset: AssetKind, who: &AccountId, depositor: &AccountId) -> DispatchResult {
933		match Criterion::convert(asset) {
934			Left(()) => <Left as AccountTouch<(), AccountId>>::touch((), who, depositor),
935			Right(a) =>
936				<Right as AccountTouch<Right::AssetId, AccountId>>::touch(a, who, depositor),
937		}
938	}
939}
940
941impl<
942		Left: fungible::Inspect<AccountId>,
943		Right: fungibles::Inspect<AccountId> + fungibles::Refund<AccountId>,
944		Criterion: Convert<AssetKind, Either<(), <Right as fungibles::Refund<AccountId>>::AssetId>>,
945		AssetKind: AssetId,
946		AccountId,
947	> fungibles::Refund<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
948{
949	type AssetId = AssetKind;
950	type Balance = <Right as fungibles::Refund<AccountId>>::Balance;
951
952	fn deposit_held(asset: AssetKind, who: AccountId) -> Option<(AccountId, Self::Balance)> {
953		match Criterion::convert(asset) {
954			Left(()) => None,
955			Right(a) => <Right as fungibles::Refund<AccountId>>::deposit_held(a, who),
956		}
957	}
958	fn refund(asset: AssetKind, who: AccountId) -> DispatchResult {
959		match Criterion::convert(asset) {
960			Left(()) => Err(DispatchError::Unavailable),
961			Right(a) => <Right as fungibles::Refund<AccountId>>::refund(a, who),
962		}
963	}
964}