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

            
70
use frame_support::pallet;
71
use frame_support::weights::Weight;
72
use sp_runtime::DispatchError;
73
use xcm::latest::prelude::Location;
74
use xcm_primitives::XcmFeeTrader;
75

            
76
pub use pallet::*;
77

            
78
#[cfg(any(test, feature = "runtime-benchmarks"))]
79
mod benchmarks;
80

            
81
#[cfg(test)]
82
pub(crate) mod mock;
83
#[cfg(test)]
84
mod tests;
85

            
86
pub mod encode;
87
pub mod migrations;
88
pub mod relay_indices;
89
pub mod weights;
90
pub use crate::weights::WeightInfo;
91

            
92
type CurrencyIdOf<T> = <T as Config>::CurrencyId;
93

            
94
/// TODO: Temporary (Will be removed when we have a proper way of getting pallet indices)
95
/// Same index on both Polkadot and Kusama Asset Hub
96
/// Kusama: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs#L1596
97
/// Polkadot: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs#L1400
98
pub const ASSET_HUB_UTILITY_PALLET_INDEX: u8 = 40;
99

            
100
/// TODO: Temporary (Will be removed when we have a proper way of getting pallet indices)
101
/// Same index on both Polkadot and Kusama Asset Hub
102
/// Kusama: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs#L1628
103
/// Polkadot: https://github.com/polkadot-fellows/runtimes/blob/release-v2.0.0/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs#L1434
104
pub const ASSET_HUB_STAKING_PALLET_INDEX: u8 = 89;
105

            
106
#[pallet]
107
pub mod pallet {
108

            
109
	use super::*;
110
	use crate::relay_indices::RelayChainIndices;
111
	use crate::weights::WeightInfo;
112
	use crate::CurrencyIdOf;
113
	use cumulus_primitives_core::{relay_chain::HrmpChannelId, ParaId};
114
	use frame_support::traits::EitherOfDiverse;
115
	use frame_support::{dispatch::DispatchResult, pallet_prelude::*};
116
	use frame_system::{ensure_signed, pallet_prelude::*};
117
	use sp_runtime::traits::{AtLeast32BitUnsigned, Convert};
118
	use sp_std::boxed::Box;
119
	use sp_std::convert::TryFrom;
120
	use sp_std::prelude::*;
121
	use sp_std::vec;
122
	use sp_std::vec::Vec;
123
	use xcm::{latest::prelude::*, VersionedLocation};
124
	use xcm_executor::traits::{TransactAsset, WeightBounds};
125
	use xcm_primitives::{
126
		FilterMaxAssetFee, HrmpAvailableCalls, HrmpEncodeCall, UtilityAvailableCalls,
127
		UtilityEncodeCall, XcmTransact,
128
	};
129

            
130
	#[pallet::pallet]
131
	#[pallet::without_storage_info]
132
	pub struct Pallet<T>(pub PhantomData<T>);
133

            
134
	#[pallet::config]
135
	pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
136
		/// The balance type.
137
		type Balance: Parameter
138
			+ Member
139
			+ AtLeast32BitUnsigned
140
			+ Default
141
			+ Copy
142
			+ MaybeSerializeDeserialize
143
			+ Into<u128>;
144

            
145
		/// Currency Id.
146
		type CurrencyId: Parameter + Member + Clone;
147

            
148
		/// Convert `T::CurrencyId` to `Location`.
149
		type CurrencyIdToLocation: Convert<Self::CurrencyId, Option<Location>>;
150

            
151
		// XcmTransact needs to be implemented. This type needs to implement
152
		// utility call encoding and multilocation gathering
153
		type Transactor: Parameter + Member + Clone + XcmTransact;
154

            
155
		/// AssetTransactor allows us to withdraw asset without being trapped
156
		/// This should change in xcm v3, which allows us to burn assets
157
		type AssetTransactor: TransactAsset;
158

            
159
		// The origin that is allowed to register derivative address indices
160
		type DerivativeAddressRegistrationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
161

            
162
		// The origin that is allowed to manipulate (open, close, accept, cancel) an Hrmp channel
163
		type HrmpManipulatorOrigin: EnsureOrigin<Self::RuntimeOrigin>;
164

            
165
		// The origin that is allowed to open and accept an Hrmp channel
166
		type HrmpOpenOrigin: EnsureOrigin<Self::RuntimeOrigin>;
167

            
168
		/// Convert `T::AccountId` to `Location`.
169
		type AccountIdToLocation: Convert<Self::AccountId, Location>;
170

            
171
		/// Means of measuring the weight consumed by an XCM message locally.
172
		type Weigher: WeightBounds<Self::RuntimeCall>;
173

            
174
		/// This chain's Universal Location.
175
		type UniversalLocation: Get<InteriorLocation>;
176

            
177
		/// Self chain location.
178
		#[pallet::constant]
179
		type SelfLocation: Get<Location>;
180

            
181
		// The origin that is allowed to dispatch calls from the sovereign account directly
182
		type SovereignAccountDispatcherOrigin: EnsureOrigin<Self::RuntimeOrigin>;
183

            
184
		/// XCM sender.
185
		type XcmSender: SendXcm;
186

            
187
		// Base XCM weight.
188
		///
189
		/// The actual weight for an XCM message is `T::BaseXcmWeight +
190
		/// T::Weigher::weight(&msg)`.
191
		#[pallet::constant]
192
		type BaseXcmWeight: Get<Weight>;
193

            
194
		/// The way to retrieve the reserve of a Asset. This can be
195
		/// configured to accept absolute or relative paths for self tokens
196
		type ReserveProvider: xcm_primitives::Reserve;
197

            
198
		/// The way to filter the max fee to use for HRMP management operations
199
		type MaxHrmpFee: FilterMaxAssetFee;
200

            
201
		/// Fee trader for computing XCM fees and managing asset pricing.
202
		/// This replaces the old `DestinationAssetFeePerSecond` storage-based approach.
203
		type FeeTrader: XcmFeeTrader;
204

            
205
		type WeightInfo: WeightInfo;
206
	}
