referrerpolicy=no-referrer-when-downgrade

pallet_xcm/
lib.rs

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