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 (tipically, 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
775
#[pallet]
91
pub mod pallet {
92

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

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

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

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

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

            
138
		// XcmTransact needs to be implemented. This type needs to implement
139
		// utility call encoding and multilocation gathering
140
		type Transactor: Parameter + Member + Clone + XcmTransact;
141

            
142
		/// AssetTransactor allows us to withdraw asset without being trapped
143
		/// This should change in xcm v3, which allows us to burn assets
144
		type AssetTransactor: TransactAsset;
145

            
146
		// The origin that is allowed to register derivative address indices
147
		type DerivativeAddressRegistrationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
148

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

            
152
		// The origin that is allowed to open and accept an Hrmp channel
153
		type HrmpOpenOrigin: EnsureOrigin<Self::RuntimeOrigin>;
154

            
155
		/// Convert `T::AccountId` to `Location`.
156
		type AccountIdToLocation: Convert<Self::AccountId, Location>;
157

            
158
		/// Means of measuring the weight consumed by an XCM message locally.
159
		type Weigher: WeightBounds<Self::RuntimeCall>;
160

            
161
		/// This chain's Universal Location.
162
		type UniversalLocation: Get<InteriorLocation>;
163

            
164
		/// Self chain location.
165
		#[pallet::constant]
166
		type SelfLocation: Get<Location>;
167

            
168
		// The origin that is allowed to dispatch calls from the sovereign account directly
169
		type SovereignAccountDispatcherOrigin: EnsureOrigin<Self::RuntimeOrigin>;
170

            
171
		/// XCM sender.
172
		type XcmSender: SendXcm;
173

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

            
181
		/// The way to retrieve the reserve of a Asset. This can be
182
		/// configured to accept absolute or relative paths for self tokens
183
		type ReserveProvider: xcm_primitives::Reserve;
184

            
185
		/// The way to filter the max fee to use for HRMP management operations
186
		type MaxHrmpFee: FilterMaxAssetFee;
187

            
188
		type WeightInfo: WeightInfo;
189
	}
190

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

            
220
	/// Enum defining the way to express a Currency.
221
60
	#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, scale_info::TypeInfo)]
222
	pub enum Currency<CurrencyId> {
223
		// Express the Currency as a CurrencyId
224
		AsCurrencyId(CurrencyId),
225
		// Express the Currency as its MultiLOcation
226
		AsMultiLocation(Box<VersionedLocation>),
227
	}
228

            
229
	impl<T> Default for Currency<T> {
230
		fn default() -> Currency<T> {
231
			Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
232
		}
233
	}
234

            
235
207
	#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, scale_info::TypeInfo)]
236
	pub struct HrmpInitParams {
237
		pub para_id: ParaId,
238
		pub proposed_max_capacity: u32,
239
		pub proposed_max_message_size: u32,
240
	}
241

            
242
	/// Enum defining the way to express a Currency.
243
345
	#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, scale_info::TypeInfo)]
244
	pub enum HrmpOperation {
245
		InitOpen(HrmpInitParams),
246
		Accept {
247
			para_id: ParaId,
248
		},
249
		Close(HrmpChannelId),
250
		Cancel {
251
			channel_id: HrmpChannelId,
252
			open_requests: u32,
253
		},
254
	}
255

            
256
	#[derive(
257
		Default,
258
		Clone,
259
		Encode,
260
		Decode,
261
		Eq,
262
		PartialEq,
263
		RuntimeDebug,
264
		MaxEncodedLen,
265
40
		scale_info::TypeInfo,
266
	)]
267

            
268
	/// Struct that defines how to express the payment in a particular currency
269
	/// currency is defined by the Currency enum, which can be expressed as:
270
	/// - CurrencyId
271
	/// - Location
272
	///
273
	/// The fee_amount is an option. In case of None, the fee will be tried to
274
	/// be calculated from storage. If the storage item for the currency is not
275
	/// populated, then it fails
276
	pub struct CurrencyPayment<CurrencyId> {
277
		// the currency in which we want to express our payment
278
		pub currency: Currency<CurrencyId>,
279
		// indicates whether we want to specify the fee amount to be used
280
		pub fee_amount: Option<u128>,
281
	}
282

            
283
138
	#[derive(Default, Clone, Encode, Decode, RuntimeDebug, PartialEq, scale_info::TypeInfo)]
