1
// Copyright 2019-2022 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
366
#[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::Vec;
108
	use xcm::{latest::prelude::*, VersionedLocation};
109
	use xcm_executor::traits::{TransactAsset, WeightBounds};
110
	use xcm_primitives::{
111
		FilterMaxAssetFee, HrmpAvailableCalls, HrmpEncodeCall, Reserve, UtilityAvailableCalls,
112
		UtilityEncodeCall, XcmTransact,
113
	};
114

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
187
		type WeightInfo: WeightInfo;
188
	}
189

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
419
	#[pallet::genesis_config]
420
	pub struct GenesisConfig<T> {
421
		pub relay_indices: RelayChainIndices,
422
		pub _phantom: PhantomData<T>,
423
	}
424

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

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

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

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

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

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

            
469
40
			Ok(())
470
		}
471

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

            
479
			// Remove index
480
1
			IndexToAccount::<T>::remove(&index);
481
1

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

            
485
1
			Ok(())
486
		}
487

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

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

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

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

            
530
30
			// Grab the destination
531
30
			let dest = dest.destination();
532

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

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

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

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

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

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

            
589
19
			Ok(())
590
		}
591

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

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

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

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

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

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

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

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

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

            
681
15
			Ok(())
682
		}
683

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

            
702
49
			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
703
49

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

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

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

            
724
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
725
1
			Ok(())
726
		}
727

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

            
750
94
			let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
751

            
752
94
			let fee_location = Self::currency_to_multilocation(fee.currency)
753
94
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
754

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

            
769
88
			let total_weight_fee_calculation = match total_weight {
770
				Unlimited => MAX_WEIGHT,
771
88
				Limited(x) => x,
772
			};
773

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

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

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

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

            
812
63
			Ok(())
813
		}
814

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

            
827
49
			DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
828
49

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

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

            
847
			DestinationAssetFeePerSecond::<T>::remove(&asset_location);
848

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

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

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

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

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

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

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

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

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

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

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

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

            
961
15
			Self::deposit_event(Event::HrmpManagementSent { action });
962
15

            
963
15
			Ok(())
964
		}
965
	}
966

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

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

            
988
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
989
			// Transact.
990
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
991
			// used to pay fees
992
			// BuyExecution: Buys "execution power" in the destination chain
993
			// Transact: Issues the transaction
994
51
			let transact_message: Xcm<()> = Self::transact_message(
995
51
				dest.clone(),
996
51
				fee,
997
51
				total_weight,
998
51
				call,
999
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
		}
85
		fn transact_in_dest_chain_asset_signed(
85
			dest: Location,
85
			fee_payer: T::AccountId,
85
			fee: Asset,
85
			call: Vec<u8>,
85
			origin_kind: OriginKind,
85
			total_weight: WeightLimit,
85
			transact_required_weight_at_most: Weight,
85
			with_appendix: Option<Vec<Instruction<()>>>,
85
		) -> DispatchResult {
85
			// Convert origin to multilocation
85
			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
85
			let mut transact_message: Xcm<()> = Self::transact_message(
85
				dest.clone(),
85
				fee,
85
				total_weight,
85
				call,
85
				transact_required_weight_at_most,
85
				origin_kind,
85
				with_appendix,
85
			)?;
			// We append DescendOrigin as the first instruction in the message
			// The new message looks like DescendOrigin||WithdrawAsset||BuyExecution||
			// Transact.
85
			let interior: Junctions = origin_as_mult
85
				.clone()
85
				.try_into()
85
				.map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
85
			transact_message.0.insert(0, DescendOrigin(interior));
			// Send to destination chain
63
			let (ticket, _price) =
85
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
85
					.map_err(|_| Error::<T>::ErrorValidating)?;
63
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
63
			Ok(())
85
		}
		/// Calculate the amount of fee based on the multilocation of the fee asset and
		/// the total weight to be spent
142
		fn calculate_fee(
142
			fee_location: Location,
142
			fee_amount: Option<u128>,
142
			destination: Location,
142
			total_weight: Weight,
142
		) -> 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
