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