referrerpolicy=no-referrer-when-downgrade

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