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 chain_indices;
83
pub mod encode;
84
pub mod migrations;
85
pub mod weights;
86
pub use crate::weights::WeightInfo;
87

            
88
type CurrencyIdOf<T> = <T as Config>::CurrencyId;
89

            
90
1785
#[pallet]
91
pub mod pallet {
92

            
93
	use crate::chain_indices::RelayChainIndices;
94
	use crate::weights::WeightInfo;
95
	use crate::CurrencyIdOf;
96
	use cumulus_primitives_core::{relay_chain::HrmpChannelId, ParaId};
97
	use frame_support::traits::EitherOfDiverse;
98
	use frame_support::{
99
		dispatch::DispatchResult, pallet_prelude::*, weights::constants::WEIGHT_REF_TIME_PER_SECOND,
100
	};
101
	use frame_system::{ensure_signed, pallet_prelude::*};
102
	use sp_runtime::traits::{AtLeast32BitUnsigned, Bounded, Convert};
103
	use sp_std::boxed::Box;
104
	use sp_std::convert::TryFrom;
105
	use sp_std::prelude::*;
106
	use sp_std::vec;
107
	use sp_std::vec::Vec;
108
	use xcm::{latest::prelude::*, VersionedLocation};
109
	use xcm_executor::traits::{TransactAsset, WeightBounds};
110
	use xcm_primitives::{
111
		FilterMaxAssetFee, HrmpAvailableCalls, HrmpEncodeCall, Reserve, UtilityAvailableCalls,
112
		UtilityEncodeCall, XcmTransact,
113
	};
114

            
115
150
	#[pallet::pallet]
116
	#[pallet::without_storage_info]
117
	pub struct Pallet<T>(pub PhantomData<T>);
118

            
119
	#[pallet::config]
120
	pub trait Config: frame_system::Config {
121
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
122
		/// The balance type.
123
		type Balance: Parameter
124
			+ Member
125
			+ AtLeast32BitUnsigned
126
			+ Default
127
			+ Copy
128
			+ MaybeSerializeDeserialize
129
			+ Into<u128>;
130

            
131
		/// Currency Id.
132
		type CurrencyId: Parameter + Member + Clone;
133

            
134
		/// Convert `T::CurrencyId` to `Location`.
135
		type CurrencyIdToLocation: Convert<Self::CurrencyId, Option<Location>>;
136

            
137
		// XcmTransact needs to be implemented. This type needs to implement
138
		// utility call encoding and multilocation gathering
139
		type Transactor: Parameter
140
			+ Member
141
			+ Clone
142
			+ XcmTransact
143
			+ serde::Serialize
144
			+ serde::de::DeserializeOwned;
145

            
146
		/// AssetTransactor allows us to withdraw asset without being trapped
147
		/// This should change in xcm v3, which allows us to burn assets
148
		type AssetTransactor: TransactAsset;
149

            
150
		// The origin that is allowed to register derivative address indices
151
		type DerivativeAddressRegistrationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
152

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

            
156
		// The origin that is allowed to open and accept an Hrmp channel
157
		type HrmpOpenOrigin: EnsureOrigin<Self::RuntimeOrigin>;
158

            
159
		/// Convert `T::AccountId` to `Location`.
160
		type AccountIdToLocation: Convert<Self::AccountId, Location>;
161

            
162
		/// Means of measuring the weight consumed by an XCM message locally.
163
		type Weigher: WeightBounds<Self::RuntimeCall>;
164

            
165
		/// This chain's Universal Location.
166
		type UniversalLocation: Get<InteriorLocation>;
167

            
168
		/// Self chain location.
169
		#[pallet::constant]
170
		type SelfLocation: Get<Location>;
171

            
172
		// The origin that is allowed to dispatch calls from the sovereign account directly
173
		type SovereignAccountDispatcherOrigin: EnsureOrigin<Self::RuntimeOrigin>;
174

            
175
		/// XCM sender.
176
		type XcmSender: SendXcm;
177

            
178
		// Base XCM weight.
179
		///
180
		/// The actual weight for an XCM message is `T::BaseXcmWeight +
181
		/// T::Weigher::weight(&msg)`.
182
		#[pallet::constant]
183
		type BaseXcmWeight: Get<Weight>;
184

            
185
		/// The way to retrieve the reserve of a Asset. This can be
186
		/// configured to accept absolute or relative paths for self tokens
187
		type ReserveProvider: xcm_primitives::Reserve;
188

            
189
		/// The way to filter the max fee to use for HRMP management operations
190
		type MaxHrmpFee: FilterMaxAssetFee;
191

            
192
		type WeightInfo: WeightInfo;
193
	}
194

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

            
225
	/// Enum defining the way to express a Currency.
226
	#[derive(
227
		Clone,
228
		Encode,
229
		Decode,
230
		Eq,
231
		PartialEq,
232
		RuntimeDebug,
233
42
		scale_info::TypeInfo,
234
		DecodeWithMemTracking,
235
	)]