142
			let amount: u128 = fee_amount.map_or_else(
142
				|| {
47
					Self::take_fee_per_second_from_storage(
47
						fee_location.clone(),
47
						destination,
47
						total_weight,
47
					)
142
				},
142
				|v| Ok(v),
142
			)?;
			// Construct Asset
137
			Ok(Asset {
137
				id: AssetId(fee_location),
137
				fun: Fungible(amount),
137
			})
142
		}
		/// Construct the transact xcm message with the provided parameters
136
		fn transact_message(
136
			dest: Location,
136
			asset: Asset,
136
			dest_weight: WeightLimit,
136
			call: Vec<u8>,
136
			dispatch_weight: Weight,
136
			origin_kind: OriginKind,
136
			with_appendix: Option<Vec<Instruction<()>>>,
136
		) -> Result<Xcm<()>, DispatchError> {
136
			let mut instructions = vec![
136
				Self::withdraw_instruction(asset.clone(), &dest)?,
136
				Self::buy_execution(asset, &dest, dest_weight)?,
			];
136
			if let Some(appendix) = with_appendix {
31
				instructions.push(Self::appendix_instruction(appendix)?);
105
			}
136
			instructions.push(Transact {
136
				origin_kind,
136
				require_weight_at_most: dispatch_weight,
136
				call: call.into(),
136
			});
136
			Ok(Xcm(instructions))
136
		}
		/// Construct a buy execution xcm order with the provided parameters
136
		fn buy_execution(
136
			asset: Asset,
136
			at: &Location,
136
			weight: WeightLimit,
136
		) -> Result<Instruction<()>, DispatchError> {
136
			let universal_location = T::UniversalLocation::get();
136
			let fees = asset
136
				.reanchored(at, &universal_location)
136
				.map_err(|_| Error::<T>::CannotReanchor)?;
136
			Ok(BuyExecution {
136
				fees,
136
				weight_limit: weight,
136
			})
136
		}
		/// Construct a withdraw instruction from a sovereign account
136
		fn withdraw_instruction(
136
			asset: Asset,
136
			at: &Location,
136
		) -> Result<Instruction<()>, DispatchError> {
136
			let universal_location = T::UniversalLocation::get();
136
			let fees = asset
136
				.reanchored(at, &universal_location)
136
				.map_err(|_| Error::<T>::CannotReanchor)?;
136
			Ok(WithdrawAsset(fees.into()))
136
		}
		/// Construct a deposit instruction to a sovereign account
31
		fn deposit_instruction(
31
			mut beneficiary: Location,
31
			at: &Location,
31
			max_assets: u32,
31
		) -> Result<Instruction<()>, DispatchError> {
31
			let universal_location = T::UniversalLocation::get();
31
			beneficiary
31
				.reanchor(at, &universal_location)
31
				.map_err(|_| Error::<T>::CannotReanchor)?;
31
			Ok(DepositAsset {
31
				assets: Wild(AllCounted(max_assets)),
31
				beneficiary,
31
			})
31
		}
		/// Construct a withdraw instruction from a sovereign account
31
		fn appendix_instruction(
31
			instructions: Vec<Instruction<()>>,
31
		) -> Result<Instruction<()>, DispatchError> {
31
			Ok(SetAppendix(Xcm(instructions)))
31
		}
		/// Ensure `dest` has chain part and none recipient part.
44
		fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
44
			let chain_location = dest.chain_location();
44
			if *dest == chain_location {
44
				Ok(chain_location)
			} else {
				Err(Error::<T>::InvalidDest.into())
			}
44
		}
		/// Check whether the transfer is allowed.
		///
		/// Returns `Err` if `asset` is not a reserved asset of `dest`,
		/// else returns `dest`, parachain or relay chain location.
44
		fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
44
			let dest = Self::ensure_valid_dest(dest)?;
44
			let self_location = T::SelfLocation::get();
44
			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
44
			let reserve =
44
				T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
			// We only allow to transact using a reserve asset as fee
44
			ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
42
			Ok(dest)
44
		}
		/// Returns weight of `weight_of_initiate_reserve_withdraw` call.
