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")]
22
pub mod benchmarking;
23
#[cfg(test)]
24
mod mock;
25
#[cfg(test)]
26
mod tests;
27

            
28
pub mod migration;
29

            
30
extern crate alloc;
31

            
32
use alloc::{boxed::Box, vec, vec::Vec};
33
use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
34
use core::{marker::PhantomData, result::Result};
35
use 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
};
46
use frame_system::pallet_prelude::{BlockNumberFor, *};
47
pub use pallet::*;
48
use scale_info::TypeInfo;
49
use sp_runtime::{
50
	traits::{
51
		AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
52
		Saturating, Zero,
53
	},
54
	Either, RuntimeDebug,
55
};
56
use xcm::{latest::QueryResponseInfo, prelude::*};
57
use xcm_builder::{
58
	ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
59
	QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
60
};
61
use 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
};
70
use 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))]
76
use sp_runtime::TryRuntimeError;
77

            
78
pub 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
102
pub struct TestWeightInfo;
103
impl 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
27133
#[frame_support::pallet]
186
pub 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
1698
	#[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
19887
		fn execute() -> Weight {
299
19887
			T::WeightInfo::execute()
300
19887
		}
301
	}
302

            
303
	impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
304
		type WeightInfo = Self;
305
22287
		fn execute(
306
22287
			origin: OriginFor<T>,
307
22287
			message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
308
22287
			max_weight: Weight,
309
22287
		) -> Result<Weight, DispatchErrorWithPostInfo> {
310
22287
			log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
311
22287
			let outcome = (|| {
312
22287
				let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
313
22287
				let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
314
22287
				let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
315
20322
				let value = (origin_location, message);
316
20322
				ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
317
20322
				let (origin_location, message) = value;
318
20322
				Ok(T::XcmExecutor::prepare_and_execute(
319
20322
					origin_location,
320
20322
					message,
321
20322
					&mut hash,
322
20322
					max_weight,
323
20322
					max_weight,
324
20322
				))
325
22287
			})()
326
22287
			.map_err(|e: DispatchError| {
327
1965
				e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
328
22287
			})?;
329

            
330
20322
			Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
331
20322
			let weight_used = outcome.weight_used();
332
20322
			outcome.ensure_complete().map_err(|error| {
333
17922
				log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
334
17922
				Error::<T>::LocalExecutionIncomplete.with_weight(
335
17922
					weight_used.saturating_add(
336
17922
						<Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
337
17922
					),
338
17922
				)
339
20322
			})?;
340
2400
			Ok(weight_used)
341
22287
		}
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
2667
		fn send(
353
2667
			origin: OriginFor<T>,
354
2667
			dest: Box<VersionedLocation>,
355
2667
			message: Box<VersionedXcm<()>>,
356
2667
		) -> Result<XcmHash, DispatchError> {
357
2667
			let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
358
2667
			let interior: Junctions =
359
2667
				origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
360
2667
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
361
2667
			let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
362

            
363
2646
			let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
364
2646
				.map_err(Error::<T>::from)?;
365
987
			let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
366
987
			Self::deposit_event(e);
367
987
			Ok(message_id)
368
2667
		}
369
	}
370

            
371
	impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
372
		fn query() -> Weight {
373
			T::WeightInfo::new_query()
374
		}
375
		fn take_response() -> Weight {
376
			T::WeightInfo::take_response()
377
		}
378
	}
379

            
380
	impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
381
		type WeightInfo = Self;
382

            
383
		fn query(
384
			origin: OriginFor<T>,
385
			timeout: BlockNumberFor<T>,
386
			match_querier: VersionedLocation,
387
		) -> Result<QueryId, DispatchError> {
388
			let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
389
			let query_id = <Self as QueryHandler>::new_query(
390
				responder,
391
				timeout,
392
				Location::try_from(match_querier)
393
					.map_err(|_| Into::<DispatchError>::into(Error::<T>::BadVersion))?,
394
			);
395

            
396
			Ok(query_id)
397
		}
398
	}
399

            
400
	#[pallet::event]
401
23757
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
402
	pub enum Event<T: Config> {
403
		/// Execution of an XCM message was attempted.
404
		Attempted { outcome: xcm::latest::Outcome },
405
		/// A XCM message was sent.
406
		Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
407
		/// Query response received which does not match a registered query. This may be because a
408
		/// matching query was never registered, it may be because it is a duplicate response, or
409
		/// because the query timed out.
410
		UnexpectedResponse { origin: Location, query_id: QueryId },
411
		/// Query response has been received and is ready for taking with `take_response`. There is
412
		/// no registered notification call.
413
		ResponseReady { query_id: QueryId, response: Response },
414
		/// Query response has been received and query is removed. The registered notification has
415
		/// been dispatched and executed successfully.
416
		Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
417
		/// Query response has been received and query is removed. The registered notification
418
		/// could not be dispatched because the dispatch weight is greater than the maximum weight
419
		/// originally budgeted by this runtime for the query result.
420
		NotifyOverweight {
421
			query_id: QueryId,
422
			pallet_index: u8,
423
			call_index: u8,
424
			actual_weight: Weight,
425
			max_budgeted_weight: Weight,
426
		},
427
		/// Query response has been received and query is removed. There was a general error with
428
		/// dispatching the notification call.
429
		NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
430
		/// Query response has been received and query is removed. The dispatch was unable to be
431
		/// decoded into a `Call`; this might be due to dispatch function having a signature which
432
		/// is not `(origin, QueryId, Response)`.
433
		NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
434
		/// Expected query response has been received but the origin location of the response does
435
		/// not match that expected. The query remains registered for a later, valid, response to
436
		/// be received and acted upon.
437
		InvalidResponder {
438
			origin: Location,
439
			query_id: QueryId,
440
			expected_location: Option<Location>,
441
		},
442
		/// Expected query response has been received but the expected origin location placed in
443
		/// storage by this runtime previously cannot be decoded. The query remains registered.
444
		///
445
		/// This is unexpected (since a location placed in storage in a previously executing
446
		/// runtime should be readable prior to query timeout) and dangerous since the possibly
447
		/// valid response will be dropped. Manual governance intervention is probably going to be
448
		/// needed.
449
		InvalidResponderVersion { origin: Location, query_id: QueryId },
450
		/// Received query response has been read and removed.
451
		ResponseTaken { query_id: QueryId },
452
		/// Some assets have been placed in an asset trap.
453
		AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
454
		/// An XCM version change notification message has been attempted to be sent.
455
		///
456
		/// The cost of sending it (borne by the chain) is included.
457
		VersionChangeNotified {
458
			destination: Location,
459
			result: XcmVersion,
460
			cost: Assets,
461
			message_id: XcmHash,
462
		},
463
		/// The supported version of a location has been changed. This might be through an
464
		/// automatic notification or a manual intervention.
465
		SupportedVersionChanged { location: Location, version: XcmVersion },
466
		/// A given location which had a version change subscription was dropped owing to an error
467
		/// sending the notification to it.
468
		NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
469
		/// A given location which had a version change subscription was dropped owing to an error
470
		/// migrating the location to our new XCM format.
471
		NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
472
		/// Expected query response has been received but the expected querier location placed in
473
		/// storage by this runtime previously cannot be decoded. The query remains registered.
474
		///
475
		/// This is unexpected (since a location placed in storage in a previously executing
476
		/// runtime should be readable prior to query timeout) and dangerous since the possibly
477
		/// valid response will be dropped. Manual governance intervention is probably going to be
478
		/// needed.
479
		InvalidQuerierVersion { origin: Location, query_id: QueryId },
480
		/// Expected query response has been received but the querier location of the response does
481
		/// not match the expected. The query remains registered for a later, valid, response to
482
		/// be received and acted upon.
483
		InvalidQuerier {
484
			origin: Location,
485
			query_id: QueryId,
486
			expected_querier: Location,
487
			maybe_actual_querier: Option<Location>,
488
		},
489
		/// A remote has requested XCM version change notification from us and we have honored it.
490
		/// A version information message is sent to them and its cost is included.
491
		VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
492
		/// We have requested that a remote chain send us XCM version change notifications.
493
		VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
494
		/// We have requested that a remote chain stops sending us XCM version change
495
		/// notifications.
496
		VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
497
		/// Fees were paid from a location for an operation (often for using `SendXcm`).
498
		FeesPaid { paying: Location, fees: Assets },
499
		/// Some assets have been claimed from an asset trap
500
		AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
501
		/// A XCM version migration finished.
502
		VersionMigrationFinished { version: XcmVersion },
503
	}
504

            
505
	#[pallet::origin]
506
12
	#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
507
	pub enum Origin {
508
291
		/// It comes from somewhere in the XCM space wanting to transact.
509
291
		Xcm(Location),
510
66
		/// It comes as an expected response from an XCM location.
511
66
		Response(Location),
512
	}
513
	impl From<Location> for Origin {
514
		fn from(location: Location) -> Origin {
515
			Origin::Xcm(location)
516
		}
517
	}
518

            
519
44010
	#[pallet::error]
520
	pub enum Error<T> {
521
		/// The desired destination was unreachable, generally because there is a no way of routing
522
		/// to it.
523
		Unreachable,
524
		/// There was some other issue (i.e. not to do with routing) in sending the message.
525
		/// Perhaps a lack of space for buffering the message.
526
		SendFailure,
527
		/// The message execution fails the filter.
528
		Filtered,
529
		/// The message's weight could not be determined.
530
		UnweighableMessage,
531
		/// The destination `Location` provided cannot be inverted.
532
		DestinationNotInvertible,
533
		/// The assets to be sent are empty.
534
		Empty,
535
		/// Could not re-anchor the assets to declare the fees for the destination chain.
536
		CannotReanchor,
537
		/// Too many assets have been attempted for transfer.
538
		TooManyAssets,
539
		/// Origin is invalid for sending.
540
		InvalidOrigin,
541
		/// The version of the `Versioned` value used is not able to be interpreted.
542
		BadVersion,
543
		/// The given location could not be used (e.g. because it cannot be expressed in the
544
		/// desired version of XCM).
545
		BadLocation,
546
		/// The referenced subscription could not be found.
547
		NoSubscription,
548
		/// The location is invalid since it already has a subscription from us.
549
		AlreadySubscribed,
550
		/// Could not check-out the assets for teleportation to the destination chain.
551
		CannotCheckOutTeleport,
552
		/// The owner does not own (all) of the asset that they wish to do the operation on.
553
		LowBalance,
554
		/// The asset owner has too many locks on the asset.
555
		TooManyLocks,
556
		/// The given account is not an identifiable sovereign account for any location.
557
		AccountNotSovereign,
558
		/// The operation required fees to be paid which the initiator could not meet.
559
		FeesNotMet,
560
		/// A remote lock with the corresponding data could not be found.
561
		LockNotFound,
562
		/// The unlock operation cannot succeed because there are still consumers of the lock.
563
		InUse,
564
		/// Invalid asset, reserve chain could not be determined for it.
565
		#[codec(index = 21)]
566
		InvalidAssetUnknownReserve,
567
		/// Invalid asset, do not support remote asset reserves with different fees reserves.
568
		#[codec(index = 22)]
569
		InvalidAssetUnsupportedReserve,
570
		/// Too many assets with different reserve locations have been attempted for transfer.
571
		#[codec(index = 23)]
572
		TooManyReserves,
573
		/// Local XCM execution incomplete.
574
		#[codec(index = 24)]
575
		LocalExecutionIncomplete,
576
	}
577

            
578
	impl<T: Config> From<SendError> for Error<T> {
579
1659
		fn from(e: SendError) -> Self {
580
1659
			match e {
581
				SendError::Fees => Error::<T>::FeesNotMet,
582
1659
				SendError::NotApplicable => Error::<T>::Unreachable,
583
				_ => Error::<T>::SendFailure,
584
			}
585
1659
		}
586
	}
587

            
588
	impl<T: Config> From<AssetTransferError> for Error<T> {
589
69
		fn from(e: AssetTransferError) -> Self {
590
69
			match e {
591
69
				AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
592
69
			}
593
69
		}
594
	}
595

            
596
	/// The status of a query.
597
	#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
