pallet_xcm/
lib.rs

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