284
	/// Struct tindicating information about transact weights
285
	/// It allows to specify:
286
	/// - transact_required_weight_at_most: the amount of weight the Transact instruction
287
	///   should consume at most
288
	/// - overall_weight: the overall weight to be used for the whole XCM message execution.
289
	///   If None, then this amount will be tried to be derived from storage.  If the storage item
290
	pub struct TransactWeights {
291
		// the amount of weight the Transact instruction should consume at most
292
		pub transact_required_weight_at_most: Weight,
293
		// the overall weight to be used for the whole XCM message execution. If None,
294
		// then this amount will be tried to be derived from storage.  If the storage item
295
		// for the chain is not populated, then it fails
296
		pub overall_weight: Option<WeightLimit>,
297
	}
298

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

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

            
311
	/// Stores the transact info of a Location. This defines how much extra weight we need to
312
	/// add when we want to transact in the destination chain and maximum amount of weight allowed
313
	/// by the destination chain
314
142
	#[pallet::storage]
315
	#[pallet::getter(fn transact_info)]
316
	pub type TransactInfoWithWeightLimit<T: Config> =
317
		StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
318

            
319
	/// Stores the fee per second for an asset in its reserve chain. This allows us to convert
320
	/// from weight to fee
321
121
	#[pallet::storage]
322
	#[pallet::getter(fn dest_asset_fee_per_second)]
323
	pub type DestinationAssetFeePerSecond<T: Config> = StorageMap<_, Twox64Concat, Location, u128>;
324

            
325
	/// Stores the indices of relay chain pallets
326
1894
	#[pallet::storage]
327
	#[pallet::getter(fn relay_indices)]
328
	pub type RelayIndices<T: Config> = StorageValue<_, RelayChainIndices, ValueQuery>;
329

            
330
	/// An error that can occur while executing the mapping pallet's logic.
331
136
	#[pallet::error]
332
	pub enum Error<T> {
333
		IndexAlreadyClaimed,
334
		UnclaimedIndex,
335
		NotOwner,
336
		UnweighableMessage,
337
		CannotReanchor,
338
		AssetHasNoReserve,
339
		InvalidDest,
340
		NotCrossChainTransfer,
341
		AssetIsNotReserveInDestination,
342
		DestinationNotInvertible,
343
		ErrorDelivering,
344
		DispatchWeightBiggerThanTotalWeight,
345
		WeightOverflow,
346
		AmountOverflow,
347
		TransactorInfoNotSet,
348
		NotCrossChainTransferableCurrency,
349
		XcmExecuteError,
350
		BadVersion,
351
		MaxWeightTransactReached,
352
		UnableToWithdrawAsset,
353
		FeePerSecondNotSet,
354
		SignedTransactNotAllowedForDestination,
355
		FailedMultiLocationToJunction,
356
		HrmpHandlerNotImplemented,
357
		TooMuchFeeUsed,
358
		ErrorValidating,
359
		RefundNotSupportedWithTransactInfo,
360
	}
361

            
362
20
	#[pallet::event]
363
249
	#[pallet::generate_deposit(pub(crate) fn deposit_event)]
364
	pub enum Event<T: Config> {
365
11
		/// Transacted the inner call through a derivative account in a destination chain.
366
		TransactedDerivative {
367
			account_id: T::AccountId,
368
			dest: Location,
369
			call: Vec<u8>,
370
			index: u16,
371
		},
372
3
		/// Transacted the call through the sovereign account in a destination chain.
373
		TransactedSovereign {
374
			fee_payer: Option<T::AccountId>,
375
			dest: Location,
376
			call: Vec<u8>,
377
		},
378
3
		/// Transacted the call through a signed account in a destination chain.
379
		TransactedSigned {
380
			fee_payer: T::AccountId,
381
			dest: Location,
382
			call: Vec<u8>,
383
		},
384
15
		/// Registered a derivative index for an account id.
385
		RegisteredDerivative {
386
			account_id: T::AccountId,
387
			index: u16,
388
		},
389
1
		DeRegisteredDerivative {
390
			index: u16,
391
		},
392
		/// Transact failed
393
		TransactFailed {
394
			error: XcmError,
395
		},
396
7
		/// Changed the transact info of a location
397
		TransactInfoChanged {
398
			location: Location,
399
			remote_info: RemoteTransactInfoWithMaxWeight,
400
		},
401
1
		/// Removed the transact info of a location
402
		TransactInfoRemoved {
403
			location: Location,
404
		},
405
7
		/// Set dest fee per second
406
		DestFeePerSecondChanged {
407
			location: Location,
408
			fee_per_second: u128,
409
		},
410
		/// Remove dest fee per second
411
		DestFeePerSecondRemoved {
412
			location: Location,
413
		},
414
		/// HRMP manage action succesfully sent
415
		HrmpManagementSent {
416
			action: HrmpOperation,
417
		},
418
	}
