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

            
72
pub use pallet::*;
73

            
74
#[cfg(any(test, feature = "runtime-benchmarks"))]
75
mod benchmarks;
76

            
77
#[cfg(test)]
78
pub(crate) mod mock;
79
#[cfg(test)]
80
mod tests;
81

            
82
pub mod encode;
83
pub mod migrations;
84
pub mod relay_indices;
85
pub mod weights;
86
pub use crate::weights::WeightInfo;
87

            
88
type 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
94
pub 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
100
pub const ASSET_HUB_STAKING_PALLET_INDEX: u8 = 89;
101

            
102
#[pallet]
103
pub 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
737
		fn default() -> Self {
478
737
			Self {
479
737
				relay_indices: RelayChainIndices::default(),
480
737
				_phantom: Default::default(),
481
737
			}
482
737
		}
483
	}
484

            
485
	#[pallet::genesis_build]
486
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
487
735
		fn build(&self) {
488
735
			<RelayIndices<T>>::put(self.relay_indices);
489
735
		}
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
41
		pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
505
41
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
506

            
507
40
			ensure!(
508
40
				IndexToAccount::<T>::get(&index).is_none(),
509
				Error::<T>::IndexAlreadyClaimed
510
			);
511

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

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

            
520
40
			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
1
		pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
528
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
529

            
530
			// Remove index
531
1
			IndexToAccount::<T>::remove(&index);
532

            
533
			// Deposit event
534
1
			Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
535

            
536
1
			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
31
		) -> DispatchResult {
565
31
			let who = ensure_signed(origin)?;
566

            
567
31
			let fee_location = Self::currency_to_multilocation(fee.currency)
568
31
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
569

            
570
			// The index exists
571
31
			let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
572
			// The derivative index is owned by the origin
573
30
			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
30
			let call_bytes: Vec<u8> = <Self as UtilityEncodeCall>::encode_call(
578
30
				dest.clone(),
579
30
				UtilityAvailableCalls::AsDerivative(index, inner_call),
580
			);
581

            
582
			// Grab the destination
583
30
			let dest = dest.destination();
584

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

            
599
21
			let total_weight_fee_calculation = match total_weight {
600
				Unlimited => MAX_WEIGHT,
601
21
				Limited(x) => x,
602
			};
603

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

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

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

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

            
641
19
			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
16
		) -> DispatchResult {
670
16
			T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
671

            
672
15
			let fee_location = Self::currency_to_multilocation(fee.currency)
673
15
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
674

            
675
15
			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
15
			let total_weight = weight_info.overall_weight.map_or_else(
680
7
				|| -> Result<_, DispatchError> {
681
7
					let weight_info = Self::take_weight_from_transact_info(
682
7
						dest.clone(),
683
7
						weight_info.transact_required_weight_at_most,
684
7
						refund,
685
					)?;
686
7
					Ok(WeightLimit::from(Some(weight_info)))
687
7
				},
688
8
				|v| Ok(v),
689
			)?;
690

            
691
15
			let total_weight_fee_calculation = match total_weight {
692
				Unlimited => MAX_WEIGHT,
693
15
				Limited(x) => x,
694
			};
695

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

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

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

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

            
733
15
			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
49
		) -> DispatchResult {
746
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
747
49
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
748
49
			let remote_info = RemoteTransactInfoWithMaxWeight {
749
49
				transact_extra_weight,
750
49
				max_weight,
751
49
				transact_extra_weight_signed,
752
49
			};
753

            
754
49
			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
755

            
756
49
			Self::deposit_event(Event::TransactInfoChanged {
757
49
				location,
758
49
				remote_info,
759
49
			});
760
49
			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
1
		) -> DispatchResult {
770
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
771
1
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
772

            
773
			// Remove transact info
774
1
			TransactInfoWithWeightLimit::<T>::remove(&location);
775

            
776
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
777
1
			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
86
		) -> DispatchResult {
800
86
			let who = ensure_signed(origin)?;
801

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

            
804
86
			let fee_location = Self::currency_to_multilocation(fee.currency)
805
86
				.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
86
			let total_weight = weight_info.overall_weight.map_or_else(
810
33
				|| -> Result<_, DispatchError> {
811
33
					let weight_info = Self::take_weight_from_transact_info_signed(
812
33
						dest.clone(),
813
33
						weight_info.transact_required_weight_at_most,
814
33
						refund,
815
6
					)?;
816
27
					Ok(WeightLimit::from(Some(weight_info)))
817
33
				},
818
53
				|v| Ok(v),
819
6
			)?;
820

            
821
80
			let total_weight_fee_calculation = match total_weight {
822
				Unlimited => MAX_WEIGHT,
823
80
				Limited(x) => x,
824
			};
825

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

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

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

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

            
864
58
			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
49
		) -> DispatchResult {
875
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
876
49
			let asset_location =
877
49
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
878

            
879
49
			DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
880

            
881
49
			Self::deposit_event(Event::DestFeePerSecondChanged {
882
49
				location: asset_location,
883
49
				fee_per_second,
884
49
			});
885
49
			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
18
		) -> DispatchResult {
918
			// WithdrawAsset
919
			// BuyExecution
920
			// SetAppendix(RefundSurplus, DepositAsset(sov account))
921
			// Transact
922

            
923
			// check permissions
924
18
			match &action {
925
				HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
926
13
					<EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
927
13
						origin,
928
					)?;
929
				}
930
				HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
931
5
					T::HrmpManipulatorOrigin::ensure_origin(origin)?;
