pallet_xcm/
lib.rs

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