referrerpolicy=no-referrer-when-downgrade

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