referrerpolicy=no-referrer-when-downgrade

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