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