419

            
420
	#[pallet::genesis_config]
421
	pub struct GenesisConfig<T> {
422
		pub relay_indices: RelayChainIndices,
423
		#[serde(skip)]
424
		pub _phantom: PhantomData<T>,
425
	}
426

            
427
	impl<T> Default for GenesisConfig<T> {
428
737
		fn default() -> Self {
429
737
			Self {
430
737
				relay_indices: RelayChainIndices::default(),
431
737
				_phantom: Default::default(),
432
737
			}
433
737
		}
434
	}
435

            
436
726
	#[pallet::genesis_build]
437
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
438
735
		fn build(&self) {
439
735
			<RelayIndices<T>>::put(self.relay_indices);
440
735
		}
441
	}
442

            
443
177
	#[pallet::call]
444
	impl<T: Config> Pallet<T> {
445
		/// Register a derivative index for an account id. Dispatchable by
446
		/// DerivativeAddressRegistrationOrigin
447
		///
448
		/// We do not store the derivative address, but only the index. We do not need to store
449
		/// the derivative address to issue calls, only the index is enough
450
		///
451
		/// For now an index is registered for all possible destinations and not per-destination.
452
		/// We can change this in the future although it would just make things more complicated
453
		#[pallet::call_index(0)]
454
		#[pallet::weight(T::WeightInfo::register())]
455
41
		pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
456
41
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
457

            
458
40
			ensure!(
459
40
				IndexToAccount::<T>::get(&index).is_none(),
460
				Error::<T>::IndexAlreadyClaimed
461
			);
462

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

            
465
40
			// Deposit event
466
40
			Self::deposit_event(Event::<T>::RegisteredDerivative {
467
40
				account_id: who,
468
40
				index: index,
469
40
			});
470
40

            
471
40
			Ok(())
472
		}
473

            
474
		/// De-Register a derivative index. This prevents an account to use a derivative address
475
		/// (represented by an index) from our of our sovereign accounts anymore
476
		#[pallet::call_index(1)]
477
		#[pallet::weight(T::WeightInfo::deregister())]
478
1
		pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
479
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
480

            
481
			// Remove index
482
1
			IndexToAccount::<T>::remove(&index);
483
1

            
484
1
			// Deposit event
485
1
			Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
486
1

            
487
1
			Ok(())
488
		}
489

            
490
		/// Transact the inner call through a derivative account in a destination chain,
491
		/// using 'fee_location' to pay for the fees. This fee_location is given as a multilocation
492
		///
493
		/// The caller needs to have the index registered in this pallet. The fee multiasset needs
494
		/// to be a reserve asset for the destination transactor::multilocation.
495
		#[pallet::call_index(2)]
496
		#[pallet::weight(
497
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
498
			.saturating_add(T::WeightInfo::transact_through_derivative())
499
		)]
