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