referrerpolicy=no-referrer-when-downgrade

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