236
	pub enum Currency<CurrencyId> {
237
		// Express the Currency as a CurrencyId
238
		AsCurrencyId(CurrencyId),
239
		// Express the Currency as its MultiLOcation
240
		AsMultiLocation(Box<VersionedLocation>),
241
	}
242

            
243
	impl<T> Default for Currency<T> {
244
		fn default() -> Currency<T> {
245
			Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
246
		}
247
	}
248

            
249
	#[derive(
250
		Clone,
251
		Encode,
252
		Decode,
253
		Eq,
254
		PartialEq,
255
		RuntimeDebug,
256
225
		scale_info::TypeInfo,
257
		DecodeWithMemTracking,
258
	)]
259
	pub struct HrmpInitParams {
260
		pub para_id: ParaId,
261
		pub proposed_max_capacity: u32,
262
		pub proposed_max_message_size: u32,
263
	}
264

            
265
	/// Enum defining the way to express a Currency.
266
	#[derive(
267
		Clone,
268
		Encode,
269
		Decode,
270
		Eq,
271
		PartialEq,
272
		RuntimeDebug,
273
375
		scale_info::TypeInfo,
274
		DecodeWithMemTracking,
275
	)]
276
	pub enum HrmpOperation {
277
		InitOpen(HrmpInitParams),
278
		Accept {
279
			para_id: ParaId,
280
		},
281
		Close(HrmpChannelId),
282
		Cancel {
283
			channel_id: HrmpChannelId,
284
			open_requests: u32,
285
		},
286
	}
287

            
288
	#[derive(
289
		Default,
290
		Clone,
291
		Encode,
292
		Decode,
293
		Eq,
294
		PartialEq,
295
		RuntimeDebug,
296
		MaxEncodedLen,
297
42
		scale_info::TypeInfo,
298
		DecodeWithMemTracking,
299
	)]
300

            
301
	/// Struct that defines how to express the payment in a particular currency
302
	/// currency is defined by the Currency enum, which can be expressed as:
303
	/// - CurrencyId
304
	/// - Location
305
	///
306
	/// The fee_amount is an option. In case of None, the fee will be tried to
307
	/// be calculated from storage. If the storage item for the currency is not
308
	/// populated, then it fails
309
	pub struct CurrencyPayment<CurrencyId> {
310
		// the currency in which we want to express our payment
311
		pub currency: Currency<CurrencyId>,
312
		// indicates whether we want to specify the fee amount to be used
313
		pub fee_amount: Option<u128>,
314
	}
315

            
316
	#[derive(
317
		Default,
318
		Clone,
319
		Encode,
320
		Decode,
321
		RuntimeDebug,
322
		PartialEq,
323
150
		scale_info::TypeInfo,
324
		DecodeWithMemTracking,
325
	)]
326
	/// Struct tindicating information about transact weights
327
	/// It allows to specify:
328
	/// - transact_required_weight_at_most: the amount of weight the Transact instruction
329
	///   should consume at most
330
	/// - overall_weight: the overall weight to be used for the whole XCM message execution.
331
	///   If None, then this amount will be tried to be derived from storage.  If the storage item
332
	pub struct TransactWeights {
333
		// the amount of weight the Transact instruction should consume at most
334
		pub transact_required_weight_at_most: Weight,
335
		// the overall weight to be used for the whole XCM message execution. If None,
336
		// then this amount will be tried to be derived from storage.  If the storage item
337
		// for the chain is not populated, then it fails
338
		pub overall_weight: Option<WeightLimit>,
339
	}
340

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

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

            
353
	/// Stores the transact info of a Location. This defines how much extra weight we need to
354
	/// add when we want to transact in the destination chain and maximum amount of weight allowed
355
	/// by the destination chain
356
144
	#[pallet::storage]
357
	#[pallet::getter(fn transact_info)]
358
	pub type TransactInfoWithWeightLimit<T: Config> =
359
		StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
360

            
361
	/// Stores the fee per second for an asset in its reserve chain. This allows us to convert
362
	/// from weight to fee
363
123
	#[pallet::storage]
364
	#[pallet::getter(fn dest_asset_fee_per_second)]
365
	pub type DestinationAssetFeePerSecond<T: Config> = StorageMap<_, Twox64Concat, Location, u128>;
366

            
367
	/// Stores the indices of relay chain pallets
368
	///
369
	/// DEPRECATED: Use ChainIndicesMap instead. This storage is kept for backwards compatibility
370
	/// and will be removed in a future version.
371
237
	#[pallet::storage]
372
	#[pallet::getter(fn relay_indices)]
373
	pub type RelayIndices<T: Config> = StorageValue<_, RelayChainIndices, ValueQuery>;
374

            
375
	/// Stores chain-specific pallet and call indices for encoding remote calls
376
	/// Maps Transactor type to its corresponding indices (Relay or AssetHub)
377
972
	#[pallet::storage]
378
	#[pallet::getter(fn chain_indices)]
379
	pub type ChainIndicesMap<T: Config> = StorageMap<
380
		_,
381
		Blake2_128Concat,
382
		T::Transactor,
383
		crate::chain_indices::ChainIndices,
