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