staging_xcm/v4/
asset.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Cross-Consensus Message format asset data structures.
18//!
19//! This encompasses four types for representing assets:
20//! - `Asset`: A description of a single asset, either an instance of a non-fungible or some amount
21//!   of a fungible.
22//! - `Assets`: A collection of `Asset`s. These are stored in a `Vec` and sorted with fungibles
23//!   first.
24//! - `Wild`: A single asset wildcard, this can either be "all" assets, or all assets of a specific
25//!   kind.
26//! - `AssetFilter`: A combination of `Wild` and `Assets` designed for efficiently filtering an XCM
27//!   holding account.
28
29use super::{InteriorLocation, Location, Reanchorable};
30use crate::v3::{
31	AssetId as OldAssetId, AssetInstance as OldAssetInstance, Fungibility as OldFungibility,
32	MultiAsset as OldAsset, MultiAssetFilter as OldAssetFilter, MultiAssets as OldAssets,
33	WildFungibility as OldWildFungibility, WildMultiAsset as OldWildAsset,
34};
35use alloc::{vec, vec::Vec};
36use bounded_collections::{BoundedVec, ConstU32};
37use codec::{self as codec, Decode, Encode, MaxEncodedLen};
38use core::cmp::Ordering;
39use scale_info::TypeInfo;
40
41/// A general identifier for an instance of a non-fungible asset class.
42#[derive(
43	Copy,
44	Clone,
45	Eq,
46	PartialEq,
47	Ord,
48	PartialOrd,
49	Encode,
50	Decode,
51	Debug,
52	TypeInfo,
53	MaxEncodedLen,
54	serde::Serialize,
55	serde::Deserialize,
56)]
57pub enum AssetInstance {
58	/// Undefined - used if the non-fungible asset class has only one instance.
59	Undefined,
60
61	/// A compact index. Technically this could be greater than `u128`, but this implementation
62	/// supports only values up to `2**128 - 1`.
63	Index(#[codec(compact)] u128),
64
65	/// A 4-byte fixed-length datum.
66	Array4([u8; 4]),
67
68	/// An 8-byte fixed-length datum.
69	Array8([u8; 8]),
70
71	/// A 16-byte fixed-length datum.
72	Array16([u8; 16]),
73
74	/// A 32-byte fixed-length datum.
75	Array32([u8; 32]),
76}
77
78impl TryFrom<OldAssetInstance> for AssetInstance {
79	type Error = ();
80	fn try_from(value: OldAssetInstance) -> Result<Self, Self::Error> {
81		use OldAssetInstance::*;
82		Ok(match value {
83			Undefined => Self::Undefined,
84			Index(n) => Self::Index(n),
85			Array4(n) => Self::Array4(n),
86			Array8(n) => Self::Array8(n),
87			Array16(n) => Self::Array16(n),
88			Array32(n) => Self::Array32(n),
89		})
90	}
91}
92
93impl From<()> for AssetInstance {
94	fn from(_: ()) -> Self {
95		Self::Undefined
96	}
97}
98
99impl From<[u8; 4]> for AssetInstance {
100	fn from(x: [u8; 4]) -> Self {
101		Self::Array4(x)
102	}
103}
104
105impl From<[u8; 8]> for AssetInstance {
106	fn from(x: [u8; 8]) -> Self {
107		Self::Array8(x)
108	}
109}
110
111impl From<[u8; 16]> for AssetInstance {
112	fn from(x: [u8; 16]) -> Self {
113		Self::Array16(x)
114	}
115}
116
117impl From<[u8; 32]> for AssetInstance {
118	fn from(x: [u8; 32]) -> Self {
119		Self::Array32(x)
120	}
121}
122
123impl From<u8> for AssetInstance {
124	fn from(x: u8) -> Self {
125		Self::Index(x as u128)
126	}
127}
128
129impl From<u16> for AssetInstance {
130	fn from(x: u16) -> Self {
131		Self::Index(x as u128)
132	}
133}
134
135impl From<u32> for AssetInstance {
136	fn from(x: u32) -> Self {
137		Self::Index(x as u128)
138	}
139}
140
141impl From<u64> for AssetInstance {
142	fn from(x: u64) -> Self {
143		Self::Index(x as u128)
144	}
145}
146
147impl TryFrom<AssetInstance> for () {
148	type Error = ();
149	fn try_from(x: AssetInstance) -> Result<Self, ()> {
150		match x {
151			AssetInstance::Undefined => Ok(()),
152			_ => Err(()),
153		}
154	}
155}
156
157impl TryFrom<AssetInstance> for [u8; 4] {
158	type Error = ();
159	fn try_from(x: AssetInstance) -> Result<Self, ()> {
160		match x {
161			AssetInstance::Array4(x) => Ok(x),
162			_ => Err(()),
163		}
164	}
165}
166
167impl TryFrom<AssetInstance> for [u8; 8] {
168	type Error = ();
169	fn try_from(x: AssetInstance) -> Result<Self, ()> {
170		match x {
171			AssetInstance::Array8(x) => Ok(x),
172			_ => Err(()),
173		}
174	}
175}
176
177impl TryFrom<AssetInstance> for [u8; 16] {
178	type Error = ();
179	fn try_from(x: AssetInstance) -> Result<Self, ()> {
180		match x {
181			AssetInstance::Array16(x) => Ok(x),
182			_ => Err(()),
183		}
184	}
185}
186
187impl TryFrom<AssetInstance> for [u8; 32] {
188	type Error = ();
189	fn try_from(x: AssetInstance) -> Result<Self, ()> {
190		match x {
191			AssetInstance::Array32(x) => Ok(x),
192			_ => Err(()),
193		}
194	}
195}
196
197impl TryFrom<AssetInstance> for u8 {
198	type Error = ();
199	fn try_from(x: AssetInstance) -> Result<Self, ()> {
200		match x {
201			AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
202			_ => Err(()),
203		}
204	}
205}
206
207impl TryFrom<AssetInstance> for u16 {
208	type Error = ();
209	fn try_from(x: AssetInstance) -> Result<Self, ()> {
210		match x {
211			AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
212			_ => Err(()),
213		}
214	}
215}
216
217impl TryFrom<AssetInstance> for u32 {
218	type Error = ();
219	fn try_from(x: AssetInstance) -> Result<Self, ()> {
220		match x {
221			AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
222			_ => Err(()),
223		}
224	}
225}
226
227impl TryFrom<AssetInstance> for u64 {
228	type Error = ();
229	fn try_from(x: AssetInstance) -> Result<Self, ()> {
230		match x {
231			AssetInstance::Index(x) => x.try_into().map_err(|_| ()),
232			_ => Err(()),
233		}
234	}
235}
236
237impl TryFrom<AssetInstance> for u128 {
238	type Error = ();
239	fn try_from(x: AssetInstance) -> Result<Self, ()> {
240		match x {
241			AssetInstance::Index(x) => Ok(x),
242			_ => Err(()),
243		}
244	}
245}
246
247/// Classification of whether an asset is fungible or not, along with a mandatory amount or
248/// instance.
249#[derive(
250	Clone,
251	Eq,
252	PartialEq,
253	Ord,
254	PartialOrd,
255	Debug,
256	Encode,
257	TypeInfo,
258	MaxEncodedLen,
259	serde::Serialize,
260	serde::Deserialize,
261)]
262pub enum Fungibility {
263	/// A fungible asset; we record a number of units, as a `u128` in the inner item.
264	Fungible(#[codec(compact)] u128),
265	/// A non-fungible asset. We record the instance identifier in the inner item. Only one asset
266	/// of each instance identifier may ever be in existence at once.
267	NonFungible(AssetInstance),
268}
269
270#[derive(Decode)]
271enum UncheckedFungibility {
272	Fungible(#[codec(compact)] u128),
273	NonFungible(AssetInstance),
274}
275
276impl Decode for Fungibility {
277	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
278		match UncheckedFungibility::decode(input)? {
279			UncheckedFungibility::Fungible(a) if a != 0 => Ok(Self::Fungible(a)),
280			UncheckedFungibility::NonFungible(i) => Ok(Self::NonFungible(i)),
281			UncheckedFungibility::Fungible(_) =>
282				Err("Fungible asset of zero amount is not allowed".into()),
283		}
284	}
285}
286
287impl Fungibility {
288	pub fn is_kind(&self, w: WildFungibility) -> bool {
289		use Fungibility::*;
290		use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible};
291		matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible))
292	}
293}
294
295impl From<i32> for Fungibility {
296	fn from(amount: i32) -> Fungibility {
297		debug_assert_ne!(amount, 0);
298		Fungibility::Fungible(amount as u128)
299	}
300}
301
302impl From<u128> for Fungibility {
303	fn from(amount: u128) -> Fungibility {
304		debug_assert_ne!(amount, 0);
305		Fungibility::Fungible(amount)
306	}
307}
308
309impl<T: Into<AssetInstance>> From<T> for Fungibility {
310	fn from(instance: T) -> Fungibility {
311		Fungibility::NonFungible(instance.into())
312	}
313}
314
315impl TryFrom<OldFungibility> for Fungibility {
316	type Error = ();
317	fn try_from(value: OldFungibility) -> Result<Self, Self::Error> {
318		use OldFungibility::*;
319		Ok(match value {
320			Fungible(n) => Self::Fungible(n),
321			NonFungible(i) => Self::NonFungible(i.try_into()?),
322		})
323	}
324}
325
326/// Classification of whether an asset is fungible or not.
327#[derive(
328	Copy,
329	Clone,
330	Eq,
331	PartialEq,
332	Ord,
333	PartialOrd,
334	Debug,
335	Encode,
336	Decode,
337	TypeInfo,
338	MaxEncodedLen,
339	serde::Serialize,
340	serde::Deserialize,
341)]
342pub enum WildFungibility {
343	/// The asset is fungible.
344	Fungible,
345	/// The asset is not fungible.
346	NonFungible,
347}
348
349impl TryFrom<OldWildFungibility> for WildFungibility {
350	type Error = ();
351	fn try_from(value: OldWildFungibility) -> Result<Self, Self::Error> {
352		use OldWildFungibility::*;
353		Ok(match value {
354			Fungible => Self::Fungible,
355			NonFungible => Self::NonFungible,
356		})
357	}
358}
359
360/// Location to identify an asset.
361#[derive(
362	Clone,
363	Eq,
364	PartialEq,
365	Ord,
366	PartialOrd,
367	Debug,
368	Encode,
369	Decode,
370	TypeInfo,
371	MaxEncodedLen,
372	serde::Serialize,
373	serde::Deserialize,
374)]
375pub struct AssetId(pub Location);
376
377impl<T: Into<Location>> From<T> for AssetId {
378	fn from(x: T) -> Self {
379		Self(x.into())
380	}
381}
382
383impl TryFrom<OldAssetId> for AssetId {
384	type Error = ();
385	fn try_from(old: OldAssetId) -> Result<Self, ()> {
386		use OldAssetId::*;
387		Ok(match old {
388			Concrete(l) => Self(l.try_into()?),
389			Abstract(_) => return Err(()),
390		})
391	}
392}
393
394impl AssetId {
395	/// Prepend a `Location` to an asset id, giving it a new root location.
396	pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> {
397		self.0.prepend_with(prepend.clone()).map_err(|_| ())?;
398		Ok(())
399	}
400
401	/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding
402	/// `Asset` value.
403	pub fn into_asset(self, fun: Fungibility) -> Asset {
404		Asset { fun, id: self }
405	}
406
407	/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding
408	/// `WildAsset` wildcard (`AllOf`) value.
409	pub fn into_wild(self, fun: WildFungibility) -> WildAsset {
410		WildAsset::AllOf { fun, id: self }
411	}
412}
413
414impl Reanchorable for AssetId {
415	type Error = ();
416
417	/// Mutate the asset to represent the same value from the perspective of a new `target`
418	/// location. The local chain's location is provided in `context`.
419	fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
420		self.0.reanchor(target, context)?;
421		Ok(())
422	}
423
424	fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, ()> {
425		match self.reanchor(target, context) {
426			Ok(()) => Ok(self),
427			Err(()) => Err(()),
428		}
429	}
430}
431
432/// Either an amount of a single fungible asset, or a single well-identified non-fungible asset.
433#[derive(
434	Clone,
435	Eq,
436	PartialEq,
437	Debug,
438	Encode,
439	Decode,
440	TypeInfo,
441	MaxEncodedLen,
442	serde::Serialize,
443	serde::Deserialize,
444)]
445pub struct Asset {
446	/// The overall asset identity (aka *class*, in the case of a non-fungible).
447	pub id: AssetId,
448	/// The fungibility of the asset, which contains either the amount (in the case of a fungible
449	/// asset) or the *instance ID*, the secondary asset identifier.
450	pub fun: Fungibility,
451}
452
453impl PartialOrd for Asset {
454	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
455		Some(self.cmp(other))
456	}
457}
458
459impl Ord for Asset {
460	fn cmp(&self, other: &Self) -> Ordering {
461		match (&self.fun, &other.fun) {
462			(Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less,
463			(Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater,
464			_ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)),
465		}
466	}
467}
468
469impl<A: Into<AssetId>, B: Into<Fungibility>> From<(A, B)> for Asset {
470	fn from((id, fun): (A, B)) -> Asset {
471		Asset { fun: fun.into(), id: id.into() }
472	}
473}
474
475impl Asset {
476	pub fn is_fungible(&self, maybe_id: Option<AssetId>) -> bool {
477		use Fungibility::*;
478		matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id)
479	}
480
481	pub fn is_non_fungible(&self, maybe_id: Option<AssetId>) -> bool {
482		use Fungibility::*;
483		matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id)
484	}
485
486	/// Prepend a `Location` to a concrete asset, giving it a new root location.
487	pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> {
488		self.id.prepend_with(prepend)
489	}
490
491	/// Returns true if `self` is a super-set of the given `inner` asset.
492	pub fn contains(&self, inner: &Asset) -> bool {
493		use Fungibility::*;
494		if self.id == inner.id {
495			match (&self.fun, &inner.fun) {
496				(Fungible(a), Fungible(i)) if a >= i => return true,
497				(NonFungible(a), NonFungible(i)) if a == i => return true,
498				_ => (),
499			}
500		}
501		false
502	}
503}
504
505impl Reanchorable for Asset {
506	type Error = ();
507
508	/// Mutate the location of the asset identifier if concrete, giving it the same location
509	/// relative to a `target` context. The local context is provided as `context`.
510	fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
511		self.id.reanchor(target, context)
512	}
513
514	/// Mutate the location of the asset identifier if concrete, giving it the same location
515	/// relative to a `target` context. The local context is provided as `context`.
516	fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, ()> {
517		self.id.reanchor(target, context)?;
518		Ok(self)
519	}
520}
521
522impl TryFrom<OldAsset> for Asset {
523	type Error = ();
524	fn try_from(old: OldAsset) -> Result<Self, ()> {
525		Ok(Self { id: old.id.try_into()?, fun: old.fun.try_into()? })
526	}
527}
528
529/// A `Vec` of `Asset`s.
530///
531/// There are a number of invariants which the construction and mutation functions must ensure are
532/// maintained:
533/// - It may contain no items of duplicate asset class;
534/// - All items must be ordered;
535/// - The number of items should grow no larger than `MAX_ITEMS_IN_ASSETS`.
536#[derive(
537	Clone,
538	Eq,
539	PartialEq,
540	Ord,
541	PartialOrd,
542	Debug,
543	Encode,
544	TypeInfo,
545	Default,
546	serde::Serialize,
547	serde::Deserialize,
548)]
549pub struct Assets(Vec<Asset>);
550
551/// Maximum number of items we expect in a single `Assets` value. Note this is not (yet)
552/// enforced, and just serves to provide a sensible `max_encoded_len` for `Assets`.
553pub const MAX_ITEMS_IN_ASSETS: usize = 20;
554
555impl MaxEncodedLen for Assets {
556	fn max_encoded_len() -> usize {
557		Asset::max_encoded_len() * MAX_ITEMS_IN_ASSETS
558	}
559}
560
561impl Decode for Assets {
562	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
563		let bounded_instructions =
564			BoundedVec::<Asset, ConstU32<{ MAX_ITEMS_IN_ASSETS as u32 }>>::decode(input)?;
565		Self::from_sorted_and_deduplicated(bounded_instructions.into_inner())
566			.map_err(|()| "Out of order".into())
567	}
568}
569
570impl TryFrom<OldAssets> for Assets {
571	type Error = ();
572	fn try_from(old: OldAssets) -> Result<Self, ()> {
573		let v = old
574			.into_inner()
575			.into_iter()
576			.map(Asset::try_from)
577			.collect::<Result<Vec<_>, ()>>()?;
578		Ok(Assets(v))
579	}
580}
581
582impl From<Vec<Asset>> for Assets {
583	fn from(mut assets: Vec<Asset>) -> Self {
584		let mut res = Vec::with_capacity(assets.len());
585		if !assets.is_empty() {
586			assets.sort();
587			let mut iter = assets.into_iter();
588			if let Some(first) = iter.next() {
589				let last = iter.fold(first, |a, b| -> Asset {
590					match (a, b) {
591						(
592							Asset { fun: Fungibility::Fungible(a_amount), id: a_id },
593							Asset { fun: Fungibility::Fungible(b_amount), id: b_id },
594						) if a_id == b_id => Asset {
595							id: a_id,
596							fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)),
597						},
598						(
599							Asset { fun: Fungibility::NonFungible(a_instance), id: a_id },
600							Asset { fun: Fungibility::NonFungible(b_instance), id: b_id },
601						) if a_id == b_id && a_instance == b_instance =>
602							Asset { fun: Fungibility::NonFungible(a_instance), id: a_id },
603						(to_push, to_remember) => {
604							res.push(to_push);
605							to_remember
606						},
607					}
608				});
609				res.push(last);
610			}
611		}
612		Self(res)
613	}
614}
615
616impl<T: Into<Asset>> From<T> for Assets {
617	fn from(x: T) -> Self {
618		Self(vec![x.into()])
619	}
620}
621
622impl Assets {
623	/// A new (empty) value.
624	pub fn new() -> Self {
625		Self(Vec::new())
626	}
627
628	/// Create a new instance of `Assets` from a `Vec<Asset>` whose contents are sorted
629	/// and which contain no duplicates.
630	///
631	/// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates.
632	/// If you can't guarantee that `r` is sorted and deduplicated, then use
633	/// `From::<Vec<Asset>>::from` which is infallible.
634	pub fn from_sorted_and_deduplicated(r: Vec<Asset>) -> Result<Self, ()> {
635		if r.is_empty() {
636			return Ok(Self(Vec::new()))
637		}
638		r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&Asset, ()> {
639			if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) {
640				Ok(b)
641			} else {
642				Err(())
643			}
644		})?;
645		Ok(Self(r))
646	}
647
648	/// Create a new instance of `Assets` from a `Vec<Asset>` whose contents are sorted
649	/// and which contain no duplicates.
650	///
651	/// In release mode, this skips any checks to ensure that `r` is correct, making it a
652	/// negligible-cost operation. Generally though you should avoid using it unless you have a
653	/// strict proof that `r` is valid.
654	#[cfg(test)]
655	pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<Asset>) -> Self {
656		Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped")
657	}
658	/// Create a new instance of `Assets` from a `Vec<Asset>` whose contents are sorted
659	/// and which contain no duplicates.
660	///
661	/// In release mode, this skips any checks to ensure that `r` is correct, making it a
662	/// negligible-cost operation. Generally though you should avoid using it unless you have a
663	/// strict proof that `r` is valid.
664	///
665	/// In test mode, this checks anyway and panics on fail.
666	#[cfg(not(test))]
667	pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<Asset>) -> Self {
668		Self(r)
669	}
670
671	/// Add some asset onto the list, saturating. This is quite a laborious operation since it
672	/// maintains the ordering.
673	pub fn push(&mut self, a: Asset) {
674		for asset in self.0.iter_mut().filter(|x| x.id == a.id) {
675			match (&a.fun, &mut asset.fun) {
676				(Fungibility::Fungible(amount), Fungibility::Fungible(balance)) => {
677					*balance = balance.saturating_add(*amount);
678					return
679				},
680				(Fungibility::NonFungible(inst1), Fungibility::NonFungible(inst2))
681					if inst1 == inst2 =>
682					return,
683				_ => (),
684			}
685		}
686		self.0.push(a);
687		self.0.sort();
688	}
689
690	/// Returns `true` if this definitely represents no asset.
691	pub fn is_none(&self) -> bool {
692		self.0.is_empty()
693	}
694
695	/// Returns true if `self` is a super-set of the given `inner` asset.
696	pub fn contains(&self, inner: &Asset) -> bool {
697		self.0.iter().any(|i| i.contains(inner))
698	}
699
700	/// Consume `self` and return the inner vec.
701	#[deprecated = "Use `into_inner()` instead"]
702	pub fn drain(self) -> Vec<Asset> {
703		self.0
704	}
705
706	/// Consume `self` and return the inner vec.
707	pub fn into_inner(self) -> Vec<Asset> {
708		self.0
709	}
710
711	/// Return a reference to the inner vec.
712	pub fn inner(&self) -> &Vec<Asset> {
713		&self.0
714	}
715
716	/// Return the number of distinct asset instances contained.
717	pub fn len(&self) -> usize {
718		self.0.len()
719	}
720
721	/// Prepend a `Location` to any concrete asset items, giving it a new root location.
722	pub fn prepend_with(&mut self, prefix: &Location) -> Result<(), ()> {
723		self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))?;
724		self.0.sort();
725		Ok(())
726	}
727
728	/// Return a reference to an item at a specific index or `None` if it doesn't exist.
729	pub fn get(&self, index: usize) -> Option<&Asset> {
730		self.0.get(index)
731	}
732}
733
734impl Reanchorable for Assets {
735	type Error = ();
736
737	fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
738		self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))?;
739		self.0.sort();
740		Ok(())
741	}
742
743	fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, ()> {
744		match self.reanchor(target, context) {
745			Ok(()) => Ok(self),
746			Err(()) => Err(()),
747		}
748	}
749}
750
751/// A wildcard representing a set of assets.
752#[derive(
753	Clone,
754	Eq,
755	PartialEq,
756	Ord,
757	PartialOrd,
758	Debug,
759	Encode,
760	Decode,
761	TypeInfo,
762	MaxEncodedLen,
763	serde::Serialize,
764	serde::Deserialize,
765)]
766pub enum WildAsset {
767	/// All assets in Holding.
768	All,
769	/// All assets in Holding of a given fungibility and ID.
770	AllOf { id: AssetId, fun: WildFungibility },
771	/// All assets in Holding, up to `u32` individual assets (different instances of non-fungibles
772	/// are separate assets).
773	AllCounted(#[codec(compact)] u32),
774	/// All assets in Holding of a given fungibility and ID up to `count` individual assets
775	/// (different instances of non-fungibles are separate assets).
776	AllOfCounted {
777		id: AssetId,
778		fun: WildFungibility,
779		#[codec(compact)]
780		count: u32,
781	},
782}
783
784impl TryFrom<OldWildAsset> for WildAsset {
785	type Error = ();
786	fn try_from(old: OldWildAsset) -> Result<WildAsset, ()> {
787		use OldWildAsset::*;
788		Ok(match old {
789			AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? },
790			All => Self::All,
791			AllOfCounted { id, fun, count } =>
792				Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count },
793			AllCounted(count) => Self::AllCounted(count),
794		})
795	}
796}
797
798impl WildAsset {
799	/// Returns true if `self` is a super-set of the given `inner` asset.
800	pub fn contains(&self, inner: &Asset) -> bool {
801		use WildAsset::*;
802		match self {
803			AllOfCounted { count: 0, .. } | AllCounted(0) => false,
804			AllOf { fun, id } | AllOfCounted { id, fun, .. } =>
805				inner.fun.is_kind(*fun) && &inner.id == id,
806			All | AllCounted(_) => true,
807		}
808	}
809
810	/// Returns true if the wild element of `self` matches `inner`.
811	///
812	/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
813	/// always returning `false` when equal to 0.
814	#[deprecated = "Use `contains` instead"]
815	pub fn matches(&self, inner: &Asset) -> bool {
816		self.contains(inner)
817	}
818
819	/// Mutate the asset to represent the same value from the perspective of a new `target`
820	/// location. The local chain's location is provided in `context`.
821	pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
822		use WildAsset::*;
823		match self {
824			AllOf { ref mut id, .. } | AllOfCounted { ref mut id, .. } =>
825				id.reanchor(target, context),
826			All | AllCounted(_) => Ok(()),
827		}
828	}
829
830	/// Maximum count of assets allowed to match, if any.
831	pub fn count(&self) -> Option<u32> {
832		use WildAsset::*;
833		match self {
834			AllOfCounted { count, .. } | AllCounted(count) => Some(*count),
835			All | AllOf { .. } => None,
836		}
837	}
838
839	/// Explicit limit on number of assets allowed to match, if any.
840	pub fn limit(&self) -> Option<u32> {
841		self.count()
842	}
843
844	/// Consume self and return the equivalent version but counted and with the `count` set to the
845	/// given parameter.
846	pub fn counted(self, count: u32) -> Self {
847		use WildAsset::*;
848		match self {
849			AllOfCounted { fun, id, .. } | AllOf { fun, id } => AllOfCounted { fun, id, count },
850			All | AllCounted(_) => AllCounted(count),
851		}
852	}
853}
854
855impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildAsset {
856	fn from((id, fun): (A, B)) -> WildAsset {
857		WildAsset::AllOf { fun: fun.into(), id: id.into() }
858	}
859}
860
861/// `Asset` collection, defined either by a number of `Assets` or a single wildcard.
862#[derive(
863	Clone,
864	Eq,
865	PartialEq,
866	Ord,
867	PartialOrd,
868	Debug,
869	Encode,
870	Decode,
871	TypeInfo,
872	MaxEncodedLen,
873	serde::Serialize,
874	serde::Deserialize,
875)]
876pub enum AssetFilter {
877	/// Specify the filter as being everything contained by the given `Assets` inner.
878	Definite(Assets),
879	/// Specify the filter as the given `WildAsset` wildcard.
880	Wild(WildAsset),
881}
882
883impl<T: Into<WildAsset>> From<T> for AssetFilter {
884	fn from(x: T) -> Self {
885		Self::Wild(x.into())
886	}
887}
888
889impl From<Asset> for AssetFilter {
890	fn from(x: Asset) -> Self {
891		Self::Definite(vec![x].into())
892	}
893}
894
895impl From<Vec<Asset>> for AssetFilter {
896	fn from(x: Vec<Asset>) -> Self {
897		Self::Definite(x.into())
898	}
899}
900
901impl From<Assets> for AssetFilter {
902	fn from(x: Assets) -> Self {
903		Self::Definite(x)
904	}
905}
906
907impl AssetFilter {
908	/// Returns true if `inner` would be matched by `self`.
909	///
910	/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
911	/// always returning `false` when equal to 0.
912	pub fn matches(&self, inner: &Asset) -> bool {
913		match self {
914			AssetFilter::Definite(ref assets) => assets.contains(inner),
915			AssetFilter::Wild(ref wild) => wild.contains(inner),
916		}
917	}
918
919	/// Mutate the location of the asset identifier if concrete, giving it the same location
920	/// relative to a `target` context. The local context is provided as `context`.
921	pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
922		match self {
923			AssetFilter::Definite(ref mut assets) => assets.reanchor(target, context),
924			AssetFilter::Wild(ref mut wild) => wild.reanchor(target, context),
925		}
926	}
927
928	/// Maximum count of assets it is possible to match, if known.
929	pub fn count(&self) -> Option<u32> {
930		use AssetFilter::*;
931		match self {
932			Definite(x) => Some(x.len() as u32),
933			Wild(x) => x.count(),
934		}
935	}
936
937	/// Explicit limit placed on the number of items, if any.
938	pub fn limit(&self) -> Option<u32> {
939		use AssetFilter::*;
940		match self {
941			Definite(_) => None,
942			Wild(x) => x.limit(),
943		}
944	}
945}
946
947impl TryFrom<OldAssetFilter> for AssetFilter {
948	type Error = ();
949	fn try_from(old: OldAssetFilter) -> Result<AssetFilter, ()> {
950		Ok(match old {
951			OldAssetFilter::Definite(x) => Self::Definite(x.try_into()?),
952			OldAssetFilter::Wild(x) => Self::Wild(x.try_into()?),
953		})
954	}
955}
956
957#[cfg(test)]
958mod tests {
959	use super::super::prelude::*;
960
961	#[test]
962	fn conversion_works() {
963		let _: Assets = (Here, 1u128).into();
964	}
965
966	#[test]
967	fn from_sorted_and_deduplicated_works() {
968		use super::*;
969		use alloc::vec;
970
971		let empty = vec![];
972		let r = Assets::from_sorted_and_deduplicated(empty);
973		assert_eq!(r, Ok(Assets(vec![])));
974
975		let dup_fun = vec![(Here, 100).into(), (Here, 10).into()];
976		let r = Assets::from_sorted_and_deduplicated(dup_fun);
977		assert!(r.is_err());
978
979		let dup_nft = vec![(Here, *b"notgood!").into(), (Here, *b"notgood!").into()];
980		let r = Assets::from_sorted_and_deduplicated(dup_nft);
981		assert!(r.is_err());
982
983		let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
984		let r = Assets::from_sorted_and_deduplicated(good_fun.clone());
985		assert_eq!(r, Ok(Assets(good_fun)));
986
987		let bad_fun = vec![(Parent, 10).into(), (Here, 10).into()];
988		let r = Assets::from_sorted_and_deduplicated(bad_fun);
989		assert!(r.is_err());
990
991		let good_nft = vec![(Here, ()).into(), (Here, *b"good").into()];
992		let r = Assets::from_sorted_and_deduplicated(good_nft.clone());
993		assert_eq!(r, Ok(Assets(good_nft)));
994
995		let bad_nft = vec![(Here, *b"bad!").into(), (Here, ()).into()];
996		let r = Assets::from_sorted_and_deduplicated(bad_nft);
997		assert!(r.is_err());
998
999		let mixed_good = vec![(Here, 10).into(), (Here, *b"good").into()];
1000		let r = Assets::from_sorted_and_deduplicated(mixed_good.clone());
1001		assert_eq!(r, Ok(Assets(mixed_good)));
1002
1003		let mixed_bad = vec![(Here, *b"bad!").into(), (Here, 10).into()];
1004		let r = Assets::from_sorted_and_deduplicated(mixed_bad);
1005		assert!(r.is_err());
1006	}
1007
1008	#[test]
1009	fn reanchor_preserves_sorting() {
1010		use super::*;
1011		use alloc::vec;
1012
1013		let reanchor_context: Junctions = Parachain(2000).into();
1014		let dest = Location::new(1, []);
1015
1016		let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1017		let mut asset_1_reanchored = asset_1.clone();
1018		assert!(asset_1_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1019		assert_eq!(
1020			asset_1_reanchored,
1021			(Location::new(0, [Parachain(2000), PalletInstance(50), GeneralIndex(1)]), 10).into()
1022		);
1023
1024		let asset_2: Asset = (Location::new(1, []), 10).into();
1025		let mut asset_2_reanchored = asset_2.clone();
1026		assert!(asset_2_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1027		assert_eq!(asset_2_reanchored, (Location::new(0, []), 10).into());
1028
1029		let asset_3: Asset = (Location::new(1, [Parachain(1000)]), 10).into();
1030		let mut asset_3_reanchored = asset_3.clone();
1031		assert!(asset_3_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1032		assert_eq!(asset_3_reanchored, (Location::new(0, [Parachain(1000)]), 10).into());
1033
1034		let mut assets: Assets = vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
1035		assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());
1036
1037		// decoding respects limits and sorting
1038		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1039
1040		assert!(assets.reanchor(&dest, &reanchor_context).is_ok());
1041		assert_eq!(assets.0, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored]);
1042
1043		// decoding respects limits and sorting
1044		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1045	}
1046
1047	#[test]
1048	fn prepend_preserves_sorting() {
1049		use super::*;
1050		use alloc::vec;
1051
1052		let prefix = Location::new(0, [Parachain(1000)]);
1053
1054		let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1055		let mut asset_1_prepended = asset_1.clone();
1056		assert!(asset_1_prepended.prepend_with(&prefix).is_ok());
1057		// changes interior X2->X3
1058		assert_eq!(
1059			asset_1_prepended,
1060			(Location::new(0, [Parachain(1000), PalletInstance(50), GeneralIndex(1)]), 10).into()
1061		);
1062
1063		let asset_2: Asset = (Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1064		let mut asset_2_prepended = asset_2.clone();
1065		assert!(asset_2_prepended.prepend_with(&prefix).is_ok());
1066		// changes parent
1067		assert_eq!(
1068			asset_2_prepended,
1069			(Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into()
1070		);
1071
1072		let asset_3: Asset = (Location::new(2, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1073		let mut asset_3_prepended = asset_3.clone();
1074		assert!(asset_3_prepended.prepend_with(&prefix).is_ok());
1075		// changes parent
1076		assert_eq!(
1077			asset_3_prepended,
1078			(Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into()
1079		);
1080
1081		// `From` impl does sorting.
1082		let mut assets: Assets = vec![asset_1, asset_2, asset_3].into();
1083		// decoding respects limits and sorting
1084		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1085
1086		// let's do `prepend_with`
1087		assert!(assets.prepend_with(&prefix).is_ok());
1088		assert_eq!(assets.0, vec![asset_2_prepended, asset_1_prepended, asset_3_prepended]);
1089
1090		// decoding respects limits and sorting
1091		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1092	}
1093
1094	#[test]
1095	fn decoding_respects_limit() {
1096		use super::*;
1097
1098		// Having lots of one asset will work since they are deduplicated
1099		let lots_of_one_asset: Assets =
1100			vec![(GeneralIndex(1), 1u128).into(); MAX_ITEMS_IN_ASSETS + 1].into();
1101		let encoded = lots_of_one_asset.encode();
1102		assert!(Assets::decode(&mut &encoded[..]).is_ok());
1103
1104		// Fewer assets than the limit works
1105		let mut few_assets: Assets = Vec::new().into();
1106		for i in 0..MAX_ITEMS_IN_ASSETS {
1107			few_assets.push((GeneralIndex(i as u128), 1u128).into());
1108		}
1109		let encoded = few_assets.encode();
1110		assert!(Assets::decode(&mut &encoded[..]).is_ok());
1111
1112		// Having lots of different assets will not work
1113		let mut too_many_different_assets: Assets = Vec::new().into();
1114		for i in 0..MAX_ITEMS_IN_ASSETS + 1 {
1115			too_many_different_assets.push((GeneralIndex(i as u128), 1u128).into());
1116		}
1117		let encoded = too_many_different_assets.encode();
1118		assert!(Assets::decode(&mut &encoded[..]).is_err());
1119	}
1120}