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;
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
77
	#[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
144
		MaxEncodedLen,
199
		RuntimeDebug,
200
		Eq,
201
		PartialEq,
202
216
		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
44
	#[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
216
	#[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
360
	#[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
44
		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
144
	#[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
122
	#[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
123
	#[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
102
	#[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
1890
	#[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
70
	#[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
	#[pallet::event]
363
252
	#[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
		pub _phantom: PhantomData<T>,
424
	}
425

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

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

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

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

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

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

            
470
40
			Ok(())
471
		}
472

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

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

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

            
486
1
			Ok(())
487
		}
488

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

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

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

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

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

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

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

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

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

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

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

            
590
19
			Ok(())
591
		}
592

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

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

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

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

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

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

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

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

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

            
682
15
			Ok(())
683
		}
684

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
813
63
			Ok(())
814
		}
815

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
989
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
990
			// Transact.
991
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
992
			// used to pay fees
993
			// BuyExecution: Buys "execution power" in the destination chain
994
			// Transact: Issues the transaction
995
51
			let transact_message: Xcm<()> = Self::transact_message(
996
51
				dest.clone(),
997
51
				fee,
998
51
				total_weight,
999
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
		}
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
		}
	}
}