207

            
208
	/// Stores the information to be able to issue a transact operation in another chain use an
209
	/// asset as fee payer.
210
	#[derive(
211
		Default,
212
		Clone,
213
		Encode,
214
		Decode,
215
		MaxEncodedLen,
216
		RuntimeDebug,
217
		Eq,
218
		PartialEq,
219
		scale_info::TypeInfo,
220
		DecodeWithMemTracking,
221
	)]
222
	pub struct RemoteTransactInfoWithMaxWeight {
223
		/// Extra weight that transacting a call in a destination chain adds
224
		/// Extra weight involved when transacting without DescendOrigin
225
		/// This should always be possible in a destination chain, since
226
		/// it involves going through the sovereign account
227
		pub transact_extra_weight: Weight,
228
		/// Max destination weight
229
		pub max_weight: Weight,
230
		/// Whether we allow transacting through signed origins in another chain, and
231
		/// how much extra cost implies
232
		/// Extra weight involved when transacting with DescendOrigin
233
		/// The reason for it being an option is because the destination chain
234
		/// might not support constructing origins based on generic MultiLocations
235
		pub transact_extra_weight_signed: Option<Weight>,
236
	}
237

            
238
	/// Enum defining the way to express a Currency.
239
	#[derive(
240
		Clone,
241
		Encode,
242
		Decode,
243
		Eq,
244
		PartialEq,
245
		RuntimeDebug,
246
		scale_info::TypeInfo,
247
		DecodeWithMemTracking,
248
	)]
249
	pub enum Currency<CurrencyId> {
250
		// Express the Currency as a CurrencyId
251
		AsCurrencyId(CurrencyId),
252
		// Express the Currency as its MultiLOcation
253
		AsMultiLocation(Box<VersionedLocation>),
254
	}
255

            
256
	impl<T> Default for Currency<T> {
257
		fn default() -> Currency<T> {
258
			Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
259
		}
260
	}
261

            
262
	#[derive(
263
		Clone,
264
		Encode,
265
		Decode,
266
		Eq,
267
		PartialEq,
268
		RuntimeDebug,
269
		scale_info::TypeInfo,
270
		DecodeWithMemTracking,
271
	)]
272
	pub struct HrmpInitParams {
273
		pub para_id: ParaId,
274
		pub proposed_max_capacity: u32,
275
		pub proposed_max_message_size: u32,
276
	}
277

            
278
	/// Enum defining the way to express a Currency.
279
	#[derive(
280
		Clone,
281
		Encode,
282
		Decode,
283
		Eq,
284
		PartialEq,
285
		RuntimeDebug,
286
		scale_info::TypeInfo,
287
		DecodeWithMemTracking,
288
	)]
289
	pub enum HrmpOperation {
290
		InitOpen(HrmpInitParams),
291
		Accept {
292
			para_id: ParaId,
293
		},
294
		Close(HrmpChannelId),
295
		Cancel {
296
			channel_id: HrmpChannelId,
297
			open_requests: u32,
298
		},
299
	}
300

            
301
	#[derive(
302
		Default,
303
		Clone,
304
		Encode,
305
		Decode,
306
		Eq,
307
		PartialEq,
308
		RuntimeDebug,
309
		MaxEncodedLen,
310
		scale_info::TypeInfo,
311
		DecodeWithMemTracking,
312
	)]
313

            
314
	/// Struct that defines how to express the payment in a particular currency
315
	/// currency is defined by the Currency enum, which can be expressed as:
316
	/// - CurrencyId
317
	/// - Location
318
	///
319
	/// The fee_amount is an option. In case of None, the fee will be tried to
320
	/// be calculated from storage. If the storage item for the currency is not
321
	/// populated, then it fails
322
	pub struct CurrencyPayment<CurrencyId> {
323
		// the currency in which we want to express our payment
324
		pub currency: Currency<CurrencyId>,
325
		// indicates whether we want to specify the fee amount to be used
326
		pub fee_amount: Option<u128>,
327
	}
328

            
329
	#[derive(
330
		Default,
331
		Clone,
332
		Encode,
333
		Decode,
334
		RuntimeDebug,
335
		PartialEq,
336
		scale_info::TypeInfo,
337
		DecodeWithMemTracking,
338
	)]
339
	/// Struct tindicating information about transact weights
340
	/// It allows to specify:
341
	/// - transact_required_weight_at_most: the amount of weight the Transact instruction
342
	///   should consume at most
343
	/// - overall_weight: the overall weight to be used for the whole XCM message execution.
344
	///   If None, then this amount will be tried to be derived from storage.  If the storage item
345
	pub struct TransactWeights {
346
		// the amount of weight the Transact instruction should consume at most
347
		pub transact_required_weight_at_most: Weight,
348
		// the overall weight to be used for the whole XCM message execution. If None,
349
		// then this amount will be tried to be derived from storage.  If the storage item
350
		// for the chain is not populated, then it fails
351
		pub overall_weight: Option<WeightLimit>,
352
	}
