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