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}
289
290impl Fungibility {
291	pub fn is_kind(&self, w: WildFungibility) -> bool {
292		use Fungibility::*;
293		use WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible};
294		matches!((self, w), (Fungible(_), WildFungible) | (NonFungible(_), WildNonFungible))
295	}
296}
297
298impl From<i32> for Fungibility {
299	fn from(amount: i32) -> Fungibility {
300		debug_assert_ne!(amount, 0);
301		Fungibility::Fungible(amount as u128)
302	}
303}
304
305impl From<u128> for Fungibility {
306	fn from(amount: u128) -> Fungibility {
307		debug_assert_ne!(amount, 0);
308		Fungibility::Fungible(amount)
309	}
310}
311
312impl<T: Into<AssetInstance>> From<T> for Fungibility {
313	fn from(instance: T) -> Fungibility {
314		Fungibility::NonFungible(instance.into())
315	}
316}
317
318impl TryFrom<OldFungibility> for Fungibility {
319	type Error = ();
320	fn try_from(value: OldFungibility) -> Result<Self, Self::Error> {
321		use OldFungibility::*;
322		Ok(match value {
323			Fungible(n) => Self::Fungible(n),
324			NonFungible(i) => Self::NonFungible(i.try_into()?),
325		})
326	}
327}
328
329/// Classification of whether an asset is fungible or not.
330#[derive(
331	Copy,
332	Clone,
333	Eq,
334	PartialEq,
335	Ord,
336	PartialOrd,
337	Debug,
338	Encode,
339	Decode,
340	DecodeWithMemTracking,
341	TypeInfo,
342	MaxEncodedLen,
343	serde::Serialize,
344	serde::Deserialize,
345)]
346pub enum WildFungibility {
347	/// The asset is fungible.
348	Fungible,
349	/// The asset is not fungible.
350	NonFungible,
351}
352
353impl TryFrom<OldWildFungibility> for WildFungibility {
354	type Error = ();
355	fn try_from(value: OldWildFungibility) -> Result<Self, Self::Error> {
356		use OldWildFungibility::*;
357		Ok(match value {
358			Fungible => Self::Fungible,
359			NonFungible => Self::NonFungible,
360		})
361	}
362}
363
364/// Location to identify an asset.
365#[derive(
366	Clone,
367	Eq,
368	PartialEq,
369	Ord,
370	PartialOrd,
371	Debug,
372	Encode,
373	Decode,
374	DecodeWithMemTracking,
375	TypeInfo,
376	MaxEncodedLen,
377	serde::Serialize,
378	serde::Deserialize,
379)]
380pub struct AssetId(pub Location);
381
382impl<T: Into<Location>> From<T> for AssetId {
383	fn from(x: T) -> Self {
384		Self(x.into())
385	}
386}
387
388impl TryFrom<OldAssetId> for AssetId {
389	type Error = ();
390	fn try_from(old: OldAssetId) -> Result<Self, ()> {
391		Ok(Self(old.0.try_into()?))
392	}
393}
394
395impl AssetId {
396	/// Prepend a `Location` to an asset id, giving it a new root location.
397	pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> {
398		self.0.prepend_with(prepend.clone()).map_err(|_| ())?;
399		Ok(())
400	}
401
402	/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding
403	/// `Asset` value.
404	pub fn into_asset(self, fun: Fungibility) -> Asset {
405		Asset { fun, id: self }
406	}
407
408	/// Use the value of `self` along with a `fun` fungibility specifier to create the corresponding
409	/// `WildAsset` wildcard (`AllOf`) value.
410	pub fn into_wild(self, fun: WildFungibility) -> WildAsset {
411		WildAsset::AllOf { fun, id: self }
412	}
413}
414
415impl Reanchorable for AssetId {
416	type Error = ();
417
418	/// Mutate the asset to represent the same value from the perspective of a new `target`
419	/// location. The local chain's location is provided in `context`.
420	fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
421		self.0.reanchor(target, context)?;
422		Ok(())
423	}
424
425	fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, ()> {
426		match self.reanchor(target, context) {
427			Ok(()) => Ok(self),
428			Err(()) => Err(()),
429		}
430	}
431}
432
433/// Either an amount of a single fungible asset, or a single well-identified non-fungible asset.
434#[derive(
435	Clone,
436	Eq,
437	PartialEq,
438	Debug,
439	Encode,
440	Decode,
441	DecodeWithMemTracking,
442	TypeInfo,
443	MaxEncodedLen,
444	serde::Serialize,
445	serde::Deserialize,
446)]
447pub struct Asset {
448	/// The overall asset identity (aka *class*, in the case of a non-fungible).
449	pub id: AssetId,
450	/// The fungibility of the asset, which contains either the amount (in the case of a fungible
451	/// asset) or the *instance ID*, the secondary asset identifier.
452	pub fun: Fungibility,
453}
454
455impl PartialOrd for Asset {
456	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
457		Some(self.cmp(other))
458	}
459}
460
461impl Ord for Asset {
462	fn cmp(&self, other: &Self) -> Ordering {
463		match (&self.fun, &other.fun) {
464			(Fungibility::Fungible(..), Fungibility::NonFungible(..)) => Ordering::Less,
465			(Fungibility::NonFungible(..), Fungibility::Fungible(..)) => Ordering::Greater,
466			_ => (&self.id, &self.fun).cmp(&(&other.id, &other.fun)),
467		}
468	}
469}
470
471impl<A: Into<AssetId>, B: Into<Fungibility>> From<(A, B)> for Asset {
472	fn from((id, fun): (A, B)) -> Asset {
473		Asset { fun: fun.into(), id: id.into() }
474	}
475}
476
477impl Asset {
478	pub fn is_fungible(&self, maybe_id: Option<AssetId>) -> bool {
479		use Fungibility::*;
480		matches!(self.fun, Fungible(..)) && maybe_id.map_or(true, |i| i == self.id)
481	}
482
483	pub fn is_non_fungible(&self, maybe_id: Option<AssetId>) -> bool {
484		use Fungibility::*;
485		matches!(self.fun, NonFungible(..)) && maybe_id.map_or(true, |i| i == self.id)
486	}
487
488	/// Prepend a `Location` to a concrete asset, giving it a new root location.
489	pub fn prepend_with(&mut self, prepend: &Location) -> Result<(), ()> {
490		self.id.prepend_with(prepend)
491	}
492
493	/// Returns true if `self` is a super-set of the given `inner` asset.
494	pub fn contains(&self, inner: &Asset) -> bool {
495		use Fungibility::*;
496		if self.id == inner.id {
497			match (&self.fun, &inner.fun) {
498				(Fungible(a), Fungible(i)) if a >= i => return true,
499				(NonFungible(a), NonFungible(i)) if a == i => return true,
500				_ => (),
501			}
502		}
503		false
504	}
505}
506
507impl Reanchorable for Asset {
508	type Error = ();
509
510	/// Mutate the location of the asset identifier if concrete, giving it the same location
511	/// relative to a `target` context. The local context is provided as `context`.
512	fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
513		self.id.reanchor(target, context)
514	}
515
516	/// Mutate the location of the asset identifier if concrete, giving it the same location
517	/// relative to a `target` context. The local context is provided as `context`.
518	fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, ()> {
519		self.id.reanchor(target, context)?;
520		Ok(self)
521	}
522}
523
524impl TryFrom<OldAsset> for Asset {
525	type Error = ();
526	fn try_from(old: OldAsset) -> Result<Self, ()> {
527		Ok(Self { id: old.id.try_into()?, fun: old.fun.try_into()? })
528	}
529}
530
531/// A `Vec` of `Asset`s.
532///
533/// There are a number of invariants which the construction and mutation functions must ensure are
534/// maintained:
535/// - It may contain no items of duplicate asset class;
536/// - All items must be ordered;
537/// - The number of items should grow no larger than `MAX_ITEMS_IN_ASSETS`.
538#[derive(
539	Clone,
540	Eq,
541	PartialEq,
542	Ord,
543	PartialOrd,
544	Debug,
545	Encode,
546	DecodeWithMemTracking,
547	TypeInfo,
548	Default,
549	serde::Serialize,
550	serde::Deserialize,
551)]
552pub struct Assets(Vec<Asset>);
553
554/// Maximum number of items we expect in a single `Assets` value.
555/// This is enforced when decoding and provides a sensible `max_encoded_len` for `Assets`.
556pub const MAX_ITEMS_IN_ASSETS: usize = 20;
557
558impl MaxEncodedLen for Assets {
559	fn max_encoded_len() -> usize {
560		Asset::max_encoded_len() * MAX_ITEMS_IN_ASSETS
561	}
562}
563
564impl Decode for Assets {
565	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
566		let bounded_instructions =
567			BoundedVec::<Asset, ConstU32<{ MAX_ITEMS_IN_ASSETS as u32 }>>::decode(input)?;
568		Self::from_sorted_and_deduplicated(bounded_instructions.into_inner())
569			.map_err(|()| "Out of order".into())
570	}
571}
572
573impl TryFrom<OldAssets> for Assets {
574	type Error = ();
575	fn try_from(old: OldAssets) -> Result<Self, ()> {
576		let v = old
577			.into_inner()
578			.into_iter()
579			.map(Asset::try_from)
580			.collect::<Result<Vec<_>, ()>>()?;
581		Ok(Assets(v))
582	}
583}
584
585impl From<Vec<Asset>> for Assets {
586	fn from(mut assets: Vec<Asset>) -> Self {
587		let mut res = Vec::with_capacity(assets.len());
588		if !assets.is_empty() {
589			assets.sort();
590			let mut iter = assets.into_iter();
591			if let Some(first) = iter.next() {
592				let last = iter.fold(first, |a, b| -> Asset {
593					match (a, b) {
594						(
595							Asset { fun: Fungibility::Fungible(a_amount), id: a_id },
596							Asset { fun: Fungibility::Fungible(b_amount), id: b_id },
597						) if a_id == b_id => Asset {
598							id: a_id,
599							fun: Fungibility::Fungible(a_amount.saturating_add(b_amount)),
600						},
601						(
602							Asset { fun: Fungibility::NonFungible(a_instance), id: a_id },
603							Asset { fun: Fungibility::NonFungible(b_instance), id: b_id },
604						) if a_id == b_id && a_instance == b_instance => {
605							Asset { fun: Fungibility::NonFungible(a_instance), id: a_id }
606						},
607						(to_push, to_remember) => {
608							res.push(to_push);
609							to_remember
610						},
611					}
612				});
613				res.push(last);
614			}
615		}
616		Self(res)
617	}
618}
619
620impl<T: Into<Asset>> From<T> for Assets {
621	fn from(x: T) -> Self {
622		Self(vec![x.into()])
623	}
624}
625
626impl Assets {
627	/// A new (empty) value.
628	pub fn new() -> Self {
629		Self(Vec::new())
630	}
631
632	/// Create a new instance of `Assets` from a `Vec<Asset>` whose contents are sorted
633	/// and which contain no duplicates.
634	///
635	/// Returns `Ok` if the operation succeeds and `Err` if `r` is out of order or had duplicates.
636	/// If you can't guarantee that `r` is sorted and deduplicated, then use
637	/// `From::<Vec<Asset>>::from` which is infallible.
638	pub fn from_sorted_and_deduplicated(r: Vec<Asset>) -> Result<Self, ()> {
639		if r.is_empty() {
640			return Ok(Self(Vec::new()));
641		}
642		r.iter().skip(1).try_fold(&r[0], |a, b| -> Result<&Asset, ()> {
643			if a.id < b.id || a < b && (a.is_non_fungible(None) || b.is_non_fungible(None)) {
644				Ok(b)
645			} else {
646				Err(())
647			}
648		})?;
649		Ok(Self(r))
650	}
651
652	/// Create a new instance of `Assets` from a `Vec<Asset>` whose contents are sorted
653	/// and which contain no duplicates.
654	///
655	/// In release mode, this skips any checks to ensure that `r` is correct, making it a
656	/// negligible-cost operation. Generally though you should avoid using it unless you have a
657	/// strict proof that `r` is valid.
658	#[cfg(test)]
659	pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<Asset>) -> Self {
660		Self::from_sorted_and_deduplicated(r).expect("Invalid input r is not sorted/deduped")
661	}
662	/// Create a new instance of `Assets` from a `Vec<Asset>` whose contents are sorted
663	/// and which contain no duplicates.
664	///
665	/// In release mode, this skips any checks to ensure that `r` is correct, making it a
666	/// negligible-cost operation. Generally though you should avoid using it unless you have a
667	/// strict proof that `r` is valid.
668	///
669	/// In test mode, this checks anyway and panics on fail.
670	#[cfg(not(test))]
671	pub fn from_sorted_and_deduplicated_skip_checks(r: Vec<Asset>) -> Self {
672		Self(r)
673	}
674
675	/// Add some asset onto the list, saturating. This is quite a laborious operation since it
676	/// maintains the ordering.
677	pub fn push(&mut self, a: Asset) {
678		for asset in self.0.iter_mut().filter(|x| x.id == a.id) {
679			match (&a.fun, &mut asset.fun) {
680				(Fungibility::Fungible(amount), Fungibility::Fungible(balance)) => {
681					*balance = balance.saturating_add(*amount);
682					return;
683				},
684				(Fungibility::NonFungible(inst1), Fungibility::NonFungible(inst2))
685					if inst1 == inst2 =>
686				{
687					return
688				},
689				_ => (),
690			}
691		}
692		self.0.push(a);
693		self.0.sort();
694	}
695
696	/// Returns `true` if this definitely represents no asset.
697	pub fn is_none(&self) -> bool {
698		self.0.is_empty()
699	}
700
701	/// Returns true if `self` is a super-set of the given `inner` asset.
702	pub fn contains(&self, inner: &Asset) -> bool {
703		self.0.iter().any(|i| i.contains(inner))
704	}
705
706	/// Consume `self` and return the inner vec.
707	#[deprecated = "Use `into_inner()` instead"]
708	pub fn drain(self) -> Vec<Asset> {
709		self.0
710	}
711
712	/// Consume `self` and return the inner vec.
713	pub fn into_inner(self) -> Vec<Asset> {
714		self.0
715	}
716
717	/// Return a reference to the inner vec.
718	pub fn inner(&self) -> &Vec<Asset> {
719		&self.0
720	}
721
722	/// Return the number of distinct asset instances contained.
723	pub fn len(&self) -> usize {
724		self.0.len()
725	}
726
727	/// Prepend a `Location` to any concrete asset items, giving it a new root location.
728	pub fn prepend_with(&mut self, prefix: &Location) -> Result<(), ()> {
729		self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))?;
730		self.0.sort();
731		Ok(())
732	}
733
734	/// Return a reference to an item at a specific index or `None` if it doesn't exist.
735	pub fn get(&self, index: usize) -> Option<&Asset> {
736		self.0.get(index)
737	}
738}
739
740impl Reanchorable for Assets {
741	type Error = ();
742
743	fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
744		self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))?;
745		self.0.sort();
746		Ok(())
747	}
748
749	fn reanchored(mut self, target: &Location, context: &InteriorLocation) -> Result<Self, ()> {
750		match self.reanchor(target, context) {
751			Ok(()) => Ok(self),
752			Err(()) => Err(()),
753		}
754	}
755}
756
757/// A wildcard representing a set of assets.
758#[derive(
759	Clone,
760	Eq,
761	PartialEq,
762	Ord,
763	PartialOrd,
764	Debug,
765	Encode,
766	Decode,
767	DecodeWithMemTracking,
768	TypeInfo,
769	MaxEncodedLen,
770	serde::Serialize,
771	serde::Deserialize,
772)]
773pub enum WildAsset {
774	/// All assets in Holding.
775	All,
776	/// All assets in Holding of a given fungibility and ID.
777	AllOf { id: AssetId, fun: WildFungibility },
778	/// All assets in Holding, up to `u32` individual assets (different instances of non-fungibles
779	/// are separate assets).
780	AllCounted(#[codec(compact)] u32),
781	/// All assets in Holding of a given fungibility and ID up to `count` individual assets
782	/// (different instances of non-fungibles are separate assets).
783	AllOfCounted {
784		id: AssetId,
785		fun: WildFungibility,
786		#[codec(compact)]
787		count: u32,
788	},
789}
790
791impl TryFrom<OldWildAsset> for WildAsset {
792	type Error = ();
793	fn try_from(old: OldWildAsset) -> Result<WildAsset, ()> {
794		use OldWildAsset::*;
795		Ok(match old {
796			AllOf { id, fun } => Self::AllOf { id: id.try_into()?, fun: fun.try_into()? },
797			All => Self::All,
798			AllOfCounted { id, fun, count } => {
799				Self::AllOfCounted { id: id.try_into()?, fun: fun.try_into()?, count }
800			},
801			AllCounted(count) => Self::AllCounted(count),
802		})
803	}
804}
805
806impl WildAsset {
807	/// Returns true if `self` is a super-set of the given `inner` asset.
808	pub fn contains(&self, inner: &Asset) -> bool {
809		use WildAsset::*;
810		match self {
811			AllOfCounted { count: 0, .. } | AllCounted(0) => false,
812			AllOf { fun, id } | AllOfCounted { id, fun, .. } => {
813				inner.fun.is_kind(*fun) && &inner.id == id
814			},
815			All | AllCounted(_) => true,
816		}
817	}
818
819	/// Returns true if the wild element of `self` matches `inner`.
820	///
821	/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
822	/// always returning `false` when equal to 0.
823	#[deprecated = "Use `contains` instead"]
824	pub fn matches(&self, inner: &Asset) -> bool {
825		self.contains(inner)
826	}
827
828	/// Mutate the asset to represent the same value from the perspective of a new `target`
829	/// location. The local chain's location is provided in `context`.
830	pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
831		use WildAsset::*;
832		match self {
833			AllOf { ref mut id, .. } | AllOfCounted { ref mut id, .. } => {
834				id.reanchor(target, context)
835			},
836			All | AllCounted(_) => Ok(()),
837		}
838	}
839
840	/// Maximum count of assets allowed to match, if any.
841	pub fn count(&self) -> Option<u32> {
842		use WildAsset::*;
843		match self {
844			AllOfCounted { count, .. } | AllCounted(count) => Some(*count),
845			All | AllOf { .. } => None,
846		}
847	}
848
849	/// Explicit limit on number of assets allowed to match, if any.
850	pub fn limit(&self) -> Option<u32> {
851		self.count()
852	}
853
854	/// Consume self and return the equivalent version but counted and with the `count` set to the
855	/// given parameter.
856	pub fn counted(self, count: u32) -> Self {
857		use WildAsset::*;
858		match self {
859			AllOfCounted { fun, id, .. } | AllOf { fun, id } => AllOfCounted { fun, id, count },
860			All | AllCounted(_) => AllCounted(count),
861		}
862	}
863}
864
865impl<A: Into<AssetId>, B: Into<WildFungibility>> From<(A, B)> for WildAsset {
866	fn from((id, fun): (A, B)) -> WildAsset {
867		WildAsset::AllOf { fun: fun.into(), id: id.into() }
868	}
869}
870
871/// `Asset` collection, defined either by a number of `Assets` or a single wildcard.
872#[derive(
873	Clone,
874	Eq,
875	PartialEq,
876	Ord,
877	PartialOrd,
878	Debug,
879	Encode,
880	Decode,
881	DecodeWithMemTracking,
882	TypeInfo,
883	MaxEncodedLen,
884	serde::Serialize,
885	serde::Deserialize,
886)]
887pub enum AssetFilter {
888	/// Specify the filter as being everything contained by the given `Assets` inner.
889	Definite(Assets),
890	/// Specify the filter as the given `WildAsset` wildcard.
891	Wild(WildAsset),
892}
893
894impl<T: Into<WildAsset>> From<T> for AssetFilter {
895	fn from(x: T) -> Self {
896		Self::Wild(x.into())
897	}
898}
899
900impl From<Asset> for AssetFilter {
901	fn from(x: Asset) -> Self {
902		Self::Definite(vec![x].into())
903	}
904}
905
906impl From<Vec<Asset>> for AssetFilter {
907	fn from(x: Vec<Asset>) -> Self {
908		Self::Definite(x.into())
909	}
910}
911
912impl From<Assets> for AssetFilter {
913	fn from(x: Assets) -> Self {
914		Self::Definite(x)
915	}
916}
917
918impl AssetFilter {
919	/// Returns true if `inner` would be matched by `self`.
920	///
921	/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
922	/// always returning `false` when equal to 0.
923	pub fn matches(&self, inner: &Asset) -> bool {
924		match self {
925			AssetFilter::Definite(ref assets) => assets.contains(inner),
926			AssetFilter::Wild(ref wild) => wild.contains(inner),
927		}
928	}
929
930	/// Mutate the location of the asset identifier if concrete, giving it the same location
931	/// relative to a `target` context. The local context is provided as `context`.
932	pub fn reanchor(&mut self, target: &Location, context: &InteriorLocation) -> Result<(), ()> {
933		match self {
934			AssetFilter::Definite(ref mut assets) => assets.reanchor(target, context),
935			AssetFilter::Wild(ref mut wild) => wild.reanchor(target, context),
936		}
937	}
938
939	/// Maximum count of assets it is possible to match, if known.
940	pub fn count(&self) -> Option<u32> {
941		use AssetFilter::*;
942		match self {
943			Definite(x) => Some(x.len() as u32),
944			Wild(x) => x.count(),
945		}
946	}
947
948	/// Explicit limit placed on the number of items, if any.
949	pub fn limit(&self) -> Option<u32> {
950		use AssetFilter::*;
951		match self {
952			Definite(_) => None,
953			Wild(x) => x.limit(),
954		}
955	}
956}
957
958impl TryFrom<OldAssetFilter> for AssetFilter {
959	type Error = ();
960	fn try_from(old: OldAssetFilter) -> Result<AssetFilter, ()> {
961		Ok(match old {
962			OldAssetFilter::Definite(x) => Self::Definite(x.try_into()?),
963			OldAssetFilter::Wild(x) => Self::Wild(x.try_into()?),
964		})
965	}
966}
967
968/// Matches assets based on inner `AssetFilter` and tags them for a specific type of asset transfer.
969/// Please note: the transfer type is specific to each particular `(asset, source, dest)`
970/// combination, so it should always be built in the context of `source` after knowing `dest`.
971#[derive(
972	Clone,
973	Eq,
974	PartialEq,
975	Ord,
976	PartialOrd,
977	Debug,
978	Encode,
979	Decode,
980	DecodeWithMemTracking,
981	TypeInfo,
982	MaxEncodedLen,
983	serde::Serialize,
984	serde::Deserialize,
985)]
986pub enum AssetTransferFilter {
987	/// teleport assets matching `AssetFilter` to a specific destination
988	Teleport(AssetFilter),
989	/// reserve-transfer assets matching `AssetFilter` to a specific destination, using the local
990	/// chain as reserve
991	ReserveDeposit(AssetFilter),
992	/// reserve-transfer assets matching `AssetFilter` to a specific destination, using the
993	/// destination as reserve
994	ReserveWithdraw(AssetFilter),
995}
996
997impl AssetTransferFilter {
998	/// Returns reference to inner `AssetFilter` ignoring the transfer type.
999	pub fn inner(&self) -> &AssetFilter {
1000		match self {
1001			AssetTransferFilter::Teleport(inner) => inner,
1002			AssetTransferFilter::ReserveDeposit(inner) => inner,
1003			AssetTransferFilter::ReserveWithdraw(inner) => inner,
1004		}
1005	}
1006}
1007
1008#[cfg(test)]
1009mod tests {
1010	use super::super::prelude::*;
1011
1012	#[test]
1013	fn conversion_works() {
1014		let _: Assets = (Here, 1u128).into();
1015	}
1016
1017	#[test]
1018	fn from_sorted_and_deduplicated_works() {
1019		use super::*;
1020		use alloc::vec;
1021
1022		let empty = vec![];
1023		let r = Assets::from_sorted_and_deduplicated(empty);
1024		assert_eq!(r, Ok(Assets(vec![])));
1025
1026		let dup_fun = vec![(Here, 100).into(), (Here, 10).into()];
1027		let r = Assets::from_sorted_and_deduplicated(dup_fun);
1028		assert!(r.is_err());
1029
1030		let dup_nft = vec![(Here, *b"notgood!").into(), (Here, *b"notgood!").into()];
1031		let r = Assets::from_sorted_and_deduplicated(dup_nft);
1032		assert!(r.is_err());
1033
1034		let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
1035		let r = Assets::from_sorted_and_deduplicated(good_fun.clone());
1036		assert_eq!(r, Ok(Assets(good_fun)));
1037
1038		let bad_fun = vec![(Parent, 10).into(), (Here, 10).into()];
1039		let r = Assets::from_sorted_and_deduplicated(bad_fun);
1040		assert!(r.is_err());
1041
1042		let good_nft = vec![(Here, ()).into(), (Here, *b"good").into()];
1043		let r = Assets::from_sorted_and_deduplicated(good_nft.clone());
1044		assert_eq!(r, Ok(Assets(good_nft)));
1045
1046		let bad_nft = vec![(Here, *b"bad!").into(), (Here, ()).into()];
1047		let r = Assets::from_sorted_and_deduplicated(bad_nft);
1048		assert!(r.is_err());
1049
1050		let mixed_good = vec![(Here, 10).into(), (Here, *b"good").into()];
1051		let r = Assets::from_sorted_and_deduplicated(mixed_good.clone());
1052		assert_eq!(r, Ok(Assets(mixed_good)));
1053
1054		let mixed_bad = vec![(Here, *b"bad!").into(), (Here, 10).into()];
1055		let r = Assets::from_sorted_and_deduplicated(mixed_bad);
1056		assert!(r.is_err());
1057	}
1058
1059	#[test]
1060	fn reanchor_preserves_sorting() {
1061		use super::*;
1062		use alloc::vec;
1063
1064		let reanchor_context: Junctions = Parachain(2000).into();
1065		let dest = Location::new(1, []);
1066
1067		let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1068		let mut asset_1_reanchored = asset_1.clone();
1069		assert!(asset_1_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1070		assert_eq!(
1071			asset_1_reanchored,
1072			(Location::new(0, [Parachain(2000), PalletInstance(50), GeneralIndex(1)]), 10).into()
1073		);
1074
1075		let asset_2: Asset = (Location::new(1, []), 10).into();
1076		let mut asset_2_reanchored = asset_2.clone();
1077		assert!(asset_2_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1078		assert_eq!(asset_2_reanchored, (Location::new(0, []), 10).into());
1079
1080		let asset_3: Asset = (Location::new(1, [Parachain(1000)]), 10).into();
1081		let mut asset_3_reanchored = asset_3.clone();
1082		assert!(asset_3_reanchored.reanchor(&dest, &reanchor_context).is_ok());
1083		assert_eq!(asset_3_reanchored, (Location::new(0, [Parachain(1000)]), 10).into());
1084
1085		let mut assets: Assets = vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
1086		assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());
1087
1088		// decoding respects limits and sorting
1089		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1090
1091		assert!(assets.reanchor(&dest, &reanchor_context).is_ok());
1092		assert_eq!(assets.0, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored]);
1093
1094		// decoding respects limits and sorting
1095		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1096	}
1097
1098	#[test]
1099	fn prepend_preserves_sorting() {
1100		use super::*;
1101		use alloc::vec;
1102
1103		let prefix = Location::new(0, [Parachain(1000)]);
1104
1105		let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1106		let mut asset_1_prepended = asset_1.clone();
1107		assert!(asset_1_prepended.prepend_with(&prefix).is_ok());
1108		// changes interior X2->X3
1109		assert_eq!(
1110			asset_1_prepended,
1111			(Location::new(0, [Parachain(1000), PalletInstance(50), GeneralIndex(1)]), 10).into()
1112		);
1113
1114		let asset_2: Asset = (Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1115		let mut asset_2_prepended = asset_2.clone();
1116		assert!(asset_2_prepended.prepend_with(&prefix).is_ok());
1117		// changes parent
1118		assert_eq!(
1119			asset_2_prepended,
1120			(Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into()
1121		);
1122
1123		let asset_3: Asset = (Location::new(2, [PalletInstance(50), GeneralIndex(1)]), 10).into();
1124		let mut asset_3_prepended = asset_3.clone();
1125		assert!(asset_3_prepended.prepend_with(&prefix).is_ok());
1126		// changes parent
1127		assert_eq!(
1128			asset_3_prepended,
1129			(Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into()
1130		);
1131
1132		// `From` impl does sorting.
1133		let mut assets: Assets = vec![asset_1, asset_2, asset_3].into();
1134		// decoding respects limits and sorting
1135		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1136
1137		// let's do `prepend_with`
1138		assert!(assets.prepend_with(&prefix).is_ok());
1139		assert_eq!(assets.0, vec![asset_2_prepended, asset_1_prepended, asset_3_prepended]);
1140
1141		// decoding respects limits and sorting
1142		assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
1143	}
1144
1145	#[test]
1146	fn decoding_respects_limit() {
1147		use super::*;
1148
1149		// Having lots of one asset will work since they are deduplicated
1150		let lots_of_one_asset: Assets =
1151			vec![(GeneralIndex(1), 1u128).into(); MAX_ITEMS_IN_ASSETS + 1].into();
1152		let encoded = lots_of_one_asset.encode();
1153		assert!(Assets::decode(&mut &encoded[..]).is_ok());
1154
1155		// Fewer assets than the limit works
1156		let mut few_assets: Assets = Vec::new().into();
1157		for i in 0..MAX_ITEMS_IN_ASSETS {
1158			few_assets.push((GeneralIndex(i as u128), 1u128).into());
1159		}
1160		let encoded = few_assets.encode();
1161		assert!(Assets::decode(&mut &encoded[..]).is_ok());
1162
1163		// Having lots of different assets will not work
1164		let mut too_many_different_assets: Assets = Vec::new().into();
1165		for i in 0..MAX_ITEMS_IN_ASSETS + 1 {
1166			too_many_different_assets.push((GeneralIndex(i as u128), 1u128).into());
1167		}
1168		let encoded = too_many_different_assets.encode();
1169		assert!(Assets::decode(&mut &encoded[..]).is_err());
1170	}
1171}