Skip to content
Snippets Groups Projects
  • Shaun Wang's avatar
    55053241
    The return of the xcm (#518) · 55053241
    Shaun Wang authored
    * Update xcm support.
    
    * Fix unit tests.
    
    * fmt
    
    * Add patches.
    
    * Update xtokens.
    
    * fmt
    
    * Clean up.
    
    * Cross-chain transfer weight.
    
    * Weight for xcm execution.
    
    * Keep clippy happy.
    
    * Clean up.
    
    * Buy execution on dest location.
    
    * Better transfer error handling.
    
    * Update deps.
    
    * fmt
    
    * fix fmt
    
    * Update events in unit tests.
    The return of the xcm (#518)
    Shaun Wang authored
    * Update xcm support.
    
    * Fix unit tests.
    
    * fmt
    
    * Add patches.
    
    * Update xtokens.
    
    * fmt
    
    * Clean up.
    
    * Cross-chain transfer weight.
    
    * Weight for xcm execution.
    
    * Keep clippy happy.
    
    * Clean up.
    
    * Buy execution on dest location.
    
    * Better transfer error handling.
    
    * Update deps.
    
    * fmt
    
    * fix fmt
    
    * Update events in unit tests.
location.rs 2.92 KiB
use xcm::v0::{
	Junction::{self, *},
	MultiAsset, MultiLocation,
};

pub trait Parse {
	/// Returns the "chain" location part. It could be parent, sibling
	/// parachain, or child parachain.
	fn chain_part(&self) -> Option<MultiLocation>;
	/// Returns "non-chain" location part.
	fn non_chain_part(&self) -> Option<MultiLocation>;
}

fn is_chain_junction(junction: Option<&Junction>) -> bool {
	matches!(junction, Some(Parent) | Some(Parachain(_)))
}

impl Parse for MultiLocation {
	fn chain_part(&self) -> Option<MultiLocation> {
		match (self.first(), self.at(1)) {
			(Some(Parent), Some(Parachain(id))) => Some((Parent, Parachain(*id)).into()),
			(Some(Parent), _) => Some(Parent.into()),
			(Some(Parachain(id)), _) => Some(Parachain(*id).into()),
			_ => None,
		}
	}

	fn non_chain_part(&self) -> Option<MultiLocation> {
		let mut location = self.clone();
		while is_chain_junction(location.first()) {
			let _ = location.take_first();
		}

		if location != MultiLocation::Null {
			Some(location)
		} else {
			None
		}
	}
}

pub trait Reserve {
	/// Returns assets reserve location.
	fn reserve(&self) -> Option<MultiLocation>;
}

impl Reserve for MultiAsset {
	fn reserve(&self) -> Option<MultiLocation> {
		if let MultiAsset::ConcreteFungible { id, .. } = self {
			id.chain_part()
		} else {
			None
		}
	}
}

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

	const PARACHAIN: Junction = Parachain(1);
	const GENERAL_INDEX: Junction = GeneralIndex { id: 1 };

	fn concrete_fungible(id: MultiLocation) -> MultiAsset {
		MultiAsset::ConcreteFungible { id, amount: 1 }
	}

	#[test]
	fn parent_as_reserve_chain() {
		assert_eq!(
			concrete_fungible(MultiLocation::X2(Parent, GENERAL_INDEX)).reserve(),
			Some(Parent.into())
		);
	}

	#[test]
	fn sibling_parachain_as_reserve_chain() {
		assert_eq!(
			concrete_fungible(MultiLocation::X3(Parent, PARACHAIN, GENERAL_INDEX)).reserve(),
			Some((Parent, PARACHAIN).into())
		);
	}

	#[test]
	fn child_parachain_as_reserve_chain() {
		assert_eq!(
			concrete_fungible(MultiLocation::X2(PARACHAIN, GENERAL_INDEX)).reserve(),
			Some(PARACHAIN.into())
		);
	}

	#[test]
	fn no_reserve_chain() {
		assert_eq!(
			concrete_fungible(MultiLocation::X1(GeneralKey("DOT".into()))).reserve(),
			None
		);
	}

	#[test]
	fn non_chain_part_works() {
		assert_eq!(MultiLocation::X1(Parent).non_chain_part(), None);
		assert_eq!(MultiLocation::X2(Parent, PARACHAIN).non_chain_part(), None);
		assert_eq!(MultiLocation::X1(PARACHAIN).non_chain_part(), None);

		assert_eq!(
			MultiLocation::X2(Parent, GENERAL_INDEX).non_chain_part(),
			Some(GENERAL_INDEX.into())
		);
		assert_eq!(
			MultiLocation::X3(Parent, GENERAL_INDEX, GENERAL_INDEX).non_chain_part(),
			Some((GENERAL_INDEX, GENERAL_INDEX).into())
		);
		assert_eq!(
			MultiLocation::X3(Parent, PARACHAIN, GENERAL_INDEX).non_chain_part(),
			Some(GENERAL_INDEX.into())
		);
		assert_eq!(
			MultiLocation::X2(PARACHAIN, GENERAL_INDEX).non_chain_part(),
			Some(GENERAL_INDEX.into())
		);
	}
}