pallet_xcm_transactor/
lib.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam 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// Moonbeam 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 Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17//! # Xcm Transactor Module
18//!
19//! ## Overview
20//!
21//! Module to provide transact capabilities on other chains
22//!
23//! In this pallet we will make distinctions between sovereign, derivative accounts and
24//! multilocation-based derived accounts. The first is the account the parachain controls
25//! in the destination chain, the second is an account derived from the
26//! sovereign account itself, e.g., by hashing it with an index, while the third is an account
27//! derived from the multilocation of a use in this chain (typically, hashing the ML).
28//! Such distinction is important since we want to keep the integrity of the sovereign account
29//!
30//! This pallet provides three ways of sending Transact operations to another chain
31//!
32//! - transact_through_derivative: Transact through an address derived from this chains sovereign
33//! 	account in the destination chain. For the transaction to successfully be dispatched in the
34//! 	destination chain, pallet-utility needs to be installed and at least paid xcm message
35//! 	execution should be allowed (and WithdrawAsset,BuyExecution and Transact messages allowed)
36//! 	in the destination chain
37//!
38//!
39//!
40//! 	The transactions are dispatched from a derivative account
41//! 	of the sovereign account
42//! 	This pallet only stores the index of the derivative account used, but
43//! 	not the derivative account itself. The only assumption this pallet makes
44//! 	is the existence of the pallet_utility pallet in the destination chain
45//! 	through the XcmTransact trait.
46//!
47//! 	All calls will be wrapped around utility::as_derivative. This makes sure
48//! 	the inner call is executed from the derivative account and not the sovereign
49//! 	account itself.
50//!
51//! 	Index registration happens through DerivativeAddressRegistrationOrigin.
52//! 	This derivative account can be funded by external users to
53//! 	ensure it has enough funds to make the calls
54//!
55//! - transact_through_sovereign: Transact through the sovereign account representing this chain.
56//! 	For the transaction to successfully be dispatched in the destination chain, at least paid
57//! 	xcm message execution should be allowed (and WithdrawAsset,BuyExecution and Transact
58//! 	messages allowed) in the destination chain. Only callable by Root
59//!
60//! - transact_through_signed: Transact through an account derived from the multilocation
61//! 	representing the signed user making the call. We ensure this by prepending DescendOrigin as
62//! 	the first instruction of the XCM message. For the transaction to successfully be dispatched
63//! 	in the destination chain, at least descended paid xcm message execution should be allowed
64//! 	(and DescendOrigin + WithdrawAsset + BuyExecution + Transact messages allowed) in the
65//! 	destination chain. Additionally, a ML-based derivation mechanism needs to be implemented
66//! 	in the destination chain.
67
68#![cfg_attr(not(feature = "std"), no_std)]
69
70use frame_support::pallet;
71
72pub use pallet::*;
73
74#[cfg(any(test, feature = "runtime-benchmarks"))]
75mod benchmarks;
76
77#[cfg(test)]
78pub(crate) mod mock;
79#[cfg(test)]
80mod tests;
81
82pub mod encode;
83pub mod migrations;
84pub mod relay_indices;
85pub mod weights;
86pub use crate::weights::WeightInfo;
87
88type CurrencyIdOf<T> = <T as Config>::CurrencyId;
89
90/// TODO: Temporary (Will be removed when we have a proper way of getting pallet indices)
91/// Same index on both Polkadot and Kusama Asset Hub
92/// Kusama: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs#L1596
93/// Polkadot: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs#L1400
94pub const ASSET_HUB_UTILITY_PALLET_INDEX: u8 = 40;
95
96/// TODO: Temporary (Will be removed when we have a proper way of getting pallet indices)
97/// Same index on both Polkadot and Kusama Asset Hub
98/// Kusama: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs#L1628
99/// Polkadot: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs#L1434
100pub const ASSET_HUB_STAKING_PALLET_INDEX: u8 = 89;
101
102#[pallet]
103pub mod pallet {
104
105	use super::*;
106	use crate::relay_indices::RelayChainIndices;
107	use crate::weights::WeightInfo;
108	use crate::CurrencyIdOf;
109	use cumulus_primitives_core::{relay_chain::HrmpChannelId, ParaId};
110	use frame_support::traits::EitherOfDiverse;
111	use frame_support::{
112		dispatch::DispatchResult, pallet_prelude::*, weights::constants::WEIGHT_REF_TIME_PER_SECOND,
113	};
114	use frame_system::{ensure_signed, pallet_prelude::*};
115	use sp_runtime::traits::{AtLeast32BitUnsigned, Convert};
116	use sp_std::boxed::Box;
117	use sp_std::convert::TryFrom;
118	use sp_std::prelude::*;
119	use sp_std::vec;
120	use sp_std::vec::Vec;
121	use xcm::{latest::prelude::*, VersionedLocation};
122	use xcm_executor::traits::{TransactAsset, WeightBounds};
123	use xcm_primitives::{
124		FilterMaxAssetFee, HrmpAvailableCalls, HrmpEncodeCall, Reserve, UtilityAvailableCalls,
125		UtilityEncodeCall, XcmTransact,
126	};
127
128	#[pallet::pallet]
129	#[pallet::without_storage_info]
130	pub struct Pallet<T>(pub PhantomData<T>);
131
132	#[pallet::config]
133	pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
134		/// The balance type.
135		type Balance: Parameter
136			+ Member
137			+ AtLeast32BitUnsigned
138			+ Default
139			+ Copy
140			+ MaybeSerializeDeserialize
141			+ Into<u128>;
142
143		/// Currency Id.
144		type CurrencyId: Parameter + Member + Clone;
145
146		/// Convert `T::CurrencyId` to `Location`.
147		type CurrencyIdToLocation: Convert<Self::CurrencyId, Option<Location>>;
148
149		// XcmTransact needs to be implemented. This type needs to implement
150		// utility call encoding and multilocation gathering
151		type Transactor: Parameter + Member + Clone + XcmTransact;
152
153		/// AssetTransactor allows us to withdraw asset without being trapped
154		/// This should change in xcm v3, which allows us to burn assets
155		type AssetTransactor: TransactAsset;
156
157		// The origin that is allowed to register derivative address indices
158		type DerivativeAddressRegistrationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
159
160		// The origin that is allowed to manipulate (open, close, accept, cancel) an Hrmp channel
161		type HrmpManipulatorOrigin: EnsureOrigin<Self::RuntimeOrigin>;
162
163		// The origin that is allowed to open and accept an Hrmp channel
164		type HrmpOpenOrigin: EnsureOrigin<Self::RuntimeOrigin>;
165
166		/// Convert `T::AccountId` to `Location`.
167		type AccountIdToLocation: Convert<Self::AccountId, Location>;
168
169		/// Means of measuring the weight consumed by an XCM message locally.
170		type Weigher: WeightBounds<Self::RuntimeCall>;
171
172		/// This chain's Universal Location.
173		type UniversalLocation: Get<InteriorLocation>;
174
175		/// Self chain location.
176		#[pallet::constant]
177		type SelfLocation: Get<Location>;
178
179		// The origin that is allowed to dispatch calls from the sovereign account directly
180		type SovereignAccountDispatcherOrigin: EnsureOrigin<Self::RuntimeOrigin>;
181
182		/// XCM sender.
183		type XcmSender: SendXcm;
184
185		// Base XCM weight.
186		///
187		/// The actual weight for an XCM message is `T::BaseXcmWeight +
188		/// T::Weigher::weight(&msg)`.
189		#[pallet::constant]
190		type BaseXcmWeight: Get<Weight>;
191
192		/// The way to retrieve the reserve of a Asset. This can be
193		/// configured to accept absolute or relative paths for self tokens
194		type ReserveProvider: xcm_primitives::Reserve;
195
196		/// The way to filter the max fee to use for HRMP management operations
197		type MaxHrmpFee: FilterMaxAssetFee;
198
199		type WeightInfo: WeightInfo;
200	}
201
202	/// Stores the information to be able to issue a transact operation in another chain use an
203	/// asset as fee payer.
204	#[derive(
205		Default,
206		Clone,
207		Encode,
208		Decode,
209		MaxEncodedLen,
210		RuntimeDebug,
211		Eq,
212		PartialEq,
213		scale_info::TypeInfo,
214		DecodeWithMemTracking,
215	)]
216	pub struct RemoteTransactInfoWithMaxWeight {
217		/// Extra weight that transacting a call in a destination chain adds
218		/// Extra weight involved when transacting without DescendOrigin
219		/// This should always be possible in a destination chain, since
220		/// it involves going through the sovereign account
221		pub transact_extra_weight: Weight,
222		/// Max destination weight
223		pub max_weight: Weight,
224		/// Whether we allow transacting through signed origins in another chain, and
225		/// how much extra cost implies
226		/// Extra weight involved when transacting with DescendOrigin
227		/// The reason for it being an option is because the destination chain
228		/// might not support constructing origins based on generic MultiLocations
229		pub transact_extra_weight_signed: Option<Weight>,
230	}
231
232	/// Enum defining the way to express a Currency.
233	#[derive(
234		Clone,
235		Encode,
236		Decode,
237		Eq,
238		PartialEq,
239		RuntimeDebug,
240		scale_info::TypeInfo,
241		DecodeWithMemTracking,
242	)]
243	pub enum Currency<CurrencyId> {
244		// Express the Currency as a CurrencyId
245		AsCurrencyId(CurrencyId),
246		// Express the Currency as its MultiLOcation
247		AsMultiLocation(Box<VersionedLocation>),
248	}
249
250	impl<T> Default for Currency<T> {
251		fn default() -> Currency<T> {
252			Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
253		}
254	}
255
256	#[derive(
257		Clone,
258		Encode,
259		Decode,
260		Eq,
261		PartialEq,
262		RuntimeDebug,
263		scale_info::TypeInfo,
264		DecodeWithMemTracking,
265	)]
266	pub struct HrmpInitParams {
267		pub para_id: ParaId,
268		pub proposed_max_capacity: u32,
269		pub proposed_max_message_size: u32,
270	}
271
272	/// Enum defining the way to express a Currency.
273	#[derive(
274		Clone,
275		Encode,
276		Decode,
277		Eq,
278		PartialEq,
279		RuntimeDebug,
280		scale_info::TypeInfo,
281		DecodeWithMemTracking,
282	)]
283	pub enum HrmpOperation {
284		InitOpen(HrmpInitParams),
285		Accept {
286			para_id: ParaId,
287		},
288		Close(HrmpChannelId),
289		Cancel {
290			channel_id: HrmpChannelId,
291			open_requests: u32,
292		},
293	}
294
295	#[derive(
296		Default,
297		Clone,
298		Encode,
299		Decode,
300		Eq,
301		PartialEq,
302		RuntimeDebug,
303		MaxEncodedLen,
304		scale_info::TypeInfo,
305		DecodeWithMemTracking,
306	)]
307
308	/// Struct that defines how to express the payment in a particular currency
309	/// currency is defined by the Currency enum, which can be expressed as:
310	/// - CurrencyId
311	/// - Location
312	///
313	/// The fee_amount is an option. In case of None, the fee will be tried to
314	/// be calculated from storage. If the storage item for the currency is not
315	/// populated, then it fails
316	pub struct CurrencyPayment<CurrencyId> {
317		// the currency in which we want to express our payment
318		pub currency: Currency<CurrencyId>,
319		// indicates whether we want to specify the fee amount to be used
320		pub fee_amount: Option<u128>,
321	}
322
323	#[derive(
324		Default,
325		Clone,
326		Encode,
327		Decode,
328		RuntimeDebug,
329		PartialEq,
330		scale_info::TypeInfo,
331		DecodeWithMemTracking,
332	)]
333	/// Struct tindicating information about transact weights
334	/// It allows to specify:
335	/// - transact_required_weight_at_most: the amount of weight the Transact instruction
336	///   should consume at most
337	/// - overall_weight: the overall weight to be used for the whole XCM message execution.
338	///   If None, then this amount will be tried to be derived from storage.  If the storage item
339	pub struct TransactWeights {
340		// the amount of weight the Transact instruction should consume at most
341		pub transact_required_weight_at_most: Weight,
342		// the overall weight to be used for the whole XCM message execution. If None,
343		// then this amount will be tried to be derived from storage.  If the storage item
344		// for the chain is not populated, then it fails
345		pub overall_weight: Option<WeightLimit>,
346	}
347
348	/// The amount of ref_time and proof_size to use for fee calculation if
349	/// we are dealing with an Unlimited variant inside 'overall_weight' field
350	/// of 'TransactWeights' struct.
351	pub const MAX_WEIGHT: Weight = Weight::from_parts(100_000_000_000, 100_000);
352
353	/// Since we are using pallet-utility for account derivation (through AsDerivative),
354	/// we need to provide an index for the account derivation. This storage item stores the index
355	/// assigned for a given local account. These indices are usable as derivative in the relay chain
356	#[pallet::storage]
357	#[pallet::getter(fn index_to_account)]
358	pub type IndexToAccount<T: Config> = StorageMap<_, Blake2_128Concat, u16, T::AccountId>;
359
360	/// Stores the transact info of a Location. This defines how much extra weight we need to
361	/// add when we want to transact in the destination chain and maximum amount of weight allowed
362	/// by the destination chain
363	#[pallet::storage]
364	#[pallet::getter(fn transact_info)]
365	pub type TransactInfoWithWeightLimit<T: Config> =
366		StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
367
368	/// Stores the fee per second for an asset in its reserve chain. This allows us to convert
369	/// from weight to fee
370	#[pallet::storage]
371	#[pallet::getter(fn dest_asset_fee_per_second)]
372	pub type DestinationAssetFeePerSecond<T: Config> = StorageMap<_, Twox64Concat, Location, u128>;
373
374	/// Stores the indices of relay chain pallets
375	#[pallet::storage]
376	#[pallet::getter(fn relay_indices)]
377	pub type RelayIndices<T: Config> = StorageValue<_, RelayChainIndices, ValueQuery>;
378
379	/// An error that can occur while executing the mapping pallet's logic.
380	#[pallet::error]
381	pub enum Error<T> {
382		IndexAlreadyClaimed,
383		UnclaimedIndex,
384		NotOwner,
385		UnweighableMessage,
386		CannotReanchor,
387		AssetHasNoReserve,
388		InvalidDest,
389		NotCrossChainTransfer,
390		AssetIsNotReserveInDestination,
391		DestinationNotInvertible,
392		ErrorDelivering,
393		DispatchWeightBiggerThanTotalWeight,
394		WeightOverflow,
395		AmountOverflow,
396		TransactorInfoNotSet,
397		NotCrossChainTransferableCurrency,
398		XcmExecuteError,
399		BadVersion,
400		MaxWeightTransactReached,
401		UnableToWithdrawAsset,
402		FeePerSecondNotSet,
403		SignedTransactNotAllowedForDestination,
404		FailedMultiLocationToJunction,
405		HrmpHandlerNotImplemented,
406		TooMuchFeeUsed,
407		ErrorValidating,
408		RefundNotSupportedWithTransactInfo,
409	}
410
411	#[pallet::event]
412	#[pallet::generate_deposit(pub(crate) fn deposit_event)]
413	pub enum Event<T: Config> {
414		/// Transacted the inner call through a derivative account in a destination chain.
415		TransactedDerivative {
416			account_id: T::AccountId,
417			dest: Location,
418			call: Vec<u8>,
419			index: u16,
420		},
421		/// Transacted the call through the sovereign account in a destination chain.
422		TransactedSovereign {
423			fee_payer: Option<T::AccountId>,
424			dest: Location,
425			call: Vec<u8>,
426		},
427		/// Transacted the call through a signed account in a destination chain.
428		TransactedSigned {
429			fee_payer: T::AccountId,
430			dest: Location,
431			call: Vec<u8>,
432		},
433		/// Registered a derivative index for an account id.
434		RegisteredDerivative {
435			account_id: T::AccountId,
436			index: u16,
437		},
438		DeRegisteredDerivative {
439			index: u16,
440		},
441		/// Transact failed
442		TransactFailed {
443			error: XcmError,
444		},
445		/// Changed the transact info of a location
446		TransactInfoChanged {
447			location: Location,
448			remote_info: RemoteTransactInfoWithMaxWeight,
449		},
450		/// Removed the transact info of a location
451		TransactInfoRemoved {
452			location: Location,
453		},
454		/// Set dest fee per second
455		DestFeePerSecondChanged {
456			location: Location,
457			fee_per_second: u128,
458		},
459		/// Remove dest fee per second
460		DestFeePerSecondRemoved {
461			location: Location,
462		},
463		/// HRMP manage action succesfully sent
464		HrmpManagementSent {
465			action: HrmpOperation,
466		},
467	}
468
469	#[pallet::genesis_config]
470	pub struct GenesisConfig<T> {
471		pub relay_indices: RelayChainIndices,
472		#[serde(skip)]
473		pub _phantom: PhantomData<T>,
474	}
475
476	impl<T> Default for GenesisConfig<T> {
477		fn default() -> Self {
478			Self {
479				relay_indices: RelayChainIndices::default(),
480				_phantom: Default::default(),
481			}
482		}
483	}
484
485	#[pallet::genesis_build]
486	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
487		fn build(&self) {
488			<RelayIndices<T>>::put(self.relay_indices);
489		}
490	}
491
492	#[pallet::call]
493	impl<T: Config> Pallet<T> {
494		/// Register a derivative index for an account id. Dispatchable by
495		/// DerivativeAddressRegistrationOrigin
496		///
497		/// We do not store the derivative address, but only the index. We do not need to store
498		/// the derivative address to issue calls, only the index is enough
499		///
500		/// For now an index is registered for all possible destinations and not per-destination.
501		/// We can change this in the future although it would just make things more complicated
502		#[pallet::call_index(0)]
503		#[pallet::weight(T::WeightInfo::register())]
504		pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
505			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
506
507			ensure!(
508				IndexToAccount::<T>::get(&index).is_none(),
509				Error::<T>::IndexAlreadyClaimed
510			);
511
512			IndexToAccount::<T>::insert(&index, who.clone());
513
514			// Deposit event
515			Self::deposit_event(Event::<T>::RegisteredDerivative {
516				account_id: who,
517				index: index,
518			});
519
520			Ok(())
521		}
522
523		/// De-Register a derivative index. This prevents an account to use a derivative address
524		/// (represented by an index) from our of our sovereign accounts anymore
525		#[pallet::call_index(1)]
526		#[pallet::weight(T::WeightInfo::deregister())]
527		pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
528			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
529
530			// Remove index
531			IndexToAccount::<T>::remove(&index);
532
533			// Deposit event
534			Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
535
536			Ok(())
537		}
538
539		/// Transact the inner call through a derivative account in a destination chain,
540		/// using 'fee_location' to pay for the fees. This fee_location is given as a multilocation
541		///
542		/// The caller needs to have the index registered in this pallet. The fee multiasset needs
543		/// to be a reserve asset for the destination transactor::multilocation.
544		#[pallet::call_index(2)]
545		#[pallet::weight(
546			Pallet::<T>::weight_of_initiate_reserve_withdraw()
547			.saturating_add(T::WeightInfo::transact_through_derivative())
548		)]
549		pub fn transact_through_derivative(
550			origin: OriginFor<T>,
551			// destination to which the message should be sent
552			dest: T::Transactor,
553			// derivative index to be used
554			index: u16,
555			// fee to be used
556			fee: CurrencyPayment<CurrencyIdOf<T>>,
557			// inner call to be executed in destination. This wiol
558			// be wrapped into utility.as_derivative
559			inner_call: Vec<u8>,
560			// weight information to be used
561			weight_info: TransactWeights,
562			// add RefundSurplus and DepositAsset appendix
563			refund: bool,
564		) -> DispatchResult {
565			let who = ensure_signed(origin)?;
566
567			let fee_location = Self::currency_to_multilocation(fee.currency)
568				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
569
570			// The index exists
571			let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
572			// The derivative index is owned by the origin
573			ensure!(account == who, Error::<T>::NotOwner);
574
575			// Encode call bytes
576			// We make sure the inner call is wrapped on a as_derivative dispatchable
577			let call_bytes: Vec<u8> = <Self as UtilityEncodeCall>::encode_call(
578				dest.clone(),
579				UtilityAvailableCalls::AsDerivative(index, inner_call),
580			);
581
582			// Grab the destination
583			let dest = dest.destination();
584
585			// Calculate the total weight that the xcm message is going to spend in the
586			// destination chain
587			let total_weight = weight_info.overall_weight.map_or_else(
588				|| -> Result<_, DispatchError> {
589					let weight_info = Self::take_weight_from_transact_info(
590						dest.clone(),
591						weight_info.transact_required_weight_at_most,
592						refund,
593					)?;
594					Ok(WeightLimit::from(Some(weight_info)))
595				},
596				|v| Ok(v),
597			)?;
598
599			let total_weight_fee_calculation = match total_weight {
600				Unlimited => MAX_WEIGHT,
601				Limited(x) => x,
602			};
603
604			// Calculate fee based on FeePerSecond
605			let fee = Self::calculate_fee(
606				fee_location,
607				fee.fee_amount,
608				dest.clone(),
609				total_weight_fee_calculation,
610			)?;
611
612			// If refund is true, the appendix instruction will be a deposit back to the sovereign
613			let appendix = refund
614				.then(|| -> Result<_, DispatchError> {
615					Ok(vec![
616						RefundSurplus,
617						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
618					])
619				})
620				.transpose()?;
621
622			Self::transact_in_dest_chain_asset_non_signed(
623				dest.clone(),
624				Some(who.clone()),
625				fee,
626				call_bytes.clone(),
627				OriginKind::SovereignAccount,
628				total_weight,
629				weight_info.transact_required_weight_at_most,
630				appendix,
631			)?;
632
633			// Deposit event
634			Self::deposit_event(Event::<T>::TransactedDerivative {
635				account_id: who,
636				dest,
637				call: call_bytes,
638				index,
639			});
640
641			Ok(())
642		}
643
644		/// Transact the call through the sovereign account in a destination chain,
645		/// 'fee_payer' pays for the fee
646		///
647		/// SovereignAccountDispatcherOrigin callable only
648		#[pallet::call_index(3)]
649		#[pallet::weight(
650			Pallet::<T>::weight_of_initiate_reserve_withdraw()
651			.saturating_add(T::WeightInfo::transact_through_sovereign())
652		)]
653		pub fn transact_through_sovereign(
654			origin: OriginFor<T>,
655			// destination to which the message should be sent
656			dest: Box<VersionedLocation>,
657			// account paying for fees
658			fee_payer: Option<T::AccountId>,
659			// fee to be used
660			fee: CurrencyPayment<CurrencyIdOf<T>>,
661			// call to be executed in destination
662			call: Vec<u8>,
663			// origin kind to be used
664			origin_kind: OriginKind,
665			// weight information to be used
666			weight_info: TransactWeights,
667			// add RefundSurplus and DepositAsset appendix
668			refund: bool,
669		) -> DispatchResult {
670			T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
671
672			let fee_location = Self::currency_to_multilocation(fee.currency)
673				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
674
675			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
676
677			// Calculate the total weight that the xcm message is going to spend in the
678			// destination chain
679			let total_weight = weight_info.overall_weight.map_or_else(
680				|| -> Result<_, DispatchError> {
681					let weight_info = Self::take_weight_from_transact_info(
682						dest.clone(),
683						weight_info.transact_required_weight_at_most,
684						refund,
685					)?;
686					Ok(WeightLimit::from(Some(weight_info)))
687				},
688				|v| Ok(v),
689			)?;
690
691			let total_weight_fee_calculation = match total_weight {
692				Unlimited => MAX_WEIGHT,
693				Limited(x) => x,
694			};
695
696			// Calculate fee based on FeePerSecond and total_weight
697			let fee = Self::calculate_fee(
698				fee_location,
699				fee.fee_amount,
700				dest.clone(),
701				total_weight_fee_calculation,
702			)?;
703
704			// If refund is true, the appendix instruction will be a deposit back to the sovereign
705			let appendix = refund
706				.then(|| -> Result<_, DispatchError> {
707					Ok(vec![
708						RefundSurplus,
709						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
710					])
711				})
712				.transpose()?;
713
714			// Grab the destination
715			Self::transact_in_dest_chain_asset_non_signed(
716				dest.clone(),
717				fee_payer.clone(),
718				fee,
719				call.clone(),
720				origin_kind,
721				total_weight,
722				weight_info.transact_required_weight_at_most,
723				appendix,
724			)?;
725
726			// Deposit event
727			Self::deposit_event(Event::<T>::TransactedSovereign {
728				fee_payer,
729				dest,
730				call,
731			});
732
733			Ok(())
734		}
735
736		/// Change the transact info of a location
737		#[pallet::call_index(4)]
738		#[pallet::weight(T::WeightInfo::set_transact_info())]
739		pub fn set_transact_info(
740			origin: OriginFor<T>,
741			location: Box<VersionedLocation>,
742			transact_extra_weight: Weight,
743			max_weight: Weight,
744			transact_extra_weight_signed: Option<Weight>,
745		) -> DispatchResult {
746			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
747			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
748			let remote_info = RemoteTransactInfoWithMaxWeight {
749				transact_extra_weight,
750				max_weight,
751				transact_extra_weight_signed,
752			};
753
754			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
755
756			Self::deposit_event(Event::TransactInfoChanged {
757				location,
758				remote_info,
759			});
760			Ok(())
761		}
762
763		/// Remove the transact info of a location
764		#[pallet::call_index(5)]
765		#[pallet::weight(T::WeightInfo::remove_transact_info())]
766		pub fn remove_transact_info(
767			origin: OriginFor<T>,
768			location: Box<VersionedLocation>,
769		) -> DispatchResult {
770			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
771			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
772
773			// Remove transact info
774			TransactInfoWithWeightLimit::<T>::remove(&location);
775
776			Self::deposit_event(Event::TransactInfoRemoved { location });
777			Ok(())
778		}
779
780		/// Transact the call through the a signed origin in this chain
781		/// that should be converted to a transaction dispatch account in the destination chain
782		/// by any method implemented in the destination chains runtime
783		///
784		/// This time we are giving the currency as a currencyId instead of multilocation
785		#[pallet::call_index(6)]
786		#[pallet::weight(T::WeightInfo::transact_through_signed())]
787		pub fn transact_through_signed(
788			origin: OriginFor<T>,
789			// destination to which the message should be sent
790			dest: Box<VersionedLocation>,
791			// fee to be used
792			fee: CurrencyPayment<CurrencyIdOf<T>>,
793			// call to be executed in destination
794			call: Vec<u8>,
795			// weight information to be used
796			weight_info: TransactWeights,
797			// add RefundSurplus and DepositAsset appendix
798			refund: bool,
799		) -> DispatchResult {
800			let who = ensure_signed(origin)?;
801
802			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
803
804			let fee_location = Self::currency_to_multilocation(fee.currency)
805				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
806
807			// Calculate the total weight that the xcm message is going to spend in the
808			// destination chain
809			let total_weight = weight_info.overall_weight.map_or_else(
810				|| -> Result<_, DispatchError> {
811					let weight_info = Self::take_weight_from_transact_info_signed(
812						dest.clone(),
813						weight_info.transact_required_weight_at_most,
814						refund,
815					)?;
816					Ok(WeightLimit::from(Some(weight_info)))
817				},
818				|v| Ok(v),
819			)?;
820
821			let total_weight_fee_calculation = match total_weight {
822				Unlimited => MAX_WEIGHT,
823				Limited(x) => x,
824			};
825
826			// Fee to be paid
827			let fee = Self::calculate_fee(
828				fee_location,
829				fee.fee_amount,
830				dest.clone(),
831				total_weight_fee_calculation,
832			)?;
833
834			// If refund is true, the appendix instruction will be a deposit back to the sender
835			let appendix = refund
836				.then(|| -> Result<_, DispatchError> {
837					let sender = T::AccountIdToLocation::convert(who.clone());
838					Ok(vec![
839						RefundSurplus,
840						Self::deposit_instruction(sender, &dest, 1u32)?,
841					])
842				})
843				.transpose()?;
844
845			// Grab the destination
846			Self::transact_in_dest_chain_asset_signed(
847				dest.clone(),
848				who.clone(),
849				fee,
850				call.clone(),
851				OriginKind::SovereignAccount,
852				total_weight,
853				weight_info.transact_required_weight_at_most,
854				appendix,
855			)?;
856
857			// Deposit event
858			Self::deposit_event(Event::<T>::TransactedSigned {
859				fee_payer: who,
860				dest,
861				call,
862			});
863
864			Ok(())
865		}
866
867		/// Set the fee per second of an asset on its reserve chain
868		#[pallet::call_index(7)]
869		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
870		pub fn set_fee_per_second(
871			origin: OriginFor<T>,
872			asset_location: Box<VersionedLocation>,
873			fee_per_second: u128,
874		) -> DispatchResult {
875			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
876			let asset_location =
877				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
878
879			DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
880
881			Self::deposit_event(Event::DestFeePerSecondChanged {
882				location: asset_location,
883				fee_per_second,
884			});
885			Ok(())
886		}
887
888		/// Remove the fee per second of an asset on its reserve chain
889		#[pallet::call_index(8)]
890		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
891		pub fn remove_fee_per_second(
892			origin: OriginFor<T>,
893			asset_location: Box<VersionedLocation>,
894		) -> DispatchResult {
895			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
896			let asset_location =
897				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
898
899			DestinationAssetFeePerSecond::<T>::remove(&asset_location);
900
901			Self::deposit_event(Event::DestFeePerSecondRemoved {
902				location: asset_location,
903			});
904			Ok(())
905		}
906
907		/// Manage HRMP operations
908		#[pallet::call_index(9)]
909		#[pallet::weight(T::WeightInfo::hrmp_manage())]
910		pub fn hrmp_manage(
911			origin: OriginFor<T>,
912			action: HrmpOperation,
913			// fee to be used
914			fee: CurrencyPayment<CurrencyIdOf<T>>,
915			// weight information to be used
916			weight_info: TransactWeights,
917		) -> DispatchResult {
918			// WithdrawAsset
919			// BuyExecution
920			// SetAppendix(RefundSurplus, DepositAsset(sov account))
921			// Transact
922
923			// check permissions
924			match &action {
925				HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
926					<EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
927						origin,
928					)?;
929				}
930				HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
931					T::HrmpManipulatorOrigin::ensure_origin(origin)?;
932				}
933			}
934
935			// process action
936			let call_bytes = match action.clone() {
937				HrmpOperation::InitOpen(params) => {
938					Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
939						params.para_id,
940						params.proposed_max_capacity,
941						params.proposed_max_message_size,
942					))
943				}
944				HrmpOperation::Accept { para_id } => {
945					Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
946				}
947				HrmpOperation::Close(close_params) => {
948					Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
949				}
950				HrmpOperation::Cancel {
951					channel_id,
952					open_requests,
953				} => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
954					channel_id,
955					open_requests,
956				)),
957			}
958			.map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
959
960			let fee_location = Self::currency_to_multilocation(fee.currency)
961				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
962
963			// Grab the destination
964			// For hrmp, it is always parent
965			let destination = Location::parent();
966
967			// Calculate the total weight that the xcm message is going to spend in the
968			// destination chain
969			let total_weight = weight_info.overall_weight.map_or_else(
970				|| -> Result<_, DispatchError> {
971					let weight_info = Self::take_weight_from_transact_info(
972						destination.clone(),
973						weight_info.transact_required_weight_at_most,
974						false,
975					)?;
976					Ok(WeightLimit::from(Some(weight_info)))
977				},
978				|v| Ok(v),
979			)?;
980
981			let total_weight_fee_calculation = match total_weight {
982				Unlimited => MAX_WEIGHT,
983				Limited(x) => x,
984			};
985
986			let fee = Self::calculate_fee(
987				fee_location,
988				fee.fee_amount,
989				destination.clone(),
990				total_weight_fee_calculation,
991			)?;
992
993			ensure!(
994				T::MaxHrmpFee::filter_max_asset_fee(&fee),
995				Error::<T>::TooMuchFeeUsed
996			);
997
998			// The appendix instruction will be a deposit back to a self location
999			let deposit_appendix =
1000				Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
1001
1002			Self::transact_in_dest_chain_asset_non_signed(
1003				destination,
1004				None,
1005				fee,
1006				call_bytes.clone(),
1007				OriginKind::Native,
1008				total_weight,
1009				weight_info.transact_required_weight_at_most,
1010				Some(vec![RefundSurplus, deposit_appendix]),
1011			)?;
1012
1013			Self::deposit_event(Event::HrmpManagementSent { action });
1014
1015			Ok(())
1016		}
1017	}
1018
1019	impl<T: Config> Pallet<T> {
1020		fn transact_in_dest_chain_asset_non_signed(
1021			dest: Location,
1022			fee_payer: Option<T::AccountId>,
1023			fee: Asset,
1024			call: Vec<u8>,
1025			origin_kind: OriginKind,
1026			total_weight: WeightLimit,
1027			transact_required_weight_at_most: Weight,
1028			with_appendix: Option<Vec<Instruction<()>>>,
1029		) -> DispatchResult {
1030			if let Some(fee_payer) = fee_payer {
1031				// Convert origin to multilocation
1032				let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
1033
1034				// Construct the local withdraw message with the previous calculated amount
1035				// This message deducts and burns "amount" from the caller when executed
1036				T::AssetTransactor::withdraw_asset(&fee.clone().into(), &origin_as_mult, None)
1037					.map_err(|_| Error::<T>::UnableToWithdrawAsset)?;
1038			}
1039
1040			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
1041			// Transact.
1042			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
1043			// used to pay fees
1044			// BuyExecution: Buys "execution power" in the destination chain
1045			// Transact: Issues the transaction
1046			let transact_message: Xcm<()> = Self::transact_message(
1047				dest.clone(),
1048				fee,
1049				total_weight,
1050				call,
1051				transact_required_weight_at_most,
1052				origin_kind,
1053				with_appendix,
1054			)?;
1055
1056			// Send to sovereign
1057			let (ticket, _price) =
1058				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
1059					.map_err(|_| Error::<T>::ErrorValidating)?;
1060			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
1061
1062			Ok(())
1063		}
1064
1065		fn transact_in_dest_chain_asset_signed(
1066			dest: Location,
1067			fee_payer: T::AccountId,
1068			fee: Asset,
1069			call: Vec<u8>,
1070			origin_kind: OriginKind,
1071			total_weight: WeightLimit,
1072			transact_required_weight_at_most: Weight,
1073			with_appendix: Option<Vec<Instruction<()>>>,
1074		) -> DispatchResult {
1075			// Convert origin to multilocation
1076			let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
1077
1078			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
1079			// Transact.
1080			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
1081			// used to pay fees
1082			// BuyExecution: Buys "execution power" in the destination chain
1083			// Transact: Issues the transaction
1084			let mut transact_message: Xcm<()> = Self::transact_message(
1085				dest.clone(),
1086				fee,
1087				total_weight,
1088				call,
1089				transact_required_weight_at_most,
1090				origin_kind,
1091				with_appendix,
1092			)?;
1093
1094			// We append DescendOrigin as the first instruction in the message
1095			// The new message looks like DescendOrigin||WithdrawAsset||BuyExecution||
1096			// Transact.
1097			let interior: Junctions = origin_as_mult
1098				.clone()
1099				.try_into()
1100				.map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
1101			transact_message.0.insert(0, DescendOrigin(interior));
1102
1103			// Send to destination chain
1104			let (ticket, _price) =
1105				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
1106					.map_err(|_| Error::<T>::ErrorValidating)?;
1107			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
1108
1109			Ok(())
1110		}
1111
1112		/// Calculate the amount of fee based on the multilocation of the fee asset and
1113		/// the total weight to be spent
1114		fn calculate_fee(
1115			fee_location: Location,
1116			fee_amount: Option<u128>,
1117			destination: Location,
1118			total_weight: Weight,
1119		) -> Result<Asset, DispatchError> {
1120			// If amount is provided, just use it
1121			// Else, multiply weight*destination_units_per_second to see how much we should charge for
1122			// this weight execution
1123			let amount: u128 = fee_amount.map_or_else(
1124				|| {
1125					Self::take_fee_per_second_from_storage(
1126						fee_location.clone(),
1127						destination,
1128						total_weight,
1129					)
1130				},
1131				|v| Ok(v),
1132			)?;
1133
1134			// Construct Asset
1135			Ok(Asset {
1136				id: AssetId(fee_location),
1137				fun: Fungible(amount),
1138			})
1139		}
1140
1141		/// Construct the transact xcm message with the provided parameters
1142		fn transact_message(
1143			dest: Location,
1144			asset: Asset,
1145			dest_weight: WeightLimit,
1146			call: Vec<u8>,
1147			dispatch_weight: Weight,
1148			origin_kind: OriginKind,
1149			with_appendix: Option<Vec<Instruction<()>>>,
1150		) -> Result<Xcm<()>, DispatchError> {
1151			let mut instructions = vec![
1152				Self::withdraw_instruction(asset.clone(), &dest)?,
1153				Self::buy_execution(asset, &dest, dest_weight)?,
1154			];
1155			if let Some(appendix) = with_appendix {
1156				instructions.push(Self::appendix_instruction(appendix)?);
1157			}
1158			instructions.push(Transact {
1159				origin_kind,
1160				fallback_max_weight: Some(dispatch_weight),
1161				call: call.into(),
1162			});
1163			Ok(Xcm(instructions))
1164		}
1165
1166		/// Construct a buy execution xcm order with the provided parameters
1167		fn buy_execution(
1168			asset: Asset,
1169			at: &Location,
1170			weight: WeightLimit,
1171		) -> Result<Instruction<()>, DispatchError> {
1172			let universal_location = T::UniversalLocation::get();
1173			let fees = asset
1174				.reanchored(at, &universal_location)
1175				.map_err(|_| Error::<T>::CannotReanchor)?;
1176
1177			Ok(BuyExecution {
1178				fees,
1179				weight_limit: weight,
1180			})
1181		}
1182
1183		/// Construct a withdraw instruction from a sovereign account
1184		fn withdraw_instruction(
1185			asset: Asset,
1186			at: &Location,
1187		) -> Result<Instruction<()>, DispatchError> {
1188			let universal_location = T::UniversalLocation::get();
1189			let fees = asset
1190				.reanchored(at, &universal_location)
1191				.map_err(|_| Error::<T>::CannotReanchor)?;
1192
1193			Ok(WithdrawAsset(fees.into()))
1194		}
1195
1196		/// Construct a deposit instruction to a sovereign account
1197		fn deposit_instruction(
1198			mut beneficiary: Location,
1199			at: &Location,
1200			max_assets: u32,
1201		) -> Result<Instruction<()>, DispatchError> {
1202			let universal_location = T::UniversalLocation::get();
1203			beneficiary
1204				.reanchor(at, &universal_location)
1205				.map_err(|_| Error::<T>::CannotReanchor)?;
1206			Ok(DepositAsset {
1207				assets: Wild(AllCounted(max_assets)),
1208				beneficiary,
1209			})
1210		}
1211
1212		/// Construct a withdraw instruction from a sovereign account
1213		fn appendix_instruction(
1214			instructions: Vec<Instruction<()>>,
1215		) -> Result<Instruction<()>, DispatchError> {
1216			Ok(SetAppendix(Xcm(instructions)))
1217		}
1218
1219		/// Ensure `dest` has chain part and none recipient part.
1220		fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
1221			let chain_location = dest.chain_location();
1222			if *dest == chain_location {
1223				Ok(chain_location)
1224			} else {
1225				Err(Error::<T>::InvalidDest.into())
1226			}
1227		}
1228
1229		/// Check whether the transfer is allowed.
1230		///
1231		/// Returns `Err` if `asset` is not a reserved asset of `dest`,
1232		/// else returns `dest`, parachain or relay chain location.
1233		fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
1234			let dest = Self::ensure_valid_dest(dest)?;
1235
1236			let self_location = T::SelfLocation::get();
1237			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
1238
1239			let reserve =
1240				T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
1241
1242			// We only allow to transact using a reserve asset as fee
1243			ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
1244
1245			Ok(dest)
1246		}
1247
1248		/// Returns weight of `weight_of_initiate_reserve_withdraw` call.
1249		fn weight_of_initiate_reserve_withdraw() -> Weight {
1250			let dest = Location::parent();
1251
1252			// We can use whatever asset here
1253			let asset = Location::parent();
1254
1255			// Construct Asset
1256			let fee = Asset {
1257				id: AssetId(asset.clone()),
1258				fun: Fungible(0),
1259			};
1260
1261			let xcm: Xcm<()> = Xcm(vec![
1262				WithdrawAsset(fee.into()),
1263				InitiateReserveWithdraw {
1264					assets: AssetFilter::Wild(All),
1265					reserve: dest.clone(),
1266					xcm: Xcm(vec![]),
1267				},
1268			]);
1269			T::Weigher::weight(&mut xcm.into(), Weight::MAX)
1270				.map_or(Weight::MAX, |w| T::BaseXcmWeight::get().saturating_add(w))
1271		}
1272
1273		/// Returns the fee for a given set of parameters
1274		/// We always round up in case of fractional division
1275		pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
1276			// grab WEIGHT_REF_TIME_PER_SECOND as u128
1277			let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
1278
1279			// we add WEIGHT_REF_TIME_PER_SECOND -1 after multiplication to make sure that
1280			// if there is a fractional part we round up the result
1281			let fee_mul_rounded_up = (fee_per_second.saturating_mul(weight.ref_time() as u128))
1282				.saturating_add(weight_per_second_u128 - 1);
1283
1284			fee_mul_rounded_up / weight_per_second_u128
1285		}
1286
1287		/// Returns the weight information for a destination from storage
1288		/// it returns the weight to be used in non-signed cases
1289		pub fn take_weight_from_transact_info(
1290			dest: Location,
1291			dest_weight: Weight,
1292			refund: bool,
1293		) -> Result<Weight, DispatchError> {
1294			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
1295			// so we have to ensure 'refund' is false
1296			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
1297			// Grab transact info for the destination provided
1298			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
1299				.ok_or(Error::<T>::TransactorInfoNotSet)?;
1300
1301			let total_weight = dest_weight
1302				.checked_add(&transactor_info.transact_extra_weight)
1303				.ok_or(Error::<T>::WeightOverflow)?;
1304
1305			ensure!(
1306				total_weight.all_lte(transactor_info.max_weight),
1307				Error::<T>::MaxWeightTransactReached
1308			);
1309			Ok(total_weight)
1310		}
1311
1312		/// Returns the weight information for a destination from storage
1313		/// it returns the weight to be used in signed cases
1314		pub fn take_weight_from_transact_info_signed(
1315			dest: Location,
1316			dest_weight: Weight,
1317			refund: bool,
1318		) -> Result<Weight, DispatchError> {
1319			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
1320			// so we have to ensure 'refund' is false
1321			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
1322			// Grab transact info for the destination provided
1323			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
1324				.ok_or(Error::<T>::TransactorInfoNotSet)?;
1325
1326			// If this storage item is not set, it means that the destination chain
1327			// does not support this kind of transact message
1328			let transact_in_dest_as_signed_weight = transactor_info
1329				.transact_extra_weight_signed
1330				.ok_or(Error::<T>::SignedTransactNotAllowedForDestination)?;
1331
1332			let total_weight = dest_weight
1333				.checked_add(&transact_in_dest_as_signed_weight)
1334				.ok_or(Error::<T>::WeightOverflow)?;
1335
1336			ensure!(
1337				total_weight.all_lte(transactor_info.max_weight),
1338				Error::<T>::MaxWeightTransactReached
1339			);
1340			Ok(total_weight)
1341		}
1342
1343		/// Returns the fee per second charged by a reserve chain for an asset
1344		/// it takes this information from storage
1345		pub fn take_fee_per_second_from_storage(
1346			fee_location: Location,
1347			destination: Location,
1348			total_weight: Weight,
1349		) -> Result<u128, DispatchError> {
1350			let fee_per_second = DestinationAssetFeePerSecond::<T>::get(&fee_location)
1351				.ok_or(Error::<T>::FeePerSecondNotSet)?;
1352
1353			// Ensure the asset is a reserve
1354			// We only store information about asset fee per second on its reserve chain
1355			// if amount is provided, we first check whether we have this information
1356			Self::transfer_allowed(&(fee_location, fee_per_second).into(), &destination)?;
1357
1358			Ok(Self::calculate_fee_per_second(total_weight, fee_per_second))
1359		}
1360
1361		/// Converts Currency to multilocation
1362		pub fn currency_to_multilocation(currency: Currency<CurrencyIdOf<T>>) -> Option<Location> {
1363			match currency {
1364				Currency::AsCurrencyId(id) => T::CurrencyIdToLocation::convert(id),
1365				Currency::AsMultiLocation(multiloc) => Location::try_from(*multiloc).ok(),
1366			}
1367		}
1368	}
1369}