diff --git a/polkadot/runtime/kusama/src/lib.rs b/polkadot/runtime/kusama/src/lib.rs index 40dcba5b66..c4f0da4cfa 100644 --- a/polkadot/runtime/kusama/src/lib.rs +++ b/polkadot/runtime/kusama/src/lib.rs @@ -1380,6 +1380,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } parameter_types! { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index b007d04b60..aa6be83610 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -755,6 +755,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } impl parachains_session_info::Config for Runtime {} diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 9583a3dbef..cdfc47da27 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1012,6 +1012,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } construct_runtime! { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index ce5155ac6b..4540b22e4b 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -38,7 +38,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_runtime::traits::AccountIdConversion; - use xcm_executor::traits::WeightBounds; + use xcm_executor::traits::{InvertLocation, WeightBounds}; #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] @@ -76,6 +76,9 @@ pub mod pallet { /// Means of measuring the weight consumed by an XCM message locally. type Weigher: WeightBounds; + + /// Means of inverting a location. + type LocationInverter: InvertLocation; } #[pallet::event] @@ -93,6 +96,10 @@ pub mod pallet { Filtered, /// The message's weight could not be determined. UnweighableMessage, + /// The assets to be sent are empty. + Empty, + /// Could not reanchor the assets to declare the fees for the destination chain. + CannotReanchor, } #[pallet::hooks] @@ -115,6 +122,8 @@ pub mod pallet { /// Teleport some assets from the local chain to some destination chain. /// + /// Fee payment on the destination side is made from the first asset listed in the `assets` vector. + /// /// - `origin`: Must be capable of withdrawing the `assets` and executing XCM. /// - `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send /// from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain. @@ -146,6 +155,9 @@ pub mod pallet { let value = (origin_location, assets); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; + let inv_dest = T::LocationInverter::invert_location(&dest); + let mut fees = assets.first().ok_or(Error::::Empty)?.clone(); + fees.reanchor(&inv_dest).map_err(|_| Error::::CannotReanchor)?; let mut message = Xcm::WithdrawAsset { assets, effects: vec![InitiateTeleport { @@ -153,7 +165,7 @@ pub mod pallet { dest, effects: vec![ BuyExecution { - fees: All, + fees, // Zero weight for additional XCM (since there are none to execute) weight: 0, debt: dest_weight, @@ -175,6 +187,8 @@ pub mod pallet { /// Transfer some assets from the local chain to the sovereign account of a destination chain and forward /// a notification XCM. /// + /// Fee payment on the destination side is made from the first asset listed in the `assets` vector. + /// /// - `origin`: Must be capable of withdrawing the `assets` and executing XCM. /// - `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send /// from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain. @@ -203,12 +217,15 @@ pub mod pallet { let value = (origin_location, assets); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); let (origin_location, assets) = value; + let inv_dest = T::LocationInverter::invert_location(&dest); + let mut fees = assets.first().ok_or(Error::::Empty)?.clone(); + fees.reanchor(&inv_dest).map_err(|_| Error::::CannotReanchor)?; let mut message = Xcm::TransferReserveAsset { assets, dest, effects: vec![ BuyExecution { - fees: All, + fees, // Zero weight for additional XCM (since there are none to execute) weight: 0, debt: dest_weight, diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index bd8375dec3..4689b256bc 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -186,6 +186,7 @@ impl pallet_xcm::Config for Test { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } impl origin::Config for Test {} @@ -194,9 +195,9 @@ pub(crate) fn last_event() -> Event { System::events().pop().expect("Event expected").event } -pub(crate) fn buy_execution(debt: Weight) -> Order { +pub(crate) fn buy_execution(debt: Weight, fees: MultiAsset) -> Order { use xcm::opaque::v0::prelude::*; - Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } + Order::BuyExecution { fees, weight: 0, debt, halt_on_error: false, xcm: vec![] } } pub(crate) fn new_test_ext_with_balances( diff --git a/polkadot/xcm/pallet-xcm/src/tests.rs b/polkadot/xcm/pallet-xcm/src/tests.rs index f0727e619b..a7e6b89430 100644 --- a/polkadot/xcm/pallet-xcm/src/tests.rs +++ b/polkadot/xcm/pallet-xcm/src/tests.rs @@ -42,7 +42,10 @@ fn send_works() { let message = Xcm::ReserveAssetDeposit { assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], effects: vec![ - buy_execution(weight), + buy_execution( + weight, + ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }, + ), DepositAsset { assets: vec![All], dest: sender.clone() }, ], }; @@ -76,7 +79,10 @@ fn send_fails_when_xcm_router_blocks() { let message = Xcm::ReserveAssetDeposit { assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], effects: vec![ - buy_execution(weight), + buy_execution( + weight, + ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }, + ), DepositAsset { assets: vec![All], dest: sender.clone() }, ], }; @@ -157,7 +163,13 @@ fn reserve_transfer_assets_works() { Parachain(PARA_ID).into(), Xcm::ReserveAssetDeposit { assets: vec![ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT }], - effects: vec![buy_execution(weight), DepositAsset { assets: vec![All], dest },] + effects: vec![ + buy_execution( + weight, + ConcreteFungible { id: Parent.into(), amount: SEND_AMOUNT } + ), + DepositAsset { assets: vec![All], dest }, + ] } )] ); @@ -185,7 +197,13 @@ fn execute_withdraw_to_deposit_works() { Origin::signed(ALICE), Box::new(Xcm::WithdrawAsset { assets: vec![ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT }], - effects: vec![buy_execution(weight), DepositAsset { assets: vec![All], dest }], + effects: vec![ + buy_execution( + weight, + ConcreteFungible { id: MultiLocation::Null, amount: SEND_AMOUNT } + ), + DepositAsset { assets: vec![All], dest }, + ], }), weight )); diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs index f4ad471ff6..30aa5ba21e 100644 --- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/parachain.rs @@ -187,7 +187,7 @@ pub mod mock_msg_queue { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { // XCMP - /// Some XCM was executed ok. + /// Some XCM was executed OK. Success(Option), /// Some XCM failed. Fail(Option, XcmError), @@ -302,6 +302,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = (); type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs index c69f20d05e..3b7b2f2d48 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs @@ -148,6 +148,7 @@ impl pallet_xcm::Config for Runtime { type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; } parameter_types! {