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