384
		OptionQuery,
385
	>;
386

            
387
	/// An error that can occur while executing the mapping pallet's logic.
388
21
	#[pallet::error]
389
	pub enum Error<T> {
390
		IndexAlreadyClaimed,
391
		UnclaimedIndex,
392
		NotOwner,
393
		UnweighableMessage,
394
		CannotReanchor,
395
		AssetHasNoReserve,
396
		InvalidDest,
397
		NotCrossChainTransfer,
398
		AssetIsNotReserveInDestination,
399
		DestinationNotInvertible,
400
		ErrorDelivering,
401
		DispatchWeightBiggerThanTotalWeight,
402
		WeightOverflow,
403
		AmountOverflow,
404
		TransactorInfoNotSet,
405
		NotCrossChainTransferableCurrency,
406
		XcmExecuteError,
407
		BadVersion,
408
		MaxWeightTransactReached,
409
		UnableToWithdrawAsset,
410
		FeePerSecondNotSet,
411
		SignedTransactNotAllowedForDestination,
412
		FailedMultiLocationToJunction,
413
		HrmpHandlerNotImplemented,
414
		TooMuchFeeUsed,
415
		ErrorValidating,
416
		RefundNotSupportedWithTransactInfo,
417
	}
418

            
419
21
	#[pallet::event]
420
251
	#[pallet::generate_deposit(pub(crate) fn deposit_event)]
421
	pub enum Event<T: Config> {
422
		/// Transacted the inner call through a derivative account in a destination chain.
423
		TransactedDerivative {
424
			account_id: T::AccountId,
425
			dest: Location,
426
			call: Vec<u8>,
427
			index: u16,
428
		},
429
		/// Transacted the call through the sovereign account in a destination chain.
430
		TransactedSovereign {
431
			fee_payer: Option<T::AccountId>,
432
			dest: Location,
433
			call: Vec<u8>,
434
		},
435
		/// Transacted the call through a signed account in a destination chain.
436
		TransactedSigned {
437
			fee_payer: T::AccountId,
438
			dest: Location,
439
			call: Vec<u8>,
440
		},
441
		/// Registered a derivative index for an account id.
442
		RegisteredDerivative {
443
			account_id: T::AccountId,
444
			index: u16,
445
		},
446
		DeRegisteredDerivative {
447
			index: u16,
448
		},
449
		/// Transact failed
450
		TransactFailed {
451
			error: XcmError,
452
		},
453
		/// Changed the transact info of a location
454
		TransactInfoChanged {
455
			location: Location,
456
			remote_info: RemoteTransactInfoWithMaxWeight,
457
		},
458
		/// Removed the transact info of a location
459
		TransactInfoRemoved {
460
			location: Location,
461
		},
462
		/// Set dest fee per second
463
		DestFeePerSecondChanged {
464
			location: Location,
465
			fee_per_second: u128,
466
		},
467
		/// Remove dest fee per second
468
		DestFeePerSecondRemoved {
469
			location: Location,
470
		},
471
		/// HRMP manage action succesfully sent
472
		HrmpManagementSent {
473
			action: HrmpOperation,
474
		},
475
	}
476

            
477
	#[pallet::genesis_config]
478
	pub struct GenesisConfig<T: Config> {
479
		/// Chain-specific indices map for multi-chain support (Relay + AssetHub)
480
		///
481
		/// Maps transactor keys to their respective chain indices.
482
		/// This should be populated for fresh chains in genesis config.
483
		pub chain_indices_map: Vec<(T::Transactor, crate::chain_indices::ChainIndices)>,
484
		#[serde(skip)]
485
		pub _phantom: PhantomData<T>,
486
	}
487

            
488
	impl<T: Config> Default for GenesisConfig<T>
489
	where
490
		T::Transactor: xcm_primitives::RelayChainTransactor + xcm_primitives::AssetHubTransactor,
491
	{
492
740
		fn default() -> Self {
493
			use crate::chain_indices::{AssetHubIndices, ChainIndices, RelayChainIndices};
494
			use xcm_primitives::{AssetHubTransactor, RelayChainTransactor};
495

            
496
740
			Self {
497
740
				chain_indices_map: vec![
498
740
					(
499
740
						T::Transactor::relay(),
500
740
						ChainIndices::Relay(RelayChainIndices::default()),
501
740
					),
502
740
					(
503
740
						T::Transactor::asset_hub(),
504
740
						ChainIndices::AssetHub(AssetHubIndices::default()),
505
740
					),
506
740
				],
507
740
				_phantom: Default::default(),
508
740
			}
509
740
		}
510
	}
511

            
512
726
	#[pallet::genesis_build]
513
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
514
738
		fn build(&self) {
515
			// Initialize ChainIndicesMap from genesis config
516
1488
			for (transactor, indices) in &self.chain_indices_map {
517
750
				ChainIndicesMap::<T>::insert(transactor, indices);
518
750
			}
519

            
520
			// Note: RelayIndices storage is populated by the migration for backwards compatibility
521
738
		}
522
	}
523

            
524
21
	#[pallet::call]