598
	pub enum QueryStatus<BlockNumber> {
599
		/// The query was sent but no response has yet been received.
600
		Pending {
601
			/// The `QueryResponse` XCM must have this origin to be considered a reply for this
602
			/// query.
603
			responder: VersionedLocation,
604
			/// The `QueryResponse` XCM must have this value as the `querier` field to be
605
			/// considered a reply for this query. If `None` then the querier is ignored.
606
			maybe_match_querier: Option<VersionedLocation>,
607
			maybe_notify: Option<(u8, u8)>,
608
			timeout: BlockNumber,
609
		},
610
		/// The query is for an ongoing version notification subscription.
611
		VersionNotifier { origin: VersionedLocation, is_active: bool },
612
		/// A response has been received.
613
		Ready { response: VersionedResponse, at: BlockNumber },
614
	}
615

            
616
	#[derive(Copy, Clone)]
617
	pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
618
	impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
619
	impl<'a> Encode for LatestVersionedLocation<'a> {
620
1880
		fn encode(&self) -> Vec<u8> {
621
1880
			let mut r = VersionedLocation::from(Location::default()).encode();
622
1880
			r.truncate(1);
623
1880
			self.0.using_encoded(|d| r.extend_from_slice(d));
624
1880
			r
625
1880
		}
626
	}
627

            
628
	#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
629
	pub enum VersionMigrationStage {
630
		MigrateSupportedVersion,
631
		MigrateVersionNotifiers,
632
		NotifyCurrentTargets(Option<Vec<u8>>),
633
		MigrateAndNotifyOldTargets,
634
	}
635

            
636
	impl Default for VersionMigrationStage {
637
		fn default() -> Self {
638
			Self::MigrateSupportedVersion
639
		}
640
	}
641

            
642
	/// The latest available query index.
643
828
	#[pallet::storage]
644
	pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
645

            
646
	/// The ongoing queries.
647
1161
	#[pallet::storage]
648
	#[pallet::getter(fn query)]
649
	pub(super) type Queries<T: Config> =
650
		StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
651

            
652
	/// The existing asset traps.
653
	///
654
	/// Key is the blake2 256 hash of (origin, versioned `Assets`) pair. Value is the number of
655
	/// times this pair has been trapped (usually just 1 if it exists at all).
656
528
	#[pallet::storage]
657
	#[pallet::getter(fn asset_trap)]
658
	pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
659

            
660
	/// Default version to encode XCM when latest version of destination is unknown. If `None`,
661
	/// then the destinations whose XCM version is unknown are considered unreachable.
662
3996
	#[pallet::storage]
663
	#[pallet::whitelist_storage]
664
	pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
665

            
666
	/// The Latest versions that we know various locations support.
667
216543
	#[pallet::storage]
668
	pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
669
		_,
670
		Twox64Concat,
671
		XcmVersion,
672
		Blake2_128Concat,
673
		VersionedLocation,
674
		XcmVersion,
675
		OptionQuery,
676
	>;
677

            
678
	/// All locations that we have requested version notifications from.
679
215016
	#[pallet::storage]
680
	pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
681
		_,
682
		Twox64Concat,
683
		XcmVersion,
684
		Blake2_128Concat,
685
		VersionedLocation,
686
		QueryId,
687
		OptionQuery,
688
	>;
689

            
690
	/// The target locations that are subscribed to our version changes, as well as the most recent
691
	/// of our versions we informed them of.
692
215373
	#[pallet::storage]
693
	pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
694
		_,
695
		Twox64Concat,
696
		XcmVersion,
697
		Blake2_128Concat,
698
		VersionedLocation,
699
		(QueryId, Weight, XcmVersion),
700
		OptionQuery,
701
	>;
702

            
703
	pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
704
	impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
705
339456
		fn get() -> u32 {
706
339456
			T::VERSION_DISCOVERY_QUEUE_SIZE
707
339456
		}
708
	}
709

            
710
	/// Destinations whose latest XCM version we would like to know. Duplicates not allowed, and
711
	/// the `u32` counter is the number of times that a send to the destination has been attempted,
712
	/// which is used as a prioritization.
713
787032
	#[pallet::storage]
714
	#[pallet::whitelist_storage]
715
	pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
716
		_,
717
		BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
718
		ValueQuery,
719
	>;
720

            
721
	/// The current migration's stage, if any.
722
496800
	#[pallet::storage]
723
	pub(super) type CurrentMigration<T: Config> =
724
		StorageValue<_, VersionMigrationStage, OptionQuery>;
725

            
726
	#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
727
	#[scale_info(skip_type_params(MaxConsumers))]
728
	pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
729
		/// Total amount of the asset held by the remote lock.
730
		pub amount: u128,
731
		/// The owner of the locked asset.
732
		pub owner: VersionedLocation,
733
		/// The location which holds the original lock.
734
		pub locker: VersionedLocation,
735
		/// Local consumers of the remote lock with a consumer identifier and the amount
736
		/// of fungible asset every consumer holds.
737
		/// Every consumer can hold up to total amount of the remote lock.
738
		pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
739
	}
740

            
741
	impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
742
		/// Amount of the remote lock in use by consumers.
743
		/// Returns `None` if the remote lock has no consumers.
744
		pub fn amount_held(&self) -> Option<u128> {
745
			self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
746
		}
747
	}
748

            
749
	/// Fungible assets which we know are locked on a remote chain.
750
	#[pallet::storage]
751
	pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
752
		_,
753
		(
754
			NMapKey<Twox64Concat, XcmVersion>,
755
			NMapKey<Blake2_128Concat, T::AccountId>,
756
			NMapKey<Blake2_128Concat, VersionedAssetId>,
757
		),
758
		RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
759
		OptionQuery,
760
	>;
761

            
762
	/// Fungible assets which we know are locked on this chain.
763
	#[pallet::storage]
764
	pub(super) type LockedFungibles<T: Config> = StorageMap<
765
		_,
766
		Blake2_128Concat,
767
		T::AccountId,
768
		BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
769
		OptionQuery,
770
	>;
771

            
772
	/// Global suspension state of the XCM executor.
773
	#[pallet::storage]
774
	pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
775

            
776
	/// Whether or not incoming XCMs (both executed locally and received) should be recorded.
777
	/// Only one XCM program will be recorded at a time.
778
	/// This is meant to be used in runtime APIs, and it's advised it stays false
779
	/// for all other use cases, so as to not degrade regular performance.
780
	///
781
	/// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`]
782
	/// implementation in the XCM executor configuration.
783
	#[pallet::storage]
784
	pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
785

            
786
	/// If [`ShouldRecordXcm`] is set to true, then the last XCM program executed locally
787
	/// will be stored here.
788
	/// Runtime APIs can fetch the XCM that was executed by accessing this value.
789
	///
790
	/// Only relevant if this pallet is being used as the [`xcm_executor::traits::RecordXcm`]
791
	/// implementation in the XCM executor configuration.
792
	#[pallet::storage]
793
	pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
794

            
795
	#[pallet::genesis_config]
796
	pub struct GenesisConfig<T: Config> {
797
		#[serde(skip)]
798
		pub _config: core::marker::PhantomData<T>,
799
		/// The default version to encode outgoing XCM messages with.
800
		pub safe_xcm_version: Option<XcmVersion>,
801
	}
802

            
803
	impl<T: Config> Default for GenesisConfig<T> {
804
3
		fn default() -> Self {
805
3
			Self { safe_xcm_version: Some(XCM_VERSION), _config: Default::default() }
806
3
		}
807
	}
808

            
809
	#[pallet::genesis_build]
810
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
811
3
		fn build(&self) {
812
3
			SafeXcmVersion::<T>::set(self.safe_xcm_version);
813
3
		}
814
	}
815

            
816
554367
	#[pallet::hooks]
817
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
818
194763
		fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
819
194763
			let mut weight_used = Weight::zero();
820
194763
			if let Some(migration) = CurrentMigration::<T>::get() {
821
				// Consume 10% of block at most
822
				let max_weight = T::BlockWeights::get().max_block / 10;
823
				let (w, maybe_migration) = Self::check_xcm_version_change(migration, max_weight);
824
				if maybe_migration.is_none() {
825
					Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
826
				}
827
				CurrentMigration::<T>::set(maybe_migration);
828
				weight_used.saturating_accrue(w);
829
194763
			}
830

            
831
			// Here we aim to get one successful version negotiation request sent per block, ordered
832
			// by the destinations being most sent to.
833
194763
			let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
834
194763
			// TODO: correct weights.
835
194763
			weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
836
194781
			q.sort_by_key(|i| i.1);
837
194817
			while let Some((versioned_dest, _)) = q.pop() {
838
261
				if let Ok(dest) = Location::try_from(versioned_dest) {
839
261
					if Self::request_version_notify(dest).is_ok() {
840
						// TODO: correct weights.
841
207
						weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
842
207
						break
843
54
					}
844
				}
845
			}
846
			// Should never fail since we only removed items. But better safe than panicking as it's
847
			// way better to drop the queue than panic on initialize.
848
194763
			if let Ok(q) = BoundedVec::try_from(q) {
849
194763
				VersionDiscoveryQueue::<T>::put(q);
850
194763
			}
851
194763
			weight_used
852
194763
		}
853

            
854
		#[cfg(feature = "try-runtime")]
855
53637
		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
856
53637
			Self::do_try_state()
857
53637
		}
858
	}
859

            
860
	pub mod migrations {
861
		use super::*;
862
		use frame_support::traits::{PalletInfoAccess, StorageVersion};
863

            
864
		#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
865
		enum QueryStatusV0<BlockNumber> {
866
			Pending {
867
				responder: VersionedLocation,
868
				maybe_notify: Option<(u8, u8)>,
869
				timeout: BlockNumber,
870
			},
871
			VersionNotifier {
872
				origin: VersionedLocation,
873
				is_active: bool,
874
			},
875
			Ready {
876
				response: VersionedResponse,
877
				at: BlockNumber,
878
			},
879
		}
880
		impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
881
			fn from(old: QueryStatusV0<B>) -> Self {
882
				use QueryStatusV0::*;
883
				match old {
884
					Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
885
						responder,
886
						maybe_notify,
887
						timeout,
888
						maybe_match_querier: Some(Location::here().into()),
889
					},
890
					VersionNotifier { origin, is_active } =>
891
						QueryStatus::VersionNotifier { origin, is_active },
892
					Ready { response, at } => QueryStatus::Ready { response, at },
893
				}
894
			}
895
		}
896

            
897
		pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
898
		) -> frame_support::weights::Weight {
899
			let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
900
			log::info!(
901
				target: "runtime::xcm",
902
				"Running migration storage v1 for xcm with storage version {:?}",
903
				on_chain_storage_version,
904
			);
905

            
906
			if on_chain_storage_version < 1 {
907
				let mut count = 0;
908
				Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
909
					count += 1;
910
					Some(value.into())
911
				});
912
				StorageVersion::new(1).put::<P>();
913
				log::info!(
914
					target: "runtime::xcm",
915
					"Running migration storage v1 for xcm with storage version {:?} was complete",
916
					on_chain_storage_version,
917
				);
918
				// calculate and return migration weights
919
				T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
920
			} else {
921
				log::warn!(
922
					target: "runtime::xcm",
923
					"Attempted to apply migration to v1 but failed because storage version is {:?}",
924
					on_chain_storage_version,
925
				);
926
				T::DbWeight::get().reads(1)
927
			}
928
		}
929
	}
930

            
931
132132
	#[pallet::call(weight(<T as Config>::WeightInfo))]
932
	impl<T: Config> Pallet<T> {
933
		#[pallet::call_index(0)]
934
		pub fn send(
935
			origin: OriginFor<T>,
936
			dest: Box<VersionedLocation>,
937
			message: Box<VersionedXcm<()>>,
938
2667
		) -> DispatchResult {
939
2667
			<Self as SendController<_>>::send(origin, dest, message)?;
940
987
			Ok(())
941
		}
942

            
943
		/// Teleport some assets from the local chain to some destination chain.
944
		///
945
		/// **This function is deprecated: Use `limited_teleport_assets` instead.**
946
		///
947
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
948
		/// index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,
949
		/// with all fees taken as needed from the asset.
950
		///
951
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
952
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
953
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
954
		///   relay to parachain.
955
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
956
		///   generally be an `AccountId32` value.
957
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
958
		///   fee on the `dest` chain.
959
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
960
		///   fees.
961
		#[pallet::call_index(1)]
962
		#[allow(deprecated)]
963
		#[deprecated(
964
			note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
965
		)]