500
		pub fn transact_through_derivative(
501
			origin: OriginFor<T>,
502
			// destination to which the message should be sent
503
			dest: T::Transactor,
504
			// derivative index to be used
505
			index: u16,
506
			// fee to be used
507
			fee: CurrencyPayment<CurrencyIdOf<T>>,
508
			// inner call to be executed in destination. This wiol
509
			// be wrapped into utility.as_derivative
510
			inner_call: Vec<u8>,
511
			// weight information to be used
512
			weight_info: TransactWeights,
513
			// add RefundSurplus and DepositAsset appendix
514
			refund: bool,
515
31
		) -> DispatchResult {
516
31
			let who = ensure_signed(origin)?;
517

            
518
31
			let fee_location = Self::currency_to_multilocation(fee.currency)
519
31
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
520

            
521
			// The index exists
522
31
			let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
523
			// The derivative index is owned by the origin
524
30
			ensure!(account == who, Error::<T>::NotOwner);
525

            
526
			// Encode call bytes
527
			// We make sure the inner call is wrapped on a as_derivative dispatchable
528
30
			let call_bytes: Vec<u8> = dest
529
30
				.clone()
530
30
				.encode_call(UtilityAvailableCalls::AsDerivative(index, inner_call));
531
30

            
532
30
			// Grab the destination
533
30
			let dest = dest.destination();
534

            
535
			// Calculate the total weight that the xcm message is going to spend in the
536
			// destination chain
537
30
			let total_weight = weight_info.overall_weight.map_or_else(
538
30
				|| -> Result<_, DispatchError> {
539
18
					let weight_info = Self::take_weight_from_transact_info(
540
18
						dest.clone(),
541
18
						weight_info.transact_required_weight_at_most,
542
18
						refund,
543
18
					)?;
544
9
					Ok(WeightLimit::from(Some(weight_info)))
545
30
				},
546
30
				|v| Ok(v),
547
30
			)?;
548

            
549
21
			let total_weight_fee_calculation = match total_weight {
550
				Unlimited => MAX_WEIGHT,
551
21
				Limited(x) => x,
552
			};
553

            
554
			// Calculate fee based on FeePerSecond
555
21
			let fee = Self::calculate_fee(
556
21
				fee_location,
557
21
				fee.fee_amount,
558
21
				dest.clone(),
559
21
				total_weight_fee_calculation,
560
21
			)?;
561

            
562
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
563
19
			let appendix = refund
564
19
				.then(|| -> Result<_, DispatchError> {
565
4
					Ok(vec![
566
4
						RefundSurplus,
567
4
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
568
					])
569
19
				})
570
19
				.transpose()?;
571

            
572
19
			Self::transact_in_dest_chain_asset_non_signed(
573
19
				dest.clone(),
574
19
				Some(who.clone()),
575
19
				fee,
576
19
				call_bytes.clone(),
577
19
				OriginKind::SovereignAccount,
578
19
				total_weight,
579
19
				weight_info.transact_required_weight_at_most,
580
19
				appendix,
581
19
			)?;
582

            
583
			// Deposit event
584
19
			Self::deposit_event(Event::<T>::TransactedDerivative {
585
19
				account_id: who,
586
19
				dest,
587
19
				call: call_bytes,
588
19
				index,
589
19
			});
590
19

            
591
19
			Ok(())
592
		}
593

            
594
		/// Transact the call through the sovereign account in a destination chain,
595
		/// 'fee_payer' pays for the fee
596
		///
597
		/// SovereignAccountDispatcherOrigin callable only
598
		#[pallet::call_index(3)]
599
		#[pallet::weight(
600
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
601
			.saturating_add(T::WeightInfo::transact_through_sovereign())
602
		)]
