referrerpolicy=no-referrer-when-downgrade

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