966
		pub fn teleport_assets(
967
			origin: OriginFor<T>,
968
			dest: Box<VersionedLocation>,
969
			beneficiary: Box<VersionedLocation>,
970
			assets: Box<VersionedAssets>,
971
			fee_asset_item: u32,
972
123
		) -> DispatchResult {
973
123
			Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
974
		}
975

            
976
		/// Transfer some assets from the local chain to the destination chain through their local,
977
		/// destination or remote reserve.
978
		///
979
		/// `assets` must have same reserve location and may not be teleportable to `dest`.
980
		///  - `assets` have local reserve: transfer assets to sovereign account of destination
981
		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
982
		///    assets to `beneficiary`.
983
		///  - `assets` have destination reserve: burn local assets and forward a notification to
984
		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
985
		///    deposit them to `beneficiary`.
986
		///  - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move
987
		///    reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest`
988
		///    to mint and deposit reserve-based assets to `beneficiary`.
989
		///
990
		/// **This function is deprecated: Use `limited_reserve_transfer_assets` instead.**
991
		///
992
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
993
		/// index `fee_asset_item`. The weight limit for fees is not provided and thus is unlimited,
994
		/// with all fees taken as needed from the asset.
995
		///
996
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
997
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
998
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
999
		///   relay to parachain.
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
		///   generally be an `AccountId32` value.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` (and possibly reserve) chains.
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
		///   fees.
		#[pallet::call_index(2)]
		#[allow(deprecated)]
		#[deprecated(
			note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
		)]
		pub fn reserve_transfer_assets(
			origin: OriginFor<T>,
			dest: Box<VersionedLocation>,
			beneficiary: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
			fee_asset_item: u32,
24
		) -> DispatchResult {
24
			Self::do_reserve_transfer_assets(
24
				origin,
24
				dest,
24
				beneficiary,
24
				assets,
24
				fee_asset_item,
24
				Unlimited,
24
			)
		}
		/// Execute an XCM message from a local, signed, origin.
		///
		/// An event is deposited indicating whether `msg` could be executed completely or only
		/// partially.
		///
		/// No more than `max_weight` will be used in its attempted execution. If this is less than
		/// the maximum amount of weight that the message could take to be executed, then no
		/// execution attempt will be made.
		#[pallet::call_index(3)]
		#[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
		pub fn execute(
			origin: OriginFor<T>,
			message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
			max_weight: Weight,
22287
		) -> DispatchResultWithPostInfo {
2400
			let weight_used =
22287
				<Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
2400
			Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
		}
		/// Extoll that a particular destination can be communicated with through a particular
		/// version of XCM.
		///
		/// - `origin`: Must be an origin specified by AdminOrigin.
		/// - `location`: The destination that is being described.
		/// - `xcm_version`: The latest version of XCM that `location` supports.
		#[pallet::call_index(4)]
		pub fn force_xcm_version(
			origin: OriginFor<T>,
			location: Box<Location>,
			version: XcmVersion,
183
		) -> DispatchResult {
183
			T::AdminOrigin::ensure_origin(origin)?;
			let location = *location;
			SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
			Self::deposit_event(Event::SupportedVersionChanged { location, version });
			Ok(())
		}
		/// Set a safe XCM version (the version that XCM should be encoded with if the most recent
		/// version a destination can accept is unknown).
		///
		/// - `origin`: Must be an origin specified by AdminOrigin.
		/// - `maybe_xcm_version`: The default XCM encoding version, or `None` to disable.
		#[pallet::call_index(5)]
		pub fn force_default_xcm_version(
			origin: OriginFor<T>,
			maybe_xcm_version: Option<XcmVersion>,
216
		) -> DispatchResult {
216
			T::AdminOrigin::ensure_origin(origin)?;
			SafeXcmVersion::<T>::set(maybe_xcm_version);
			Ok(())
		}
		/// Ask a location to notify us regarding their XCM version and any changes to it.
		///
		/// - `origin`: Must be an origin specified by AdminOrigin.
		/// - `location`: The location to which we should subscribe for XCM version notifications.
		#[pallet::call_index(6)]
		pub fn force_subscribe_version_notify(
			origin: OriginFor<T>,
			location: Box<VersionedLocation>,
747
		) -> DispatchResult {
747
			T::AdminOrigin::ensure_origin(origin)?;
			let location: Location =
				(*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
			Self::request_version_notify(location).map_err(|e| {
				match e {
					XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
					_ => Error::<T>::InvalidOrigin,
				}
				.into()
			})
		}
		/// Require that a particular destination should no longer notify us regarding any XCM
		/// version changes.
		///
		/// - `origin`: Must be an origin specified by AdminOrigin.
		/// - `location`: The location to which we are currently subscribed for XCM version
		///   notifications which we no longer desire.
		#[pallet::call_index(7)]
		pub fn force_unsubscribe_version_notify(
			origin: OriginFor<T>,
			location: Box<VersionedLocation>,
555
		) -> DispatchResult {
555
			T::AdminOrigin::ensure_origin(origin)?;
			let location: Location =
				(*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
			Self::unrequest_version_notify(location).map_err(|e| {
				match e {
					XcmError::InvalidLocation => Error::<T>::NoSubscription,
					_ => Error::<T>::InvalidOrigin,
				}
				.into()
			})
		}
		/// Transfer some assets from the local chain to the destination chain through their local,
		/// destination or remote reserve.
		///
		/// `assets` must have same reserve location and may not be teleportable to `dest`.
		///  - `assets` have local reserve: transfer assets to sovereign account of destination
		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
		///    assets to `beneficiary`.
		///  - `assets` have destination reserve: burn local assets and forward a notification to
		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
		///    deposit them to `beneficiary`.
		///  - `assets` have remote reserve: burn local assets, forward XCM to reserve chain to move
		///    reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest`
		///    to mint and deposit reserve-based assets to `beneficiary`.
		///
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
		/// index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight
		/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
		/// at risk.
		///
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
		///   relay to parachain.
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
		///   generally be an `AccountId32` value.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` (and possibly reserve) chains.
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
		///   fees.
		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
		#[pallet::call_index(8)]
		#[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
		pub fn limited_reserve_transfer_assets(
			origin: OriginFor<T>,
			dest: Box<VersionedLocation>,
			beneficiary: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
			fee_asset_item: u32,
			weight_limit: WeightLimit,
84
		) -> DispatchResult {
84
			Self::do_reserve_transfer_assets(
84
				origin,
84
				dest,
84
				beneficiary,
84
				assets,
84
				fee_asset_item,
84
				weight_limit,
84
			)
		}
		/// Teleport some assets from the local chain to some destination chain.
		///
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
		/// index `fee_asset_item`, up to enough to pay for `weight_limit` of weight. If more weight
		/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
		/// at risk.
		///
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
		///   relay to parachain.
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
		///   generally be an `AccountId32` value.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` chain.
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
		///   fees.
		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
		#[pallet::call_index(9)]
		#[pallet::weight(T::WeightInfo::teleport_assets())]
		pub fn limited_teleport_assets(
			origin: OriginFor<T>,
			dest: Box<VersionedLocation>,
			beneficiary: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
			fee_asset_item: u32,
			weight_limit: WeightLimit,
36
		) -> DispatchResult {
36
			Self::do_teleport_assets(
36
				origin,
36
				dest,
36
				beneficiary,
36
				assets,
36
				fee_asset_item,
36
				weight_limit,
36
			)
		}
		/// Set or unset the global suspension state of the XCM executor.
		///
		/// - `origin`: Must be an origin specified by AdminOrigin.
		/// - `suspended`: `true` to suspend, `false` to resume.
		#[pallet::call_index(10)]
33
		pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
33
			T::AdminOrigin::ensure_origin(origin)?;
			XcmExecutionSuspended::<T>::set(suspended);
			Ok(())
		}
		/// Transfer some assets from the local chain to the destination chain through their local,
		/// destination or remote reserve, or through teleports.
		///
		/// Fee payment on the destination side is made from the asset in the `assets` vector of
		/// index `fee_asset_item` (hence referred to as `fees`), up to enough to pay for
		/// `weight_limit` of weight. If more weight is needed than `weight_limit`, then the
		/// operation will fail and the sent assets may be at risk.
		///
		/// `assets` (excluding `fees`) must have same reserve location or otherwise be teleportable
		/// to `dest`, no limitations imposed on `fees`.
		///  - for local reserve: transfer assets to sovereign account of destination chain and
		///    forward a notification XCM to `dest` to mint and deposit reserve-based assets to
		///    `beneficiary`.
		///  - for destination reserve: burn local assets and forward a notification to `dest` chain
		///    to withdraw the reserve assets from this chain's sovereign account and deposit them
		///    to `beneficiary`.
		///  - for remote reserve: burn local assets, forward XCM to reserve chain to move reserves
		///    from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint
		///    and deposit reserve-based assets to `beneficiary`.
		///  - for teleports: burn local assets and forward XCM to `dest` chain to mint/teleport
		///    assets and deposit them to `beneficiary`.
		///
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
		/// - `dest`: Destination context for the assets. Will typically be `X2(Parent,
		///   Parachain(..))` to send from parachain to parachain, or `X1(Parachain(..))` to send
		///   from relay to parachain.
		/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
		///   generally be an `AccountId32` value.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` (and possibly reserve) chains.
		/// - `fee_asset_item`: The index into `assets` of the item which should be used to pay
		///   fees.
		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
		#[pallet::call_index(11)]
		pub fn transfer_assets(
			origin: OriginFor<T>,
			dest: Box<VersionedLocation>,
			beneficiary: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
			fee_asset_item: u32,
			weight_limit: WeightLimit,
93
		) -> DispatchResult {
93
			let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
93
			let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
93
			let beneficiary: Location =
93
				(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
93
			let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
90
			log::debug!(
				target: "xcm::pallet_xcm::transfer_assets",
				"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}, weight_limit {:?}",
				origin, dest, beneficiary, assets, fee_asset_item, weight_limit,
			);
90
			ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
90
			let assets = assets.into_inner();
90
			let fee_asset_item = fee_asset_item as usize;
			// Find transfer types for fee and non-fee assets.
12
			let (fees_transfer_type, assets_transfer_type) =
90
				Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
12
			Self::do_transfer_assets(
12
				origin,
12
				dest,
12
				Either::Left(beneficiary),
12
				assets,
12
				assets_transfer_type,
12
				fee_asset_item,
12
				fees_transfer_type,
12
				weight_limit,
12
			)
		}
		/// Claims assets trapped on this pallet because of leftover assets during XCM execution.
		///
		/// - `origin`: Anyone can call this extrinsic.
		/// - `assets`: The exact assets that were trapped. Use the version to specify what version
		/// was the latest when they were trapped.
		/// - `beneficiary`: The location/account where the claimed assets will be deposited.
		#[pallet::call_index(12)]
		pub fn claim_assets(
			origin: OriginFor<T>,
			assets: Box<VersionedAssets>,
			beneficiary: Box<VersionedLocation>,
69
		) -> DispatchResult {
69
			let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
69
			log::debug!(target: "xcm::pallet_xcm::claim_assets", "origin: {:?}, assets: {:?}, beneficiary: {:?}", origin_location, assets, beneficiary);
			// Extract version from `assets`.
69
			let assets_version = assets.identify_version();
69
			let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
57
			let number_of_assets = assets.len() as u32;
57
			let beneficiary: Location =
57
				(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
57
			let ticket: Location = GeneralIndex(assets_version as u128).into();
57
			let mut message = Xcm(vec![
57
				ClaimAsset { assets, ticket },
57
				DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
57
			]);
57
			let weight =
57
				T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
57
			let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
57
			let outcome = T::XcmExecutor::prepare_and_execute(
57
				origin_location,
57
				message,
57
				&mut hash,
57
				weight,
57
				weight,
57
			);
57
			outcome.ensure_complete().map_err(|error| {
57
				log::error!(target: "xcm::pallet_xcm::claim_assets", "XCM execution failed with error: {:?}", error);
57
				Error::<T>::LocalExecutionIncomplete
57
			})?;
			Ok(())
		}
		/// Transfer assets from the local chain to the destination chain using explicit transfer
		/// types for assets and fees.
		///
		/// `assets` must have same reserve location or may be teleportable to `dest`. Caller must
		/// provide the `assets_transfer_type` to be used for `assets`:
		///  - `TransferType::LocalReserve`: transfer assets to sovereign account of destination
		///    chain and forward a notification XCM to `dest` to mint and deposit reserve-based
		///    assets to `beneficiary`.
		///  - `TransferType::DestinationReserve`: burn local assets and forward a notification to
		///    `dest` chain to withdraw the reserve assets from this chain's sovereign account and
		///    deposit them to `beneficiary`.
		///  - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve`
		///    chain to move reserves from this chain's SA to `dest` chain's SA, and forward another
		///    XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically
		///    the remote `reserve` is Asset Hub.
		///  - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to
		///    mint/teleport assets and deposit them to `beneficiary`.
		///
		/// On the destination chain, as well as any intermediary hops, `BuyExecution` is used to
		/// buy execution using transferred `assets` identified by `remote_fees_id`.
		/// Make sure enough of the specified `remote_fees_id` asset is included in the given list
		/// of `assets`. `remote_fees_id` should be enough to pay for `weight_limit`. If more weight
		/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
		/// at risk.
		///
		/// `remote_fees_id` may use different transfer type than rest of `assets` and can be
		/// specified through `fees_transfer_type`.
		///
		/// The caller needs to specify what should happen to the transferred assets once they reach
		/// the `dest` chain. This is done through the `custom_xcm_on_dest` parameter, which
		/// contains the instructions to execute on `dest` as a final step.
		///   This is usually as simple as:
		///   `Xcm(vec![DepositAsset { assets: Wild(AllCounted(assets.len())), beneficiary }])`,
		///   but could be something more exotic like sending the `assets` even further.
		///
		/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
		/// - `dest`: Destination context for the assets. Will typically be `[Parent,
		///   Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
		///   relay to parachain, or `(parents: 2, (GlobalConsensus(..), ..))` to send from
		///   parachain across a bridge to another ecosystem destination.
		/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
		///   fee on the `dest` (and possibly reserve) chains.
		/// - `assets_transfer_type`: The XCM `TransferType` used to transfer the `assets`.
		/// - `remote_fees_id`: One of the included `assets` to be used to pay fees.
		/// - `fees_transfer_type`: The XCM `TransferType` used to transfer the `fees` assets.
		/// - `custom_xcm_on_dest`: The XCM to be executed on `dest` chain as the last step of the
		///   transfer, which also determines what happens to the assets on the destination chain.
		/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
		#[pallet::call_index(13)]
		#[pallet::weight(T::WeightInfo::transfer_assets())]
		pub fn transfer_assets_using_type_and_then(
			origin: OriginFor<T>,
			dest: Box<VersionedLocation>,
			assets: Box<VersionedAssets>,
			assets_transfer_type: Box<TransferType>,
			remote_fees_id: Box<VersionedAssetId>,
			fees_transfer_type: Box<TransferType>,
			custom_xcm_on_dest: Box<VersionedXcm<()>>,
			weight_limit: WeightLimit,
9
		) -> DispatchResult {
9
			let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
9
			let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
9
			let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
3
			let fees_id: AssetId =
3
				(*remote_fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?;
3
			let remote_xcm: Xcm<()> =
3
				(*custom_xcm_on_dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
3
			log::debug!(
				target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
				"origin {origin_location:?}, dest {dest:?}, assets {assets:?} through {assets_transfer_type:?}, \
				remote_fees_id {fees_id:?} through {fees_transfer_type:?}, \
				custom_xcm_on_dest {remote_xcm:?}, weight-limit {weight_limit:?}",
			);
3
			let assets = assets.into_inner();
3
			ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
			let fee_asset_index =
3
				assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
			Self::do_transfer_assets(
				origin_location,
				dest,
				Either::Right(remote_xcm),
				assets,
				*assets_transfer_type,
				fee_asset_index,
				*fees_transfer_type,
				weight_limit,
			)
		}
	}
}
/// The maximum number of distinct assets allowed to be transferred in a single helper extrinsic.
const MAX_ASSETS_FOR_TRANSFER: usize = 2;
/// Specify how assets used for fees are handled during asset transfers.
#[derive(Clone, PartialEq)]
enum FeesHandling<T: Config> {
	/// `fees` asset can be batch-transferred with rest of assets using same XCM instructions.
	Batched { fees: Asset },
	/// fees cannot be batched, they are handled separately using XCM programs here.
	Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
}
impl<T: Config> core::fmt::Debug for FeesHandling<T> {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		match self {
			Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
			Self::Separate { local_xcm, remote_xcm } => write!(
				f,
				"FeesHandling::Separate(local: {:?}, remote: {:?})",
				local_xcm, remote_xcm
			),
		}
	}
}
impl<T: Config> QueryHandler for Pallet<T> {
	type BlockNumber = BlockNumberFor<T>;
	type Error = XcmError;
	type UniversalLocation = T::UniversalLocation;
	/// Attempt to create a new query ID and register it as a query that is yet to respond.
	fn new_query(
		responder: impl Into<Location>,
		timeout: BlockNumberFor<T>,
		match_querier: impl Into<Location>,
	) -> QueryId {
		Self::do_new_query(responder, None, timeout, match_querier)
	}
	/// To check the status of the query, use `fn query()` passing the resultant `QueryId`
	/// value.
	fn report_outcome(
		message: &mut Xcm<()>,
		responder: impl Into<Location>,
		timeout: Self::BlockNumber,
	) -> Result<QueryId, Self::Error> {
		let responder = responder.into();
		let destination = Self::UniversalLocation::get()
			.invert_target(&responder)
			.map_err(|()| XcmError::LocationNotInvertible)?;
		let query_id = Self::new_query(responder, timeout, Here);
		let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
		let report_error = Xcm(vec![ReportError(response_info)]);
		message.0.insert(0, SetAppendix(report_error));
		Ok(query_id)
	}
	/// Removes response when ready and emits [Event::ResponseTaken] event.
	fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
		match Queries::<T>::get(query_id) {
			Some(QueryStatus::Ready { response, at }) => match response.try_into() {
				Ok(response) => {
					Queries::<T>::remove(query_id);
					Self::deposit_event(Event::ResponseTaken { query_id });
					QueryResponseStatus::Ready { response, at }
				},
				Err(_) => QueryResponseStatus::UnexpectedVersion,
			},
			Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
			Some(_) => QueryResponseStatus::UnexpectedVersion,
			None => QueryResponseStatus::NotFound,
		}
	}
	#[cfg(feature = "runtime-benchmarks")]
	fn expect_response(id: QueryId, response: Response) {
		let response = response.into();
		Queries::<T>::insert(
			id,
			QueryStatus::Ready { response, at: frame_system::Pallet::<T>::block_number() },
		);
	}
}
impl<T: Config> Pallet<T> {
	/// Find `TransferType`s for `assets` and fee identified through `fee_asset_item`, when
	/// transferring to `dest`.
	///
	/// Validate `assets` to all have same `TransferType`.
138
	fn find_fee_and_assets_transfer_types(
138
		assets: &[Asset],
138
		fee_asset_item: usize,
138
		dest: &Location,
138
	) -> Result<(TransferType, TransferType), Error<T>> {
138
		let mut fees_transfer_type = None;
138
		let mut assets_transfer_type = None;
138
		for (idx, asset) in assets.iter().enumerate() {
114
			if let Fungible(x) = asset.fun {
				// If fungible asset, ensure non-zero amount.
72
				ensure!(!x.is_zero(), Error::<T>::Empty);
42
			}
84
			let transfer_type =
105
				T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
84
			if idx == fee_asset_item {
57
				fees_transfer_type = Some(transfer_type);
57
			} else {
27
				if let Some(existing) = assets_transfer_type.as_ref() {
					// Ensure transfer for multiple assets uses same transfer type (only fee may
					// have different transfer type/path)
					ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
27
				} else {
27
					// asset reserve identified
27
					assets_transfer_type = Some(transfer_type);
27
				}
			}
		}
		// single asset also marked as fee item
108
		if assets.len() == 1 {
84
			assets_transfer_type = fees_transfer_type.clone()
24
		}
		Ok((
108
			fees_transfer_type.ok_or(Error::<T>::Empty)?,
57
			assets_transfer_type.ok_or(Error::<T>::Empty)?,
		))
138
	}
108
	fn do_reserve_transfer_assets(
108
		origin: OriginFor<T>,
108
		dest: Box<VersionedLocation>,
108
		beneficiary: Box<VersionedLocation>,
108
		assets: Box<VersionedAssets>,
108
		fee_asset_item: u32,
108
		weight_limit: WeightLimit,
108
	) -> DispatchResult {
108
		let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
108
		let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
108
		let beneficiary: Location =
108
			(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
108
		let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
105
		log::debug!(
			target: "xcm::pallet_xcm::do_reserve_transfer_assets",
			"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}",
			origin_location, dest, beneficiary, assets, fee_asset_item,
		);
105
		ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
105
		let value = (origin_location, assets.into_inner());
105
		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
105
		let (origin, assets) = value;
105

            
105
		let fee_asset_item = fee_asset_item as usize;
105
		let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
		// Find transfer types for fee and non-fee assets.
45
		let (fees_transfer_type, assets_transfer_type) =
48
			Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
		// Ensure assets (and fees according to check below) are not teleportable to `dest`.
45
		ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
		// Ensure all assets (including fees) have same reserve location.
45
		ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
45
		let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
45
			origin.clone(),
45
			dest.clone(),
45
			Either::Left(beneficiary),
45
			assets,
45
			assets_transfer_type,
45
			FeesHandling::Batched { fees },
45
			weight_limit,
45
		)?;
		Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
108
	}
159
	fn do_teleport_assets(
159
		origin: OriginFor<T>,
159
		dest: Box<VersionedLocation>,
159
		beneficiary: Box<VersionedLocation>,
159
		assets: Box<VersionedAssets>,
159
		fee_asset_item: u32,
159
		weight_limit: WeightLimit,
159
	) -> DispatchResult {
159
		let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
159
		let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
156
		let beneficiary: Location =
159
			(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
156
		let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
153
		log::debug!(
			target: "xcm::pallet_xcm::do_teleport_assets",
			"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}, weight_limit {:?}",
			origin_location, dest, beneficiary, assets, fee_asset_item, weight_limit,
		);
153
		ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
153
		let value = (origin_location, assets.into_inner());
153
		ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
153
		let (origin_location, assets) = value;
153
		for asset in assets.iter() {
3
			let transfer_type =
51
				T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
3
			ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
		}
102
		let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
		let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
			origin_location.clone(),
			dest.clone(),
			Either::Left(beneficiary),
			assets,
			TransferType::Teleport,
			FeesHandling::Batched { fees },
			weight_limit,
		)?;
		Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
159
	}
12
	fn do_transfer_assets(
12
		origin: Location,
12
		dest: Location,
12
		beneficiary: Either<Location, Xcm<()>>,
12
		mut assets: Vec<Asset>,
12
		assets_transfer_type: TransferType,
12
		fee_asset_index: usize,
12
		fees_transfer_type: TransferType,
12
		weight_limit: WeightLimit,
12
	) -> DispatchResult {
		// local and remote XCM programs to potentially handle fees separately
12
		let fees = if fees_transfer_type == assets_transfer_type {
12
			let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
12
			// no need for custom fees instructions, fees are batched with assets
12
			FeesHandling::Batched { fees }
		} else {
			// Disallow _remote reserves_ unless assets & fees have same remote reserve (covered
			// by branch above). The reason for this is that we'd need to send XCMs to separate
			// chains with no guarantee of delivery order on final destination; therefore we
			// cannot guarantee to have fees in place on final destination chain to pay for
			// assets transfer.
			ensure!(
				!matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
				Error::<T>::InvalidAssetUnsupportedReserve
			);
			let weight_limit = weight_limit.clone();
			// remove `fees` from `assets` and build separate fees transfer instructions to be
			// added to assets transfers XCM programs
			let fees = assets.remove(fee_asset_index);
			let (local_xcm, remote_xcm) = match fees_transfer_type {
				TransferType::LocalReserve => Self::local_reserve_fees_instructions(
					origin.clone(),
					dest.clone(),
					fees,
					weight_limit,
				)?,
				TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
					origin.clone(),
					dest.clone(),
					fees,
					weight_limit,
				)?,
				TransferType::Teleport => Self::teleport_fees_instructions(
					origin.clone(),
					dest.clone(),
					fees,
					weight_limit,
				)?,
				TransferType::RemoteReserve(_) =>
					return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
			};
			FeesHandling::Separate { local_xcm, remote_xcm }
		};
12
		let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
12
			origin.clone(),
12
			dest.clone(),
12
			beneficiary,
12
			assets,
12
			assets_transfer_type,
12
			fees,
12
			weight_limit,
12
		)?;
3
		Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
12
	}
