diff --git a/polkadot/xcm/xcm-executor/Cargo.toml b/polkadot/xcm/xcm-executor/Cargo.toml
index 65104c5793..4eaa4833b2 100644
--- a/polkadot/xcm/xcm-executor/Cargo.toml
+++ b/polkadot/xcm/xcm-executor/Cargo.toml
@@ -6,9 +6,8 @@ description = "An abstract and configurable XCM message executor."
version = "0.8.22"
[dependencies]
-codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
impl-trait-for-tuples = "0.1.3"
-
+codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
xcm = { path = "..", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -21,10 +20,11 @@ frame-support = { git = "https://github.com/paritytech/substrate", branch = "mas
default = ["std"]
std = [
"codec/std",
- "frame-support/std",
+ "xcm/std",
"sp-std/std",
"sp-io/std",
"sp-arithmetic/std",
"sp-core/std",
"sp-runtime/std",
+ "frame-support/std",
]
diff --git a/polkadot/xcm/xcm-executor/src/assets.rs b/polkadot/xcm/xcm-executor/src/assets.rs
index 79b67425d8..1f37c28d9b 100644
--- a/polkadot/xcm/xcm-executor/src/assets.rs
+++ b/polkadot/xcm/xcm-executor/src/assets.rs
@@ -14,10 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
-use sp_std::{prelude::*, mem::swap, collections::{btree_map::BTreeMap, btree_set::BTreeSet}};
+use sp_std::{prelude::*, mem, collections::{btree_map::BTreeMap, btree_set::BTreeSet}};
use xcm::v0::{MultiAsset, MultiLocation, AssetInstance};
use sp_runtime::RuntimeDebug;
+/// Classification of an asset being concrete or abstract.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug)]
pub enum AssetId {
Concrete(MultiLocation),
@@ -25,6 +26,7 @@ pub enum AssetId {
}
impl AssetId {
+ /// Prepend a MultiLocation to a concrete asset, giving it a new root location.
pub fn reanchor(&mut self, prepend: &MultiLocation) -> Result<(), ()> {
if let AssetId::Concrete(ref mut l) = self {
l.prepend_with(prepend.clone()).map_err(|_| ())?;
@@ -33,6 +35,7 @@ impl AssetId {
}
}
+/// List of concretely identified fungible and non-fungible assets.
#[derive(Default, Clone, RuntimeDebug)]
pub struct Assets {
pub fungible: BTreeMap,
@@ -56,6 +59,25 @@ impl From for Vec {
}
impl Assets {
+ /// An iterator over the fungible assets.
+ pub fn fungible_assets_iter<'a>(&'a self) -> impl Iterator- + 'a {
+ self.fungible.iter()
+ .map(|(id, &amount)| match id.clone() {
+ AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount },
+ AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount },
+ })
+ }
+
+ /// An iterator over the non-fungible assets.
+ pub fn non_fungible_assets_iter<'a>(&'a self) -> impl Iterator
- + 'a {
+ self.non_fungible.iter()
+ .map(|&(ref class, ref instance)| match class.clone() {
+ AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance: instance.clone() },
+ AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance: instance.clone() },
+ })
+ }
+
+ /// An iterator over all assets.
pub fn into_assets_iter(self) -> impl Iterator
- {
let fungible = self.fungible.into_iter()
.map(|(id, amount)| match id {
@@ -70,45 +92,35 @@ impl Assets {
fungible.chain(non_fungible)
}
+ /// An iterator over all assets.
pub fn assets_iter<'a>(&'a self) -> impl Iterator
- + 'a {
- let fungible = self.fungible.iter()
- .map(|(id, &amount)| match id.clone() {
- AssetId::Concrete(id) => MultiAsset::ConcreteFungible { id, amount },
- AssetId::Abstract(id) => MultiAsset::AbstractFungible { id, amount },
- });
- let non_fungible = self.non_fungible.iter()
- .map(|&(ref class, ref instance)| match class.clone() {
- AssetId::Concrete(class) => MultiAsset::ConcreteNonFungible { class, instance: instance.clone() },
- AssetId::Abstract(class) => MultiAsset::AbstractNonFungible { class, instance: instance.clone() },
- });
+ let fungible = self.fungible_assets_iter();
+ let non_fungible = self.non_fungible_assets_iter();
fungible.chain(non_fungible)
}
- /// Modify `self` to include `MultiAsset`, saturating if necessary.
+ /// Modify `self` to include a `MultiAsset`, saturating if necessary.
+ /// Only works on concretely identified assets; wildcards will be swallowed without error.
pub fn saturating_subsume(&mut self, asset: MultiAsset) {
match asset {
MultiAsset::ConcreteFungible { id, amount } => {
- self.fungible
- .entry(AssetId::Concrete(id))
- .and_modify(|e| *e = e.saturating_add(amount))
- .or_insert(amount);
+ self.saturating_subsume_fungible(AssetId::Concrete(id), amount);
}
MultiAsset::AbstractFungible { id, amount } => {
- self.fungible
- .entry(AssetId::Abstract(id))
- .and_modify(|e| *e = e.saturating_add(amount))
- .or_insert(amount);
+ self.saturating_subsume_fungible(AssetId::Abstract(id), amount);
}
MultiAsset::ConcreteNonFungible { class, instance} => {
- self.non_fungible.insert((AssetId::Concrete(class), instance));
+ self.saturating_subsume_non_fungible(AssetId::Concrete(class), instance);
}
MultiAsset::AbstractNonFungible { class, instance} => {
- self.non_fungible.insert((AssetId::Abstract(class), instance));
+ self.saturating_subsume_non_fungible(AssetId::Abstract(class), instance);
}
_ => (),
}
}
+ /// Modify `self` to include a new fungible asset by `id` and `amount`,
+ /// saturating if necessary.
pub fn saturating_subsume_fungible(&mut self, id: AssetId, amount: u128) {
self.fungible
.entry(id)
@@ -116,6 +128,7 @@ impl Assets {
.or_insert(amount);
}
+ /// Modify `self` to include a new non-fungible asset by `class` and `instance`.
pub fn saturating_subsume_non_fungible(&mut self, class: AssetId, instance: AssetInstance) {
self.non_fungible.insert((class, instance));
}
@@ -126,12 +139,12 @@ impl Assets {
/// ensure that any internal asset IDs are able to be prepended without overflow.
pub fn reanchor(&mut self, prepend: &MultiLocation) {
let mut fungible = Default::default();
- sp_std::mem::swap(&mut self.fungible, &mut fungible);
+ mem::swap(&mut self.fungible, &mut fungible);
self.fungible = fungible.into_iter()
.map(|(mut id, amount)| { let _ = id.reanchor(prepend); (id, amount) })
.collect();
let mut non_fungible = Default::default();
- sp_std::mem::swap(&mut self.non_fungible, &mut non_fungible);
+ mem::swap(&mut self.non_fungible, &mut non_fungible);
self.non_fungible = non_fungible.into_iter()
.map(|(mut class, inst)| { let _ = class.reanchor(prepend); (class, inst) })
.collect();
@@ -140,12 +153,93 @@ impl Assets {
/// Return the assets in `self`, but (asset-wise) of no greater value than `assets`.
///
/// Result is undefined if `assets` includes elements which match to the same asset more than once.
- pub fn min<'a, I: Iterator
- >(&self, assets: I) -> Self {
+ ///
+ /// Example:
+ ///
+ /// ```
+ /// use xcm_executor::Assets;
+ /// use xcm::v0::{MultiAsset, MultiLocation};
+ /// let assets_i_have: Assets = vec![
+ /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 },
+ /// MultiAsset::AbstractFungible { id: vec![0], amount: 100 },
+ /// ].into();
+ /// let assets_they_want: Assets = vec![
+ /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 200 },
+ /// MultiAsset::AbstractFungible { id: vec![0], amount: 50 },
+ /// ].into();
+ ///
+ /// let assets_we_can_trade: Assets = assets_i_have.min(assets_they_want.assets_iter());
+ /// assert_eq!(assets_we_can_trade.into_assets_iter().collect::>(), vec![
+ /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 },
+ /// MultiAsset::AbstractFungible { id: vec![0], amount: 50 },
+ /// ]);
+ /// ```
+ pub fn min<'a, M, I>(&self, assets: I) -> Self
+ where
+ M: 'a + sp_std::borrow::Borrow,
+ I: IntoIterator
- ,
+ {
let mut result = Assets::default();
for asset in assets.into_iter() {
- match asset {
+ match asset.borrow() {
MultiAsset::None => (),
MultiAsset::All => return self.clone(),
+ MultiAsset::AllFungible => {
+ // Replace `result.fungible` with all fungible assets,
+ // keeping `result.non_fungible` the same.
+ result = Assets {
+ fungible: self.fungible.clone(),
+ non_fungible: result.non_fungible,
+ }
+ },
+ MultiAsset::AllNonFungible => {
+ // Replace `result.non_fungible` with all non-fungible assets,
+ // keeping `result.fungible` the same.
+ result = Assets {
+ fungible: result.fungible,
+ non_fungible: self.non_fungible.clone(),
+ }
+ },
+ MultiAsset::AllAbstractFungible { id } => {
+ for asset in self.fungible_assets_iter() {
+ match &asset {
+ MultiAsset::AbstractFungible { id: identifier, .. } => {
+ if id == identifier { result.saturating_subsume(asset) }
+ },
+ _ => (),
+ }
+ }
+ },
+ MultiAsset::AllAbstractNonFungible { class } => {
+ for asset in self.non_fungible_assets_iter() {
+ match &asset {
+ MultiAsset::AbstractNonFungible { class: c, .. } => {
+ if class == c { result.saturating_subsume(asset) }
+ },
+ _ => (),
+ }
+ }
+ }
+ MultiAsset::AllConcreteFungible { id } => {
+ for asset in self.fungible_assets_iter() {
+ match &asset {
+ MultiAsset::ConcreteFungible { id: identifier, .. } => {
+ if id == identifier { result.saturating_subsume(asset) }
+ },
+ _ => (),
+ }
+ }
+ },
+ MultiAsset::AllConcreteNonFungible { class } => {
+ for asset in self.non_fungible_assets_iter() {
+ match &asset {
+ MultiAsset::ConcreteNonFungible { class: c, .. } => {
+ if class == c { result.saturating_subsume(asset) }
+ },
+ _ => (),
+ }
+ }
+ }
x @ MultiAsset::ConcreteFungible { .. } | x @ MultiAsset::AbstractFungible { .. } => {
let (id, amount) = match x {
MultiAsset::ConcreteFungible { id, amount } => (AssetId::Concrete(id.clone()), *amount),
@@ -153,9 +247,9 @@ impl Assets {
_ => unreachable!(),
};
if let Some(v) = self.fungible.get(&id) {
- result.saturating_subsume_fungible(id, amount.max(*v));
+ result.saturating_subsume_fungible(id, amount.min(*v));
}
- }
+ },
x @ MultiAsset::ConcreteNonFungible { .. } | x @ MultiAsset::AbstractNonFungible { .. } => {
let (class, instance) = match x {
MultiAsset::ConcreteNonFungible { class, instance } => (AssetId::Concrete(class.clone()), instance.clone()),
@@ -167,14 +261,6 @@ impl Assets {
result.non_fungible.insert(item);
}
}
- // TODO: implement partial wildcards.
- _ => (),
- // MultiAsset::AllFungible
- // | MultiAsset::AllNonFungible
- // | MultiAsset::AllAbstractFungible { id }
- // | MultiAsset::AllAbstractNonFungible { class }
- // | MultiAsset::AllConcreteFungible { id }
- // | MultiAsset::AllConcreteNonFungible { class } => (),
}
}
result
@@ -184,12 +270,87 @@ impl Assets {
/// assets taken.
///
/// Wildcards work.
- pub fn saturating_take(&mut self, assets: Vec) -> Assets {
+ ///
+ /// Example:
+ ///
+ /// ```
+ /// use xcm_executor::Assets;
+ /// use xcm::v0::{MultiAsset, MultiLocation};
+ /// let mut assets_i_have: Assets = vec![
+ /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 },
+ /// MultiAsset::AbstractFungible { id: vec![0], amount: 100 },
+ /// ].into();
+ /// let assets_they_want = vec![
+ /// MultiAsset::AllAbstractFungible { id: vec![0] },
+ /// ];
+ ///
+ /// let assets_they_took: Assets = assets_i_have.saturating_take(assets_they_want);
+ /// assert_eq!(assets_they_took.into_assets_iter().collect::>(), vec![
+ /// MultiAsset::AbstractFungible { id: vec![0], amount: 100 },
+ /// ]);
+ /// assert_eq!(assets_i_have.into_assets_iter().collect::>(), vec![
+ /// MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount: 100 },
+ /// ]);
+ /// ```
+ pub fn saturating_take(&mut self, assets: I) -> Assets
+ where
+ I: IntoIterator
- ,
+ {
let mut result = Assets::default();
for asset in assets.into_iter() {
match asset {
MultiAsset::None => (),
MultiAsset::All => return self.swapped(Assets::default()),
+ MultiAsset::AllFungible => {
+ // Remove all fungible assets, and copy them into `result`.
+ let fungible = mem::replace(&mut self.fungible, Default::default());
+ fungible.into_iter().for_each(|(id, amount)| {
+ result.saturating_subsume_fungible(id, amount);
+ })
+ },
+ MultiAsset::AllNonFungible => {
+ // Remove all non-fungible assets, and copy them into `result`.
+ let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
+ non_fungible.into_iter().for_each(|(class, instance)| {
+ result.saturating_subsume_non_fungible(class, instance);
+ });
+ },
+ x @ MultiAsset::AllAbstractFungible { .. } | x @ MultiAsset::AllConcreteFungible { .. } => {
+ let id = match x {
+ MultiAsset::AllConcreteFungible { id } => AssetId::Concrete(id),
+ MultiAsset::AllAbstractFungible { id } => AssetId::Abstract(id),
+ _ => unreachable!(),
+ };
+ // At the end of this block, we will be left with only the non-matching fungibles.
+ let mut non_matching_fungibles = BTreeMap::::new();
+ let fungible = mem::replace(&mut self.fungible, Default::default());
+ fungible.into_iter().for_each(|(iden, amount)| {
+ if iden == id {
+ result.saturating_subsume_fungible(iden, amount);
+ } else {
+ non_matching_fungibles.insert(iden, amount);
+ }
+ });
+ self.fungible = non_matching_fungibles;
+ },
+ x @ MultiAsset::AllAbstractNonFungible { .. } | x @ MultiAsset::AllConcreteNonFungible { .. } => {
+ let class = match x {
+ MultiAsset::AllConcreteNonFungible { class } => AssetId::Concrete(class),
+ MultiAsset::AllAbstractNonFungible { class } => AssetId::Abstract(class),
+ _ => unreachable!(),
+ };
+ // At the end of this block, we will be left with only the non-matching non-fungibles.
+ let mut non_matching_non_fungibles = BTreeSet::<(AssetId, AssetInstance)>::new();
+ let non_fungible = mem::replace(&mut self.non_fungible, Default::default());
+ non_fungible.into_iter().for_each(|(c, instance)| {
+ if class == c {
+ result.saturating_subsume_non_fungible(c, instance);
+ } else {
+ non_matching_non_fungibles.insert((c, instance));
+ }
+ });
+ self.non_fungible = non_matching_non_fungibles;
+ },
x @ MultiAsset::ConcreteFungible {..} | x @ MultiAsset::AbstractFungible {..} => {
let (id, amount) = match x {
MultiAsset::ConcreteFungible { id, amount } => (AssetId::Concrete(id), amount),
@@ -198,14 +359,16 @@ impl Assets {
};
// remove the maxmimum possible up to id/amount from self, add the removed onto
// result
- self.fungible.entry(id.clone())
- .and_modify(|e| if *e >= amount {
+ let maybe_value = self.fungible.get(&id);
+ if let Some(&e) = maybe_value {
+ if e > amount {
+ self.fungible.insert(id.clone(), e - amount);
result.saturating_subsume_fungible(id, amount);
- *e = *e - amount;
} else {
- result.saturating_subsume_fungible(id, *e);
- *e = 0
- });
+ self.fungible.remove(&id);
+ result.saturating_subsume_fungible(id, e.clone());
+ }
+ }
}
x @ MultiAsset::ConcreteNonFungible {..} | x @ MultiAsset::AbstractNonFungible {..} => {
let (class, instance) = match x {
@@ -216,26 +379,244 @@ impl Assets {
// remove the maxmimum possible up to id/amount from self, add the removed onto
// result
if let Some(entry) = self.non_fungible.take(&(class, instance)) {
- self.non_fungible.insert(entry);
+ result.non_fungible.insert(entry);
}
}
- // TODO: implement partial wildcards.
- _ => {
- Default::default()
- }
- // MultiAsset::AllFungible
- // | MultiAsset::AllNonFungible
- // | MultiAsset::AllAbstractFungible { id }
- // | MultiAsset::AllAbstractNonFungible { class }
- // | MultiAsset::AllConcreteFungible { id }
- // | MultiAsset::AllConcreteNonFungible { class } => (),
}
}
result
}
+ /// Swaps two mutable Assets, without deinitializing either one.
pub fn swapped(&mut self, mut with: Assets) -> Self {
- swap(&mut *self, &mut with);
+ mem::swap(&mut *self, &mut with);
with
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[allow(non_snake_case)]
+ fn AF(id: u8, amount: u128) -> MultiAsset {
+ MultiAsset::AbstractFungible { id: vec![id], amount }
+ }
+ #[allow(non_snake_case)]
+ fn ANF(class: u8, instance_id: u128) -> MultiAsset {
+ MultiAsset::AbstractNonFungible { class: vec![class], instance: AssetInstance::Index { id: instance_id } }
+ }
+ #[allow(non_snake_case)]
+ fn CF(amount: u128) -> MultiAsset {
+ MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount }
+ }
+ #[allow(non_snake_case)]
+ fn CNF(instance_id: u128) -> MultiAsset {
+ MultiAsset::ConcreteNonFungible { class: MultiLocation::Null, instance: AssetInstance::Index { id: instance_id } }
+ }
+
+ fn test_assets() -> Assets {
+ let mut assets_vec: Vec = Vec::new();
+ assets_vec.push(AF(1, 100));
+ assets_vec.push(ANF(2, 200));
+ assets_vec.push(CF(300));
+ assets_vec.push(CNF(400));
+ assets_vec.into()
+ }
+
+ #[test]
+ fn into_assets_iter_works() {
+ let assets = test_assets();
+ let mut iter = assets.into_assets_iter();
+ // Order defined by implementation: CF, AF, CNF, ANF
+ assert_eq!(Some(CF(300)), iter.next());
+ assert_eq!(Some(AF(1, 100)), iter.next());
+ assert_eq!(Some(CNF(400)), iter.next());
+ assert_eq!(Some(ANF(2, 200)), iter.next());
+ assert_eq!(None, iter.next());
+ }
+
+ #[test]
+ fn assets_into_works() {
+ let mut assets_vec: Vec = Vec::new();
+ assets_vec.push(AF(1, 100));
+ assets_vec.push(ANF(2, 200));
+ assets_vec.push(CF(300));
+ assets_vec.push(CNF(400));
+ // Push same group of tokens again
+ assets_vec.push(AF(1, 100));
+ assets_vec.push(ANF(2, 200));
+ assets_vec.push(CF(300));
+ assets_vec.push(CNF(400));
+
+ let assets: Assets = assets_vec.into();
+ let mut iter = assets.into_assets_iter();
+ // Fungibles add
+ assert_eq!(Some(CF(600)), iter.next());
+ assert_eq!(Some(AF(1, 200)), iter.next());
+ // Non-fungibles collapse
+ assert_eq!(Some(CNF(400)), iter.next());
+ assert_eq!(Some(ANF(2, 200)), iter.next());
+ assert_eq!(None, iter.next());
+ }
+
+ #[test]
+ fn min_all_and_none_works() {
+ let assets = test_assets();
+ let none = vec![MultiAsset::None];
+ let all = vec![MultiAsset::All];
+
+ let none_min = assets.min(none.iter());
+ assert_eq!(None, none_min.assets_iter().next());
+ let all_min = assets.min(all.iter());
+ assert!(all_min.assets_iter().eq(assets.assets_iter()));
+ }
+
+ #[test]
+ fn min_all_fungible_and_all_non_fungible_works() {
+ let assets = test_assets();
+ let fungible = vec![MultiAsset::AllFungible];
+ let non_fungible = vec![MultiAsset::AllNonFungible];
+
+ let fungible = assets.min(fungible.iter());
+ let fungible = fungible.assets_iter().collect::>();
+ assert_eq!(fungible, vec![CF(300), AF(1, 100)]);
+ let non_fungible = assets.min(non_fungible.iter());
+ let non_fungible = non_fungible.assets_iter().collect::>();
+ assert_eq!(non_fungible, vec![CNF(400), ANF(2, 200)]);
+ }
+
+ #[test]
+ fn min_all_abstract_works() {
+ let assets = test_assets();
+ let fungible = vec![MultiAsset::AllAbstractFungible { id: vec![1] }];
+ let non_fungible = vec![MultiAsset::AllAbstractNonFungible { class: vec![2] }];
+
+ let fungible = assets.min(fungible.iter());
+ let fungible = fungible.assets_iter().collect::>();
+ assert_eq!(fungible, vec![AF(1, 100)]);
+ let non_fungible = assets.min(non_fungible.iter());
+ let non_fungible = non_fungible.assets_iter().collect::>();
+ assert_eq!(non_fungible, vec![ANF(2, 200)]);
+ }
+
+ #[test]
+ fn min_all_concrete_works() {
+ let assets = test_assets();
+ let fungible = vec![MultiAsset::AllConcreteFungible { id: MultiLocation::Null }];
+ let non_fungible = vec![MultiAsset::AllConcreteNonFungible { class: MultiLocation::Null }];
+
+ let fungible = assets.min(fungible.iter());
+ let fungible = fungible.assets_iter().collect::>();
+ assert_eq!(fungible, vec![CF(300)]);
+ let non_fungible = assets.min(non_fungible.iter());
+ let non_fungible = non_fungible.assets_iter().collect::>();
+ assert_eq!(non_fungible, vec![CNF(400)]);
+ }
+
+ #[test]
+ fn min_basic_works() {
+ let assets1 = test_assets();
+
+ let mut assets2_vec: Vec = Vec::new();
+ // This is less than 100, so it will decrease to 50
+ assets2_vec.push(AF(1, 50));
+ // This asset does not exist, so not included
+ assets2_vec.push(ANF(2, 400));
+ // This is more then 300, so it should stay at 300
+ assets2_vec.push(CF(600));
+ // This asset should be included
+ assets2_vec.push(CNF(400));
+ let assets2: Assets = assets2_vec.into();
+
+ let assets_min = assets1.min(assets2.assets_iter());
+ let assets_min = assets_min.into_assets_iter().collect::>();
+ assert_eq!(assets_min, vec![CF(300), AF(1, 50), CNF(400)]);
+ }
+
+ #[test]
+ fn saturating_take_all_and_none_works() {
+ let mut assets = test_assets();
+ let none = vec![MultiAsset::None];
+ let all = vec![MultiAsset::All];
+
+ let taken_none = assets.saturating_take(none);
+ assert_eq!(None, taken_none.assets_iter().next());
+ let taken_all = assets.saturating_take(all);
+ // Everything taken
+ assert_eq!(None, assets.assets_iter().next());
+ let all_iter = taken_all.assets_iter();
+ assert!(all_iter.eq(test_assets().assets_iter()));
+ }
+
+ #[test]
+ fn saturating_take_all_fungible_and_all_non_fungible_works() {
+ let mut assets = test_assets();
+ let fungible = vec![MultiAsset::AllFungible];
+ let non_fungible = vec![MultiAsset::AllNonFungible];
+
+ let fungible = assets.saturating_take(fungible);
+ let fungible = fungible.assets_iter().collect::>();
+ assert_eq!(fungible, vec![CF(300), AF(1, 100)]);
+ let non_fungible = assets.saturating_take(non_fungible);
+ let non_fungible = non_fungible.assets_iter().collect::>();
+ assert_eq!(non_fungible, [CNF(400), ANF(2, 200)]);
+ // Assets completely drained
+ assert_eq!(None, assets.assets_iter().next());
+ }
+
+ #[test]
+ fn saturating_take_all_abstract_works() {
+ let mut assets = test_assets();
+ let fungible = vec![MultiAsset::AllAbstractFungible { id: vec![1] }];
+ let non_fungible = vec![MultiAsset::AllAbstractNonFungible { class: vec![2] }];
+
+ let fungible = assets.saturating_take(fungible);
+ let fungible = fungible.assets_iter().collect::>();
+ assert_eq!(fungible, vec![AF(1, 100)]);
+ let non_fungible = assets.saturating_take(non_fungible);
+ let non_fungible = non_fungible.assets_iter().collect::>();
+ assert_eq!(non_fungible, vec![ANF(2, 200)]);
+ // Assets drained of abstract
+ let final_assets = assets.assets_iter().collect::>();
+ assert_eq!(final_assets, vec![CF(300), CNF(400)]);
+ }
+
+ #[test]
+ fn saturating_take_all_concrete_works() {
+ let mut assets = test_assets();
+ let fungible = vec![MultiAsset::AllConcreteFungible { id: MultiLocation::Null }];
+ let non_fungible = vec![MultiAsset::AllConcreteNonFungible { class: MultiLocation::Null }];
+
+ let fungible = assets.saturating_take(fungible);
+ let fungible = fungible.assets_iter().collect::>();
+ assert_eq!(fungible, vec![CF(300)]);
+ let non_fungible = assets.saturating_take(non_fungible);
+ let non_fungible = non_fungible.assets_iter().collect::>();
+ assert_eq!(non_fungible, vec![CNF(400)]);
+ // Assets drained of concrete
+ let assets = assets.assets_iter().collect::>();
+ assert_eq!(assets, vec![AF(1, 100), ANF(2, 200)]);
+ }
+
+ #[test]
+ fn saturating_take_basic_works() {
+ let mut assets1 = test_assets();
+
+ let mut assets2_vec: Vec = Vec::new();
+ // We should take 50
+ assets2_vec.push(AF(1, 50));
+ // This asset should not be taken
+ assets2_vec.push(ANF(2, 400));
+ // This is more then 300, so it takes everything
+ assets2_vec.push(CF(600));
+ // This asset should be taken
+ assets2_vec.push(CNF(400));
+
+ let taken = assets1.saturating_take(assets2_vec);
+ let taken = taken.into_assets_iter().collect::>();
+ assert_eq!(taken, vec![CF(300), AF(1, 50), CNF(400)]);
+
+ let assets = assets1.into_assets_iter().collect::>();
+ assert_eq!(assets, vec![AF(1, 50), ANF(2, 200)]);
+ }
+}