referrerpolicy=no-referrer-when-downgrade

staging_xcm_executor/
assets.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
17use alloc::{
18	collections::{btree_map::BTreeMap, btree_set::BTreeSet},
19	vec::Vec,
20};
21use core::mem;
22use sp_runtime::{traits::Saturating, RuntimeDebug};
23use xcm::latest::{
24	Asset, AssetFilter, AssetId, AssetInstance, Assets,
25	Fungibility::{Fungible, NonFungible},
26	InteriorLocation, Location, Reanchorable,
27	WildAsset::{All, AllCounted, AllOf, AllOfCounted},
28	WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible},
29};
30
31/// Map of non-wildcard fungible and non-fungible assets held in the holding register.
32#[derive(Default, Clone, RuntimeDebug, Eq, PartialEq)]
33pub struct AssetsInHolding {
34	/// The fungible assets.
35	pub fungible: BTreeMap<AssetId, u128>,
36
37	/// The non-fungible assets.
38	// TODO: Consider BTreeMap<AssetId, BTreeSet<AssetInstance>>
39	//   or even BTreeMap<AssetId, SortedVec<AssetInstance>>
40	pub non_fungible: BTreeSet<(AssetId, AssetInstance)>,
41}
42
43impl From<Asset> for AssetsInHolding {
44	fn from(asset: Asset) -> AssetsInHolding {
45		let mut result = Self::default();
46		result.subsume(asset);
47		result
48	}
49}
50
51impl From<Vec<Asset>> for AssetsInHolding {
52	fn from(assets: Vec<Asset>) -> AssetsInHolding {
53		let mut result = Self::default();
54		for asset in assets.into_iter() {
55			result.subsume(asset)
56		}
57		result
58	}
59}
60
61impl From<Assets> for AssetsInHolding {
62	fn from(assets: Assets) -> AssetsInHolding {
63		assets.into_inner().into()
64	}
65}
66
67impl From<AssetsInHolding> for Vec<Asset> {
68	fn from(a: AssetsInHolding) -> Self {
69		a.into_assets_iter().collect()
70	}
71}
72
73impl From<AssetsInHolding> for Assets {
74	fn from(a: AssetsInHolding) -> Self {
75		a.into_assets_iter().collect::<Vec<Asset>>().into()
76	}
77}
78
79/// An error emitted by `take` operations.
80#[derive(Debug)]
81pub enum TakeError {
82	/// There was an attempt to take an asset without saturating (enough of) which did not exist.
83	AssetUnderflow(Asset),
84}
85
86impl AssetsInHolding {
87	/// New value, containing no assets.
88	pub fn new() -> Self {
89		Self::default()
90	}
91
92	/// Total number of distinct assets.
93	pub fn len(&self) -> usize {
94		self.fungible.len() + self.non_fungible.len()
95	}
96
97	/// Returns `true` if `self` contains no assets.
98	pub fn is_empty(&self) -> bool {
99		self.fungible.is_empty() && self.non_fungible.is_empty()
100	}
101
102	/// A borrowing iterator over the fungible assets.
103	pub fn fungible_assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
104		self.fungible
105			.iter()
106			.map(|(id, &amount)| Asset { fun: Fungible(amount), id: id.clone() })
107	}
108
109	/// A borrowing iterator over the non-fungible assets.
110	pub fn non_fungible_assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
111		self.non_fungible
112			.iter()
113			.map(|(id, instance)| Asset { fun: NonFungible(*instance), id: id.clone() })
114	}
115
116	/// A consuming iterator over all assets.
117	pub fn into_assets_iter(self) -> impl Iterator<Item = Asset> {
118		self.fungible
119			.into_iter()
120			.map(|(id, amount)| Asset { fun: Fungible(amount), id })
121			.chain(
122				self.non_fungible
123					.into_iter()
124					.map(|(id, instance)| Asset { fun: NonFungible(instance), id }),
125			)
126	}
127
128	/// A borrowing iterator over all assets.
129	pub fn assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
130		self.fungible_assets_iter().chain(self.non_fungible_assets_iter())
131	}
132
133	/// Mutate `self` to contain all given `assets`, saturating if necessary.
134	///
135	/// NOTE: [`AssetsInHolding`] are always sorted
136	pub fn subsume_assets(&mut self, mut assets: AssetsInHolding) {
137		// for fungibles, find matching fungibles and sum their amounts so we end-up having just
138		// single such fungible but with increased amount inside
139		for (asset_id, asset_amount) in assets.fungible {
140			self.fungible
141				.entry(asset_id)
142				.and_modify(|current_asset_amount| {
143					current_asset_amount.saturating_accrue(asset_amount)
144				})
145				.or_insert(asset_amount);
146		}
147		// for non-fungibles, every entry is unique so there is no notion of amount to sum-up
148		// together if there is the same non-fungible in both holdings (same instance_id) these
149		// will be collapsed into just single one
150		self.non_fungible.append(&mut assets.non_fungible);
151	}
152
153	/// Mutate `self` to contain the given `asset`, saturating if necessary.
154	///
155	/// Wildcard values of `asset` do nothing.
156	pub fn subsume(&mut self, asset: Asset) {
157		match asset.fun {
158			Fungible(amount) => {
159				self.fungible
160					.entry(asset.id)
161					.and_modify(|e| *e = e.saturating_add(amount))
162					.or_insert(amount);
163			},
164			NonFungible(instance) => {
165				self.non_fungible.insert((asset.id, instance));
166			},
167		}
168	}
169
170	/// Swaps two mutable AssetsInHolding, without deinitializing either one.
171	pub fn swapped(&mut self, mut with: AssetsInHolding) -> Self {
172		mem::swap(&mut *self, &mut with);
173		with
174	}
175
176	/// Alter any concretely identified assets by prepending the given `Location`.
177	///
178	/// WARNING: For now we consider this infallible and swallow any errors. It is thus the caller's
179	/// responsibility to ensure that any internal asset IDs are able to be prepended without
180	/// overflow.
181	pub fn prepend_location(&mut self, prepend: &Location) {
182		let mut fungible = Default::default();
183		mem::swap(&mut self.fungible, &mut fungible);
184		self.fungible = fungible
185			.into_iter()
186			.map(|(mut id, amount)| {
187				let _ = id.prepend_with(prepend);
188				(id, amount)
189			})
190			.collect();
191		let mut non_fungible = Default::default();
192		mem::swap(&mut self.non_fungible, &mut non_fungible);
193		self.non_fungible = non_fungible
194			.into_iter()
195			.map(|(mut class, inst)| {
196				let _ = class.prepend_with(prepend);
197				(class, inst)
198			})
199			.collect();
200	}
201
202	/// Mutate the assets to be interpreted as the same assets from the perspective of a `target`
203	/// chain. The local chain's `context` is provided.
204	///
205	/// Any assets which were unable to be reanchored are introduced into `failed_bin`.
206	pub fn reanchor(
207		&mut self,
208		target: &Location,
209		context: &InteriorLocation,
210		mut maybe_failed_bin: Option<&mut Self>,
211	) {
212		let mut fungible = Default::default();
213		mem::swap(&mut self.fungible, &mut fungible);
214		self.fungible = fungible
215			.into_iter()
216			.filter_map(|(mut id, amount)| match id.reanchor(target, context) {
217				Ok(()) => Some((id, amount)),
218				Err(()) => {
219					maybe_failed_bin.as_mut().map(|f| f.fungible.insert(id, amount));
220					None
221				},
222			})
223			.collect();
224		let mut non_fungible = Default::default();
225		mem::swap(&mut self.non_fungible, &mut non_fungible);
226		self.non_fungible = non_fungible
227			.into_iter()
228			.filter_map(|(mut class, inst)| match class.reanchor(target, context) {
229				Ok(()) => Some((class, inst)),
230				Err(()) => {
231					maybe_failed_bin.as_mut().map(|f| f.non_fungible.insert((class, inst)));
232					None
233				},
234			})
235			.collect();
236	}
237
238	/// Returns `true` if `asset` is contained within `self`.
239	pub fn contains_asset(&self, asset: &Asset) -> bool {
240		match asset {
241			Asset { fun: Fungible(amount), id } =>
242				self.fungible.get(id).map_or(false, |a| a >= amount),
243			Asset { fun: NonFungible(instance), id } =>
244				self.non_fungible.contains(&(id.clone(), *instance)),
245		}
246	}
247
248	/// Returns `true` if all `assets` are contained within `self`.
249	pub fn contains_assets(&self, assets: &Assets) -> bool {
250		assets.inner().iter().all(|a| self.contains_asset(a))
251	}
252
253	/// Returns `true` if all `assets` are contained within `self`.
254	pub fn contains(&self, assets: &AssetsInHolding) -> bool {
255		assets
256			.fungible
257			.iter()
258			.all(|(k, v)| self.fungible.get(k).map_or(false, |a| a >= v)) &&
259			self.non_fungible.is_superset(&assets.non_fungible)
260	}
261
262	/// Returns an error unless all `assets` are contained in `self`. In the case of an error, the
263	/// first asset in `assets` which is not wholly in `self` is returned.
264	pub fn ensure_contains(&self, assets: &Assets) -> Result<(), TakeError> {
265		for asset in assets.inner().iter() {
266			match asset {
267				Asset { fun: Fungible(amount), id } => {
268					if self.fungible.get(id).map_or(true, |a| a < amount) {
269						return Err(TakeError::AssetUnderflow((id.clone(), *amount).into()))
270					}
271				},
272				Asset { fun: NonFungible(instance), id } => {
273					let id_instance = (id.clone(), *instance);
274					if !self.non_fungible.contains(&id_instance) {
275						return Err(TakeError::AssetUnderflow(id_instance.into()))
276					}
277				},
278			}
279		}
280		return Ok(())
281	}
282
283	/// Mutates `self` to its original value less `mask` and returns assets that were removed.
284	///
285	/// If `saturate` is `true`, then `self` is considered to be masked by `mask`, thereby avoiding
286	/// any attempt at reducing it by assets it does not contain. In this case, the function is
287	/// infallible. If `saturate` is `false` and `mask` references a definite asset which `self`
288	/// does not contain then an error is returned.
289	///
290	/// The number of unique assets which are removed will respect the `count` parameter in the
291	/// counted wildcard variants.
292	///
293	/// Returns `Ok` with the definite assets token from `self` and mutates `self` to its value
294	/// minus `mask`. Returns `Err` in the non-saturating case where `self` did not contain (enough
295	/// of) a definite asset to be removed.
296	fn general_take(
297		&mut self,
298		mask: AssetFilter,
299		saturate: bool,
300	) -> Result<AssetsInHolding, TakeError> {
301		let mut taken = AssetsInHolding::new();
302		let maybe_limit = mask.limit().map(|x| x as usize);
303		match mask {
304			AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => match maybe_limit {
305				None => return Ok(self.swapped(AssetsInHolding::new())),
306				Some(limit) if self.len() <= limit =>
307					return Ok(self.swapped(AssetsInHolding::new())),
308				Some(0) => return Ok(AssetsInHolding::new()),
309				Some(limit) => {
310					let fungible = mem::replace(&mut self.fungible, Default::default());
311					fungible.into_iter().for_each(|(c, amount)| {
312						if taken.len() < limit {
313							taken.fungible.insert(c, amount);
314						} else {
315							self.fungible.insert(c, amount);
316						}
317					});
318					let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
319					non_fungible.into_iter().for_each(|(c, instance)| {
320						if taken.len() < limit {
321							taken.non_fungible.insert((c, instance));
322						} else {
323							self.non_fungible.insert((c, instance));
324						}
325					});
326				},
327			},
328			AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) |
329			AssetFilter::Wild(AllOf { fun: WildFungible, id }) =>
330				if maybe_limit.map_or(true, |l| l >= 1) {
331					if let Some((id, amount)) = self.fungible.remove_entry(&id) {
332						taken.fungible.insert(id, amount);
333					}
334				},
335			AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) |
336			AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => {
337				let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
338				non_fungible.into_iter().for_each(|(c, instance)| {
339					if c == id && maybe_limit.map_or(true, |l| taken.len() < l) {
340						taken.non_fungible.insert((c, instance));
341					} else {
342						self.non_fungible.insert((c, instance));
343					}
344				});
345			},
346			AssetFilter::Definite(assets) => {
347				if !saturate {
348					self.ensure_contains(&assets)?;
349				}
350				for asset in assets.into_inner().into_iter() {
351					match asset {
352						Asset { fun: Fungible(amount), id } => {
353							let (remove, amount) = match self.fungible.get_mut(&id) {
354								Some(self_amount) => {
355									let amount = amount.min(*self_amount);
356									*self_amount -= amount;
357									(*self_amount == 0, amount)
358								},
359								None => (false, 0),
360							};
361							if remove {
362								self.fungible.remove(&id);
363							}
364							if amount > 0 {
365								taken.subsume(Asset::from((id, amount)).into());
366							}
367						},
368						Asset { fun: NonFungible(instance), id } => {
369							let id_instance = (id, instance);
370							if self.non_fungible.remove(&id_instance) {
371								taken.subsume(id_instance.into())
372							}
373						},
374					}
375				}
376			},
377		}
378		Ok(taken)
379	}
380
381	/// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least
382	/// `mask`.
383	///
384	/// Returns `Ok` with the non-wildcard equivalence of `mask` taken and mutates `self` to its
385	/// value minus `mask` if `self` contains `asset`, and return `Err` otherwise.
386	pub fn saturating_take(&mut self, asset: AssetFilter) -> AssetsInHolding {
387		self.general_take(asset, true)
388			.expect("general_take never results in error when saturating")
389	}
390
391	/// Mutates `self` to its original value less `mask` and returns `true` iff it contains at least
392	/// `mask`.
393	///
394	/// Returns `Ok` with the non-wildcard equivalence of `asset` taken and mutates `self` to its
395	/// value minus `asset` if `self` contains `asset`, and return `Err` otherwise.
396	pub fn try_take(&mut self, mask: AssetFilter) -> Result<AssetsInHolding, TakeError> {
397		self.general_take(mask, false)
398	}
399
400	/// Consumes `self` and returns its original value excluding `asset` iff it contains at least
401	/// `asset`.
402	pub fn checked_sub(mut self, asset: Asset) -> Result<AssetsInHolding, AssetsInHolding> {
403		match asset.fun {
404			Fungible(amount) => {
405				let remove = if let Some(balance) = self.fungible.get_mut(&asset.id) {
406					if *balance >= amount {
407						*balance -= amount;
408						*balance == 0
409					} else {
410						return Err(self)
411					}
412				} else {
413					return Err(self)
414				};
415				if remove {
416					self.fungible.remove(&asset.id);
417				}
418				Ok(self)
419			},
420			NonFungible(instance) =>
421				if self.non_fungible.remove(&(asset.id, instance)) {
422					Ok(self)
423				} else {
424					Err(self)
425				},
426		}
427	}
428
429	/// Return the assets in `self`, but (asset-wise) of no greater value than `mask`.
430	///
431	/// The number of unique assets which are returned will respect the `count` parameter in the
432	/// counted wildcard variants of `mask`.
433	///
434	/// Example:
435	///
436	/// ```
437	/// use staging_xcm_executor::AssetsInHolding;
438	/// use xcm::latest::prelude::*;
439	/// let assets_i_have: AssetsInHolding = vec![ (Here, 100).into(), (Junctions::from([GeneralIndex(0)]), 100).into() ].into();
440	/// let assets_they_want: AssetFilter = vec![ (Here, 200).into(), (Junctions::from([GeneralIndex(0)]), 50).into() ].into();
441	///
442	/// let assets_we_can_trade: AssetsInHolding = assets_i_have.min(&assets_they_want);
443	/// assert_eq!(assets_we_can_trade.into_assets_iter().collect::<Vec<_>>(), vec![
444	/// 	(Here, 100).into(), (Junctions::from([GeneralIndex(0)]), 50).into(),
445	/// ]);
446	/// ```
447	pub fn min(&self, mask: &AssetFilter) -> AssetsInHolding {
448		let mut masked = AssetsInHolding::new();
449		let maybe_limit = mask.limit().map(|x| x as usize);
450		if maybe_limit.map_or(false, |l| l == 0) {
451			return masked
452		}
453		match mask {
454			AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => {
455				if maybe_limit.map_or(true, |l| self.len() <= l) {
456					return self.clone()
457				} else {
458					for (c, &amount) in self.fungible.iter() {
459						masked.fungible.insert(c.clone(), amount);
460						if maybe_limit.map_or(false, |l| masked.len() >= l) {
461							return masked
462						}
463					}
464					for (c, instance) in self.non_fungible.iter() {
465						masked.non_fungible.insert((c.clone(), *instance));
466						if maybe_limit.map_or(false, |l| masked.len() >= l) {
467							return masked
468						}
469					}
470				}
471			},
472			AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) |
473			AssetFilter::Wild(AllOf { fun: WildFungible, id }) =>
474				if let Some(&amount) = self.fungible.get(&id) {
475					masked.fungible.insert(id.clone(), amount);
476				},
477			AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) |
478			AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) =>
479				for (c, instance) in self.non_fungible.iter() {
480					if c == id {
481						masked.non_fungible.insert((c.clone(), *instance));
482						if maybe_limit.map_or(false, |l| masked.len() >= l) {
483							return masked
484						}
485					}
486				},
487			AssetFilter::Definite(assets) =>
488				for asset in assets.inner().iter() {
489					match asset {
490						Asset { fun: Fungible(amount), id } => {
491							if let Some(m) = self.fungible.get(id) {
492								masked.subsume((id.clone(), Fungible(*amount.min(m))).into());
493							}
494						},
495						Asset { fun: NonFungible(instance), id } => {
496							let id_instance = (id.clone(), *instance);
497							if self.non_fungible.contains(&id_instance) {
498								masked.subsume(id_instance.into());
499							}
500						},
501					}
502				},
503		}
504		masked
505	}
506}
507
508#[cfg(test)]
509mod tests {
510	use super::*;
511	use alloc::vec;
512	use xcm::latest::prelude::*;
513
514	#[allow(non_snake_case)]
515	/// Concrete fungible constructor
516	fn CF(amount: u128) -> Asset {
517		(Here, amount).into()
518	}
519	#[allow(non_snake_case)]
520	/// Concrete fungible constructor with index for GeneralIndex
521	fn CFG(index: u128, amount: u128) -> Asset {
522		(GeneralIndex(index), amount).into()
523	}
524	#[allow(non_snake_case)]
525	/// Concrete fungible constructor (parent=1)
526	fn CFP(amount: u128) -> Asset {
527		(Parent, amount).into()
528	}
529	#[allow(non_snake_case)]
530	/// Concrete fungible constructor (parent=2)
531	fn CFPP(amount: u128) -> Asset {
532		((Parent, Parent), amount).into()
533	}
534	#[allow(non_snake_case)]
535	/// Concrete non-fungible constructor
536	fn CNF(instance_id: u8) -> Asset {
537		(Here, [instance_id; 4]).into()
538	}
539
540	fn test_assets() -> AssetsInHolding {
541		let mut assets = AssetsInHolding::new();
542		assets.subsume(CF(300));
543		assets.subsume(CNF(40));
544		assets
545	}
546
547	#[test]
548	fn assets_in_holding_order_works() {
549		// populate assets in non-ordered fashion
550		let mut assets = AssetsInHolding::new();
551		assets.subsume(CFPP(300));
552		assets.subsume(CFP(200));
553		assets.subsume(CNF(2));
554		assets.subsume(CF(100));
555		assets.subsume(CNF(1));
556		assets.subsume(CFG(10, 400));
557		assets.subsume(CFG(15, 500));
558
559		// following is the order we expect from AssetsInHolding
560		// - fungibles before non-fungibles
561		// - for fungibles, sort by parent first, if parents match, then by other components like
562		//   general index
563		// - for non-fungibles, sort by instance_id
564		let mut iter = assets.clone().into_assets_iter();
565		// fungible, order by parent, parent=0
566		assert_eq!(Some(CF(100)), iter.next());
567		// fungible, order by parent then by general index, parent=0, general index=10
568		assert_eq!(Some(CFG(10, 400)), iter.next());
569		// fungible, order by parent then by general index, parent=0, general index=15
570		assert_eq!(Some(CFG(15, 500)), iter.next());
571		// fungible, order by parent, parent=1
572		assert_eq!(Some(CFP(200)), iter.next());
573		// fungible, order by parent, parent=2
574		assert_eq!(Some(CFPP(300)), iter.next());
575		// non-fungible, after fungibles, order by instance id, id=1
576		assert_eq!(Some(CNF(1)), iter.next());
577		// non-fungible, after fungibles, order by instance id, id=2
578		assert_eq!(Some(CNF(2)), iter.next());
579		// nothing else in the assets
580		assert_eq!(None, iter.next());
581
582		// lets add copy of the assets to the assets itself, just to check if order stays the same
583		// we also expect 2x amount for every fungible and collapsed non-fungibles
584		let assets_same = assets.clone();
585		assets.subsume_assets(assets_same);
586
587		let mut iter = assets.into_assets_iter();
588		assert_eq!(Some(CF(200)), iter.next());
589		assert_eq!(Some(CFG(10, 800)), iter.next());
590		assert_eq!(Some(CFG(15, 1000)), iter.next());
591		assert_eq!(Some(CFP(400)), iter.next());
592		assert_eq!(Some(CFPP(600)), iter.next());
593		assert_eq!(Some(CNF(1)), iter.next());
594		assert_eq!(Some(CNF(2)), iter.next());
595		assert_eq!(None, iter.next());
596	}
597
598	#[test]
599	fn subsume_assets_equal_length_holdings() {
600		let mut t1 = test_assets();
601		let mut t2 = AssetsInHolding::new();
602		t2.subsume(CF(300));
603		t2.subsume(CNF(50));
604
605		let t1_clone = t1.clone();
606		let mut t2_clone = t2.clone();
607
608		// ensure values for same fungibles are summed up together
609		// and order is also ok (see assets_in_holding_order_works())
610		t1.subsume_assets(t2.clone());
611		let mut iter = t1.into_assets_iter();
612		assert_eq!(Some(CF(600)), iter.next());
613		assert_eq!(Some(CNF(40)), iter.next());
614		assert_eq!(Some(CNF(50)), iter.next());
615		assert_eq!(None, iter.next());
616
617		// try the same initial holdings but other way around
618		// expecting same exact result as above
619		t2_clone.subsume_assets(t1_clone.clone());
620		let mut iter = t2_clone.into_assets_iter();
621		assert_eq!(Some(CF(600)), iter.next());
622		assert_eq!(Some(CNF(40)), iter.next());
623		assert_eq!(Some(CNF(50)), iter.next());
624		assert_eq!(None, iter.next());
625	}
626
627	#[test]
628	fn subsume_assets_different_length_holdings() {
629		let mut t1 = AssetsInHolding::new();
630		t1.subsume(CFP(400));
631		t1.subsume(CFPP(100));
632
633		let mut t2 = AssetsInHolding::new();
634		t2.subsume(CF(100));
635		t2.subsume(CNF(50));
636		t2.subsume(CNF(40));
637		t2.subsume(CFP(100));
638		t2.subsume(CFPP(100));
639
640		let t1_clone = t1.clone();
641		let mut t2_clone = t2.clone();
642
643		// ensure values for same fungibles are summed up together
644		// and order is also ok (see assets_in_holding_order_works())
645		t1.subsume_assets(t2);
646		let mut iter = t1.into_assets_iter();
647		assert_eq!(Some(CF(100)), iter.next());
648		assert_eq!(Some(CFP(500)), iter.next());
649		assert_eq!(Some(CFPP(200)), iter.next());
650		assert_eq!(Some(CNF(40)), iter.next());
651		assert_eq!(Some(CNF(50)), iter.next());
652		assert_eq!(None, iter.next());
653
654		// try the same initial holdings but other way around
655		// expecting same exact result as above
656		t2_clone.subsume_assets(t1_clone);
657		let mut iter = t2_clone.into_assets_iter();
658		assert_eq!(Some(CF(100)), iter.next());
659		assert_eq!(Some(CFP(500)), iter.next());
660		assert_eq!(Some(CFPP(200)), iter.next());
661		assert_eq!(Some(CNF(40)), iter.next());
662		assert_eq!(Some(CNF(50)), iter.next());
663		assert_eq!(None, iter.next());
664	}
665
666	#[test]
667	fn subsume_assets_empty_holding() {
668		let mut t1 = AssetsInHolding::new();
669		let t2 = AssetsInHolding::new();
670		t1.subsume_assets(t2.clone());
671		let mut iter = t1.clone().into_assets_iter();
672		assert_eq!(None, iter.next());
673
674		t1.subsume(CFP(400));
675		t1.subsume(CNF(40));
676		t1.subsume(CFPP(100));
677
678		let t1_clone = t1.clone();
679		let mut t2_clone = t2.clone();
680
681		// ensure values for same fungibles are summed up together
682		// and order is also ok (see assets_in_holding_order_works())
683		t1.subsume_assets(t2.clone());
684		let mut iter = t1.into_assets_iter();
685		assert_eq!(Some(CFP(400)), iter.next());
686		assert_eq!(Some(CFPP(100)), iter.next());
687		assert_eq!(Some(CNF(40)), iter.next());
688		assert_eq!(None, iter.next());
689
690		// try the same initial holdings but other way around
691		// expecting same exact result as above
692		t2_clone.subsume_assets(t1_clone.clone());
693		let mut iter = t2_clone.into_assets_iter();
694		assert_eq!(Some(CFP(400)), iter.next());
695		assert_eq!(Some(CFPP(100)), iter.next());
696		assert_eq!(Some(CNF(40)), iter.next());
697		assert_eq!(None, iter.next());
698	}
699
700	#[test]
701	fn checked_sub_works() {
702		let t = test_assets();
703		let t = t.checked_sub(CF(150)).unwrap();
704		let t = t.checked_sub(CF(151)).unwrap_err();
705		let t = t.checked_sub(CF(150)).unwrap();
706		let t = t.checked_sub(CF(1)).unwrap_err();
707		let t = t.checked_sub(CNF(41)).unwrap_err();
708		let t = t.checked_sub(CNF(40)).unwrap();
709		let t = t.checked_sub(CNF(40)).unwrap_err();
710		assert_eq!(t, AssetsInHolding::new());
711	}
712
713	#[test]
714	fn into_assets_iter_works() {
715		let assets = test_assets();
716		let mut iter = assets.into_assets_iter();
717		// Order defined by implementation: CF, CNF
718		assert_eq!(Some(CF(300)), iter.next());
719		assert_eq!(Some(CNF(40)), iter.next());
720		assert_eq!(None, iter.next());
721	}
722
723	#[test]
724	fn assets_into_works() {
725		let mut assets_vec: Vec<Asset> = Vec::new();
726		assets_vec.push(CF(300));
727		assets_vec.push(CNF(40));
728		// Push same group of tokens again
729		assets_vec.push(CF(300));
730		assets_vec.push(CNF(40));
731
732		let assets: AssetsInHolding = assets_vec.into();
733		let mut iter = assets.into_assets_iter();
734		// Fungibles add
735		assert_eq!(Some(CF(600)), iter.next());
736		// Non-fungibles collapse
737		assert_eq!(Some(CNF(40)), iter.next());
738		assert_eq!(None, iter.next());
739	}
740
741	#[test]
742	fn min_all_and_none_works() {
743		let assets = test_assets();
744		let none = Assets::new().into();
745		let all = All.into();
746
747		let none_min = assets.min(&none);
748		assert_eq!(None, none_min.assets_iter().next());
749		let all_min = assets.min(&all);
750		assert!(all_min.assets_iter().eq(assets.assets_iter()));
751	}
752
753	#[test]
754	fn min_counted_works() {
755		let mut assets = AssetsInHolding::new();
756		assets.subsume(CNF(40));
757		assets.subsume(CF(3000));
758		assets.subsume(CNF(80));
759		let all = WildAsset::AllCounted(6).into();
760
761		let all = assets.min(&all);
762		let all = all.assets_iter().collect::<Vec<_>>();
763		assert_eq!(all, vec![CF(3000), CNF(40), CNF(80)]);
764	}
765
766	#[test]
767	fn min_all_concrete_works() {
768		let assets = test_assets();
769		let fungible = Wild((Here, WildFungible).into());
770		let non_fungible = Wild((Here, WildNonFungible).into());
771
772		let fungible = assets.min(&fungible);
773		let fungible = fungible.assets_iter().collect::<Vec<_>>();
774		assert_eq!(fungible, vec![CF(300)]);
775		let non_fungible = assets.min(&non_fungible);
776		let non_fungible = non_fungible.assets_iter().collect::<Vec<_>>();
777		assert_eq!(non_fungible, vec![CNF(40)]);
778	}
779
780	#[test]
781	fn min_basic_works() {
782		let assets1 = test_assets();
783
784		let mut assets2 = AssetsInHolding::new();
785		// This is more then 300, so it should stay at 300
786		assets2.subsume(CF(600));
787		// This asset should be included
788		assets2.subsume(CNF(40));
789		let assets2: Assets = assets2.into();
790
791		let assets_min = assets1.min(&assets2.into());
792		let assets_min = assets_min.into_assets_iter().collect::<Vec<_>>();
793		assert_eq!(assets_min, vec![CF(300), CNF(40)]);
794	}
795
796	#[test]
797	fn saturating_take_all_and_none_works() {
798		let mut assets = test_assets();
799
800		let taken_none = assets.saturating_take(vec![].into());
801		assert_eq!(None, taken_none.assets_iter().next());
802		let taken_all = assets.saturating_take(All.into());
803		// Everything taken
804		assert_eq!(None, assets.assets_iter().next());
805		let all_iter = taken_all.assets_iter();
806		assert!(all_iter.eq(test_assets().assets_iter()));
807	}
808
809	#[test]
810	fn saturating_take_all_concrete_works() {
811		let mut assets = test_assets();
812		let fungible = Wild((Here, WildFungible).into());
813		let non_fungible = Wild((Here, WildNonFungible).into());
814
815		let fungible = assets.saturating_take(fungible);
816		let fungible = fungible.assets_iter().collect::<Vec<_>>();
817		assert_eq!(fungible, vec![CF(300)]);
818		let non_fungible = assets.saturating_take(non_fungible);
819		let non_fungible = non_fungible.assets_iter().collect::<Vec<_>>();
820		assert_eq!(non_fungible, vec![CNF(40)]);
821	}
822
823	#[test]
824	fn saturating_take_basic_works() {
825		let mut assets1 = test_assets();
826
827		let mut assets2 = AssetsInHolding::new();
828		// This is more then 300, so it takes everything
829		assets2.subsume(CF(600));
830		// This asset should be taken
831		assets2.subsume(CNF(40));
832		let assets2: Assets = assets2.into();
833
834		let taken = assets1.saturating_take(assets2.into());
835		let taken = taken.into_assets_iter().collect::<Vec<_>>();
836		assert_eq!(taken, vec![CF(300), CNF(40)]);
837	}
838
839	#[test]
840	fn try_take_all_counted_works() {
841		let mut assets = AssetsInHolding::new();
842		assets.subsume(CNF(40));
843		assets.subsume(CF(3000));
844		assets.subsume(CNF(80));
845		let all = assets.try_take(WildAsset::AllCounted(6).into()).unwrap();
846		assert_eq!(Assets::from(all).inner(), &vec![CF(3000), CNF(40), CNF(80)]);
847	}
848
849	#[test]
850	fn try_take_fungibles_counted_works() {
851		let mut assets = AssetsInHolding::new();
852		assets.subsume(CNF(40));
853		assets.subsume(CF(3000));
854		assets.subsume(CNF(80));
855		assert_eq!(Assets::from(assets).inner(), &vec![CF(3000), CNF(40), CNF(80),]);
856	}
857
858	#[test]
859	fn try_take_non_fungibles_counted_works() {
860		let mut assets = AssetsInHolding::new();
861		assets.subsume(CNF(40));
862		assets.subsume(CF(3000));
863		assets.subsume(CNF(80));
864		assert_eq!(Assets::from(assets).inner(), &vec![CF(3000), CNF(40), CNF(80)]);
865	}
866}