57
	fn build_xcm_transfer_type(
57
		origin: Location,
57
		dest: Location,
57
		beneficiary: Either<Location, Xcm<()>>,
57
		assets: Vec<Asset>,
57
		transfer_type: TransferType,
57
		fees: FeesHandling<T>,
57
		weight_limit: WeightLimit,
57
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
57
		log::debug!(
			target: "xcm::pallet_xcm::build_xcm_transfer_type",
			"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, transfer_type {:?}, \
			fees_handling {:?}, weight_limit: {:?}",
			origin, dest, beneficiary, assets, transfer_type, fees, weight_limit,
		);
57
		match transfer_type {
57
			TransferType::LocalReserve => Self::local_reserve_transfer_programs(
57
				origin.clone(),
57
				dest.clone(),
57
				beneficiary,
57
				assets,
57
				fees,
57
				weight_limit,
57
			)
57
			.map(|(local, remote)| (local, Some(remote))),
			TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
				origin.clone(),
				dest.clone(),
				beneficiary,
				assets,
				fees,
				weight_limit,
			)
			.map(|(local, remote)| (local, Some(remote))),
			TransferType::RemoteReserve(reserve) => {
				let fees = match fees {
					FeesHandling::Batched { fees } => fees,
					_ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
				};
				Self::remote_reserve_transfer_program(
					origin.clone(),
					reserve.try_into().map_err(|()| Error::<T>::BadVersion)?,
					beneficiary,
					dest.clone(),
					assets,
					fees,
					weight_limit,
				)
				.map(|local| (local, None))
			},
			TransferType::Teleport => Self::teleport_assets_program(
				origin.clone(),
				dest.clone(),
				beneficiary,
				assets,
				fees,
				weight_limit,
			)
			.map(|(local, remote)| (local, Some(remote))),
		}
