pallet_xcm/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Pallet to handle XCM messages.
18
19#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "runtime-benchmarks")]
22pub mod benchmarking;
23#[cfg(test)]
24mod mock;
25#[cfg(test)]
26mod tests;
27
28pub mod migration;
29
30extern crate alloc;
31
32use alloc::{boxed::Box, vec, vec::Vec};
33use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
34use core::{marker::PhantomData, result::Result};
35use frame_support::{
36	dispatch::{
37		DispatchErrorWithPostInfo, GetDispatchInfo, PostDispatchInfo, WithPostDispatchInfo,
38	},
39	pallet_prelude::*,
40	traits::{
41		Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency,
42		OriginTrait, WithdrawReasons,
43	},
44	PalletId,
45};
46use frame_system::pallet_prelude::{BlockNumberFor, *};
47pub use pallet::*;
48use scale_info::TypeInfo;
49use sp_runtime::{
50	traits::{
51		AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
52		Saturating, Zero,
53	},
54	Either, RuntimeDebug,
55};
56use xcm::{latest::QueryResponseInfo, prelude::*};
57use xcm_builder::{
58	ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
59	QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
60};
61use xcm_executor::{
62	traits::{
63		AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
64		DropAssets, MatchesFungible, OnResponse, Properties, QueryHandler, QueryResponseStatus,
65		RecordXcm, TransactAsset, TransferType, VersionChangeNotifier, WeightBounds,
66		XcmAssetTransfers,
67	},
68	AssetsInHolding,
69};
70use xcm_runtime_apis::{
71	dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
72	fees::Error as XcmPaymentApiError,
73	trusted_query::Error as TrustedQueryApiError,
74};
75
76#[cfg(any(feature = "try-runtime", test))]
77use sp_runtime::TryRuntimeError;
78
79pub trait WeightInfo {
80	fn send() -> Weight;
81	fn teleport_assets() -> Weight;
82	fn reserve_transfer_assets() -> Weight;
83	fn transfer_assets() -> Weight;
84	fn execute() -> Weight;
85	fn force_xcm_version() -> Weight;
86	fn force_default_xcm_version() -> Weight;
87	fn force_subscribe_version_notify() -> Weight;
88	fn force_unsubscribe_version_notify() -> Weight;
89	fn force_suspension() -> Weight;
90	fn migrate_supported_version() -> Weight;
91	fn migrate_version_notifiers() -> Weight;
92	fn already_notified_target() -> Weight;
93	fn notify_current_targets() -> Weight;
94	fn notify_target_migration_fail() -> Weight;
95	fn migrate_version_notify_targets() -> Weight;
96	fn migrate_and_notify_old_targets() -> Weight;
97	fn new_query() -> Weight;
98	fn take_response() -> Weight;
99	fn claim_assets() -> Weight;
100}
101
102/// fallback implementation
103pub struct TestWeightInfo;
104impl WeightInfo for TestWeightInfo {
105	fn send() -> Weight {
106		Weight::from_parts(100_000_000, 0)
107	}
108
109	fn teleport_assets() -> Weight {
110		Weight::from_parts(100_000_000, 0)
111	}
112
113	fn reserve_transfer_assets() -> Weight {
114		Weight::from_parts(100_000_000, 0)
115	}
116
117	fn transfer_assets() -> Weight {
118		Weight::from_parts(100_000_000, 0)
119	}
120
121	fn execute() -> Weight {
122		Weight::from_parts(100_000_000, 0)
123	}
124
125	fn force_xcm_version() -> Weight {
126		Weight::from_parts(100_000_000, 0)
127	}
128
129	fn force_default_xcm_version() -> Weight {
130		Weight::from_parts(100_000_000, 0)
131	}
132
133	fn force_subscribe_version_notify() -> Weight {
134		Weight::from_parts(100_000_000, 0)
135	}
136
137	fn force_unsubscribe_version_notify() -> Weight {
138		Weight::from_parts(100_000_000, 0)
139	}
140
141	fn force_suspension() -> Weight {
142		Weight::from_parts(100_000_000, 0)
143	}
144
145	fn migrate_supported_version() -> Weight {
146		Weight::from_parts(100_000_000, 0)
147	}
148
149	fn migrate_version_notifiers() -> Weight {
150		Weight::from_parts(100_000_000, 0)
151	}
152
153	fn already_notified_target() -> Weight {
154		Weight::from_parts(100_000_000, 0)
155	}
156
157	fn notify_current_targets() -> Weight {
158		Weight::from_parts(100_000_000, 0)
159	}
160
161	fn notify_target_migration_fail() -> Weight {
162		Weight::from_parts(100_000_000, 0)
163	}
164
165	fn migrate_version_notify_targets() -> Weight {
166		Weight::from_parts(100_000_000, 0)
167	}
168
169	fn migrate_and_notify_old_targets() -> Weight {
170		Weight::from_parts(100_000_000, 0)
171	}
172
173	fn new_query() -> Weight {
174		Weight::from_parts(100_000_000, 0)
175	}
176
177	fn take_response() -> Weight {
178		Weight::from_parts(100_000_000, 0)
179	}
180
181	fn claim_assets() -> Weight {
182		Weight::from_parts(100_000_000, 0)
183	}
184}
185
186#[frame_support::pallet]
187pub mod pallet {
188	use super::*;
189	use frame_support::{
190		dispatch::{GetDispatchInfo, PostDispatchInfo},
191		parameter_types,
192	};
193	use frame_system::Config as SysConfig;
194	use sp_core::H256;
195	use sp_runtime::traits::Dispatchable;
196	use xcm_executor::traits::{MatchesFungible, WeightBounds};
197
198	parameter_types! {
199		/// An implementation of `Get<u32>` which just returns the latest XCM version which we can
200		/// support.
201		pub const CurrentXcmVersion: u32 = XCM_VERSION;
202	}
203
204	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
205
206	#[pallet::pallet]
207	#[pallet::storage_version(STORAGE_VERSION)]
208	#[pallet::without_storage_info]
209	pub struct Pallet<T>(_);
210
211	pub type BalanceOf<T> =
212		<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
213
214	#[pallet::config]
215	/// The module configuration trait.
216	pub trait Config: frame_system::Config {
217		/// The overarching event type.
218		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
219
220		/// A lockable currency.
221		// TODO: We should really use a trait which can handle multiple currencies.
222		type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
223
224		/// The `Asset` matcher for `Currency`.
225		type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
226
227		/// Required origin for sending XCM messages. If successful, it resolves to `Location`
228		/// which exists as an interior location within this chain's XCM context.
229		type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
230
231		/// The type used to actually dispatch an XCM to its destination.
232		type XcmRouter: SendXcm;
233
234		/// Required origin for executing XCM messages, including the teleport functionality. If
235		/// successful, then it resolves to `Location` which exists as an interior location
236		/// within this chain's XCM context.
237		type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
238
239		/// Our XCM filter which messages to be executed using `XcmExecutor` must pass.
240		type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
241
242		/// Something to execute an XCM message.
243		type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers;
244
245		/// Our XCM filter which messages to be teleported using the dedicated extrinsic must pass.
246		type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
247
248		/// Our XCM filter which messages to be reserve-transferred using the dedicated extrinsic
249		/// must pass.
250		type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
251
252		/// Means of measuring the weight consumed by an XCM message locally.
253		type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
254
255		/// This chain's Universal Location.
256		type UniversalLocation: Get<InteriorLocation>;
257
258		/// The runtime `Origin` type.
259		type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
260
261		/// The runtime `Call` type.
262		type RuntimeCall: Parameter
263			+ GetDispatchInfo
264			+ Dispatchable<
265				RuntimeOrigin = <Self as Config>::RuntimeOrigin,
266				PostInfo = PostDispatchInfo,
267			>;
268
269		const VERSION_DISCOVERY_QUEUE_SIZE: u32;
270
271		/// The latest supported version that we advertise. Generally just set it to
272		/// `pallet_xcm::CurrentXcmVersion`.
273		type AdvertisedXcmVersion: Get<XcmVersion>;
274
275		/// The origin that is allowed to call privileged operations on the XCM pallet
276		type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
277
278		/// The assets which we consider a given origin is trusted if they claim to have placed a
279		/// lock.
280		type TrustedLockers: ContainsPair<Location, Asset>;
281
282		/// How to get an `AccountId` value from a `Location`, useful for handling asset locks.
283		type SovereignAccountOf: ConvertLocation<Self::AccountId>;
284
285		/// The maximum number of local XCM locks that a single account may have.
286		type MaxLockers: Get<u32>;
287
288		/// The maximum number of consumers a single remote lock may have.
289		type MaxRemoteLockConsumers: Get<u32>;
290
291		/// The ID type for local consumers of remote locks.
292		type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
293
294		/// Weight information for extrinsics in this pallet.
295		type WeightInfo: WeightInfo;
296	}
297
298	impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
299		fn execute() -> Weight {
300			T::WeightInfo::execute()
301		}
302	}
303
304	impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
305		type WeightInfo = Self;
306		fn execute(
307			origin: OriginFor<T>,
308			message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
309			max_weight: Weight,
310		) -> Result<Weight, DispatchErrorWithPostInfo> {
311			tracing::trace!(target: "xcm::pallet_xcm::execute", ?message, ?max_weight);
312			let outcome = (|| {
313				let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
314				let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
315				let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
316				let value = (origin_location, message);
317				ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
318				let (origin_location, message) = value;
319				Ok(T::XcmExecutor::prepare_and_execute(
320					origin_location,
321					message,
322					&mut hash,
323					max_weight,
324					max_weight,
325				))
326			})()
327			.map_err(|e: DispatchError| {
328				e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
329			})?;
330
331			Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
332			let weight_used = outcome.weight_used();
333			outcome.ensure_complete().map_err(|error| {
334				tracing::error!(target: "xcm::pallet_xcm::execute", ?error, "XCM execution failed with error");
335				Error::<T>::LocalExecutionIncomplete.with_weight(
336					weight_used.saturating_add(
337						<Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
338					),
339				)
340			})?;
341			Ok(weight_used)
342		}
343	}
344
345	impl<T: Config> SendControllerWeightInfo for Pallet<T> {
346		fn send() -> Weight {
347			T::WeightInfo::send()
348		}
349	}
350
351	impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
352		type WeightInfo = Self;
353		fn send(
354			origin: OriginFor<T>,
355			dest: Box<VersionedLocation>,
356			message: Box<VersionedXcm<()>>,
357		) -> Result<XcmHash, DispatchError> {
358			let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
359			let interior: Junctions =
360				origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
361			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
362			let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
363
364			let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
365				.map_err(|error| {
366					tracing::error!(target: "xcm::pallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
367					Error::<T>::from(error)
368				})?;
369			let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
370			Self::deposit_event(e);
371			Ok(message_id)
372		}
373	}
374
375	impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
376		fn query() -> Weight {
377			T::WeightInfo::new_query()
378		}
379		fn take_response() -> Weight {
380			T::WeightInfo::take_response()
381		}
382	}
383
384	impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
385		type WeightInfo = Self;
386
387		fn query(
388			origin: OriginFor<T>,
389			timeout: BlockNumberFor<T>,
390			match_querier: VersionedLocation,
391		) -> Result<QueryId, DispatchError> {
392			let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
393			let query_id = <Self as QueryHandler>::new_query(
394				responder,
395				timeout,
396				Location::try_from(match_querier)
397					.map_err(|_| Into::<DispatchError>::into(Error::<T>::BadVersion))?,
398			);
399
400			Ok(query_id)
401		}
402	}
403
404	#[pallet::event]
405	#[pallet::generate_deposit(pub(super) fn deposit_event)]
406	pub enum Event<T: Config> {
407		/// Execution of an XCM message was attempted.
408		Attempted { outcome: xcm::latest::Outcome },
409		/// A XCM message was sent.
410		Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
411		/// Query response received which does not match a registered query. This may be because a
412		/// matching query was never registered, it may be because it is a duplicate response, or
413		/// because the query timed out.
414		UnexpectedResponse { origin: Location, query_id: QueryId },
415		/// Query response has been received and is ready for taking with `take_response`. There is
416		/// no registered notification call.
417		ResponseReady { query_id: QueryId, response: Response },
418		/// Query response has been received and query is removed. The registered notification has
419		/// been dispatched and executed successfully.
420		Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
421		/// Query response has been received and query is removed. The registered notification
422		/// could not be dispatched because the dispatch weight is greater than the maximum weight
423		/// originally budgeted by this runtime for the query result.
424		NotifyOverweight {
425			query_id: QueryId,
426			pallet_index: u8,
427			call_index: u8,
428			actual_weight: Weight,
429			max_budgeted_weight: Weight,
430		},
431		/// Query response has been received and query is removed. There was a general error with
432		/// dispatching the notification call.
433		NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
434		/// Query response has been received and query is removed. The dispatch was unable to be
435		/// decoded into a `Call`; this might be due to dispatch function having a signature which
436		/// is not `(origin, QueryId, Response)`.
437		NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
438		/// Expected query response has been received but the origin location of the response does
439		/// not match that expected. The query remains registered for a later, valid, response to
440		/// be received and acted upon.
441		InvalidResponder {
442			origin: Location,
443			query_id: QueryId,
444			expected_location: Option<Location>,
445		},
446		/// Expected query response has been received but the expected origin location placed in
447		/// storage by this runtime previously cannot be decoded. The query remains registered.
448		///
449		/// This is unexpected (since a location placed in storage in a previously executing
450		/// runtime should be readable prior to query timeout) and dangerous since the possibly
451		/// valid response will be dropped. Manual governance intervention is probably going to be
452		/// needed.
453		InvalidResponderVersion { origin: Location, query_id: QueryId },
454		/// Received query response has been read and removed.
455		ResponseTaken { query_id: QueryId },
456		/// Some assets have been placed in an asset trap.
457		AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
458		/// An XCM version change notification message has been attempted to be sent.
459		///
460		/// The cost of sending it (borne by the chain) is included.
461		VersionChangeNotified {
462			destination: Location,
463			result: XcmVersion,
464			cost: Assets,
465			message_id: XcmHash,
466		},
467		/// The supported version of a location has been changed. This might be through an
468		/// automatic notification or a manual intervention.
469		SupportedVersionChanged { location: Location, version: XcmVersion },
470		/// A given location which had a version change subscription was dropped owing to an error
471		/// sending the notification to it.
472		NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
473		/// A given location which had a version change subscription was dropped owing to an error
474		/// migrating the location to our new XCM format.
475		NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
476		/// Expected query response has been received but the expected querier location placed in
477		/// storage by this runtime previously cannot be decoded. The query remains registered.
478		///
479		/// This is unexpected (since a location placed in storage in a previously executing
480		/// runtime should be readable prior to query timeout) and dangerous since the possibly
481		/// valid response will be dropped. Manual governance intervention is probably going to be
482		/// needed.
483		InvalidQuerierVersion { origin: Location, query_id: QueryId },
484		/// Expected query response has been received but the querier location of the response does
485		/// not match the expected. The query remains registered for a later, valid, response to
486		/// be received and acted upon.
487		InvalidQuerier {
488			origin: Location,
489			query_id: QueryId,
490			expected_querier: Location,
491			maybe_actual_querier: Option<Location>,
492		},
493		/// A remote has requested XCM version change notification from us and we have honored it.
494		/// A version information message is sent to them and its cost is included.
495		VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
496		/// We have requested that a remote chain send us XCM version change notifications.
497		VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
498		/// We have requested that a remote chain stops sending us XCM version change
499		/// notifications.
500		VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
501		/// Fees were paid from a location for an operation (often for using `SendXcm`).
502		FeesPaid { paying: Location, fees: Assets },
503		/// Some assets have been claimed from an asset trap
504		AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
505		/// A XCM version migration finished.
506		VersionMigrationFinished { version: XcmVersion },
507	}
508
509	#[pallet::origin]
510	#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
511	pub enum Origin {
512		/// It comes from somewhere in the XCM space wanting to transact.
513		Xcm(Location),
514		/// It comes as an expected response from an XCM location.
515		Response(Location),
516	}
517	impl From<Location> for Origin {
518		fn from(location: Location) -> Origin {
519			Origin::Xcm(location)
520		}
521	}
522
523	#[pallet::error]
524	pub enum Error<T> {
525		/// The desired destination was unreachable, generally because there is a no way of routing
526		/// to it.
527		Unreachable,
528		/// There was some other issue (i.e. not to do with routing) in sending the message.
529		/// Perhaps a lack of space for buffering the message.
530		SendFailure,
531		/// The message execution fails the filter.
532		Filtered,
533		/// The message's weight could not be determined.
534		UnweighableMessage,
535		/// The destination `Location` provided cannot be inverted.
536		DestinationNotInvertible,
537		/// The assets to be sent are empty.
538		Empty,
539		/// Could not re-anchor the assets to declare the fees for the destination chain.
540		CannotReanchor,
541		/// Too many assets have been attempted for transfer.
542		TooManyAssets,
543		/// Origin is invalid for sending.
544		InvalidOrigin,
545		/// The version of the `Versioned` value used is not able to be interpreted.
546		BadVersion,
547		/// The given location could not be used (e.g. because it cannot be expressed in the
548		/// desired version of XCM).
549		BadLocation,
550		/// The referenced subscription could not be found.
551		NoSubscription,
552		/// The location is invalid since it already has a subscription from us.
553		AlreadySubscribed,
554		/// Could not check-out the assets for teleportation to the destination chain.
555		CannotCheckOutTeleport,
556		/// The owner does not own (all) of the asset that they wish to do the operation on.
557		LowBalance,
558		/// The asset owner has too many locks on the asset.
559		TooManyLocks,
560		/// The given account is not an identifiable sovereign account for any location.
561		AccountNotSovereign,
562		/// The operation required fees to be paid which the initiator could not meet.
563		FeesNotMet,
564		/// A remote lock with the corresponding data could not be found.
565		LockNotFound,
566		/// The unlock operation cannot succeed because there are still consumers of the lock.
567		InUse,
568		/// Invalid asset, reserve chain could not be determined for it.
569		#[codec(index = 21)]
570		InvalidAssetUnknownReserve,
571		/// Invalid asset, do not support remote asset reserves with different fees reserves.
572		#[codec(index = 22)]
573		InvalidAssetUnsupportedReserve,
574		/// Too many assets with different reserve locations have been attempted for transfer.
575		#[codec(index = 23)]
576		TooManyReserves,
577		/// Local XCM execution incomplete.
578		#[codec(index = 24)]
579		LocalExecutionIncomplete,
580	}
581
582	impl<T: Config> From<SendError> for Error<T> {
583		fn from(e: SendError) -> Self {
584			match e {
585				SendError::Fees => Error::<T>::FeesNotMet,
586				SendError::NotApplicable => Error::<T>::Unreachable,
587				_ => Error::<T>::SendFailure,
588			}
589		}
590	}
591
592	impl<T: Config> From<AssetTransferError> for Error<T> {
593		fn from(e: AssetTransferError) -> Self {
594			match e {
595				AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
596			}
597		}
598	}
599
600	/// The status of a query.
601	#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
602	pub enum QueryStatus<BlockNumber> {
603		/// The query was sent but no response has yet been received.
604		Pending {
605			/// The `QueryResponse` XCM must have this origin to be considered a reply for this
606			/// query.
607			responder: VersionedLocation,
608			/// The `QueryResponse` XCM must have this value as the `querier` field to be
609			/// considered a reply for this query. If `None` then the querier is ignored.
610			maybe_match_querier: Option<VersionedLocation>,
611			maybe_notify: Option<(u8, u8)>,
612			timeout: BlockNumber,
613		},
614		/// The query is for an ongoing version notification subscription.
615		VersionNotifier { origin: VersionedLocation, is_active: bool },
616		/// A response has been received.
617		Ready { response: VersionedResponse, at: BlockNumber },
618	}
619
620	#[derive(Copy, Clone)]
621	pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
622	impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
623	impl<'a> Encode for LatestVersionedLocation<'a> {
624		fn encode(&self) -> Vec<u8> {
625			let mut r = VersionedLocation::from(Location::default()).encode();
626			r.truncate(1);
627			self.0.using_encoded(|d| r.extend_from_slice(d));
628			r
629		}
630	}
631
632	#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
633	pub enum VersionMigrationStage {
634		MigrateSupportedVersion,
635		MigrateVersionNotifiers,
636		NotifyCurrentTargets(Option<Vec<u8>>),
637		MigrateAndNotifyOldTargets,
638	}
639
640	impl Default for VersionMigrationStage {
641		fn default() -> Self {
642			Self::MigrateSupportedVersion
643		}
644	}
645
646	/// The latest available query index.
647	#[pallet::storage]
648	pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
649
650	/// The ongoing queries.
651	#[pallet::storage]
652	#[pallet::getter(fn query)]
653	pub(super) type Queries<T: Config> =
654		StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
655
656	/// The existing asset traps.
657	///
658	/// Key is the blake2 256 hash of (origin, versioned `Assets`) pair. Value is the number of
659	/// times this pair has been trapped (usually just 1 if it exists at all).
660	#[pallet::storage]
661	#[pallet::getter(fn asset_trap)]
662	pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
663
664	/// Default version to encode XCM when latest version of destination is unknown. If `None`,
665	/// then the destinations whose XCM version is unknown are considered unreachable.
666	#[pallet::storage]
667	#[pallet::whitelist_storage]
668	pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
669
670	/// The Latest versions that we know various locations support.
671	#[pallet::storage]
672	pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
673		_,
674		Twox64Concat,
675		XcmVersion,
676		Blake2_128Concat,
677		VersionedLocation,
678		XcmVersion,
679		OptionQuery,
680	>;
681
682	/// All locations that we have requested version notifications from.
683	#[pallet::storage]
684	pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
685		_,
686		Twox64Concat,
687		XcmVersion,
688		Blake2_128Concat,
689		VersionedLocation,
690		QueryId,
691		OptionQuery,
692	>;
693
694	/// The target locations that are subscribed to our version changes, as well as the most recent
695	/// of our versions we informed them of.
696	#[pallet::storage]
697	pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
698		_,
699		Twox64Concat,
700		XcmVersion,
701		Blake2_128Concat,
702		VersionedLocation,
703		(QueryId, Weight, XcmVersion),
704		OptionQuery,
705	>;
706
707	pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
708	impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
709		fn get() -> u32 {
710			T::VERSION_DISCOVERY_QUEUE_SIZE
711		}
712	}
713
714	/// Destinations whose latest XCM version we would like to know. Duplicates not allowed, and
715	/// the `u32` counter is the number of times that a send to the destination has been attempted,
716	/// which is used as a prioritization.
717	#[pallet::storage]
718	#[pallet::whitelist_storage]
719	pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
720		_,
721		BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
722		ValueQuery,
723	>;
724
725	/// The current migration's stage, if any.
726	#[pallet::storage]
727	pub(super) type CurrentMigration<T: Config> =
728		StorageValue<_, VersionMigrationStage, OptionQuery>;
729
730	#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
731	#[scale_info(skip_type_params(MaxConsumers))]
732	pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
733		/// Total amount of the asset held by the remote lock.
734		pub amount: u128,
735		/// The owner of the locked asset.
736		pub owner: VersionedLocation,
737		/// The location which holds the original lock.
738		pub locker: VersionedLocation,
739		/// Local consumers of the remote lock with a consumer identifier and the amount
740		/// of fungible asset every consumer holds.
741		/// Every consumer can hold up to total amount of the remote lock.
742		pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
743	}
744
745	impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
746		/// Amount of the remote lock in use by consumers.
747		/// Returns `None` if the remote lock has no consumers.
748		pub fn amount_held(&self) -> Option<u128> {
749			self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
750		}
751	}
752
753	/// Fungible assets which we know are locked on a remote chain.
754	#[pallet::storage]
755	pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
756		_,
757		(
758			NMapKey<Twox64Concat, XcmVersion>,
759			NMapKey<Blake2_128Concat, T::AccountId>,
760			NMapKey<Blake2_128Concat, VersionedAssetId>,
761		),
762		RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
763		OptionQuery,
764	>;
765
766	/// Fungible assets which we know are locked on this chain.
767	#[pallet::storage]
768	pub(super) type LockedFungibles<T: Config> = StorageMap<
769		_,
770		Blake2_128Concat,
771		T::AccountId,
772		BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
773		OptionQuery,
774	>;
775
776	/// Global suspension state of the XCM executor.
777	#[pallet::storage]
778	pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
779
780	/// Whether or not incoming XCMs (both executed locally and received) should be recorded.
781	/// Only one XCM program will be recorded at a time.
782	/// This is meant to be used in runtime APIs, and it's advised it stays false
783	/// for all other use cases, so as to not degrade regular performance.
784	///
785	/// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`]
786	/// implementation in the XCM executor configuration.
787	#[pallet::storage]
788	pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
789
790	/// If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally
791	/// will be stored here.
792	/// Runtime APIs can fetch the XCM that was executed by accessing this value.
793	///
794	/// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`]
795	/// implementation in the XCM executor configuration.
796	#[pallet::storage]
797	pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
798
799	#[pallet::genesis_config]
800	pub struct GenesisConfig<T: Config> {
801		#[serde(skip)]
802		pub _config: core::marker::PhantomData<T>,
803		/// The default version to encode outgoing XCM messages with.
804		pub safe_xcm_version: Option<XcmVersion>,
805	}
806
807	impl<T: Config> Default for GenesisConfig<T> {
808		fn default() -> Self {
809			Self { safe_xcm_version: Some(XCM_VERSION), _config: Default::default() }
810		}
811	}
812
813	#[pallet::genesis_build]
814	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
815		fn build(&self) {
816			SafeXcmVersion::<T>::set(self.safe_xcm_version);
817		}
818	}
819
820	#[pallet::hooks]
821	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
822		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
823			let mut weight_used = Weight::zero();
824			if let Some(migration) = CurrentMigration::<T>::get() {
825				// Consume 10% of block at most
826				let max_weight = T::BlockWeights::get().max_block / 10;
827				let (w, maybe_migration) = Self::check_xcm_version_change(migration, max_weight);
828				if maybe_migration.is_none() {
829					Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
830				}
831				CurrentMigration::<T>::set(maybe_migration);
832				weight_used.saturating_accrue(w);
833			}
834
835			// Here we aim to get one successful version negotiation request sent per block, ordered
836			// by the destinations being most sent to.
837			let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
838			// TODO: correct weights.
839			weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
840			q.sort_by_key(|i| i.1);
841			while let Some((versioned_dest, _)) = q.pop() {
842				if let Ok(dest) = Location::try_from(versioned_dest) {
843					if Self::request_version_notify(dest).is_ok() {
844						// TODO: correct weights.
845						weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
846						break
847					}
848				}
849			}
850			// Should never fail since we only removed items. But better safe than panicking as it's
851			// way better to drop the queue than panic on initialize.
852			if let Ok(q) = BoundedVec::try_from(q) {
853				VersionDiscoveryQueue::<T>::put(q);
854			}
855			weight_used
856		}
857
858		#[cfg(feature = "try-runtime")]
859		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
860			Self::do_try_state()
861		}
862	}
863
864	pub mod migrations {
865		use super::*;
866		use frame_support::traits::{PalletInfoAccess, StorageVersion};
867
868		#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
869		enum QueryStatusV0<BlockNumber> {
870			Pending {
871				responder: VersionedLocation,
872				maybe_notify: Option<(u8, u8)>,
873				timeout: BlockNumber,
874			},
875			VersionNotifier {
876				origin: VersionedLocation,
877				is_active: bool,
878			},
879			Ready {
880				response: VersionedResponse,
881				at: BlockNumber,
882			},
883		}
884		impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
885			fn from(old: QueryStatusV0<B>) -> Self {
886				use QueryStatusV0::*;
887				match old {
888					Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
889						responder,
890						maybe_notify,
891						timeout,
892						maybe_match_querier: Some(Location::here().into()),
893					},
894					VersionNotifier { origin, is_active } =>
895						QueryStatus::VersionNotifier { origin, is_active },
896					Ready { response, at } => QueryStatus::Ready { response, at },
897				}
898			}
899		}
900
901		pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
902		) -> frame_support::weights::Weight {
903			let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
904			tracing::info!(
905				target: "runtime::xcm",
906				?on_chain_storage_version,
907				"Running migration storage v1 for xcm with storage version",
908			);
909
910			if on_chain_storage_version < 1 {
911				let mut count = 0;
912				Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
913					count += 1;
914					Some(value.into())
915				});
916				StorageVersion::new(1).put::<P>();
917				tracing::info!(
918					target: "runtime::xcm",
919					?on_chain_storage_version,
920					"Running migration storage v1 for xcm with storage version was complete",
921				);
922				// calculate and return migration weights
923				T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
924			} else {
925				tracing::warn!(
926					target: "runtime::xcm",
927					?on_chain_storage_version,
928					"Attempted to apply migration to v1 but failed because storage version is",
929				);
930				T::DbWeight::get().reads(1)
931			}
932		}
933	}
934
935	#[pallet::call(weight(<T as Config>::WeightInfo))]
936	impl<T: Config> Pallet<T> {
937		#[pallet::call_index(0)]
938		pub fn send(
939			origin: OriginFor<T>,
940			dest: Box<VersionedLocation>,
941			message: Box<VersionedXcm<()>>,
942		) -> DispatchResult {
943			<Self as SendController<_>>::send(origin, dest, message)?;
944			Ok(())
945		}
946
947		/// Teleport some assets from the local chain to some destination chain.
948		///
949		/// **This function is deprecated: Use `limited_teleport_assets` instead.**
950		///
951		/// Fee payment on the destination side is made from the asset in the `assets` vector of
952		/// index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,
953		/// with all fees taken as needed from the asset.
954		///
955		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
956		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
957		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
958		///   relay to parachain.
959		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
960		///   generally be an `AccountId32` value.
961		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
962		///   fee on the `dest` chain.
963		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
964		///   fees.
965		#[pallet::call_index(1)]
966		#[allow(deprecated)]
967		#[deprecated(
968			note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
969		)]
970		pub fn teleport_assets(
971			origin: OriginFor<T>,
972			dest: Box<VersionedLocation>,
973			beneficiary: Box<VersionedLocation>,
974			assets: Box<VersionedAssets>,
975			fee_asset_item: u32,
976		) -> DispatchResult {
977			Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
978		}
979
980		/// Transfer some assets from the local chain to the destination chain through their local,
981		/// destination or remote reserve.
982		///
983		/// `assets` must have same reserve location and may not be teleportable to `dest`.
984		///  - `assets` have local reserve: transfer assets to sovereign account of destination
985		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
986		///    assets to `beneficiary`.
987		///  - `assets` have destination reserve: burn local assets and forward a notification to
988		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
989		///    deposit them to `beneficiary`.
990		///  - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move
991		///    reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest`
992		///    to mint and deposit reserve-based assets to `beneficiary`.
993		///
994		/// **This function is deprecated: Use `limited_reserve_transfer_assets` instead.**
995		///
996		/// Fee payment on the destination side is made from the asset in the `assets` vector of
997		/// index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,
998		/// with all fees taken as needed from the asset.
999		///
1000		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
1001		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
1002		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
1003		///   relay to parachain.
1004		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
1005		///   generally be an `AccountId32` value.
1006		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
1007		///   fee on the `dest` (and possibly reserve) chains.
1008		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
1009		///   fees.
1010		#[pallet::call_index(2)]
1011		#[allow(deprecated)]
1012		#[deprecated(
1013			note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1014		)]
1015		pub fn reserve_transfer_assets(
1016			origin: OriginFor<T>,
1017			dest: Box<VersionedLocation>,
1018			beneficiary: Box<VersionedLocation>,
1019			assets: Box<VersionedAssets>,
1020			fee_asset_item: u32,
1021		) -> DispatchResult {
1022			Self::do_reserve_transfer_assets(
1023				origin,
1024				dest,
1025				beneficiary,
1026				assets,
1027				fee_asset_item,
1028				Unlimited,
1029			)
1030		}
1031
1032		/// Execute an XCM message from a local, signed, origin.
1033		///
1034		/// An event is deposited indicating whether `msg` could be executed completely or only
1035		/// partially.
1036		///
1037		/// No more than `max_weight` will be used in its attempted execution. If this is less than
1038		/// the maximum amount of weight that the message could take to be executed, then no
1039		/// execution attempt will be made.
1040		#[pallet::call_index(3)]
1041		#[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1042		pub fn execute(
1043			origin: OriginFor<T>,
1044			message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1045			max_weight: Weight,
1046		) -> DispatchResultWithPostInfo {
1047			let weight_used =
1048				<Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1049			Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1050		}
1051
1052		/// Extoll that a particular destination can be communicated with through a particular
1053		/// version of XCM.
1054		///
1055		/// - `origin`: Must be an origin specified by AdminOrigin.
1056		/// - `location`: The destination that is being described.
1057		/// - `xcm_version`: The latest version of XCM that `location` supports.
1058		#[pallet::call_index(4)]
1059		pub fn force_xcm_version(
1060			origin: OriginFor<T>,
1061			location: Box<Location>,
1062			version: XcmVersion,
1063		) -> DispatchResult {
1064			T::AdminOrigin::ensure_origin(origin)?;
1065			let location = *location;
1066			SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1067			Self::deposit_event(Event::SupportedVersionChanged { location, version });
1068			Ok(())
1069		}
1070
1071		/// Set a safe XCM version (the version that XCM should be encoded with if the most recent
1072		/// version a destination can accept is unknown).
1073		///
1074		/// - `origin`: Must be an origin specified by AdminOrigin.
1075		/// - `maybe_xcm_version`: The default XCM encoding version, or `None` to disable.
1076		#[pallet::call_index(5)]
1077		pub fn force_default_xcm_version(
1078			origin: OriginFor<T>,
1079			maybe_xcm_version: Option<XcmVersion>,
1080		) -> DispatchResult {
1081			T::AdminOrigin::ensure_origin(origin)?;
1082			SafeXcmVersion::<T>::set(maybe_xcm_version);
1083			Ok(())
1084		}
1085
1086		/// Ask a location to notify us regarding their XCM version and any changes to it.
1087		///
1088		/// - `origin`: Must be an origin specified by AdminOrigin.
1089		/// - `location`: The location to which we should subscribe for XCM version notifications.
1090		#[pallet::call_index(6)]
1091		pub fn force_subscribe_version_notify(
1092			origin: OriginFor<T>,
1093			location: Box<VersionedLocation>,
1094		) -> DispatchResult {
1095			T::AdminOrigin::ensure_origin(origin)?;
1096			let location: Location =
1097				(*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
1098			Self::request_version_notify(location).map_err(|e| {
1099				match e {
1100					XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1101					_ => Error::<T>::InvalidOrigin,
1102				}
1103				.into()
1104			})
1105		}
1106
1107		/// Require that a particular destination should no longer notify us regarding any XCM
1108		/// version changes.
1109		///
1110		/// - `origin`: Must be an origin specified by AdminOrigin.
1111		/// - `location`: The location to which we are currently subscribed for XCM version
1112		///   notifications which we no longer desire.
1113		#[pallet::call_index(7)]
1114		pub fn force_unsubscribe_version_notify(
1115			origin: OriginFor<T>,
1116			location: Box<VersionedLocation>,
1117		) -> DispatchResult {
1118			T::AdminOrigin::ensure_origin(origin)?;
1119			let location: Location =
1120				(*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
1121			Self::unrequest_version_notify(location).map_err(|e| {
1122				match e {
1123					XcmError::InvalidLocation => Error::<T>::NoSubscription,
1124					_ => Error::<T>::InvalidOrigin,
1125				}
1126				.into()
1127			})
1128		}
1129
1130		/// Transfer some assets from the local chain to the destination chain through their local,
1131		/// destination or remote reserve.
1132		///
1133		/// `assets` must have same reserve location and may not be teleportable to `dest`.
1134		///  - `assets` have local reserve: transfer assets to sovereign account of destination
1135		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
1136		///    assets to `beneficiary`.
1137		///  - `assets` have destination reserve: burn local assets and forward a notification to
1138		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
1139		///    deposit them to `beneficiary`.
1140		///  - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move
1141		///    reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest`
1142		///    to mint and deposit reserve-based assets to `beneficiary`.
1143		///
1144		/// Fee payment on the destination side is made from the asset in the `assets` vector of
1145		/// index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight
1146		/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
1147		/// at risk.
1148		///
1149		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
1150		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
1151		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
1152		///   relay to parachain.
1153		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
1154		///   generally be an `AccountId32` value.
1155		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
1156		///   fee on the `dest` (and possibly reserve) chains.
1157		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
1158		///   fees.
1159		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
1160		#[pallet::call_index(8)]
1161		#[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
1162		pub fn limited_reserve_transfer_assets(
1163			origin: OriginFor<T>,
1164			dest: Box<VersionedLocation>,
1165			beneficiary: Box<VersionedLocation>,
1166			assets: Box<VersionedAssets>,
1167			fee_asset_item: u32,
1168			weight_limit: WeightLimit,
1169		) -> DispatchResult {
1170			Self::do_reserve_transfer_assets(
1171				origin,
1172				dest,
1173				beneficiary,
1174				assets,
1175				fee_asset_item,
1176				weight_limit,
1177			)
1178		}
1179
1180		/// Teleport some assets from the local chain to some destination chain.
1181		///
1182		/// Fee payment on the destination side is made from the asset in the `assets` vector of
1183		/// index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight
1184		/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
1185		/// at risk.
1186		///
1187		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
1188		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
1189		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
1190		///   relay to parachain.
1191		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
1192		///   generally be an `AccountId32` value.
1193		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
1194		///   fee on the `dest` chain.
1195		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
1196		///   fees.
1197		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
1198		#[pallet::call_index(9)]
1199		#[pallet::weight(T::WeightInfo::teleport_assets())]
1200		pub fn limited_teleport_assets(
1201			origin: OriginFor<T>,
1202			dest: Box<VersionedLocation>,
1203			beneficiary: Box<VersionedLocation>,
1204			assets: Box<VersionedAssets>,
1205			fee_asset_item: u32,
1206			weight_limit: WeightLimit,
1207		) -> DispatchResult {
1208			Self::do_teleport_assets(
1209				origin,
1210				dest,
1211				beneficiary,
1212				assets,
1213				fee_asset_item,
1214				weight_limit,
1215			)
1216		}
1217
1218		/// Set or unset the global suspension state of the XCM executor.
1219		///
1220		/// - `origin`: Must be an origin specified by AdminOrigin.
1221		/// - `suspended`: `true` to suspend, `false` to resume.
1222		#[pallet::call_index(10)]
1223		pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1224			T::AdminOrigin::ensure_origin(origin)?;
1225			XcmExecutionSuspended::<T>::set(suspended);
1226			Ok(())
1227		}
1228
1229		/// Transfer some assets from the local chain to the destination chain through their local,
1230		/// destination or remote reserve, or through teleports.
1231		///
1232		/// Fee payment on the destination side is made from the asset in the `assets` vector of
1233		/// index `fee_asset_item` (hence referred to as `fees`), up to enough to pay for
1234		/// `weight_limit` of weight. If more weight is needed than `weight_limit`, then the
1235		/// operation will fail and the sent assets may be at risk.
1236		///
1237		/// `assets` (excluding `fees`) must have same reserve location or otherwise be teleportable
1238		/// to `dest`, no limitations imposed on `fees`.
1239		///  - for local reserve: transfer assets to sovereign account of destination chain and
1240		///    forward a notification XCM to `dest` to mint and deposit reserve-based assets to
1241		///    `beneficiary`.
1242		///  - for destination reserve: burn local assets and forward a notification to `dest` chain
1243		///    to withdraw the reserve assets from this chain's sovereign account and deposit them
1244		///    to `beneficiary`.
1245		///  - for remote reserve: burn local assets, forward XCM to reserve chain to move reserves
1246		///    from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint
1247		///    and deposit reserve-based assets to `beneficiary`.
1248		///  - for teleports: burn local assets and forward XCM to `dest` chain to mint/teleport
1249		///    assets and deposit them to `beneficiary`.
1250		///
1251		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
1252		/// - `dest`: Destination context for the assets. Will typically be `X2(Parent,
1253		///   Parachain(..))` to send from parachain to parachain, or `X1(Parachain(..))` to send
1254		///   from relay to parachain.
1255		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
1256		///   generally be an `AccountId32` value.
1257		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
1258		///   fee on the `dest` (and possibly reserve) chains.
1259		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
1260		///   fees.
1261		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
1262		#[pallet::call_index(11)]
1263		pub fn transfer_assets(
1264			origin: OriginFor<T>,
1265			dest: Box<VersionedLocation>,
1266			beneficiary: Box<VersionedLocation>,
1267			assets: Box<VersionedAssets>,
1268			fee_asset_item: u32,
1269			weight_limit: WeightLimit,
1270		) -> DispatchResult {
1271			let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1272			let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1273			let beneficiary: Location =
1274				(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1275			let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1276			tracing::debug!(
1277				target: "xcm::pallet_xcm::transfer_assets",
1278				?origin, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1279			);
1280
1281			ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1282			let assets = assets.into_inner();
1283			let fee_asset_item = fee_asset_item as usize;
1284			// Find transfer types for fee and non-fee assets.
1285			let (fees_transfer_type, assets_transfer_type) =
1286				Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1287
1288			Self::do_transfer_assets(
1289				origin,
1290				dest,
1291				Either::Left(beneficiary),
1292				assets,
1293				assets_transfer_type,
1294				fee_asset_item,
1295				fees_transfer_type,
1296				weight_limit,
1297			)
1298		}
1299
1300		/// Claims assets trapped on this pallet because of leftover assets during XCM execution.
1301		///
1302		/// - `origin`: Anyone can call this extrinsic.
1303		/// - `assets`: The exact assets that were trapped. Use the version to specify what version
1304		/// was the latest when they were trapped.
1305		/// - `beneficiary`: The location/account where the claimed assets will be deposited.
1306		#[pallet::call_index(12)]
1307		pub fn claim_assets(
1308			origin: OriginFor<T>,
1309			assets: Box<VersionedAssets>,
1310			beneficiary: Box<VersionedLocation>,
1311		) -> DispatchResult {
1312			let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1313			tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?origin_location, ?assets, ?beneficiary);
1314			// Extract version from `assets`.
1315			let assets_version = assets.identify_version();
1316			let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1317			let number_of_assets = assets.len() as u32;
1318			let beneficiary: Location =
1319				(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1320			let ticket: Location = GeneralIndex(assets_version as u128).into();
1321			let mut message = Xcm(vec![
1322				ClaimAsset { assets, ticket },
1323				DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1324			]);
1325			let weight =
1326				T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
1327			let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
1328			let outcome = T::XcmExecutor::prepare_and_execute(
1329				origin_location,
1330				message,
1331				&mut hash,
1332				weight,
1333				weight,
1334			);
1335			outcome.ensure_complete().map_err(|error| {
1336				tracing::error!(target: "xcm::pallet_xcm::claim_assets", ?error, "XCM execution failed with error");
1337				Error::<T>::LocalExecutionIncomplete
1338			})?;
1339			Ok(())
1340		}
1341
1342		/// Transfer assets from the local chain to the destination chain using explicit transfer
1343		/// types for assets and fees.
1344		///
1345		/// `assets` must have same reserve location or may be teleportable to `dest`. Caller must
1346		/// provide the `assets_transfer_type` to be used for `assets`:
1347		///  - `TransferType::LocalReserve`: transfer assets to sovereign account of destination
1348		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
1349		///    assets to `beneficiary`.
1350		///  - `TransferType::DestinationReserve`: burn local assets and forward a notification to
1351		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
1352		///    deposit them to `beneficiary`.
1353		///  - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve`
1354		///    chain to move reserves from this chain's SA to `dest` chain's SA, and forward another
1355		///    XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically
1356		///    the remote `reserve` is Asset Hub.
1357		///  - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to
1358		///    mint/teleport assets and deposit them to `beneficiary`.
1359		///
1360		/// On the destination chain, as well as any intermediary hops, `BuyExecution` is used to
1361		/// buy execution using transferred `assets` identified by `remote_fees_id`.
1362		/// Make sure enough of the specified `remote_fees_id` asset is included in the given list
1363		/// of `assets`. `remote_fees_id` should be enough to pay for `weight_limit`. If more weight
1364		/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
1365		/// at risk.
1366		///
1367		/// `remote_fees_id` may use different transfer type than rest of `assets` and can be
1368		/// specified through `fees_transfer_type`.
1369		///
1370		/// The caller needs to specify what should happen to the transferred assets once they reach
1371		/// the `dest` chain. This is done through the `custom_xcm_on_dest` parameter, which
1372		/// contains the instructions to execute on `dest` as a final step.
1373		///   This is usually as simple as:
1374		///   `Xcm(vec![DepositAsset { assets: Wild(AllCounted(assets.len())), beneficiary }])`,
1375		///   but could be something more exotic like sending the `assets` even further.
1376		///
1377		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
1378		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
1379		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
1380		///   relay to parachain, or `(parents: 2, (GlobalConsensus(..), ..))` to send from
1381		///   parachain across a bridge to another ecosystem destination.
1382		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
1383		///   fee on the `dest` (and possibly reserve) chains.
1384		/// - `assets_transfer_type`: The XCM `TransferType` used to transfer the `assets`.
1385		/// - `remote_fees_id`: One of the included `assets` to be used to pay fees.
1386		/// - `fees_transfer_type`: The XCM `TransferType` used to transfer the `fees` assets.
1387		/// - `custom_xcm_on_dest`: The XCM to be executed on `dest` chain as the last step of the
1388		///   transfer, which also determines what happens to the assets on the destination chain.
1389		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
1390		#[pallet::call_index(13)]
1391		#[pallet::weight(T::WeightInfo::transfer_assets())]
1392		pub fn transfer_assets_using_type_and_then(
1393			origin: OriginFor<T>,
1394			dest: Box<VersionedLocation>,
1395			assets: Box<VersionedAssets>,
1396			assets_transfer_type: Box<TransferType>,
1397			remote_fees_id: Box<VersionedAssetId>,
1398			fees_transfer_type: Box<TransferType>,
1399			custom_xcm_on_dest: Box<VersionedXcm<()>>,
1400			weight_limit: WeightLimit,
1401		) -> DispatchResult {
1402			let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1403			let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1404			let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1405			let fees_id: AssetId =
1406				(*remote_fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?;
1407			let remote_xcm: Xcm<()> =
1408				(*custom_xcm_on_dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1409			tracing::debug!(
1410				target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1411				?origin_location, ?dest, ?assets, ?assets_transfer_type, ?fees_id, ?fees_transfer_type,
1412				?remote_xcm, ?weight_limit,
1413			);
1414
1415			let assets = assets.into_inner();
1416			ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1417
1418			let fee_asset_index =
1419				assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
1420			Self::do_transfer_assets(
1421				origin_location,
1422				dest,
1423				Either::Right(remote_xcm),
1424				assets,
1425				*assets_transfer_type,
1426				fee_asset_index,
1427				*fees_transfer_type,
1428				weight_limit,
1429			)
1430		}
1431	}
1432}
1433
1434/// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic.
1435const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1436
1437/// Specify how assets used for fees are handled during asset transfers.
1438#[derive(Clone, PartialEq)]
1439enum FeesHandling<T: Config> {
1440	/// `fees` asset can be batch-transferred with rest of assets using same XCM instructions.
1441	Batched { fees: Asset },
1442	/// fees cannot be batched, they are handled separately using XCM programs here.
1443	Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1444}
1445
1446impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1447	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1448		match self {
1449			Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1450			Self::Separate { local_xcm, remote_xcm } => write!(
1451				f,
1452				"FeesHandling::Separate(local: {:?}, remote: {:?})",
1453				local_xcm, remote_xcm
1454			),
1455		}
1456	}
1457}
1458
1459impl<T: Config> QueryHandler for Pallet<T> {
1460	type BlockNumber = BlockNumberFor<T>;
1461	type Error = XcmError;
1462	type UniversalLocation = T::UniversalLocation;
1463
1464	/// Attempt to create a new query ID and register it as a query that is yet to respond.
1465	fn new_query(
1466		responder: impl Into<Location>,
1467		timeout: BlockNumberFor<T>,
1468		match_querier: impl Into<Location>,
1469	) -> QueryId {
1470		Self::do_new_query(responder, None, timeout, match_querier)
1471	}
1472
1473	/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
1474	/// value.
1475	fn report_outcome(
1476		message: &mut Xcm<()>,
1477		responder: impl Into<Location>,
1478		timeout: Self::BlockNumber,
1479	) -> Result<QueryId, Self::Error> {
1480		let responder = responder.into();
1481		let destination = Self::UniversalLocation::get()
1482			.invert_target(&responder)
1483			.map_err(|()| XcmError::LocationNotInvertible)?;
1484		let query_id = Self::new_query(responder, timeout, Here);
1485		let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1486		let report_error = Xcm(vec![ReportError(response_info)]);
1487		message.0.insert(0, SetAppendix(report_error));
1488		Ok(query_id)
1489	}
1490
1491	/// Removes response when ready and emits [Event::ResponseTaken] event.
1492	fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1493		match Queries::<T>::get(query_id) {
1494			Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1495				Ok(response) => {
1496					Queries::<T>::remove(query_id);
1497					Self::deposit_event(Event::ResponseTaken { query_id });
1498					QueryResponseStatus::Ready { response, at }
1499				},
1500				Err(_) => QueryResponseStatus::UnexpectedVersion,
1501			},
1502			Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
1503			Some(_) => QueryResponseStatus::UnexpectedVersion,
1504			None => QueryResponseStatus::NotFound,
1505		}
1506	}
1507
1508	#[cfg(feature = "runtime-benchmarks")]
1509	fn expect_response(id: QueryId, response: Response) {
1510		let response = response.into();
1511		Queries::<T>::insert(
1512			id,
1513			QueryStatus::Ready { response, at: frame_system::Pallet::<T>::block_number() },
1514		);
1515	}
1516}
1517
1518impl<T: Config> Pallet<T> {
1519	/// Find `TransferType`s for `assets` and fee identified through `fee_asset_item`, when
1520	/// transferring to `dest`.
1521	///
1522	/// Validate `assets` to all have same `TransferType`.
1523	fn find_fee_and_assets_transfer_types(
1524		assets: &[Asset],
1525		fee_asset_item: usize,
1526		dest: &Location,
1527	) -> Result<(TransferType, TransferType), Error<T>> {
1528		let mut fees_transfer_type = None;
1529		let mut assets_transfer_type = None;
1530		for (idx, asset) in assets.iter().enumerate() {
1531			if let Fungible(x) = asset.fun {
1532				// If fungible asset, ensure non-zero amount.
1533				ensure!(!x.is_zero(), Error::<T>::Empty);
1534			}
1535			let transfer_type =
1536				T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
1537			if idx == fee_asset_item {
1538				fees_transfer_type = Some(transfer_type);
1539			} else {
1540				if let Some(existing) = assets_transfer_type.as_ref() {
1541					// Ensure transfer for multiple assets uses same transfer type (only fee may
1542					// have different transfer type/path)
1543					ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
1544				} else {
1545					// asset reserve identified
1546					assets_transfer_type = Some(transfer_type);
1547				}
1548			}
1549		}
1550		// single asset also marked as fee item
1551		if assets.len() == 1 {
1552			assets_transfer_type = fees_transfer_type.clone()
1553		}
1554		Ok((
1555			fees_transfer_type.ok_or(Error::<T>::Empty)?,
1556			assets_transfer_type.ok_or(Error::<T>::Empty)?,
1557		))
1558	}
1559
1560	fn do_reserve_transfer_assets(
1561		origin: OriginFor<T>,
1562		dest: Box<VersionedLocation>,
1563		beneficiary: Box<VersionedLocation>,
1564		assets: Box<VersionedAssets>,
1565		fee_asset_item: u32,
1566		weight_limit: WeightLimit,
1567	) -> DispatchResult {
1568		let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1569		let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1570		let beneficiary: Location =
1571			(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1572		let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1573		tracing::debug!(
1574			target: "xcm::pallet_xcm::do_reserve_transfer_assets",
1575			?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item,
1576		);
1577
1578		ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1579		let value = (origin_location, assets.into_inner());
1580		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1581		let (origin, assets) = value;
1582
1583		let fee_asset_item = fee_asset_item as usize;
1584		let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1585
1586		// Find transfer types for fee and non-fee assets.
1587		let (fees_transfer_type, assets_transfer_type) =
1588			Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1589		// Ensure assets (and fees according to check below) are not teleportable to `dest`.
1590		ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
1591		// Ensure all assets (including fees) have same reserve location.
1592		ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
1593
1594		let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1595			origin.clone(),
1596			dest.clone(),
1597			Either::Left(beneficiary),
1598			assets,
1599			assets_transfer_type,
1600			FeesHandling::Batched { fees },
1601			weight_limit,
1602		)?;
1603		Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1604	}
1605
1606	fn do_teleport_assets(
1607		origin: OriginFor<T>,
1608		dest: Box<VersionedLocation>,
1609		beneficiary: Box<VersionedLocation>,
1610		assets: Box<VersionedAssets>,
1611		fee_asset_item: u32,
1612		weight_limit: WeightLimit,
1613	) -> DispatchResult {
1614		let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1615		let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1616		let beneficiary: Location =
1617			(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1618		let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1619		tracing::debug!(
1620			target: "xcm::pallet_xcm::do_teleport_assets",
1621			?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1622		);
1623
1624		ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1625		let value = (origin_location, assets.into_inner());
1626		ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
1627		let (origin_location, assets) = value;
1628		for asset in assets.iter() {
1629			let transfer_type =
1630				T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
1631			ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
1632		}
1633		let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1634
1635		let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1636			origin_location.clone(),
1637			dest.clone(),
1638			Either::Left(beneficiary),
1639			assets,
1640			TransferType::Teleport,
1641			FeesHandling::Batched { fees },
1642			weight_limit,
1643		)?;
1644		Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
1645	}
1646
1647	fn do_transfer_assets(
1648		origin: Location,
1649		dest: Location,
1650		beneficiary: Either<Location, Xcm<()>>,
1651		mut assets: Vec<Asset>,
1652		assets_transfer_type: TransferType,
1653		fee_asset_index: usize,
1654		fees_transfer_type: TransferType,
1655		weight_limit: WeightLimit,
1656	) -> DispatchResult {
1657		// local and remote XCM programs to potentially handle fees separately
1658		let fees = if fees_transfer_type == assets_transfer_type {
1659			let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
1660			// no need for custom fees instructions, fees are batched with assets
1661			FeesHandling::Batched { fees }
1662		} else {
1663			// Disallow _remote reserves_ unless assets & fees have same remote reserve (covered
1664			// by branch above). The reason for this is that we'd need to send XCMs to separate
1665			// chains with no guarantee of delivery order on final destination; therefore we
1666			// cannot guarantee to have fees in place on final destination chain to pay for
1667			// assets transfer.
1668			ensure!(
1669				!matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
1670				Error::<T>::InvalidAssetUnsupportedReserve
1671			);
1672			let weight_limit = weight_limit.clone();
1673			// remove `fees` from `assets` and build separate fees transfer instructions to be
1674			// added to assets transfers XCM programs
1675			let fees = assets.remove(fee_asset_index);
1676			let (local_xcm, remote_xcm) = match fees_transfer_type {
1677				TransferType::LocalReserve => Self::local_reserve_fees_instructions(
1678					origin.clone(),
1679					dest.clone(),
1680					fees,
1681					weight_limit,
1682				)?,
1683				TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
1684					origin.clone(),
1685					dest.clone(),
1686					fees,
1687					weight_limit,
1688				)?,
1689				TransferType::Teleport => Self::teleport_fees_instructions(
1690					origin.clone(),
1691					dest.clone(),
1692					fees,
1693					weight_limit,
1694				)?,
1695				TransferType::RemoteReserve(_) =>
1696					return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
1697			};
1698			FeesHandling::Separate { local_xcm, remote_xcm }
1699		};
1700
1701		let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1702			origin.clone(),
1703			dest.clone(),
1704			beneficiary,
1705			assets,
1706			assets_transfer_type,
1707			fees,
1708			weight_limit,
1709		)?;
1710		Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1711	}
1712
1713	fn build_xcm_transfer_type(
1714		origin: Location,
1715		dest: Location,
1716		beneficiary: Either<Location, Xcm<()>>,
1717		assets: Vec<Asset>,
1718		transfer_type: TransferType,
1719		fees: FeesHandling<T>,
1720		weight_limit: WeightLimit,
1721	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
1722		tracing::debug!(
1723			target: "xcm::pallet_xcm::build_xcm_transfer_type",
1724			?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
1725		);
1726		match transfer_type {
1727			TransferType::LocalReserve => Self::local_reserve_transfer_programs(
1728				origin.clone(),
1729				dest.clone(),
1730				beneficiary,
1731				assets,
1732				fees,
1733				weight_limit,
1734			)
1735			.map(|(local, remote)| (local, Some(remote))),
1736			TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
1737				origin.clone(),
1738				dest.clone(),
1739				beneficiary,
1740				assets,
1741				fees,
1742				weight_limit,
1743			)
1744			.map(|(local, remote)| (local, Some(remote))),
1745			TransferType::RemoteReserve(reserve) => {
1746				let fees = match fees {
1747					FeesHandling::Batched { fees } => fees,
1748					_ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
1749				};
1750				Self::remote_reserve_transfer_program(
1751					origin.clone(),
1752					reserve.try_into().map_err(|()| Error::<T>::BadVersion)?,
1753					beneficiary,
1754					dest.clone(),
1755					assets,
1756					fees,
1757					weight_limit,
1758				)
1759				.map(|local| (local, None))
1760			},
1761			TransferType::Teleport => Self::teleport_assets_program(
1762				origin.clone(),
1763				dest.clone(),
1764				beneficiary,
1765				assets,
1766				fees,
1767				weight_limit,
1768			)
1769			.map(|(local, remote)| (local, Some(remote))),
1770		}
1771	}
1772
1773	fn execute_xcm_transfer(
1774		origin: Location,
1775		dest: Location,
1776		mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
1777		remote_xcm: Option<Xcm<()>>,
1778	) -> DispatchResult {
1779		tracing::debug!(
1780			target: "xcm::pallet_xcm::execute_xcm_transfer",
1781			?origin, ?dest, ?local_xcm, ?remote_xcm,
1782		);
1783
1784		let weight =
1785			T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?;
1786		let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
1787		let outcome = T::XcmExecutor::prepare_and_execute(
1788			origin.clone(),
1789			local_xcm,
1790			&mut hash,
1791			weight,
1792			weight,
1793		);
1794		Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
1795		outcome.clone().ensure_complete().map_err(|error| {
1796			tracing::error!(
1797				target: "xcm::pallet_xcm::execute_xcm_transfer",
1798				?error, "XCM execution failed with error with outcome: {:?}", outcome
1799			);
1800			Error::<T>::LocalExecutionIncomplete
1801		})?;
1802
1803		if let Some(remote_xcm) = remote_xcm {
1804			let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
1805				.map_err(|error| {
1806					tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
1807					Error::<T>::from(error)
1808				})?;
1809			if origin != Here.into_location() {
1810				Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
1811					tracing::error!(
1812						target: "xcm::pallet_xcm::execute_xcm_transfer",
1813						?error, ?price, ?origin, "Unable to charge fee",
1814					);
1815					Error::<T>::FeesNotMet
1816				})?;
1817			}
1818			let message_id = T::XcmRouter::deliver(ticket)
1819				.map_err(|error| {
1820					tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
1821					Error::<T>::from(error)
1822				})?;
1823
1824			let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
1825			Self::deposit_event(e);
1826		}
1827		Ok(())
1828	}
1829
1830	fn add_fees_to_xcm(
1831		dest: Location,
1832		fees: FeesHandling<T>,
1833		weight_limit: WeightLimit,
1834		local: &mut Xcm<<T as Config>::RuntimeCall>,
1835		remote: &mut Xcm<()>,
1836	) -> Result<(), Error<T>> {
1837		match fees {
1838			FeesHandling::Batched { fees } => {
1839				let context = T::UniversalLocation::get();
1840				// no custom fees instructions, they are batched together with `assets` transfer;
1841				// BuyExecution happens after receiving all `assets`
1842				let reanchored_fees =
1843					fees.reanchored(&dest, &context).map_err(|e| {
1844						tracing::error!(target: "xcm::pallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
1845						Error::<T>::CannotReanchor
1846					})?;
1847				// buy execution using `fees` batched together with above `reanchored_assets`
1848				remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
1849			},
1850			FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
1851				// fees are handled by separate XCM instructions, prepend fees instructions (for
1852				// remote XCM they have to be prepended instead of appended to pass barriers).
1853				core::mem::swap(local, &mut local_fees);
1854				core::mem::swap(remote, &mut remote_fees);
1855				// these are now swapped so fees actually go first
1856				local.inner_mut().append(&mut local_fees.into_inner());
1857				remote.inner_mut().append(&mut remote_fees.into_inner());
1858			},
1859		}
1860		Ok(())
1861	}
1862
1863	fn local_reserve_fees_instructions(
1864		origin: Location,
1865		dest: Location,
1866		fees: Asset,
1867		weight_limit: WeightLimit,
1868	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1869		let value = (origin, vec![fees.clone()]);
1870		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1871
1872		let context = T::UniversalLocation::get();
1873		let reanchored_fees = fees
1874			.clone()
1875			.reanchored(&dest, &context)
1876			.map_err(|_| Error::<T>::CannotReanchor)?;
1877
1878		let local_execute_xcm = Xcm(vec![
1879			// move `fees` to `dest`s local sovereign account
1880			TransferAsset { assets: fees.into(), beneficiary: dest },
1881		]);
1882		let xcm_on_dest = Xcm(vec![
1883			// let (dest) chain know `fees` are in its SA on reserve
1884			ReserveAssetDeposited(reanchored_fees.clone().into()),
1885			// buy exec using `fees` in holding deposited in above instruction
1886			BuyExecution { fees: reanchored_fees, weight_limit },
1887		]);
1888		Ok((local_execute_xcm, xcm_on_dest))
1889	}
1890
1891	fn local_reserve_transfer_programs(
1892		origin: Location,
1893		dest: Location,
1894		beneficiary: Either<Location, Xcm<()>>,
1895		assets: Vec<Asset>,
1896		fees: FeesHandling<T>,
1897		weight_limit: WeightLimit,
1898	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1899		let value = (origin, assets);
1900		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1901		let (_, assets) = value;
1902
1903		// max assets is `assets` (+ potentially separately handled fee)
1904		let max_assets =
1905			assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
1906		let assets: Assets = assets.into();
1907		let context = T::UniversalLocation::get();
1908		let mut reanchored_assets = assets.clone();
1909		reanchored_assets
1910			.reanchor(&dest, &context)
1911			.map_err(|e| {
1912				tracing::error!(target: "xcm::pallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
1913				Error::<T>::CannotReanchor
1914			})?;
1915
1916		// XCM instructions to be executed on local chain
1917		let mut local_execute_xcm = Xcm(vec![
1918			// locally move `assets` to `dest`s local sovereign account
1919			TransferAsset { assets, beneficiary: dest.clone() },
1920		]);
1921		// XCM instructions to be executed on destination chain
1922		let mut xcm_on_dest = Xcm(vec![
1923			// let (dest) chain know assets are in its SA on reserve
1924			ReserveAssetDeposited(reanchored_assets),
1925			// following instructions are not exec'ed on behalf of origin chain anymore
1926			ClearOrigin,
1927		]);
1928		// handle fees
1929		Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
1930
1931		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
1932		let custom_remote_xcm = match beneficiary {
1933			Either::Right(custom_xcm) => custom_xcm,
1934			Either::Left(beneficiary) => {
1935				// deposit all remaining assets in holding to `beneficiary` location
1936				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
1937			},
1938		};
1939		xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
1940
1941		Ok((local_execute_xcm, xcm_on_dest))
1942	}
1943
1944	fn destination_reserve_fees_instructions(
1945		origin: Location,
1946		dest: Location,
1947		fees: Asset,
1948		weight_limit: WeightLimit,
1949	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1950		let value = (origin, vec![fees.clone()]);
1951		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1952		ensure!(
1953			<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
1954			Error::<T>::InvalidAssetUnsupportedReserve
1955		);
1956
1957		let context = T::UniversalLocation::get();
1958		let reanchored_fees = fees
1959			.clone()
1960			.reanchored(&dest, &context)
1961			.map_err(|e| {
1962				tracing::error!(target: "xcm::pallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
1963				Error::<T>::CannotReanchor
1964			})?;
1965		let fees: Assets = fees.into();
1966
1967		let local_execute_xcm = Xcm(vec![
1968			// withdraw reserve-based fees (derivatives)
1969			WithdrawAsset(fees.clone()),
1970			// burn derivatives
1971			BurnAsset(fees),
1972		]);
1973		let xcm_on_dest = Xcm(vec![
1974			// withdraw `fees` from origin chain's sovereign account
1975			WithdrawAsset(reanchored_fees.clone().into()),
1976			// buy exec using `fees` in holding withdrawn in above instruction
1977			BuyExecution { fees: reanchored_fees, weight_limit },
1978		]);
1979		Ok((local_execute_xcm, xcm_on_dest))
1980	}
1981
1982	fn destination_reserve_transfer_programs(
1983		origin: Location,
1984		dest: Location,
1985		beneficiary: Either<Location, Xcm<()>>,
1986		assets: Vec<Asset>,
1987		fees: FeesHandling<T>,
1988		weight_limit: WeightLimit,
1989	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1990		let value = (origin, assets);
1991		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1992		let (_, assets) = value;
1993		for asset in assets.iter() {
1994			ensure!(
1995				<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
1996				Error::<T>::InvalidAssetUnsupportedReserve
1997			);
1998		}
1999
2000		// max assets is `assets` (+ potentially separately handled fee)
2001		let max_assets =
2002			assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2003		let assets: Assets = assets.into();
2004		let context = T::UniversalLocation::get();
2005		let mut reanchored_assets = assets.clone();
2006		reanchored_assets
2007			.reanchor(&dest, &context)
2008			.map_err(|e| {
2009				tracing::error!(target: "xcm::pallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2010				Error::<T>::CannotReanchor
2011			})?;
2012
2013		// XCM instructions to be executed on local chain
2014		let mut local_execute_xcm = Xcm(vec![
2015			// withdraw reserve-based assets
2016			WithdrawAsset(assets.clone()),
2017			// burn reserve-based assets
2018			BurnAsset(assets),
2019		]);
2020		// XCM instructions to be executed on destination chain
2021		let mut xcm_on_dest = Xcm(vec![
2022			// withdraw `assets` from origin chain's sovereign account
2023			WithdrawAsset(reanchored_assets),
2024			// following instructions are not exec'ed on behalf of origin chain anymore
2025			ClearOrigin,
2026		]);
2027		// handle fees
2028		Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2029
2030		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
2031		let custom_remote_xcm = match beneficiary {
2032			Either::Right(custom_xcm) => custom_xcm,
2033			Either::Left(beneficiary) => {
2034				// deposit all remaining assets in holding to `beneficiary` location
2035				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2036			},
2037		};
2038		xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2039
2040		Ok((local_execute_xcm, xcm_on_dest))
2041	}
2042
2043	// function assumes fees and assets have the same remote reserve
2044	fn remote_reserve_transfer_program(
2045		origin: Location,
2046		reserve: Location,
2047		beneficiary: Either<Location, Xcm<()>>,
2048		dest: Location,
2049		assets: Vec<Asset>,
2050		fees: Asset,
2051		weight_limit: WeightLimit,
2052	) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2053		let value = (origin, assets);
2054		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2055		let (_, assets) = value;
2056
2057		let max_assets = assets.len() as u32;
2058		let context = T::UniversalLocation::get();
2059		// we spend up to half of fees for execution on reserve and other half for execution on
2060		// destination
2061		let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2062		// identifies fee item as seen by `reserve` - to be used at reserve chain
2063		let reserve_fees = fees_half_1
2064			.reanchored(&reserve, &context)
2065			.map_err(|e| {
2066				tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2067				Error::<T>::CannotReanchor
2068			})?;
2069		// identifies fee item as seen by `dest` - to be used at destination chain
2070		let dest_fees = fees_half_2
2071			.reanchored(&dest, &context)
2072			.map_err(|e| {
2073				tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2074				Error::<T>::CannotReanchor
2075			})?;
2076		// identifies `dest` as seen by `reserve`
2077		let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2078			tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2079			Error::<T>::CannotReanchor
2080		})?;
2081		// xcm to be executed at dest
2082		let mut xcm_on_dest =
2083			Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2084		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
2085		let custom_xcm_on_dest = match beneficiary {
2086			Either::Right(custom_xcm) => custom_xcm,
2087			Either::Left(beneficiary) => {
2088				// deposit all remaining assets in holding to `beneficiary` location
2089				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2090			},
2091		};
2092		xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2093		// xcm to be executed on reserve
2094		let xcm_on_reserve = Xcm(vec![
2095			BuyExecution { fees: reserve_fees, weight_limit },
2096			DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2097		]);
2098		Ok(Xcm(vec![
2099			WithdrawAsset(assets.into()),
2100			SetFeesMode { jit_withdraw: true },
2101			InitiateReserveWithdraw {
2102				assets: Wild(AllCounted(max_assets)),
2103				reserve,
2104				xcm: xcm_on_reserve,
2105			},
2106		]))
2107	}
2108
2109	fn teleport_fees_instructions(
2110		origin: Location,
2111		dest: Location,
2112		fees: Asset,
2113		weight_limit: WeightLimit,
2114	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2115		let value = (origin, vec![fees.clone()]);
2116		ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2117		ensure!(
2118			<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2119			Error::<T>::Filtered
2120		);
2121
2122		let context = T::UniversalLocation::get();
2123		let reanchored_fees = fees
2124			.clone()
2125			.reanchored(&dest, &context)
2126			.map_err(|e| {
2127				tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2128				Error::<T>::CannotReanchor
2129			})?;
2130
2131		// XcmContext irrelevant in teleports checks
2132		let dummy_context =
2133			XcmContext { origin: None, message_id: Default::default(), topic: None };
2134		// We should check that the asset can actually be teleported out (for this to
2135		// be in error, there would need to be an accounting violation by ourselves,
2136		// so it's unlikely, but we don't want to allow that kind of bug to leak into
2137		// a trusted chain.
2138		<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2139			&dest,
2140			&fees,
2141			&dummy_context,
2142		)
2143		.map_err(|e| {
2144			tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2145			Error::<T>::CannotCheckOutTeleport
2146		})?;
2147		// safe to do this here, we're in a transactional call that will be reverted on any
2148		// errors down the line
2149		<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2150			&dest,
2151			&fees,
2152			&dummy_context,
2153		);
2154
2155		let fees: Assets = fees.into();
2156		let local_execute_xcm = Xcm(vec![
2157			// withdraw fees
2158			WithdrawAsset(fees.clone()),
2159			// burn fees
2160			BurnAsset(fees),
2161		]);
2162		let xcm_on_dest = Xcm(vec![
2163			// (dest) chain receive teleported assets burned on origin chain
2164			ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2165			// buy exec using `fees` in holding received in above instruction
2166			BuyExecution { fees: reanchored_fees, weight_limit },
2167		]);
2168		Ok((local_execute_xcm, xcm_on_dest))
2169	}
2170
2171	fn teleport_assets_program(
2172		origin: Location,
2173		dest: Location,
2174		beneficiary: Either<Location, Xcm<()>>,
2175		assets: Vec<Asset>,
2176		fees: FeesHandling<T>,
2177		weight_limit: WeightLimit,
2178	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2179		let value = (origin, assets);
2180		ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2181		let (_, assets) = value;
2182		for asset in assets.iter() {
2183			ensure!(
2184				<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2185				Error::<T>::Filtered
2186			);
2187		}
2188
2189		// max assets is `assets` (+ potentially separately handled fee)
2190		let max_assets =
2191			assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2192		let context = T::UniversalLocation::get();
2193		let assets: Assets = assets.into();
2194		let mut reanchored_assets = assets.clone();
2195		reanchored_assets
2196			.reanchor(&dest, &context)
2197			.map_err(|e| {
2198				tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2199				Error::<T>::CannotReanchor
2200			})?;
2201
2202		// XcmContext irrelevant in teleports checks
2203		let dummy_context =
2204			XcmContext { origin: None, message_id: Default::default(), topic: None };
2205		for asset in assets.inner() {
2206			// We should check that the asset can actually be teleported out (for this to
2207			// be in error, there would need to be an accounting violation by ourselves,
2208			// so it's unlikely, but we don't want to allow that kind of bug to leak into
2209			// a trusted chain.
2210			<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2211				&dest,
2212				asset,
2213				&dummy_context,
2214			)
2215			.map_err(|e| {
2216				tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2217				Error::<T>::CannotCheckOutTeleport
2218			})?;
2219		}
2220		for asset in assets.inner() {
2221			// safe to do this here, we're in a transactional call that will be reverted on any
2222			// errors down the line
2223			<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2224				&dest,
2225				asset,
2226				&dummy_context,
2227			);
2228		}
2229
2230		// XCM instructions to be executed on local chain
2231		let mut local_execute_xcm = Xcm(vec![
2232			// withdraw assets to be teleported
2233			WithdrawAsset(assets.clone()),
2234			// burn assets on local chain
2235			BurnAsset(assets),
2236		]);
2237		// XCM instructions to be executed on destination chain
2238		let mut xcm_on_dest = Xcm(vec![
2239			// teleport `assets` in from origin chain
2240			ReceiveTeleportedAsset(reanchored_assets),
2241			// following instructions are not exec'ed on behalf of origin chain anymore
2242			ClearOrigin,
2243		]);
2244		// handle fees
2245		Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2246
2247		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
2248		let custom_remote_xcm = match beneficiary {
2249			Either::Right(custom_xcm) => custom_xcm,
2250			Either::Left(beneficiary) => {
2251				// deposit all remaining assets in holding to `beneficiary` location
2252				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2253			},
2254		};
2255		xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2256
2257		Ok((local_execute_xcm, xcm_on_dest))
2258	}
2259
2260	/// Halve `fees` fungible amount.
2261	pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2262		match fees.fun {
2263			Fungible(amount) => {
2264				let fee1 = amount.saturating_div(2);
2265				let fee2 = amount.saturating_sub(fee1);
2266				ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2267				ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2268				Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2269			},
2270			NonFungible(_) => Err(Error::<T>::FeesNotMet),
2271		}
2272	}
2273
2274	/// Will always make progress, and will do its best not to use much more than `weight_cutoff`
2275	/// in doing so.
2276	pub(crate) fn check_xcm_version_change(
2277		mut stage: VersionMigrationStage,
2278		weight_cutoff: Weight,
2279	) -> (Weight, Option<VersionMigrationStage>) {
2280		let mut weight_used = Weight::zero();
2281
2282		let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2283		let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2284		let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2285		let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2286		let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2287		let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2288		let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2289
2290		use VersionMigrationStage::*;
2291
2292		if stage == MigrateSupportedVersion {
2293			// We assume that supported XCM version only ever increases, so just cycle through lower
2294			// XCM versioned from the current.
2295			for v in 0..XCM_VERSION {
2296				for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2297					if let Ok(new_key) = old_key.into_latest() {
2298						SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2299					}
2300					weight_used.saturating_accrue(sv_migrate_weight);
2301					if weight_used.any_gte(weight_cutoff) {
2302						return (weight_used, Some(stage))
2303					}
2304				}
2305			}
2306			stage = MigrateVersionNotifiers;
2307		}
2308		if stage == MigrateVersionNotifiers {
2309			for v in 0..XCM_VERSION {
2310				for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2311					if let Ok(new_key) = old_key.into_latest() {
2312						VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2313					}
2314					weight_used.saturating_accrue(vn_migrate_weight);
2315					if weight_used.any_gte(weight_cutoff) {
2316						return (weight_used, Some(stage))
2317					}
2318				}
2319			}
2320			stage = NotifyCurrentTargets(None);
2321		}
2322
2323		let xcm_version = T::AdvertisedXcmVersion::get();
2324
2325		if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2326			let mut iter = match maybe_last_raw_key {
2327				Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2328				None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2329			};
2330			while let Some((key, value)) = iter.next() {
2331				let (query_id, max_weight, target_xcm_version) = value;
2332				let new_key: Location = match key.clone().try_into() {
2333					Ok(k) if target_xcm_version != xcm_version => k,
2334					_ => {
2335						// We don't early return here since we need to be certain that we
2336						// make some progress.
2337						weight_used.saturating_accrue(vnt_already_notified_weight);
2338						continue
2339					},
2340				};
2341				let response = Response::Version(xcm_version);
2342				let message =
2343					Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2344				let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2345					Ok((message_id, cost)) => {
2346						let value = (query_id, max_weight, xcm_version);
2347						VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2348						Event::VersionChangeNotified {
2349							destination: new_key,
2350							result: xcm_version,
2351							cost,
2352							message_id,
2353						}
2354					},
2355					Err(e) => {
2356						VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2357						Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2358					},
2359				};
2360				Self::deposit_event(event);
2361				weight_used.saturating_accrue(vnt_notify_weight);
2362				if weight_used.any_gte(weight_cutoff) {
2363					let last = Some(iter.last_raw_key().into());
2364					return (weight_used, Some(NotifyCurrentTargets(last)))
2365				}
2366			}
2367			stage = MigrateAndNotifyOldTargets;
2368		}
2369		if stage == MigrateAndNotifyOldTargets {
2370			for v in 0..XCM_VERSION {
2371				for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2372					let (query_id, max_weight, target_xcm_version) = value;
2373					let new_key = match Location::try_from(old_key.clone()) {
2374						Ok(k) => k,
2375						Err(()) => {
2376							Self::deposit_event(Event::NotifyTargetMigrationFail {
2377								location: old_key,
2378								query_id: value.0,
2379							});
2380							weight_used.saturating_accrue(vnt_migrate_fail_weight);
2381							if weight_used.any_gte(weight_cutoff) {
2382								return (weight_used, Some(stage))
2383							}
2384							continue
2385						},
2386					};
2387
2388					let versioned_key = LatestVersionedLocation(&new_key);
2389					if target_xcm_version == xcm_version {
2390						VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2391						weight_used.saturating_accrue(vnt_migrate_weight);
2392					} else {
2393						// Need to notify target.
2394						let response = Response::Version(xcm_version);
2395						let message = Xcm(vec![QueryResponse {
2396							query_id,
2397							response,
2398							max_weight,
2399							querier: None,
2400						}]);
2401						let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2402							Ok((message_id, cost)) => {
2403								VersionNotifyTargets::<T>::insert(
2404									XCM_VERSION,
2405									versioned_key,
2406									(query_id, max_weight, xcm_version),
2407								);
2408								Event::VersionChangeNotified {
2409									destination: new_key,
2410									result: xcm_version,
2411									cost,
2412									message_id,
2413								}
2414							},
2415							Err(e) => Event::NotifyTargetSendFail {
2416								location: new_key,
2417								query_id,
2418								error: e.into(),
2419							},
2420						};
2421						Self::deposit_event(event);
2422						weight_used.saturating_accrue(vnt_notify_migrate_weight);
2423					}
2424					if weight_used.any_gte(weight_cutoff) {
2425						return (weight_used, Some(stage))
2426					}
2427				}
2428			}
2429		}
2430		(weight_used, None)
2431	}
2432
2433	/// Request that `dest` informs us of its version.
2434	pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2435		let dest = dest.into();
2436		let versioned_dest = VersionedLocation::from(dest.clone());
2437		let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
2438		ensure!(!already, XcmError::InvalidLocation);
2439		let query_id = QueryCounter::<T>::mutate(|q| {
2440			let r = *q;
2441			q.saturating_inc();
2442			r
2443		});
2444		// TODO #3735: Correct weight.
2445		let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
2446		let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
2447		Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
2448		VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
2449		let query_status =
2450			QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
2451		Queries::<T>::insert(query_id, query_status);
2452		Ok(())
2453	}
2454
2455	/// Request that `dest` ceases informing us of its version.
2456	pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
2457		let dest = dest.into();
2458		let versioned_dest = LatestVersionedLocation(&dest);
2459		let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
2460			.ok_or(XcmError::InvalidLocation)?;
2461		let (message_id, cost) =
2462			send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
2463		Self::deposit_event(Event::VersionNotifyUnrequested {
2464			destination: dest,
2465			cost,
2466			message_id,
2467		});
2468		Queries::<T>::remove(query_id);
2469		Ok(())
2470	}
2471
2472	/// Relay an XCM `message` from a given `interior` location in this context to a given `dest`
2473	/// location. The `fee_payer` is charged for the delivery unless `None` in which case fees
2474	/// are not charged (and instead borne by the chain).
2475	pub fn send_xcm(
2476		interior: impl Into<Junctions>,
2477		dest: impl Into<Location>,
2478		mut message: Xcm<()>,
2479	) -> Result<XcmHash, SendError> {
2480		let interior = interior.into();
2481		let dest = dest.into();
2482		let maybe_fee_payer = if interior != Junctions::Here {
2483			message.0.insert(0, DescendOrigin(interior.clone()));
2484			Some(interior.into())
2485		} else {
2486			None
2487		};
2488		tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
2489		let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
2490		if let Some(fee_payer) = maybe_fee_payer {
2491			Self::charge_fees(fee_payer, price).map_err(|e| {
2492				tracing::error!(
2493					target: "xcm::pallet_xcm::send_xcm",
2494					?e,
2495					"Charging fees failed with error",
2496				);
2497				SendError::Fees
2498			})?;
2499		}
2500		T::XcmRouter::deliver(ticket)
2501	}
2502
2503	pub fn check_account() -> T::AccountId {
2504		const ID: PalletId = PalletId(*b"py/xcmch");
2505		AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
2506	}
2507
2508	/// Dry-runs `call` with the given `origin`.
2509	///
2510	/// Returns not only the call result and events, but also the local XCM, if any,
2511	/// and any XCMs forwarded to other locations.
2512	/// Meant to be used in the `xcm_runtime_apis::dry_run::DryRunApi` runtime API.
2513	pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
2514		origin: OriginCaller,
2515		call: RuntimeCall,
2516		result_xcms_version: XcmVersion,
2517	) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2518	where
2519		Runtime: crate::Config,
2520		Router: InspectMessageQueues,
2521		RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
2522		<RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
2523	{
2524		crate::Pallet::<Runtime>::set_record_xcm(true);
2525		// Clear other messages in queues...
2526		Router::clear_messages();
2527		// ...and reset events to make sure we only record events from current call.
2528		frame_system::Pallet::<Runtime>::reset_events();
2529		let result = call.dispatch(origin.into());
2530		crate::Pallet::<Runtime>::set_record_xcm(false);
2531		let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
2532			.map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
2533			.transpose()
2534			.map_err(|()| {
2535				tracing::error!(
2536					target: "xcm::DryRunApi::dry_run_call",
2537					"Local xcm version conversion failed"
2538				);
2539
2540				XcmDryRunApiError::VersionedConversionFailed
2541			})?;
2542
2543		// Should only get messages from this call since we cleared previous ones.
2544		let forwarded_xcms =
2545			Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
2546				|error| {
2547					tracing::error!(
2548						target: "xcm::DryRunApi::dry_run_call",
2549						?error, "Forwarded xcms version conversion failed with error"
2550					);
2551				},
2552			)?;
2553		let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2554			frame_system::Pallet::<Runtime>::read_events_no_consensus()
2555				.map(|record| record.event.clone())
2556				.collect();
2557		Ok(CallDryRunEffects {
2558			local_xcm: local_xcm.map(VersionedXcm::<()>::from),
2559			forwarded_xcms,
2560			emitted_events: events,
2561			execution_result: result,
2562		})
2563	}
2564
2565	/// Dry-runs `xcm` with the given `origin_location`.
2566	///
2567	/// Returns execution result, events, and any forwarded XCMs to other locations.
2568	/// Meant to be used in the `xcm_runtime_apis::dry_run::DryRunApi` runtime API.
2569	pub fn dry_run_xcm<Runtime, Router, RuntimeCall: Decode + GetDispatchInfo, XcmConfig>(
2570		origin_location: VersionedLocation,
2571		xcm: VersionedXcm<RuntimeCall>,
2572	) -> Result<XcmDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2573	where
2574		Runtime: frame_system::Config,
2575		Router: InspectMessageQueues,
2576		XcmConfig: xcm_executor::Config<RuntimeCall = RuntimeCall>,
2577	{
2578		let origin_location: Location = origin_location.try_into().map_err(|error| {
2579			tracing::error!(
2580				target: "xcm::DryRunApi::dry_run_xcm",
2581				?error, "Location version conversion failed with error"
2582			);
2583			XcmDryRunApiError::VersionedConversionFailed
2584		})?;
2585		let xcm_version = xcm.identify_version();
2586		let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
2587			tracing::error!(
2588				target: "xcm::DryRunApi::dry_run_xcm",
2589				?error, "Xcm version conversion failed with error"
2590			);
2591			XcmDryRunApiError::VersionedConversionFailed
2592		})?;
2593		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
2594
2595		// To make sure we only record events from current call.
2596		Router::clear_messages();
2597		frame_system::Pallet::<Runtime>::reset_events();
2598
2599		let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
2600			origin_location,
2601			xcm,
2602			&mut hash,
2603			Weight::MAX, // Max limit available for execution.
2604			Weight::zero(),
2605		);
2606		let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
2607			.inspect_err(|error| {
2608				tracing::error!(
2609					target: "xcm::DryRunApi::dry_run_xcm",
2610					?error, "Forwarded xcms version conversion failed with error"
2611				);
2612			})?;
2613		let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2614			frame_system::Pallet::<Runtime>::read_events_no_consensus()
2615				.map(|record| record.event.clone())
2616				.collect();
2617		Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
2618	}
2619
2620	fn convert_xcms(
2621		xcm_version: XcmVersion,
2622		xcms: Vec<VersionedXcm<()>>,
2623	) -> Result<Vec<VersionedXcm<()>>, ()> {
2624		xcms.into_iter()
2625			.map(|xcm| xcm.into_version(xcm_version))
2626			.collect::<Result<Vec<_>, ()>>()
2627	}
2628
2629	fn convert_forwarded_xcms(
2630		xcm_version: XcmVersion,
2631		forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
2632	) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
2633		forwarded_xcms
2634			.into_iter()
2635			.map(|(dest, forwarded_xcms)| {
2636				let dest = dest.into_version(xcm_version)?;
2637				let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
2638
2639				Ok((dest, forwarded_xcms))
2640			})
2641			.collect::<Result<Vec<_>, ()>>()
2642			.map_err(|()| XcmDryRunApiError::VersionedConversionFailed)
2643	}
2644
2645	/// Given a list of asset ids, returns the correct API response for
2646	/// `XcmPaymentApi::query_acceptable_payment_assets`.
2647	///
2648	/// The assets passed in have to be supported for fee payment.
2649	pub fn query_acceptable_payment_assets(
2650		version: xcm::Version,
2651		asset_ids: Vec<AssetId>,
2652	) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
2653		Ok(asset_ids
2654			.into_iter()
2655			.map(|asset_id| VersionedAssetId::from(asset_id))
2656			.filter_map(|asset_id| asset_id.into_version(version).ok())
2657			.collect())
2658	}
2659
2660	pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
2661		let message = Xcm::<()>::try_from(message.clone())
2662			.map_err(|e| {
2663				tracing::error!(target: "xcm::pallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
2664				XcmPaymentApiError::VersionedConversionFailed
2665			})?;
2666
2667		T::Weigher::weight(&mut message.clone().into()).map_err(|()| {
2668			tracing::error!(target: "xcm::pallet_xcm::query_xcm_weight", ?message, "Error when querying XCM weight");
2669			XcmPaymentApiError::WeightNotComputable
2670		})
2671	}
2672
2673	/// Given an Asset and a Location, returns if the provided location is a trusted reserve for the
2674	/// given asset.
2675	pub fn is_trusted_reserve(
2676		asset: VersionedAsset,
2677		location: VersionedLocation,
2678	) -> Result<bool, TrustedQueryApiError> {
2679		let location: Location = location.try_into().map_err(|e| {
2680			tracing::debug!(
2681				target: "xcm::pallet_xcm::is_trusted_reserve",
2682				"Asset version conversion failed with error: {:?}",
2683				e,
2684			);
2685			TrustedQueryApiError::VersionedLocationConversionFailed
2686		})?;
2687
2688		let a: Asset = asset.try_into().map_err(|e| {
2689			tracing::debug!(
2690				target: "xcm::pallet_xcm::is_trusted_reserve",
2691				"Location version conversion failed with error: {:?}",
2692				e,
2693			);
2694			TrustedQueryApiError::VersionedAssetConversionFailed
2695		})?;
2696
2697		Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
2698	}
2699
2700	/// Given an Asset and a Location, returns if the asset can be teleported to provided location.
2701	pub fn is_trusted_teleporter(
2702		asset: VersionedAsset,
2703		location: VersionedLocation,
2704	) -> Result<bool, TrustedQueryApiError> {
2705		let location: Location = location.try_into().map_err(|e| {
2706			tracing::debug!(
2707				target: "xcm::pallet_xcm::is_trusted_teleporter",
2708				"Asset version conversion failed with error: {:?}",
2709				e,
2710			);
2711			TrustedQueryApiError::VersionedLocationConversionFailed
2712		})?;
2713		let a: Asset = asset.try_into().map_err(|e| {
2714			tracing::debug!(
2715				target: "xcm::pallet_xcm::is_trusted_teleporter",
2716				"Location version conversion failed with error: {:?}",
2717				e,
2718			);
2719			TrustedQueryApiError::VersionedAssetConversionFailed
2720		})?;
2721		Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
2722	}
2723
2724	pub fn query_delivery_fees(
2725		destination: VersionedLocation,
2726		message: VersionedXcm<()>,
2727	) -> Result<VersionedAssets, XcmPaymentApiError> {
2728		let result_version = destination.identify_version().max(message.identify_version());
2729
2730		let destination: Location = destination
2731			.clone()
2732			.try_into()
2733			.map_err(|e| {
2734				tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
2735				XcmPaymentApiError::VersionedConversionFailed
2736			})?;
2737
2738		let message: Xcm<()> =
2739			message.clone().try_into().map_err(|e| {
2740				tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
2741				XcmPaymentApiError::VersionedConversionFailed
2742			})?;
2743
2744		let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
2745			tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
2746			XcmPaymentApiError::Unroutable
2747		})?;
2748
2749		VersionedAssets::from(fees)
2750			.into_version(result_version)
2751			.map_err(|e| {
2752				tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?result_version, "Failed to convert fees into version");
2753				XcmPaymentApiError::VersionedConversionFailed
2754			})
2755	}
2756
2757	/// Create a new expectation of a query response with the querier being here.
2758	fn do_new_query(
2759		responder: impl Into<Location>,
2760		maybe_notify: Option<(u8, u8)>,
2761		timeout: BlockNumberFor<T>,
2762		match_querier: impl Into<Location>,
2763	) -> u64 {
2764		QueryCounter::<T>::mutate(|q| {
2765			let r = *q;
2766			q.saturating_inc();
2767			Queries::<T>::insert(
2768				r,
2769				QueryStatus::Pending {
2770					responder: responder.into().into(),
2771					maybe_match_querier: Some(match_querier.into().into()),
2772					maybe_notify,
2773					timeout,
2774				},
2775			);
2776			r
2777		})
2778	}
2779
2780	/// Consume `message` and return another which is equivalent to it except that it reports
2781	/// back the outcome and dispatches `notify` on this chain.
2782	///
2783	/// - `message`: The message whose outcome should be reported.
2784	/// - `responder`: The origin from which a response should be expected.
2785	/// - `notify`: A dispatchable function which will be called once the outcome of `message` is
2786	///   known. It may be a dispatchable in any pallet of the local chain, but other than the usual
2787	///   origin, it must accept exactly two arguments: `query_id: QueryId` and `outcome: Response`,
2788	///   and in that order. It should expect that the origin is `Origin::Response` and will contain
2789	///   the responder's location.
2790	/// - `timeout`: The block number after which it is permissible for `notify` not to be called
2791	///   even if a response is received.
2792	///
2793	/// `report_outcome_notify` may return an error if the `responder` is not invertible.
2794	///
2795	/// It is assumed that the querier of the response will be `Here`.
2796	///
2797	/// NOTE: `notify` gets called as part of handling an incoming message, so it should be
2798	/// lightweight. Its weight is estimated during this function and stored ready for
2799	/// weighing `ReportOutcome` on the way back. If it turns out to be heavier once it returns
2800	/// then reporting the outcome will fail. Futhermore if the estimate is too high, then it
2801	/// may be put in the overweight queue and need to be manually executed.
2802	pub fn report_outcome_notify(
2803		message: &mut Xcm<()>,
2804		responder: impl Into<Location>,
2805		notify: impl Into<<T as Config>::RuntimeCall>,
2806		timeout: BlockNumberFor<T>,
2807	) -> Result<(), XcmError> {
2808		let responder = responder.into();
2809		let destination = T::UniversalLocation::get()
2810			.invert_target(&responder)
2811			.map_err(|()| XcmError::LocationNotInvertible)?;
2812		let notify: <T as Config>::RuntimeCall = notify.into();
2813		let max_weight = notify.get_dispatch_info().call_weight;
2814		let query_id = Self::new_notify_query(responder, notify, timeout, Here);
2815		let response_info = QueryResponseInfo { destination, query_id, max_weight };
2816		let report_error = Xcm(vec![ReportError(response_info)]);
2817		message.0.insert(0, SetAppendix(report_error));
2818		Ok(())
2819	}
2820
2821	/// Attempt to create a new query ID and register it as a query that is yet to respond, and
2822	/// which will call a dispatchable when a response happens.
2823	pub fn new_notify_query(
2824		responder: impl Into<Location>,
2825		notify: impl Into<<T as Config>::RuntimeCall>,
2826		timeout: BlockNumberFor<T>,
2827		match_querier: impl Into<Location>,
2828	) -> u64 {
2829		let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
2830			"decode input is output of Call encode; Call guaranteed to have two enums; qed",
2831		);
2832		Self::do_new_query(responder, Some(notify), timeout, match_querier)
2833	}
2834
2835	/// Note that a particular destination to whom we would like to send a message is unknown
2836	/// and queue it for version discovery.
2837	fn note_unknown_version(dest: &Location) {
2838		tracing::trace!(
2839			target: "xcm::pallet_xcm::note_unknown_version",
2840			?dest, "XCM version is unknown for destination"
2841		);
2842		let versioned_dest = VersionedLocation::from(dest.clone());
2843		VersionDiscoveryQueue::<T>::mutate(|q| {
2844			if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
2845				// exists - just bump the count.
2846				q[index].1.saturating_inc();
2847			} else {
2848				let _ = q.try_push((versioned_dest, 1));
2849			}
2850		});
2851	}
2852
2853	/// Withdraw given `assets` from the given `location` and pay as XCM fees.
2854	///
2855	/// Fails if:
2856	/// - the `assets` are not known on this chain;
2857	/// - the `assets` cannot be withdrawn with that location as the Origin.
2858	fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
2859		T::XcmExecutor::charge_fees(location.clone(), assets.clone())
2860			.map_err(|_| Error::<T>::FeesNotMet)?;
2861		Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
2862		Ok(())
2863	}
2864
2865	/// Ensure the correctness of the state of this pallet.
2866	///
2867	/// This should be valid before and after each state transition of this pallet.
2868	///
2869	/// ## Invariants
2870	///
2871	/// All entries stored in the `SupportedVersion` / `VersionNotifiers` / `VersionNotifyTargets`
2872	/// need to be migrated to the `XCM_VERSION`. If they are not, then `CurrentMigration` has to be
2873	/// set.
2874	#[cfg(any(feature = "try-runtime", test))]
2875	pub fn do_try_state() -> Result<(), TryRuntimeError> {
2876		use migration::data::NeedsMigration;
2877
2878		// Take the minimum version between `SafeXcmVersion` and `latest - 1` and ensure that the
2879		// operational data is stored at least at that version, for example, to prevent issues when
2880		// removing older XCM versions.
2881		let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
2882		{
2883			XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
2884		} else {
2885			XCM_VERSION.saturating_sub(1)
2886		};
2887
2888		// check `Queries`
2889		ensure!(
2890			!Queries::<T>::iter_values()
2891				.any(|data| data.needs_migration(minimal_allowed_xcm_version)),
2892			TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
2893		);
2894
2895		// check `LockedFungibles`
2896		ensure!(
2897			!LockedFungibles::<T>::iter_values()
2898				.any(|data| data.needs_migration(minimal_allowed_xcm_version)),
2899			TryRuntimeError::Other(
2900				"`LockedFungibles` data should be migrated to the higher xcm version!"
2901			)
2902		);
2903
2904		// check `RemoteLockedFungibles`
2905		ensure!(
2906			!RemoteLockedFungibles::<T>::iter()
2907				.any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
2908					data.needs_migration(minimal_allowed_xcm_version)),
2909			TryRuntimeError::Other(
2910				"`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
2911			)
2912		);
2913
2914		// if migration has been already scheduled, everything is ok and data will be eventually
2915		// migrated
2916		if CurrentMigration::<T>::exists() {
2917			return Ok(())
2918		}
2919
2920		// if migration has NOT been scheduled yet, we need to check all operational data
2921		for v in 0..XCM_VERSION {
2922			ensure!(
2923				SupportedVersion::<T>::iter_prefix(v).next().is_none(),
2924				TryRuntimeError::Other(
2925					"`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
2926				)
2927			);
2928			ensure!(
2929				VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
2930				TryRuntimeError::Other(
2931					"`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
2932				)
2933			);
2934			ensure!(
2935				VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
2936				TryRuntimeError::Other(
2937					"`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
2938				)
2939			);
2940		}
2941
2942		Ok(())
2943	}
2944}
2945
2946pub struct LockTicket<T: Config> {
2947	sovereign_account: T::AccountId,
2948	amount: BalanceOf<T>,
2949	unlocker: Location,
2950	item_index: Option<usize>,
2951}
2952
2953impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
2954	fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
2955		use xcm_executor::traits::LockError::UnexpectedState;
2956		let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
2957		match self.item_index {
2958			Some(index) => {
2959				ensure!(locks.len() > index, UnexpectedState);
2960				ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
2961				locks[index].0 = locks[index].0.max(self.amount);
2962			},
2963			None => {
2964				locks
2965					.try_push((self.amount, self.unlocker.into()))
2966					.map_err(|(_balance, _location)| UnexpectedState)?;
2967			},
2968		}
2969		LockedFungibles::<T>::insert(&self.sovereign_account, locks);
2970		T::Currency::extend_lock(
2971			*b"py/xcmlk",
2972			&self.sovereign_account,
2973			self.amount,
2974			WithdrawReasons::all(),
2975		);
2976		Ok(())
2977	}
2978}
2979
2980pub struct UnlockTicket<T: Config> {
2981	sovereign_account: T::AccountId,
2982	amount: BalanceOf<T>,
2983	unlocker: Location,
2984}
2985
2986impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
2987	fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
2988		use xcm_executor::traits::LockError::UnexpectedState;
2989		let mut locks =
2990			LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
2991		let mut maybe_remove_index = None;
2992		let mut locked = BalanceOf::<T>::zero();
2993		let mut found = false;
2994		// We could just as well do with an into_iter, filter_map and collect, however this way
2995		// avoids making an allocation.
2996		for (i, x) in locks.iter_mut().enumerate() {
2997			if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
2998				x.0 = x.0.saturating_sub(self.amount);
2999				if x.0.is_zero() {
3000					maybe_remove_index = Some(i);
3001				}
3002				found = true;
3003			}
3004			locked = locked.max(x.0);
3005		}
3006		ensure!(found, UnexpectedState);
3007		if let Some(remove_index) = maybe_remove_index {
3008			locks.swap_remove(remove_index);
3009		}
3010		LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3011		let reasons = WithdrawReasons::all();
3012		T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3013		Ok(())
3014	}
3015}
3016
3017pub struct ReduceTicket<T: Config> {
3018	key: (u32, T::AccountId, VersionedAssetId),
3019	amount: u128,
3020	locker: VersionedLocation,
3021	owner: VersionedLocation,
3022}
3023
3024impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3025	fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3026		use xcm_executor::traits::LockError::UnexpectedState;
3027		let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3028		ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3029		let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3030		ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3031		if new_amount == 0 {
3032			RemoteLockedFungibles::<T>::remove(&self.key);
3033		} else {
3034			record.amount = new_amount;
3035			RemoteLockedFungibles::<T>::insert(&self.key, &record);
3036		}
3037		Ok(())
3038	}
3039}
3040
3041impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
3042	type LockTicket = LockTicket<T>;
3043	type UnlockTicket = UnlockTicket<T>;
3044	type ReduceTicket = ReduceTicket<T>;
3045
3046	fn prepare_lock(
3047		unlocker: Location,
3048		asset: Asset,
3049		owner: Location,
3050	) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3051		use xcm_executor::traits::LockError::*;
3052		let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3053		let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3054		ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3055		let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3056		let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3057		ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3058		Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3059	}
3060
3061	fn prepare_unlock(
3062		unlocker: Location,
3063		asset: Asset,
3064		owner: Location,
3065	) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3066		use xcm_executor::traits::LockError::*;
3067		let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3068		let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3069		ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3070		let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3071		let item_index =
3072			locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3073		ensure!(locks[item_index].0 >= amount, NotLocked);
3074		Ok(UnlockTicket { sovereign_account, amount, unlocker })
3075	}
3076
3077	fn note_unlockable(
3078		locker: Location,
3079		asset: Asset,
3080		mut owner: Location,
3081	) -> Result<(), xcm_executor::traits::LockError> {
3082		use xcm_executor::traits::LockError::*;
3083		ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3084		let amount = match asset.fun {
3085			Fungible(a) => a,
3086			NonFungible(_) => return Err(Unimplemented),
3087		};
3088		owner.remove_network_id();
3089		let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3090		let locker = locker.into();
3091		let owner = owner.into();
3092		let id: VersionedAssetId = asset.id.into();
3093		let key = (XCM_VERSION, account, id);
3094		let mut record =
3095			RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3096		if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3097			// Make sure that the new record wouldn't clobber any old data.
3098			ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3099			record.consumers = old.consumers;
3100			record.amount = record.amount.max(old.amount);
3101		}
3102		RemoteLockedFungibles::<T>::insert(&key, record);
3103		Ok(())
3104	}
3105
3106	fn prepare_reduce_unlockable(
3107		locker: Location,
3108		asset: Asset,
3109		mut owner: Location,
3110	) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3111		use xcm_executor::traits::LockError::*;
3112		let amount = match asset.fun {
3113			Fungible(a) => a,
3114			NonFungible(_) => return Err(Unimplemented),
3115		};
3116		owner.remove_network_id();
3117		let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3118		let locker = locker.into();
3119		let owner = owner.into();
3120		let id: VersionedAssetId = asset.id.into();
3121		let key = (XCM_VERSION, sovereign_account, id);
3122
3123		let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3124		// Make sure that the record contains what we expect and there's enough to unlock.
3125		ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3126		ensure!(record.amount >= amount, NotEnoughLocked);
3127		ensure!(
3128			record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3129			InUse
3130		);
3131		Ok(ReduceTicket { key, amount, locker, owner })
3132	}
3133}
3134
3135impl<T: Config> WrapVersion for Pallet<T> {
3136	fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3137		dest: &Location,
3138		xcm: impl Into<VersionedXcm<RuntimeCall>>,
3139	) -> Result<VersionedXcm<RuntimeCall>, ()> {
3140		Self::get_version_for(dest)
3141			.or_else(|| {
3142				Self::note_unknown_version(dest);
3143				SafeXcmVersion::<T>::get()
3144			})
3145			.ok_or_else(|| {
3146				tracing::trace!(
3147					target: "xcm::pallet_xcm::wrap_version",
3148					?dest, "Could not determine a version to wrap XCM for destination",
3149				);
3150				()
3151			})
3152			.and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3153	}
3154}
3155
3156impl<T: Config> GetVersion for Pallet<T> {
3157	fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3158		SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3159	}
3160}
3161
3162impl<T: Config> VersionChangeNotifier for Pallet<T> {
3163	/// Start notifying `location` should the XCM version of this chain change.
3164	///
3165	/// When it does, this type should ensure a `QueryResponse` message is sent with the given
3166	/// `query_id` & `max_weight` and with a `response` of `Response::Version`. This should happen
3167	/// until/unless `stop` is called with the correct `query_id`.
3168	///
3169	/// If the `location` has an ongoing notification and when this function is called, then an
3170	/// error should be returned.
3171	fn start(
3172		dest: &Location,
3173		query_id: QueryId,
3174		max_weight: Weight,
3175		_context: &XcmContext,
3176	) -> XcmResult {
3177		let versioned_dest = LatestVersionedLocation(dest);
3178		let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3179		ensure!(!already, XcmError::InvalidLocation);
3180
3181		let xcm_version = T::AdvertisedXcmVersion::get();
3182		let response = Response::Version(xcm_version);
3183		let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3184		let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3185		Self::deposit_event(Event::<T>::VersionNotifyStarted {
3186			destination: dest.clone(),
3187			cost,
3188			message_id,
3189		});
3190
3191		let value = (query_id, max_weight, xcm_version);
3192		VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3193		Ok(())
3194	}
3195
3196	/// Stop notifying `location` should the XCM change. This is a no-op if there was never a
3197	/// subscription.
3198	fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3199		VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3200		Ok(())
3201	}
3202
3203	/// Return true if a location is subscribed to XCM version changes.
3204	fn is_subscribed(dest: &Location) -> bool {
3205		let versioned_dest = LatestVersionedLocation(dest);
3206		VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3207	}
3208}
3209
3210impl<T: Config> DropAssets for Pallet<T> {
3211	fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
3212		if assets.is_empty() {
3213			return Weight::zero()
3214		}
3215		let versioned = VersionedAssets::from(Assets::from(assets));
3216		let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3217		AssetTraps::<T>::mutate(hash, |n| *n += 1);
3218		Self::deposit_event(Event::AssetsTrapped {
3219			hash,
3220			origin: origin.clone(),
3221			assets: versioned,
3222		});
3223		// TODO #3735: Put the real weight in there.
3224		Weight::zero()
3225	}
3226}
3227
3228impl<T: Config> ClaimAssets for Pallet<T> {
3229	fn claim_assets(
3230		origin: &Location,
3231		ticket: &Location,
3232		assets: &Assets,
3233		_context: &XcmContext,
3234	) -> bool {
3235		let mut versioned = VersionedAssets::from(assets.clone());
3236		match ticket.unpack() {
3237			(0, [GeneralIndex(i)]) =>
3238				versioned = match versioned.into_version(*i as u32) {
3239					Ok(v) => v,
3240					Err(()) => return false,
3241				},
3242			(0, []) => (),
3243			_ => return false,
3244		};
3245		let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3246		match AssetTraps::<T>::get(hash) {
3247			0 => return false,
3248			1 => AssetTraps::<T>::remove(hash),
3249			n => AssetTraps::<T>::insert(hash, n - 1),
3250		}
3251		Self::deposit_event(Event::AssetsClaimed {
3252			hash,
3253			origin: origin.clone(),
3254			assets: versioned,
3255		});
3256		return true
3257	}
3258}
3259
3260impl<T: Config> OnResponse for Pallet<T> {
3261	fn expecting_response(
3262		origin: &Location,
3263		query_id: QueryId,
3264		querier: Option<&Location>,
3265	) -> bool {
3266		match Queries::<T>::get(query_id) {
3267			Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) =>
3268				Location::try_from(responder).map_or(false, |r| origin == &r) &&
3269					maybe_match_querier.map_or(true, |match_querier| {
3270						Location::try_from(match_querier).map_or(false, |match_querier| {
3271							querier.map_or(false, |q| q == &match_querier)
3272						})
3273					}),
3274			Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
3275				Location::try_from(r).map_or(false, |r| origin == &r),
3276			_ => false,
3277		}
3278	}
3279
3280	fn on_response(
3281		origin: &Location,
3282		query_id: QueryId,
3283		querier: Option<&Location>,
3284		response: Response,
3285		max_weight: Weight,
3286		_context: &XcmContext,
3287	) -> Weight {
3288		let origin = origin.clone();
3289		match (response, Queries::<T>::get(query_id)) {
3290			(
3291				Response::Version(v),
3292				Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
3293			) => {
3294				let origin: Location = match expected_origin.try_into() {
3295					Ok(o) if o == origin => o,
3296					Ok(o) => {
3297						Self::deposit_event(Event::InvalidResponder {
3298							origin: origin.clone(),
3299							query_id,
3300							expected_location: Some(o),
3301						});
3302						return Weight::zero()
3303					},
3304					_ => {
3305						Self::deposit_event(Event::InvalidResponder {
3306							origin: origin.clone(),
3307							query_id,
3308							expected_location: None,
3309						});
3310						// TODO #3735: Correct weight for this.
3311						return Weight::zero()
3312					},
3313				};
3314				// TODO #3735: Check max_weight is correct.
3315				if !is_active {
3316					Queries::<T>::insert(
3317						query_id,
3318						QueryStatus::VersionNotifier {
3319							origin: origin.clone().into(),
3320							is_active: true,
3321						},
3322					);
3323				}
3324				// We're being notified of a version change.
3325				SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
3326				Self::deposit_event(Event::SupportedVersionChanged {
3327					location: origin,
3328					version: v,
3329				});
3330				Weight::zero()
3331			},
3332			(
3333				response,
3334				Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
3335			) => {
3336				if let Some(match_querier) = maybe_match_querier {
3337					let match_querier = match Location::try_from(match_querier) {
3338						Ok(mq) => mq,
3339						Err(_) => {
3340							Self::deposit_event(Event::InvalidQuerierVersion {
3341								origin: origin.clone(),
3342								query_id,
3343							});
3344							return Weight::zero()
3345						},
3346					};
3347					if querier.map_or(true, |q| q != &match_querier) {
3348						Self::deposit_event(Event::InvalidQuerier {
3349							origin: origin.clone(),
3350							query_id,
3351							expected_querier: match_querier,
3352							maybe_actual_querier: querier.cloned(),
3353						});
3354						return Weight::zero()
3355					}
3356				}
3357				let responder = match Location::try_from(responder) {
3358					Ok(r) => r,
3359					Err(_) => {
3360						Self::deposit_event(Event::InvalidResponderVersion {
3361							origin: origin.clone(),
3362							query_id,
3363						});
3364						return Weight::zero()
3365					},
3366				};
3367				if origin != responder {
3368					Self::deposit_event(Event::InvalidResponder {
3369						origin: origin.clone(),
3370						query_id,
3371						expected_location: Some(responder),
3372					});
3373					return Weight::zero()
3374				}
3375				match maybe_notify {
3376					Some((pallet_index, call_index)) => {
3377						// This is a bit horrible, but we happen to know that the `Call` will
3378						// be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`.
3379						// So we just encode that and then re-encode to a real Call.
3380						let bare = (pallet_index, call_index, query_id, response);
3381						if let Ok(call) = bare.using_encoded(|mut bytes| {
3382							<T as Config>::RuntimeCall::decode(&mut bytes)
3383						}) {
3384							Queries::<T>::remove(query_id);
3385							let weight = call.get_dispatch_info().call_weight;
3386							if weight.any_gt(max_weight) {
3387								let e = Event::NotifyOverweight {
3388									query_id,
3389									pallet_index,
3390									call_index,
3391									actual_weight: weight,
3392									max_budgeted_weight: max_weight,
3393								};
3394								Self::deposit_event(e);
3395								return Weight::zero()
3396							}
3397							let dispatch_origin = Origin::Response(origin.clone()).into();
3398							match call.dispatch(dispatch_origin) {
3399								Ok(post_info) => {
3400									let e = Event::Notified { query_id, pallet_index, call_index };
3401									Self::deposit_event(e);
3402									post_info.actual_weight
3403								},
3404								Err(error_and_info) => {
3405									let e = Event::NotifyDispatchError {
3406										query_id,
3407										pallet_index,
3408										call_index,
3409									};
3410									Self::deposit_event(e);
3411									// Not much to do with the result as it is. It's up to the
3412									// parachain to ensure that the message makes sense.
3413									error_and_info.post_info.actual_weight
3414								},
3415							}
3416							.unwrap_or(weight)
3417						} else {
3418							let e =
3419								Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
3420							Self::deposit_event(e);
3421							Weight::zero()
3422						}
3423					},
3424					None => {
3425						let e = Event::ResponseReady { query_id, response: response.clone() };
3426						Self::deposit_event(e);
3427						let at = frame_system::Pallet::<T>::current_block_number();
3428						let response = response.into();
3429						Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
3430						Weight::zero()
3431					},
3432				}
3433			},
3434			_ => {
3435				let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
3436				Self::deposit_event(e);
3437				Weight::zero()
3438			},
3439		}
3440	}
3441}
3442
3443impl<T: Config> CheckSuspension for Pallet<T> {
3444	fn is_suspended<Call>(
3445		_origin: &Location,
3446		_instructions: &mut [Instruction<Call>],
3447		_max_weight: Weight,
3448		_properties: &mut Properties,
3449	) -> bool {
3450		XcmExecutionSuspended::<T>::get()
3451	}
3452}
3453
3454impl<T: Config> RecordXcm for Pallet<T> {
3455	fn should_record() -> bool {
3456		ShouldRecordXcm::<T>::get()
3457	}
3458
3459	fn set_record_xcm(enabled: bool) {
3460		ShouldRecordXcm::<T>::put(enabled);
3461	}
3462
3463	fn recorded_xcm() -> Option<Xcm<()>> {
3464		RecordedXcm::<T>::get()
3465	}
3466
3467	fn record(xcm: Xcm<()>) {
3468		RecordedXcm::<T>::put(xcm);
3469	}
3470}
3471
3472/// Ensure that the origin `o` represents an XCM (`Transact`) origin.
3473///
3474/// Returns `Ok` with the location of the XCM sender or an `Err` otherwise.
3475pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3476where
3477	OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3478{
3479	match o.into() {
3480		Ok(Origin::Xcm(location)) => Ok(location),
3481		_ => Err(BadOrigin),
3482	}
3483}
3484
3485/// Ensure that the origin `o` represents an XCM response origin.
3486///
3487/// Returns `Ok` with the location of the responder or an `Err` otherwise.
3488pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3489where
3490	OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3491{
3492	match o.into() {
3493		Ok(Origin::Response(location)) => Ok(location),
3494		_ => Err(BadOrigin),
3495	}
3496}
3497
3498/// Filter for `Location` to find those which represent a strict majority approval of an
3499/// identified plurality.
3500///
3501/// May reasonably be used with `EnsureXcm`.
3502pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3503impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
3504	for IsMajorityOfBody<Prefix, Body>
3505{
3506	fn contains(l: &Location) -> bool {
3507		let maybe_suffix = l.match_and_split(&Prefix::get());
3508		matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
3509	}
3510}
3511
3512/// Filter for `Location` to find those which represent a voice of an identified plurality.
3513///
3514/// May reasonably be used with `EnsureXcm`.
3515pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3516impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
3517	fn contains(l: &Location) -> bool {
3518		let maybe_suffix = l.match_and_split(&Prefix::get());
3519		matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
3520	}
3521}
3522
3523/// `EnsureOrigin` implementation succeeding with a `Location` value to recognize and filter
3524/// the `Origin::Xcm` item.
3525pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
3526impl<
3527		O: OriginTrait + From<Origin>,
3528		F: Contains<L>,
3529		L: TryFrom<Location> + TryInto<Location> + Clone,
3530	> EnsureOrigin<O> for EnsureXcm<F, L>
3531where
3532	O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
3533{
3534	type Success = L;
3535
3536	fn try_origin(outer: O) -> Result<Self::Success, O> {
3537		outer.try_with_caller(|caller| {
3538			caller.try_into().and_then(|o| match o {
3539				Origin::Xcm(ref location)
3540					if F::contains(&location.clone().try_into().map_err(|_| o.clone().into())?) =>
3541					Ok(location.clone().try_into().map_err(|_| o.clone().into())?),
3542				Origin::Xcm(location) => Err(Origin::Xcm(location).into()),
3543				o => Err(o.into()),
3544			})
3545		})
3546	}
3547
3548	#[cfg(feature = "runtime-benchmarks")]
3549	fn try_successful_origin() -> Result<O, ()> {
3550		Ok(O::from(Origin::Xcm(Here.into())))
3551	}
3552}
3553
3554/// `EnsureOrigin` implementation succeeding with a `Location` value to recognize and filter
3555/// the `Origin::Response` item.
3556pub struct EnsureResponse<F>(PhantomData<F>);
3557impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
3558where
3559	O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
3560{
3561	type Success = Location;
3562
3563	fn try_origin(outer: O) -> Result<Self::Success, O> {
3564		outer.try_with_caller(|caller| {
3565			caller.try_into().and_then(|o| match o {
3566				Origin::Response(responder) => Ok(responder),
3567				o => Err(o.into()),
3568			})
3569		})
3570	}
3571
3572	#[cfg(feature = "runtime-benchmarks")]
3573	fn try_successful_origin() -> Result<O, ()> {
3574		Ok(O::from(Origin::Response(Here.into())))
3575	}
3576}
3577
3578/// A simple passthrough where we reuse the `Location`-typed XCM origin as the inner value of
3579/// this crate's `Origin::Xcm` value.
3580pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
3581impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
3582	for XcmPassthrough<RuntimeOrigin>
3583{
3584	fn convert_origin(
3585		origin: impl Into<Location>,
3586		kind: OriginKind,
3587	) -> Result<RuntimeOrigin, Location> {
3588		let origin = origin.into();
3589		match kind {
3590			OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
3591			_ => Err(origin),
3592		}
3593	}
3594}