referrerpolicy=no-referrer-when-downgrade

staging_xcm_executor/
lib.rs

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