referrerpolicy=no-referrer-when-downgrade

pallet_xcm/
lib.rs

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