603
		pub fn transact_through_sovereign(
604
			origin: OriginFor<T>,
605
			// destination to which the message should be sent
606
			dest: Box<VersionedLocation>,
607
			// account paying for fees
608
			fee_payer: Option<T::AccountId>,
609
			// fee to be used
610
			fee: CurrencyPayment<CurrencyIdOf<T>>,
611
			// call to be executed in destination
612
			call: Vec<u8>,
613
			// origin kind to be used
614
			origin_kind: OriginKind,
615
			// weight information to be used
616
			weight_info: TransactWeights,
617
			// add RefundSurplus and DepositAsset appendix
618
			refund: bool,
619
16
		) -> DispatchResult {
620
16
			T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
621

            
622
15
			let fee_location = Self::currency_to_multilocation(fee.currency)
623
15
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
624

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

            
627
			// Calculate the total weight that the xcm message is going to spend in the
628
			// destination chain
629
15
			let total_weight = weight_info.overall_weight.map_or_else(
630
15
				|| -> Result<_, DispatchError> {
631
7
					let weight_info = Self::take_weight_from_transact_info(
632
7
						dest.clone(),
633
7
						weight_info.transact_required_weight_at_most,
634
7
						refund,
635
7
					)?;
636
7
					Ok(WeightLimit::from(Some(weight_info)))
637
15
				},
638
15
				|v| Ok(v),
639
15
			)?;
640

            
641
15
			let total_weight_fee_calculation = match total_weight {
642
				Unlimited => MAX_WEIGHT,
643
15
				Limited(x) => x,
644
			};
645

            
646
			// Calculate fee based on FeePerSecond and total_weight
647
15
			let fee = Self::calculate_fee(
648
15
				fee_location,
649
15
				fee.fee_amount,
650
15
				dest.clone(),
651
15
				total_weight_fee_calculation,
652
15
			)?;
653

            
654
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
655
15
			let appendix = refund
656
15
				.then(|| -> Result<_, DispatchError> {
657
3
					Ok(vec![
658
3
						RefundSurplus,
659
3
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
660
					])
661
15
				})
662
15
				.transpose()?;
663

            
664
			// Grab the destination
665
15
			Self::transact_in_dest_chain_asset_non_signed(
666
15
				dest.clone(),
667
15
				fee_payer.clone(),
668
15
				fee,
669
15
				call.clone(),
670
15
				origin_kind,
671
15
				total_weight,
672
15
				weight_info.transact_required_weight_at_most,
673
15
				appendix,
674
15
			)?;
675

            
676
			// Deposit event
677
15
			Self::deposit_event(Event::<T>::TransactedSovereign {
678
15
				fee_payer,
679
15
				dest,
680
15
				call,
681
15
			});
682
15

            
683
15
			Ok(())
684
		}
685

            
686
		/// Change the transact info of a location
687
		#[pallet::call_index(4)]
688
		#[pallet::weight(T::WeightInfo::set_transact_info())]
689
		pub fn set_transact_info(
690
			origin: OriginFor<T>,
691
			location: Box<VersionedLocation>,
692
			transact_extra_weight: Weight,
693
			max_weight: Weight,
694
			transact_extra_weight_signed: Option<Weight>,
695
49
		) -> DispatchResult {
696
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
697
49
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
698
49
			let remote_info = RemoteTransactInfoWithMaxWeight {
699
49
				transact_extra_weight,
700
49
				max_weight,
701
49
				transact_extra_weight_signed,
702
49
			};
703
49

            
704
49
			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
705
49

            
706
49
			Self::deposit_event(Event::TransactInfoChanged {
707
49
				location,
708
49
				remote_info,
709
49
			});
710
49
			Ok(())
711
		}
712

            
713
		/// Remove the transact info of a location
714
		#[pallet::call_index(5)]
715
		#[pallet::weight(T::WeightInfo::remove_transact_info())]
716
		pub fn remove_transact_info(
717
			origin: OriginFor<T>,
718
			location: Box<VersionedLocation>,
719
1
		) -> DispatchResult {
720
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
721
1
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
722

            
723
			// Remove transact info
724
1
			TransactInfoWithWeightLimit::<T>::remove(&location);
725
1

            
726
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
727
1
			Ok(())
728
		}
729

            
730
		/// Transact the call through the a signed origin in this chain
731
		/// that should be converted to a transaction dispatch account in the destination chain
732
		/// by any method implemented in the destination chains runtime
733
		///
734
		/// This time we are giving the currency as a currencyId instead of multilocation
735
		#[pallet::call_index(6)]
736
		#[pallet::weight(T::WeightInfo::transact_through_signed())]
