referrerpolicy=no-referrer-when-downgrade

staging_xcm_executor/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17#![cfg_attr(not(feature = "std"), no_std)]
18
19extern crate alloc;
20
21use alloc::{vec, vec::Vec};
22use codec::{Decode, Encode};
23use core::{fmt::Debug, marker::PhantomData};
24use frame_support::{
25	dispatch::GetDispatchInfo,
26	ensure,
27	traits::{Contains, ContainsPair, Defensive, Get, PalletsInfoAccess},
28};
29use sp_core::defer;
30use sp_io::hashing::blake2_128;
31use sp_weights::Weight;
32use xcm::latest::{prelude::*, AssetTransferFilter};
33
34pub mod traits;
35use traits::{
36	validate_export, AssetExchange, AssetLock, CallDispatcher, ClaimAssets, ConvertOrigin,
37	DropAssets, Enact, EventEmitter, ExportXcm, FeeManager, FeeReason, HandleHrmpChannelAccepted,
38	HandleHrmpChannelClosing, HandleHrmpNewChannelOpenRequest, OnResponse, ProcessTransaction,
39	Properties, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader,
40	XcmAssetTransfers,
41};
42
43pub use traits::RecordXcm;
44
45mod assets;
46pub use assets::AssetsInHolding;
47mod config;
48pub use config::Config;
49
50#[cfg(test)]
51mod tests;
52
53/// A struct to specify how fees are being paid.
54#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55pub struct FeesMode {
56	/// If true, then the fee assets are taken directly from the origin's on-chain account,
57	/// otherwise the fee assets are taken from the holding register.
58	///
59	/// Defaults to false.
60	pub jit_withdraw: bool,
61}
62
63/// The maximum recursion depth allowed when executing nested XCM instructions.
64///
65/// Exceeding this limit results in `XcmError::ExceedsStackLimit` or
66/// `ProcessMessageError::StackLimitReached`.
67///
68/// Also used in the `DenyRecursively` barrier.
69pub const RECURSION_LIMIT: u8 = 10;
70
71environmental::environmental!(recursion_count: u8);
72
73/// The XCM executor.
74pub struct XcmExecutor<Config: config::Config> {
75	holding: AssetsInHolding,
76	holding_limit: usize,
77	context: XcmContext,
78	original_origin: Location,
79	trader: Config::Trader,
80	/// The most recent error result and instruction index into the fragment in which it occurred,
81	/// if any.
82	error: Option<(u32, XcmError)>,
83	/// The surplus weight, defined as the amount by which `max_weight` is
84	/// an over-estimate of the actual weight consumed. We do it this way to avoid needing the
85	/// execution engine to keep track of all instructions' weights (it only needs to care about
86	/// the weight of dynamically determined instructions such as `Transact`).
87	total_surplus: Weight,
88	total_refunded: Weight,
89	error_handler: Xcm<Config::RuntimeCall>,
90	error_handler_weight: Weight,
91	appendix: Xcm<Config::RuntimeCall>,
92	appendix_weight: Weight,
93	transact_status: MaybeErrorCode,
94	fees_mode: FeesMode,
95	fees: AssetsInHolding,
96	/// Asset provided in last `BuyExecution` instruction (if any) in current XCM program. Same
97	/// asset type will be used for paying any potential delivery fees incurred by the program.
98	asset_used_in_buy_execution: Option<AssetId>,
99	/// Stores the current message's weight.
100	message_weight: Weight,
101	asset_claimer: Option<Location>,
102	already_paid_fees: bool,
103	_config: PhantomData<Config>,
104}
105
106#[cfg(any(test, feature = "runtime-benchmarks"))]
107impl<Config: config::Config> XcmExecutor<Config> {
108	pub fn holding(&self) -> &AssetsInHolding {
109		&self.holding
110	}
111	pub fn set_holding(&mut self, v: AssetsInHolding) {
112		self.holding = v
113	}
114	pub fn holding_limit(&self) -> &usize {
115		&self.holding_limit
116	}
117	pub fn set_holding_limit(&mut self, v: usize) {
118		self.holding_limit = v
119	}
120	pub fn origin(&self) -> &Option<Location> {
121		&self.context.origin
122	}
123	pub fn set_origin(&mut self, v: Option<Location>) {
124		self.context.origin = v
125	}
126	pub fn original_origin(&self) -> &Location {
127		&self.original_origin
128	}
129	pub fn set_original_origin(&mut self, v: Location) {
130		self.original_origin = v
131	}
132	pub fn trader(&self) -> &Config::Trader {
133		&self.trader
134	}
135	pub fn set_trader(&mut self, v: Config::Trader) {
136		self.trader = v
137	}
138	pub fn error(&self) -> &Option<(u32, XcmError)> {
139		&self.error
140	}
141	pub fn set_error(&mut self, v: Option<(u32, XcmError)>) {
142		self.error = v
143	}
144	pub fn total_surplus(&self) -> &Weight {
145		&self.total_surplus
146	}
147	pub fn set_total_surplus(&mut self, v: Weight) {
148		self.total_surplus = v
149	}
150	pub fn total_refunded(&self) -> &Weight {
151		&self.total_refunded
152	}
153	pub fn set_total_refunded(&mut self, v: Weight) {
154		self.total_refunded = v
155	}
156	pub fn error_handler(&self) -> &Xcm<Config::RuntimeCall> {
157		&self.error_handler
158	}
159	pub fn set_error_handler(&mut self, v: Xcm<Config::RuntimeCall>) {
160		self.error_handler = v
161	}
162	pub fn error_handler_weight(&self) -> &Weight {
163		&self.error_handler_weight
164	}
165	pub fn set_error_handler_weight(&mut self, v: Weight) {
166		self.error_handler_weight = v
167	}
168	pub fn appendix(&self) -> &Xcm<Config::RuntimeCall> {
169		&self.appendix
170	}
171	pub fn set_appendix(&mut self, v: Xcm<Config::RuntimeCall>) {
172		self.appendix = v
173	}
174	pub fn appendix_weight(&self) -> &Weight {
175		&self.appendix_weight
176	}
177	pub fn set_appendix_weight(&mut self, v: Weight) {
178		self.appendix_weight = v
179	}
180	pub fn transact_status(&self) -> &MaybeErrorCode {
181		&self.transact_status
182	}
183	pub fn set_transact_status(&mut self, v: MaybeErrorCode) {
184		self.transact_status = v
185	}
186	pub fn fees_mode(&self) -> &FeesMode {
187		&self.fees_mode
188	}
189	pub fn set_fees_mode(&mut self, v: FeesMode) {
190		self.fees_mode = v
191	}
192	pub fn fees(&self) -> &AssetsInHolding {
193		&self.fees
194	}
195	pub fn set_fees(&mut self, value: AssetsInHolding) {
196		self.fees = value;
197	}
198	pub fn topic(&self) -> &Option<[u8; 32]> {
199		&self.context.topic
200	}
201	pub fn set_topic(&mut self, v: Option<[u8; 32]>) {
202		self.context.topic = v;
203	}
204	pub fn asset_claimer(&self) -> Option<Location> {
205		self.asset_claimer.clone()
206	}
207	pub fn set_message_weight(&mut self, weight: Weight) {
208		self.message_weight = weight;
209	}
210	pub fn already_paid_fees(&self) -> bool {
211		self.already_paid_fees
212	}
213}
214
215pub struct WeighedMessage<Call>(Weight, Xcm<Call>);
216impl<C> PreparedMessage for WeighedMessage<C> {
217	fn weight_of(&self) -> Weight {
218		self.0
219	}
220}
221
222#[cfg(any(test, feature = "std"))]
223impl<C> WeighedMessage<C> {
224	pub fn new(weight: Weight, message: Xcm<C>) -> Self {
225		Self(weight, message)
226	}
227}
228
229impl<Config: config::Config> ExecuteXcm<Config::RuntimeCall> for XcmExecutor<Config> {
230	type Prepared = WeighedMessage<Config::RuntimeCall>;
231	fn prepare(
232		mut message: Xcm<Config::RuntimeCall>,
233		weight_limit: Weight,
234	) -> Result<Self::Prepared, InstructionError> {
235		match Config::Weigher::weight(&mut message, weight_limit) {
236			Ok(weight) => Ok(WeighedMessage(weight, message)),
237			Err(error) => {
238				tracing::debug!(
239					target: "xcm::prepare",
240					?error,
241					?message,
242					"Failed to calculate weight for XCM message; execution aborted"
243				);
244				Err(error)
245			},
246		}
247	}
248	fn execute(
249		origin: impl Into<Location>,
250		WeighedMessage(xcm_weight, mut message): WeighedMessage<Config::RuntimeCall>,
251		id: &mut XcmHash,
252		weight_credit: Weight,
253	) -> Outcome {
254		let origin = origin.into();
255		tracing::trace!(
256			target: "xcm::execute",
257			?origin,
258			?message,
259			?id,
260			?weight_credit,
261			"Executing message",
262		);
263		let mut properties = Properties { weight_credit, message_id: None };
264
265		// We only want to record under certain conditions (mainly only during dry-running),
266		// so as to not degrade regular performance.
267		if Config::XcmRecorder::should_record() {
268			Config::XcmRecorder::record(message.clone().into());
269		}
270
271		if let Err(e) = Config::Barrier::should_execute(
272			&origin,
273			message.inner_mut(),
274			xcm_weight,
275			&mut properties,
276		) {
277			tracing::trace!(
278				target: "xcm::execute",
279				?origin,
280				?message,
281				?properties,
282				error = ?e,
283				"Barrier blocked execution",
284			);
285
286			return Outcome::Incomplete {
287				used: xcm_weight, // Weight consumed before the error
288				error: InstructionError { index: 0, error: XcmError::Barrier }, // The error that occurred
289			};
290		}
291
292		*id = properties.message_id.unwrap_or(*id);
293
294		let mut vm = Self::new(origin, *id);
295		vm.message_weight = xcm_weight;
296
297		while !message.0.is_empty() {
298			let result = vm.process(message);
299			tracing::trace!(target: "xcm::execute", ?result, "Message executed");
300			message = if let Err(error) = result {
301				vm.total_surplus.saturating_accrue(error.weight);
302				vm.error = Some((error.index, error.xcm_error));
303				vm.take_error_handler().or_else(|| vm.take_appendix())
304			} else {
305				vm.drop_error_handler();
306				vm.take_appendix()
307			}
308		}
309
310		vm.post_process(xcm_weight)
311	}
312
313	fn charge_fees(origin: impl Into<Location>, fees: Assets) -> XcmResult {
314		let origin = origin.into();
315		if !Config::FeeManager::is_waived(Some(&origin), FeeReason::ChargeFees) {
316			for asset in fees.inner() {
317				Config::AssetTransactor::withdraw_asset(&asset, &origin, None)?;
318			}
319			Config::FeeManager::handle_fee(fees.into(), None, FeeReason::ChargeFees);
320		}
321		Ok(())
322	}
323}
324
325impl<Config: config::Config> XcmAssetTransfers for XcmExecutor<Config> {
326	type IsReserve = Config::IsReserve;
327	type IsTeleporter = Config::IsTeleporter;
328	type AssetTransactor = Config::AssetTransactor;
329}
330
331impl<Config: config::Config> FeeManager for XcmExecutor<Config> {
332	fn is_waived(origin: Option<&Location>, r: FeeReason) -> bool {
333		Config::FeeManager::is_waived(origin, r)
334	}
335
336	fn handle_fee(fee: Assets, context: Option<&XcmContext>, r: FeeReason) {
337		Config::FeeManager::handle_fee(fee, context, r)
338	}
339}
340
341#[derive(Debug, PartialEq)]
342pub struct ExecutorError {
343	pub index: u32,
344	pub xcm_error: XcmError,
345	pub weight: Weight,
346}
347
348#[cfg(feature = "runtime-benchmarks")]
349impl From<ExecutorError> for frame_benchmarking::BenchmarkError {
350	fn from(error: ExecutorError) -> Self {
351		tracing::error!(
352			index = ?error.index,
353			xcm_error = ?error.xcm_error,
354			weight = ?error.weight,
355			"XCM ERROR",
356		);
357		Self::Stop("xcm executor error: see error logs")
358	}
359}
360
361impl<Config: config::Config> XcmExecutor<Config> {
362	pub fn new(origin: impl Into<Location>, message_id: XcmHash) -> Self {
363		let origin = origin.into();
364		Self {
365			holding: AssetsInHolding::new(),
366			holding_limit: Config::MaxAssetsIntoHolding::get() as usize,
367			context: XcmContext { origin: Some(origin.clone()), message_id, topic: None },
368			original_origin: origin,
369			trader: Config::Trader::new(),
370			error: None,
371			total_surplus: Weight::zero(),
372			total_refunded: Weight::zero(),
373			error_handler: Xcm(vec![]),
374			error_handler_weight: Weight::zero(),
375			appendix: Xcm(vec![]),
376			appendix_weight: Weight::zero(),
377			transact_status: Default::default(),
378			fees_mode: FeesMode { jit_withdraw: false },
379			fees: AssetsInHolding::new(),
380			asset_used_in_buy_execution: None,
381			message_weight: Weight::zero(),
382			asset_claimer: None,
383			already_paid_fees: false,
384			_config: PhantomData,
385		}
386	}
387
388	/// Execute any final operations after having executed the XCM message.
389	/// This includes refunding surplus weight, trapping extra holding funds, and returning any
390	/// errors during execution.
391	pub fn post_process(mut self, xcm_weight: Weight) -> Outcome {
392		// We silently drop any error from our attempt to refund the surplus as it's a charitable
393		// thing so best-effort is all we will do.
394		let _ = self.refund_surplus();
395		drop(self.trader);
396
397		let mut weight_used = xcm_weight.saturating_sub(self.total_surplus);
398
399		if !self.holding.is_empty() {
400			tracing::trace!(
401				target: "xcm::post_process",
402				holding_register = ?self.holding,
403				context = ?self.context,
404				original_origin = ?self.original_origin,
405				"Trapping assets in holding register",
406			);
407			let claimer = if let Some(asset_claimer) = self.asset_claimer.as_ref() {
408				asset_claimer
409			} else {
410				self.context.origin.as_ref().unwrap_or(&self.original_origin)
411			};
412			let trap_weight = Config::AssetTrap::drop_assets(claimer, self.holding, &self.context);
413			weight_used.saturating_accrue(trap_weight);
414		};
415
416		match self.error {
417			None => Outcome::Complete { used: weight_used },
418			// TODO: #2841 #REALWEIGHT We should deduct the cost of any instructions following
419			// the error which didn't end up being executed.
420			Some((index, error)) => {
421				tracing::trace!(
422					target: "xcm::post_process",
423					instruction = ?index,
424					?error,
425					original_origin = ?self.original_origin,
426					"Execution failed",
427				);
428				Outcome::Incomplete {
429					used: weight_used,
430					error: InstructionError { index: index.try_into().unwrap_or(u8::MAX), error },
431				}
432			},
433		}
434	}
435
436	fn origin_ref(&self) -> Option<&Location> {
437		self.context.origin.as_ref()
438	}
439
440	fn cloned_origin(&self) -> Option<Location> {
441		self.context.origin.clone()
442	}
443
444	/// Send an XCM, charging fees from Holding as needed.
445	fn send(
446		&mut self,
447		dest: Location,
448		msg: Xcm<()>,
449		reason: FeeReason,
450	) -> Result<XcmHash, XcmError> {
451		let mut msg = msg;
452		// Only the last `SetTopic` instruction is considered relevant. If the message does not end
453		// with it, a `topic_or_message_id()` from the context is appended to it. This behaviour is
454		// then consistent with `WithUniqueTopic`.
455		if !matches!(msg.last(), Some(SetTopic(_))) {
456			let topic_id = self.context.topic_or_message_id();
457			msg.0.push(SetTopic(topic_id.into()));
458		}
459		tracing::trace!(
460			target: "xcm::send",
461			?msg,
462			destination = ?dest,
463			reason = ?reason,
464			"Sending msg",
465		);
466		let (ticket, fee) = validate_send::<Config::XcmSender>(dest.clone(), msg)?;
467		self.take_fee(fee, reason)?;
468		match Config::XcmSender::deliver(ticket) {
469			Ok(message_id) => {
470				Config::XcmEventEmitter::emit_sent_event(
471					self.original_origin.clone(),
472					dest,
473					None, /* Avoid logging the full XCM message to prevent inconsistencies and
474					       * reduce storage usage. */
475					message_id,
476				);
477				Ok(message_id)
478			},
479			Err(error) => {
480				tracing::debug!(target: "xcm::send", ?error, "XCM failed to deliver with error");
481				Config::XcmEventEmitter::emit_send_failure_event(
482					self.original_origin.clone(),
483					dest,
484					error.clone(),
485					self.context.topic_or_message_id(),
486				);
487				Err(error.into())
488			},
489		}
490	}
491
492	/// Remove the registered error handler and return it. Do not refund its weight.
493	fn take_error_handler(&mut self) -> Xcm<Config::RuntimeCall> {
494		let mut r = Xcm::<Config::RuntimeCall>(vec![]);
495		core::mem::swap(&mut self.error_handler, &mut r);
496		self.error_handler_weight = Weight::zero();
497		r
498	}
499
500	/// Drop the registered error handler and refund its weight.
501	fn drop_error_handler(&mut self) {
502		self.error_handler = Xcm::<Config::RuntimeCall>(vec![]);
503		self.total_surplus.saturating_accrue(self.error_handler_weight);
504		self.error_handler_weight = Weight::zero();
505	}
506
507	/// Remove the registered appendix and return it.
508	fn take_appendix(&mut self) -> Xcm<Config::RuntimeCall> {
509		let mut r = Xcm::<Config::RuntimeCall>(vec![]);
510		core::mem::swap(&mut self.appendix, &mut r);
511		self.appendix_weight = Weight::zero();
512		r
513	}
514
515	fn ensure_can_subsume_assets(&self, assets_length: usize) -> Result<(), XcmError> {
516		// worst-case, holding.len becomes 2 * holding_limit.
517		// this guarantees that if holding.len() == holding_limit and you have more than
518		// `holding_limit` items (which has a best case outcome of holding.len() == holding_limit),
519		// then the operation is guaranteed to succeed.
520		let worst_case_holding_len = self.holding.len() + assets_length;
521		tracing::trace!(
522			target: "xcm::ensure_can_subsume_assets",
523			?worst_case_holding_len,
524			holding_limit = ?self.holding_limit,
525			"Ensuring subsume assets work",
526		);
527		ensure!(worst_case_holding_len <= self.holding_limit * 2, XcmError::HoldingWouldOverflow);
528		Ok(())
529	}
530
531	/// Refund any unused weight.
532	fn refund_surplus(&mut self) -> Result<(), XcmError> {
533		let current_surplus = self.total_surplus.saturating_sub(self.total_refunded);
534		tracing::trace!(
535			target: "xcm::refund_surplus",
536			total_surplus = ?self.total_surplus,
537			total_refunded = ?self.total_refunded,
538			?current_surplus,
539			"Refunding surplus",
540		);
541		if current_surplus.any_gt(Weight::zero()) {
542			if let Some(w) = self.trader.refund_weight(current_surplus, &self.context) {
543				if !self.holding.contains_asset(&(w.id.clone(), 1).into()) &&
544					self.ensure_can_subsume_assets(1).is_err()
545				{
546					let _ = self
547						.trader
548						.buy_weight(current_surplus, w.into(), &self.context)
549						.defensive_proof(
550							"refund_weight returned an asset capable of buying weight; qed",
551						);
552					tracing::error!(
553						target: "xcm::refund_surplus",
554						"error: HoldingWouldOverflow",
555					);
556					return Err(XcmError::HoldingWouldOverflow);
557				}
558				self.total_refunded.saturating_accrue(current_surplus);
559				self.holding.subsume_assets(w.into());
560			}
561		}
562		// If there are any leftover `fees`, merge them with `holding`.
563		if !self.fees.is_empty() {
564			let leftover_fees = self.fees.saturating_take(Wild(All));
565			tracing::trace!(
566				target: "xcm::refund_surplus",
567				?leftover_fees,
568			);
569			self.holding.subsume_assets(leftover_fees);
570		}
571		tracing::trace!(
572			target: "xcm::refund_surplus",
573			total_refunded = ?self.total_refunded,
574		);
575		Ok(())
576	}
577
578	fn take_fee(&mut self, fees: Assets, reason: FeeReason) -> XcmResult {
579		if Config::FeeManager::is_waived(self.origin_ref(), reason.clone()) {
580			return Ok(());
581		}
582		tracing::trace!(
583			target: "xcm::fees",
584			?fees,
585			origin_ref = ?self.origin_ref(),
586			fees_mode = ?self.fees_mode,
587			?reason,
588			"Taking fees",
589		);
590		// We only ever use the first asset from `fees`.
591		let asset_needed_for_fees = match fees.get(0) {
592			Some(fee) => fee,
593			None => return Ok(()), // No delivery fees need to be paid.
594		};
595		// If `BuyExecution` or `PayFees` was called, we use that asset for delivery fees as well.
596		let asset_to_pay_for_fees =
597			self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone());
598		tracing::trace!(target: "xcm::fees", ?asset_to_pay_for_fees);
599		// We withdraw or take from holding the asset the user wants to use for fee payment.
600		let withdrawn_fee_asset: AssetsInHolding = if self.fees_mode.jit_withdraw {
601			let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
602			Config::AssetTransactor::withdraw_asset(
603				&asset_to_pay_for_fees,
604				origin,
605				Some(&self.context),
606			)?;
607			tracing::trace!(target: "xcm::fees", ?asset_needed_for_fees);
608			asset_to_pay_for_fees.clone().into()
609		} else {
610			// This condition exists to support `BuyExecution` while the ecosystem
611			// transitions to `PayFees`.
612			let assets_to_pay_delivery_fees: AssetsInHolding = if self.fees.is_empty() {
613				// Means `BuyExecution` was used, we'll find the fees in the `holding` register.
614				self.holding
615					.try_take(asset_to_pay_for_fees.clone().into())
616					.map_err(|e| {
617						tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees,
618							"Holding doesn't hold enough for fees");
619						XcmError::NotHoldingFees
620					})?
621					.into()
622			} else {
623				// Means `PayFees` was used, we'll find the fees in the `fees` register.
624				self.fees
625					.try_take(asset_to_pay_for_fees.clone().into())
626					.map_err(|e| {
627						tracing::error!(target: "xcm::fees", ?e, ?asset_to_pay_for_fees,
628							"Fees register doesn't hold enough for fees");
629						XcmError::NotHoldingFees
630					})?
631					.into()
632			};
633			tracing::trace!(target: "xcm::fees", ?assets_to_pay_delivery_fees);
634			let mut iter = assets_to_pay_delivery_fees.fungible_assets_iter();
635			let asset = iter.next().ok_or(XcmError::NotHoldingFees)?;
636			asset.into()
637		};
638		// We perform the swap, if needed, to pay fees.
639		let paid = if asset_to_pay_for_fees.id != asset_needed_for_fees.id {
640			let swapped_asset: Assets = Config::AssetExchanger::exchange_asset(
641				self.origin_ref(),
642				withdrawn_fee_asset.clone().into(),
643				&asset_needed_for_fees.clone().into(),
644				false,
645			)
646			.map_err(|given_assets| {
647				tracing::error!(
648					target: "xcm::fees",
649					?given_assets, "Swap was deemed necessary but couldn't be done for withdrawn_fee_asset: {:?} and asset_needed_for_fees: {:?}", withdrawn_fee_asset.clone(), asset_needed_for_fees,
650				);
651				XcmError::FeesNotMet
652			})?
653			.into();
654			swapped_asset
655		} else {
656			// If the asset wanted to pay for fees is the one that was needed,
657			// we don't need to do any swap.
658			// We just use the assets withdrawn or taken from holding.
659			withdrawn_fee_asset.into()
660		};
661		Config::FeeManager::handle_fee(paid, Some(&self.context), reason);
662		Ok(())
663	}
664
665	/// Calculates the amount of asset used in `PayFees` or `BuyExecution` that would be
666	/// charged for swapping to `asset_needed_for_fees`.
667	///
668	/// The calculation is done by `Config::AssetExchanger`.
669	/// If neither `PayFees` or `BuyExecution` were used, or no swap is required,
670	/// it will just return `asset_needed_for_fees`.
671	fn calculate_asset_for_delivery_fees(&self, asset_needed_for_fees: Asset) -> Asset {
672		let Some(asset_wanted_for_fees) =
673			// we try to swap first asset in the fees register (should only ever be one),
674			self.fees.fungible.first_key_value().map(|(id, _)| id).or_else(|| {
675				// or the one used in BuyExecution
676				self.asset_used_in_buy_execution.as_ref()
677			})
678			// if it is different than what we need
679			.filter(|&id| asset_needed_for_fees.id.ne(id))
680		else {
681			// either nothing to swap or we're already holding the right asset
682			return asset_needed_for_fees
683		};
684		Config::AssetExchanger::quote_exchange_price(
685			&(asset_wanted_for_fees.clone(), Fungible(0)).into(),
686			&asset_needed_for_fees.clone().into(),
687			false, // Minimal.
688		)
689		.and_then(|necessary_assets| {
690			// We only use the first asset for fees.
691			// If this is not enough to swap for the fee asset then it will error later down
692			// the line.
693			necessary_assets.into_inner().into_iter().next()
694		})
695		.unwrap_or_else(|| {
696			// If we can't convert, then we return the original asset.
697			// It will error later in any case.
698			tracing::trace!(
699				target: "xcm::calculate_asset_for_delivery_fees",
700				?asset_wanted_for_fees, "Could not convert fees",
701			);
702			asset_needed_for_fees
703		})
704	}
705
706	/// Calculates what `local_querier` would be from the perspective of `destination`.
707	fn to_querier(
708		local_querier: Option<Location>,
709		destination: &Location,
710	) -> Result<Option<Location>, XcmError> {
711		Ok(match local_querier {
712			None => None,
713			Some(q) => Some(
714				q.reanchored(&destination, &Config::UniversalLocation::get()).map_err(|e| {
715					tracing::error!(target: "xcm::xcm_executor::to_querier", ?e, ?destination, "Failed to re-anchor local_querier");
716					XcmError::ReanchorFailed
717				})?,
718			),
719		})
720	}
721
722	/// Send a bare `QueryResponse` message containing `response` informed by the given `info`.
723	///
724	/// The `local_querier` argument is the querier (if any) specified from the *local* perspective.
725	fn respond(
726		&mut self,
727		local_querier: Option<Location>,
728		response: Response,
729		info: QueryResponseInfo,
730		fee_reason: FeeReason,
731	) -> Result<XcmHash, XcmError> {
732		let querier = Self::to_querier(local_querier, &info.destination)?;
733		let QueryResponseInfo { destination, query_id, max_weight } = info;
734		let instruction = QueryResponse { query_id, response, max_weight, querier };
735		let message = Xcm(vec![instruction]);
736		self.send(destination, message, fee_reason)
737	}
738
739	fn do_reserve_deposit_assets(
740		assets: AssetsInHolding,
741		dest: &Location,
742		remote_xcm: &mut Vec<Instruction<()>>,
743		context: Option<&XcmContext>,
744	) -> Result<Assets, XcmError> {
745		Self::deposit_assets_with_retry(&assets, dest, context)?;
746		// Note that we pass `None` as `maybe_failed_bin` and drop any assets which
747		// cannot be reanchored, because we have already called `deposit_asset` on
748		// all assets.
749		let reanchored_assets = Self::reanchored(assets, dest, None);
750		remote_xcm.push(ReserveAssetDeposited(reanchored_assets.clone()));
751
752		Ok(reanchored_assets)
753	}
754
755	fn do_reserve_withdraw_assets(
756		assets: AssetsInHolding,
757		failed_bin: &mut AssetsInHolding,
758		reserve: &Location,
759		remote_xcm: &mut Vec<Instruction<()>>,
760	) -> Result<Assets, XcmError> {
761		// Must ensure that we recognise the assets as being managed by the destination.
762		#[cfg(not(any(test, feature = "runtime-benchmarks")))]
763		for asset in assets.assets_iter() {
764			ensure!(
765				Config::IsReserve::contains(&asset, &reserve),
766				XcmError::UntrustedReserveLocation
767			);
768		}
769		// Note that here we are able to place any assets which could not be
770		// reanchored back into Holding.
771		let reanchored_assets = Self::reanchored(assets, reserve, Some(failed_bin));
772		remote_xcm.push(WithdrawAsset(reanchored_assets.clone()));
773
774		Ok(reanchored_assets)
775	}
776
777	fn do_teleport_assets(
778		assets: AssetsInHolding,
779		dest: &Location,
780		remote_xcm: &mut Vec<Instruction<()>>,
781		context: &XcmContext,
782	) -> Result<Assets, XcmError> {
783		for asset in assets.assets_iter() {
784			// Must ensure that we have teleport trust with destination for these assets.
785			#[cfg(not(any(test, feature = "runtime-benchmarks")))]
786			ensure!(
787				Config::IsTeleporter::contains(&asset, &dest),
788				XcmError::UntrustedTeleportLocation
789			);
790			// We should check that the asset can actually be teleported out (for
791			// this to be in error, there would need to be an accounting violation
792			// by ourselves, so it's unlikely, but we don't want to allow that kind
793			// of bug to leak into a trusted chain.
794			Config::AssetTransactor::can_check_out(dest, &asset, context)?;
795		}
796		for asset in assets.assets_iter() {
797			Config::AssetTransactor::check_out(dest, &asset, context);
798		}
799		// Note that we pass `None` as `maybe_failed_bin` and drop any assets which
800		// cannot be reanchored, because we have already checked all assets out.
801		let reanchored_assets = Self::reanchored(assets, dest, None);
802		remote_xcm.push(ReceiveTeleportedAsset(reanchored_assets.clone()));
803
804		Ok(reanchored_assets)
805	}
806
807	fn try_reanchor<T: Reanchorable>(
808		reanchorable: T,
809		destination: &Location,
810	) -> Result<(T, InteriorLocation), XcmError> {
811		let reanchor_context = Config::UniversalLocation::get();
812		let reanchored =
813			reanchorable.reanchored(&destination, &reanchor_context).map_err(|error| {
814				tracing::error!(target: "xcm::reanchor", ?error, ?destination, ?reanchor_context, "Failed reanchoring with error.");
815				XcmError::ReanchorFailed
816			})?;
817		Ok((reanchored, reanchor_context))
818	}
819
820	/// NOTE: Any assets which were unable to be reanchored are introduced into `failed_bin`.
821	fn reanchored(
822		mut assets: AssetsInHolding,
823		dest: &Location,
824		maybe_failed_bin: Option<&mut AssetsInHolding>,
825	) -> Assets {
826		let reanchor_context = Config::UniversalLocation::get();
827		assets.reanchor(dest, &reanchor_context, maybe_failed_bin);
828		assets.into_assets_iter().collect::<Vec<_>>().into()
829	}
830
831	#[cfg(any(test, feature = "runtime-benchmarks"))]
832	pub fn bench_process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
833		self.process(xcm)
834	}
835
836	#[cfg(any(test, feature = "runtime-benchmarks"))]
837	pub fn bench_post_process(self, xcm_weight: Weight) -> Outcome {
838		self.post_process(xcm_weight)
839	}
840
841	fn process(&mut self, xcm: Xcm<Config::RuntimeCall>) -> Result<(), ExecutorError> {
842		tracing::trace!(
843			target: "xcm::process",
844			origin = ?self.origin_ref(),
845			total_surplus = ?self.total_surplus,
846			total_refunded = ?self.total_refunded,
847			error_handler_weight = ?self.error_handler_weight,
848		);
849		let mut result = Ok(());
850		for (i, mut instr) in xcm.0.into_iter().enumerate() {
851			match &mut result {
852				r @ Ok(()) => {
853					// Initialize the recursion count only the first time we hit this code in our
854					// potential recursive execution.
855					let inst_res = recursion_count::using_once(&mut 1, || {
856						recursion_count::with(|count| {
857							if *count > RECURSION_LIMIT {
858								return None;
859							}
860							*count = count.saturating_add(1);
861							Some(())
862						})
863						.flatten()
864						.ok_or(XcmError::ExceedsStackLimit)?;
865
866						// Ensure that we always decrement the counter whenever we finish processing
867						// the instruction.
868						defer! {
869							recursion_count::with(|count| {
870								*count = count.saturating_sub(1);
871							});
872						}
873
874						self.process_instruction(instr)
875					});
876					if let Err(error) = inst_res {
877						tracing::debug!(
878							target: "xcm::process",
879							?error, "XCM execution failed at instruction index={i}"
880						);
881						Config::XcmEventEmitter::emit_process_failure_event(
882							self.original_origin.clone(),
883							error,
884							self.context.topic_or_message_id(),
885						);
886						*r = Err(ExecutorError {
887							index: i as u32,
888							xcm_error: error,
889							weight: Weight::zero(),
890						});
891					}
892				},
893				Err(ref mut error) =>
894					if let Ok(x) = Config::Weigher::instr_weight(&mut instr) {
895						error.weight.saturating_accrue(x)
896					},
897			}
898		}
899		result
900	}
901
902	/// Process a single XCM instruction, mutating the state of the XCM virtual machine.
903	fn process_instruction(
904		&mut self,
905		instr: Instruction<Config::RuntimeCall>,
906	) -> Result<(), XcmError> {
907		tracing::trace!(
908			target: "xcm::process_instruction",
909			instruction = ?instr,
910			"Processing instruction",
911		);
912
913		match instr {
914			WithdrawAsset(assets) => {
915				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
916				self.ensure_can_subsume_assets(assets.len())?;
917				let mut total_surplus = Weight::zero();
918				Config::TransactionalProcessor::process(|| {
919					// Take `assets` from the origin account (on-chain)...
920					for asset in assets.inner() {
921						let (_, surplus) = Config::AssetTransactor::withdraw_asset_with_surplus(
922							asset,
923							origin,
924							Some(&self.context),
925						)?;
926						// If we have some surplus, aggregate it.
927						total_surplus.saturating_accrue(surplus);
928					}
929					Ok(())
930				})
931				.and_then(|_| {
932					// ...and place into holding.
933					self.holding.subsume_assets(assets.into());
934					// Credit the total surplus.
935					self.total_surplus.saturating_accrue(total_surplus);
936					Ok(())
937				})
938			},
939			ReserveAssetDeposited(assets) => {
940				// check whether we trust origin to be our reserve location for this asset.
941				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
942				self.ensure_can_subsume_assets(assets.len())?;
943				for asset in assets.inner() {
944					// Must ensure that we recognise the asset as being managed by the origin.
945					ensure!(
946						Config::IsReserve::contains(asset, origin),
947						XcmError::UntrustedReserveLocation
948					);
949				}
950				self.holding.subsume_assets(assets.into());
951				Ok(())
952			},
953			TransferAsset { assets, beneficiary } => {
954				Config::TransactionalProcessor::process(|| {
955					// Take `assets` from the origin account (on-chain) and place into dest account.
956					let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
957					let mut total_surplus = Weight::zero();
958					for asset in assets.inner() {
959						let (_, surplus) = Config::AssetTransactor::transfer_asset_with_surplus(
960							&asset,
961							origin,
962							&beneficiary,
963							&self.context,
964						)?;
965						// If we have some surplus, aggregate it.
966						total_surplus.saturating_accrue(surplus);
967					}
968					// Credit the total surplus.
969					self.total_surplus.saturating_accrue(total_surplus);
970					Ok(())
971				})
972			},
973			TransferReserveAsset { mut assets, dest, xcm } => {
974				Config::TransactionalProcessor::process(|| {
975					let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
976					let mut total_surplus = Weight::zero();
977					// Take `assets` from the origin account (on-chain) and place into dest account.
978					for asset in assets.inner() {
979						let (_, surplus) = Config::AssetTransactor::transfer_asset_with_surplus(
980							asset,
981							origin,
982							&dest,
983							&self.context,
984						)?;
985						// If we have some surplus, aggregate it.
986						total_surplus.saturating_accrue(surplus);
987					}
988					let reanchor_context = Config::UniversalLocation::get();
989					assets
990						.reanchor(&dest, &reanchor_context)
991						.map_err(|()| {
992							tracing::debug!(
993								target: "xcm::process_instruction::transfer_reserve_asset",
994								?assets,
995								?dest,
996								?reanchor_context,
997								"Failed to reanchor assets to destination in context"
998							);
999							XcmError::LocationFull
1000						})?;
1001					let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin];
1002					message.extend(xcm.0.into_iter());
1003					self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?;
1004					// Credit the total surplus.
1005					self.total_surplus.saturating_accrue(total_surplus);
1006					Ok(())
1007				})
1008			},
1009			ReceiveTeleportedAsset(assets) => {
1010				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1011				self.ensure_can_subsume_assets(assets.len())?;
1012				Config::TransactionalProcessor::process(|| {
1013					// check whether we trust origin to teleport this asset to us via config trait.
1014					for asset in assets.inner() {
1015						// We only trust the origin to send us assets that they identify as their
1016						// sovereign assets.
1017						ensure!(
1018							Config::IsTeleporter::contains(asset, origin),
1019							XcmError::UntrustedTeleportLocation
1020						);
1021						// We should check that the asset can actually be teleported in (for this to
1022						// be in error, there would need to be an accounting violation by one of the
1023						// trusted chains, so it's unlikely, but we don't want to punish a possibly
1024						// innocent chain/user).
1025						Config::AssetTransactor::can_check_in(origin, asset, &self.context)?;
1026						Config::AssetTransactor::check_in(origin, asset, &self.context);
1027					}
1028					Ok(())
1029				})
1030				.and_then(|_| {
1031					self.holding.subsume_assets(assets.into());
1032					Ok(())
1033				})
1034			},
1035			// `fallback_max_weight` is not used in the executor, it's only for conversions.
1036			Transact { origin_kind, mut call, .. } => {
1037				let origin = self.cloned_origin().ok_or_else(|| {
1038					tracing::trace!(
1039						target: "xcm::process_instruction::transact",
1040						"No origin provided",
1041					);
1042
1043					XcmError::BadOrigin
1044				})?;
1045
1046				let message_call = call.take_decoded().map_err(|_| {
1047					tracing::trace!(
1048						target: "xcm::process_instruction::transact",
1049						"Failed to decode call",
1050					);
1051
1052					XcmError::FailedToDecode
1053				})?;
1054
1055				tracing::trace!(
1056					target: "xcm::process_instruction::transact",
1057					?call,
1058					"Processing call",
1059				);
1060
1061				if !Config::SafeCallFilter::contains(&message_call) {
1062					tracing::trace!(
1063						target: "xcm::process_instruction::transact",
1064						"Call filtered by `SafeCallFilter`",
1065					);
1066
1067					return Err(XcmError::NoPermission)
1068				}
1069
1070				let dispatch_origin =
1071					Config::OriginConverter::convert_origin(origin.clone(), origin_kind).map_err(
1072						|_| {
1073							tracing::trace!(
1074								target: "xcm::process_instruction::transact",
1075								?origin,
1076								?origin_kind,
1077								"Failed to convert origin to a local origin."
1078							);
1079
1080							XcmError::BadOrigin
1081						},
1082					)?;
1083
1084				tracing::trace!(
1085					target: "xcm::process_instruction::transact",
1086					origin = ?dispatch_origin,
1087					call = ?message_call,
1088					"Dispatching call with origin",
1089				);
1090
1091				let weight = message_call.get_dispatch_info().call_weight;
1092				let maybe_actual_weight =
1093					match Config::CallDispatcher::dispatch(message_call, dispatch_origin) {
1094						Ok(post_info) => {
1095							tracing::trace!(
1096								target: "xcm::process_instruction::transact",
1097								?post_info,
1098								"Dispatch successful"
1099							);
1100							self.transact_status = MaybeErrorCode::Success;
1101							post_info.actual_weight
1102						},
1103						Err(error_and_info) => {
1104							tracing::trace!(
1105								target: "xcm::process_instruction::transact",
1106								?error_and_info,
1107								"Dispatch failed"
1108							);
1109
1110							self.transact_status = error_and_info.error.encode().into();
1111							error_and_info.post_info.actual_weight
1112						},
1113					};
1114				let actual_weight = maybe_actual_weight.unwrap_or(weight);
1115				let surplus = weight.saturating_sub(actual_weight);
1116				// If the actual weight of the call was less than the specified weight, we credit it.
1117				//
1118				// We make the adjustment for the total surplus, which is used eventually
1119				// reported back to the caller and this ensures that they account for the total
1120				// weight consumed correctly (potentially allowing them to do more operations in a
1121				// block than they otherwise would).
1122				self.total_surplus.saturating_accrue(surplus);
1123				Ok(())
1124			},
1125			QueryResponse { query_id, response, max_weight, querier } => {
1126				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1127				Config::ResponseHandler::on_response(
1128					origin,
1129					query_id,
1130					querier.as_ref(),
1131					response,
1132					max_weight,
1133					&self.context,
1134				);
1135				Ok(())
1136			},
1137			DescendOrigin(who) => self.do_descend_origin(who),
1138			ClearOrigin => self.do_clear_origin(),
1139			ExecuteWithOrigin { .. } => Err(XcmError::Unimplemented),
1140			ReportError(response_info) => {
1141				// Report the given result by sending a QueryResponse XCM to a previously given
1142				// outcome destination if one was registered.
1143				self.respond(
1144					self.cloned_origin(),
1145					Response::ExecutionResult(self.error),
1146					response_info,
1147					FeeReason::Report,
1148				)?;
1149				Ok(())
1150			},
1151			DepositAsset { assets, beneficiary } => {
1152				let old_holding = self.holding.clone();
1153				let result = Config::TransactionalProcessor::process(|| {
1154					let deposited = self.holding.saturating_take(assets);
1155					let surplus = Self::deposit_assets_with_retry(&deposited, &beneficiary, Some(&self.context))?;
1156					self.total_surplus.saturating_accrue(surplus);
1157					Ok(())
1158				});
1159				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1160					self.holding = old_holding;
1161				}
1162				result
1163			},
1164			DepositReserveAsset { assets, dest, xcm } => {
1165				let old_holding = self.holding.clone();
1166				let result = Config::TransactionalProcessor::process(|| {
1167					let mut assets = self.holding.saturating_take(assets);
1168					// When not using `PayFees`, nor `JIT_WITHDRAW`, delivery fees are paid from
1169					// transferred assets.
1170					let maybe_delivery_fee_from_assets = if self.fees.is_empty() && !self.fees_mode.jit_withdraw {
1171						// Deduct and return the part of `assets` that shall be used for delivery fees.
1172						self.take_delivery_fee_from_assets(&mut assets, &dest, FeeReason::DepositReserveAsset, &xcm)?
1173					} else {
1174						None
1175					};
1176					let mut message = Vec::with_capacity(xcm.len() + 2);
1177					tracing::trace!(target: "xcm::DepositReserveAsset", ?assets, "Assets except delivery fee");
1178					Self::do_reserve_deposit_assets(
1179						assets,
1180						&dest,
1181						&mut message,
1182						Some(&self.context),
1183					)?;
1184					// clear origin for subsequent custom instructions
1185					message.push(ClearOrigin);
1186					// append custom instructions
1187					message.extend(xcm.0.into_iter());
1188					if let Some(delivery_fee) = maybe_delivery_fee_from_assets {
1189						// Put back delivery_fee in holding register to be charged by XcmSender.
1190						self.holding.subsume_assets(delivery_fee);
1191					}
1192					self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?;
1193					Ok(())
1194				});
1195				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1196					self.holding = old_holding;
1197				}
1198				result
1199			},
1200			InitiateReserveWithdraw { assets, reserve, xcm } => {
1201				let old_holding = self.holding.clone();
1202				let result = Config::TransactionalProcessor::process(|| {
1203					let mut assets = self.holding.saturating_take(assets);
1204					// When not using `PayFees`, nor `JIT_WITHDRAW`, delivery fees are paid from
1205					// transferred assets.
1206					let maybe_delivery_fee_from_assets = if self.fees.is_empty() && !self.fees_mode.jit_withdraw {
1207						// Deduct and return the part of `assets` that shall be used for delivery fees.
1208						self.take_delivery_fee_from_assets(&mut assets, &reserve, FeeReason::InitiateReserveWithdraw, &xcm)?
1209					} else {
1210						None
1211					};
1212					let mut message = Vec::with_capacity(xcm.len() + 2);
1213					Self::do_reserve_withdraw_assets(
1214						assets,
1215						&mut self.holding,
1216						&reserve,
1217						&mut message,
1218					)?;
1219					// clear origin for subsequent custom instructions
1220					message.push(ClearOrigin);
1221					// append custom instructions
1222					message.extend(xcm.0.into_iter());
1223					if let Some(delivery_fee) = maybe_delivery_fee_from_assets {
1224						// Put back delivery_fee in holding register to be charged by XcmSender.
1225						self.holding.subsume_assets(delivery_fee);
1226					}
1227					self.send(reserve, Xcm(message), FeeReason::InitiateReserveWithdraw)?;
1228					Ok(())
1229				});
1230				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1231					self.holding = old_holding;
1232				}
1233				result
1234			},
1235			InitiateTeleport { assets, dest, xcm } => {
1236				let old_holding = self.holding.clone();
1237				let result = Config::TransactionalProcessor::process(|| {
1238					let mut assets = self.holding.saturating_take(assets);
1239					// When not using `PayFees`, nor `JIT_WITHDRAW`, delivery fees are paid from
1240					// transferred assets.
1241					let maybe_delivery_fee_from_assets = if self.fees.is_empty() && !self.fees_mode.jit_withdraw {
1242						// Deduct and return the part of `assets` that shall be used for delivery fees.
1243						self.take_delivery_fee_from_assets(&mut assets, &dest, FeeReason::InitiateTeleport, &xcm)?
1244					} else {
1245						None
1246					};
1247					let mut message = Vec::with_capacity(xcm.len() + 2);
1248					Self::do_teleport_assets(assets, &dest, &mut message, &self.context)?;
1249					// clear origin for subsequent custom instructions
1250					message.push(ClearOrigin);
1251					// append custom instructions
1252					message.extend(xcm.0.into_iter());
1253					if let Some(delivery_fee) = maybe_delivery_fee_from_assets {
1254						// Put back delivery_fee in holding register to be charged by XcmSender.
1255						self.holding.subsume_assets(delivery_fee);
1256					}
1257					self.send(dest.clone(), Xcm(message), FeeReason::InitiateTeleport)?;
1258					Ok(())
1259				});
1260				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1261					self.holding = old_holding;
1262				}
1263				result
1264			},
1265			InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => {
1266				let old_holding = self.holding.clone();
1267				let result = Config::TransactionalProcessor::process(|| {
1268					let mut message = Vec::with_capacity(assets.len() + remote_xcm.len() + 2);
1269
1270					// We need to transfer the fees and buy execution on remote chain _BEFORE_
1271					// transferring the other assets. This is required to satisfy the
1272					// `MAX_ASSETS_FOR_BUY_EXECUTION` limit in the `AllowTopLevelPaidExecutionFrom`
1273					// barrier.
1274					let remote_fees_paid = if let Some(remote_fees) = remote_fees {
1275						let reanchored_fees = match remote_fees {
1276							AssetTransferFilter::Teleport(fees_filter) => {
1277								let teleport_fees = self
1278									.holding
1279									.try_take(fees_filter)
1280									.map_err(|error| {
1281										tracing::debug!(
1282											target: "xcm::process_instruction::initiate_transfer", ?error,
1283											"Failed to take specified teleport fees from holding"
1284										);
1285										XcmError::NotHoldingFees
1286									})?;
1287								Self::do_teleport_assets(
1288									teleport_fees,
1289									&destination,
1290									&mut message,
1291									&self.context,
1292								)?
1293							},
1294							AssetTransferFilter::ReserveDeposit(fees_filter) => {
1295								let reserve_deposit_fees = self
1296									.holding
1297									.try_take(fees_filter)
1298									.map_err(|error| {
1299										tracing::debug!(
1300											target: "xcm::process_instruction::initiate_transfer", ?error,
1301											"Failed to take specified reserve deposit fees from holding"
1302										);
1303										XcmError::NotHoldingFees
1304									})?;
1305								Self::do_reserve_deposit_assets(
1306									reserve_deposit_fees,
1307									&destination,
1308									&mut message,
1309									Some(&self.context),
1310								)?
1311							},
1312							AssetTransferFilter::ReserveWithdraw(fees_filter) => {
1313								let reserve_withdraw_fees = self
1314									.holding
1315									.try_take(fees_filter)
1316									.map_err(|error| {
1317										tracing::debug!(
1318											target: "xcm::process_instruction::initiate_transfer", ?error,
1319											"Failed to take specified reserve withdraw fees from holding"
1320										);
1321										XcmError::NotHoldingFees
1322									})?;
1323								Self::do_reserve_withdraw_assets(
1324									reserve_withdraw_fees,
1325									&mut self.holding,
1326									&destination,
1327									&mut message,
1328								)?
1329							},
1330						};
1331						ensure!(reanchored_fees.len() == 1, XcmError::TooManyAssets);
1332						let fees =
1333							reanchored_fees.into_inner().pop().ok_or(XcmError::NotHoldingFees)?;
1334						// move these assets to the fees register for covering execution and paying
1335						// any subsequent fees
1336						message.push(PayFees { asset: fees });
1337						true
1338					} else {
1339						false
1340					};
1341
1342					// add any extra asset transfers
1343					for asset_filter in assets {
1344						match asset_filter {
1345							AssetTransferFilter::Teleport(assets) => Self::do_teleport_assets(
1346								self.holding.saturating_take(assets),
1347								&destination,
1348								&mut message,
1349								&self.context,
1350							)?,
1351							AssetTransferFilter::ReserveDeposit(assets) =>
1352								Self::do_reserve_deposit_assets(
1353									self.holding.saturating_take(assets),
1354									&destination,
1355									&mut message,
1356									Some(&self.context),
1357								)?,
1358							AssetTransferFilter::ReserveWithdraw(assets) =>
1359								Self::do_reserve_withdraw_assets(
1360									self.holding.saturating_take(assets),
1361									&mut self.holding,
1362									&destination,
1363									&mut message,
1364								)?,
1365						};
1366					}
1367
1368					if preserve_origin {
1369						// We alias the origin if it's not a noop (origin != `Here`).
1370						if let Some(original_origin) = self
1371							.origin_ref()
1372							.filter(|origin| *origin != &Location::here())
1373							.cloned()
1374						{
1375							// preserve current origin for subsequent user-controlled instructions on
1376							// remote chain
1377							let reanchored_origin = Self::try_reanchor(original_origin, &destination)?.0;
1378							message.push(AliasOrigin(reanchored_origin));
1379						}
1380					} else {
1381						// clear origin for subsequent user-controlled instructions on remote chain
1382						message.push(ClearOrigin);
1383					}
1384
1385					// If not intending to pay for fees then we append the `UnpaidExecution`
1386					// _AFTER_ origin altering instructions.
1387					// When origin is not preserved, it's probably going to fail on the receiver.
1388					if !remote_fees_paid {
1389						// We push the UnpaidExecution instruction to notify we do not intend to pay
1390						// for fees.
1391						// The receiving chain must decide based on the origin of the message if they
1392						// accept this.
1393						message
1394							.push(UnpaidExecution { weight_limit: Unlimited, check_origin: None });
1395					}
1396
1397					// append custom instructions
1398					message.extend(remote_xcm.0.into_iter());
1399					// send the onward XCM
1400					self.send(destination, Xcm(message), FeeReason::InitiateTransfer)?;
1401					Ok(())
1402				});
1403				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1404					self.holding = old_holding;
1405				}
1406				result
1407			},
1408			ReportHolding { response_info, assets } => {
1409				// Note that we pass `None` as `maybe_failed_bin` since no assets were ever removed
1410				// from Holding.
1411				let assets =
1412					Self::reanchored(self.holding.min(&assets), &response_info.destination, None);
1413				self.respond(
1414					self.cloned_origin(),
1415					Response::Assets(assets),
1416					response_info,
1417					FeeReason::Report,
1418				)?;
1419				Ok(())
1420			},
1421			BuyExecution { fees, weight_limit } => {
1422				// There is no need to buy any weight if `weight_limit` is `Unlimited` since it
1423				// would indicate that `AllowTopLevelPaidExecutionFrom` was unused for execution
1424				// and thus there is some other reason why it has been determined that this XCM
1425				// should be executed.
1426				let Some(weight) = Option::<Weight>::from(weight_limit) else { return Ok(()) };
1427				let old_holding = self.holding.clone();
1428				// Save the asset being used for execution fees, so we later know what should be
1429				// used for delivery fees.
1430				self.asset_used_in_buy_execution = Some(fees.id.clone());
1431				tracing::trace!(
1432					target: "xcm::executor::BuyExecution",
1433					asset_used_in_buy_execution = ?self.asset_used_in_buy_execution
1434				);
1435				// pay for `weight` using up to `fees` of the holding register.
1436				let max_fee =
1437					self.holding.try_take(fees.clone().into()).map_err(|e| {
1438						tracing::error!(target: "xcm::process_instruction::buy_execution", ?e, ?fees,
1439							"Failed to take fees from holding");
1440						XcmError::NotHoldingFees
1441					})?;
1442				let result = Config::TransactionalProcessor::process(|| {
1443					let unspent = self.trader.buy_weight(weight, max_fee, &self.context)?;
1444					self.holding.subsume_assets(unspent);
1445					Ok(())
1446				});
1447				if result.is_err() {
1448					self.holding = old_holding;
1449				}
1450				result
1451			},
1452			PayFees { asset } => {
1453				// If we've already paid for fees, do nothing.
1454				if self.already_paid_fees {
1455					return Ok(());
1456				}
1457				// Make sure `PayFees` won't be processed again.
1458				self.already_paid_fees = true;
1459				// Record old holding in case we need to rollback.
1460				let old_holding = self.holding.clone();
1461				// The max we're willing to pay for fees is decided by the `asset` operand.
1462				tracing::trace!(
1463					target: "xcm::executor::PayFees",
1464					asset_for_fees = ?asset,
1465					message_weight = ?self.message_weight,
1466				);
1467				// Pay for execution fees.
1468				let result = Config::TransactionalProcessor::process(|| {
1469					let max_fee =
1470						self.holding.try_take(asset.into()).map_err(|error| {
1471							tracing::debug!(
1472								target: "xcm::process_instruction::pay_fees", ?error,
1473								"Failed to take fees from holding"
1474							);
1475							XcmError::NotHoldingFees
1476						})?;
1477					let unspent =
1478						self.trader.buy_weight(self.message_weight, max_fee, &self.context)?;
1479					// Move unspent to the `fees` register, it can later be moved to holding
1480					// by calling `RefundSurplus`.
1481					self.fees.subsume_assets(unspent);
1482					Ok(())
1483				});
1484				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1485					// Rollback on error.
1486					self.holding = old_holding;
1487					self.already_paid_fees = false;
1488				}
1489				result
1490			},
1491			RefundSurplus => self.refund_surplus(),
1492			SetErrorHandler(mut handler) => {
1493				let handler_weight = Config::Weigher::weight(&mut handler, Weight::MAX)
1494					.map_err(|error| {
1495						tracing::debug!(
1496							target: "xcm::executor::SetErrorHandler",
1497							?error,
1498							?handler,
1499							"Failed to calculate weight"
1500						);
1501						XcmError::WeightNotComputable
1502					})?;
1503				self.total_surplus.saturating_accrue(self.error_handler_weight);
1504				self.error_handler = handler;
1505				self.error_handler_weight = handler_weight;
1506				Ok(())
1507			},
1508			SetAppendix(mut appendix) => {
1509				let appendix_weight = Config::Weigher::weight(&mut appendix, Weight::MAX)
1510					.map_err(|error| {
1511						tracing::debug!(
1512							target: "xcm::executor::SetErrorHandler",
1513							?error,
1514							?appendix,
1515							"Failed to calculate weight"
1516						);
1517						XcmError::WeightNotComputable
1518					})?;
1519				self.total_surplus.saturating_accrue(self.appendix_weight);
1520				self.appendix = appendix;
1521				self.appendix_weight = appendix_weight;
1522				Ok(())
1523			},
1524			ClearError => {
1525				self.error = None;
1526				Ok(())
1527			},
1528			SetHints { hints } => {
1529				for hint in hints.into_iter() {
1530					match hint {
1531						AssetClaimer { location } => {
1532							self.asset_claimer = Some(location)
1533						},
1534					}
1535				}
1536				Ok(())
1537			},
1538			ClaimAsset { assets, ticket } => {
1539				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1540				self.ensure_can_subsume_assets(assets.len())?;
1541				let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets, &self.context);
1542				ensure!(ok, XcmError::UnknownClaim);
1543				self.holding.subsume_assets(assets.into());
1544				Ok(())
1545			},
1546			Trap(code) => Err(XcmError::Trap(code)),
1547			SubscribeVersion { query_id, max_response_weight } => {
1548				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1549				// We don't allow derivative origins to subscribe since it would otherwise pose a
1550				// DoS risk.
1551				ensure!(&self.original_origin == origin, XcmError::BadOrigin);
1552				Config::SubscriptionService::start(
1553					origin,
1554					query_id,
1555					max_response_weight,
1556					&self.context,
1557				)
1558			},
1559			UnsubscribeVersion => {
1560				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1561				ensure!(&self.original_origin == origin, XcmError::BadOrigin);
1562				Config::SubscriptionService::stop(origin, &self.context)
1563			},
1564			BurnAsset(assets) => {
1565				self.holding.saturating_take(assets.into());
1566				Ok(())
1567			},
1568			ExpectAsset(assets) =>
1569				self.holding.ensure_contains(&assets).map_err(|e| {
1570					tracing::error!(target: "xcm::process_instruction::expect_asset", ?e, ?assets, "assets not contained in holding");
1571					XcmError::ExpectationFalse
1572				}),
1573			ExpectOrigin(origin) => {
1574				ensure!(self.context.origin == origin, XcmError::ExpectationFalse);
1575				Ok(())
1576			},
1577			ExpectError(error) => {
1578				ensure!(self.error == error, XcmError::ExpectationFalse);
1579				Ok(())
1580			},
1581			ExpectTransactStatus(transact_status) => {
1582				ensure!(self.transact_status == transact_status, XcmError::ExpectationFalse);
1583				Ok(())
1584			},
1585			QueryPallet { module_name, response_info } => {
1586				let pallets = Config::PalletInstancesInfo::infos()
1587					.into_iter()
1588					.filter(|x| x.module_name.as_bytes() == &module_name[..])
1589					.map(|x| {
1590						PalletInfo::new(
1591							x.index as u32,
1592							x.name.as_bytes().into(),
1593							x.module_name.as_bytes().into(),
1594							x.crate_version.major as u32,
1595							x.crate_version.minor as u32,
1596							x.crate_version.patch as u32,
1597						)
1598					})
1599					.collect::<Result<Vec<_>, XcmError>>()?;
1600				let QueryResponseInfo { destination, query_id, max_weight } = response_info;
1601				let response =
1602					Response::PalletsInfo(pallets.try_into().map_err(|error| {
1603						tracing::debug!(
1604							target: "xcm::process_instruction::query_pallet", ?error,
1605							"Failed to convert pallets to response info"
1606						);
1607						XcmError::Overflow
1608					})?);
1609				let querier = Self::to_querier(self.cloned_origin(), &destination)?;
1610				let instruction = QueryResponse { query_id, response, max_weight, querier };
1611				let message = Xcm(vec![instruction]);
1612				self.send(destination, message, FeeReason::QueryPallet)?;
1613				Ok(())
1614			},
1615			ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1616				let pallet = Config::PalletInstancesInfo::infos()
1617					.into_iter()
1618					.find(|x| x.index == index as usize)
1619					.ok_or(XcmError::PalletNotFound)?;
1620				ensure!(pallet.name.as_bytes() == &name[..], XcmError::NameMismatch);
1621				ensure!(pallet.module_name.as_bytes() == &module_name[..], XcmError::NameMismatch);
1622				let major = pallet.crate_version.major as u32;
1623				ensure!(major == crate_major, XcmError::VersionIncompatible);
1624				let minor = pallet.crate_version.minor as u32;
1625				ensure!(minor >= min_crate_minor, XcmError::VersionIncompatible);
1626				Ok(())
1627			},
1628			ReportTransactStatus(response_info) => {
1629				self.respond(
1630					self.cloned_origin(),
1631					Response::DispatchResult(self.transact_status.clone()),
1632					response_info,
1633					FeeReason::Report,
1634				)?;
1635				Ok(())
1636			},
1637			ClearTransactStatus => {
1638				self.transact_status = Default::default();
1639				Ok(())
1640			},
1641			UniversalOrigin(new_global) => {
1642				let universal_location = Config::UniversalLocation::get();
1643				ensure!(universal_location.first() != Some(&new_global), XcmError::InvalidLocation);
1644				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1645				let origin_xform = (origin, new_global);
1646				let ok = Config::UniversalAliases::contains(&origin_xform);
1647				ensure!(ok, XcmError::InvalidLocation);
1648				let (_, new_global) = origin_xform;
1649				let new_origin = Junctions::from([new_global]).relative_to(&universal_location);
1650				self.context.origin = Some(new_origin);
1651				Ok(())
1652			},
1653			ExportMessage { network, destination, xcm } => {
1654				// The actual message sent to the bridge for forwarding is prepended with
1655				// `UniversalOrigin` and `DescendOrigin` in order to ensure that the message is
1656				// executed with this Origin.
1657				//
1658				// Prepend the desired message with instructions which effectively rewrite the
1659				// origin.
1660				//
1661				// This only works because the remote chain empowers the bridge
1662				// to speak for the local network.
1663				let origin = self.context.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone();
1664				let universal_source = Config::UniversalLocation::get()
1665					.within_global(origin)
1666					.map_err(|()| {
1667						tracing::debug!(
1668							target: "xcm::process_instruction::export_message",
1669							"Failed to reanchor origin to universal location",
1670						);
1671						XcmError::Unanchored
1672					})?;
1673				let hash = (self.origin_ref(), &destination).using_encoded(blake2_128);
1674				let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0);
1675				// Hash identifies the lane on the exporter which we use. We use the pairwise
1676				// combination of the origin and destination to ensure origin/destination pairs
1677				// will generally have their own lanes.
1678				let (ticket, fee) = validate_export::<Config::MessageExporter>(
1679					network,
1680					channel,
1681					universal_source,
1682					destination.clone(),
1683					xcm,
1684				)?;
1685				let old_holding = self.holding.clone();
1686				let result = Config::TransactionalProcessor::process(|| {
1687					self.take_fee(fee, FeeReason::Export { network, destination })?;
1688					let _ = Config::MessageExporter::deliver(ticket).defensive_proof(
1689						"`deliver` called immediately after `validate_export`; \
1690						`take_fee` does not affect the validity of the ticket; qed",
1691					);
1692					Ok(())
1693				});
1694				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1695					self.holding = old_holding;
1696				}
1697				result
1698			},
1699			LockAsset { asset, unlocker } => {
1700				let old_holding = self.holding.clone();
1701				let result = Config::TransactionalProcessor::process(|| {
1702					let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1703					let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?;
1704					let lock_ticket =
1705						Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?;
1706					let owner = origin.reanchored(&unlocker, &context).map_err(|e| {
1707						tracing::error!(target: "xcm::xcm_executor::process_instruction", ?e, ?unlocker, ?context, "Failed to re-anchor origin");
1708						XcmError::ReanchorFailed
1709					})?;
1710					let msg = Xcm::<()>(vec![NoteUnlockable { asset: remote_asset, owner }]);
1711					let (ticket, price) = validate_send::<Config::XcmSender>(unlocker, msg)?;
1712					self.take_fee(price, FeeReason::LockAsset)?;
1713					lock_ticket.enact()?;
1714					Config::XcmSender::deliver(ticket)?;
1715					Ok(())
1716				});
1717				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1718					self.holding = old_holding;
1719				}
1720				result
1721			},
1722			UnlockAsset { asset, target } => {
1723				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1724				Config::AssetLocker::prepare_unlock(origin, asset, target)?.enact()?;
1725				Ok(())
1726			},
1727			NoteUnlockable { asset, owner } => {
1728				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1729				Config::AssetLocker::note_unlockable(origin, asset, owner)?;
1730				Ok(())
1731			},
1732			RequestUnlock { asset, locker } => {
1733				let origin = self.cloned_origin().ok_or(XcmError::BadOrigin)?;
1734				let remote_asset = Self::try_reanchor(asset.clone(), &locker)?.0;
1735				let remote_target = Self::try_reanchor(origin.clone(), &locker)?.0;
1736				let reduce_ticket = Config::AssetLocker::prepare_reduce_unlockable(
1737					locker.clone(),
1738					asset,
1739					origin.clone(),
1740				)?;
1741				let msg =
1742					Xcm::<()>(vec![UnlockAsset { asset: remote_asset, target: remote_target }]);
1743				let (ticket, price) = validate_send::<Config::XcmSender>(locker, msg)?;
1744				let old_holding = self.holding.clone();
1745				let result = Config::TransactionalProcessor::process(|| {
1746					self.take_fee(price, FeeReason::RequestUnlock)?;
1747					reduce_ticket.enact()?;
1748					Config::XcmSender::deliver(ticket)?;
1749					Ok(())
1750				});
1751				if Config::TransactionalProcessor::IS_TRANSACTIONAL && result.is_err() {
1752					self.holding = old_holding;
1753				}
1754				result
1755			},
1756			ExchangeAsset { give, want, maximal } => {
1757				let old_holding = self.holding.clone();
1758				let give = self.holding.saturating_take(give);
1759				let result = Config::TransactionalProcessor::process(|| {
1760					self.ensure_can_subsume_assets(want.len())?;
1761					let exchange_result = Config::AssetExchanger::exchange_asset(
1762						self.origin_ref(),
1763						give,
1764						&want,
1765						maximal,
1766					);
1767					if let Ok(received) = exchange_result {
1768						self.holding.subsume_assets(received.into());
1769						Ok(())
1770					} else {
1771						Err(XcmError::NoDeal)
1772					}
1773				});
1774				if result.is_err() {
1775					self.holding = old_holding;
1776				}
1777				result
1778			},
1779			SetFeesMode { jit_withdraw } => {
1780				self.fees_mode = FeesMode { jit_withdraw };
1781				Ok(())
1782			},
1783			SetTopic(topic) => {
1784				self.context.topic = Some(topic);
1785				Ok(())
1786			},
1787			ClearTopic => {
1788				self.context.topic = None;
1789				Ok(())
1790			},
1791			AliasOrigin(target) => {
1792				let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?;
1793				if Config::Aliasers::contains(origin, &target) {
1794					self.context.origin = Some(target);
1795					Ok(())
1796				} else {
1797					Err(XcmError::NoPermission)
1798				}
1799			},
1800			UnpaidExecution { check_origin, .. } => {
1801				ensure!(
1802					check_origin.is_none() || self.context.origin == check_origin,
1803					XcmError::BadOrigin
1804				);
1805				Ok(())
1806			},
1807			HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1808				Config::TransactionalProcessor::process(|| {
1809					Config::HrmpNewChannelOpenRequestHandler::handle(
1810						sender,
1811						max_message_size,
1812						max_capacity,
1813					)
1814				}),
1815			HrmpChannelAccepted { recipient } => Config::TransactionalProcessor::process(|| {
1816				Config::HrmpChannelAcceptedHandler::handle(recipient)
1817			}),
1818			HrmpChannelClosing { initiator, sender, recipient } =>
1819				Config::TransactionalProcessor::process(|| {
1820					Config::HrmpChannelClosingHandler::handle(initiator, sender, recipient)
1821				}),
1822		}
1823	}
1824
1825	fn do_descend_origin(&mut self, who: InteriorLocation) -> XcmResult {
1826		self.context
1827			.origin
1828			.as_mut()
1829			.ok_or(XcmError::BadOrigin)?
1830			.append_with(who)
1831			.map_err(|e| {
1832				tracing::error!(target: "xcm::do_descend_origin", ?e, "Failed to append junctions");
1833				XcmError::LocationFull
1834			})
1835	}
1836
1837	fn do_clear_origin(&mut self) -> XcmResult {
1838		self.context.origin = None;
1839		Ok(())
1840	}
1841
1842	/// Deposit `to_deposit` assets to `beneficiary`, without giving up on the first (transient)
1843	/// error, and retrying once just in case one of the subsequently deposited assets satisfy some
1844	/// requirement.
1845	///
1846	/// Most common transient error is: `beneficiary` account does not yet exist and the first
1847	/// asset(s) in the (sorted) list does not satisfy ED, but a subsequent one in the list does.
1848	///
1849	/// Deposits also proceed without aborting on “below minimum” (dust) errors. This ensures
1850	/// that a batch of assets containing some legitimately depositable amounts will succeed
1851	/// even if some “dust” deposits fall below the chain’s configured minimum balance.
1852	///
1853	/// This function can write into storage and also return an error at the same time, it should
1854	/// always be called within a transactional context.
1855	fn deposit_assets_with_retry(
1856		to_deposit: &AssetsInHolding,
1857		beneficiary: &Location,
1858		context: Option<&XcmContext>,
1859	) -> Result<Weight, XcmError> {
1860		let mut total_surplus = Weight::zero();
1861		let mut failed_deposits = Vec::with_capacity(to_deposit.len());
1862		for asset in to_deposit.assets_iter() {
1863			match Config::AssetTransactor::deposit_asset_with_surplus(&asset, &beneficiary, context)
1864			{
1865				Ok(surplus) => {
1866					total_surplus.saturating_accrue(surplus);
1867				},
1868				Err(_) => {
1869					// if deposit failed for asset, mark it for retry.
1870					failed_deposits.push(asset);
1871				},
1872			}
1873		}
1874		tracing::trace!(
1875			target: "xcm::deposit_assets_with_retry",
1876			?failed_deposits,
1877			"First‐pass failures, about to retry"
1878		);
1879		// retry previously failed deposits, this time short-circuiting on any error.
1880		for asset in failed_deposits {
1881			match Config::AssetTransactor::deposit_asset_with_surplus(&asset, &beneficiary, context)
1882			{
1883				Ok(surplus) => {
1884					total_surplus.saturating_accrue(surplus);
1885				},
1886				Err(error) => {
1887					// Ignore dust deposit errors.
1888					if !matches!(
1889						error,
1890						XcmError::FailedToTransactAsset(string)
1891							if *string == *<&'static str>::from(sp_runtime::TokenError::BelowMinimum)
1892					) {
1893						return Err(error);
1894					}
1895				},
1896			};
1897		}
1898		Ok(total_surplus)
1899	}
1900
1901	/// Take from transferred `assets` the delivery fee required to send an onward transfer message
1902	/// to `destination`.
1903	///
1904	/// Will be removed once the transition from `BuyExecution` to `PayFees` is complete.
1905	fn take_delivery_fee_from_assets(
1906		&self,
1907		assets: &mut AssetsInHolding,
1908		destination: &Location,
1909		reason: FeeReason,
1910		xcm: &Xcm<()>,
1911	) -> Result<Option<AssetsInHolding>, XcmError> {
1912		let to_weigh = assets.clone();
1913		let to_weigh_reanchored = Self::reanchored(to_weigh, &destination, None);
1914		let remote_instruction = match reason {
1915			FeeReason::DepositReserveAsset => ReserveAssetDeposited(to_weigh_reanchored),
1916			FeeReason::InitiateReserveWithdraw => WithdrawAsset(to_weigh_reanchored),
1917			FeeReason::InitiateTeleport => ReceiveTeleportedAsset(to_weigh_reanchored),
1918			_ => {
1919				tracing::debug!(
1920					target: "xcm::take_delivery_fee_from_assets",
1921					"Unexpected delivery fee reason",
1922				);
1923				return Err(XcmError::NotHoldingFees);
1924			},
1925		};
1926		let mut message_to_weigh = Vec::with_capacity(xcm.len() + 2);
1927		message_to_weigh.push(remote_instruction);
1928		message_to_weigh.push(ClearOrigin);
1929		message_to_weigh.extend(xcm.0.clone().into_iter());
1930		let (_, fee) =
1931			validate_send::<Config::XcmSender>(destination.clone(), Xcm(message_to_weigh))?;
1932		let maybe_delivery_fee = fee.get(0).map(|asset_needed_for_fees| {
1933			tracing::trace!(
1934				target: "xcm::fees::take_delivery_fee_from_assets",
1935				"Asset provided to pay for fees {:?}, asset required for delivery fees: {:?}",
1936				self.asset_used_in_buy_execution, asset_needed_for_fees,
1937			);
1938			let asset_to_pay_for_fees =
1939				self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone());
1940			// set aside fee to be charged by XcmSender
1941			let delivery_fee = assets.saturating_take(asset_to_pay_for_fees.into());
1942			tracing::trace!(target: "xcm::fees::take_delivery_fee_from_assets", ?delivery_fee);
1943			delivery_fee
1944		});
1945		Ok(maybe_delivery_fee)
1946	}
1947}