multiasset.rs 36.5 KiB
Newer Older
	Encode,
	Decode,
	TypeInfo,
	MaxEncodedLen,
	serde::Serialize,
	serde::Deserialize,
)]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
Gavin Wood's avatar
Gavin Wood committed
pub enum MultiAssetFilter {
	/// Specify the filter as being everything contained by the given `MultiAssets` inner.
	Definite(MultiAssets),
	/// Specify the filter as the given `WildMultiAsset` wildcard.
	Wild(WildMultiAsset),
}

impl<T: Into<WildMultiAsset>> From<T> for MultiAssetFilter {
	fn from(x: T) -> Self {
		Self::Wild(x.into())
	}
}

impl From<MultiAsset> for MultiAssetFilter {
	fn from(x: MultiAsset) -> Self {
		Self::Definite(vec![x].into())
	}
}

impl From<Vec<MultiAsset>> for MultiAssetFilter {
	fn from(x: Vec<MultiAsset>) -> Self {
		Self::Definite(x.into())
	}
}

impl From<MultiAssets> for MultiAssetFilter {
	fn from(x: MultiAssets) -> Self {
		Self::Definite(x)
	}
}

impl MultiAssetFilter {
	/// Returns true if `inner` would be matched by `self`.
	///
	/// Note that for `Counted` variants of wildcards, then it will disregard the count except for
	/// always returning `false` when equal to 0.
	pub fn matches(&self, inner: &MultiAsset) -> bool {
		match self {
			MultiAssetFilter::Definite(ref assets) => assets.contains(inner),
			MultiAssetFilter::Wild(ref wild) => wild.contains(inner),
		}
	}

	/// Mutate the location of the asset identifier if concrete, giving it the same location
	/// relative to a `target` context. The local context is provided as `context`.
	pub fn reanchor(
		&mut self,
		target: &MultiLocation,
		context: InteriorMultiLocation,
	) -> Result<(), ()> {
		match self {
			MultiAssetFilter::Definite(ref mut assets) => assets.reanchor(target, context),
			MultiAssetFilter::Wild(ref mut wild) => wild.reanchor(target, context),
		}
	}

	/// Maximum count of assets it is possible to match, if known.
	pub fn count(&self) -> Option<u32> {
		use MultiAssetFilter::*;
		match self {
			Definite(x) => Some(x.len() as u32),
			Wild(x) => x.count(),
		}
	}

	/// Explicit limit placed on the number of items, if any.
	pub fn limit(&self) -> Option<u32> {
		use MultiAssetFilter::*;
		match self {
			Definite(_) => None,
			Wild(x) => x.limit(),
		}
	}
}

impl TryFrom<OldMultiAssetFilter> for MultiAssetFilter {
	type Error = ();
	fn try_from(old: OldMultiAssetFilter) -> Result<MultiAssetFilter, ()> {
		Ok(match old {
			OldMultiAssetFilter::Definite(x) => Self::Definite(x.try_into()?),
			OldMultiAssetFilter::Wild(x) => Self::Wild(x.try_into()?),
		})
	}
}

Francisco Aguirre's avatar
Francisco Aguirre committed
impl TryFrom<NewMultiAssetFilter> for MultiAssetFilter {
	type Error = ();
	fn try_from(new: NewMultiAssetFilter) -> Result<MultiAssetFilter, Self::Error> {
		use NewMultiAssetFilter::*;
		Ok(match new {
			Definite(x) => Self::Definite(x.try_into()?),
			Wild(x) => Self::Wild(x.try_into()?),
		})
	}
}

Gavin Wood's avatar
Gavin Wood committed
impl TryFrom<(OldMultiAssetFilter, u32)> for MultiAssetFilter {
	type Error = ();
	fn try_from(old: (OldMultiAssetFilter, u32)) -> Result<MultiAssetFilter, ()> {
		let count = old.1;
		Ok(match old.0 {
			OldMultiAssetFilter::Definite(x) if count >= x.len() as u32 =>
				Self::Definite(x.try_into()?),
			OldMultiAssetFilter::Wild(x) => Self::Wild((x, count).try_into()?),
			_ => return Err(()),
		})
	}
}

