referrerpolicy=no-referrer-when-downgrade

cumulus_primitives_utility/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Helper datatypes for cumulus. This includes the [`ParentAsUmp`] routing type which will route
18//! messages into an [`UpwardMessageSender`] if the destination is `Parent`.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate alloc;
23
24use alloc::{boxed::Box, vec, vec::Vec};
25use codec::Encode;
26use core::marker::PhantomData;
27use cumulus_primitives_core::{MessageSendError, UpwardMessageSender};
28use frame_support::{
29	defensive,
30	traits::{
31		tokens::{fungibles, imbalance::UnsafeManualAccounting},
32		Get, OnUnbalanced as OnUnbalancedT,
33	},
34	weights::{Weight, WeightToFee as WeightToFeeT},
35};
36use pallet_asset_conversion::{QuotePrice, SwapCredit as SwapCreditT};
37use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery;
38use sp_runtime::traits::Zero;
39use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm, WrapVersion};
40use xcm_builder::InspectMessageQueues;
41use xcm_executor::{
42	traits::{MatchesFungibles, WeightTrader},
43	AssetsInHolding,
44};
45
46#[cfg(test)]
47mod tests;
48
49#[cfg(test)]
50mod test_helpers {
51	pub use xcm_executor::test_helpers::mock_asset_to_holding as asset_to_holding;
52}
53
54/// Xcm router which recognises the `Parent` destination and handles it by sending the message into
55/// the given UMP `UpwardMessageSender` implementation. Thus this essentially adapts an
56/// `UpwardMessageSender` trait impl into a `SendXcm` trait impl.
57///
58/// NOTE: This is a pretty dumb "just send it" router; we will probably want to introduce queuing
59/// to UMP eventually and when we do, the pallet which implements the queuing will be responsible
60/// for the `SendXcm` implementation.
61pub struct ParentAsUmp<T, W, P>(PhantomData<(T, W, P)>);
62
63impl<T, W, P> SendXcm for ParentAsUmp<T, W, P>
64where
65	T: UpwardMessageSender,
66	W: WrapVersion,
67	P: PriceForMessageDelivery<Id = ()>,
68{
69	type Ticket = Vec<u8>;
70
71	fn validate(dest: &mut Option<Location>, msg: &mut Option<Xcm<()>>) -> SendResult<Vec<u8>> {
72		let d = dest.take().ok_or(SendError::MissingArgument)?;
73
74		if d.contains_parents_only(1) {
75			// An upward message for the relay chain.
76			let xcm = msg.take().ok_or(SendError::MissingArgument)?;
77			let price = P::price_for_delivery((), &xcm);
78			let versioned_xcm =
79				W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?;
80			versioned_xcm
81				.check_is_decodable()
82				.map_err(|()| SendError::ExceedsMaxMessageSize)?;
83			let data = versioned_xcm.encode();
84
85			// Pre-check with our message sender if everything else is okay.
86			T::can_send_upward_message(&data).map_err(Self::map_upward_sender_err)?;
87
88			Ok((data, price))
89		} else {
90			// Anything else is unhandled. This includes a message that is not meant for us.
91			// We need to make sure that dest/msg is not consumed here.
92			*dest = Some(d);
93			Err(SendError::NotApplicable)
94		}
95	}
96
97	fn deliver(data: Vec<u8>) -> Result<XcmHash, SendError> {
98		let (_, hash) = T::send_upward_message(data).map_err(Self::map_upward_sender_err)?;
99		Ok(hash)
100	}
101
102	#[cfg(feature = "runtime-benchmarks")]
103	fn ensure_successful_delivery(location: Option<Location>) {
104		if location.as_ref().map_or(false, |l| l.contains_parents_only(1)) {
105			T::ensure_successful_delivery();
106		}
107	}
108}
109
110impl<T, W, P> ParentAsUmp<T, W, P> {
111	fn map_upward_sender_err(message_send_error: MessageSendError) -> SendError {
112		match message_send_error {
113			MessageSendError::TooBig => SendError::ExceedsMaxMessageSize,
114			e => SendError::Transport(e.into()),
115		}
116	}
117}
118
119impl<T: UpwardMessageSender + InspectMessageQueues, W, P> InspectMessageQueues
120	for ParentAsUmp<T, W, P>
121{
122	fn clear_messages() {
123		T::clear_messages();
124	}
125
126	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
127		T::get_messages()
128	}
129}
130
131/// Charges for execution in the first asset of those selected for fee payment
132/// Only succeeds for Concrete Fungible Assets
133/// First tries to convert the this Asset into a local assetId
134/// Then charges for this assetId as described by FeeCharger
135/// Weight, paid balance, local asset Id and the location is stored for
136/// later refund purposes
137/// Important: Errors if the Trader is being called twice by 2 BuyExecution instructions
138/// Alternatively we could just return payment in the aforementioned case
139pub struct TakeFirstAssetTrader<
140	AccountId: Eq,
141	FeeCharger: ChargeWeightInFungibles<AccountId, Fungibles>,
142	Matcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
143	Fungibles: fungibles::Balanced<AccountId>,
144	OnUnbalanced: OnUnbalancedT<fungibles::Credit<AccountId, Fungibles>>,
145> {
146	/// Accumulated fee paid for XCM execution.
147	outstanding_credit: Option<fungibles::Credit<AccountId, Fungibles>>,
148	/// The amount of weight bought minus the weight already refunded
149	weight_outstanding: Weight,
150	_phantom_data: PhantomData<(AccountId, FeeCharger, Matcher, Fungibles, OnUnbalanced)>,
151}
152
153impl<
154		AccountId: Eq,
155		FeeCharger: ChargeWeightInFungibles<AccountId, Fungibles>,
156		Matcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
157		Fungibles: fungibles::Inspect<AccountId, Balance = u128, AssetId: Into<Location> + 'static>
158			+ fungibles::Balanced<AccountId, OnDropCredit: 'static, OnDropDebt: 'static>,
159		OnUnbalanced: OnUnbalancedT<fungibles::Credit<AccountId, Fungibles>>,
160	> WeightTrader for TakeFirstAssetTrader<AccountId, FeeCharger, Matcher, Fungibles, OnUnbalanced>
161{
162	fn new() -> Self {
163		Self {
164			outstanding_credit: None,
165			weight_outstanding: Weight::zero(),
166			_phantom_data: PhantomData,
167		}
168	}
169	// We take first asset
170	// Check whether we can convert fee to asset_fee (is_sufficient, min_deposit)
171	// If everything goes well, we charge.
172	fn buy_weight(
173		&mut self,
174		weight: Weight,
175		mut payment: AssetsInHolding,
176		context: &XcmContext,
177	) -> Result<AssetsInHolding, (AssetsInHolding, XcmError)> {
178		log::trace!(target: "xcm::weight", "TakeFirstAssetTrader::buy_weight weight: {:?}, payment: {:?}, context: {:?}", weight, payment, context);
179
180		// Make sure we don't enter twice
181		if self.outstanding_credit.is_some() {
182			return Err((payment, XcmError::NotWithdrawable));
183		}
184
185		// We take the very first asset from payment
186		let Some(used) = payment.fungible_assets_iter().next() else {
187			return Err((payment, XcmError::AssetNotFound));
188		};
189
190		// Get the local asset id in which we can pay for fees
191		let Ok((fungibles_asset_id, _)) = Matcher::matches_fungibles(&used) else {
192			return Err((payment, XcmError::AssetNotFound));
193		};
194
195		// Calculate how much we should charge in the asset_id for such amount of weight
196		// Require at least a payment of minimum_balance
197		// Necessary for fully collateral-backed assets
198		let required_amount: u128 =
199			match FeeCharger::charge_weight_in_fungibles(fungibles_asset_id.clone(), weight)
200				.map(|amount| amount.max(Fungibles::minimum_balance(fungibles_asset_id.clone())))
201			{
202				Ok(a) => a,
203				Err(_) => return Err((payment, XcmError::Overflow)),
204			};
205
206		// Convert to the same kind of asset, with the required fungible balance
207		let required = used.id.into_asset(required_amount.into());
208
209		// Subtract required from payment.
210		// Note: `payment` may contain multiple assets, but we only take from the first fungible
211		// asset that was matched above. Any remaining assets stay in `payment` and are returned.
212		let Some(imbalance) = payment
213			.try_take(required.into())
214			.ok()
215			.and_then(|taken| taken.fungible.into_iter().next().map(|(_, v)| v))
216		else {
217			return Err((payment, XcmError::TooExpensive));
218		};
219		// "manually" build the concrete credit and move the imbalance there.
220		let mut credit = fungibles::Credit::<AccountId, Fungibles>::zero(fungibles_asset_id);
221		credit.saturating_subsume(imbalance);
222
223		// Record weight and credit.
224		self.outstanding_credit = Some(credit);
225		self.weight_outstanding = weight;
226
227		// return the unused payment
228		Ok(payment)
229	}
230
231	/// Refunds unused weight back to holding.
232	///
233	/// Note: This is a best-effort refund. The actual refunded amount may differ from the
234	/// weight-equivalent amount due to existential deposit (ED) constraints. Specifically:
235	/// - If refunding the full amount would leave less than ED in outstanding credit, we only
236	///   refund enough to keep ED for the drop handler.
237	/// - This ensures collateral-backed assets always have sufficient balance for proper cleanup.
238	fn refund_weight(&mut self, weight: Weight, context: &XcmContext) -> Option<AssetsInHolding> {
239		log::trace!(target: "xcm::weight", "TakeFirstAssetTrader::refund_weight weight: {:?}, context: {:?}", weight, context);
240		if weight.is_zero() {
241			return None;
242		}
243		let outstanding_credit = self.outstanding_credit.as_mut()?;
244		let id = outstanding_credit.asset();
245		let fun = Fungible(outstanding_credit.peek());
246		let asset = (id.clone(), fun).into();
247
248		// Get the local asset id in which we can refund fees.
249		let (fungibles_asset_id, _) = Matcher::matches_fungibles(&asset).ok()?;
250		let minimum_balance = Fungibles::minimum_balance(fungibles_asset_id.clone());
251
252		// Calculate how much to refund based on unused weight.
253		// This read should have already been cached in buy_weight.
254		let refund_credit = FeeCharger::charge_weight_in_fungibles(fungibles_asset_id, weight)
255			.ok()
256			.map(|refund_balance| {
257				// Ensure at least minimum_balance remains for the drop handler.
258				// This is necessary for fully collateral-backed assets.
259				if outstanding_credit.peek().saturating_sub(refund_balance) >= minimum_balance {
260					outstanding_credit.extract(refund_balance)
261				} else {
262					// Keep at least ED in outstanding credit for the OnUnbalanced drop
263					// handler. Refund only the surplus above ED (zero if outstanding < ED).
264					let keep = minimum_balance.min(outstanding_credit.peek());
265					let refund_amount = outstanding_credit.peek().saturating_sub(keep);
266					outstanding_credit.extract(refund_amount)
267				}
268			})?;
269		// Subtract the refunded weight from existing weight.
270		self.weight_outstanding = self.weight_outstanding.saturating_sub(weight);
271
272		// Only return refund if non-zero.
273		if refund_credit.peek() != Zero::zero() {
274			Some(AssetsInHolding::new_from_fungible_credit(asset.id, Box::new(refund_credit)))
275		} else {
276			None
277		}
278	}
279
280	fn quote_weight(
281		&mut self,
282		weight: Weight,
283		given_id: AssetId,
284		context: &XcmContext,
285	) -> Result<Asset, XcmError> {
286		log::trace!(
287			target: "xcm::weight",
288			"TakeFirstAssetTrader::quote_weight weight: {:?}, given_id: {:?}, context: {:?}",
289			weight, given_id, context
290		);
291		if weight.is_zero() {
292			return Err(XcmError::NoDeal);
293		}
294
295		let give_matcher: Asset = (given_id.clone(), 1).into();
296		// Get the local asset id in which we can pay for fees
297		let (give_fungibles_id, _) =
298			Matcher::matches_fungibles(&give_matcher).map_err(|_| XcmError::AssetNotFound)?;
299
300		// Calculate how much we should charge in the asset_id for such amount of weight
301		// Require at least a payment of minimum_balance
302		// Necessary for fully collateral-backed assets
303		let required_amount: u128 =
304			FeeCharger::charge_weight_in_fungibles(give_fungibles_id.clone(), weight)
305				.map(|amount| amount.max(Fungibles::minimum_balance(give_fungibles_id.clone())))
306				.map_err(|_| XcmError::Overflow)?;
307
308		// Convert to the same kind of asset, with the required fungible balance
309		let required = given_id.into_asset(required_amount.into());
310		Ok(required)
311	}
312}
313
314impl<
315		AccountId: Eq,
316		FeeCharger: ChargeWeightInFungibles<AccountId, Fungibles>,
317		Matcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
318		Fungibles: fungibles::Balanced<AccountId>,
319		OnUnbalanced: OnUnbalancedT<fungibles::Credit<AccountId, Fungibles>>,
320	> Drop for TakeFirstAssetTrader<AccountId, FeeCharger, Matcher, Fungibles, OnUnbalanced>
321{
322	fn drop(&mut self) {
323		self.outstanding_credit
324			.take()
325			.filter(|credit| !credit.peek().is_zero())
326			.map(OnUnbalanced::on_unbalanced);
327	}
328}
329
330/// ChargeWeightInFungibles trait, which converts a given amount of weight
331/// and an assetId, and it returns the balance amount that should be charged
332/// in such assetId for that amount of weight
333pub trait ChargeWeightInFungibles<AccountId, Assets: fungibles::Inspect<AccountId>> {
334	fn charge_weight_in_fungibles(
335		asset_id: <Assets as fungibles::Inspect<AccountId>>::AssetId,
336		weight: Weight,
337	) -> Result<<Assets as fungibles::Inspect<AccountId>>::Balance, XcmError>;
338}
339
340/// Provides an implementation of [`WeightTrader`] to charge for weight using the first asset
341/// specified in the `payment` argument.
342///
343/// The asset used to pay for the weight must differ from the `Target` asset and be exchangeable for
344/// the same `Target` asset through `SwapCredit`.
345///
346/// ### Parameters:
347/// - `Target`: the asset into which the user's payment will be exchanged using `SwapCredit`.
348/// - `SwapCredit`: mechanism used for the exchange of the user's payment asset into the `Target`.
349/// - `WeightToFee`: weight to the `Target` asset fee calculator.
350/// - `Fungibles`: registry of fungible assets.
351/// - `FungiblesAssetMatcher`: utility for mapping [`Asset`] to `Fungibles::AssetId` and
352///   `Fungibles::Balance`.
353/// - `OnUnbalanced`: handler for the fee payment.
354/// - `AccountId`: the account identifier type.
355pub struct SwapFirstAssetTrader<
356	Target: Get<Fungibles::AssetId>,
357	SwapCredit: SwapCreditT<
358			AccountId,
359			Balance = Fungibles::Balance,
360			AssetKind = Fungibles::AssetId,
361			Credit = fungibles::Credit<AccountId, Fungibles>,
362		> + QuotePrice,
363	WeightToFee: WeightToFeeT<Balance = Fungibles::Balance>,
364	Fungibles: fungibles::Balanced<AccountId>,
365	FungiblesAssetMatcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
366	OnUnbalanced: OnUnbalancedT<fungibles::Credit<AccountId, Fungibles>>,
367	AccountId,
368> where
369	Fungibles::Balance: From<u128> + Into<u128>,
370{
371	/// Accumulated fee paid for XCM execution.
372	total_fee: fungibles::Credit<AccountId, Fungibles>,
373	/// Last asset utilized by a client to settle a fee.
374	last_fee_asset: Option<AssetId>,
375	_phantom_data: PhantomData<(
376		Target,
377		SwapCredit,
378		WeightToFee,
379		Fungibles,
380		FungiblesAssetMatcher,
381		OnUnbalanced,
382		AccountId,
383	)>,
384}
385
386impl<
387		Target: Get<Fungibles::AssetId>,
388		SwapCredit: SwapCreditT<
389				AccountId,
390				Balance = Fungibles::Balance,
391				AssetKind = Fungibles::AssetId,
392				Credit = fungibles::Credit<AccountId, Fungibles>,
393			> + QuotePrice<AssetKind = Fungibles::AssetId, Balance = Fungibles::Balance>,
394		WeightToFee: WeightToFeeT<Balance = Fungibles::Balance>,
395		Fungibles: fungibles::Balanced<
396			AccountId,
397			AssetId: 'static,
398			OnDropCredit: 'static,
399			OnDropDebt: 'static,
400		>,
401		FungiblesAssetMatcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
402		OnUnbalanced: OnUnbalancedT<fungibles::Credit<AccountId, Fungibles>>,
403		AccountId,
404	> WeightTrader
405	for SwapFirstAssetTrader<
406		Target,
407		SwapCredit,
408		WeightToFee,
409		Fungibles,
410		FungiblesAssetMatcher,
411		OnUnbalanced,
412		AccountId,
413	>
414where
415	Fungibles::Balance: From<u128> + Into<u128>,
416{
417	fn new() -> Self {
418		Self {
419			total_fee: fungibles::Credit::<AccountId, Fungibles>::zero(Target::get()),
420			last_fee_asset: None,
421			_phantom_data: PhantomData,
422		}
423	}
424
425	fn buy_weight(
426		&mut self,
427		weight: Weight,
428		mut payment: AssetsInHolding,
429		_context: &XcmContext,
430	) -> Result<AssetsInHolding, (AssetsInHolding, XcmError)> {
431		log::trace!(
432			target: "xcm::weight",
433			"SwapFirstAssetTrader::buy_weight weight: {:?}, payment: {:?}",
434			weight,
435			payment,
436		);
437		let Some((id, given_credit)) = payment.fungible.first_key_value() else {
438			return Err((payment, XcmError::AssetNotFound));
439		};
440		let id = id.clone();
441		let given_credit_amount = given_credit.amount();
442		let first_asset: Asset = (id.clone(), given_credit_amount).into();
443		let Ok((fungibles_id, _)) = FungiblesAssetMatcher::matches_fungibles(&first_asset) else {
444			log::trace!(
445				target: "xcm::weight",
446				"SwapFirstAssetTrader::buy_weight asset {:?} didn't match",
447				first_asset,
448			);
449			return Err((payment, XcmError::AssetNotFound));
450		};
451
452		let swap_asset = fungibles_id.clone().into();
453		if Target::get().eq(&swap_asset) {
454			log::trace!(
455				target: "xcm::weight",
456				"SwapFirstAssetTrader::buy_weight Asset was same as Target, swap not needed.",
457			);
458			// current trader is not applicable.
459			return Err((payment, XcmError::FeesNotMet));
460		}
461		// Subtract required from payment
462		let Some(imbalance) = payment.fungible.remove(&first_asset.id) else {
463			return Err((payment, XcmError::TooExpensive));
464		};
465		// "manually" build the concrete credit and move the imbalance there.
466		let mut credit_in = fungibles::Credit::<AccountId, Fungibles>::zero(fungibles_id);
467		credit_in.saturating_subsume(imbalance);
468
469		let fee = WeightToFee::weight_to_fee(&weight);
470		// swap the user's asset for the `Target` asset.
471		let (credit_out, credit_change) = match SwapCredit::swap_tokens_for_exact_tokens(
472			vec![swap_asset, Target::get()],
473			credit_in,
474			fee,
475		) {
476			Ok(a) => a,
477			Err((credit_in, error)) => {
478				log::trace!(
479					target: "xcm::weight",
480					"SwapFirstAssetTrader::buy_weight swap couldn't be done. Error was: {:?}",
481					error,
482				);
483				// put back the taken credit
484				let taken =
485					AssetsInHolding::new_from_fungible_credit(id.clone(), Box::new(credit_in));
486				payment.subsume_assets(taken);
487				return Err((payment, XcmError::FeesNotMet));
488			},
489		};
490
491		match self.total_fee.subsume(credit_out) {
492			Err(credit_out) => {
493				// error may occur if `total_fee.asset` differs from `credit_out.asset`, which does
494				// not apply in this context.
495				defensive!(
496					"`total_fee.asset` must be equal to `credit_out.asset`",
497					(self.total_fee.asset(), credit_out.asset())
498				);
499				return Err((payment, XcmError::FeesNotMet));
500			},
501			_ => (),
502		};
503		self.last_fee_asset = Some(id.clone());
504
505		if credit_change.peek() != Zero::zero() {
506			let unspent = AssetsInHolding::new_from_fungible_credit(id, Box::new(credit_change));
507			payment.subsume_assets(unspent);
508		}
509		Ok(payment)
510	}
511
512	fn refund_weight(&mut self, weight: Weight, _context: &XcmContext) -> Option<AssetsInHolding> {
513		log::trace!(
514			target: "xcm::weight",
515			"SwapFirstAssetTrader::refund_weight weight: {:?}, self.total_fee: {:?}",
516			weight,
517			self.total_fee,
518		);
519		if weight.is_zero() || self.total_fee.peek().is_zero() {
520			// noting to refund.
521			return None;
522		}
523		let refund_asset = if let Some(asset) = &self.last_fee_asset {
524			// create an initial zero refund in the asset used in the last `buy_weight`.
525			(asset.clone(), Fungible(0)).into()
526		} else {
527			return None;
528		};
529		let refund_amount = WeightToFee::weight_to_fee(&weight);
530		if refund_amount >= self.total_fee.peek() {
531			// not enough was paid to refund the `weight`.
532			return None;
533		}
534
535		let refund_swap_asset = FungiblesAssetMatcher::matches_fungibles(&refund_asset)
536			.map(|(a, _)| a.into())
537			.ok()?;
538
539		let refund = self.total_fee.extract(refund_amount);
540		let refund = match SwapCredit::swap_exact_tokens_for_tokens(
541			vec![Target::get(), refund_swap_asset],
542			refund,
543			None,
544		) {
545			Ok(refund_in_target) => refund_in_target,
546			Err((refund, _)) => {
547				// return an attempted refund back to the `total_fee`.
548				let _ = self.total_fee.subsume(refund).map_err(|refund| {
549					// error may occur if `total_fee.asset` differs from `refund.asset`, which does
550					// not apply in this context.
551					defensive!(
552						"`total_fee.asset` must be equal to `refund.asset`",
553						(self.total_fee.asset(), refund.asset())
554					);
555				});
556				return None;
557			},
558		};
559
560		let refund = AssetsInHolding::new_from_fungible_credit(refund_asset.id, Box::new(refund));
561		Some(refund)
562	}
563
564	fn quote_weight(
565		&mut self,
566		weight: Weight,
567		given_id: AssetId,
568		_context: &XcmContext,
569	) -> Result<Asset, XcmError> {
570		log::trace!(
571			target: "xcm::weight",
572			"SwapFirstAssetTrader::quote_weight weight: {:?}, given_id: {:?}",
573			weight,
574			given_id,
575		);
576		if weight.is_zero() {
577			return Err(XcmError::NoDeal);
578		}
579
580		let give_matcher: Asset = (given_id.clone(), 1).into();
581		let (give_fungibles_id, _) = FungiblesAssetMatcher::matches_fungibles(&give_matcher)
582			.map_err(|_| XcmError::AssetNotFound)?;
583		let want_fungibles_id = Target::get();
584		if give_fungibles_id.eq(&want_fungibles_id.clone().into()) {
585			return Err(XcmError::FeesNotMet);
586		}
587
588		let want_amount = WeightToFee::weight_to_fee(&weight);
589		// The `give` amount required to obtain `want`.
590		let necessary_give: u128 = <SwapCredit as QuotePrice>::quote_price_tokens_for_exact_tokens(
591			give_fungibles_id,
592			want_fungibles_id,
593			want_amount,
594			true, // Include fee.
595		)
596		.filter(|amount| *amount > 0u128.into())
597		.ok_or(XcmError::FeesNotMet)?
598		.into();
599		Ok((given_id, necessary_give).into())
600	}
601}
602
603impl<
604		Target: Get<Fungibles::AssetId>,
605		SwapCredit: SwapCreditT<
606				AccountId,
607				Balance = Fungibles::Balance,
608				AssetKind = Fungibles::AssetId,
609				Credit = fungibles::Credit<AccountId, Fungibles>,
610			> + QuotePrice,
611		WeightToFee: WeightToFeeT<Balance = Fungibles::Balance>,
612		Fungibles: fungibles::Balanced<AccountId>,
613		FungiblesAssetMatcher: MatchesFungibles<Fungibles::AssetId, Fungibles::Balance>,
614		OnUnbalanced: OnUnbalancedT<fungibles::Credit<AccountId, Fungibles>>,
615		AccountId,
616	> Drop
617	for SwapFirstAssetTrader<
618		Target,
619		SwapCredit,
620		WeightToFee,
621		Fungibles,
622		FungiblesAssetMatcher,
623		OnUnbalanced,
624		AccountId,
625	>
626where
627	Fungibles::Balance: From<u128> + Into<u128>,
628{
629	fn drop(&mut self) {
630		if self.total_fee.peek().is_zero() {
631			return;
632		}
633		let total_fee = self.total_fee.extract(self.total_fee.peek());
634		OnUnbalanced::on_unbalanced(total_fee);
635	}
636}
637
638#[cfg(test)]
639mod test_xcm_router {
640	use super::*;
641	use cumulus_primitives_core::UpwardMessage;
642	use frame_support::assert_ok;
643	use xcm::MAX_XCM_DECODE_DEPTH;
644
645	/// Validates [`validate`] for required Some(destination) and Some(message)
646	struct OkFixedXcmHashWithAssertingRequiredInputsSender;
647
648	impl OkFixedXcmHashWithAssertingRequiredInputsSender {
649		const FIXED_XCM_HASH: [u8; 32] = [9; 32];
650
651		fn fixed_delivery_asset() -> Assets {
652			Assets::new()
653		}
654
655		fn expected_delivery_result() -> Result<(XcmHash, Assets), SendError> {
656			Ok((Self::FIXED_XCM_HASH, Self::fixed_delivery_asset()))
657		}
658	}
659
660	impl SendXcm for OkFixedXcmHashWithAssertingRequiredInputsSender {
661		type Ticket = ();
662
663		fn validate(
664			destination: &mut Option<Location>,
665			message: &mut Option<Xcm<()>>,
666		) -> SendResult<Self::Ticket> {
667			assert!(destination.is_some());
668			assert!(message.is_some());
669			Ok(((), OkFixedXcmHashWithAssertingRequiredInputsSender::fixed_delivery_asset()))
670		}
671
672		fn deliver(_: Self::Ticket) -> Result<XcmHash, SendError> {
673			Ok(Self::FIXED_XCM_HASH)
674		}
675	}
676
677	/// Impl [`UpwardMessageSender`] that return `Ok` for `can_send_upward_message`.
678	struct CanSendUpwardMessageSender;
679
680	impl UpwardMessageSender for CanSendUpwardMessageSender {
681		fn send_upward_message(_: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError> {
682			Err(MessageSendError::Other)
683		}
684
685		fn can_send_upward_message(_: &UpwardMessage) -> Result<(), MessageSendError> {
686			Ok(())
687		}
688	}
689
690	#[test]
691	fn parent_as_ump_does_not_consume_dest_or_msg_on_not_applicable() {
692		// dummy message
693		let message = Xcm(vec![Trap(5)]);
694
695		// ParentAsUmp - check dest is really not applicable
696		let dest = (Parent, Parent, Parent);
697		let mut dest_wrapper = Some(dest.into());
698		let mut msg_wrapper = Some(message.clone());
699		assert_eq!(
700			Err(SendError::NotApplicable),
701			<ParentAsUmp<(), (), ()> as SendXcm>::validate(&mut dest_wrapper, &mut msg_wrapper)
702		);
703
704		// check wrapper were not consumed
705		assert_eq!(Some(dest.into()), dest_wrapper.take());
706		assert_eq!(Some(message.clone()), msg_wrapper.take());
707
708		// another try with router chain with asserting sender
709		assert_eq!(
710			OkFixedXcmHashWithAssertingRequiredInputsSender::expected_delivery_result(),
711			send_xcm::<(ParentAsUmp<(), (), ()>, OkFixedXcmHashWithAssertingRequiredInputsSender)>(
712				dest.into(),
713				message,
714			)
715		);
716	}
717
718	#[test]
719	fn parent_as_ump_consumes_dest_and_msg_on_ok_validate() {
720		// dummy message
721		let message = Xcm(vec![Trap(5)]);
722
723		// ParentAsUmp - check dest/msg is valid
724		let dest = (Parent, Here);
725		let mut dest_wrapper = Some(dest.clone().into());
726		let mut msg_wrapper = Some(message.clone());
727		assert!(<ParentAsUmp<CanSendUpwardMessageSender, (), ()> as SendXcm>::validate(
728			&mut dest_wrapper,
729			&mut msg_wrapper,
730		)
731		.is_ok());
732
733		// check wrapper were consumed
734		assert_eq!(None, dest_wrapper.take());
735		assert_eq!(None, msg_wrapper.take());
736
737		// another try with router chain with asserting sender
738		assert_eq!(
739			Err(SendError::Transport("Other")),
740			send_xcm::<(
741				ParentAsUmp<CanSendUpwardMessageSender, (), ()>,
742				OkFixedXcmHashWithAssertingRequiredInputsSender
743			)>(dest.into(), message)
744		);
745	}
746
747	#[test]
748	fn parent_as_ump_validate_nested_xcm_works() {
749		let dest = Parent;
750
751		type Router = ParentAsUmp<CanSendUpwardMessageSender, (), ()>;
752
753		// Message that is not too deeply nested:
754		let mut good = Xcm(vec![ClearOrigin]);
755		for _ in 0..MAX_XCM_DECODE_DEPTH - 1 {
756			good = Xcm(vec![SetAppendix(good)]);
757		}
758
759		// Check that the good message is validated:
760		assert_ok!(<Router as SendXcm>::validate(&mut Some(dest.into()), &mut Some(good.clone())));
761
762		// Nesting the message one more time should reject it:
763		let bad = Xcm(vec![SetAppendix(good)]);
764		assert_eq!(
765			Err(SendError::ExceedsMaxMessageSize),
766			<Router as SendXcm>::validate(&mut Some(dest.into()), &mut Some(bad))
767		);
768	}
769}
770
771#[cfg(test)]
772mod test_trader {
773	use super::{test_helpers::asset_to_holding, *};
774	use frame_support::{
775		assert_ok,
776		traits::tokens::{
777			DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence,
778		},
779	};
780	use sp_runtime::DispatchError;
781	use xcm_builder::TakeRevenue;
782	use xcm_executor::traits::Error;
783
784	#[test]
785	fn take_first_asset_trader_buy_weight_called_twice_throws_error() {
786		const AMOUNT: u128 = 100;
787
788		// prepare prerequisites to instantiate `TakeFirstAssetTrader`
789		type TestAccountId = u32;
790		type TestAssetId = Location; // Use Location directly as AssetId
791		type TestBalance = u128;
792
793		struct TestAssets;
794		impl MatchesFungibles<TestAssetId, TestBalance> for TestAssets {
795			fn matches_fungibles(a: &Asset) -> Result<(TestAssetId, TestBalance), Error> {
796				match a {
797					Asset { fun: Fungible(amount), id: AssetId(_id) } => {
798						Ok((Location::new(0, [GeneralIndex(1)]), *amount))
799					},
800					_ => Err(Error::AssetNotHandled),
801				}
802			}
803		}
804		impl fungibles::Inspect<TestAccountId> for TestAssets {
805			type AssetId = TestAssetId;
806			type Balance = TestBalance;
807
808			fn total_issuance(_: Self::AssetId) -> Self::Balance {
809				0
810			}
811
812			fn minimum_balance(_: Self::AssetId) -> Self::Balance {
813				0
814			}
815
816			fn balance(_: Self::AssetId, _: &TestAccountId) -> Self::Balance {
817				0
818			}
819
820			fn total_balance(_: Self::AssetId, _: &TestAccountId) -> Self::Balance {
821				0
822			}
823
824			fn reducible_balance(
825				_: Self::AssetId,
826				_: &TestAccountId,
827				_: Preservation,
828				_: Fortitude,
829			) -> Self::Balance {
830				0
831			}
832
833			fn can_deposit(
834				_: Self::AssetId,
835				_: &TestAccountId,
836				_: Self::Balance,
837				_: Provenance,
838			) -> DepositConsequence {
839				DepositConsequence::Success
840			}
841
842			fn can_withdraw(
843				_: Self::AssetId,
844				_: &TestAccountId,
845				_: Self::Balance,
846			) -> WithdrawConsequence<Self::Balance> {
847				WithdrawConsequence::Success
848			}
849
850			fn asset_exists(_: Self::AssetId) -> bool {
851				true
852			}
853		}
854		impl fungibles::Mutate<TestAccountId> for TestAssets {}
855		impl fungibles::Balanced<TestAccountId> for TestAssets {
856			type OnDropCredit = fungibles::DecreaseIssuance<TestAccountId, Self>;
857			type OnDropDebt = fungibles::IncreaseIssuance<TestAccountId, Self>;
858		}
859		impl fungibles::Unbalanced<TestAccountId> for TestAssets {
860			fn handle_dust(_: fungibles::Dust<TestAccountId, Self>) {}
861			fn write_balance(
862				_: Self::AssetId,
863				_: &TestAccountId,
864				_: Self::Balance,
865			) -> Result<Option<Self::Balance>, DispatchError> {
866				Ok(None)
867			}
868
869			fn set_total_issuance(_: Self::AssetId, _: Self::Balance) {}
870		}
871
872		struct FeeChargerAssetsHandleRefund;
873		impl ChargeWeightInFungibles<TestAccountId, TestAssets> for FeeChargerAssetsHandleRefund {
874			fn charge_weight_in_fungibles(
875				_: <TestAssets as fungibles::Inspect<TestAccountId>>::AssetId,
876				_: Weight,
877			) -> Result<<TestAssets as fungibles::Inspect<TestAccountId>>::Balance, XcmError> {
878				Ok(AMOUNT)
879			}
880		}
881		impl TakeRevenue for FeeChargerAssetsHandleRefund {
882			fn take_revenue(_: AssetsInHolding) {}
883		}
884
885		// Implement OnUnbalanced for the test
886		struct HandleFees;
887		impl OnUnbalancedT<fungibles::Credit<TestAccountId, TestAssets>> for HandleFees {
888			fn on_unbalanced(_: fungibles::Credit<TestAccountId, TestAssets>) {
889				// Just drop it for tests
890			}
891		}
892
893		// create new instance
894		type Trader = TakeFirstAssetTrader<
895			TestAccountId,
896			FeeChargerAssetsHandleRefund,
897			TestAssets,
898			TestAssets,
899			HandleFees,
900		>;
901		let mut trader = <Trader as WeightTrader>::new();
902		let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
903
904		// prepare test data
905		let asset: Asset = (Here, AMOUNT).into();
906		let payment1 = asset_to_holding(asset.clone());
907		let payment2 = asset_to_holding(asset);
908		let weight_to_buy = Weight::from_parts(1_000, 1_000);
909
910		// lets do first call (success)
911		assert_ok!(trader.buy_weight(weight_to_buy, payment1, &ctx));
912
913		// lets do second call (error)
914		let (_, error) = trader.buy_weight(weight_to_buy, payment2, &ctx).unwrap_err();
915		assert_eq!(error, XcmError::NotWithdrawable);
916	}
917
918	#[test]
919	fn take_first_asset_trader_returns_unused_amount() {
920		// Regression test for fix: buy_weight should only take the required amount,
921		// not the entire balance from payment
922		const REQUIRED_AMOUNT: u128 = 100;
923		const TOTAL_AMOUNT: u128 = 500; // More than required
924
925		// prepare prerequisites to instantiate `TakeFirstAssetTrader`
926		type TestAccountId = u32;
927		type TestAssetId = Location;
928		type TestBalance = u128;
929
930		struct TestAssets;
931		impl MatchesFungibles<TestAssetId, TestBalance> for TestAssets {
932			fn matches_fungibles(a: &Asset) -> Result<(TestAssetId, TestBalance), Error> {
933				match a {
934					Asset { fun: Fungible(amount), id: AssetId(_id) } => {
935						Ok((Location::new(0, [GeneralIndex(1)]), *amount))
936					},
937					_ => Err(Error::AssetNotHandled),
938				}
939			}
940		}
941		impl fungibles::Inspect<TestAccountId> for TestAssets {
942			type AssetId = TestAssetId;
943			type Balance = TestBalance;
944
945			fn total_issuance(_: Self::AssetId) -> Self::Balance {
946				0
947			}
948
949			fn minimum_balance(_: Self::AssetId) -> Self::Balance {
950				0
951			}
952
953			fn balance(_: Self::AssetId, _: &TestAccountId) -> Self::Balance {
954				0
955			}
956
957			fn total_balance(_: Self::AssetId, _: &TestAccountId) -> Self::Balance {
958				0
959			}
960
961			fn reducible_balance(
962				_: Self::AssetId,
963				_: &TestAccountId,
964				_: Preservation,
965				_: Fortitude,
966			) -> Self::Balance {
967				0
968			}
969
970			fn can_deposit(
971				_: Self::AssetId,
972				_: &TestAccountId,
973				_: Self::Balance,
974				_: Provenance,
975			) -> DepositConsequence {
976				DepositConsequence::Success
977			}
978
979			fn can_withdraw(
980				_: Self::AssetId,
981				_: &TestAccountId,
982				_: Self::Balance,
983			) -> WithdrawConsequence<Self::Balance> {
984				WithdrawConsequence::Success
985			}
986
987			fn asset_exists(_: Self::AssetId) -> bool {
988				true
989			}
990		}
991		impl fungibles::Mutate<TestAccountId> for TestAssets {}
992		impl fungibles::Balanced<TestAccountId> for TestAssets {
993			type OnDropCredit = fungibles::DecreaseIssuance<TestAccountId, Self>;
994			type OnDropDebt = fungibles::IncreaseIssuance<TestAccountId, Self>;
995		}
996		impl fungibles::Unbalanced<TestAccountId> for TestAssets {
997			fn handle_dust(_: fungibles::Dust<TestAccountId, Self>) {}
998			fn write_balance(
999				_: Self::AssetId,
1000				_: &TestAccountId,
1001				_: Self::Balance,
1002			) -> Result<Option<Self::Balance>, DispatchError> {
1003				Ok(None)
1004			}
1005
1006			fn set_total_issuance(_: Self::AssetId, _: Self::Balance) {}
1007		}
1008
1009		struct FeeChargerAssetsHandleRefund;
1010		impl ChargeWeightInFungibles<TestAccountId, TestAssets> for FeeChargerAssetsHandleRefund {
1011			fn charge_weight_in_fungibles(
1012				_: <TestAssets as fungibles::Inspect<TestAccountId>>::AssetId,
1013				_: Weight,
1014			) -> Result<<TestAssets as fungibles::Inspect<TestAccountId>>::Balance, XcmError> {
1015				Ok(REQUIRED_AMOUNT)
1016			}
1017		}
1018		impl TakeRevenue for FeeChargerAssetsHandleRefund {
1019			fn take_revenue(_: AssetsInHolding) {}
1020		}
1021
1022		struct HandleFees;
1023		impl OnUnbalancedT<fungibles::Credit<TestAccountId, TestAssets>> for HandleFees {
1024			fn on_unbalanced(_: fungibles::Credit<TestAccountId, TestAssets>) {}
1025		}
1026
1027		// create new instance
1028		type Trader = TakeFirstAssetTrader<
1029			TestAccountId,
1030			FeeChargerAssetsHandleRefund,
1031			TestAssets,
1032			TestAssets,
1033			HandleFees,
1034		>;
1035		let mut trader = <Trader as WeightTrader>::new();
1036		let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
1037
1038		// prepare test data - payment with MORE than required
1039		let asset: Asset = (Here, TOTAL_AMOUNT).into();
1040		let payment = asset_to_holding(asset.clone());
1041		let weight_to_buy = Weight::from_parts(1_000, 1_000);
1042
1043		// call buy_weight - should succeed and return the excess
1044		let result = trader.buy_weight(weight_to_buy, payment, &ctx);
1045		assert_ok!(&result);
1046
1047		let unused_payment = result.unwrap();
1048
1049		// verify that the unused payment contains the excess amount
1050		let expected_excess = TOTAL_AMOUNT - REQUIRED_AMOUNT;
1051		let unused_assets: Vec<Asset> = unused_payment.fungible_assets_iter().collect();
1052
1053		// should have exactly one asset remaining
1054		assert_eq!(unused_assets.len(), 1);
1055
1056		// verify it's the correct amount (excess)
1057		match &unused_assets[0] {
1058			Asset { fun: Fungible(amount), .. } => {
1059				assert_eq!(*amount, expected_excess, "Expected excess amount to be returned");
1060			},
1061			_ => panic!("Expected fungible asset"),
1062		}
1063	}
1064
1065	#[test]
1066	fn take_first_asset_trader_refund_keeps_ed_for_drop_handler() {
1067		// Regression test: when refunding would leave less than ED in outstanding
1068		// credit, `refund_weight` must refund `outstanding - ED` and keep ED for the
1069		// `OnUnbalanced` drop handler.
1070		use core::cell::Cell;
1071
1072		const ED: u128 = 10;
1073		const BUY_FEE: u128 = 15;
1074		const REFUND_FEE: u128 = 10;
1075
1076		type TestAccountId = u32;
1077		type TestAssetId = Location;
1078		type TestBalance = u128;
1079
1080		struct TestAssets;
1081		impl MatchesFungibles<TestAssetId, TestBalance> for TestAssets {
1082			fn matches_fungibles(a: &Asset) -> Result<(TestAssetId, TestBalance), Error> {
1083				match a {
1084					Asset { fun: Fungible(amount), id: AssetId(_) } => {
1085						Ok((Location::new(0, [GeneralIndex(1)]), *amount))
1086					},
1087					_ => Err(Error::AssetNotHandled),
1088				}
1089			}
1090		}
1091		impl fungibles::Inspect<TestAccountId> for TestAssets {
1092			type AssetId = TestAssetId;
1093			type Balance = TestBalance;
1094			fn total_issuance(_: Self::AssetId) -> Self::Balance {
1095				0
1096			}
1097			fn minimum_balance(_: Self::AssetId) -> Self::Balance {
1098				ED
1099			}
1100			fn balance(_: Self::AssetId, _: &TestAccountId) -> Self::Balance {
1101				0
1102			}
1103			fn total_balance(_: Self::AssetId, _: &TestAccountId) -> Self::Balance {
1104				0
1105			}
1106			fn reducible_balance(
1107				_: Self::AssetId,
1108				_: &TestAccountId,
1109				_: Preservation,
1110				_: Fortitude,
1111			) -> Self::Balance {
1112				0
1113			}
1114			fn can_deposit(
1115				_: Self::AssetId,
1116				_: &TestAccountId,
1117				_: Self::Balance,
1118				_: Provenance,
1119			) -> DepositConsequence {
1120				DepositConsequence::Success
1121			}
1122			fn can_withdraw(
1123				_: Self::AssetId,
1124				_: &TestAccountId,
1125				_: Self::Balance,
1126			) -> WithdrawConsequence<Self::Balance> {
1127				WithdrawConsequence::Success
1128			}
1129			fn asset_exists(_: Self::AssetId) -> bool {
1130				true
1131			}
1132		}
1133		impl fungibles::Mutate<TestAccountId> for TestAssets {}
1134		impl fungibles::Balanced<TestAccountId> for TestAssets {
1135			type OnDropCredit = fungibles::DecreaseIssuance<TestAccountId, Self>;
1136			type OnDropDebt = fungibles::IncreaseIssuance<TestAccountId, Self>;
1137		}
1138		impl fungibles::Unbalanced<TestAccountId> for TestAssets {
1139			fn handle_dust(_: fungibles::Dust<TestAccountId, Self>) {}
1140			fn write_balance(
1141				_: Self::AssetId,
1142				_: &TestAccountId,
1143				_: Self::Balance,
1144			) -> Result<Option<Self::Balance>, DispatchError> {
1145				Ok(None)
1146			}
1147			fn set_total_issuance(_: Self::AssetId, _: Self::Balance) {}
1148		}
1149
1150		// Charge BUY_FEE on the first call (during `buy_weight`), REFUND_FEE on the
1151		// next (during `refund_weight`).
1152		thread_local! {
1153			static CALLS: Cell<u32> = const { Cell::new(0) };
1154			static HANDLER_RECEIVED: Cell<u128> = const { Cell::new(0) };
1155		}
1156		struct FeeCharger;
1157		impl ChargeWeightInFungibles<TestAccountId, TestAssets> for FeeCharger {
1158			fn charge_weight_in_fungibles(
1159				_: <TestAssets as fungibles::Inspect<TestAccountId>>::AssetId,
1160				_: Weight,
1161			) -> Result<<TestAssets as fungibles::Inspect<TestAccountId>>::Balance, XcmError> {
1162				let n = CALLS.with(|c| {
1163					let v = c.get();
1164					c.set(v + 1);
1165					v
1166				});
1167				Ok(if n == 0 { BUY_FEE } else { REFUND_FEE })
1168			}
1169		}
1170
1171		struct HandleFees;
1172		impl OnUnbalancedT<fungibles::Credit<TestAccountId, TestAssets>> for HandleFees {
1173			fn on_unbalanced(credit: fungibles::Credit<TestAccountId, TestAssets>) {
1174				HANDLER_RECEIVED.with(|h| h.set(credit.peek()));
1175			}
1176		}
1177
1178		type Trader =
1179			TakeFirstAssetTrader<TestAccountId, FeeCharger, TestAssets, TestAssets, HandleFees>;
1180		let mut trader = <Trader as WeightTrader>::new();
1181		let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
1182
1183		// `buy_weight` populates outstanding_credit with BUY_FEE = 15.
1184		let payment = asset_to_holding((Here, BUY_FEE).into());
1185		assert_ok!(trader.buy_weight(Weight::from_parts(1_000, 1_000), payment, &ctx));
1186
1187		// `refund_weight` would compute refund=10, but outstanding(15) - 10 = 5 < ED(10),
1188		// so it must refund only `outstanding - ED` = 5 and keep ED for the handler.
1189		let refund = trader
1190			.refund_weight(Weight::from_parts(500, 500), &ctx)
1191			.expect("non-zero refund");
1192		let refund_assets: Vec<Asset> = refund.fungible_assets_iter().collect();
1193		assert_eq!(refund_assets.len(), 1);
1194		match &refund_assets[0] {
1195			Asset { fun: Fungible(amount), .. } => {
1196				assert_eq!(*amount, BUY_FEE - ED, "refund must be `outstanding - ED`, not ED")
1197			},
1198			_ => panic!("expected fungible refund"),
1199		}
1200
1201		// Drop the trader; the handler must receive at least ED.
1202		drop(trader);
1203		HANDLER_RECEIVED.with(|h| {
1204			assert_eq!(h.get(), ED, "OnUnbalanced drop handler must receive at least ED")
1205		});
1206	}
1207}
1208
1209/// Implementation of `xcm_builder::EnsureDelivery` which helps to ensure delivery to the
1210/// parent relay chain. Deposits existential deposit for origin (if needed).
1211/// Deposits estimated fee to the origin account (if needed).
1212/// Allows triggering of additional logic for a specific `ParaId` (e.g. to open an HRMP channel) if
1213/// needed.
1214#[cfg(feature = "runtime-benchmarks")]
1215pub struct ToParentDeliveryHelper<XcmConfig, ExistentialDeposit, PriceForDelivery>(
1216	core::marker::PhantomData<(XcmConfig, ExistentialDeposit, PriceForDelivery)>,
1217);
1218
1219#[cfg(feature = "runtime-benchmarks")]
1220impl<
1221		XcmConfig: xcm_executor::Config,
1222		ExistentialDeposit: Get<Option<Asset>>,
1223		PriceForDelivery: PriceForMessageDelivery<Id = ()>,
1224	> xcm_builder::EnsureDelivery
1225	for ToParentDeliveryHelper<XcmConfig, ExistentialDeposit, PriceForDelivery>
1226{
1227	fn ensure_successful_delivery(
1228		origin_ref: &Location,
1229		dest: &Location,
1230		fee_reason: xcm_executor::traits::FeeReason,
1231	) -> (Option<xcm_executor::FeesMode>, Option<Assets>) {
1232		use xcm::{latest::MAX_ITEMS_IN_ASSETS, MAX_INSTRUCTIONS_TO_DECODE};
1233		use xcm_executor::{
1234			traits::{FeeManager, TransactAsset},
1235			FeesMode,
1236		};
1237
1238		// check if the destination is relay/parent
1239		if dest.ne(&Location::parent()) {
1240			return (None, None);
1241		}
1242
1243		// Ensure routers
1244		XcmConfig::XcmSender::ensure_successful_delivery(Some(Location::parent()));
1245
1246		let mut fees_mode = None;
1247		if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) {
1248			// if not waived, we need to set up accounts for paying and receiving fees
1249			let context = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
1250
1251			// mint ED to origin if needed
1252			if let Some(ed) = ExistentialDeposit::get() {
1253				let holdings = XcmConfig::AssetTransactor::mint_asset(&ed, &context).unwrap();
1254				XcmConfig::AssetTransactor::deposit_asset(holdings, &origin_ref, Some(&context))
1255					.unwrap();
1256			}
1257
1258			// overestimate delivery fee
1259			let mut max_assets: Vec<Asset> = Vec::new();
1260			for i in 0..MAX_ITEMS_IN_ASSETS {
1261				max_assets.push((GeneralIndex(i as u128), 100u128).into());
1262			}
1263			let overestimated_xcm =
1264				vec![WithdrawAsset(max_assets.into()); MAX_INSTRUCTIONS_TO_DECODE as usize].into();
1265			let overestimated_fees = PriceForDelivery::price_for_delivery((), &overestimated_xcm);
1266
1267			// mint overestimated fee to origin
1268			for fee in overestimated_fees.inner() {
1269				let holdings = XcmConfig::AssetTransactor::mint_asset(fee, &context).unwrap();
1270				XcmConfig::AssetTransactor::deposit_asset(holdings, &origin_ref, Some(&context))
1271					.unwrap();
1272			}
1273
1274			// expected worst case - direct withdraw
1275			fees_mode = Some(FeesMode { jit_withdraw: true });
1276		}
1277		(fees_mode, None)
1278	}
1279}