Skip to main content

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