525
	impl<T: Config> Pallet<T> {
526
		/// Register a derivative index for an account id. Dispatchable by
527
		/// DerivativeAddressRegistrationOrigin
528
		///
529
		/// We do not store the derivative address, but only the index. We do not need to store
530
		/// the derivative address to issue calls, only the index is enough
531
		///
532
		/// For now an index is registered for all possible destinations and not per-destination.
533
		/// We can change this in the future although it would just make things more complicated
534
		#[pallet::call_index(0)]
535
		#[pallet::weight(T::WeightInfo::register())]
536
41
		pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
537
41
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
538

            
539
40
			ensure!(
540
40
				IndexToAccount::<T>::get(&index).is_none(),
541
				Error::<T>::IndexAlreadyClaimed
542
			);
543

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

            
546
40
			// Deposit event
547
40
			Self::deposit_event(Event::<T>::RegisteredDerivative {
548
40
				account_id: who,
549
40
				index: index,
550
40
			});
551
40

            
552
40
			Ok(())
553
		}
554

            
555
		/// De-Register a derivative index. This prevents an account to use a derivative address
556
		/// (represented by an index) from our of our sovereign accounts anymore
557
		#[pallet::call_index(1)]
558
		#[pallet::weight(T::WeightInfo::deregister())]
559
1
		pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
560
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
561

            
562
			// Remove index
563
1
			IndexToAccount::<T>::remove(&index);
564
1

            
565
1
			// Deposit event
566
1
			Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
567
1

            
568
1
			Ok(())
569
		}
570

            
571
		/// Transact the inner call through a derivative account in a destination chain,
572
		/// using 'fee_location' to pay for the fees. This fee_location is given as a multilocation
573
		///
574
		/// The caller needs to have the index registered in this pallet. The fee multiasset needs
575
		/// to be a reserve asset for the destination transactor::multilocation.
576
		#[pallet::call_index(2)]
577
		#[pallet::weight(
578
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
579
			.saturating_add(T::WeightInfo::transact_through_derivative())
580
		)]
581
		pub fn transact_through_derivative(
582
			origin: OriginFor<T>,
583
			// destination to which the message should be sent
584
			dest: T::Transactor,
585
			// derivative index to be used
586
			index: u16,
587
			// fee to be used
588
			fee: CurrencyPayment<CurrencyIdOf<T>>,
589
			// inner call to be executed in destination. This wiol
590
			// be wrapped into utility.as_derivative
591
			inner_call: Vec<u8>,
592
			// weight information to be used
593
			weight_info: TransactWeights,
594
			// add RefundSurplus and DepositAsset appendix
595
			refund: bool,
596
31
		) -> DispatchResult {
597
31
			let who = ensure_signed(origin)?;
598

            
599
31
			let fee_location = Self::currency_to_multilocation(fee.currency)
600
31
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
601

            
602
			// The index exists
603
31
			let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
604
			// The derivative index is owned by the origin
605
30
			ensure!(account == who, Error::<T>::NotOwner);
606

            
607
			// Encode call bytes
608
			// We make sure the inner call is wrapped on a as_derivative dispatchable
609
30
			let call_bytes: Vec<u8> = dest
610
30
				.clone()
611
30
				.encode_call(UtilityAvailableCalls::AsDerivative(index, inner_call));
612
30

            
613
30
			// Grab the destination
614
30
			let dest = dest.destination();
615

            
616
			// Calculate the total weight that the xcm message is going to spend in the
617
			// destination chain
618
30
			let total_weight = weight_info.overall_weight.map_or_else(
619
30
				|| -> Result<_, DispatchError> {
620
18
					let weight_info = Self::take_weight_from_transact_info(
621
18
						dest.clone(),
622
18
						weight_info.transact_required_weight_at_most,
623
18
						refund,
624
18
					)?;
625
9
					Ok(WeightLimit::from(Some(weight_info)))
626
30
				},
627
30
				|v| Ok(v),
628
30
			)?;
629

            
630
21
			let total_weight_fee_calculation = match total_weight {
631
				Unlimited => MAX_WEIGHT,
632
21
				Limited(x) => x,
633
			};
634

            
635
			// Calculate fee based on FeePerSecond
636
21
			let fee = Self::calculate_fee(
637
21
				fee_location,
638
21
				fee.fee_amount,
639
21
				dest.clone(),
640
21
				total_weight_fee_calculation,
641
21
			)?;
642

            
643
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
644
19
			let appendix = refund
645
19
				.then(|| -> Result<_, DispatchError> {
646
4
					Ok(vec![
647
4
						RefundSurplus,
648
4
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
649
					])
650
19
				})
651
19
				.transpose()?;
652

            
653
19
			Self::transact_in_dest_chain_asset_non_signed(
654
19
				dest.clone(),
655
19
				Some(who.clone()),
656
19
				fee,
657
19
				call_bytes.clone(),
658
19
				OriginKind::SovereignAccount,
659
19
				total_weight,
660
19
				weight_info.transact_required_weight_at_most,
661
19
				appendix,
662
19
			)?;
663

            
664
			// Deposit event
665
19
			Self::deposit_event(Event::<T>::TransactedDerivative {
666
19
				account_id: who,
667
19
				dest,
668
19
				call: call_bytes,
669
19
				index,
670
19
			});
671
19

            
672
19
			Ok(())
673
		}
