pallet_xcm/
lib.rs

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