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