353

            
354
	/// The amount of ref_time and proof_size to use for fee calculation if
355
	/// we are dealing with an Unlimited variant inside 'overall_weight' field
356
	/// of 'TransactWeights' struct.
357
	pub const MAX_WEIGHT: Weight = Weight::from_parts(100_000_000_000, 100_000);
358

            
359
	/// Since we are using pallet-utility for account derivation (through AsDerivative),
360
	/// we need to provide an index for the account derivation. This storage item stores the index
361
	/// assigned for a given local account. These indices are usable as derivative in the relay chain
362
	#[pallet::storage]
363
	#[pallet::getter(fn index_to_account)]
364
	pub type IndexToAccount<T: Config> = StorageMap<_, Blake2_128Concat, u16, T::AccountId>;
365

            
366
	/// Stores the transact info of a Location. This defines how much extra weight we need to
367
	/// add when we want to transact in the destination chain and maximum amount of weight allowed
368
	/// by the destination chain
369
	#[pallet::storage]
370
	#[pallet::getter(fn transact_info)]
371
	pub type TransactInfoWithWeightLimit<T: Config> =
372
		StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
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
		// Removed: FeePerSecondNotSet (was index 20)
403
		// This error was removed. Fee-related errors are now handled by pallet-xcm-weight-trader.
404
		#[codec(index = 21)]
405
		SignedTransactNotAllowedForDestination,
406
		#[codec(index = 22)]
407
		FailedMultiLocationToJunction,
408
		#[codec(index = 23)]
409
		HrmpHandlerNotImplemented,
410
		#[codec(index = 24)]
411
		TooMuchFeeUsed,
412
		#[codec(index = 25)]
413
		ErrorValidating,
414
		#[codec(index = 26)]
415
		RefundNotSupportedWithTransactInfo,
416
	}
417

            
418
	#[pallet::event]
419
	#[pallet::generate_deposit(pub(crate) fn deposit_event)]
420
	pub enum Event<T: Config> {
421
		/// Transacted the inner call through a derivative account in a destination chain.
422
		TransactedDerivative {
423
			account_id: T::AccountId,
424
			dest: Location,
425
			call: Vec<u8>,
426
			index: u16,
427
		},
428
		/// Transacted the call through the sovereign account in a destination chain.
429
		TransactedSovereign {
430
			fee_payer: Option<T::AccountId>,
431
			dest: Location,
432
			call: Vec<u8>,
433
		},
434
		/// Transacted the call through a signed account in a destination chain.
435
		TransactedSigned {
436
			fee_payer: T::AccountId,
437
			dest: Location,
438
			call: Vec<u8>,
439
		},
440
		/// Registered a derivative index for an account id.
441
		RegisteredDerivative {
442
			account_id: T::AccountId,
443
			index: u16,
444
		},
445
		DeRegisteredDerivative {
446
			index: u16,
447
		},
448
		/// Transact failed
449
		TransactFailed {
450
			error: XcmError,
451
		},
452
		/// Changed the transact info of a location
453
		TransactInfoChanged {
454
			location: Location,
455
			remote_info: RemoteTransactInfoWithMaxWeight,
456
		},
457
		/// Removed the transact info of a location
458
		TransactInfoRemoved {
459
			location: Location,
460
		},
461
		// Removed: DestFeePerSecondChanged (was index 8)
462
		// This event was removed. Fee configuration events are now emitted by pallet-xcm-weight-trader.
463
		// Removed: DestFeePerSecondRemoved (was index 9)
464
		// This event was removed. Fee removal events are now emitted by pallet-xcm-weight-trader.
465
		/// HRMP manage action succesfully sent
466
		#[codec(index = 10)]
467
		HrmpManagementSent {
468
			action: HrmpOperation,
469
		},
470
	}
471

            
472
	#[pallet::genesis_config]
473
	pub struct GenesisConfig<T> {
474
		pub relay_indices: RelayChainIndices,
475
		#[serde(skip)]
476
		pub _phantom: PhantomData<T>,
477
	}
478

            
479
	impl<T> Default for GenesisConfig<T> {
480
737
		fn default() -> Self {
481
737
			Self {
482
737
				relay_indices: RelayChainIndices::default(),
483
737
				_phantom: Default::default(),
484
737
			}
485
737
		}
486
	}
487

            
488
	#[pallet::genesis_build]
489
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
490
735
		fn build(&self) {
491
735
			<RelayIndices<T>>::put(self.relay_indices);
492
735
		}
493
	}
494

            
495
	#[pallet::call]
496
	impl<T: Config> Pallet<T> {
497
		/// Register a derivative index for an account id. Dispatchable by
498
		/// DerivativeAddressRegistrationOrigin
499
		///
500
		/// We do not store the derivative address, but only the index. We do not need to store
501
		/// the derivative address to issue calls, only the index is enough
502
		///
503
		/// For now an index is registered for all possible destinations and not per-destination.
504
		/// We can change this in the future although it would just make things more complicated
505
		#[pallet::call_index(0)]
506
		#[pallet::weight(T::WeightInfo::register())]
507
41
		pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
508
41
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
509

            
510
40
			ensure!(
511
40
				IndexToAccount::<T>::get(&index).is_none(),
512
				Error::<T>::IndexAlreadyClaimed
513
			);
514

            
515
40
			IndexToAccount::<T>::insert(&index, who.clone());
516

            
517
			// Deposit event
518
40
			Self::deposit_event(Event::<T>::RegisteredDerivative {
519
40
				account_id: who,
520
40
				index: index,
521
40
			});
522

            
523
40
			Ok(())
524
		}