#[cfg(test)]
mod tests {
	use super::super::prelude::*;

	#[test]
	fn conversion_works() {
		let _: MultiAssets = (Here, 1u128).into();
	}

	#[test]
	fn from_sorted_and_deduplicated_works() {
		use super::*;
		use alloc::vec;

		let empty = vec![];
		let r = MultiAssets::from_sorted_and_deduplicated(empty);
		assert_eq!(r, Ok(MultiAssets(vec![])));

		let dup_fun = vec![(Here, 100).into(), (Here, 10).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(dup_fun);
		assert!(r.is_err());

		let dup_nft = vec![(Here, *b"notgood!").into(), (Here, *b"notgood!").into()];
		let r = MultiAssets::from_sorted_and_deduplicated(dup_nft);
		assert!(r.is_err());

		let good_fun = vec![(Here, 10).into(), (Parent, 10).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(good_fun.clone());
		assert_eq!(r, Ok(MultiAssets(good_fun)));

		let bad_fun = vec![(Parent, 10).into(), (Here, 10).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(bad_fun);
		assert!(r.is_err());

		let good_abstract_fun = vec![(Here, 100).into(), ([0u8; 32], 10).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(good_abstract_fun.clone());
		assert_eq!(r, Ok(MultiAssets(good_abstract_fun)));

		let bad_abstract_fun = vec![([0u8; 32], 10).into(), (Here, 10).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(bad_abstract_fun);
		assert!(r.is_err());

		let good_nft = vec![(Here, ()).into(), (Here, *b"good").into()];
		let r = MultiAssets::from_sorted_and_deduplicated(good_nft.clone());
		assert_eq!(r, Ok(MultiAssets(good_nft)));

		let bad_nft = vec![(Here, *b"bad!").into(), (Here, ()).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(bad_nft);
		assert!(r.is_err());

		let good_abstract_nft = vec![(Here, ()).into(), ([0u8; 32], ()).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(good_abstract_nft.clone());
		assert_eq!(r, Ok(MultiAssets(good_abstract_nft)));

		let bad_abstract_nft = vec![([0u8; 32], ()).into(), (Here, ()).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(bad_abstract_nft);
		assert!(r.is_err());

		let mixed_good = vec![(Here, 10).into(), (Here, *b"good").into()];
		let r = MultiAssets::from_sorted_and_deduplicated(mixed_good.clone());
		assert_eq!(r, Ok(MultiAssets(mixed_good)));

		let mixed_bad = vec![(Here, *b"bad!").into(), (Here, 10).into()];
		let r = MultiAssets::from_sorted_and_deduplicated(mixed_bad);
		assert!(r.is_err());
	}
	#[test]
	fn reanchor_preserves_sorting() {
		use super::*;
		use alloc::vec;

		let reanchor_context = X1(Parachain(2000));
		let dest = MultiLocation::new(1, Here);

		let asset_1: MultiAsset =
			(MultiLocation::new(0, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
		let mut asset_1_reanchored = asset_1.clone();
		assert!(asset_1_reanchored.reanchor(&dest, reanchor_context).is_ok());
		assert_eq!(
			asset_1_reanchored,
			(MultiLocation::new(0, X3(Parachain(2000), PalletInstance(50), GeneralIndex(1))), 10)
				.into()
		);

		let asset_2: MultiAsset = (MultiLocation::new(1, Here), 10).into();
		let mut asset_2_reanchored = asset_2.clone();
		assert!(asset_2_reanchored.reanchor(&dest, reanchor_context).is_ok());
		assert_eq!(asset_2_reanchored, (MultiLocation::new(0, Here), 10).into());

		let asset_3: MultiAsset = (MultiLocation::new(1, X1(Parachain(1000))), 10).into();
		let mut asset_3_reanchored = asset_3.clone();
		assert!(asset_3_reanchored.reanchor(&dest, reanchor_context).is_ok());
		assert_eq!(asset_3_reanchored, (MultiLocation::new(0, X1(Parachain(1000))), 10).into());

		let mut assets: MultiAssets =
			vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
		assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());

		// decoding respects limits and sorting
		assert!(assets
			.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
			.is_ok());

		assert!(assets.reanchor(&dest, reanchor_context).is_ok());
		assert_eq!(assets.0, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored]);

		// decoding respects limits and sorting
		assert!(assets
			.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
			.is_ok());
	}

	#[test]
	fn prepend_preserves_sorting() {
		use super::*;
		use alloc::vec;

		let prefix = MultiLocation::new(0, X1(Parachain(1000)));

		let asset_1: MultiAsset =
			(MultiLocation::new(0, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
		let mut asset_1_prepended = asset_1.clone();
		assert!(asset_1_prepended.prepend_with(&prefix).is_ok());
		// changes interior X2->X3
		assert_eq!(
			asset_1_prepended,
			(MultiLocation::new(0, X3(Parachain(1000), PalletInstance(50), GeneralIndex(1))), 10)
				.into()
		);

		let asset_2: MultiAsset =
			(MultiLocation::new(1, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
		let mut asset_2_prepended = asset_2.clone();
		assert!(asset_2_prepended.prepend_with(&prefix).is_ok());
		// changes parent
		assert_eq!(
			asset_2_prepended,
			(MultiLocation::new(0, X2(PalletInstance(50), GeneralIndex(1))), 10).into()
		);

		let asset_3: MultiAsset =
			(MultiLocation::new(2, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
		let mut asset_3_prepended = asset_3.clone();
		assert!(asset_3_prepended.prepend_with(&prefix).is_ok());
		// changes parent
		assert_eq!(
			asset_3_prepended,
			(MultiLocation::new(1, X2(PalletInstance(50), GeneralIndex(1))), 10).into()
		);

		// `From` impl does sorting.
		let mut assets: MultiAssets = vec![asset_1, asset_2, asset_3].into();
		// decoding respects limits and sorting
		assert!(assets
			.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
			.is_ok());

		// let's do `prepend_with`
		assert!(assets.prepend_with(&prefix).is_ok());
		assert_eq!(assets.0, vec![asset_2_prepended, asset_1_prepended, asset_3_prepended]);

		// decoding respects limits and sorting
		assert!(assets
			.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
			.is_ok());
	#[test]
	fn decoding_respects_limit() {
		use super::*;

		// Having lots of one asset will work since they are deduplicated
		let lots_of_one_asset: MultiAssets =
			vec![(GeneralIndex(1), 1u128).into(); MAX_ITEMS_IN_MULTIASSETS + 1].into();
		let encoded = lots_of_one_asset.encode();
		assert!(MultiAssets::decode(&mut &encoded[..]).is_ok());

		// Fewer assets than the limit works
		let mut few_assets: MultiAssets = Vec::new().into();
		for i in 0..MAX_ITEMS_IN_MULTIASSETS {
			few_assets.push((GeneralIndex(i as u128), 1u128).into());
		}
		let encoded = few_assets.encode();
		assert!(MultiAssets::decode(&mut &encoded[..]).is_ok());

		// Having lots of different assets will not work
		let mut too_many_different_assets: MultiAssets = Vec::new().into();
		for i in 0..MAX_ITEMS_IN_MULTIASSETS + 1 {
			too_many_different_assets.push((GeneralIndex(i as u128), 1u128).into());
		}
		let encoded = too_many_different_assets.encode();
		assert!(MultiAssets::decode(&mut &encoded[..]).is_err());
	}
Gavin Wood's avatar
Gavin Wood committed
}