737
		pub fn transact_through_signed(
738
			origin: OriginFor<T>,
739
			// destination to which the message should be sent
740
			dest: Box<VersionedLocation>,
741
			// fee to be used
742
			fee: CurrencyPayment<CurrencyIdOf<T>>,
743
			// call to be executed in destination
744
			call: Vec<u8>,
745
			// weight information to be used
746
			weight_info: TransactWeights,
747
			// add RefundSurplus and DepositAsset appendix
748
			refund: bool,
749
89
		) -> DispatchResult {
750
89
			let who = ensure_signed(origin)?;
751

            
752
89
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
753

            
754
89
			let fee_location = Self::currency_to_multilocation(fee.currency)
755
89
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
756

            
757
			// Calculate the total weight that the xcm message is going to spend in the
758
			// destination chain
759
89
			let total_weight = weight_info.overall_weight.map_or_else(
760
89
				|| -> Result<_, DispatchError> {
761
34
					let weight_info = Self::take_weight_from_transact_info_signed(
762
34
						dest.clone(),
763
34
						weight_info.transact_required_weight_at_most,
764
34
						refund,
765
34
					)?;
766
28
					Ok(WeightLimit::from(Some(weight_info)))
767
89
				},
768
89
				|v| Ok(v),
769
89
			)?;
770

            
771
83
			let total_weight_fee_calculation = match total_weight {
772
				Unlimited => MAX_WEIGHT,
773
83
				Limited(x) => x,
774
			};
775

            
776
			// Fee to be paid
777
83
			let fee = Self::calculate_fee(
778
83
				fee_location,
779
83
				fee.fee_amount,
780
83
				dest.clone(),
781
83
				total_weight_fee_calculation,
782
83
			)?;
783

            
784
			// If refund is true, the appendix instruction will be a deposit back to the sender
785
80
			let appendix = refund
786
80
				.then(|| -> Result<_, DispatchError> {
787
7
					let sender = T::AccountIdToLocation::convert(who.clone());
788
7
					Ok(vec![
789
7
						RefundSurplus,
790
7
						Self::deposit_instruction(sender, &dest, 1u32)?,
791
					])
792
80
				})
793
80
				.transpose()?;
794

            
795
			// Grab the destination
796
80
			Self::transact_in_dest_chain_asset_signed(
797
80
				dest.clone(),
798
80
				who.clone(),
799
80
				fee,
800
80
				call.clone(),
801
80
				OriginKind::SovereignAccount,
802
80
				total_weight,
803
80
				weight_info.transact_required_weight_at_most,
804
80
				appendix,
805
80
			)?;
806

            
807
			// Deposit event
808
60
			Self::deposit_event(Event::<T>::TransactedSigned {
809
60
				fee_payer: who,
810
60
				dest,
811
60
				call,
812
60
			});
813
60

            
814
60
			Ok(())
815
		}
816

            
817
		/// Set the fee per second of an asset on its reserve chain
818
		#[pallet::call_index(7)]
819
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
820
		pub fn set_fee_per_second(
821
			origin: OriginFor<T>,
822
			asset_location: Box<VersionedLocation>,
823
			fee_per_second: u128,
824
49
		) -> DispatchResult {
825
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
826
49
			let asset_location =
827
49
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
828

            
829
49
			DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
830
49

            
831
49
			Self::deposit_event(Event::DestFeePerSecondChanged {
832
49
				location: asset_location,
833
49
				fee_per_second,
834
49
			});
835
49
			Ok(())
836
		}
837

            
838
		/// Remove the fee per second of an asset on its reserve chain
839
		#[pallet::call_index(8)]
840
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
841
		pub fn remove_fee_per_second(
842
			origin: OriginFor<T>,
843
			asset_location: Box<VersionedLocation>,
844
		) -> DispatchResult {
845
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
846
			let asset_location =
847
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
848

            
849
			DestinationAssetFeePerSecond::<T>::remove(&asset_location);
850

            
851
			Self::deposit_event(Event::DestFeePerSecondRemoved {
852
				location: asset_location,
853
			});
854
			Ok(())
855
		}
856

            
857
		/// Manage HRMP operations
858
		#[pallet::call_index(9)]
859
		#[pallet::weight(T::WeightInfo::hrmp_manage())]
860
		pub fn hrmp_manage(
861
			origin: OriginFor<T>,
862
			action: HrmpOperation,
863
			// fee to be used
864
			fee: CurrencyPayment<CurrencyIdOf<T>>,
865
			// weight information to be used
866
			weight_info: TransactWeights,
867
18
		) -> DispatchResult {
868
18
			// WithdrawAsset
869
18
			// BuyExecution
870
18
			// SetAppendix(RefundSurplus, DepositAsset(sov account))
871
18
			// Transact
872
18

            
873
18
			// check permissions
874
18
			match &action {
875
				HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
876
13
					<EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
877
13
						origin,
878
13
					)?;
879
				}
880
				HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
881
5
					T::HrmpManipulatorOrigin::ensure_origin(origin)?;
882
				}
883
			}
884

            
885
			// process action
886
18
			let call_bytes = match action.clone() {
887
8
				HrmpOperation::InitOpen(params) => {
888
8
					Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
889
8
						params.para_id,
890
8
						params.proposed_max_capacity,
891
8
						params.proposed_max_message_size,
892
8
					))