525

            
526
		/// De-Register a derivative index. This prevents an account to use a derivative address
527
		/// (represented by an index) from our of our sovereign accounts anymore
528
		#[pallet::call_index(1)]
529
		#[pallet::weight(T::WeightInfo::deregister())]
530
1
		pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
531
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
532

            
533
			// Remove index
534
1
			IndexToAccount::<T>::remove(&index);
535

            
536
			// Deposit event
537
1
			Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
538

            
539
1
			Ok(())
540
		}
541

            
542
		/// Transact the inner call through a derivative account in a destination chain,
543
		/// using 'fee_location' to pay for the fees. This fee_location is given as a multilocation
544
		///
545
		/// The caller needs to have the index registered in this pallet. The fee multiasset needs
546
		/// to be a reserve asset for the destination transactor::multilocation.
547
		#[pallet::call_index(2)]
548
		#[pallet::weight(
549
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
550
			.saturating_add(T::WeightInfo::transact_through_derivative())
551
		)]
552
		pub fn transact_through_derivative(
553
			origin: OriginFor<T>,
554
			// destination to which the message should be sent
555
			dest: T::Transactor,
556
			// derivative index to be used
557
			index: u16,
558
			// fee to be used
559
			fee: CurrencyPayment<CurrencyIdOf<T>>,
560
			// inner call to be executed in destination. This wiol
561
			// be wrapped into utility.as_derivative
562
			inner_call: Vec<u8>,
563
			// weight information to be used
564
			weight_info: TransactWeights,
565
			// add RefundSurplus and DepositAsset appendix
566
			refund: bool,
567
31
		) -> DispatchResult {
568
31
			let who = ensure_signed(origin)?;
569

            
570
31
			let fee_location = Self::currency_to_multilocation(fee.currency)
571
31
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
572

            
573
			// The index exists
574
31
			let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
575
			// The derivative index is owned by the origin
576
30
			ensure!(account == who, Error::<T>::NotOwner);
577

            
578
			// Encode call bytes
579
			// We make sure the inner call is wrapped on a as_derivative dispatchable
580
30
			let call_bytes: Vec<u8> = <Self as UtilityEncodeCall>::encode_call(
581
30
				dest.clone(),
582
30
				UtilityAvailableCalls::AsDerivative(index, inner_call),
583
			);
584

            
585
			// Grab the destination
586
30
			let dest = dest.destination();
587

            
588
			// Calculate the total weight that the xcm message is going to spend in the
589
			// destination chain
590
30
			let total_weight = weight_info.overall_weight.map_or_else(
591
18
				|| -> Result<_, DispatchError> {
592
18
					let weight_info = Self::take_weight_from_transact_info(
593
18
						dest.clone(),
594
18
						weight_info.transact_required_weight_at_most,
595
18
						refund,
596
9
					)?;
597
9
					Ok(WeightLimit::from(Some(weight_info)))
598
18
				},
599
12
				|v| Ok(v),
600
9
			)?;
601

            
602
21
			let total_weight_fee_calculation = match total_weight {
603
				Unlimited => MAX_WEIGHT,
604
21
				Limited(x) => x,
605
			};
606

            
607
			// Calculate fee based on FeePerSecond
608
21
			let fee = Self::calculate_fee(
609
21
				fee_location,
610
21
				fee.fee_amount,
611
21
				dest.clone(),
612
21
				total_weight_fee_calculation,
613
2
			)?;
614

            
615
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
616
19
			let appendix = refund
617
19
				.then(|| -> Result<_, DispatchError> {
618
4
					Ok(vec![
619
4
						RefundSurplus,
620
4
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
621
					])
622
4
				})
623
19
				.transpose()?;
624

            
625
19
			Self::transact_in_dest_chain_asset_non_signed(
626
19
				dest.clone(),
627
19
				Some(who.clone()),
628
19
				fee,
629
19
				call_bytes.clone(),
630
19
				OriginKind::SovereignAccount,
631
19
				total_weight,
632
19
				weight_info.transact_required_weight_at_most,
633
19
				appendix,
634
			)?;
635

            
636
			// Deposit event
637
19
			Self::deposit_event(Event::<T>::TransactedDerivative {
638
19
				account_id: who,
639
19
				dest,
640
19
				call: call_bytes,
641
19
				index,
642
19
			});
643

            
644
19
			Ok(())
645
		}
646

            
647
		/// Transact the call through the sovereign account in a destination chain,
648
		/// 'fee_payer' pays for the fee
649
		///
650
		/// SovereignAccountDispatcherOrigin callable only
651
		#[pallet::call_index(3)]
652
		#[pallet::weight(
653
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
654
			.saturating_add(T::WeightInfo::transact_through_sovereign())
655
		)]
