From 55053241229a8e842dbb717c2bb281daa9fbdf04 Mon Sep 17 00:00:00 2001
From: Shaun Wang <spxwang@gmail.com>
Date: Mon, 21 Jun 2021 13:34:59 +1200
Subject: [PATCH] The return of the xcm (#518)

* 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.
---
 Cargo.dev.toml                      |  31 ++--
 tokens/Cargo.toml                   |   2 +-
 traits/Cargo.toml                   |   4 +-
 traits/src/lib.rs                   |   2 +-
 traits/src/location.rs              |   8 +-
 unknown-tokens/src/tests.rs         |  14 +-
 xcm-support/src/currency_adapter.rs |  42 ++---
 xcm-support/src/lib.rs              |   4 +-
 xcm-support/src/tests.rs            |  18 +-
 xtokens/Cargo.toml                  |  25 +--
 xtokens/src/lib.rs                  | 268 +++++++++++++++++++++-------
 11 files changed, 282 insertions(+), 136 deletions(-)

diff --git a/Cargo.dev.toml b/Cargo.dev.toml
index 7ce5324..38460e6 100644
--- a/Cargo.dev.toml
+++ b/Cargo.dev.toml
@@ -17,9 +17,9 @@ members = [
 	"vesting",
 	"rewards",
 	"nft",
-	# "xtokens",
-	# "xcm-support",
-	# "unknown-tokens",
+	"xtokens",
+	"xcm-support",
+	"unknown-tokens",
 	"build-script-utils",
 	"weight-gen",
 	"weight-meter",
@@ -82,20 +82,19 @@ sp-trie = { git = "https://github.com/paritytech//substrate", rev = "9c572625f65
 sp-version = { git = "https://github.com/paritytech//substrate", rev = "9c572625f6557dfdb19f47474369a0327d51dfbc" }
 sp-wasm-interface = { git = "https://github.com/paritytech//substrate", rev = "9c572625f6557dfdb19f47474369a0327d51dfbc" }
 
-# [patch.'https://github.com/paritytech/cumulus']
-# cumulus-primitives-core = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
-# cumulus-pallet-parachain-system = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
-# parachain-info = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
-# cumulus-pallet-xcm-handler = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
+[patch.'https://github.com/paritytech/cumulus']
+cumulus-primitives-core = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
+cumulus-pallet-parachain-system = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
+parachain-info = { git = "https://github.com/paritytech//cumulus", rev = "05ab7a377d2c3b73566c0c3bd41b003486d6913b" }
 
-# [patch.'https://github.com/paritytech/polkadot']
-# xcm = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
-# xcm-executor = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
-# xcm-builder = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
-# polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
-# polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
-# polkadot-parachain = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
-# polkadot-primitives = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+[patch.'https://github.com/paritytech/polkadot']
+xcm = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+xcm-executor = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+xcm-builder = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+polkadot-core-primitives = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+polkadot-runtime-parachains = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+polkadot-parachain = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
+polkadot-primitives = { git = "https://github.com/paritytech//polkadot", rev = "3ce09da502dd74d88f24d76f579d19fd4e6c5624" }
 
 # [patch.'https://github.com/shaunxw/xcm-simulator']
 # xcm-simulator = { git = "https://github.com/shaunxw//xcm-simulator", rev = "c52bd64a41a51d08bd5a1e27f32c47419b41f3e5" }
diff --git a/tokens/Cargo.toml b/tokens/Cargo.toml
index 3a6e836..6f652e6 100644
--- a/tokens/Cargo.toml
+++ b/tokens/Cargo.toml
@@ -23,7 +23,7 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-
 pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.5" }
 # Patch doesn't work as `pallet-elections-phragmen` is now 4.0.0 version. Revert `rev` to `statemint` branch after
 # other `statemint` dependencies upgraded.
-pallet-elections-phragmen = { git = "https://github.com/paritytech/substrate", rev = "85fa0ab80c3ceccf4bb98380d7833578aaf8815e" }
+pallet-elections-phragmen = { git = "https://github.com/paritytech/substrate", rev = "1d04678e20555e623c974ee1127bc8a45abcf3d6" }
 
 [features]
 default = ["std"]
diff --git a/traits/Cargo.toml b/traits/Cargo.toml
index 56f5219..2d7db47 100644
--- a/traits/Cargo.toml
+++ b/traits/Cargo.toml
@@ -17,7 +17,7 @@ num-traits = { version = "0.2.14", default-features = false }
 impl-trait-for-tuples = "0.2.1"
 frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.5", default-features = false }
 orml-utilities = { path = "../utilities", version = "0.4.1-dev", default-features = false }
-# xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5", default-features = false }
+xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5", default-features = false }
 
 [features]
 default = ["std"]
@@ -30,5 +30,5 @@ std = [
 	"num-traits/std",
 	"frame-support/std",
 	"orml-utilities/std",
-	# "xcm/std",
+	"xcm/std",
 ]
diff --git a/traits/src/lib.rs b/traits/src/lib.rs
index ff3c3b9..7ec0efb 100644
--- a/traits/src/lib.rs
+++ b/traits/src/lib.rs
@@ -27,7 +27,7 @@ pub mod auction;
 pub mod currency;
 pub mod data_provider;
 pub mod get_by_key;
-// pub mod location;
+pub mod location;
 pub mod nft;
 pub mod price;
 pub mod rewards;
diff --git a/traits/src/location.rs b/traits/src/location.rs
index a0b9bdd..95620b0 100644
--- a/traits/src/location.rs
+++ b/traits/src/location.rs
@@ -12,15 +12,15 @@ pub trait Parse {
 }
 
 fn is_chain_junction(junction: Option<&Junction>) -> bool {
-	matches!(junction, Some(Parent) | Some(Parachain { id: _ }))
+	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: *id }).into()),
+			(Some(Parent), Some(Parachain(id))) => Some((Parent, Parachain(*id)).into()),
 			(Some(Parent), _) => Some(Parent.into()),
-			(Some(Parachain { id }), _) => Some(Parachain { id: *id }.into()),
+			(Some(Parachain(id)), _) => Some(Parachain(*id).into()),
 			_ => None,
 		}
 	}
