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
1615
#[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, Bounded, 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
137
	#[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 {
134
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
135
		/// The balance type.
136
		type Balance: Parameter
137
			+ Member
138
			+ AtLeast32BitUnsigned
139
			+ Default
140
			+ Copy
141
			+ MaybeSerializeDeserialize
142
			+ Into<u128>;
143

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
200
		type WeightInfo: WeightInfo;
201
	}
202

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

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

            
251
	impl<T> Default for Currency<T> {
252
		fn default() -> Currency<T> {
253
			Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
254
		}
255
	}
256

            
257
	#[derive(
258
		Clone,
259
		Encode,
260
		Decode,
261
		Eq,
262
		PartialEq,
263
		RuntimeDebug,
264
198
		scale_info::TypeInfo,
265
		DecodeWithMemTracking,
266
	)]
267
	pub struct HrmpInitParams {
268
		pub para_id: ParaId,
269
		pub proposed_max_capacity: u32,
270
		pub proposed_max_message_size: u32,
271
	}
272

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

            
296
	#[derive(
297
		Default,
298
		Clone,
299
		Encode,
300
		Decode,
301
		Eq,
302
		PartialEq,
303
		RuntimeDebug,
304
		MaxEncodedLen,
305
38
		scale_info::TypeInfo,
306
		DecodeWithMemTracking,
307
	)]
308

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

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

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

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

            
361
	/// Stores the transact info of a Location. This defines how much extra weight we need to
362
	/// add when we want to transact in the destination chain and maximum amount of weight allowed
363
	/// by the destination chain
364
140
	#[pallet::storage]
365
	#[pallet::getter(fn transact_info)]
366
	pub type TransactInfoWithWeightLimit<T: Config> =
367
		StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
368

            
369
	/// Stores the fee per second for an asset in its reserve chain. This allows us to convert
370
	/// from weight to fee
371
119
	#[pallet::storage]
372
	#[pallet::getter(fn dest_asset_fee_per_second)]
373
	pub type DestinationAssetFeePerSecond<T: Config> = StorageMap<_, Twox64Concat, Location, u128>;
374

            
375
	/// Stores the indices of relay chain pallets
376
2022
	#[pallet::storage]
377
	#[pallet::getter(fn relay_indices)]
378
	pub type RelayIndices<T: Config> = StorageValue<_, RelayChainIndices, ValueQuery>;
379

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

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

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

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

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

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

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

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

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

            
521
40
			Ok(())
522
		}
523

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

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

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

            
537
1
			Ok(())
538
		}
539

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

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

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

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

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

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

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

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

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

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

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

            
642
19
			Ok(())
643
		}
644

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

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

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

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

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

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

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

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

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

            
734
15
			Ok(())
735
		}
736

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

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

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

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

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

            
777
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
778
1
			Ok(())
779
		}
780

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

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

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

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

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

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

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

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

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

            
865
58
			Ok(())
866
		}
867

            
868
		/// Set the fee per second of an asset on its reserve chain
869
		#[pallet::call_index(7)]
870
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
871
		pub fn set_fee_per_second(
872
			origin: OriginFor<T>,
873
			asset_location: Box<VersionedLocation>,
874
			fee_per_second: u128,
875
49
		) -> DispatchResult {
876
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
877
49
			let asset_location =
878
49
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
879

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

            
882
49
			Self::deposit_event(Event::DestFeePerSecondChanged {
883
49
				location: asset_location,
884
49
				fee_per_second,
885
49
			});
886
49
			Ok(())
887
		}
888

            
889
		/// Remove the fee per second of an asset on its reserve chain
890
		#[pallet::call_index(8)]
891
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
892
		pub fn remove_fee_per_second(
893
			origin: OriginFor<T>,
894
			asset_location: Box<VersionedLocation>,
895
		) -> DispatchResult {
896
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
897
			let asset_location =
898
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
899

            
900
			DestinationAssetFeePerSecond::<T>::remove(&asset_location);
901

            
902
			Self::deposit_event(Event::DestFeePerSecondRemoved {
903
				location: asset_location,
904
			});
905
			Ok(())
906
		}
907

            
908
		/// Manage HRMP operations
909
		#[pallet::call_index(9)]
910
		#[pallet::weight(T::WeightInfo::hrmp_manage())]
911
		pub fn hrmp_manage(
912
			origin: OriginFor<T>,
913
			action: HrmpOperation,
914
			// fee to be used
915
			fee: CurrencyPayment<CurrencyIdOf<T>>,
916
			// weight information to be used
917
			weight_info: TransactWeights,
918
18
		) -> DispatchResult {
919
18
			// WithdrawAsset
920
18
			// BuyExecution
921
18
			// SetAppendix(RefundSurplus, DepositAsset(sov account))
922
18
			// Transact
923
18

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

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

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

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

            
968
			// Calculate the total weight that the xcm message is going to spend in the
969
			// destination chain
970
18
			let total_weight = weight_info.overall_weight.map_or_else(
971
18
				|| -> Result<_, DispatchError> {
972
					let weight_info = Self::take_weight_from_transact_info(
973
						destination.clone(),
974
						weight_info.transact_required_weight_at_most,
975
						false,
976
					)?;
977
					Ok(WeightLimit::from(Some(weight_info)))
978
18
				},
979
18
				|v| Ok(v),
980
18
			)?;
981

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

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

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

            
999
			// 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
		}
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 {
77
			// 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,
77
			)?;
			// 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(
134
				|| {
45
					Self::take_fee_per_second_from_storage(
45
						fee_location.clone(),
45
						destination,
45
						total_weight,
45
					)
134
				},
134
				|v| Ok(v),
134
			)?;
			// 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();
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
42
		pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
42
			// grab WEIGHT_REF_TIME_PER_SECOND as u128
42
			let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
42

            
42
			// we add WEIGHT_REF_TIME_PER_SECOND -1 after multiplication to make sure that
42
			// 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

            
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> {
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
33
		pub fn take_weight_from_transact_info_signed(
33
			dest: Location,
33
			dest_weight: Weight,
33
			refund: bool,
33
		) -> Result<Weight, DispatchError> {
33
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
33
			// 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
		}
	}
}