Skip to main content

pallet_xcm/
lib.rs

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