932
				}
933
			}
934

            
935
			// process action
936
18
			let call_bytes = match action.clone() {
937
8
				HrmpOperation::InitOpen(params) => {
938
8
					Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
939
8
						params.para_id,
940
8
						params.proposed_max_capacity,
941
8
						params.proposed_max_message_size,
942
8
					))
943
				}
944
5
				HrmpOperation::Accept { para_id } => {
945
5
					Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
946
				}
947
4
				HrmpOperation::Close(close_params) => {
948
4
					Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
949
				}
950
				HrmpOperation::Cancel {
951
1
					channel_id,
952
1
					open_requests,
953
1
				} => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
954
1
					channel_id,
955
1
					open_requests,
956
1
				)),
957
			}
958
18
			.map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
959

            
960
18
			let fee_location = Self::currency_to_multilocation(fee.currency)
961
18
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
962

            
963
			// Grab the destination
964
			// For hrmp, it is always parent
965
18
			let destination = Location::parent();
966

            
967
			// Calculate the total weight that the xcm message is going to spend in the
968
			// destination chain
969
18
			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
18
				|v| Ok(v),
979
			)?;
980

            
981
18
			let total_weight_fee_calculation = match total_weight {
982
				Unlimited => MAX_WEIGHT,
983
18
				Limited(x) => x,
984
			};
985

            
986
18
			let fee = Self::calculate_fee(
987
18
				fee_location,
988
18
				fee.fee_amount,
989
18
				destination.clone(),
990
18
				total_weight_fee_calculation,
991
			)?;
992

            
993
18
			ensure!(
994
18
				T::MaxHrmpFee::filter_max_asset_fee(&fee),
995
1
				Error::<T>::TooMuchFeeUsed
996
			);
997

            
998
			// The appendix instruction will be a deposit back to a self location
999
17
			let deposit_appendix =
17
				Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
17
			Self::transact_in_dest_chain_asset_non_signed(
17
				destination,
17
				None,
17
				fee,
17
				call_bytes.clone(),
17
				OriginKind::Native,
17
				total_weight,
17
				weight_info.transact_required_weight_at_most,
17
				Some(vec![RefundSurplus, deposit_appendix]),
2
			)?;
15
			Self::deposit_event(Event::HrmpManagementSent { action });
15
			Ok(())
		}
	}
	impl<T: Config> Pallet<T> {
51
		fn transact_in_dest_chain_asset_non_signed(
51
			dest: Location,
51
			fee_payer: Option<T::AccountId>,
51
			fee: Asset,
51
			call: Vec<u8>,
51
			origin_kind: OriginKind,
51
			total_weight: WeightLimit,
51
			transact_required_weight_at_most: Weight,
51
			with_appendix: Option<Vec<Instruction<()>>>,
51
		) -> DispatchResult {
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
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 provided, just use it
			// Else, multiply weight*destination_units_per_second to see how much we should charge for
			// this weight execution
134
			let amount: u128 = fee_amount.map_or_else(
45
				|| {
45
					Self::take_fee_per_second_from_storage(
45
						fee_location.clone(),
45
						destination,
45
						total_weight,
					)
45
				},
89
				|v| Ok(v),
5
			)?;
			// Construct Asset
129
			Ok(Asset {
129
				id: AssetId(fee_location),
129
				fun: Fungible(amount),
129
			})
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
		}
		/// Ensure `dest` has chain part and none recipient part.
42
		fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
42
			let chain_location = dest.chain_location();
42
			if *dest == chain_location {
42
				Ok(chain_location)
			} else {
				Err(Error::<T>::InvalidDest.into())
			}
42
		}
		/// Check whether the transfer is allowed.
		///
		/// Returns `Err` if `asset` is not a reserved asset of `dest`,
		/// else returns `dest`, parachain or relay chain location.
42
		fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
42
			let dest = Self::ensure_valid_dest(dest)?;
42
			let self_location = T::SelfLocation::get();
42
			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
42
			let reserve =
42
				T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
			// We only allow to transact using a reserve asset as fee
42
			ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
40
			Ok(dest)
42
		}
		/// 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 fee for a given set of parameters
		/// We always round up in case of fractional division
42
		pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
			// grab WEIGHT_REF_TIME_PER_SECOND as u128
42
			let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
			// we add WEIGHT_REF_TIME_PER_SECOND -1 after multiplication to make sure that
			// if there is a fractional part we round up the result
42
			let fee_mul_rounded_up = (fee_per_second.saturating_mul(weight.ref_time() as u128))
42
				.saturating_add(weight_per_second_u128 - 1);
42
			fee_mul_rounded_up / weight_per_second_u128
42
		}
		/// 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
		}
		/// Returns the fee per second charged by a reserve chain for an asset
		/// it takes this information from storage
45
		pub fn take_fee_per_second_from_storage(
45
			fee_location: Location,
45
			destination: Location,
45
			total_weight: Weight,
45
		) -> Result<u128, DispatchError> {
45
			let fee_per_second = DestinationAssetFeePerSecond::<T>::get(&fee_location)
45
				.ok_or(Error::<T>::FeePerSecondNotSet)?;
			// Ensure the asset is a reserve
			// We only store information about asset fee per second on its reserve chain
			// if amount is provided, we first check whether we have this information
42
			Self::transfer_allowed(&(fee_location, fee_per_second).into(), &destination)?;
40
			Ok(Self::calculate_fee_per_second(total_weight, fee_per_second))
45
		}
		/// 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
		}
	}
}