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}
209
210impl<
211		Left: fungible::Inspect<AccountId> + fungible::metadata::Inspect<AccountId>,
212		Right: fungibles::Inspect<AccountId, Balance = Left::Balance>
213			+ fungibles::metadata::Inspect<AccountId>,
214		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
215		AssetKind: AssetId,
216		AccountId,
217	> fungibles::metadata::Inspect<AccountId>
218	for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
219{
220	fn name(asset: Self::AssetId) -> alloc::vec::Vec<u8> {
221		match Criterion::convert(asset) {
222			Left(()) => <Left as fungible::metadata::Inspect<AccountId>>::name(),
223			Right(a) => <Right as fungibles::metadata::Inspect<AccountId>>::name(a),
224		}
225	}
226	fn symbol(asset: Self::AssetId) -> alloc::vec::Vec<u8> {
227		match Criterion::convert(asset) {
228			Left(()) => <Left as fungible::metadata::Inspect<AccountId>>::symbol(),
229			Right(a) => <Right as fungibles::metadata::Inspect<AccountId>>::symbol(a),
230		}
231	}
232	fn decimals(asset: Self::AssetId) -> u8 {
233		match Criterion::convert(asset) {
234			Left(()) => <Left as fungible::metadata::Inspect<AccountId>>::decimals(),
235			Right(a) => <Right as fungibles::metadata::Inspect<AccountId>>::decimals(a),
236		}
237	}
238}
239
240impl<
241		Left: fungible::Inspect<AccountId>
242			+ fungible::metadata::Inspect<AccountId>
243			+ fungible::metadata::Mutate<AccountId>,
244		Right: fungibles::Inspect<AccountId, Balance = Left::Balance>
245			+ fungibles::metadata::Inspect<AccountId>
246			+ fungibles::metadata::Mutate<AccountId>,
247		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
248		AssetKind: AssetId,
249		AccountId,
250	> fungibles::metadata::Mutate<AccountId>
251	for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
252{
253	fn set(
254		asset: Self::AssetId,
255		from: &AccountId,
256		name: alloc::vec::Vec<u8>,
257		symbol: alloc::vec::Vec<u8>,
258		decimals: u8,
259	) -> DispatchResult {
260		match Criterion::convert(asset) {
261			Left(()) => {
262				<Left as fungible::metadata::Mutate<AccountId>>::set(from, name, symbol, decimals)
263			},
264			Right(a) => <Right as fungibles::metadata::Mutate<AccountId>>::set(
265				a, from, name, symbol, decimals,
266			),
267		}
268	}
269}
270
271impl<
272		Left: fungible::InspectHold<AccountId>,
273		Right: fungibles::InspectHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
274		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
275		AssetKind: AssetId,
276		AccountId,
277	> fungibles::InspectHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
278{
279	type Reason = Left::Reason;
280
281	fn reducible_total_balance_on_hold(
282		asset: Self::AssetId,
283		who: &AccountId,
284		force: Fortitude,
285	) -> Self::Balance {
286		match Criterion::convert(asset) {
287			Left(()) => {
288				<Left as fungible::InspectHold<AccountId>>::reducible_total_balance_on_hold(
289					who, force,
290				)
291			},
292			Right(a) => {
293				<Right as fungibles::InspectHold<AccountId>>::reducible_total_balance_on_hold(
294					a, who, force,
295				)
296			},
297		}
298	}
299	fn hold_available(asset: Self::AssetId, reason: &Self::Reason, who: &AccountId) -> bool {
300		match Criterion::convert(asset) {
301			Left(()) => <Left as fungible::InspectHold<AccountId>>::hold_available(reason, who),
302			Right(a) => {
303				<Right as fungibles::InspectHold<AccountId>>::hold_available(a, reason, who)
304			},
305		}
306	}
307	fn total_balance_on_hold(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
308		match Criterion::convert(asset) {
309			Left(()) => <Left as fungible::InspectHold<AccountId>>::total_balance_on_hold(who),
310			Right(a) => <Right as fungibles::InspectHold<AccountId>>::total_balance_on_hold(a, who),
311		}
312	}
313	fn balance_on_hold(
314		asset: Self::AssetId,
315		reason: &Self::Reason,
316		who: &AccountId,
317	) -> Self::Balance {
318		match Criterion::convert(asset) {
319			Left(()) => <Left as fungible::InspectHold<AccountId>>::balance_on_hold(reason, who),
320			Right(a) => {
321				<Right as fungibles::InspectHold<AccountId>>::balance_on_hold(a, reason, who)
322			},
323		}
324	}
325	fn can_hold(
326		asset: Self::AssetId,
327		reason: &Self::Reason,
328		who: &AccountId,
329		amount: Self::Balance,
330	) -> bool {
331		match Criterion::convert(asset) {
332			Left(()) => <Left as fungible::InspectHold<AccountId>>::can_hold(reason, who, amount),
333			Right(a) => {
334				<Right as fungibles::InspectHold<AccountId>>::can_hold(a, reason, who, amount)
335			},
336		}
337	}
338}
339
340impl<
341		Left: fungible::InspectFreeze<AccountId>,
342		Right: fungibles::InspectFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
343		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
344		AssetKind: AssetId,
345		AccountId,
346	> fungibles::InspectFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
347{
348	type Id = Left::Id;
349	fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> Self::Balance {
350		match Criterion::convert(asset) {
351			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::balance_frozen(id, who),
352			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_frozen(a, id, who),
353		}
354	}
355	fn balance_freezable(asset: Self::AssetId, who: &AccountId) -> Self::Balance {
356		match Criterion::convert(asset) {
357			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::balance_freezable(who),
358			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::balance_freezable(a, who),
359		}
360	}
361	fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> bool {
362		match Criterion::convert(asset) {
363			Left(()) => <Left as fungible::InspectFreeze<AccountId>>::can_freeze(id, who),
364			Right(a) => <Right as fungibles::InspectFreeze<AccountId>>::can_freeze(a, id, who),
365		}
366	}
367}
368
369impl<
370		Left: fungible::Unbalanced<AccountId>,
371		Right: fungibles::Unbalanced<AccountId, Balance = Left::Balance>,
372		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
373		AssetKind: AssetId,
374		AccountId,
375	> fungibles::Unbalanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
376{
377	fn handle_dust(dust: fungibles::Dust<AccountId, Self>)
378	where
379		Self: Sized,
380	{
381		match Criterion::convert(dust.0) {
382			Left(()) => {
383				<Left as fungible::Unbalanced<AccountId>>::handle_dust(fungible::Dust(dust.1))
384			},
385			Right(a) => {
386				<Right as fungibles::Unbalanced<AccountId>>::handle_dust(fungibles::Dust(a, dust.1))
387			},
388		}
389	}
390	fn write_balance(
391		asset: Self::AssetId,
392		who: &AccountId,
393		amount: Self::Balance,
394	) -> Result<Option<Self::Balance>, DispatchError> {
395		match Criterion::convert(asset) {
396			Left(()) => <Left as fungible::Unbalanced<AccountId>>::write_balance(who, amount),
397			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::write_balance(a, who, amount),
398		}
399	}
400	fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) -> () {
401		match Criterion::convert(asset) {
402			Left(()) => <Left as fungible::Unbalanced<AccountId>>::set_total_issuance(amount),
403			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::set_total_issuance(a, amount),
404		}
405	}
406	fn decrease_balance(
407		asset: Self::AssetId,
408		who: &AccountId,
409		amount: Self::Balance,
410		precision: Precision,
411		preservation: Preservation,
412		force: Fortitude,
413	) -> Result<Self::Balance, DispatchError> {
414		match Criterion::convert(asset) {
415			Left(()) => <Left as fungible::Unbalanced<AccountId>>::decrease_balance(
416				who,
417				amount,
418				precision,
419				preservation,
420				force,
421			),
422			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::decrease_balance(
423				a,
424				who,
425				amount,
426				precision,
427				preservation,
428				force,
429			),
430		}
431	}
432	fn increase_balance(
433		asset: Self::AssetId,
434		who: &AccountId,
435		amount: Self::Balance,
436		precision: Precision,
437	) -> Result<Self::Balance, DispatchError> {
438		match Criterion::convert(asset) {
439			Left(()) => {
440				<Left as fungible::Unbalanced<AccountId>>::increase_balance(who, amount, precision)
441			},
442			Right(a) => <Right as fungibles::Unbalanced<AccountId>>::increase_balance(
443				a, who, amount, precision,
444			),
445		}
446	}
447}
448
449impl<
450		Left: fungible::UnbalancedHold<AccountId>,
451		Right: fungibles::UnbalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
452		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
453		AssetKind: AssetId,
454		AccountId,
455	> fungibles::UnbalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
456{
457	fn set_balance_on_hold(
458		asset: Self::AssetId,
459		reason: &Self::Reason,
460		who: &AccountId,
461		amount: Self::Balance,
462	) -> DispatchResult {
463		match Criterion::convert(asset) {
464			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::set_balance_on_hold(
465				reason, who, amount,
466			),
467			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::set_balance_on_hold(
468				a, reason, who, amount,
469			),
470		}
471	}
472	fn decrease_balance_on_hold(
473		asset: Self::AssetId,
474		reason: &Self::Reason,
475		who: &AccountId,
476		amount: Self::Balance,
477		precision: Precision,
478	) -> Result<Self::Balance, DispatchError> {
479		match Criterion::convert(asset) {
480			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
481				reason, who, amount, precision,
482			),
483			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::decrease_balance_on_hold(
484				a, reason, who, amount, precision,
485			),
486		}
487	}
488	fn increase_balance_on_hold(
489		asset: Self::AssetId,
490		reason: &Self::Reason,
491		who: &AccountId,
492		amount: Self::Balance,
493		precision: Precision,
494	) -> Result<Self::Balance, DispatchError> {
495		match Criterion::convert(asset) {
496			Left(()) => <Left as fungible::UnbalancedHold<AccountId>>::increase_balance_on_hold(
497				reason, who, amount, precision,
498			),
499			Right(a) => <Right as fungibles::UnbalancedHold<AccountId>>::increase_balance_on_hold(
500				a, reason, who, amount, precision,
501			),
502		}
503	}
504}
505
506impl<
507		Left: fungible::Mutate<AccountId>,
508		Right: fungibles::Mutate<AccountId, Balance = Left::Balance>,
509		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
510		AssetKind: AssetId,
511		AccountId: Eq,
512	> fungibles::Mutate<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
513{
514	fn mint_into(
515		asset: Self::AssetId,
516		who: &AccountId,
517		amount: Self::Balance,
518	) -> Result<Self::Balance, DispatchError> {
519		match Criterion::convert(asset) {
520			Left(()) => <Left as fungible::Mutate<AccountId>>::mint_into(who, amount),
521			Right(a) => <Right as fungibles::Mutate<AccountId>>::mint_into(a, who, amount),
522		}
523	}
524	fn burn_from(
525		asset: Self::AssetId,
526		who: &AccountId,
527		amount: Self::Balance,
528		preservation: Preservation,
529		precision: Precision,
530		force: Fortitude,
531	) -> Result<Self::Balance, DispatchError> {
532		match Criterion::convert(asset) {
533			Left(()) => <Left as fungible::Mutate<AccountId>>::burn_from(
534				who,
535				amount,
536				preservation,
537				precision,
538				force,
539			),
540			Right(a) => <Right as fungibles::Mutate<AccountId>>::burn_from(
541				a,
542				who,
543				amount,
544				preservation,
545				precision,
546				force,
547			),
548		}
549	}
550	fn shelve(
551		asset: Self::AssetId,
552		who: &AccountId,
553		amount: Self::Balance,
554	) -> Result<Self::Balance, DispatchError> {
555		match Criterion::convert(asset) {
556			Left(()) => <Left as fungible::Mutate<AccountId>>::shelve(who, amount),
557			Right(a) => <Right as fungibles::Mutate<AccountId>>::shelve(a, who, amount),
558		}
559	}
560	fn restore(
561		asset: Self::AssetId,
562		who: &AccountId,
563		amount: Self::Balance,
564	) -> Result<Self::Balance, DispatchError> {
565		match Criterion::convert(asset) {
566			Left(()) => <Left as fungible::Mutate<AccountId>>::restore(who, amount),
567			Right(a) => <Right as fungibles::Mutate<AccountId>>::restore(a, who, amount),
568		}
569	}
570	fn transfer(
571		asset: Self::AssetId,
572		source: &AccountId,
573		dest: &AccountId,
574		amount: Self::Balance,
575		preservation: Preservation,
576	) -> Result<Self::Balance, DispatchError> {
577		match Criterion::convert(asset) {
578			Left(()) => {
579				<Left as fungible::Mutate<AccountId>>::transfer(source, dest, amount, preservation)
580			},
581			Right(a) => <Right as fungibles::Mutate<AccountId>>::transfer(
582				a,
583				source,
584				dest,
585				amount,
586				preservation,
587			),
588		}
589	}
590
591	fn set_balance(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> Self::Balance {
592		match Criterion::convert(asset) {
593			Left(()) => <Left as fungible::Mutate<AccountId>>::set_balance(who, amount),
594			Right(a) => <Right as fungibles::Mutate<AccountId>>::set_balance(a, who, amount),
595		}
596	}
597}
598
599impl<
600		Left: fungible::MutateHold<AccountId>,
601		Right: fungibles::MutateHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>,
602		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
603		AssetKind: AssetId,
604		AccountId,
605	> fungibles::MutateHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
606{
607	fn hold(
608		asset: Self::AssetId,
609		reason: &Self::Reason,
610		who: &AccountId,
611		amount: Self::Balance,
612	) -> DispatchResult {
613		match Criterion::convert(asset) {
614			Left(()) => <Left as fungible::MutateHold<AccountId>>::hold(reason, who, amount),
615			Right(a) => <Right as fungibles::MutateHold<AccountId>>::hold(a, reason, who, amount),
616		}
617	}
618	fn release(
619		asset: Self::AssetId,
620		reason: &Self::Reason,
621		who: &AccountId,
622		amount: Self::Balance,
623		precision: Precision,
624	) -> Result<Self::Balance, DispatchError> {
625		match Criterion::convert(asset) {
626			Left(()) => {
627				<Left as fungible::MutateHold<AccountId>>::release(reason, who, amount, precision)
628			},
629			Right(a) => <Right as fungibles::MutateHold<AccountId>>::release(
630				a, reason, who, amount, precision,
631			),
632		}
633	}
634	fn burn_held(
635		asset: Self::AssetId,
636		reason: &Self::Reason,
637		who: &AccountId,
638		amount: Self::Balance,
639		precision: Precision,
640		force: Fortitude,
641	) -> Result<Self::Balance, DispatchError> {
642		match Criterion::convert(asset) {
643			Left(()) => <Left as fungible::MutateHold<AccountId>>::burn_held(
644				reason, who, amount, precision, force,
645			),
646			Right(a) => <Right as fungibles::MutateHold<AccountId>>::burn_held(
647				a, reason, who, amount, precision, force,
648			),
649		}
650	}
651	fn transfer_on_hold(
652		asset: Self::AssetId,
653		reason: &Self::Reason,
654		source: &AccountId,
655		dest: &AccountId,
656		amount: Self::Balance,
657		precision: Precision,
658		mode: Restriction,
659		force: Fortitude,
660	) -> Result<Self::Balance, DispatchError> {
661		match Criterion::convert(asset) {
662			Left(()) => <Left as fungible::MutateHold<AccountId>>::transfer_on_hold(
663				reason, source, dest, amount, precision, mode, force,
664			),
665			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_on_hold(
666				a, reason, source, dest, amount, precision, mode, force,
667			),
668		}
669	}
670	fn transfer_and_hold(
671		asset: Self::AssetId,
672		reason: &Self::Reason,
673		source: &AccountId,
674		dest: &AccountId,
675		amount: Self::Balance,
676		precision: Precision,
677		preservation: Preservation,
678		force: Fortitude,
679	) -> Result<Self::Balance, DispatchError> {
680		match Criterion::convert(asset) {
681			Left(()) => <Left as fungible::MutateHold<AccountId>>::transfer_and_hold(
682				reason,
683				source,
684				dest,
685				amount,
686				precision,
687				preservation,
688				force,
689			),
690			Right(a) => <Right as fungibles::MutateHold<AccountId>>::transfer_and_hold(
691				a,
692				reason,
693				source,
694				dest,
695				amount,
696				precision,
697				preservation,
698				force,
699			),
700		}
701	}
702}
703
704impl<
705		Left: fungible::MutateFreeze<AccountId>,
706		Right: fungibles::MutateFreeze<AccountId, Balance = Left::Balance, Id = Left::Id>,
707		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
708		AssetKind: AssetId,
709		AccountId,
710	> fungibles::MutateFreeze<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
711{
712	fn set_freeze(
713		asset: Self::AssetId,
714		id: &Self::Id,
715		who: &AccountId,
716		amount: Self::Balance,
717	) -> DispatchResult {
718		match Criterion::convert(asset) {
719			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::set_freeze(id, who, amount),
720			Right(a) => {
721				<Right as fungibles::MutateFreeze<AccountId>>::set_freeze(a, id, who, amount)
722			},
723		}
724	}
725	fn extend_freeze(
726		asset: Self::AssetId,
727		id: &Self::Id,
728		who: &AccountId,
729		amount: Self::Balance,
730	) -> DispatchResult {
731		match Criterion::convert(asset) {
732			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::extend_freeze(id, who, amount),
733			Right(a) => {
734				<Right as fungibles::MutateFreeze<AccountId>>::extend_freeze(a, id, who, amount)
735			},
736		}
737	}
738	fn thaw(asset: Self::AssetId, id: &Self::Id, who: &AccountId) -> DispatchResult {
739		match Criterion::convert(asset) {
740			Left(()) => <Left as fungible::MutateFreeze<AccountId>>::thaw(id, who),
741			Right(a) => <Right as fungibles::MutateFreeze<AccountId>>::thaw(a, id, who),
742		}
743	}
744}
745
746pub struct ConvertImbalanceDropHandler<
747	Left,
748	Right,
749	Criterion,
750	AssetKind,
751	Balance,
752	AssetId,
753	AccountId,
754>(core::marker::PhantomData<(Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId)>);
755
756impl<
757		Left: fungible::HandleImbalanceDrop<Balance>,
758		Right: fungibles::HandleImbalanceDrop<AssetId, Balance>,
759		Criterion: Convert<AssetKind, Either<(), AssetId>>,
760		AssetKind,
761		Balance,
762		AssetId,
763		AccountId,
764	> fungibles::HandleImbalanceDrop<AssetKind, Balance>
765	for ConvertImbalanceDropHandler<Left, Right, Criterion, AssetKind, Balance, AssetId, AccountId>
766{
767	fn handle(asset: AssetKind, amount: Balance) {
768		match Criterion::convert(asset) {
769			Left(()) => Left::handle(amount),
770			Right(a) => Right::handle(a, amount),
771		}
772	}
773}
774
775impl<
776		Left: fungible::Balanced<AccountId>,
777		Right: fungibles::Balanced<AccountId, Balance = Left::Balance>,
778		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
779		AssetKind: AssetId,
780		AccountId,
781	> fungibles::Balanced<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
782{
783	type OnDropDebt = ConvertImbalanceDropHandler<
784		Left::OnDropDebt,
785		Right::OnDropDebt,
786		Criterion,
787		AssetKind,
788		Left::Balance,
789		Right::AssetId,
790		AccountId,
791	>;
792	type OnDropCredit = ConvertImbalanceDropHandler<
793		Left::OnDropCredit,
794		Right::OnDropCredit,
795		Criterion,
796		AssetKind,
797		Left::Balance,
798		Right::AssetId,
799		AccountId,
800	>;
801
802	fn deposit(
803		asset: Self::AssetId,
804		who: &AccountId,
805		value: Self::Balance,
806		precision: Precision,
807	) -> Result<fungibles::Debt<AccountId, Self>, DispatchError> {
808		match Criterion::convert(asset.clone()) {
809			Left(()) => <Left as fungible::Balanced<AccountId>>::deposit(who, value, precision)
810				.map(|d| fungibles::imbalance::from_fungible(d, asset)),
811			Right(a) => {
812				<Right as fungibles::Balanced<AccountId>>::deposit(a, who, value, precision)
813					.map(|d| fungibles::imbalance::from_fungibles(d, asset))
814			},
815		}
816	}
817	fn issue(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Credit<AccountId, Self> {
818		match Criterion::convert(asset.clone()) {
819			Left(()) => {
820				let credit = <Left as fungible::Balanced<AccountId>>::issue(amount);
821				fungibles::imbalance::from_fungible(credit, asset)
822			},
823			Right(a) => {
824				let credit = <Right as fungibles::Balanced<AccountId>>::issue(a, amount);
825				fungibles::imbalance::from_fungibles(credit, asset)
826			},
827		}
828	}
829	fn pair(
830		asset: Self::AssetId,
831		amount: Self::Balance,
832	) -> Result<(fungibles::Debt<AccountId, Self>, fungibles::Credit<AccountId, Self>), DispatchError>
833	{
834		match Criterion::convert(asset.clone()) {
835			Left(()) => {
836				let (a, b) = <Left as fungible::Balanced<AccountId>>::pair(amount)?;
837				Ok((
838					fungibles::imbalance::from_fungible(a, asset.clone()),
839					fungibles::imbalance::from_fungible(b, asset),
840				))
841			},
842			Right(a) => {
843				let (a, b) = <Right as fungibles::Balanced<AccountId>>::pair(a, amount)?;
844				Ok((
845					fungibles::imbalance::from_fungibles(a, asset.clone()),
846					fungibles::imbalance::from_fungibles(b, asset),
847				))
848			},
849		}
850	}
851	fn rescind(asset: Self::AssetId, amount: Self::Balance) -> fungibles::Debt<AccountId, Self> {
852		match Criterion::convert(asset.clone()) {
853			Left(()) => {
854				let debt = <Left as fungible::Balanced<AccountId>>::rescind(amount);
855				fungibles::imbalance::from_fungible(debt, asset)
856			},
857			Right(a) => {
858				let debt = <Right as fungibles::Balanced<AccountId>>::rescind(a, amount);
859				fungibles::imbalance::from_fungibles(debt, asset)
860			},
861		}
862	}
863	fn resolve(
864		who: &AccountId,
865		credit: fungibles::Credit<AccountId, Self>,
866	) -> Result<(), fungibles::Credit<AccountId, Self>> {
867		let asset = credit.asset();
868		match Criterion::convert(asset.clone()) {
869			Left(()) => {
870				let credit = imbalance::from_fungibles(credit);
871				<Left as fungible::Balanced<AccountId>>::resolve(who, credit)
872					.map_err(|credit| fungibles::imbalance::from_fungible(credit, asset))
873			},
874			Right(a) => {
875				let credit = fungibles::imbalance::from_fungibles(credit, a);
876				<Right as fungibles::Balanced<AccountId>>::resolve(who, credit)
877					.map_err(|credit| fungibles::imbalance::from_fungibles(credit, asset))
878			},
879		}
880	}
881	fn settle(
882		who: &AccountId,
883		debt: fungibles::Debt<AccountId, Self>,
884		preservation: Preservation,
885	) -> Result<fungibles::Credit<AccountId, Self>, fungibles::Debt<AccountId, Self>> {
886		let asset = debt.asset();
887		match Criterion::convert(asset.clone()) {
888			Left(()) => {
889				let debt = imbalance::from_fungibles(debt);
890				match <Left as fungible::Balanced<AccountId>>::settle(who, debt, preservation) {
891					Ok(c) => Ok(fungibles::imbalance::from_fungible(c, asset)),
892					Err(d) => Err(fungibles::imbalance::from_fungible(d, asset)),
893				}
894			},
895			Right(a) => {
896				let debt = fungibles::imbalance::from_fungibles(debt, a);
897				match <Right as fungibles::Balanced<AccountId>>::settle(who, debt, preservation) {
898					Ok(c) => Ok(fungibles::imbalance::from_fungibles(c, asset)),
899					Err(d) => Err(fungibles::imbalance::from_fungibles(d, asset)),
900				}
901			},
902		}
903	}
904	fn withdraw(
905		asset: Self::AssetId,
906		who: &AccountId,
907		value: Self::Balance,
908		precision: Precision,
909		preservation: Preservation,
910		force: Fortitude,
911	) -> Result<fungibles::Credit<AccountId, Self>, DispatchError> {
912		match Criterion::convert(asset.clone()) {
913			Left(()) => <Left as fungible::Balanced<AccountId>>::withdraw(
914				who,
915				value,
916				precision,
917				preservation,
918				force,
919			)
920			.map(|c| fungibles::imbalance::from_fungible(c, asset)),
921			Right(a) => <Right as fungibles::Balanced<AccountId>>::withdraw(
922				a,
923				who,
924				value,
925				precision,
926				preservation,
927				force,
928			)
929			.map(|c| fungibles::imbalance::from_fungibles(c, asset)),
930		}
931	}
932}
933
934impl<
935		Left: fungible::BalancedHold<AccountId>
936			+ fungible::hold::DoneSlash<Self::Reason, AccountId, Self::Balance>,
937		Right: fungibles::BalancedHold<AccountId, Balance = Left::Balance, Reason = Left::Reason>
938			+ fungibles::hold::DoneSlash<AssetKind, Left::Reason, AccountId, Left::Balance>,
939		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
940		AssetKind: AssetId,
941		AccountId,
942	> fungibles::BalancedHold<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
943{
944	fn slash(
945		asset: Self::AssetId,
946		reason: &Self::Reason,
947		who: &AccountId,
948		amount: Self::Balance,
949	) -> (fungibles::Credit<AccountId, Self>, Self::Balance) {
950		match Criterion::convert(asset.clone()) {
951			Left(()) => {
952				let (credit, amount) =
953					<Left as fungible::BalancedHold<AccountId>>::slash(reason, who, amount);
954				(fungibles::imbalance::from_fungible(credit, asset), amount)
955			},
956			Right(a) => {
957				let (credit, amount) =
958					<Right as fungibles::BalancedHold<AccountId>>::slash(a, reason, who, amount);
959				(fungibles::imbalance::from_fungibles(credit, asset), amount)
960			},
961		}
962	}
963}
964impl<
965		Reason,
966		Balance,
967		Left: fungible::hold::DoneSlash<Reason, AccountId, Balance>,
968		Right: fungibles::hold::DoneSlash<Right::AssetId, Reason, AccountId, Balance>
969			+ fungibles::Inspect<AccountId>,
970		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
971		AssetKind: AssetId,
972		AccountId,
973	> fungibles::hold::DoneSlash<AssetKind, Reason, AccountId, Balance>
974	for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
975{
976	fn done_slash(asset: AssetKind, reason: &Reason, who: &AccountId, amount: Balance) {
977		match Criterion::convert(asset.clone()) {
978			Left(()) => {
979				Left::done_slash(reason, who, amount);
980			},
981			Right(a) => {
982				Right::done_slash(a, reason, who, amount);
983			},
984		}
985	}
986}
987
988impl<
989		Left: fungible::Inspect<AccountId>,
990		Right: fungibles::Inspect<AccountId, Balance = Left::Balance> + fungibles::Create<AccountId>,
991		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
992		AssetKind: AssetId,
993		AccountId,
994	> fungibles::Create<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
995{
996	fn create(
997		asset: AssetKind,
998		admin: AccountId,
999		is_sufficient: bool,
1000		min_balance: Self::Balance,
1001	) -> DispatchResult {
1002		match Criterion::convert(asset) {
1003			// no-op for `Left` since `Create` trait is not defined within `fungible::*`.
1004			Left(()) => Ok(()),
1005			Right(a) => <Right as fungibles::Create<AccountId>>::create(
1006				a,
1007				admin,
1008				is_sufficient,
1009				min_balance,
1010			),
1011		}
1012	}
1013}
1014
1015impl<
1016		Left: fungible::Inspect<AccountId>
1017			+ AccountTouch<(), AccountId, Balance = <Left as fungible::Inspect<AccountId>>::Balance>,
1018		Right: fungibles::Inspect<AccountId>
1019			+ AccountTouch<
1020				Right::AssetId,
1021				AccountId,
1022				Balance = <Left as fungible::Inspect<AccountId>>::Balance,
1023			>,
1024		Criterion: Convert<AssetKind, Either<(), Right::AssetId>>,
1025		AssetKind: AssetId,
1026		AccountId,
1027	> AccountTouch<AssetKind, AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
1028{
1029	type Balance = <Left as fungible::Inspect<AccountId>>::Balance;
1030
1031	fn deposit_required(asset: AssetKind) -> Self::Balance {
1032		match Criterion::convert(asset) {
1033			Left(()) => <Left as AccountTouch<(), AccountId>>::deposit_required(()),
1034			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::deposit_required(a),
1035		}
1036	}
1037
1038	fn should_touch(asset: AssetKind, who: &AccountId) -> bool {
1039		match Criterion::convert(asset) {
1040			Left(()) => <Left as AccountTouch<(), AccountId>>::should_touch((), who),
1041			Right(a) => <Right as AccountTouch<Right::AssetId, AccountId>>::should_touch(a, who),
1042		}
1043	}
1044
1045	fn touch(asset: AssetKind, who: &AccountId, depositor: &AccountId) -> DispatchResult {
1046		match Criterion::convert(asset) {
1047			Left(()) => <Left as AccountTouch<(), AccountId>>::touch((), who, depositor),
1048			Right(a) => {
1049				<Right as AccountTouch<Right::AssetId, AccountId>>::touch(a, who, depositor)
1050			},
1051		}
1052	}
1053}
1054
1055impl<
1056		Left: fungible::Inspect<AccountId>,
1057		Right: fungibles::Inspect<AccountId> + fungibles::Refund<AccountId>,
1058		Criterion: Convert<AssetKind, Either<(), <Right as fungibles::Refund<AccountId>>::AssetId>>,
1059		AssetKind: AssetId,
1060		AccountId,
1061	> fungibles::Refund<AccountId> for UnionOf<Left, Right, Criterion, AssetKind, AccountId>
1062{
1063	type AssetId = AssetKind;
1064	type Balance = <Right as fungibles::Refund<AccountId>>::Balance;
1065
1066	fn deposit_held(asset: AssetKind, who: AccountId) -> Option<(AccountId, Self::Balance)> {
1067		match Criterion::convert(asset) {
1068			Left(()) => None,
1069			Right(a) => <Right as fungibles::Refund<AccountId>>::deposit_held(a, who),
1070		}
1071	}
1072	fn refund(asset: AssetKind, who: AccountId) -> DispatchResult {
1073		match Criterion::convert(asset) {
1074			Left(()) => Err(DispatchError::Unavailable),
1075			Right(a) => <Right as fungibles::Refund<AccountId>>::refund(a, who),
1076		}
1077	}
1078}