893
				}
894
5
				HrmpOperation::Accept { para_id } => {
895
5
					Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
896
				}
897
4
				HrmpOperation::Close(close_params) => {
898
4
					Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
899
				}
900
				HrmpOperation::Cancel {
901
1
					channel_id,
902
1
					open_requests,
903
1
				} => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
904
1
					channel_id,
905
1
					open_requests,
906
1
				)),
907
			}
908
18
			.map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
909

            
910
18
			let fee_location = Self::currency_to_multilocation(fee.currency)
911
18
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
912

            
913
			// Grab the destination
914
			// For hrmp, it is always parent
915
18
			let destination = Location::parent();
916

            
917
			// Calculate the total weight that the xcm message is going to spend in the
918
			// destination chain
919
18
			let total_weight = weight_info.overall_weight.map_or_else(
920
18
				|| -> Result<_, DispatchError> {
921
					let weight_info = Self::take_weight_from_transact_info(
922
						destination.clone(),
923
						weight_info.transact_required_weight_at_most,
924
						false,
925
					)?;
926
					Ok(WeightLimit::from(Some(weight_info)))
927
18
				},
928
18
				|v| Ok(v),
929
18
			)?;
930

            
931
18
			let total_weight_fee_calculation = match total_weight {
932
				Unlimited => MAX_WEIGHT,
933
18
				Limited(x) => x,
934
			};
935

            
936
18
			let fee = Self::calculate_fee(
937
18
				fee_location,
938
18
				fee.fee_amount,
939
18
				destination.clone(),
940
18
				total_weight_fee_calculation,
941
18
			)?;
942

            
943
18
			ensure!(
944
18
				T::MaxHrmpFee::filter_max_asset_fee(&fee),
945
1
				Error::<T>::TooMuchFeeUsed
946
			);
947

            
948
			// The appendix instruction will be a deposit back to a self location
949
17
			let deposit_appendix =
950
17
				Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
951

            
952
17
			Self::transact_in_dest_chain_asset_non_signed(
953
17
				destination,
954
17
				None,
955
17
				fee,
956
17
				call_bytes.clone(),
957
17
				OriginKind::Native,
958
17
				total_weight,
959
17
				weight_info.transact_required_weight_at_most,
960
17
				Some(vec![RefundSurplus, deposit_appendix]),
961
17
			)?;
962

            
963
15
			Self::deposit_event(Event::HrmpManagementSent { action });
964
15

            
965
15
			Ok(())
966
		}
967
	}