656
		pub fn transact_through_sovereign(
657
			origin: OriginFor<T>,
658
			// destination to which the message should be sent
659
			dest: Box<VersionedLocation>,
660
			// account paying for fees
661
			fee_payer: Option<T::AccountId>,
662
			// fee to be used
663
			fee: CurrencyPayment<CurrencyIdOf<T>>,
664
			// call to be executed in destination
665
			call: Vec<u8>,
666
			// origin kind to be used
667
			origin_kind: OriginKind,
668
			// weight information to be used
669
			weight_info: TransactWeights,
670
			// add RefundSurplus and DepositAsset appendix
671
			refund: bool,
672
16
		) -> DispatchResult {
673
16
			T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
674

            
675
15
			let fee_location = Self::currency_to_multilocation(fee.currency)
676
15
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
677

            
678
15
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
679

            
680
			// Calculate the total weight that the xcm message is going to spend in the
681
			// destination chain
682
15
			let total_weight = weight_info.overall_weight.map_or_else(
683
7
				|| -> Result<_, DispatchError> {
684
7
					let weight_info = Self::take_weight_from_transact_info(
685
7
						dest.clone(),
686
7
						weight_info.transact_required_weight_at_most,
687
7
						refund,
688
					)?;
689
7
					Ok(WeightLimit::from(Some(weight_info)))
690
7
				},
691
8
				|v| Ok(v),
692
			)?;
693

            
694
15
			let total_weight_fee_calculation = match total_weight {
695
				Unlimited => MAX_WEIGHT,
696
15
				Limited(x) => x,
697
			};
698

            
699
			// Calculate fee based on FeePerSecond and total_weight
700
15
			let fee = Self::calculate_fee(
701
15
				fee_location,
702
15
				fee.fee_amount,
703
15
				dest.clone(),
704
15
				total_weight_fee_calculation,
705
			)?;
706

            
707
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
708
15
			let appendix = refund
709
15
				.then(|| -> Result<_, DispatchError> {
710
3
					Ok(vec![
711
3
						RefundSurplus,
712
3
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
713
					])
714
3
				})
715
15
				.transpose()?;
716

            
717
			// Grab the destination
718
15
			Self::transact_in_dest_chain_asset_non_signed(
719
15
				dest.clone(),
720
15
				fee_payer.clone(),
721
15
				fee,
722
15
				call.clone(),
723
15
				origin_kind,
724
15
				total_weight,
725
15
				weight_info.transact_required_weight_at_most,
726
15
				appendix,
727
			)?;
728

            
729
			// Deposit event
730
15
			Self::deposit_event(Event::<T>::TransactedSovereign {
731
15
				fee_payer,
732
15
				dest,
733
15
				call,
734
15
			});
735

            
736
15
			Ok(())
737
		}
738

            
739
		/// Change the transact info of a location
740
		#[pallet::call_index(4)]
741
		#[pallet::weight(T::WeightInfo::set_transact_info())]
742
		pub fn set_transact_info(
743
			origin: OriginFor<T>,
744
			location: Box<VersionedLocation>,
745
			transact_extra_weight: Weight,
746
			max_weight: Weight,
747
			transact_extra_weight_signed: Option<Weight>,
748
49
		) -> DispatchResult {
749
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
750
49
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
751
49
			let remote_info = RemoteTransactInfoWithMaxWeight {
752
49
				transact_extra_weight,
753
49
				max_weight,
754
49
				transact_extra_weight_signed,
755
49
			};
756

            
757
49
			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
758

            
759
49
			Self::deposit_event(Event::TransactInfoChanged {
760
49
				location,
761
49
				remote_info,
762
49
			});
763
49
			Ok(())
764
		}
765

            
766
		/// Remove the transact info of a location
767
		#[pallet::call_index(5)]
768
		#[pallet::weight(T::WeightInfo::remove_transact_info())]
769
		pub fn remove_transact_info(
770
			origin: OriginFor<T>,
771
			location: Box<VersionedLocation>,
772
1
		) -> DispatchResult {
773
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
774
1
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
775

            
776
			// Remove transact info
777
1
			TransactInfoWithWeightLimit::<T>::remove(&location);
778

            
779
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
780
1
			Ok(())
781
		}
782

            
783
		/// Transact the call through the a signed origin in this chain
784
		/// that should be converted to a transaction dispatch account in the destination chain
785
		/// by any method implemented in the destination chains runtime
786
		///
787
		/// This time we are giving the currency as a currencyId instead of multilocation
788
		#[pallet::call_index(6)]
789
		#[pallet::weight(T::WeightInfo::transact_through_signed())]