674

            
675
		/// Transact the call through the sovereign account in a destination chain,
676
		/// 'fee_payer' pays for the fee
677
		///
678
		/// SovereignAccountDispatcherOrigin callable only
679
		#[pallet::call_index(3)]
680
		#[pallet::weight(
681
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
682
			.saturating_add(T::WeightInfo::transact_through_sovereign())
683
		)]
684
		pub fn transact_through_sovereign(
685
			origin: OriginFor<T>,
686
			// destination to which the message should be sent
687
			dest: Box<VersionedLocation>,
688
			// account paying for fees
689
			fee_payer: Option<T::AccountId>,
690
			// fee to be used
691
			fee: CurrencyPayment<CurrencyIdOf<T>>,
692
			// call to be executed in destination
693
			call: Vec<u8>,
694
			// origin kind to be used
695
			origin_kind: OriginKind,
696
			// weight information to be used
697
			weight_info: TransactWeights,
698
			// add RefundSurplus and DepositAsset appendix
699
			refund: bool,
700
16
		) -> DispatchResult {
701
16
			T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
702

            
703
15
			let fee_location = Self::currency_to_multilocation(fee.currency)
704
15
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
705

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

            
708
			// Calculate the total weight that the xcm message is going to spend in the
709
			// destination chain
710
15
			let total_weight = weight_info.overall_weight.map_or_else(
711
15
				|| -> Result<_, DispatchError> {
712
7
					let weight_info = Self::take_weight_from_transact_info(
713
7
						dest.clone(),
714
7
						weight_info.transact_required_weight_at_most,
715
7
						refund,
716
7
					)?;
717
7
					Ok(WeightLimit::from(Some(weight_info)))
718
15
				},
719
15
				|v| Ok(v),
720
15
			)?;
721

            
722
15
			let total_weight_fee_calculation = match total_weight {
723
				Unlimited => MAX_WEIGHT,
724
15
				Limited(x) => x,
725
			};
726

            
727
			// Calculate fee based on FeePerSecond and total_weight
728
15
			let fee = Self::calculate_fee(
729
15
				fee_location,
730
15
				fee.fee_amount,
731
15
				dest.clone(),
732
15
				total_weight_fee_calculation,
733
15
			)?;
734

            
735
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
736
15
			let appendix = refund
737
15
				.then(|| -> Result<_, DispatchError> {
738
3
					Ok(vec![
739
3
						RefundSurplus,
740
3
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
741
					])
742
15
				})
743
15
				.transpose()?;
744

            
745
			// Grab the destination
746
15
			Self::transact_in_dest_chain_asset_non_signed(
747
15
				dest.clone(),
748
15
				fee_payer.clone(),
749
15
				fee,
750
15
				call.clone(),
751
15
				origin_kind,
752
15
				total_weight,
753
15
				weight_info.transact_required_weight_at_most,
754
15
				appendix,
755
15
			)?;
756

            
757
			// Deposit event
758
15
			Self::deposit_event(Event::<T>::TransactedSovereign {
759
15
				fee_payer,
760
15
				dest,
761
15
				call,
762
15
			});
763
15

            
764
15
			Ok(())
765
		}
766

            
767
		/// Change the transact info of a location
768
		#[pallet::call_index(4)]
769
		#[pallet::weight(T::WeightInfo::set_transact_info())]
770
		pub fn set_transact_info(
771
			origin: OriginFor<T>,
772
			location: Box<VersionedLocation>,
773
			transact_extra_weight: Weight,
774
			max_weight: Weight,
775
			transact_extra_weight_signed: Option<Weight>,
776
49
		) -> DispatchResult {
777
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
778
49
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
779
49
			let remote_info = RemoteTransactInfoWithMaxWeight {
780
49
				transact_extra_weight,
781
49
				max_weight,
782
49
				transact_extra_weight_signed,
783
49
			};
784
49

            
785
49
			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
786
49

            
787
49
			Self::deposit_event(Event::TransactInfoChanged {
788
49
				location,
789
49
				remote_info,
790
49
			});
791
49
			Ok(())
792
		}
793

            
794
		/// Remove the transact info of a location
795
		#[pallet::call_index(5)]
796
		#[pallet::weight(T::WeightInfo::remove_transact_info())]
797
		pub fn remove_transact_info(
798
			origin: OriginFor<T>,
799
			location: Box<VersionedLocation>,
800
1
		) -> DispatchResult {
801
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
802
1
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
803

            
804
			// Remove transact info
805
1
			TransactInfoWithWeightLimit::<T>::remove(&location);
806
1

            
807
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
808
1
			Ok(())
809
		}
810

            
811
		/// Transact the call through the a signed origin in this chain
812
		/// that should be converted to a transaction dispatch account in the destination chain