57
	}
3
	fn execute_xcm_transfer(
3
		origin: Location,
3
		dest: Location,
3
		mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
3
		remote_xcm: Option<Xcm<()>>,
3
	) -> DispatchResult {
3
		log::debug!(
			target: "xcm::pallet_xcm::execute_xcm_transfer",
			"origin {:?}, dest {:?}, local_xcm {:?}, remote_xcm {:?}",
			origin, dest, local_xcm, remote_xcm,
		);
3
		let weight =
3
			T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?;
3
		let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
3
		let outcome = T::XcmExecutor::prepare_and_execute(
3
			origin.clone(),
3
			local_xcm,
3
			&mut hash,
3
			weight,
3
			weight,
3
		);
3
		Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
3
		outcome.ensure_complete().map_err(|error| {
3
			log::error!(
				target: "xcm::pallet_xcm::execute_xcm_transfer",
3
				"XCM execution failed with error {:?}", error
			);
3
			Error::<T>::LocalExecutionIncomplete
3
		})?;
		if let Some(remote_xcm) = remote_xcm {
			let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
				.map_err(Error::<T>::from)?;
			if origin != Here.into_location() {
				Self::charge_fees(origin.clone(), price).map_err(|error| {
					log::error!(
						target: "xcm::pallet_xcm::execute_xcm_transfer",
						"Unable to charge fee with error {:?}", error
					);
					Error::<T>::FeesNotMet
				})?;
			}
			let message_id = T::XcmRouter::deliver(ticket).map_err(Error::<T>::from)?;
			let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
			Self::deposit_event(e);
		}
		Ok(())
3
	}
3
	fn add_fees_to_xcm(
3
		dest: Location,
3
		fees: FeesHandling<T>,
3
		weight_limit: WeightLimit,
3
		local: &mut Xcm<<T as Config>::RuntimeCall>,
3
		remote: &mut Xcm<()>,
3
	) -> Result<(), Error<T>> {
3
		match fees {
3
			FeesHandling::Batched { fees } => {
3
				let context = T::UniversalLocation::get();
				// no custom fees instructions, they are batched together with `assets` transfer;
				// BuyExecution happens after receiving all `assets`
3
				let reanchored_fees =
3
					fees.reanchored(&dest, &context).map_err(|_| Error::<T>::CannotReanchor)?;
				// buy execution using `fees` batched together with above `reanchored_assets`
3
				remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
			},
			FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
				// fees are handled by separate XCM instructions, prepend fees instructions (for
				// remote XCM they have to be prepended instead of appended to pass barriers).
				core::mem::swap(local, &mut local_fees);
				core::mem::swap(remote, &mut remote_fees);
				// these are now swapped so fees actually go first
				local.inner_mut().append(&mut local_fees.into_inner());
				remote.inner_mut().append(&mut remote_fees.into_inner());
			},
		}
3
		Ok(())
3
	}
	fn local_reserve_fees_instructions(
		origin: Location,
		dest: Location,
		fees: Asset,
		weight_limit: WeightLimit,
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
		let value = (origin, vec![fees.clone()]);
		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
		let context = T::UniversalLocation::get();
		let reanchored_fees = fees
			.clone()
			.reanchored(&dest, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		let local_execute_xcm = Xcm(vec![
			// move `fees` to `dest`s local sovereign account
			TransferAsset { assets: fees.into(), beneficiary: dest },
		]);
		let xcm_on_dest = Xcm(vec![
			// let (dest) chain know `fees` are in its SA on reserve
			ReserveAssetDeposited(reanchored_fees.clone().into()),
			// buy exec using `fees` in holding deposited in above instruction
			BuyExecution { fees: reanchored_fees, weight_limit },
		]);
		Ok((local_execute_xcm, xcm_on_dest))
	}
57
	fn local_reserve_transfer_programs(
57
		origin: Location,
57
		dest: Location,
57
		beneficiary: Either<Location, Xcm<()>>,
57
		assets: Vec<Asset>,
57
		fees: FeesHandling<T>,
57
		weight_limit: WeightLimit,
57
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
57
		let value = (origin, assets);
57
		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
57
		let (_, assets) = value;
		// max assets is `assets` (+ potentially separately handled fee)
57
		let max_assets =
57
			assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
57
		let assets: Assets = assets.into();
57
		let context = T::UniversalLocation::get();
57
		let mut reanchored_assets = assets.clone();
57
		reanchored_assets
57
			.reanchor(&dest, &context)
57
			.map_err(|_| Error::<T>::CannotReanchor)?;
		// XCM instructions to be executed on local chain
3
		let mut local_execute_xcm = Xcm(vec![
3
			// locally move `assets` to `dest`s local sovereign account
3
			TransferAsset { assets, beneficiary: dest.clone() },
3
		]);
3
		// XCM instructions to be executed on destination chain
3
		let mut xcm_on_dest = Xcm(vec![
3
			// let (dest) chain know assets are in its SA on reserve
3
			ReserveAssetDeposited(reanchored_assets),
3
			// following instructions are not exec'ed on behalf of origin chain anymore
3
			ClearOrigin,
3
		]);
3
		// handle fees
3
		Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
3
		let custom_remote_xcm = match beneficiary {
			Either::Right(custom_xcm) => custom_xcm,
3
			Either::Left(beneficiary) => {
3
				// deposit all remaining assets in holding to `beneficiary` location
3
				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
			},
		};
3
		xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
3

            
3
		Ok((local_execute_xcm, xcm_on_dest))
57
	}
	fn destination_reserve_fees_instructions(
		origin: Location,
		dest: Location,
		fees: Asset,
		weight_limit: WeightLimit,
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
		let value = (origin, vec![fees.clone()]);
		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
		let context = T::UniversalLocation::get();
		let reanchored_fees = fees
			.clone()
			.reanchored(&dest, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		let fees: Assets = fees.into();
		let local_execute_xcm = Xcm(vec![
			// withdraw reserve-based fees (derivatives)
			WithdrawAsset(fees.clone()),
			// burn derivatives
			BurnAsset(fees),
		]);
		let xcm_on_dest = Xcm(vec![
			// withdraw `fees` from origin chain's sovereign account
			WithdrawAsset(reanchored_fees.clone().into()),
			// buy exec using `fees` in holding withdrawn in above instruction
			BuyExecution { fees: reanchored_fees, weight_limit },
		]);
		Ok((local_execute_xcm, xcm_on_dest))
	}
	fn destination_reserve_transfer_programs(
		origin: Location,
		dest: Location,
		beneficiary: Either<Location, Xcm<()>>,
		assets: Vec<Asset>,
		fees: FeesHandling<T>,
		weight_limit: WeightLimit,
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
		let value = (origin, assets);
		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
		let (_, assets) = value;
		// max assets is `assets` (+ potentially separately handled fee)
		let max_assets =
			assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
		let assets: Assets = assets.into();
		let context = T::UniversalLocation::get();
		let mut reanchored_assets = assets.clone();
		reanchored_assets
			.reanchor(&dest, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		// XCM instructions to be executed on local chain
		let mut local_execute_xcm = Xcm(vec![
			// withdraw reserve-based assets
			WithdrawAsset(assets.clone()),
			// burn reserve-based assets
			BurnAsset(assets),
		]);
		// XCM instructions to be executed on destination chain
		let mut xcm_on_dest = Xcm(vec![
			// withdraw `assets` from origin chain's sovereign account
			WithdrawAsset(reanchored_assets),
			// following instructions are not exec'ed on behalf of origin chain anymore
			ClearOrigin,
		]);
		// handle fees
		Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
		let custom_remote_xcm = match beneficiary {
			Either::Right(custom_xcm) => custom_xcm,
			Either::Left(beneficiary) => {
				// deposit all remaining assets in holding to `beneficiary` location
				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
			},
		};
		xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
		Ok((local_execute_xcm, xcm_on_dest))
	}
	// function assumes fees and assets have the same remote reserve
	fn remote_reserve_transfer_program(
		origin: Location,
		reserve: Location,
		beneficiary: Either<Location, Xcm<()>>,
		dest: Location,
		assets: Vec<Asset>,
		fees: Asset,
		weight_limit: WeightLimit,
	) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
		let value = (origin, assets);
		ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
		let (_, assets) = value;
		let max_assets = assets.len() as u32;
		let context = T::UniversalLocation::get();
		// we spend up to half of fees for execution on reserve and other half for execution on
		// destination
		let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
		// identifies fee item as seen by `reserve` - to be used at reserve chain
		let reserve_fees = fees_half_1
			.reanchored(&reserve, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		// identifies fee item as seen by `dest` - to be used at destination chain
		let dest_fees = fees_half_2
			.reanchored(&dest, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		// identifies `dest` as seen by `reserve`
		let dest = dest.reanchored(&reserve, &context).map_err(|_| Error::<T>::CannotReanchor)?;
		// xcm to be executed at dest
		let mut xcm_on_dest =
			Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
		let custom_xcm_on_dest = match beneficiary {
			Either::Right(custom_xcm) => custom_xcm,
			Either::Left(beneficiary) => {
				// deposit all remaining assets in holding to `beneficiary` location
				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
			},
		};
		xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
		// xcm to be executed on reserve
		let xcm_on_reserve = Xcm(vec![
			BuyExecution { fees: reserve_fees, weight_limit },
			DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
		]);
		Ok(Xcm(vec![
			WithdrawAsset(assets.into()),
			SetFeesMode { jit_withdraw: true },
			InitiateReserveWithdraw {
				assets: Wild(AllCounted(max_assets)),
				reserve,
				xcm: xcm_on_reserve,
			},
		]))
	}
	fn teleport_fees_instructions(
		origin: Location,
		dest: Location,
		fees: Asset,
		weight_limit: WeightLimit,
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
		let value = (origin, vec![fees.clone()]);
		ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
		let context = T::UniversalLocation::get();
		let reanchored_fees = fees
			.clone()
			.reanchored(&dest, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		// XcmContext irrelevant in teleports checks
		let dummy_context =
			XcmContext { origin: None, message_id: Default::default(), topic: None };
		// We should check that the asset can actually be teleported out (for this to
		// be in error, there would need to be an accounting violation by ourselves,
		// so it's unlikely, but we don't want to allow that kind of bug to leak into
		// a trusted chain.
		<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
			&dest,
			&fees,
			&dummy_context,
		)
		.map_err(|_| Error::<T>::CannotCheckOutTeleport)?;
		// safe to do this here, we're in a transactional call that will be reverted on any
		// errors down the line
		<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
			&dest,
			&fees,
			&dummy_context,
		);
		let fees: Assets = fees.into();
		let local_execute_xcm = Xcm(vec![
			// withdraw fees
			WithdrawAsset(fees.clone()),
			// burn fees
			BurnAsset(fees),
		]);
		let xcm_on_dest = Xcm(vec![
			// (dest) chain receive teleported assets burned on origin chain
			ReceiveTeleportedAsset(reanchored_fees.clone().into()),
			// buy exec using `fees` in holding received in above instruction
			BuyExecution { fees: reanchored_fees, weight_limit },
		]);
		Ok((local_execute_xcm, xcm_on_dest))
	}
	fn teleport_assets_program(
		origin: Location,
		dest: Location,
		beneficiary: Either<Location, Xcm<()>>,
		assets: Vec<Asset>,
		fees: FeesHandling<T>,
		weight_limit: WeightLimit,
	) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
		let value = (origin, assets);
		ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
		let (_, assets) = value;
		// max assets is `assets` (+ potentially separately handled fee)
		let max_assets =
			assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
		let context = T::UniversalLocation::get();
		let assets: Assets = assets.into();
		let mut reanchored_assets = assets.clone();
		reanchored_assets
			.reanchor(&dest, &context)
			.map_err(|_| Error::<T>::CannotReanchor)?;
		// XcmContext irrelevant in teleports checks
		let dummy_context =
			XcmContext { origin: None, message_id: Default::default(), topic: None };
		for asset in assets.inner() {
			// We should check that the asset can actually be teleported out (for this to
			// be in error, there would need to be an accounting violation by ourselves,
			// so it's unlikely, but we don't want to allow that kind of bug to leak into
			// a trusted chain.
			<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
				&dest,
				asset,
				&dummy_context,
			)
			.map_err(|_| Error::<T>::CannotCheckOutTeleport)?;
		}
		for asset in assets.inner() {
			// safe to do this here, we're in a transactional call that will be reverted on any
			// errors down the line
			<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
				&dest,
				asset,
				&dummy_context,
			);
		}
		// XCM instructions to be executed on local chain
		let mut local_execute_xcm = Xcm(vec![
			// withdraw assets to be teleported
			WithdrawAsset(assets.clone()),
			// burn assets on local chain
			BurnAsset(assets),
		]);
		// XCM instructions to be executed on destination chain
		let mut xcm_on_dest = Xcm(vec![
			// teleport `assets` in from origin chain
			ReceiveTeleportedAsset(reanchored_assets),
			// following instructions are not exec'ed on behalf of origin chain anymore
			ClearOrigin,
		]);
		// handle fees
		Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
		// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
		let custom_remote_xcm = match beneficiary {
			Either::Right(custom_xcm) => custom_xcm,
			Either::Left(beneficiary) => {
				// deposit all remaining assets in holding to `beneficiary` location
				Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
			},
		};
		xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
		Ok((local_execute_xcm, xcm_on_dest))
	}
	/// Halve `fees` fungible amount.
	pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
		match fees.fun {
			Fungible(amount) => {
				let fee1 = amount.saturating_div(2);
				let fee2 = amount.saturating_sub(fee1);
				ensure!(fee1 > 0, Error::<T>::FeesNotMet);
				ensure!(fee2 > 0, Error::<T>::FeesNotMet);
				Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
			},
			NonFungible(_) => Err(Error::<T>::FeesNotMet),
		}
	}
	/// Will always make progress, and will do its best not to use much more than `weight_cutoff`
	/// in doing so.
	pub(crate) fn check_xcm_version_change(
		mut stage: VersionMigrationStage,
		weight_cutoff: Weight,
	) -> (Weight, Option<VersionMigrationStage>) {
		let mut weight_used = Weight::zero();
		let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
		let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
		let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
		let vnt_notify_weight = T::WeightInfo::notify_current_targets();
		let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
		let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
		let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
		use VersionMigrationStage::*;
		if stage == MigrateSupportedVersion {
			// We assume that supported XCM version only ever increases, so just cycle through lower
			// XCM versioned from the current.
			for v in 0..XCM_VERSION {
				for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
					if let Ok(new_key) = old_key.into_latest() {
						SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
					}
					weight_used.saturating_accrue(sv_migrate_weight);
					if weight_used.any_gte(weight_cutoff) {
						return (weight_used, Some(stage))
					}
				}
			}
			stage = MigrateVersionNotifiers;
		}
		if stage == MigrateVersionNotifiers {
			for v in 0..XCM_VERSION {
				for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
					if let Ok(new_key) = old_key.into_latest() {
						VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
					}
					weight_used.saturating_accrue(vn_migrate_weight);
					if weight_used.any_gte(weight_cutoff) {
						return (weight_used, Some(stage))
					}
				}
			}
			stage = NotifyCurrentTargets(None);
		}
		let xcm_version = T::AdvertisedXcmVersion::get();
		if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
			let mut iter = match maybe_last_raw_key {
				Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
				None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
			};
			while let Some((key, value)) = iter.next() {
				let (query_id, max_weight, target_xcm_version) = value;
				let new_key: Location = match key.clone().try_into() {
					Ok(k) if target_xcm_version != xcm_version => k,
					_ => {
						// We don't early return here since we need to be certain that we
						// make some progress.
						weight_used.saturating_accrue(vnt_already_notified_weight);
						continue
					},
				};
				let response = Response::Version(xcm_version);
				let message =
					Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
				let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
					Ok((message_id, cost)) => {
						let value = (query_id, max_weight, xcm_version);
						VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
						Event::VersionChangeNotified {
							destination: new_key,
							result: xcm_version,
							cost,
							message_id,
						}
					},
					Err(e) => {
						VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
						Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
					},
				};
				Self::deposit_event(event);
				weight_used.saturating_accrue(vnt_notify_weight);
				if weight_used.any_gte(weight_cutoff) {
					let last = Some(iter.last_raw_key().into());
					return (weight_used, Some(NotifyCurrentTargets(last)))
				}
			}
			stage = MigrateAndNotifyOldTargets;
		}
		if stage == MigrateAndNotifyOldTargets {
			for v in 0..XCM_VERSION {
				for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
					let (query_id, max_weight, target_xcm_version) = value;
					let new_key = match Location::try_from(old_key.clone()) {
						Ok(k) => k,
						Err(()) => {
							Self::deposit_event(Event::NotifyTargetMigrationFail {
								location: old_key,
								query_id: value.0,
							});
							weight_used.saturating_accrue(vnt_migrate_fail_weight);
							if weight_used.any_gte(weight_cutoff) {
								return (weight_used, Some(stage))
							}
							continue
						},
					};
					let versioned_key = LatestVersionedLocation(&new_key);
					if target_xcm_version == xcm_version {
						VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
						weight_used.saturating_accrue(vnt_migrate_weight);
					} else {
						// Need to notify target.
						let response = Response::Version(xcm_version);
						let message = Xcm(vec![QueryResponse {
							query_id,
							response,
							max_weight,
							querier: None,
						}]);
						let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
							Ok((message_id, cost)) => {
								VersionNotifyTargets::<T>::insert(
									XCM_VERSION,
									versioned_key,
									(query_id, max_weight, xcm_version),
								);
								Event::VersionChangeNotified {
									destination: new_key,
									result: xcm_version,
									cost,
									message_id,
								}
							},
							Err(e) => Event::NotifyTargetSendFail {
								location: new_key,
								query_id,
								error: e.into(),
							},
						};
						Self::deposit_event(event);
						weight_used.saturating_accrue(vnt_notify_migrate_weight);
					}
					if weight_used.any_gte(weight_cutoff) {
						return (weight_used, Some(stage))
					}
				}
			}
		}
		(weight_used, None)
	}
	/// Request that `dest` informs us of its version.
