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