1use alloc::{
18 boxed::Box,
19 collections::{
20 btree_map::{self, BTreeMap},
21 btree_set::BTreeSet,
22 },
23 vec::Vec,
24};
25use core::{fmt::Formatter, mem};
26use frame_support::traits::tokens::imbalance::ImbalanceAccounting;
27use xcm::latest::{
28 Asset, AssetFilter, AssetId, AssetInstance, Assets,
29 Fungibility::{Fungible, NonFungible},
30 InteriorLocation, Location, Reanchorable,
31 WildAsset::{All, AllCounted, AllOf, AllOfCounted},
32 WildFungibility::{Fungible as WildFungible, NonFungible as WildNonFungible},
33};
34
35#[derive(Debug)]
37pub enum TakeError {
38 AssetUnderflow(Asset),
40}
41
42pub struct BackupAssetsInHolding {
47 inner: AssetsInHolding,
49}
50
51impl BackupAssetsInHolding {
52 pub fn safe_backup(other: &AssetsInHolding) -> Self {
55 Self {
56 inner: AssetsInHolding {
57 fungible: other
58 .fungible
59 .iter()
60 .map(|(id, accounting)| (id.clone(), accounting.unsafe_clone()))
61 .collect(),
62 non_fungible: other.non_fungible.clone(),
63 },
64 }
65 }
66
67 pub fn restore_into(&mut self, target: &mut AssetsInHolding) {
70 core::mem::swap(target, &mut self.inner);
71 }
72
73 pub fn safe_drop(&mut self) {
76 self.inner.fungible.iter_mut().for_each(|(_, accounting)| {
78 accounting.forget_imbalance();
79 });
80 }
81}
82
83impl Drop for BackupAssetsInHolding {
84 fn drop(&mut self) {
85 self.safe_drop();
86 }
87}
88
89pub struct AssetsInHolding {
91 pub fungible: BTreeMap<AssetId, Box<dyn ImbalanceAccounting<u128>>>,
93 pub non_fungible: BTreeSet<(AssetId, AssetInstance)>,
97}
98
99impl PartialEq for AssetsInHolding {
100 fn eq(&self, other: &Self) -> bool {
101 if self.non_fungible != other.non_fungible {
102 return false;
103 }
104 if self.fungible.len() != other.fungible.len() {
105 return false;
106 }
107 if !self
108 .fungible
109 .iter()
110 .zip(other.fungible.iter())
111 .all(|(left, right)| left.0 == right.0 && left.1.amount() == right.1.amount())
112 {
113 return false;
114 }
115 true
116 }
117}
118
119impl core::fmt::Debug for AssetsInHolding {
120 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
121 let fungibles: BTreeMap<&AssetId, u128> =
122 self.fungible.iter().map(|(id, accounting)| (id, accounting.amount())).collect();
123 f.debug_struct("AssetsInHolding")
124 .field("fungible", &fungibles)
125 .field("non_fungible", &self.non_fungible)
126 .finish()
127 }
128}
129
130impl AssetsInHolding {
131 pub fn new() -> Self {
133 AssetsInHolding { fungible: BTreeMap::new(), non_fungible: BTreeSet::new() }
134 }
135
136 pub fn new_from_fungible_credit(
138 asset: AssetId,
139 credit: Box<dyn ImbalanceAccounting<u128>>,
140 ) -> Self {
141 let mut new = AssetsInHolding { fungible: BTreeMap::new(), non_fungible: BTreeSet::new() };
142 new.fungible.insert(asset, credit);
143 new
144 }
145
146 pub fn new_from_non_fungible(class: AssetId, instance: AssetInstance) -> Self {
148 let mut new = AssetsInHolding { fungible: BTreeMap::new(), non_fungible: BTreeSet::new() };
149 new.non_fungible.insert((class, instance));
150 new
151 }
152
153 pub fn len(&self) -> usize {
155 self.fungible.len() + self.non_fungible.len()
156 }
157
158 pub fn is_empty(&self) -> bool {
160 self.fungible.is_empty() && self.non_fungible.is_empty()
161 }
162
163 pub fn fungible_assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
165 self.fungible
166 .iter()
167 .map(|(id, accounting)| Asset { fun: Fungible(accounting.amount()), id: id.clone() })
168 }
169
170 pub fn non_fungible_assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
172 self.non_fungible
173 .iter()
174 .map(|(id, instance)| Asset { fun: NonFungible(*instance), id: id.clone() })
175 }
176
177 pub fn into_assets_iter(self) -> impl Iterator<Item = Asset> {
179 self.fungible
180 .into_iter()
181 .map(|(id, accounting)| Asset { fun: Fungible(accounting.amount()), id })
182 .chain(
183 self.non_fungible
184 .into_iter()
185 .map(|(id, instance)| Asset { fun: NonFungible(instance), id }),
186 )
187 }
188
189 pub fn assets_iter(&self) -> impl Iterator<Item = Asset> + '_ {
191 self.fungible_assets_iter().chain(self.non_fungible_assets_iter())
192 }
193
194 pub fn subsume_assets(&mut self, assets: AssetsInHolding) {
198 for (asset_id, accounting) in assets.fungible.into_iter() {
201 match self.fungible.entry(asset_id) {
202 btree_map::Entry::Occupied(mut e) => {
203 e.get_mut().saturating_subsume(accounting);
204 },
205 btree_map::Entry::Vacant(e) => {
206 e.insert(accounting);
207 },
208 }
209 }
210 let mut non_fungible = assets.non_fungible;
214 self.non_fungible.append(&mut non_fungible);
215 }
216
217 pub fn swapped(&mut self, mut with: AssetsInHolding) -> Self {
219 mem::swap(&mut *self, &mut with);
220 with
221 }
222
223 pub fn reanchor_and_burn_local(
232 self,
233 target: &Location,
234 context: &InteriorLocation,
235 failed_bin: &mut Self,
236 ) -> Assets {
237 let mut assets: Vec<Asset> = self
238 .fungible
239 .into_iter()
240 .filter_map(|(mut id, accounting)| match id.reanchor(target, context) {
241 Ok(()) => Some(Asset::from((id, Fungible(accounting.amount())))),
242 Err(()) => {
243 failed_bin.fungible.insert(id, accounting);
244 None
245 },
246 })
247 .chain(self.non_fungible.into_iter().filter_map(|(mut class, inst)| {
248 match class.reanchor(target, context) {
249 Ok(()) => Some(Asset::from((class, inst))),
250 Err(()) => {
251 failed_bin.non_fungible.insert((class, inst));
252 None
253 },
254 }
255 }))
256 .collect();
257 assets.sort();
258 assets.into()
259 }
260
261 pub fn reanchored_assets(&self, target: &Location, context: &InteriorLocation) -> Assets {
269 let mut assets: Vec<Asset> = self
270 .fungible
271 .iter()
272 .filter_map(|(id, accounting)| match id.clone().reanchored(target, context) {
273 Ok(new_id) => Some(Asset::from((new_id, Fungible(accounting.amount())))),
274 Err(()) => None,
275 })
276 .chain(self.non_fungible.iter().filter_map(|(class, inst)| {
277 match class.clone().reanchored(target, context) {
278 Ok(new_class) => Some(Asset::from((new_class, *inst))),
279 Err(()) => None,
280 }
281 }))
282 .collect();
283 assets.sort();
284 assets.into()
285 }
286
287 pub fn contains_asset(&self, asset: &Asset) -> bool {
289 match asset {
290 Asset { fun: Fungible(amount), id } => {
291 self.fungible.get(id).map_or(false, |a| a.amount() >= *amount)
292 },
293 Asset { fun: NonFungible(instance), id } => {
294 self.non_fungible.contains(&(id.clone(), *instance))
295 },
296 }
297 }
298
299 pub fn contains_assets(&self, assets: &Assets) -> bool {
301 assets.inner().iter().all(|a| self.contains_asset(a))
302 }
303
304 pub fn ensure_contains(&self, assets: &Assets) -> Result<(), TakeError> {
306 for asset in assets.inner().iter() {
307 match asset {
308 Asset { fun: Fungible(amount), id } => {
309 if self.fungible.get(id).map_or(true, |a| a.amount() < *amount) {
310 return Err(TakeError::AssetUnderflow((id.clone(), *amount).into()));
311 }
312 },
313 Asset { fun: NonFungible(instance), id } => {
314 let id_instance = (id.clone(), *instance);
315 if !self.non_fungible.contains(&id_instance) {
316 return Err(TakeError::AssetUnderflow(id_instance.into()));
317 }
318 },
319 }
320 }
321 return Ok(());
322 }
323
324 fn general_take(
338 &mut self,
339 mask: AssetFilter,
340 saturate: bool,
341 ) -> Result<AssetsInHolding, TakeError> {
342 let mut taken = AssetsInHolding::new();
343 let maybe_limit = mask.limit().map(|x| x as usize);
344 match mask {
345 AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => match maybe_limit {
346 None => return Ok(self.swapped(AssetsInHolding::new())),
347 Some(limit) if self.len() <= limit => {
348 return Ok(self.swapped(AssetsInHolding::new()))
349 },
350 Some(0) => return Ok(AssetsInHolding::new()),
351 Some(limit) => {
352 let fungible = mem::replace(&mut self.fungible, Default::default());
353 fungible.into_iter().for_each(|(c, amount)| {
354 if taken.len() < limit {
355 taken.fungible.insert(c, amount);
356 } else {
357 self.fungible.insert(c, amount);
358 }
359 });
360 let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
361 non_fungible.into_iter().for_each(|(c, instance)| {
362 if taken.len() < limit {
363 taken.non_fungible.insert((c, instance));
364 } else {
365 self.non_fungible.insert((c, instance));
366 }
367 });
368 },
369 },
370 AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) |
371 AssetFilter::Wild(AllOf { fun: WildFungible, id }) => {
372 if maybe_limit.map_or(true, |l| l >= 1) {
373 if let Some((id, amount)) = self.fungible.remove_entry(&id) {
374 taken.fungible.insert(id, amount);
375 }
376 }
377 },
378 AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) |
379 AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => {
380 let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
381 non_fungible.into_iter().for_each(|(c, instance)| {
382 if c == id && maybe_limit.map_or(true, |l| taken.len() < l) {
383 taken.non_fungible.insert((c, instance));
384 } else {
385 self.non_fungible.insert((c, instance));
386 }
387 });
388 },
389 AssetFilter::Definite(assets) => {
390 if !saturate {
391 self.ensure_contains(&assets)?;
392 }
393 for asset in assets.into_inner().into_iter() {
394 match asset {
395 Asset { fun: Fungible(amount), id } => {
396 let (remove, balance) = match self.fungible.get_mut(&id) {
397 Some(self_amount) => {
398 let balance = self_amount.saturating_take(amount);
401 (self_amount.amount() == 0, Some(balance))
402 },
403 None => (false, None),
404 };
405 if remove {
406 self.fungible.remove(&id);
407 }
408 if let Some(balance) = balance {
409 let other = Self::new_from_fungible_credit(id, balance);
410 taken.subsume_assets(other);
411 }
412 },
413 Asset { fun: NonFungible(instance), id } => {
414 let id_instance = (id, instance);
415 if self.non_fungible.remove(&id_instance) {
416 taken.non_fungible.insert((id_instance.0, id_instance.1));
417 }
418 },
419 }
420 }
421 },
422 }
423 Ok(taken)
424 }
425
426 pub fn saturating_take(&mut self, asset: AssetFilter) -> Self {
432 self.general_take(asset, true)
433 .expect("general_take never results in error when saturating")
434 }
435
436 pub fn try_take(&mut self, mask: AssetFilter) -> Result<Self, TakeError> {
442 self.general_take(mask, false)
443 }
444
445 pub fn min(&self, mask: &AssetFilter) -> Assets {
467 let mut masked = Assets::new();
468 let maybe_limit = mask.limit().map(|x| x as usize);
469 if maybe_limit.map_or(false, |l| l == 0) {
470 return masked;
471 }
472 match mask {
473 AssetFilter::Wild(All) | AssetFilter::Wild(AllCounted(_)) => {
474 if maybe_limit.map_or(true, |l| self.len() <= l) {
475 return self.assets_iter().collect::<Vec<Asset>>().into();
476 } else {
477 for (c, accounting) in self.fungible.iter() {
478 masked.push((c.clone(), accounting.amount()).into());
479 if maybe_limit.map_or(false, |l| masked.len() >= l) {
480 return masked;
481 }
482 }
483 for (c, instance) in self.non_fungible.iter() {
484 masked.push((c.clone(), *instance).into());
485 if maybe_limit.map_or(false, |l| masked.len() >= l) {
486 return masked;
487 }
488 }
489 }
490 },
491 AssetFilter::Wild(AllOfCounted { fun: WildFungible, id, .. }) |
492 AssetFilter::Wild(AllOf { fun: WildFungible, id }) => {
493 if let Some(accounting) = self.fungible.get(&id) {
494 masked.push((id.clone(), accounting.amount()).into());
495 }
496 },
497 AssetFilter::Wild(AllOfCounted { fun: WildNonFungible, id, .. }) |
498 AssetFilter::Wild(AllOf { fun: WildNonFungible, id }) => {
499 for (c, instance) in self.non_fungible.iter() {
500 if c == id {
501 masked.push((c.clone(), *instance).into());
502 if maybe_limit.map_or(false, |l| masked.len() >= l) {
503 return masked;
504 }
505 }
506 }
507 },
508 AssetFilter::Definite(assets) => {
509 for asset in assets.inner().iter() {
510 match asset {
511 Asset { fun: Fungible(amount), id } => {
512 if let Some(m) = self.fungible.get(id) {
513 masked
514 .push((id.clone(), Fungible(*amount.min(&m.amount()))).into());
515 }
516 },
517 Asset { fun: NonFungible(instance), id } => {
518 let id_instance = (id.clone(), *instance);
519 if self.non_fungible.contains(&id_instance) {
520 masked.push(id_instance.into());
521 }
522 },
523 }
524 }
525 },
526 }
527 masked
528 }
529
530 #[cfg(feature = "std")]
535 pub fn unsafe_clone_for_tests(&self) -> Self {
536 Self {
537 fungible: self
538 .fungible
539 .iter()
540 .map(|(id, accounting)| (id.clone(), accounting.unsafe_clone()))
541 .collect(),
542 non_fungible: self.non_fungible.clone(),
543 }
544 }
545}
546
547#[cfg(test)]
548mod tests {
549 use super::*;
550 use crate::tests::mock::*;
551 use alloc::vec;
552 use xcm::latest::prelude::*;
553
554 #[allow(non_snake_case)]
555 fn CF(amount: u128) -> Asset {
557 (Here, amount).into()
558 }
559 #[allow(non_snake_case)]
560 fn CFG(index: u128, amount: u128) -> Asset {
562 (GeneralIndex(index), amount).into()
563 }
564 #[allow(non_snake_case)]
565 fn CFP(amount: u128) -> Asset {
567 (Parent, amount).into()
568 }
569 #[allow(non_snake_case)]
570 fn CFPP(amount: u128) -> Asset {
572 ((Parent, Parent), amount).into()
573 }
574 #[allow(non_snake_case)]
575 fn CNF(instance_id: u8) -> Asset {
577 (Here, [instance_id; 4]).into()
578 }
579
580 fn asset_to_holding(asset: Asset) -> AssetsInHolding {
582 let mut holding = AssetsInHolding::new();
585 match asset.fun {
586 Fungible(amount) => {
587 holding.fungible.insert(asset.id, Box::new(MockCredit(amount)));
588 },
589 NonFungible(instance) => {
590 holding.non_fungible.insert((asset.id, instance));
591 },
592 }
593 holding
594 }
595
596 fn test_assets() -> AssetsInHolding {
597 let mut assets = AssetsInHolding::new();
598 assets.subsume_assets(asset_to_holding(CF(300)));
599 assets.subsume_assets(asset_to_holding(CNF(40)));
600 assets
601 }
602
603 #[test]
604 fn assets_in_holding_order_works() {
605 let mut assets = AssetsInHolding::new();
607 assets.subsume_assets(asset_to_holding(CFPP(300)));
608 assets.subsume_assets(asset_to_holding(CFP(200)));
609 assets.subsume_assets(asset_to_holding(CNF(2)));
610 assets.subsume_assets(asset_to_holding(CF(100)));
611 assets.subsume_assets(asset_to_holding(CNF(1)));
612 assets.subsume_assets(asset_to_holding(CFG(10, 400)));
613 assets.subsume_assets(asset_to_holding(CFG(15, 500)));
614
615 let mut iter = assets.unsafe_clone_for_tests().into_assets_iter();
621 assert_eq!(Some(CF(100)), iter.next());
623 assert_eq!(Some(CFG(10, 400)), iter.next());
625 assert_eq!(Some(CFG(15, 500)), iter.next());
627 assert_eq!(Some(CFP(200)), iter.next());
629 assert_eq!(Some(CFPP(300)), iter.next());
631 assert_eq!(Some(CNF(1)), iter.next());
633 assert_eq!(Some(CNF(2)), iter.next());
635 assert_eq!(None, iter.next());
637
638 let assets_same = assets.unsafe_clone_for_tests();
641 assets.subsume_assets(assets_same);
642
643 let mut iter = assets.into_assets_iter();
644 assert_eq!(Some(CF(200)), iter.next());
645 assert_eq!(Some(CFG(10, 800)), iter.next());
646 assert_eq!(Some(CFG(15, 1000)), iter.next());
647 assert_eq!(Some(CFP(400)), iter.next());
648 assert_eq!(Some(CFPP(600)), iter.next());
649 assert_eq!(Some(CNF(1)), iter.next());
650 assert_eq!(Some(CNF(2)), iter.next());
651 assert_eq!(None, iter.next());
652 }
653
654 #[test]
655 fn subsume_assets_equal_length_holdings() {
656 let mut t1 = test_assets();
657 let mut t2 = AssetsInHolding::new();
658 t2.subsume_assets(asset_to_holding(CF(300)));
659 t2.subsume_assets(asset_to_holding(CNF(50)));
660
661 let t1_clone = t1.unsafe_clone_for_tests();
662 let mut t2_clone = t2.unsafe_clone_for_tests();
663
664 t1.subsume_assets(t2.unsafe_clone_for_tests());
667 let mut iter = t1.into_assets_iter();
668 assert_eq!(Some(CF(600)), iter.next());
669 assert_eq!(Some(CNF(40)), iter.next());
670 assert_eq!(Some(CNF(50)), iter.next());
671 assert_eq!(None, iter.next());
672
673 t2_clone.subsume_assets(t1_clone.unsafe_clone_for_tests());
676 let mut iter = t2_clone.into_assets_iter();
677 assert_eq!(Some(CF(600)), iter.next());
678 assert_eq!(Some(CNF(40)), iter.next());
679 assert_eq!(Some(CNF(50)), iter.next());
680 assert_eq!(None, iter.next());
681 }
682
683 #[test]
684 fn subsume_assets_different_length_holdings() {
685 let mut t1 = AssetsInHolding::new();
686 t1.subsume_assets(asset_to_holding(CFP(400)));
687 t1.subsume_assets(asset_to_holding(CFPP(100)));
688
689 let mut t2 = AssetsInHolding::new();
690 t2.subsume_assets(asset_to_holding(CF(100)));
691 t2.subsume_assets(asset_to_holding(CNF(50)));
692 t2.subsume_assets(asset_to_holding(CNF(40)));
693 t2.subsume_assets(asset_to_holding(CFP(100)));
694 t2.subsume_assets(asset_to_holding(CFPP(100)));
695
696 let t1_clone = t1.unsafe_clone_for_tests();
697 let mut t2_clone = t2.unsafe_clone_for_tests();
698
699 t1.subsume_assets(t2);
702 let mut iter = t1.into_assets_iter();
703 assert_eq!(Some(CF(100)), iter.next());
704 assert_eq!(Some(CFP(500)), iter.next());
705 assert_eq!(Some(CFPP(200)), iter.next());
706 assert_eq!(Some(CNF(40)), iter.next());
707 assert_eq!(Some(CNF(50)), iter.next());
708 assert_eq!(None, iter.next());
709
710 t2_clone.subsume_assets(t1_clone);
713 let mut iter = t2_clone.into_assets_iter();
714 assert_eq!(Some(CF(100)), iter.next());
715 assert_eq!(Some(CFP(500)), iter.next());
716 assert_eq!(Some(CFPP(200)), iter.next());
717 assert_eq!(Some(CNF(40)), iter.next());
718 assert_eq!(Some(CNF(50)), iter.next());
719 assert_eq!(None, iter.next());
720 }
721
722 #[test]
723 fn subsume_assets_empty_holding() {
724 let mut t1 = AssetsInHolding::new();
725 let t2 = AssetsInHolding::new();
726 t1.subsume_assets(t2.unsafe_clone_for_tests());
727 let mut iter = t1.unsafe_clone_for_tests().into_assets_iter();
728 assert_eq!(None, iter.next());
729
730 t1.subsume_assets(asset_to_holding(CFP(400)));
731 t1.subsume_assets(asset_to_holding(CNF(40)));
732 t1.subsume_assets(asset_to_holding(CFPP(100)));
733
734 let t1_clone = t1.unsafe_clone_for_tests();
735 let mut t2_clone = t2.unsafe_clone_for_tests();
736
737 t1.subsume_assets(t2.unsafe_clone_for_tests());
740 let mut iter = t1.into_assets_iter();
741 assert_eq!(Some(CFP(400)), iter.next());
742 assert_eq!(Some(CFPP(100)), iter.next());
743 assert_eq!(Some(CNF(40)), iter.next());
744 assert_eq!(None, iter.next());
745
746 t2_clone.subsume_assets(t1_clone.unsafe_clone_for_tests());
749 let mut iter = t2_clone.into_assets_iter();
750 assert_eq!(Some(CFP(400)), iter.next());
751 assert_eq!(Some(CFPP(100)), iter.next());
752 assert_eq!(Some(CNF(40)), iter.next());
753 assert_eq!(None, iter.next());
754 }
755
756 #[test]
757 fn into_assets_iter_works() {
758 let assets = test_assets();
759 let mut iter = assets.into_assets_iter();
760 assert_eq!(Some(CF(300)), iter.next());
762 assert_eq!(Some(CNF(40)), iter.next());
763 assert_eq!(None, iter.next());
764 }
765
766 #[test]
767 fn assets_into_works() {
768 let mut assets_vec: Vec<Asset> = Vec::new();
769 assets_vec.push(CF(300));
770 assets_vec.push(CNF(40));
771 assets_vec.push(CF(300));
773 assets_vec.push(CNF(40));
774
775 let mut assets = AssetsInHolding::new();
776 for asset in assets_vec {
777 assets.subsume_assets(asset_to_holding(asset));
778 }
779 let mut iter = assets.into_assets_iter();
780 assert_eq!(Some(CF(600)), iter.next());
782 assert_eq!(Some(CNF(40)), iter.next());
784 assert_eq!(None, iter.next());
785 }
786
787 #[test]
788 fn min_all_and_none_works() {
789 let assets = test_assets();
790 let none = Assets::new().into();
791 let all = All.into();
792
793 let none_min = assets.min(&none);
794 assert_eq!(None, none_min.inner().iter().next());
795 let all_min = assets.min(&all);
796 let all_min_vec: Vec<_> = all_min.inner().iter().cloned().collect();
797 let assets_vec: Vec<_> = assets.assets_iter().collect();
798 assert_eq!(all_min_vec, assets_vec);
799 }
800
801 #[test]
802 fn min_counted_works() {
803 let mut assets = AssetsInHolding::new();
804 assets.subsume_assets(asset_to_holding(CNF(40)));
805 assets.subsume_assets(asset_to_holding(CF(3000)));
806 assets.subsume_assets(asset_to_holding(CNF(80)));
807 let all = WildAsset::AllCounted(6).into();
808
809 let all = assets.min(&all);
810 assert_eq!(all.inner(), &vec![CF(3000), CNF(40), CNF(80)]);
811 }
812
813 #[test]
814 fn min_all_concrete_works() {
815 let assets = test_assets();
816 let fungible = Wild((Here, WildFungible).into());
817 let non_fungible = Wild((Here, WildNonFungible).into());
818
819 let fungible = assets.min(&fungible);
820 assert_eq!(fungible.inner(), &vec![CF(300)]);
821 let non_fungible = assets.min(&non_fungible);
822 assert_eq!(non_fungible.inner(), &vec![CNF(40)]);
823 }
824
825 #[test]
826 fn min_basic_works() {
827 let assets1 = test_assets();
828
829 let assets2: Assets = vec![
831 CF(600),
833 CNF(40),
835 ]
836 .into();
837
838 let assets_min = assets1.min(&assets2.into());
839 assert_eq!(assets_min.inner(), &vec![CF(300), CNF(40)]);
840 }
841
842 #[test]
843 fn saturating_take_all_and_none_works() {
844 let mut assets = test_assets();
845
846 let taken_none = assets.saturating_take(vec![].into());
847 assert_eq!(None, taken_none.assets_iter().next());
848 let taken_all = assets.saturating_take(All.into());
849 assert_eq!(None, assets.assets_iter().next());
851 let all_iter = taken_all.assets_iter();
852 assert!(all_iter.eq(test_assets().assets_iter()));
853 }
854
855 #[test]
856 fn saturating_take_all_concrete_works() {
857 let mut assets = test_assets();
858 let fungible = Wild((Here, WildFungible).into());
859 let non_fungible = Wild((Here, WildNonFungible).into());
860
861 let fungible = assets.saturating_take(fungible);
862 let fungible = fungible.assets_iter().collect::<Vec<_>>();
863 assert_eq!(fungible, vec![CF(300)]);
864 let non_fungible = assets.saturating_take(non_fungible);
865 let non_fungible = non_fungible.assets_iter().collect::<Vec<_>>();
866 assert_eq!(non_fungible, vec![CNF(40)]);
867 }
868
869 #[test]
870 fn saturating_take_basic_works() {
871 let mut assets1 = test_assets();
872
873 let assets2: Assets = vec![
875 CF(600),
877 CNF(40),
879 ]
880 .into();
881
882 let taken = assets1.saturating_take(assets2.into());
883 let taken_vec: Vec<_> = taken.assets_iter().collect();
884 assert_eq!(taken_vec, vec![CF(300), CNF(40)]);
885 }
886
887 #[test]
888 fn try_take_all_counted_works() {
889 let mut assets = AssetsInHolding::new();
890 assets.subsume_assets(asset_to_holding(CNF(40)));
891 assets.subsume_assets(asset_to_holding(CF(3000)));
892 assets.subsume_assets(asset_to_holding(CNF(80)));
893 let all = assets.try_take(WildAsset::AllCounted(6).into()).unwrap();
894 let all_vec: Vec<_> = all.assets_iter().collect();
895 assert_eq!(all_vec, vec![CF(3000), CNF(40), CNF(80)]);
896 }
897
898 #[test]
899 fn try_take_fungibles_counted_works() {
900 let mut assets = AssetsInHolding::new();
901 assets.subsume_assets(asset_to_holding(CNF(40)));
902 assets.subsume_assets(asset_to_holding(CF(3000)));
903 assets.subsume_assets(asset_to_holding(CNF(80)));
904 let assets_vec: Vec<_> = assets.assets_iter().collect();
905 assert_eq!(assets_vec, vec![CF(3000), CNF(40), CNF(80)]);
906 }
907
908 #[test]
909 fn try_take_non_fungibles_counted_works() {
910 let mut assets = AssetsInHolding::new();
911 assets.subsume_assets(asset_to_holding(CNF(40)));
912 assets.subsume_assets(asset_to_holding(CF(3000)));
913 assets.subsume_assets(asset_to_holding(CNF(80)));
914 let assets_vec: Vec<_> = assets.assets_iter().collect();
915 assert_eq!(assets_vec, vec![CF(3000), CNF(40), CNF(80)]);
916 }
917}