813
		/// by any method implemented in the destination chains runtime
814
		///
815
		/// This time we are giving the currency as a currencyId instead of multilocation
816
		#[pallet::call_index(6)]
817
		#[pallet::weight(T::WeightInfo::transact_through_signed())]
818
		pub fn transact_through_signed(
819
			origin: OriginFor<T>,
820
			// destination to which the message should be sent
821
			dest: Box<VersionedLocation>,
822
			// fee to be used
823
			fee: CurrencyPayment<CurrencyIdOf<T>>,
824
			// call to be executed in destination
825
			call: Vec<u8>,
826
			// weight information to be used
827
			weight_info: TransactWeights,
828
			// add RefundSurplus and DepositAsset appendix
829
			refund: bool,
830
92
		) -> DispatchResult {
831
92
			let who = ensure_signed(origin)?;
832

            
833
92
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
834

            
835
92
			let fee_location = Self::currency_to_multilocation(fee.currency)
836
92
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
837

            
838
			// Calculate the total weight that the xcm message is going to spend in the
839
			// destination chain
840
92
			let total_weight = weight_info.overall_weight.map_or_else(
841
92
				|| -> Result<_, DispatchError> {
842
35
					let weight_info = Self::take_weight_from_transact_info_signed(
843
35
						dest.clone(),
844
35
						weight_info.transact_required_weight_at_most,
845
35
						refund,
846
35
					)?;
847
29
					Ok(WeightLimit::from(Some(weight_info)))
848
92
				},
849
92
				|v| Ok(v),
850
92
			)?;
851

            
852
86
			let total_weight_fee_calculation = match total_weight {
853
				Unlimited => MAX_WEIGHT,
854
86
				Limited(x) => x,
855
			};
856

            
857
			// Fee to be paid
858
86
			let fee = Self::calculate_fee(
859
86
				fee_location,
860
86
				fee.fee_amount,
861
86
				dest.clone(),
862
86
				total_weight_fee_calculation,
863
86
			)?;
864

            
865
			// If refund is true, the appendix instruction will be a deposit back to the sender
866
83
			let appendix = refund
867
83
				.then(|| -> Result<_, DispatchError> {
868
7
					let sender = T::AccountIdToLocation::convert(who.clone());
869
7
					Ok(vec![
870
7
						RefundSurplus,
871
7
						Self::deposit_instruction(sender, &dest, 1u32)?,
872
					])
873
83
				})
874
83
				.transpose()?;
875

            
876
			// Grab the destination
877
83
			Self::transact_in_dest_chain_asset_signed(
878
83
				dest.clone(),
879
83
				who.clone(),
880
83
				fee,
881
83
				call.clone(),
882
83
				OriginKind::SovereignAccount,
883
83
				total_weight,
884
83
				weight_info.transact_required_weight_at_most,
885
83
				appendix,
886
83
			)?;
887

            
888
			// Deposit event
889
62
			Self::deposit_event(Event::<T>::TransactedSigned {
890
62
				fee_payer: who,
891
62
				dest,
892
62
				call,
893
62
			});
894
62

            
895
62
			Ok(())
896
		}
897

            
898
		/// Set the fee per second of an asset on its reserve chain
899
		#[pallet::call_index(7)]
900
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
901
		pub fn set_fee_per_second(
902
			origin: OriginFor<T>,
903
			asset_location: Box<VersionedLocation>,
904
			fee_per_second: u128,
905
49
		) -> DispatchResult {
906
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
907
49
			let asset_location =
908
49
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
909

            
910
49
			DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
911
49

            
912
49
			Self::deposit_event(Event::DestFeePerSecondChanged {
913
49
				location: asset_location,
914
49
				fee_per_second,
915
49
			});
916
49
			Ok(())
917
		}
918

            
919
		/// Remove the fee per second of an asset on its reserve chain
920
		#[pallet::call_index(8)]
921
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
922
		pub fn remove_fee_per_second(
923
			origin: OriginFor<T>,
924
			asset_location: Box<VersionedLocation>,
925
		) -> DispatchResult {
926
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
927
			let asset_location =
928
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
929

            
930
			DestinationAssetFeePerSecond::<T>::remove(&asset_location);
931

            
932
			Self::deposit_event(Event::DestFeePerSecondRemoved {
933
				location: asset_location,
934
			});
935
			Ok(())
936
		}
937

            
938
		/// Manage HRMP operations
939
		#[pallet::call_index(9)]
940
		#[pallet::weight(T::WeightInfo::hrmp_manage())]
941
		pub fn hrmp_manage(
942
			origin: OriginFor<T>,
943
			action: HrmpOperation,
944
			// fee to be used
945
			fee: CurrencyPayment<CurrencyIdOf<T>>,
946
			// weight information to be used
947
			weight_info: TransactWeights,
948
18
		) -> DispatchResult {
949
18
			// WithdrawAsset
950
18
			// BuyExecution
951
18
			// SetAppendix(RefundSurplus, DepositAsset(sov account))
952
18
			// Transact
953
18

            
954
18
			// check permissions
955
18
			match &action {
956
				HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
957
13
					<EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
958
13
						origin,
959
13
					)?;
960
				}