790
		pub fn transact_through_signed(
791
			origin: OriginFor<T>,
792
			// destination to which the message should be sent
793
			dest: Box<VersionedLocation>,
794
			// fee to be used
795
			fee: CurrencyPayment<CurrencyIdOf<T>>,
796
			// call to be executed in destination
797
			call: Vec<u8>,
798
			// weight information to be used
799
			weight_info: TransactWeights,
800
			// add RefundSurplus and DepositAsset appendix
801
			refund: bool,
802
86
		) -> DispatchResult {
803
86
			let who = ensure_signed(origin)?;
804

            
805
86
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
806

            
807
86
			let fee_location = Self::currency_to_multilocation(fee.currency)
808
86
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
809

            
810
			// Calculate the total weight that the xcm message is going to spend in the
811
			// destination chain
812
86
			let total_weight = weight_info.overall_weight.map_or_else(
813
33
				|| -> Result<_, DispatchError> {
814
33
					let weight_info = Self::take_weight_from_transact_info_signed(
815
33
						dest.clone(),
816
33
						weight_info.transact_required_weight_at_most,
817
33
						refund,
818
6
					)?;
819
27
					Ok(WeightLimit::from(Some(weight_info)))
820
33
				},
821
53
				|v| Ok(v),
822
6
			)?;
823

            
824
80
			let total_weight_fee_calculation = match total_weight {
825
				Unlimited => MAX_WEIGHT,
826
80
				Limited(x) => x,
827
			};
828

            
829
			// Fee to be paid
830
80
			let fee = Self::calculate_fee(
831
80
				fee_location,
832
80
				fee.fee_amount,
833
80
				dest.clone(),
834
80
				total_weight_fee_calculation,
835
3
			)?;
836

            
837
			// If refund is true, the appendix instruction will be a deposit back to the sender
838
77
			let appendix = refund
839
77
				.then(|| -> Result<_, DispatchError> {
840
7
					let sender = T::AccountIdToLocation::convert(who.clone());
841
7
					Ok(vec![
842
7
						RefundSurplus,
843
7
						Self::deposit_instruction(sender, &dest, 1u32)?,
844
					])
845
7
				})
846
77
				.transpose()?;
847

            
848
			// Grab the destination
849
77
			Self::transact_in_dest_chain_asset_signed(
850
77
				dest.clone(),
851
77
				who.clone(),
852
77
				fee,
853
77
				call.clone(),
854
77
				OriginKind::SovereignAccount,
855
77
				total_weight,
856
77
				weight_info.transact_required_weight_at_most,
857
77
				appendix,
858
19
			)?;
859

            
860
			// Deposit event
861
58
			Self::deposit_event(Event::<T>::TransactedSigned {
862
58
				fee_payer: who,
863
58
				dest,
864
58
				call,
865
58
			});
866

            
867
58
			Ok(())
868
		}
869

            
870
		// Removed: set_fee_per_second (was call_index 7)
871
		// This extrinsic was removed. Fee configuration is now handled by pallet-xcm-weight-trader.
872

            
873
		// Removed: remove_fee_per_second (was call_index 8)
874
		// This extrinsic was removed. Fee removal is now handled by pallet-xcm-weight-trader.
875

            
876
		/// Manage HRMP operations
877
		#[pallet::call_index(9)]
878
		#[pallet::weight(T::WeightInfo::hrmp_manage())]
879
		pub fn hrmp_manage(
880
			origin: OriginFor<T>,
881
			action: HrmpOperation,
882
			// fee to be used
883
			fee: CurrencyPayment<CurrencyIdOf<T>>,
884
			// weight information to be used
885
			weight_info: TransactWeights,
886
18
		) -> DispatchResult {
887
			// WithdrawAsset
888
			// BuyExecution
889
			// SetAppendix(RefundSurplus, DepositAsset(sov account))
890
			// Transact
891

            
892
			// check permissions
893
18
			match &action {
894
				HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
895
13
					<EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
896
13
						origin,
897
					)?;
898
				}
899
				HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
900
5
					T::HrmpManipulatorOrigin::ensure_origin(origin)?;
901
				}
902
			}
903

            
904
			// process action
905
18
			let call_bytes = match action.clone() {
906
8
				HrmpOperation::InitOpen(params) => {
907
8
					Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
908
8
						params.para_id,
909
8
						params.proposed_max_capacity,
910
8
						params.proposed_max_message_size,
911
8
					))
912
				}
913
5
				HrmpOperation::Accept { para_id } => {
914
5
					Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
915
				}
916
4
				HrmpOperation::Close(close_params) => {
917
4
					Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
918
				}
919
				HrmpOperation::Cancel {
920
1
					channel_id,
921
1
					open_requests,
922
1
				} => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
923
1
					channel_id,
924
1
					open_requests,
925
1
				)),
926
			}
927
18
			.map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
928

            
929
18
			let fee_location = Self::currency_to_multilocation(fee.currency)
930
18
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
931

            
932
			// Grab the destination
933
			// For hrmp, it is always parent
934
18
			let destination = Location::parent();
935

            
936
			// Calculate the total weight that the xcm message is going to spend in the
937
			// destination chain
938
18
			let total_weight = weight_info.overall_weight.map_or_else(
939
				|| -> Result<_, DispatchError> {
940
					let weight_info = Self::take_weight_from_transact_info(
941
						destination.clone(),
942
						weight_info.transact_required_weight_at_most,
943
						false,
944
					)?;
945
					Ok(WeightLimit::from(Some(weight_info)))
946
				},
947
18
				|v| Ok(v),
948
			)?;
949

            
950
18
			let total_weight_fee_calculation = match total_weight {
951
				Unlimited => MAX_WEIGHT,
952
18
				Limited(x) => x,
953
			};
954

            
955
18
			let fee = Self::calculate_fee(
956
18
				fee_location,
957
18
				fee.fee_amount,
958
18
				destination.clone(),
959
18
				total_weight_fee_calculation,
960
			)?;
961

            
962
18
			ensure!(
963
18
				T::MaxHrmpFee::filter_max_asset_fee(&fee),
964
1
				Error::<T>::TooMuchFeeUsed
965
			);
966

            
967
			// The appendix instruction will be a deposit back to a self location
968
17
			let deposit_appendix =
969
17
				Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
970

            
971
17
			Self::transact_in_dest_chain_asset_non_signed(
972
17
				destination,
973
17
				None,
974
17
				fee,
975
17
				call_bytes.clone(),
976
17
				OriginKind::Native,
977
17
				total_weight,
978
17
				weight_info.transact_required_weight_at_most,
979
17
				Some(vec![RefundSurplus, deposit_appendix]),
980
2
			)?;