968

            
969
	impl<T: Config> Pallet<T> {
970
51
		fn transact_in_dest_chain_asset_non_signed(
971
51
			dest: Location,
972
51
			fee_payer: Option<T::AccountId>,
973
51
			fee: Asset,
974
51
			call: Vec<u8>,
975
51
			origin_kind: OriginKind,
976
51
			total_weight: WeightLimit,
977
51
			transact_required_weight_at_most: Weight,
978
51
			with_appendix: Option<Vec<Instruction<()>>>,
979
51
		) -> DispatchResult {
980
51
			if let Some(fee_payer) = fee_payer {
981
				// Convert origin to multilocation
982
30
				let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
983
30

            
984
30
				// Construct the local withdraw message with the previous calculated amount
985
30
				// This message deducts and burns "amount" from the caller when executed
986
30
				T::AssetTransactor::withdraw_asset(&fee.clone().into(), &origin_as_mult, None)
987
30
					.map_err(|_| Error::<T>::UnableToWithdrawAsset)?;
988
21
			}
989

            
990
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
991
			// Transact.
992
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
993
			// used to pay fees
994
			// BuyExecution: Buys "execution power" in the destination chain
995
			// Transact: Issues the transaction
996
51
			let transact_message: Xcm<()> = Self::transact_message(
997
51
				dest.clone(),
998
51
				fee,
999
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
		}
80
		fn transact_in_dest_chain_asset_signed(
80
			dest: Location,
80
			fee_payer: T::AccountId,
80
			fee: Asset,
80
			call: Vec<u8>,
80
			origin_kind: OriginKind,
80
			total_weight: WeightLimit,
80
			transact_required_weight_at_most: Weight,
80
			with_appendix: Option<Vec<Instruction<()>>>,
80
		) -> DispatchResult {
80
			// Convert origin to multilocation
80
			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
80
			let mut transact_message: Xcm<()> = Self::transact_message(
80
				dest.clone(),
80
				fee,
80
				total_weight,
80
				call,
80
				transact_required_weight_at_most,
80
				origin_kind,
80
				with_appendix,
80
			)?;
			// We append DescendOrigin as the first instruction in the message
			// The new message looks like DescendOrigin||WithdrawAsset||BuyExecution||
			// Transact.
80
			let interior: Junctions = origin_as_mult
80
				.clone()
80
				.try_into()
80
				.map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
80
			transact_message.0.insert(0, DescendOrigin(interior));
			// Send to destination chain
60
			let (ticket, _price) =
80
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
80
					.map_err(|_| Error::<T>::ErrorValidating)?;
60
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
60
			Ok(())
80
		}
		/// Calculate the amount of fee based on the multilocation of the fee asset and
		/// the total weight to be spent
137
		fn calculate_fee(
137
			fee_location: Location,
137
			fee_amount: Option<u128>,
137
			destination: Location,
137
			total_weight: Weight,
137
		) -> 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
137
			let amount: u128 = fee_amount.map_or_else(
137
				|| {
46
					Self::take_fee_per_second_from_storage(
46
						fee_location.clone(),
46
						destination,
46
						total_weight,
46
					)
137
				},
137
				|v| Ok(v),
137
			)?;
			// Construct Asset
132
			Ok(Asset {
132
				id: AssetId(fee_location),
132
				fun: Fungible(amount),
132
			})
137
		}
		/// Construct the transact xcm message with the provided parameters
131
		fn transact_message(
131
			dest: Location,
131
			asset: Asset,
131
			dest_weight: WeightLimit,
131
			call: Vec<u8>,
131
			dispatch_weight: Weight,
131
			origin_kind: OriginKind,
131
			with_appendix: Option<Vec<Instruction<()>>>,
131
		) -> Result<Xcm<()>, DispatchError> {
131
			let mut instructions = vec![
131
				Self::withdraw_instruction(asset.clone(), &dest)?,
131
				Self::buy_execution(asset, &dest, dest_weight)?,
			];
131
			if let Some(appendix) = with_appendix {
31
				instructions.push(Self::appendix_instruction(appendix)?);
100
			}
131
			instructions.push(Transact {
131
				origin_kind,
131
				fallback_max_weight: Some(dispatch_weight),
131
				call: call.into(),
131
			});
131
			Ok(Xcm(instructions))
131
		}
		/// Construct a buy execution xcm order with the provided parameters
131
		fn buy_execution(
131
			asset: Asset,
131
			at: &Location,
131
			weight: WeightLimit,
131
		) -> Result<Instruction<()>, DispatchError> {
131
			let universal_location = T::UniversalLocation::get();
131
			let fees = asset
131
				.reanchored(at, &universal_location)
131
				.map_err(|_| Error::<T>::CannotReanchor)?;
131
			Ok(BuyExecution {
131
				fees,
131
				weight_limit: weight,
131
			})
131
		}
		/// Construct a withdraw instruction from a sovereign account
131
		fn withdraw_instruction(
131
			asset: Asset,
131
			at: &Location,
131
		) -> Result<Instruction<()>, DispatchError> {
131
			let universal_location = T::UniversalLocation::get();
131
			let fees = asset
131
				.reanchored(at, &universal_location)
131
				.map_err(|_| Error::<T>::CannotReanchor)?;
131
			Ok(WithdrawAsset(fees.into()))
131
		}
		/// 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.
43
		fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
43
			let chain_location = dest.chain_location();
43
			if *dest == chain_location {
43
				Ok(chain_location)
			} else {
				Err(Error::<T>::InvalidDest.into())
			}
43
		}
		/// 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.
43
		fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
43
			let dest = Self::ensure_valid_dest(dest)?;
43
			let self_location = T::SelfLocation::get();
43
			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
43
			let reserve =
43
				T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
			// We only allow to transact using a reserve asset as fee
43
			ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
41
			Ok(dest)
43
		}
		/// 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
43
		pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
43
			// grab WEIGHT_REF_TIME_PER_SECOND as u128
43
			let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
43

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

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