961
				HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
962
5
					T::HrmpManipulatorOrigin::ensure_origin(origin)?;
963
				}
964
			}
965

            
966
			// process action
967
18
			let call_bytes = match action.clone() {
968
8
				HrmpOperation::InitOpen(params) => {
969
8
					Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
970
8
						params.para_id,
971
8
						params.proposed_max_capacity,
972
8
						params.proposed_max_message_size,
973
8
					))
974
				}
975
5
				HrmpOperation::Accept { para_id } => {
976
5
					Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
977
				}
978
4
				HrmpOperation::Close(close_params) => {
979
4
					Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
980
				}
981
				HrmpOperation::Cancel {
982
1
					channel_id,
983
1
					open_requests,
984
1
				} => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
985
1
					channel_id,
986
1
					open_requests,
987
1
				)),
988
			}
989
18
			.map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
990

            
991
18
			let fee_location = Self::currency_to_multilocation(fee.currency)
992
18
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
993

            
994
			// Grab the destination
995
			// For hrmp, it is always parent
996
18
			let destination = Location::parent();
997

            
998
			// Calculate the total weight that the xcm message is going to spend in the
999
			// destination chain
18
			let total_weight = weight_info.overall_weight.map_or_else(
18
				|| -> Result<_, DispatchError> {
					let weight_info = Self::take_weight_from_transact_info(
						destination.clone(),
						weight_info.transact_required_weight_at_most,
						false,
					)?;
					Ok(WeightLimit::from(Some(weight_info)))
18
				},
18
				|v| Ok(v),
18
			)?;
18
			let total_weight_fee_calculation = match total_weight {
				Unlimited => MAX_WEIGHT,
18
				Limited(x) => x,
			};
18
			let fee = Self::calculate_fee(
18
				fee_location,
18
				fee.fee_amount,
18
				destination.clone(),
18
				total_weight_fee_calculation,
18
			)?;
18
			ensure!(
18
				T::MaxHrmpFee::filter_max_asset_fee(&fee),
1
				Error::<T>::TooMuchFeeUsed
			);
			// The appendix instruction will be a deposit back to a self location
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]),
17
			)?;
15
			Self::deposit_event(Event::HrmpManagementSent { action });
15

            
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);
30

            
30
				// Construct the local withdraw message with the previous calculated amount
30
				// 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,
51
			)?;
			// 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
		}
83
		fn transact_in_dest_chain_asset_signed(
83
			dest: Location,
83
			fee_payer: T::AccountId,
83
			fee: Asset,
83
			call: Vec<u8>,
83
			origin_kind: OriginKind,
83
			total_weight: WeightLimit,
83
			transact_required_weight_at_most: Weight,
83
			with_appendix: Option<Vec<Instruction<()>>>,
83
		) -> DispatchResult {
83
			// Convert origin to multilocation
83
			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
83
			let mut transact_message: Xcm<()> = Self::transact_message(
83
				dest.clone(),
83
				fee,
83
				total_weight,
83
				call,
83
				transact_required_weight_at_most,
83
				origin_kind,
83
				with_appendix,
83
			)?;
			// We append DescendOrigin as the first instruction in the message
			// The new message looks like DescendOrigin||WithdrawAsset||BuyExecution||
			// Transact.
83
			let interior: Junctions = origin_as_mult
83
				.clone()
83
				.try_into()
83
				.map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
83
			transact_message.0.insert(0, DescendOrigin(interior));
			// Send to destination chain
62
			let (ticket, _price) =
83
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
83
					.map_err(|_| Error::<T>::ErrorValidating)?;
62
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
62
			Ok(())
83
		}
		/// Calculate the amount of fee based on the multilocation of the fee asset and
		/// the total weight to be spent
140
		fn calculate_fee(
140
			fee_location: Location,
140
			fee_amount: Option<u128>,
140
			destination: Location,
140
			total_weight: Weight,
140
		) -> 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
140
			let amount: u128 = fee_amount.map_or_else(
140
				|| {
47
					Self::take_fee_per_second_from_storage(
47
						fee_location.clone(),
47
						destination,
47
						total_weight,
47
					)
140
				},
140
				|v| Ok(v),
140
			)?;
			// Construct Asset
135
			Ok(Asset {
135
				id: AssetId(fee_location),
135
				fun: Fungible(amount),
135
			})
140
		}
		/// Construct the transact xcm message with the provided parameters
134
		fn transact_message(
134
			dest: Location,
134
			asset: Asset,
134
			dest_weight: WeightLimit,
134
			call: Vec<u8>,
134
			dispatch_weight: Weight,
134
			origin_kind: OriginKind,
134
			with_appendix: Option<Vec<Instruction<()>>>,
134
		) -> Result<Xcm<()>, DispatchError> {
134
			let mut instructions = vec![
134
				Self::withdraw_instruction(asset.clone(), &dest)?,
134
				Self::buy_execution(asset, &dest, dest_weight)?,
			];
134
			if let Some(appendix) = with_appendix {
31
				instructions.push(Self::appendix_instruction(appendix)?);
103
			}
134
			instructions.push(Transact {
134
				origin_kind,
134
				fallback_max_weight: Some(dispatch_weight),
134
				call: call.into(),
134
			});
134
			Ok(Xcm(instructions))
134
		}
		/// Construct a buy execution xcm order with the provided parameters