6
		fn weight_of_initiate_reserve_withdraw() -> Weight {
6
			let dest = Location::parent();
6

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

            
6
			// Construct Asset
6
			let fee = Asset {
6
				id: AssetId(asset.clone()),
6
				fun: Fungible(0),
6
			};
6

            
6
			let xcm: Xcm<()> = Xcm(vec![
6
				WithdrawAsset(fee.into()),
6
				InitiateReserveWithdraw {
6
					assets: AssetFilter::Wild(All),
6
					reserve: dest.clone(),
6
					xcm: Xcm(vec![]),
6
				},
6
			]);
6
			T::Weigher::weight(&mut xcm.into()).map_or(Weight::max_value(), |w| {
6
				T::BaseXcmWeight::get().saturating_add(w)
6
			})
6
		}
		/// Returns the fee for a given set of parameters
		/// We always round up in case of fractional division
44
		pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
44
			// grab WEIGHT_REF_TIME_PER_SECOND as u128
44
			let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
44

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

            
44
			fee_mul_rounded_up / weight_per_second_u128
44
		}
		/// Returns the weight information for a destination from storage
		/// it returns the weight to be used in non-signed cases
25
		pub fn take_weight_from_transact_info(
25
			dest: Location,
25
			dest_weight: Weight,
25
			refund: bool,
25
		) -> Result<Weight, DispatchError> {
25
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
25
			// so we have to ensure 'refund' is false
25
			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
			// Grab transact info for the destination provided
24
			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
24
				.ok_or(Error::<T>::TransactorInfoNotSet)?;
23
			let total_weight = dest_weight
23
				.checked_add(&transactor_info.transact_extra_weight)
23
				.ok_or(Error::<T>::WeightOverflow)?;
23
			ensure!(
23
				total_weight.all_lte(transactor_info.max_weight),
7
				Error::<T>::MaxWeightTransactReached
			);
16
			Ok(total_weight)
25
		}
		/// Returns the weight information for a destination from storage
		/// it returns the weight to be used in signed cases
35
		pub fn take_weight_from_transact_info_signed(
35
			dest: Location,
35
			dest_weight: Weight,
35
			refund: bool,
35
		) -> Result<Weight, DispatchError> {
35
			// this is due to TransactInfo only has info of cases where RefundSurplus is not used
35
			// so we have to ensure 'refund' is false
35
			ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
			// Grab transact info for the destination provided
35
			let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
35
				.ok_or(Error::<T>::TransactorInfoNotSet)?;
			// If this storage item is not set, it means that the destination chain
			// does not support this kind of transact message
33
			let transact_in_dest_as_signed_weight = transactor_info
33
				.transact_extra_weight_signed
33
				.ok_or(Error::<T>::SignedTransactNotAllowedForDestination)?;
31
			let total_weight = dest_weight
31
				.checked_add(&transact_in_dest_as_signed_weight)
31
				.ok_or(Error::<T>::WeightOverflow)?;
30
			ensure!(
30
				total_weight.all_lte(transactor_info.max_weight),
1
				Error::<T>::MaxWeightTransactReached
			);
29
			Ok(total_weight)
35
		}
		/// Returns the fee per second charged by a reserve chain for an asset
		/// it takes this information from storage
47
		pub fn take_fee_per_second_from_storage(
47
			fee_location: Location,
47
			destination: Location,
47
			total_weight: Weight,
47
		) -> Result<u128, DispatchError> {
47
			let fee_per_second = DestinationAssetFeePerSecond::<T>::get(&fee_location)
47
				.ok_or(Error::<T>::FeePerSecondNotSet)?;
			// Ensure the asset is a reserve
			// We only store information about asset fee per second on its reserve chain
			// if amount is provided, we first check whether we have this information
44
			Self::transfer_allowed(&(fee_location, fee_per_second).into(), &destination)?;
42
			Ok(Self::calculate_fee_per_second(total_weight, fee_per_second))
47
		}
		/// Converts Currency to multilocation
158
		pub fn currency_to_multilocation(currency: Currency<CurrencyIdOf<T>>) -> Option<Location> {
158
			match currency {
20
				Currency::AsCurrencyId(id) => T::CurrencyIdToLocation::convert(id),
138
				Currency::AsMultiLocation(multiloc) => Location::try_from(*multiloc).ok(),
			}
158
		}
	}
}