981

            
982
15
			Self::deposit_event(Event::HrmpManagementSent { action });
983

            
984
15
			Ok(())
985
		}
986
	}
987

            
988
	impl<T: Config> Pallet<T> {
989
51
		fn transact_in_dest_chain_asset_non_signed(
990
51
			dest: Location,
991
51
			fee_payer: Option<T::AccountId>,
992
51
			fee: Asset,
993
51
			call: Vec<u8>,
994
51
			origin_kind: OriginKind,
995
51
			total_weight: WeightLimit,
996
51
			transact_required_weight_at_most: Weight,
997
51
			with_appendix: Option<Vec<Instruction<()>>>,
998
51
		) -> DispatchResult {
999
51
			if let Some(fee_payer) = fee_payer {
				// Convert origin to multilocation
30
				let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
				// Construct the local withdraw message with the previous calculated amount
				// This message deducts and burns "amount" from the caller when executed
30
				T::AssetTransactor::withdraw_asset(&fee.clone().into(), &origin_as_mult, None)
30
					.map_err(|_| Error::<T>::UnableToWithdrawAsset)?;
21
			}
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
			// Transact.
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
			// used to pay fees
			// BuyExecution: Buys "execution power" in the destination chain
			// Transact: Issues the transaction
51
			let transact_message: Xcm<()> = Self::transact_message(
51
				dest.clone(),
51
				fee,
51
				total_weight,
51
				call,
51
				transact_required_weight_at_most,
51
				origin_kind,
51
				with_appendix,
			)?;
			// Send to sovereign
49
			let (ticket, _price) =
51
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
51
					.map_err(|_| Error::<T>::ErrorValidating)?;
49
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
49
			Ok(())
51
		}
77
		fn transact_in_dest_chain_asset_signed(
77
			dest: Location,
77
			fee_payer: T::AccountId,
77
			fee: Asset,
77
			call: Vec<u8>,
77
			origin_kind: OriginKind,
77
			total_weight: WeightLimit,
77
			transact_required_weight_at_most: Weight,
77
			with_appendix: Option<Vec<Instruction<()>>>,
77
		) -> DispatchResult {
			// Convert origin to multilocation
77
			let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
			// Transact.
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
			// used to pay fees
			// BuyExecution: Buys "execution power" in the destination chain
			// Transact: Issues the transaction
77
			let mut transact_message: Xcm<()> = Self::transact_message(
77
				dest.clone(),
77
				fee,
77
				total_weight,
77
				call,
77
				transact_required_weight_at_most,
77
				origin_kind,
77
				with_appendix,
			)?;
			// We append DescendOrigin as the first instruction in the message
			// The new message looks like DescendOrigin||WithdrawAsset||BuyExecution||
			// Transact.
77
			let interior: Junctions = origin_as_mult
77
				.clone()
77
				.try_into()
77
				.map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
77
			transact_message.0.insert(0, DescendOrigin(interior));
			// Send to destination chain
58
			let (ticket, _price) =
77
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
77
					.map_err(|_| Error::<T>::ErrorValidating)?;
58
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
58
			Ok(())
77
		}
		/// Calculate the amount of fee based on the multilocation of the fee asset and
		/// the total weight to be spent.
		/// This now delegates to the FeeTrader instead of reading from local storage.
134
		fn calculate_fee(
134
			fee_location: Location,
134
			fee_amount: Option<u128>,
134
			destination: Location,
134
			total_weight: Weight,
134
		) -> Result<Asset, DispatchError> {
			// If amount is explicitly provided, keep legacy behavior: do not enforce reserve/destination
			// validation here. Historically, the reserve check only ran when converting weight->fee.
134
			if let Some(amount) = fee_amount {
89
				return Ok(Asset {
89
					id: AssetId(fee_location),
89
					fun: Fungible(amount),
89
				});
45
			}
			// Otherwise, delegate to FeeTrader to compute the fee based on weight and asset pricing,
			// and validate that the fee asset is a reserve for the destination.
45
			let amount: u128 = T::FeeTrader::compute_fee(total_weight, &fee_location, None)?;
42
			let asset = Asset {
42
				id: AssetId(fee_location.clone()),
42
				fun: Fungible(amount),
42
			};
42
			let reserve = <T::ReserveProvider as xcm_primitives::Reserve>::reserve(&asset)
42
				.ok_or(Error::<T>::AssetHasNoReserve)?;
42
			if reserve != destination {
2
				return Err(Error::<T>::AssetIsNotReserveInDestination.into());
40
			}
40
			Ok(asset)
134
		}
		/// Construct the transact xcm message with the provided parameters
128
		fn transact_message(
128
			dest: Location,
128
			asset: Asset,
128
			dest_weight: WeightLimit,
128
			call: Vec<u8>,
128
			dispatch_weight: Weight,
128
			origin_kind: OriginKind,
128
			with_appendix: Option<Vec<Instruction<()>>>,
128
		) -> Result<Xcm<()>, DispatchError> {
128
			let mut instructions = vec![
128
				Self::withdraw_instruction(asset.clone(), &dest)?,
128
				Self::buy_execution(asset, &dest, dest_weight)?,
			];
128
			if let Some(appendix) = with_appendix {
31
				instructions.push(Self::appendix_instruction(appendix)?);
97
			}
128
			instructions.push(Transact {
128
				origin_kind,
128
				fallback_max_weight: Some(dispatch_weight),
128
				call: call.into(),
128
			});
128
			Ok(Xcm(instructions))
128
		}
		/// Construct a buy execution xcm order with the provided parameters
128
		fn buy_execution(
128
			asset: Asset,
128
			at: &Location,
128
			weight: WeightLimit,
128
		) -> Result<Instruction<()>, DispatchError> {
128
			let universal_location = T::UniversalLocation::get();
128
			let fees = asset
128
				.reanchored(at, &universal_location)
128
				.map_err(|_| Error::<T>::CannotReanchor)?;
128
			Ok(BuyExecution {
128
				fees,
128
				weight_limit: weight,
128
			})
128
		}
		/// Construct a withdraw instruction from a sovereign account
128
		fn withdraw_instruction(
128
			asset: Asset,
128
			at: &Location,
128
		) -> Result<Instruction<()>, DispatchError> {
128
			let universal_location = T::UniversalLocation::get();
128
			let fees = asset
128
				.reanchored(at, &universal_location)
128
				.map_err(|_| Error::<T>::CannotReanchor)?;
128
			Ok(WithdrawAsset(fees.into()))
128
		}
		/// Construct a deposit instruction to a sovereign account
31
		fn deposit_instruction(
31
			mut beneficiary: Location,
31
			at: &Location,
31
			max_assets: u32,
31
		) -> Result<Instruction<()>, DispatchError> {
31
			let universal_location = T::UniversalLocation::get();
31
			beneficiary
31
				.reanchor(at, &universal_location)
31
				.map_err(|_| Error::<T>::CannotReanchor)?;
31
			Ok(DepositAsset {
31
				assets: Wild(AllCounted(max_assets)),
31
				beneficiary,
31
			})
31
		}
		/// Construct a withdraw instruction from a sovereign account
31
		fn appendix_instruction(
31
			instructions: Vec<Instruction<()>>,
31
		) -> Result<Instruction<()>, DispatchError> {
31
			Ok(SetAppendix(Xcm(instructions)))
31
		}
		/// Returns weight of `weight_of_initiate_reserve_withdraw` call.
6
		fn weight_of_initiate_reserve_withdraw() -> Weight {
6
			let dest = Location::parent();
			// We can use whatever asset here
6
			let asset = Location::parent();
			// Construct Asset
6
			let fee = Asset {
6
				id: AssetId(asset.clone()),
6
				fun: Fungible(0),
6
			};
6
			let xcm: Xcm<()> = Xcm(vec![
6
				WithdrawAsset(fee.into()),
6
				InitiateReserveWithdraw {
6
					assets: AssetFilter::Wild(All),
6
					reserve: dest.clone(),
6
					xcm: Xcm(vec![]),
6
				},
6
			]);
6
			T::Weigher::weight(&mut xcm.into(), Weight::MAX)
6
				.map_or(Weight::MAX, |w| T::BaseXcmWeight::get().saturating_add(w))
6
		}
		/// Returns the weight information for a destination from storage
		/// it returns the weight to be used in non-signed cases
25
		pub fn take_weight_from_transact_info(
25
			dest: Location,
25
			dest_weight: Weight,
25
			refund: bool,
25
		) -> Result<Weight, DispatchError> {
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
			// so we have to ensure 'refund' is false
25
			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
			// Grab transact info for the destination provided
24
			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
24
				.ok_or(Error::<T>::TransactorInfoNotSet)?;
23
			let total_weight = dest_weight
23
				.checked_add(&transactor_info.transact_extra_weight)
23
				.ok_or(Error::<T>::WeightOverflow)?;
23
			ensure!(
23
				total_weight.all_lte(transactor_info.max_weight),
7
				Error::<T>::MaxWeightTransactReached
			);
16
			Ok(total_weight)
25
		}
		/// Returns the weight information for a destination from storage
		/// it returns the weight to be used in signed cases
33
		pub fn take_weight_from_transact_info_signed(
33
			dest: Location,
33
			dest_weight: Weight,
33
			refund: bool,
33
		) -> Result<Weight, DispatchError> {
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
			// so we have to ensure 'refund' is false
33
			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
			// Grab transact info for the destination provided
33
			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
33
				.ok_or(Error::<T>::TransactorInfoNotSet)?;
			// If this storage item is not set, it means that the destination chain
			// does not support this kind of transact message
31
			let transact_in_dest_as_signed_weight = transactor_info
31
				.transact_extra_weight_signed
31
				.ok_or(Error::<T>::SignedTransactNotAllowedForDestination)?;
29
			let total_weight = dest_weight
29
				.checked_add(&transact_in_dest_as_signed_weight)
29
				.ok_or(Error::<T>::WeightOverflow)?;
28
			ensure!(
28
				total_weight.all_lte(transactor_info.max_weight),
1
				Error::<T>::MaxWeightTransactReached
			);
27
			Ok(total_weight)
33
		}
		/// Get the fee per second for an asset location.
		/// This now delegates to the FeeTrader instead of reading from local storage.
3
		pub fn dest_asset_fee_per_second(location: &Location) -> Option<u128> {
3
			T::FeeTrader::get_asset_price(location)
3
		}
		/// Converts Currency to multilocation
150
		pub fn currency_to_multilocation(currency: Currency<CurrencyIdOf<T>>) -> Option<Location> {
150
			match currency {
20
				Currency::AsCurrencyId(id) => T::CurrencyIdToLocation::convert(id),
130
				Currency::AsMultiLocation(multiloc) => Location::try_from(*multiloc).ok(),
			}
150
		}
	}
}