261
	pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
261
		let dest = dest.into();
261
		let versioned_dest = VersionedLocation::from(dest.clone());
261
		let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
261
		ensure!(!already, XcmError::InvalidLocation);
276
		let query_id = QueryCounter::<T>::mutate(|q| {
207
			let r = *q;
207
			q.saturating_inc();
207
			r
276
		});
207
		// TODO #3735: Correct weight.
207
		let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
207
		let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
207
		Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
207
		VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
207
		let query_status =
207
			QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
207
		Queries::<T>::insert(query_id, query_status);
207
		Ok(())
261
	}
	/// Request that `dest` ceases informing us of its version.
	pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
		let dest = dest.into();
		let versioned_dest = LatestVersionedLocation(&dest);
		let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
			.ok_or(XcmError::InvalidLocation)?;
		let (message_id, cost) =
			send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
		Self::deposit_event(Event::VersionNotifyUnrequested {
			destination: dest,
			cost,
			message_id,
		});
		Queries::<T>::remove(query_id);
		Ok(())
	}
	/// Relay an XCM `message` from a given `interior` location in this context to a given `dest`
	/// location. The `fee_payer` is charged for the delivery unless `None` in which case fees
	/// are not charged (and instead borne by the chain).
2646
	pub fn send_xcm(
2646
		interior: impl Into<Junctions>,
2646
		dest: impl Into<Location>,
2646
		mut message: Xcm<()>,
2646
	) -> Result<XcmHash, SendError> {
2646
		let interior = interior.into();
2646
		let dest = dest.into();
2646
		let maybe_fee_payer = if interior != Junctions::Here {
2646
			message.0.insert(0, DescendOrigin(interior.clone()));
2646
			Some(interior.into())
		} else {
			None
		};
2646
		log::debug!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message);
2646
		let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
987
		if let Some(fee_payer) = maybe_fee_payer {
987
			Self::charge_fees(fee_payer, price).map_err(|_| SendError::Fees)?;
		}
987
		T::XcmRouter::deliver(ticket)
2646
	}
