pallet_xcm/
lib.rs

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