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