36
	pub fn check_account() -> T::AccountId {
		const ID: PalletId = PalletId(*b"py/xcmch");
36
		AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
36
	}
	/// Dry-runs `call` with the given `origin`.
	///
	/// Returns not only the call result and events, but also the local XCM, if any,
	/// and any XCMs forwarded to other locations.
	/// Meant to be used in the `xcm_runtime_apis::dry_run::DryRunApi` runtime API.
	pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
		origin: OriginCaller,
		call: RuntimeCall,
	) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
	where
		Runtime: crate::Config,
		Router: InspectMessageQueues,
		RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
		<RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
	{
		crate::Pallet::<Runtime>::set_record_xcm(true);
		frame_system::Pallet::<Runtime>::reset_events(); // To make sure we only record events from current call.
		let result = call.dispatch(origin.into());
		crate::Pallet::<Runtime>::set_record_xcm(false);
		let local_xcm = crate::Pallet::<Runtime>::recorded_xcm();
		let forwarded_xcms = Router::get_messages();
		let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
			frame_system::Pallet::<Runtime>::read_events_no_consensus()
				.map(|record| record.event.clone())
				.collect();
		Ok(CallDryRunEffects {
			local_xcm: local_xcm.map(VersionedXcm::<()>::from),
			forwarded_xcms,
			emitted_events: events,
			execution_result: result,
		})
	}
	/// Dry-runs `xcm` with the given `origin_location`.
	///
	/// Returns execution result, events, and any forwarded XCMs to other locations.
	/// Meant to be used in the `xcm_runtime_apis::dry_run::DryRunApi` runtime API.
	pub fn dry_run_xcm<Runtime, Router, RuntimeCall, XcmConfig>(
		origin_location: VersionedLocation,
		xcm: VersionedXcm<RuntimeCall>,
	) -> Result<XcmDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
	where
		Runtime: frame_system::Config,
		Router: InspectMessageQueues,
		XcmConfig: xcm_executor::Config<RuntimeCall = RuntimeCall>,
	{
		let origin_location: Location = origin_location.try_into().map_err(|error| {
			log::error!(
				target: "xcm::DryRunApi::dry_run_xcm",
				"Location version conversion failed with error: {:?}",
				error,
			);
			XcmDryRunApiError::VersionedConversionFailed
		})?;
		let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
			log::error!(
				target: "xcm::DryRunApi::dry_run_xcm",
				"Xcm version conversion failed with error {:?}",
				error,
			);
			XcmDryRunApiError::VersionedConversionFailed
		})?;
		let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
		frame_system::Pallet::<Runtime>::reset_events(); // To make sure we only record events from current call.
		let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
			origin_location,
			xcm,
			&mut hash,
			Weight::MAX, // Max limit available for execution.
			Weight::zero(),
		);
		let forwarded_xcms = Router::get_messages();
		let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
			frame_system::Pallet::<Runtime>::read_events_no_consensus()
				.map(|record| record.event.clone())
				.collect();
		Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
	}
	/// Given a list of asset ids, returns the correct API response for
	/// `XcmPaymentApi::query_acceptable_payment_assets`.
	///
	/// The assets passed in have to be supported for fee payment.
	pub fn query_acceptable_payment_assets(
		version: xcm::Version,
		asset_ids: Vec<AssetId>,
	) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
		Ok(asset_ids
			.into_iter()
			.map(|asset_id| VersionedAssetId::from(asset_id))
			.filter_map(|asset_id| asset_id.into_version(version).ok())
			.collect())
	}
	pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
		let message = Xcm::<()>::try_from(message)
			.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
		T::Weigher::weight(&mut message.into()).map_err(|()| {
			log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight");
			XcmPaymentApiError::WeightNotComputable
		})
	}
	pub fn query_delivery_fees(
		destination: VersionedLocation,
		message: VersionedXcm<()>,
	) -> Result<VersionedAssets, XcmPaymentApiError> {
		let result_version = destination.identify_version().max(message.identify_version());
		let destination = destination
			.try_into()
			.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
		let message =
			message.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
		let (_, fees) = validate_send::<T::XcmRouter>(destination, message).map_err(|error| {
			log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error);
			XcmPaymentApiError::Unroutable
		})?;
		VersionedAssets::from(fees)
			.into_version(result_version)
			.map_err(|_| XcmPaymentApiError::VersionedConversionFailed)
	}
	/// Create a new expectation of a query response with the querier being here.
	fn do_new_query(
		responder: impl Into<Location>,
		maybe_notify: Option<(u8, u8)>,
		timeout: BlockNumberFor<T>,
		match_querier: impl Into<Location>,
	) -> u64 {
		QueryCounter::<T>::mutate(|q| {
			let r = *q;
			q.saturating_inc();
			Queries::<T>::insert(
				r,
				QueryStatus::Pending {
					responder: responder.into().into(),
					maybe_match_querier: Some(match_querier.into().into()),
					maybe_notify,
					timeout,
				},
			);
			r
		})
	}
	/// Consume `message` and return another which is equivalent to it except that it reports
	/// back the outcome and dispatches `notify` on this chain.
	///
	/// - `message`: The message whose outcome should be reported.
	/// - `responder`: The origin from which a response should be expected.
	/// - `notify`: A dispatchable function which will be called once the outcome of `message` is
	///   known. It may be a dispatchable in any pallet of the local chain, but other than the usual
	///   origin, it must accept exactly two arguments: `query_id: QueryId` and `outcome: Response`,
	///   and in that order. It should expect that the origin is `Origin::Response` and will contain
	///   the responder's location.
	/// - `timeout`: The block number after which it is permissible for `notify` not to be called
	///   even if a response is received.
	///
	/// `report_outcome_notify` may return an error if the `responder` is not invertible.
	///
	/// It is assumed that the querier of the response will be `Here`.
	///
	/// NOTE: `notify` gets called as part of handling an incoming message, so it should be
	/// lightweight. Its weight is estimated during this function and stored ready for
	/// weighing `ReportOutcome` on the way back. If it turns out to be heavier once it returns
	/// then reporting the outcome will fail. Futhermore if the estimate is too high, then it
	/// may be put in the overweight queue and need to be manually executed.
	pub fn report_outcome_notify(
		message: &mut Xcm<()>,
		responder: impl Into<Location>,
		notify: impl Into<<T as Config>::RuntimeCall>,
		timeout: BlockNumberFor<T>,
	) -> Result<(), XcmError> {
		let responder = responder.into();
		let destination = T::UniversalLocation::get()
			.invert_target(&responder)
			.map_err(|()| XcmError::LocationNotInvertible)?;
		let notify: <T as Config>::RuntimeCall = notify.into();
		let max_weight = notify.get_dispatch_info().weight;
		let query_id = Self::new_notify_query(responder, notify, timeout, Here);
		let response_info = QueryResponseInfo { destination, query_id, max_weight };
		let report_error = Xcm(vec![ReportError(response_info)]);
		message.0.insert(0, SetAppendix(report_error));
		Ok(())
	}
	/// Attempt to create a new query ID and register it as a query that is yet to respond, and
	/// which will call a dispatchable when a response happens.
	pub fn new_notify_query(
		responder: impl Into<Location>,
		notify: impl Into<<T as Config>::RuntimeCall>,
		timeout: BlockNumberFor<T>,
		match_querier: impl Into<Location>,
	) -> u64 {
		let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
			"decode input is output of Call encode; Call guaranteed to have two enums; qed",
		);
		Self::do_new_query(responder, Some(notify), timeout, match_querier)
	}
	/// Note that a particular destination to whom we would like to send a message is unknown
	/// and queue it for version discovery.
1995
	fn note_unknown_version(dest: &Location) {
1995
		log::trace!(
			target: "xcm::pallet_xcm::note_unknown_version",
			"XCM version is unknown for destination: {:?}",
			dest,
		);
1995
		let versioned_dest = VersionedLocation::from(dest.clone());
2660
		VersionDiscoveryQueue::<T>::mutate(|q| {
1995
			if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
216
				// exists - just bump the count.
216
				q[index].1.saturating_inc();
1779
			} else {
1779
				let _ = q.try_push((versioned_dest, 1));
1779
			}
2660
		});
1995
	}
	/// Withdraw given `assets` from the given `location` and pay as XCM fees.
	///
	/// Fails if:
	/// - the `assets` are not known on this chain;
	/// - the `assets` cannot be withdrawn with that location as the Origin.
987
	fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
987
		T::XcmExecutor::charge_fees(location.clone(), assets.clone())
987
			.map_err(|_| Error::<T>::FeesNotMet)?;
987
		Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
987
		Ok(())
987
	}
	/// Ensure the correctness of the state of this pallet.
	///
	/// This should be valid before and after each state transition of this pallet.
	///
	/// ## Invariants
	///
	/// All entries stored in the `SupportedVersion` / `VersionNotifiers` / `VersionNotifyTargets`
	/// need to be migrated to the `XCM_VERSION`. If they are not, then `CurrentMigration` has to be
	/// set.
	#[cfg(any(feature = "try-runtime", test))]
53637
	pub fn do_try_state() -> Result<(), TryRuntimeError> {
53637
		// if migration has been already scheduled, everything is ok and data will be eventually
53637
		// migrated
53637
		if CurrentMigration::<T>::exists() {
			return Ok(())
53637
		}
		// if migration has NOT been scheduled yet, we need to check all operational data
268185
		for v in 0..XCM_VERSION {
214548
			ensure!(
214548
				SupportedVersion::<T>::iter_prefix(v).next().is_none(),
				TryRuntimeError::Other(
					"`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
				)
			);
214548
			ensure!(
214548
				VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
				TryRuntimeError::Other(
					"`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
				)
			);
214548
			ensure!(
214548
				VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
				TryRuntimeError::Other(
					"`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
				)
			);
		}
53637
		Ok(())
53637
	}
}
pub struct LockTicket<T: Config> {
	sovereign_account: T::AccountId,
	amount: BalanceOf<T>,
	unlocker: Location,
	item_index: Option<usize>,
}
impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
	fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::UnexpectedState;
		let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
		match self.item_index {
			Some(index) => {
				ensure!(locks.len() > index, UnexpectedState);
				ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
				locks[index].0 = locks[index].0.max(self.amount);
			},
			None => {
				locks
					.try_push((self.amount, self.unlocker.into()))
					.map_err(|(_balance, _location)| UnexpectedState)?;
			},
		}
		LockedFungibles::<T>::insert(&self.sovereign_account, locks);
		T::Currency::extend_lock(
			*b"py/xcmlk",
			&self.sovereign_account,
			self.amount,
			WithdrawReasons::all(),
		);
		Ok(())
	}
}
pub struct UnlockTicket<T: Config> {
	sovereign_account: T::AccountId,
	amount: BalanceOf<T>,
	unlocker: Location,
}
impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
	fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::UnexpectedState;
		let mut locks =
			LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
		let mut maybe_remove_index = None;
		let mut locked = BalanceOf::<T>::zero();
		let mut found = false;
		// We could just as well do with with an into_iter, filter_map and collect, however this way
		// avoids making an allocation.
		for (i, x) in locks.iter_mut().enumerate() {
			if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
				x.0 = x.0.saturating_sub(self.amount);
				if x.0.is_zero() {
					maybe_remove_index = Some(i);
				}
				found = true;
			}
			locked = locked.max(x.0);
		}
		ensure!(found, UnexpectedState);
		if let Some(remove_index) = maybe_remove_index {
			locks.swap_remove(remove_index);
		}
		LockedFungibles::<T>::insert(&self.sovereign_account, locks);
		let reasons = WithdrawReasons::all();
		T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
		Ok(())
	}
}
pub struct ReduceTicket<T: Config> {
	key: (u32, T::AccountId, VersionedAssetId),
	amount: u128,
	locker: VersionedLocation,
	owner: VersionedLocation,
}
impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
	fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::UnexpectedState;
		let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
		ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
		let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
		ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
		if new_amount == 0 {
			RemoteLockedFungibles::<T>::remove(&self.key);
		} else {
			record.amount = new_amount;
			RemoteLockedFungibles::<T>::insert(&self.key, &record);
		}
		Ok(())
	}
}
impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
	type LockTicket = LockTicket<T>;
	type UnlockTicket = UnlockTicket<T>;
	type ReduceTicket = ReduceTicket<T>;
	fn prepare_lock(
		unlocker: Location,
		asset: Asset,
		owner: Location,
	) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::*;
		let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
		let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
		ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
		let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
		let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
		ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
		Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
	}
	fn prepare_unlock(
		unlocker: Location,
		asset: Asset,
		owner: Location,
	) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::*;
		let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
		let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
		ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
		let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
		let item_index =
			locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
		ensure!(locks[item_index].0 >= amount, NotLocked);
		Ok(UnlockTicket { sovereign_account, amount, unlocker })
	}
	fn note_unlockable(
		locker: Location,
		asset: Asset,
		mut owner: Location,
	) -> Result<(), xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::*;
		ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
		let amount = match asset.fun {
			Fungible(a) => a,
			NonFungible(_) => return Err(Unimplemented),
		};
		owner.remove_network_id();
		let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
		let locker = locker.into();
		let owner = owner.into();
		let id: VersionedAssetId = asset.id.into();
		let key = (XCM_VERSION, account, id);
		let mut record =
			RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
		if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
			// Make sure that the new record wouldn't clobber any old data.
			ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
			record.consumers = old.consumers;
			record.amount = record.amount.max(old.amount);
		}
		RemoteLockedFungibles::<T>::insert(&key, record);
		Ok(())
	}
	fn prepare_reduce_unlockable(
		locker: Location,
		asset: Asset,
		mut owner: Location,
	) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
		use xcm_executor::traits::LockError::*;
		let amount = match asset.fun {
			Fungible(a) => a,
			NonFungible(_) => return Err(Unimplemented),
		};
		owner.remove_network_id();
		let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
		let locker = locker.into();
		let owner = owner.into();
		let id: VersionedAssetId = asset.id.into();
		let key = (XCM_VERSION, sovereign_account, id);
		let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
		// Make sure that the record contains what we expect and there's enough to unlock.
		ensure!(locker == record.locker && owner == record.owner, WouldClobber);
		ensure!(record.amount >= amount, NotEnoughLocked);
		ensure!(
			record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
			InUse
		);
		Ok(ReduceTicket { key, amount, locker, owner })
	}
}
impl<T: Config> WrapVersion for Pallet<T> {
1995
	fn wrap_version<RuntimeCall>(
1995
		dest: &Location,
1995
		xcm: impl Into<VersionedXcm<RuntimeCall>>,
1995
	) -> Result<VersionedXcm<RuntimeCall>, ()> {
1995
		Self::get_version_for(dest)
1995
			.or_else(|| {
1995
				Self::note_unknown_version(dest);
1995
				SafeXcmVersion::<T>::get()
1995
			})
1995
			.ok_or_else(|| {
				log::trace!(
					target: "xcm::pallet_xcm::wrap_version",
					"Could not determine a version to wrap XCM for destination: {:?}",
					dest,
				);
				()
1995
			})
1995
			.and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
1995
	}
}
impl<T: Config> GetVersion for Pallet<T> {
1995
	fn get_version_for(dest: &Location) -> Option<XcmVersion> {
1995
		SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
1995
	}
}
impl<T: Config> VersionChangeNotifier for Pallet<T> {
	/// Start notifying `location` should the XCM version of this chain change.
	///
	/// When it does, this type should ensure a `QueryResponse` message is sent with the given
	/// `query_id` & `max_weight` and with a `response` of `Response::Version`. This should happen
	/// until/unless `stop` is called with the correct `query_id`.
	///
	/// If the `location` has an ongoing notification and when this function is called, then an
	/// error should be returned.
339
	fn start(
339
		dest: &Location,
339
		query_id: QueryId,
339
		max_weight: Weight,
339
		_context: &XcmContext,
339
	) -> XcmResult {
339
		let versioned_dest = LatestVersionedLocation(dest);
339
		let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
339
		ensure!(!already, XcmError::InvalidLocation);
339
		let xcm_version = T::AdvertisedXcmVersion::get();
339
		let response = Response::Version(xcm_version);
339
		let instruction = QueryResponse { query_id, response, max_weight, querier: None };
339
		let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
		Self::deposit_event(Event::<T>::VersionNotifyStarted {
			destination: dest.clone(),
			cost,
			message_id,
		});
		let value = (query_id, max_weight, xcm_version);
		VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
		Ok(())
339
	}
	/// Stop notifying `location` should the XCM change. This is a no-op if there was never a
	/// subscription.
486
	fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
486
		VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
486
		Ok(())
486
	}
	/// Return true if a location is subscribed to XCM version changes.
	fn is_subscribed(dest: &Location) -> bool {
		let versioned_dest = LatestVersionedLocation(dest);
		VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
	}
}
impl<T: Config> DropAssets for Pallet<T> {
297
	fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
297
		if assets.is_empty() {
			return Weight::zero()
297
		}
297
		let versioned = VersionedAssets::from(Assets::from(assets));
297
		let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
396
		AssetTraps::<T>::mutate(hash, |n| *n += 1);
297
		Self::deposit_event(Event::AssetsTrapped {
297
			hash,
297
			origin: origin.clone(),
297
			assets: versioned,
297
		});
297
		// TODO #3735: Put the real weight in there.
297
		Weight::zero()
297
	}
}
impl<T: Config> ClaimAssets for Pallet<T> {
315
	fn claim_assets(
315
		origin: &Location,
315
		ticket: &Location,
315
		assets: &Assets,
315
		_context: &XcmContext,
315
	) -> bool {
315
		let mut versioned = VersionedAssets::from(assets.clone());
315
		match ticket.unpack() {
57
			(0, [GeneralIndex(i)]) =>
57
				versioned = match versioned.into_version(*i as u32) {
57
					Ok(v) => v,
					Err(()) => return false,
				},
186
			(0, []) => (),
84
			_ => return false,
		};
231
		let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
231
		match AssetTraps::<T>::get(hash) {
231
			0 => return false,
			1 => AssetTraps::<T>::remove(hash),
			n => AssetTraps::<T>::insert(hash, n - 1),
		}
		Self::deposit_event(Event::AssetsClaimed {
			hash,
			origin: origin.clone(),
			assets: versioned,
		});
		return true
315
	}
}
impl<T: Config> OnResponse for Pallet<T> {
	fn expecting_response(
		origin: &Location,
		query_id: QueryId,
		querier: Option<&Location>,
	) -> bool {
		match Queries::<T>::get(query_id) {
			Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) =>
				Location::try_from(responder).map_or(false, |r| origin == &r) &&
					maybe_match_querier.map_or(true, |match_querier| {
						Location::try_from(match_querier).map_or(false, |match_querier| {
							querier.map_or(false, |q| q == &match_querier)
						})
					}),
			Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
				Location::try_from(r).map_or(false, |r| origin == &r),
			_ => false,
		}
	}
