referrerpolicy=no-referrer-when-downgrade

staging_xcm/v4/
asset.rs

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