pallet_xcm/
lib.rs

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