@@ -58,7 +58,7 @@ impl Reserve for MultiAsset {
 mod tests {
 	use super::*;
 
-	const PARACHAIN: Junction = Parachain { id: 1 };
+	const PARACHAIN: Junction = Parachain(1);
 	const GENERAL_INDEX: Junction = GeneralIndex { id: 1 };
 
 	fn concrete_fungible(id: MultiLocation) -> MultiAsset {
diff --git a/unknown-tokens/src/tests.rs b/unknown-tokens/src/tests.rs
index 3334783..d17abf5 100644
--- a/unknown-tokens/src/tests.rs
+++ b/unknown-tokens/src/tests.rs
@@ -38,7 +38,7 @@ fn deposit_concrete_fungible_asset_works() {
 			UnknownTokens::concrete_fungible_balances(&MOCK_RECIPIENT, &MOCK_CONCRETE_FUNGIBLE_ID),
 			3
 		);
-		System::assert_last_event(Event::unknown_tokens(crate::Event::Deposited(asset, MOCK_RECIPIENT)));
+		System::assert_last_event(Event::UnknownTokens(crate::Event::Deposited(asset, MOCK_RECIPIENT)));
 
 		// overflow case
 		let max_asset = concrete_fungible(u128::max_value());
@@ -58,7 +58,7 @@ fn deposit_abstract_fungible_asset() {
 			UnknownTokens::abstract_fungible_balances(&MOCK_RECIPIENT, &mock_abstract_fungible_id()),
 			3
 		);
-		System::assert_last_event(Event::unknown_tokens(crate::Event::Deposited(asset, MOCK_RECIPIENT)));
+		System::assert_last_event(Event::UnknownTokens(crate::Event::Deposited(asset, MOCK_RECIPIENT)));
 
 		// overflow case
 		let max_asset = abstract_fungible(u128::max_value());
@@ -94,7 +94,10 @@ fn withdraw_concrete_fungible_asset_works() {
 			UnknownTokens::concrete_fungible_balances(&MOCK_RECIPIENT, &MOCK_CONCRETE_FUNGIBLE_ID),
 			0
 		);
-		System::assert_last_event(Event::unknown_tokens(crate::Event::Withdrawn(asset.clone(), MOCK_RECIPIENT)));
+		System::assert_last_event(Event::UnknownTokens(crate::Event::Withdrawn(
+			asset.clone(),
+			MOCK_RECIPIENT,
+		)));
 
 		// balance too low case
 		assert_err!(
@@ -115,7 +118,10 @@ fn withdraw_abstract_fungible_asset_works() {
 			UnknownTokens::abstract_fungible_balances(&MOCK_RECIPIENT, &mock_abstract_fungible_id()),
 			0
 		);
-		System::assert_last_event(Event::unknown_tokens(crate::Event::Withdrawn(asset.clone(), MOCK_RECIPIENT)));
+		System::assert_last_event(Event::UnknownTokens(crate::Event::Withdrawn(
+			asset.clone(),
+			MOCK_RECIPIENT,
+		)));
 
 		// balance too low case
 		assert_err!(
diff --git a/xcm-support/src/currency_adapter.rs b/xcm-support/src/currency_adapter.rs
index 8a9b820..fb2cd36 100644
--- a/xcm-support/src/currency_adapter.rs
+++ b/xcm-support/src/currency_adapter.rs
@@ -9,7 +9,10 @@ use sp_std::{
 };
 
 use xcm::v0::{Error as XcmError, MultiAsset, MultiLocation, Result};
-use xcm_executor::traits::{LocationConversion, MatchesFungible, TransactAsset};
+use xcm_executor::{
+	traits::{Convert as MoreConvert, MatchesFungible, TransactAsset},
+	Assets,
+};
 
 use crate::UnknownAsset as UnknownAssetT;
 
@@ -34,13 +37,14 @@ impl From<Error> for XcmError {
 }
 
 /// The `TransactAsset` implementation, to handle `MultiAsset` deposit/withdraw.
+/// Note that teleport related functions are unimplemented.
 ///
 /// If the asset is known, deposit/withdraw will be handled by `MultiCurrency`,
 /// else by `UnknownAsset` if unknown.
 pub struct MultiCurrencyAdapter<
 	MultiCurrency,
 	UnknownAsset,
-	Matcher,
+	Match,
 	AccountId,
 	AccountIdConvert,
 	CurrencyId,
@@ -49,7 +53,7 @@ pub struct MultiCurrencyAdapter<
 	PhantomData<(
 		MultiCurrency,
 		UnknownAsset,
-		Matcher,
+		Match,
 		AccountId,
 		AccountIdConvert,
 		CurrencyId,
@@ -60,30 +64,22 @@ pub struct MultiCurrencyAdapter<
 impl<
 		MultiCurrency: orml_traits::MultiCurrency<AccountId, CurrencyId = CurrencyId>,
 		UnknownAsset: UnknownAssetT,
-		Matcher: MatchesFungible<MultiCurrency::Balance>,
-		AccountId: sp_std::fmt::Debug,
-		AccountIdConvert: LocationConversion<AccountId>,
+		Match: MatchesFungible<MultiCurrency::Balance>,
+		AccountId: sp_std::fmt::Debug + Clone,
+		AccountIdConvert: MoreConvert<MultiLocation, AccountId>,
 		CurrencyId: FullCodec + Eq + PartialEq + Copy + MaybeSerializeDeserialize + Debug,
 		CurrencyIdConvert: Convert<MultiAsset, Option<CurrencyId>>,
 	> TransactAsset
-	for MultiCurrencyAdapter<
-		MultiCurrency,
-		UnknownAsset,
-		Matcher,
-		AccountId,
-		AccountIdConvert,
-		CurrencyId,
-		CurrencyIdConvert,
-	>
+	for MultiCurrencyAdapter<MultiCurrency, UnknownAsset, Match, AccountId, AccountIdConvert, CurrencyId, CurrencyIdConvert>
 {
 	fn deposit_asset(asset: &MultiAsset, location: &MultiLocation) -> Result {
 		match (
-			AccountIdConvert::from_location(location),
+			AccountIdConvert::convert_ref(location),
 			CurrencyIdConvert::convert(asset.clone()),
-			Matcher::matches_fungible(&asset),
+			Match::matches_fungible(&asset),
 		) {
 			// known asset
-			(Some(who), Some(currency_id), Some(amount)) => {
+			(Ok(who), Some(currency_id), Some(amount)) => {
 				MultiCurrency::deposit(currency_id, &who, amount).map_err(|e| XcmError::FailedToTransactAsset(e.into()))
 			}
 			// unknown asset
@@ -91,18 +87,18 @@ impl<
 		}
 	}
 
-	fn withdraw_asset(asset: &MultiAsset, location: &MultiLocation) -> result::Result<MultiAsset, XcmError> {
+	fn withdraw_asset(asset: &MultiAsset, location: &MultiLocation) -> result::Result<Assets, XcmError> {
 		UnknownAsset::withdraw(asset, location).or_else(|_| {
-			let who = AccountIdConvert::from_location(location)
-				.ok_or_else(|| XcmError::from(Error::AccountIdConversionFailed))?;
+			let who = AccountIdConvert::convert_ref(location)
+				.map_err(|_| XcmError::from(Error::AccountIdConversionFailed))?;
 			let currency_id = CurrencyIdConvert::convert(asset.clone())
 				.ok_or_else(|| XcmError::from(Error::CurrencyIdConversionFailed))?;
-			let amount: MultiCurrency::Balance = Matcher::matches_fungible(&asset)
+			let amount: MultiCurrency::Balance = Match::matches_fungible(&asset)
 				.ok_or_else(|| XcmError::from(Error::FailedToMatchFungible))?
 				.saturated_into();
 			MultiCurrency::withdraw(currency_id, &who, amount).map_err(|e| XcmError::FailedToTransactAsset(e.into()))
 		})?;
 
-		Ok(asset.clone())
+		Ok(asset.clone().into())
 	}
 }
diff --git a/xcm-support/src/lib.rs b/xcm-support/src/lib.rs
index 039af02..a5070a4 100644
--- a/xcm-support/src/lib.rs
+++ b/xcm-support/src/lib.rs
@@ -25,8 +25,8 @@ mod currency_adapter;
 mod tests;
 
 /// The XCM handler to execute XCM locally.
-pub trait XcmHandler<AccountId> {
-	fn execute_xcm(origin: AccountId, xcm: Xcm) -> DispatchResult;
+pub trait ExecuteXcm<AccountId, Call> {
+	fn execute_xcm(origin: AccountId, xcm: Xcm<Call>) -> DispatchResult;
 }
 
 /// A `MatchesFungible` implementation. It matches concrete fungible assets
diff --git a/xcm-support/src/tests.rs b/xcm-support/src/tests.rs
index c83b502..d866793 100644
--- a/xcm-support/src/tests.rs
+++ b/xcm-support/src/tests.rs
@@ -21,8 +21,8 @@ impl Convert<MultiLocation, Option<TestCurrencyId>> for CurrencyIdConvert {
 		let token_b: Vec<u8> = "TokenB".into();
 		match l {
 			X1(Parent) => Some(RelayChainToken),
-			X3(Parent, Parachain { id: 1 }, GeneralKey(k)) if k == token_a => Some(TokenA),
-			X3(Parent, Parachain { id: 2 }, GeneralKey(k)) if k == token_b => Some(TokenB),
+			X3(Parent, Parachain(1), GeneralKey(k)) if k == token_a => Some(TokenA),
+			X3(Parent, Parachain(2), GeneralKey(k)) if k == token_b => Some(TokenB),
 			_ => None,
 		}
 	}
@@ -41,14 +41,14 @@ fn is_native_concrete_matches_native_currencies() {
 	);
 	assert_eq!(
 		MatchesCurrencyId::matches_fungible(&ConcreteFungible {
-			id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())),
+			id: X3(Parent, Parachain(1), GeneralKey("TokenA".into())),
 			amount: 100
 		}),
 		Some(100),
 	);
 	assert_eq!(
 		MatchesCurrencyId::matches_fungible(&ConcreteFungible {
-			id: X3(Parent, Parachain { id: 2 }, GeneralKey("TokenB".into())),
+			id: X3(Parent, Parachain(2), GeneralKey("TokenB".into())),
 			amount: 100
 		}),
 		Some(100),
@@ -59,14 +59,14 @@ fn is_native_concrete_matches_native_currencies() {
 fn is_native_concrete_does_not_matches_non_native_currencies() {
 	assert!(
 		<MatchesCurrencyId as MatchesFungible<u128>>::matches_fungible(&ConcreteFungible {
-			id: X3(Parent, Parachain { id: 2 }, GeneralKey("TokenC".into())),
+			id: X3(Parent, Parachain(2), GeneralKey("TokenC".into())),
 			amount: 100
 		})
 		.is_none()
 	);
 	assert!(
 		<MatchesCurrencyId as MatchesFungible<u128>>::matches_fungible(&ConcreteFungible {
-			id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenB".into())),
+			id: X3(Parent, Parachain(1), GeneralKey("TokenB".into())),
 			amount: 100
 		})
 		.is_none()
@@ -91,15 +91,15 @@ fn multi_native_asset() {
 	));
 	assert!(MultiNativeAsset::filter_asset_location(
 		&ConcreteFungible {
-			id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())),
+			id: X3(Parent, Parachain(1), GeneralKey("TokenA".into())),
 			amount: 10,
 		},
-		&X2(Parent, Parachain { id: 1 }),
+		&X2(Parent, Parachain(1)),
 	));
 	assert_eq!(
 		MultiNativeAsset::filter_asset_location(
 			&ConcreteFungible {
-				id: X3(Parent, Parachain { id: 1 }, GeneralKey("TokenA".into())),
+				id: X3(Parent, Parachain(1), GeneralKey("TokenA".into())),
 				amount: 10,
 			},
 			&X1(Parent),
diff --git a/xtokens/Cargo.toml b/xtokens/Cargo.toml
index e4d7b90..ba1dada 100644
--- a/xtokens/Cargo.toml
+++ b/xtokens/Cargo.toml
@@ -20,23 +20,23 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "polk
 cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.5", default-features = false }
 
 xcm = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5", default-features = false }
+xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5", default-features = false }
 
 orml-xcm-support = { path = "../xcm-support", default-features = false }
 orml-traits = { path = "../traits", default-features = false}
 
 [dev-dependencies]
-sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.5" }
-polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
-polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
-pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.5" }
-xcm-simulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" }
-cumulus-pallet-xcm-handler = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.5" }
-parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.5" }
-xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
-xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
-
-orml-tokens = { path = "../tokens", version = "0.4.1-dev" }
-orml-traits = { path = "../traits", version = "0.4.1-dev" }
+# sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.5" }
+# polkadot-core-primitives = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
+# polkadot-parachain = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
+# pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.5" }
+# xcm-simulator = { git = "https://github.com/shaunxw/xcm-simulator", branch = "master" }
+# parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.5" }
+# xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
+# xcm-builder = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.5" }
+
+# orml-tokens = { path = "../tokens" }
+# orml-traits = { path = "../traits" }
 
 [features]
 default = ["std"]
@@ -50,6 +50,7 @@ std = [
 	"frame-system/std",
 	"cumulus-primitives-core/std",
 	"xcm/std",
+	"xcm-executor/std",
 	"orml-xcm-support/std",
 	"orml-traits/std",
 ]
diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs
index c989811..3563519 100644
--- a/xtokens/src/lib.rs
+++ b/xtokens/src/lib.rs
@@ -22,7 +22,12 @@
 #![allow(clippy::unused_unit)]
 #![allow(clippy::large_enum_variant)]
 
-use frame_support::{pallet_prelude::*, traits::Get, transactional, Parameter};
+use frame_support::{
+	pallet_prelude::*,
+	storage::{with_transaction, TransactionOutcome},
+	traits::Get,
+	Parameter,
+};
 use frame_system::{ensure_signed, pallet_prelude::*};
 use sp_runtime::{
 	traits::{AtLeast32BitUnsigned, Convert, MaybeSerializeDeserialize, Member, Zero},
@@ -30,20 +35,24 @@ use sp_runtime::{
 };
 use sp_std::prelude::*;
 
-use xcm::v0::{
-	Junction::*,
-	MultiAsset, MultiLocation, Order,
-	Order::*,
-	Xcm::{self, *},
-};
+use xcm::v0::prelude::*;
+use xcm_executor::traits::WeightBounds;
 
+pub use module::*;
 use orml_traits::location::{Parse, Reserve};
-use orml_xcm_support::XcmHandler;
 
-mod mock;
-mod tests;
+// mod mock;
+// mod tests;
 
-pub use module::*;
+enum TransferKind {
+	/// Transfer self reserve asset.
+	SelfReserveAsset,
+	/// To reserve location.
+	ToReserve,
+	/// To non-reserve location.
+	ToNonReserve,
+}
+use TransferKind::*;
 
 #[frame_support::pallet]
 pub mod module {
@@ -65,18 +74,21 @@ pub mod module {
 		/// Currency Id.
 		type CurrencyId: Parameter + Member + Clone;
 
-		/// Convert `T::CurrencyIn` to `MultiLocation`.
+		/// Convert `T::CurrencyId` to `MultiLocation`.
 		type CurrencyIdConvert: Convert<Self::CurrencyId, Option<MultiLocation>>;
 
-		/// Convert `Self::Account` to `AccountId32`
-		type AccountId32Convert: Convert<Self::AccountId, [u8; 32]>;
+		/// Convert `T::AccountId` to `MultiLocation`.
+		type AccountIdToMultiLocation: Convert<Self::AccountId, MultiLocation>;
 
 		/// Self chain location.
 		#[pallet::constant]
 		type SelfLocation: Get<MultiLocation>;
 
-		/// Xcm handler to execute XCM.
-		type XcmHandler: XcmHandler<Self::AccountId>;
+		/// XCM executor.
+		type XcmExecutor: ExecuteXcm<Self::Call>;
+
+		/// Means of measuring the weight consumed by an XCM message locally.
+		type Weigher: WeightBounds<Self::Call>;
 	}
 
 	#[pallet::event]
@@ -85,8 +97,14 @@ pub mod module {
 	pub enum Event<T: Config> {
 		/// Transferred. \[sender, currency_id, amount, dest\]
 		Transferred(T::AccountId, T::CurrencyId, T::Balance, MultiLocation),
+		/// Transfer XCM execution failed. \[sender, currency_id, amount, dest,
+		/// xcm_err\]
+		TransferFailed(T::AccountId, T::CurrencyId, T::Balance, MultiLocation, XcmError),
 		/// Transferred `MultiAsset`. \[sender, asset, dest\]
 		TransferredMultiAsset(T::AccountId, MultiAsset, MultiLocation),
+		/// Transfer `MultiAsset` XCM execution failed. \[sender, asset, dest,
+		/// xcm_err\]
+		TransferredMultiAssetFailed(T::AccountId, MultiAsset, MultiLocation, XcmError),
 	}
 
 	#[pallet::error]
@@ -99,6 +117,8 @@ pub mod module {
 		InvalidDest,
 		/// Currency is not cross-chain transferable.
 		NotCrossChainTransferableCurrency,
+		/// The message's weight could not be determined.
+		UnweighableMessage,
 	}
 
 	#[pallet::hooks]
@@ -110,18 +130,18 @@ pub mod module {
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
 		/// Transfer native currencies.
-		#[transactional]
-		#[pallet::weight(1000)]
+		#[pallet::weight(Pallet::<T>::weight_of_transfer(currency_id.clone(), *amount, &dest))]
 		pub fn transfer(
 			origin: OriginFor<T>,
 			currency_id: T::CurrencyId,
 			amount: T::Balance,
 			dest: MultiLocation,
-		) -> DispatchResultWithPostInfo {
+			dest_weight: Weight,
+		) -> DispatchResult {
 			let who = ensure_signed(origin)?;
 
 			if amount == Zero::zero() {
-				return Ok(().into());
+				return Ok(());
 			}
 
 			let id: MultiLocation = T::CurrencyIdConvert::convert(currency_id.clone())
@@ -130,28 +150,43 @@ pub mod module {
 				id,
 				amount: amount.into(),
 			};
-			Self::do_transfer_multiasset(who.clone(), asset, dest.clone())?;
-			Self::deposit_event(Event::<T>::Transferred(who, currency_id, amount, dest));
-			Ok(().into())
+
+			let maybe_xcm_err = with_xcm_execution_transaction(|| {
+				Self::do_transfer_multiasset(who.clone(), asset, dest.clone(), dest_weight)
+			})?;
+			if let Some(xcm_err) = maybe_xcm_err {
+				Self::deposit_event(Event::<T>::TransferFailed(who, currency_id, amount, dest, xcm_err));
+			} else {
+				Self::deposit_event(Event::<T>::Transferred(who, currency_id, amount, dest));
+			}
+
+			Ok(())
 		}
 
 		/// Transfer `MultiAsset`.
-		#[transactional]
-		#[pallet::weight(1000)]
+		#[pallet::weight(Pallet::<T>::weight_of_transfer_multiasset(&asset, &dest))]
 		pub fn transfer_multiasset(
 			origin: OriginFor<T>,
 			asset: MultiAsset,
 			dest: MultiLocation,
-		) -> DispatchResultWithPostInfo {
+			dest_weight: Weight,
+		) -> DispatchResult {
 			let who = ensure_signed(origin)?;
 
 			if Self::is_zero_amount(&asset) {
-				return Ok(().into());
+				return Ok(());
 			}
 
-			Self::do_transfer_multiasset(who.clone(), asset.clone(), dest.clone())?;
-			Self::deposit_event(Event::<T>::TransferredMultiAsset(who, asset, dest));
-			Ok(().into())
+			let maybe_xcm_err = with_xcm_execution_transaction(|| {
+				Self::do_transfer_multiasset(who.clone(), asset.clone(), dest.clone(), dest_weight)
+			})?;
+			if let Some(xcm_err) = maybe_xcm_err {
+				Self::deposit_event(Event::<T>::TransferredMultiAssetFailed(who, asset, dest, xcm_err));
+			} else {
+				Self::deposit_event(Event::<T>::TransferredMultiAsset(who, asset, dest));
+			}
+
+			Ok(())
 		}
 	}
 
@@ -161,44 +196,62 @@ pub mod module {
 			who: T::AccountId,
 			asset: MultiAsset,
 			dest: MultiLocation,
-		) -> DispatchResultWithPostInfo {
-			let (dest, recipient) = Self::ensure_valid_dest(dest)?;
-
-			let self_location = T::SelfLocation::get();
-			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
-
-			let reserve = asset.reserve().ok_or(Error::<T>::AssetHasNoReserve)?;
-			let xcm = if reserve == self_location {
-				Self::transfer_self_reserve_asset(asset, dest, recipient)
-			} else if reserve == dest {
-				Self::transfer_to_reserve(asset, dest, recipient)
-			} else {
-				Self::transfer_to_non_reserve(asset, reserve, dest, recipient)
+			dest_weight: Weight,
+		) -> XcmExecutionResult {
+			let (transfer_kind, reserve, dest, recipient) = Self::transfer_kind(&asset, &dest)?;
+			let buy_order = BuyExecution {
+				fees: All,
+				// Zero weight for additional XCM (since there are none to execute)
+				weight: 0,
+				debt: dest_weight,
+				halt_on_error: false,
+				xcm: vec![],
+			};
+			let mut msg = match transfer_kind {
+				SelfReserveAsset => Self::transfer_self_reserve_asset(asset, dest, recipient, buy_order),
+				ToReserve => Self::transfer_to_reserve(asset, dest, recipient, buy_order),
+				ToNonReserve => Self::transfer_to_non_reserve(asset, reserve, dest, recipient, buy_order),
 			};
 
-			T::XcmHandler::execute_xcm(who, xcm)?;
-
-			Ok(().into())
+			let origin_location = T::AccountIdToMultiLocation::convert(who);
+			let weight = T::Weigher::weight(&mut msg).map_err(|()| Error::<T>::UnweighableMessage)?;
+			let outcome = T::XcmExecutor::execute_xcm_in_credit(origin_location, msg, weight, weight);
+			let maybe_xcm_err: Option<XcmError> = match outcome {
+				Outcome::Complete(_w) => Option::None,
+				Outcome::Incomplete(_w, err) => Some(err),
+				Outcome::Error(err) => Some(err),
+			};
+			Ok(maybe_xcm_err)
 		}
 
-		fn transfer_self_reserve_asset(asset: MultiAsset, dest: MultiLocation, recipient: MultiLocation) -> Xcm {
+		fn transfer_self_reserve_asset(
+			asset: MultiAsset,
+			dest: MultiLocation,
+			recipient: MultiLocation,
+			buy_order: Order<()>,
+		) -> Xcm<T::Call> {
 			WithdrawAsset {
 				assets: vec![asset],
 				effects: vec![DepositReserveAsset {
 					assets: vec![MultiAsset::All],
 					dest,
-					effects: Self::deposit_asset(recipient),
+					effects: vec![buy_order, Self::deposit_asset(recipient)],
 				}],
 			}
 		}
 
-		fn transfer_to_reserve(asset: MultiAsset, reserve: MultiLocation, recipient: MultiLocation) -> Xcm {
+		fn transfer_to_reserve(
+			asset: MultiAsset,
+			reserve: MultiLocation,
+			recipient: MultiLocation,
+			buy_order: Order<()>,
+		) -> Xcm<T::Call> {
 			WithdrawAsset {
 				assets: vec![asset],
 				effects: vec![InitiateReserveWithdraw {
 					assets: vec![MultiAsset::All],
 					reserve,
-					effects: Self::deposit_asset(recipient),
+					effects: vec![buy_order, Self::deposit_asset(recipient)],
 				}],
 			}
 		}
@@ -208,11 +261,12 @@ pub mod module {
 			reserve: MultiLocation,
 			dest: MultiLocation,
 			recipient: MultiLocation,
-		) -> Xcm {
+			buy_order: Order<()>,
+		) -> Xcm<T::Call> {
 			let mut reanchored_dest = dest.clone();
 			if reserve == Parent.into() {
-				if let MultiLocation::X2(Parent, Parachain { id }) = dest {
-					reanchored_dest = Parachain { id }.into();
+				if let MultiLocation::X2(Parent, Parachain(id)) = dest {
+					reanchored_dest = Parachain(id).into();
 				}
 			}
 
@@ -221,20 +275,23 @@ pub mod module {
 				effects: vec![InitiateReserveWithdraw {
 					assets: vec![MultiAsset::All],
 					reserve,
-					effects: vec![DepositReserveAsset {
-						assets: vec![MultiAsset::All],
-						dest: reanchored_dest,
-						effects: Self::deposit_asset(recipient),
-					}],
+					effects: vec![
+						buy_order.clone(),
+						DepositReserveAsset {
+							assets: vec![MultiAsset::All],
+							dest: reanchored_dest,
+							effects: vec![buy_order, Self::deposit_asset(recipient)],
+						},
+					],
 				}],
 			}
 		}
 
-		fn deposit_asset(recipient: MultiLocation) -> Vec<Order> {
-			vec![DepositAsset {
+		fn deposit_asset(recipient: MultiLocation) -> Order<()> {
+			DepositAsset {
 				assets: vec![MultiAsset::All],
 				dest: recipient,
-			}]
+			}
 		}
 
 		fn is_zero_amount(asset: &MultiAsset) -> bool {
@@ -255,7 +312,7 @@ pub mod module {
 
 		/// Ensure has the `dest` has chain part and recipient part.
 		fn ensure_valid_dest(
-			dest: MultiLocation,
+			dest: &MultiLocation,
 		) -> sp_std::result::Result<(MultiLocation, MultiLocation), DispatchError> {
 			if let (Some(dest), Some(recipient)) = (dest.chain_part(), dest.non_chain_part()) {
 				Ok((dest, recipient))
@@ -263,5 +320,92 @@ pub mod module {
 				Err(Error::<T>::InvalidDest.into())
 			}
 		}
+
+		/// Get the transfer kind.
+		///
+		/// Returns `Err` if `asset` and `dest` combination doesn't make sense,
+		/// else returns a tuple of:
+		/// - `transfer_kind`.
+		/// - asset's `reserve` parachain or relay chain location,
+		/// - `dest` parachain or relay chain location.
+		/// - `recipient` location.
+		fn transfer_kind(
+			asset: &MultiAsset,
+			dest: &MultiLocation,
+		) -> sp_std::result::Result<(TransferKind, MultiLocation, MultiLocation, MultiLocation), DispatchError> {
+			let (dest, recipient) = Self::ensure_valid_dest(dest)?;
+
+			let self_location = T::SelfLocation::get();
+			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
+
+			let reserve = asset.reserve().ok_or(Error::<T>::AssetHasNoReserve)?;
+			let transfer_kind = if reserve == self_location {
+				SelfReserveAsset
+			} else if reserve == dest {
+				ToReserve
+			} else {
+				ToNonReserve
+			};
+			Ok((transfer_kind, dest, reserve, recipient))
+		}
+	}
+
+	// weights
+	impl<T: Config> Pallet<T> {
+		/// Returns weight of `transfer_multiasset` call.
+		fn weight_of_transfer_multiasset(asset: &MultiAsset, dest: &MultiLocation) -> Weight {
+			if let Ok((transfer_kind, dest, _, reserve)) = Self::transfer_kind(asset, dest) {
+				let mut msg = match transfer_kind {
+					SelfReserveAsset => WithdrawAsset {
+						assets: vec![asset.clone()],
+						effects: vec![DepositReserveAsset {
+							assets: vec![All],
+							dest,
+							effects: vec![],
+						}],
+					},
+					ToReserve | ToNonReserve => {
+						WithdrawAsset {
+							assets: vec![asset.clone()],
+							effects: vec![InitiateReserveWithdraw {
+								assets: vec![All],
+								// `dest` is always (equal to) `reserve` in both cases
+								reserve,
+								effects: vec![],
+							}],
+						}
+					}
+				};
+				T::Weigher::weight(&mut msg).map_or(Weight::max_value(), |w| 100_000_000 + w)
+			} else {
+				0
+			}
+		}
+
+		/// Returns weight of `transfer` call.
+		fn weight_of_transfer(currency_id: T::CurrencyId, amount: T::Balance, dest: &MultiLocation) -> Weight {
+			if let Some(id) = T::CurrencyIdConvert::convert(currency_id) {
+				let asset = MultiAsset::ConcreteFungible {
+					id,
+					amount: amount.into(),
+				};
+				Self::weight_of_transfer_multiasset(&asset, &dest)
+			} else {
+				0
+			}
+		}
 	}
 }
+
+type XcmExecutionResult = sp_std::result::Result<Option<XcmError>, DispatchError>;
+
+/// Only commit storage if no `DispatchError` and no `XcmError`, else roll back.
+fn with_xcm_execution_transaction(f: impl FnOnce() -> XcmExecutionResult) -> XcmExecutionResult {
+	with_transaction(|| {
+		let res = f();
+		match res {
+			Ok(ref err) if err.is_none() => TransactionOutcome::Commit(res),
+			_ => TransactionOutcome::Rollback(res),
+		}
+	})
+}
-- 
GitLab