134
		fn buy_execution(
134
			asset: Asset,
134
			at: &Location,
134
			weight: WeightLimit,
134
		) -> Result<Instruction<()>, DispatchError> {
134
			let universal_location = T::UniversalLocation::get();
134
			let fees = asset
134
				.reanchored(at, &universal_location)
134
				.map_err(|_| Error::<T>::CannotReanchor)?;
134
			Ok(BuyExecution {
134
				fees,
134
				weight_limit: weight,
134
			})
134
		}
		/// Construct a withdraw instruction from a sovereign account
134
		fn withdraw_instruction(
134
			asset: Asset,
134
			at: &Location,
134
		) -> Result<Instruction<()>, DispatchError> {
134
			let universal_location = T::UniversalLocation::get();
134
			let fees = asset
134
				.reanchored(at, &universal_location)
134
				.map_err(|_| Error::<T>::CannotReanchor)?;
134
			Ok(WithdrawAsset(fees.into()))
134
		}
		/// 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.
44
		fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
44
			let chain_location = dest.chain_location();
44
			if *dest == chain_location {
44
				Ok(chain_location)
			} else {
				Err(Error::<T>::InvalidDest.into())
			}
44
		}
		/// 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.
44
		fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
44
			let dest = Self::ensure_valid_dest(dest)?;
44
			let self_location = T::SelfLocation::get();
44
			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
44
			let reserve =
44
				T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
			// We only allow to transact using a reserve asset as fee
44
			ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
42
			Ok(dest)
44
		}
		/// Returns weight of `weight_of_initiate_reserve_withdraw` call.
6
		fn weight_of_initiate_reserve_withdraw() -> Weight {
6
			let dest = Location::parent();
6

            
6
			// We can use whatever asset here
6
			let asset = Location::parent();
6

            
6
			// Construct Asset
6
			let fee = Asset {
6
				id: AssetId(asset.clone()),
6
				fun: Fungible(0),
6
			};
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()).map_or(Weight::max_value(), |w| {
6
				T::BaseXcmWeight::get().saturating_add(w)
6
			})
6
		}
		/// Returns the fee for a given set of parameters
		/// We always round up in case of fractional division
44
		pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
44
			// grab WEIGHT_REF_TIME_PER_SECOND as u128
44
			let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
44

            
44
			// we add WEIGHT_REF_TIME_PER_SECOND -1 after multiplication to make sure that
44
			// if there is a fractional part we round up the result
44
			let fee_mul_rounded_up = (fee_per_second.saturating_mul(weight.ref_time() as u128))
44
				.saturating_add(weight_per_second_u128 - 1);
44

            
44
			fee_mul_rounded_up / weight_per_second_u128
44
		}
		/// 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> {
25
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
25
			// 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
35
		pub fn take_weight_from_transact_info_signed(
35
			dest: Location,
35
			dest_weight: Weight,
35
			refund: bool,
35
		) -> Result<Weight, DispatchError> {
35
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
35
			// so we have to ensure 'refund' is false
35
			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
			// Grab transact info for the destination provided
35
			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
35
				.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
33
			let transact_in_dest_as_signed_weight = transactor_info
33
				.transact_extra_weight_signed
33
				.ok_or(Error::<T>::SignedTransactNotAllowedForDestination)?;
31
			let total_weight = dest_weight
31
				.checked_add(&transact_in_dest_as_signed_weight)
31
				.ok_or(Error::<T>::WeightOverflow)?;
30
			ensure!(
30
				total_weight.all_lte(transactor_info.max_weight),
1
				Error::<T>::MaxWeightTransactReached
			);
29
			Ok(total_weight)
35
		}
		/// Returns the fee per second charged by a reserve chain for an asset
		/// it takes this information from storage
47
		pub fn take_fee_per_second_from_storage(
47
			fee_location: Location,
47
			destination: Location,
47
			total_weight: Weight,
47
		) -> Result<u128, DispatchError> {
47
			let fee_per_second = DestinationAssetFeePerSecond::<T>::get(&fee_location)
47
				.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
44
			Self::transfer_allowed(&(fee_location, fee_per_second).into(), &destination)?;
42
			Ok(Self::calculate_fee_per_second(total_weight, fee_per_second))
47
		}
		/// Converts Currency to multilocation
156
		pub fn currency_to_multilocation(currency: Currency<CurrencyIdOf<T>>) -> Option<Location> {
156
			match currency {
20
				Currency::AsCurrencyId(id) => T::CurrencyIdToLocation::convert(id),
136
				Currency::AsMultiLocation(multiloc) => Location::try_from(*multiloc).ok(),
			}
156
		}
	}
}