954
	fn on_response(
954
		origin: &Location,
954
		query_id: QueryId,
954
		querier: Option<&Location>,
954
		response: Response,
954
		max_weight: Weight,
954
		_context: &XcmContext,
954
	) -> Weight {
954
		let origin = origin.clone();
954
		match (response, Queries::<T>::get(query_id)) {
			(
				Response::Version(v),
				Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
			) => {
				let origin: Location = match expected_origin.try_into() {
					Ok(o) if o == origin => o,
					Ok(o) => {
						Self::deposit_event(Event::InvalidResponder {
							origin: origin.clone(),
							query_id,
							expected_location: Some(o),
						});
						return Weight::zero()
					},
					_ => {
						Self::deposit_event(Event::InvalidResponder {
							origin: origin.clone(),
							query_id,
							expected_location: None,
						});
						// TODO #3735: Correct weight for this.
						return Weight::zero()
					},
				};
				// TODO #3735: Check max_weight is correct.
				if !is_active {
					Queries::<T>::insert(
						query_id,
						QueryStatus::VersionNotifier {
							origin: origin.clone().into(),
							is_active: true,
						},
					);
				}
				// We're being notified of a version change.
				SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
				Self::deposit_event(Event::SupportedVersionChanged {
					location: origin,
					version: v,
				});
				Weight::zero()
			},
			(
				response,
				Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
			) => {
				if let Some(match_querier) = maybe_match_querier {
					let match_querier = match Location::try_from(match_querier) {
						Ok(mq) => mq,
						Err(_) => {
							Self::deposit_event(Event::InvalidQuerierVersion {
								origin: origin.clone(),
								query_id,
							});
							return Weight::zero()
						},
					};
					if querier.map_or(true, |q| q != &match_querier) {
						Self::deposit_event(Event::InvalidQuerier {
							origin: origin.clone(),
							query_id,
							expected_querier: match_querier,
							maybe_actual_querier: querier.cloned(),
						});
						return Weight::zero()
					}
				}
				let responder = match Location::try_from(responder) {
					Ok(r) => r,
					Err(_) => {
						Self::deposit_event(Event::InvalidResponderVersion {
							origin: origin.clone(),
							query_id,
						});
						return Weight::zero()
					},
				};
				if origin != responder {
					Self::deposit_event(Event::InvalidResponder {
						origin: origin.clone(),
						query_id,
						expected_location: Some(responder),
					});
					return Weight::zero()
				}
				return match maybe_notify {
					Some((pallet_index, call_index)) => {
						// This is a bit horrible, but we happen to know that the `Call` will
						// be built by `(pallet_index: u8, call_index: u8, QueryId, Response)`.
						// So we just encode that and then re-encode to a real Call.
						let bare = (pallet_index, call_index, query_id, response);
						if let Ok(call) = bare.using_encoded(|mut bytes| {
							<T as Config>::RuntimeCall::decode(&mut bytes)
						}) {
							Queries::<T>::remove(query_id);
							let weight = call.get_dispatch_info().weight;
							if weight.any_gt(max_weight) {
								let e = Event::NotifyOverweight {
									query_id,
									pallet_index,
									call_index,
									actual_weight: weight,
									max_budgeted_weight: max_weight,
								};
								Self::deposit_event(e);
								return Weight::zero()
							}
							let dispatch_origin = Origin::Response(origin.clone()).into();
							match call.dispatch(dispatch_origin) {
								Ok(post_info) => {
									let e = Event::Notified { query_id, pallet_index, call_index };
									Self::deposit_event(e);
									post_info.actual_weight
								},
								Err(error_and_info) => {
									let e = Event::NotifyDispatchError {
										query_id,
										pallet_index,
										call_index,
									};
									Self::deposit_event(e);
									// Not much to do with the result as it is. It's up to the
									// parachain to ensure that the message makes sense.
									error_and_info.post_info.actual_weight
								},
							}
							.unwrap_or(weight)
						} else {
							let e =
								Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
							Self::deposit_event(e);
							Weight::zero()
						}
					},
					None => {
						let e = Event::ResponseReady { query_id, response: response.clone() };
						Self::deposit_event(e);
						let at = frame_system::Pallet::<T>::current_block_number();
						let response = response.into();
						Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
						Weight::zero()
					},
				}
			},
			_ => {
954
				let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
954
				Self::deposit_event(e);
954
				Weight::zero()
			},
		}
954
	}
}
impl<T: Config> CheckSuspension for Pallet<T> {
	fn is_suspended<Call>(
		_origin: &Location,
		_instructions: &mut [Instruction<Call>],
		_max_weight: Weight,
		_properties: &mut Properties,
	) -> bool {
		XcmExecutionSuspended::<T>::get()
	}
}
impl<T: Config> RecordXcm for Pallet<T> {
	fn should_record() -> bool {
		ShouldRecordXcm::<T>::get()
	}
	fn set_record_xcm(enabled: bool) {
		ShouldRecordXcm::<T>::put(enabled);
	}
	fn recorded_xcm() -> Option<Xcm<()>> {
		RecordedXcm::<T>::get()
	}
	fn record(xcm: Xcm<()>) {
		RecordedXcm::<T>::put(xcm);
	}
}
/// Ensure that the origin `o` represents an XCM (`Transact`) origin.
///
/// Returns `Ok` with the location of the XCM sender or an `Err` otherwise.
pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
where
	OuterOrigin: Into<Result<Origin, OuterOrigin>>,
{
	match o.into() {
		Ok(Origin::Xcm(location)) => Ok(location),
		_ => Err(BadOrigin),
	}
}
/// Ensure that the origin `o` represents an XCM response origin.
///
/// Returns `Ok` with the location of the responder or an `Err` otherwise.
pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
where
	OuterOrigin: Into<Result<Origin, OuterOrigin>>,
{
	match o.into() {
		Ok(Origin::Response(location)) => Ok(location),
		_ => Err(BadOrigin),
	}
}
/// Filter for `Location` to find those which represent a strict majority approval of an
/// identified plurality.
///
/// May reasonably be used with `EnsureXcm`.
pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
	for IsMajorityOfBody<Prefix, Body>
{
	fn contains(l: &Location) -> bool {
		let maybe_suffix = l.match_and_split(&Prefix::get());
		matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
	}
}
/// Filter for `Location` to find those which represent a voice of an identified plurality.
///
/// May reasonably be used with `EnsureXcm`.
pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
	fn contains(l: &Location) -> bool {
		let maybe_suffix = l.match_and_split(&Prefix::get());
		matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
	}
}
/// `EnsureOrigin` implementation succeeding with a `Location` value to recognize and filter
/// the `Origin::Xcm` item.
pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
impl<
		O: OriginTrait + From<Origin>,
		F: Contains<L>,
		L: TryFrom<Location> + TryInto<Location> + Clone,
	> EnsureOrigin<O> for EnsureXcm<F, L>
where
	O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
{
	type Success = L;
	fn try_origin(outer: O) -> Result<Self::Success, O> {
		outer.try_with_caller(|caller| {
			caller.try_into().and_then(|o| match o {
				Origin::Xcm(ref location)
					if F::contains(&location.clone().try_into().map_err(|_| o.clone().into())?) =>
					Ok(location.clone().try_into().map_err(|_| o.clone().into())?),
				Origin::Xcm(location) => Err(Origin::Xcm(location).into()),
				o => Err(o.into()),
			})
		})
	}
	#[cfg(feature = "runtime-benchmarks")]
	fn try_successful_origin() -> Result<O, ()> {
		Ok(O::from(Origin::Xcm(Here.into())))
	}
}
/// `EnsureOrigin` implementation succeeding with a `Location` value to recognize and filter
/// the `Origin::Response` item.
pub struct EnsureResponse<F>(PhantomData<F>);
impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
where
	O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
{
	type Success = Location;
	fn try_origin(outer: O) -> Result<Self::Success, O> {
		outer.try_with_caller(|caller| {
			caller.try_into().and_then(|o| match o {
				Origin::Response(responder) => Ok(responder),
				o => Err(o.into()),
			})
		})
	}
	#[cfg(feature = "runtime-benchmarks")]
	fn try_successful_origin() -> Result<O, ()> {
		Ok(O::from(Origin::Response(Here.into())))
	}
}
/// A simple passthrough where we reuse the `Location`-typed XCM origin as the inner value of
/// this crate's `Origin::Xcm` value.
pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
	for XcmPassthrough<RuntimeOrigin>
{
	fn convert_origin(
		origin: impl Into<Location>,
		kind: OriginKind,
	) -> Result<RuntimeOrigin, Location> {
		let origin = origin.into();
		match kind {
			OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
			_ => Err(origin),
		}
	}
}