1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! # Xcm Transactor Module
18
//!
19
//! ## Overview
20
//!
21
//! Module to provide transact capabilities on other chains
22
//!
23
//! In this pallet we will make distinctions between sovereign, derivative accounts and
24
//! multilocation-based derived accounts. The first is the account the parachain controls
25
//! in the destination chain, the second is an account derived from the
26
//! sovereign account itself, e.g., by hashing it with an index, while the third is an account
27
//! derived from the multilocation of a use in this chain (typically, hashing the ML).
28
//! Such distinction is important since we want to keep the integrity of the sovereign account
29
//!
30
//! This pallet provides three ways of sending Transact operations to another chain
31
//!
32
//! - transact_through_derivative: Transact through an address derived from this chains sovereign
33
//! 	account in the destination chain. For the transaction to successfully be dispatched in the
34
//! 	destination chain, pallet-utility needs to be installed and at least paid xcm message
35
//! 	execution should be allowed (and WithdrawAsset,BuyExecution and Transact messages allowed)
36
//! 	in the destination chain
37
//!
38
//!
39
//!
40
//! 	The transactions are dispatched from a derivative account
41
//! 	of the sovereign account
42
//! 	This pallet only stores the index of the derivative account used, but
43
//! 	not the derivative account itself. The only assumption this pallet makes
44
//! 	is the existence of the pallet_utility pallet in the destination chain
45
//! 	through the XcmTransact trait.
46
//!
47
//! 	All calls will be wrapped around utility::as_derivative. This makes sure
48
//! 	the inner call is executed from the derivative account and not the sovereign
49
//! 	account itself.
50
//!
51
//! 	Index registration happens through DerivativeAddressRegistrationOrigin.
52
//! 	This derivative account can be funded by external users to
53
//! 	ensure it has enough funds to make the calls
54
//!
55
//! - transact_through_sovereign: Transact through the sovereign account representing this chain.
56
//! 	For the transaction to successfully be dispatched in the destination chain, at least paid
57
//! 	xcm message execution should be allowed (and WithdrawAsset,BuyExecution and Transact
58
//! 	messages allowed) in the destination chain. Only callable by Root
59
//!
60
//! - transact_through_signed: Transact through an account derived from the multilocation
61
//! 	representing the signed user making the call. We ensure this by prepending DescendOrigin as
62
//! 	the first instruction of the XCM message. For the transaction to successfully be dispatched
63
//! 	in the destination chain, at least descended paid xcm message execution should be allowed
64
//! 	(and DescendOrigin + WithdrawAsset + BuyExecution + Transact messages allowed) in the
65
//! 	destination chain. Additionally, a ML-based derivation mechanism needs to be implemented
66
//! 	in the destination chain.
67

            
68
#![cfg_attr(not(feature = "std"), no_std)]
69

            
70
use frame_support::pallet;
71

            
72
pub use pallet::*;
73

            
74
#[cfg(any(test, feature = "runtime-benchmarks"))]
75
mod benchmarks;
76

            
77
#[cfg(test)]
78
pub(crate) mod mock;
79
#[cfg(test)]
80
mod tests;
81

            
82
pub mod encode;
83
pub mod migrations;
84
pub mod relay_indices;
85
pub mod weights;
86
pub use crate::weights::WeightInfo;
87

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

            
90
1700
#[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
142
	#[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
		MaxEncodedLen,
199
		RuntimeDebug,
200
		Eq,
201
		PartialEq,
202
207
		scale_info::TypeInfo,
203
		DecodeWithMemTracking,
204
	)]
205
	pub struct RemoteTransactInfoWithMaxWeight {
206
		/// Extra weight that transacting a call in a destination chain adds
207
		/// Extra weight involved when transacting without DescendOrigin
208
		/// This should always be possible in a destination chain, since
209
		/// it involves going through the sovereign account
210
		pub transact_extra_weight: Weight,
211
		/// Max destination weight
212
		pub max_weight: Weight,
213
		/// Whether we allow transacting through signed origins in another chain, and
214
		/// how much extra cost implies
215
		/// Extra weight involved when transacting with DescendOrigin
216
		/// The reason for it being an option is because the destination chain
217
		/// might not support constructing origins based on generic MultiLocations
218
		pub transact_extra_weight_signed: Option<Weight>,
219
	}
220

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

            
239
	impl<T> Default for Currency<T> {
240
		fn default() -> Currency<T> {
241
			Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
242
		}
243
	}
244

            
245
	#[derive(
246
		Clone,
247
		Encode,
248
		Decode,
249
		Eq,
250
		PartialEq,
251
		RuntimeDebug,
252
207
		scale_info::TypeInfo,
253
		DecodeWithMemTracking,
254
	)]
255
	pub struct HrmpInitParams {
256
		pub para_id: ParaId,
257
		pub proposed_max_capacity: u32,
258
		pub proposed_max_message_size: u32,
259
	}
260

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

            
284
	#[derive(
285
		Default,
286
		Clone,
287
		Encode,
288
		Decode,
289
		Eq,
290
		PartialEq,
291
		RuntimeDebug,
292
		MaxEncodedLen,
293
40
		scale_info::TypeInfo,
294
		DecodeWithMemTracking,
295
	)]
296

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

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

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

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

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

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

            
363
	/// Stores the indices of relay chain pallets
364
1894
	#[pallet::storage]
365
	#[pallet::getter(fn relay_indices)]
366
	pub type RelayIndices<T: Config> = StorageValue<_, RelayChainIndices, ValueQuery>;
367

            
368
	/// An error that can occur while executing the mapping pallet's logic.
369
20
	#[pallet::error]
370
	pub enum Error<T> {
371
		IndexAlreadyClaimed,
372
		UnclaimedIndex,
373
		NotOwner,
374
		UnweighableMessage,
375
		CannotReanchor,
376
		AssetHasNoReserve,
377
		InvalidDest,
378
		NotCrossChainTransfer,
379
		AssetIsNotReserveInDestination,
380
		DestinationNotInvertible,
381
		ErrorDelivering,
382
		DispatchWeightBiggerThanTotalWeight,
383
		WeightOverflow,
384
		AmountOverflow,
385
		TransactorInfoNotSet,
386
		NotCrossChainTransferableCurrency,
387
		XcmExecuteError,
388
		BadVersion,
389
		MaxWeightTransactReached,
390
		UnableToWithdrawAsset,
391
		FeePerSecondNotSet,
392
		SignedTransactNotAllowedForDestination,
393
		FailedMultiLocationToJunction,
394
		HrmpHandlerNotImplemented,
395
		TooMuchFeeUsed,
396
		ErrorValidating,
397
		RefundNotSupportedWithTransactInfo,
398
	}
399

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

            
458
	#[pallet::genesis_config]
459
	pub struct GenesisConfig<T> {
460
		pub relay_indices: RelayChainIndices,
461
		#[serde(skip)]
462
		pub _phantom: PhantomData<T>,
463
	}
464

            
465
	impl<T> Default for GenesisConfig<T> {
466
737
		fn default() -> Self {
467
737
			Self {
468
737
				relay_indices: RelayChainIndices::default(),
469
737
				_phantom: Default::default(),
470
737
			}
471
737
		}
472
	}
473

            
474
726
	#[pallet::genesis_build]
475
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
476
735
		fn build(&self) {
477
735
			<RelayIndices<T>>::put(self.relay_indices);
478
735
		}
479
	}
480

            
481
20
	#[pallet::call]
482
	impl<T: Config> Pallet<T> {
483
		/// Register a derivative index for an account id. Dispatchable by
484
		/// DerivativeAddressRegistrationOrigin
485
		///
486
		/// We do not store the derivative address, but only the index. We do not need to store
487
		/// the derivative address to issue calls, only the index is enough
488
		///
489
		/// For now an index is registered for all possible destinations and not per-destination.
490
		/// We can change this in the future although it would just make things more complicated
491
		#[pallet::call_index(0)]
492
		#[pallet::weight(T::WeightInfo::register())]
493
41
		pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
494
41
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
495

            
496
40
			ensure!(
497
40
				IndexToAccount::<T>::get(&index).is_none(),
498
				Error::<T>::IndexAlreadyClaimed
499
			);
500

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

            
503
40
			// Deposit event
504
40
			Self::deposit_event(Event::<T>::RegisteredDerivative {
505
40
				account_id: who,
506
40
				index: index,
507
40
			});
508
40

            
509
40
			Ok(())
510
		}
511

            
512
		/// De-Register a derivative index. This prevents an account to use a derivative address
513
		/// (represented by an index) from our of our sovereign accounts anymore
514
		#[pallet::call_index(1)]
515
		#[pallet::weight(T::WeightInfo::deregister())]
516
1
		pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
517
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
518

            
519
			// Remove index
520
1
			IndexToAccount::<T>::remove(&index);
521
1

            
522
1
			// Deposit event
523
1
			Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
524
1

            
525
1
			Ok(())
526
		}
527

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

            
556
31
			let fee_location = Self::currency_to_multilocation(fee.currency)
557
31
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
558

            
559
			// The index exists
560
31
			let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
561
			// The derivative index is owned by the origin
562
30
			ensure!(account == who, Error::<T>::NotOwner);
563

            
564
			// Encode call bytes
565
			// We make sure the inner call is wrapped on a as_derivative dispatchable
566
30
			let call_bytes: Vec<u8> = dest
567
30
				.clone()
568
30
				.encode_call(UtilityAvailableCalls::AsDerivative(index, inner_call));
569
30

            
570
30
			// Grab the destination
571
30
			let dest = dest.destination();
572

            
573
			// Calculate the total weight that the xcm message is going to spend in the
574
			// destination chain
575
30
			let total_weight = weight_info.overall_weight.map_or_else(
576
30
				|| -> Result<_, DispatchError> {
577
18
					let weight_info = Self::take_weight_from_transact_info(
578
18
						dest.clone(),
579
18
						weight_info.transact_required_weight_at_most,
580
18
						refund,
581
18
					)?;
582
9
					Ok(WeightLimit::from(Some(weight_info)))
583
30
				},
584
30
				|v| Ok(v),
585
30
			)?;
586

            
587
21
			let total_weight_fee_calculation = match total_weight {
588
				Unlimited => MAX_WEIGHT,
589
21
				Limited(x) => x,
590
			};
591

            
592
			// Calculate fee based on FeePerSecond
593
21
			let fee = Self::calculate_fee(
594
21
				fee_location,
595
21
				fee.fee_amount,
596
21
				dest.clone(),
597
21
				total_weight_fee_calculation,
598
21
			)?;
599

            
600
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
601
19
			let appendix = refund
602
19
				.then(|| -> Result<_, DispatchError> {
603
4
					Ok(vec![
604
4
						RefundSurplus,
605
4
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
606
					])
607
19
				})
608
19
				.transpose()?;
609

            
610
19
			Self::transact_in_dest_chain_asset_non_signed(
611
19
				dest.clone(),
612
19
				Some(who.clone()),
613
19
				fee,
614
19
				call_bytes.clone(),
615
19
				OriginKind::SovereignAccount,
616
19
				total_weight,
617
19
				weight_info.transact_required_weight_at_most,
618
19
				appendix,
619
19
			)?;
620

            
621
			// Deposit event
622
19
			Self::deposit_event(Event::<T>::TransactedDerivative {
623
19
				account_id: who,
624
19
				dest,
625
19
				call: call_bytes,
626
19
				index,
627
19
			});
628
19

            
629
19
			Ok(())
630
		}
631

            
632
		/// Transact the call through the sovereign account in a destination chain,
633
		/// 'fee_payer' pays for the fee
634
		///
635
		/// SovereignAccountDispatcherOrigin callable only
636
		#[pallet::call_index(3)]
637
		#[pallet::weight(
638
			Pallet::<T>::weight_of_initiate_reserve_withdraw()
639
			.saturating_add(T::WeightInfo::transact_through_sovereign())
640
		)]
641
		pub fn transact_through_sovereign(
642
			origin: OriginFor<T>,
643
			// destination to which the message should be sent
644
			dest: Box<VersionedLocation>,
645
			// account paying for fees
646
			fee_payer: Option<T::AccountId>,
647
			// fee to be used
648
			fee: CurrencyPayment<CurrencyIdOf<T>>,
649
			// call to be executed in destination
650
			call: Vec<u8>,
651
			// origin kind to be used
652
			origin_kind: OriginKind,
653
			// weight information to be used
654
			weight_info: TransactWeights,
655
			// add RefundSurplus and DepositAsset appendix
656
			refund: bool,
657
16
		) -> DispatchResult {
658
16
			T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
659

            
660
15
			let fee_location = Self::currency_to_multilocation(fee.currency)
661
15
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
662

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

            
665
			// Calculate the total weight that the xcm message is going to spend in the
666
			// destination chain
667
15
			let total_weight = weight_info.overall_weight.map_or_else(
668
15
				|| -> Result<_, DispatchError> {
669
7
					let weight_info = Self::take_weight_from_transact_info(
670
7
						dest.clone(),
671
7
						weight_info.transact_required_weight_at_most,
672
7
						refund,
673
7
					)?;
674
7
					Ok(WeightLimit::from(Some(weight_info)))
675
15
				},
676
15
				|v| Ok(v),
677
15
			)?;
678

            
679
15
			let total_weight_fee_calculation = match total_weight {
680
				Unlimited => MAX_WEIGHT,
681
15
				Limited(x) => x,
682
			};
683

            
684
			// Calculate fee based on FeePerSecond and total_weight
685
15
			let fee = Self::calculate_fee(
686
15
				fee_location,
687
15
				fee.fee_amount,
688
15
				dest.clone(),
689
15
				total_weight_fee_calculation,
690
15
			)?;
691

            
692
			// If refund is true, the appendix instruction will be a deposit back to the sovereign
693
15
			let appendix = refund
694
15
				.then(|| -> Result<_, DispatchError> {
695
3
					Ok(vec![
696
3
						RefundSurplus,
697
3
						Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
698
					])
699
15
				})
700
15
				.transpose()?;
701

            
702
			// Grab the destination
703
15
			Self::transact_in_dest_chain_asset_non_signed(
704
15
				dest.clone(),
705
15
				fee_payer.clone(),
706
15
				fee,
707
15
				call.clone(),
708
15
				origin_kind,
709
15
				total_weight,
710
15
				weight_info.transact_required_weight_at_most,
711
15
				appendix,
712
15
			)?;
713

            
714
			// Deposit event
715
15
			Self::deposit_event(Event::<T>::TransactedSovereign {
716
15
				fee_payer,
717
15
				dest,
718
15
				call,
719
15
			});
720
15

            
721
15
			Ok(())
722
		}
723

            
724
		/// Change the transact info of a location
725
		#[pallet::call_index(4)]
726
		#[pallet::weight(T::WeightInfo::set_transact_info())]
727
		pub fn set_transact_info(
728
			origin: OriginFor<T>,
729
			location: Box<VersionedLocation>,
730
			transact_extra_weight: Weight,
731
			max_weight: Weight,
732
			transact_extra_weight_signed: Option<Weight>,
733
49
		) -> DispatchResult {
734
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
735
49
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
736
49
			let remote_info = RemoteTransactInfoWithMaxWeight {
737
49
				transact_extra_weight,
738
49
				max_weight,
739
49
				transact_extra_weight_signed,
740
49
			};
741
49

            
742
49
			TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
743
49

            
744
49
			Self::deposit_event(Event::TransactInfoChanged {
745
49
				location,
746
49
				remote_info,
747
49
			});
748
49
			Ok(())
749
		}
750

            
751
		/// Remove the transact info of a location
752
		#[pallet::call_index(5)]
753
		#[pallet::weight(T::WeightInfo::remove_transact_info())]
754
		pub fn remove_transact_info(
755
			origin: OriginFor<T>,
756
			location: Box<VersionedLocation>,
757
1
		) -> DispatchResult {
758
1
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
759
1
			let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
760

            
761
			// Remove transact info
762
1
			TransactInfoWithWeightLimit::<T>::remove(&location);
763
1

            
764
1
			Self::deposit_event(Event::TransactInfoRemoved { location });
765
1
			Ok(())
766
		}
767

            
768
		/// Transact the call through the a signed origin in this chain
769
		/// that should be converted to a transaction dispatch account in the destination chain
770
		/// by any method implemented in the destination chains runtime
771
		///
772
		/// This time we are giving the currency as a currencyId instead of multilocation
773
		#[pallet::call_index(6)]
774
		#[pallet::weight(T::WeightInfo::transact_through_signed())]
775
		pub fn transact_through_signed(
776
			origin: OriginFor<T>,
777
			// destination to which the message should be sent
778
			dest: Box<VersionedLocation>,
779
			// fee to be used
780
			fee: CurrencyPayment<CurrencyIdOf<T>>,
781
			// call to be executed in destination
782
			call: Vec<u8>,
783
			// weight information to be used
784
			weight_info: TransactWeights,
785
			// add RefundSurplus and DepositAsset appendix
786
			refund: bool,
787
89
		) -> DispatchResult {
788
89
			let who = ensure_signed(origin)?;
789

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

            
792
89
			let fee_location = Self::currency_to_multilocation(fee.currency)
793
89
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
794

            
795
			// Calculate the total weight that the xcm message is going to spend in the
796
			// destination chain
797
89
			let total_weight = weight_info.overall_weight.map_or_else(
798
89
				|| -> Result<_, DispatchError> {
799
34
					let weight_info = Self::take_weight_from_transact_info_signed(
800
34
						dest.clone(),
801
34
						weight_info.transact_required_weight_at_most,
802
34
						refund,
803
34
					)?;
804
28
					Ok(WeightLimit::from(Some(weight_info)))
805
89
				},
806
89
				|v| Ok(v),
807
89
			)?;
808

            
809
83
			let total_weight_fee_calculation = match total_weight {
810
				Unlimited => MAX_WEIGHT,
811
83
				Limited(x) => x,
812
			};
813

            
814
			// Fee to be paid
815
83
			let fee = Self::calculate_fee(
816
83
				fee_location,
817
83
				fee.fee_amount,
818
83
				dest.clone(),
819
83
				total_weight_fee_calculation,
820
83
			)?;
821

            
822
			// If refund is true, the appendix instruction will be a deposit back to the sender
823
80
			let appendix = refund
824
80
				.then(|| -> Result<_, DispatchError> {
825
7
					let sender = T::AccountIdToLocation::convert(who.clone());
826
7
					Ok(vec![
827
7
						RefundSurplus,
828
7
						Self::deposit_instruction(sender, &dest, 1u32)?,
829
					])
830
80
				})
831
80
				.transpose()?;
832

            
833
			// Grab the destination
834
80
			Self::transact_in_dest_chain_asset_signed(
835
80
				dest.clone(),
836
80
				who.clone(),
837
80
				fee,
838
80
				call.clone(),
839
80
				OriginKind::SovereignAccount,
840
80
				total_weight,
841
80
				weight_info.transact_required_weight_at_most,
842
80
				appendix,
843
80
			)?;
844

            
845
			// Deposit event
846
60
			Self::deposit_event(Event::<T>::TransactedSigned {
847
60
				fee_payer: who,
848
60
				dest,
849
60
				call,
850
60
			});
851
60

            
852
60
			Ok(())
853
		}
854

            
855
		/// Set the fee per second of an asset on its reserve chain
856
		#[pallet::call_index(7)]
857
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
858
		pub fn set_fee_per_second(
859
			origin: OriginFor<T>,
860
			asset_location: Box<VersionedLocation>,
861
			fee_per_second: u128,
862
49
		) -> DispatchResult {
863
49
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
864
49
			let asset_location =
865
49
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
866

            
867
49
			DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
868
49

            
869
49
			Self::deposit_event(Event::DestFeePerSecondChanged {
870
49
				location: asset_location,
871
49
				fee_per_second,
872
49
			});
873
49
			Ok(())
874
		}
875

            
876
		/// Remove the fee per second of an asset on its reserve chain
877
		#[pallet::call_index(8)]
878
		#[pallet::weight(T::WeightInfo::set_fee_per_second())]
879
		pub fn remove_fee_per_second(
880
			origin: OriginFor<T>,
881
			asset_location: Box<VersionedLocation>,
882
		) -> DispatchResult {
883
			T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
884
			let asset_location =
885
				Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
886

            
887
			DestinationAssetFeePerSecond::<T>::remove(&asset_location);
888

            
889
			Self::deposit_event(Event::DestFeePerSecondRemoved {
890
				location: asset_location,
891
			});
892
			Ok(())
893
		}
894

            
895
		/// Manage HRMP operations
896
		#[pallet::call_index(9)]
897
		#[pallet::weight(T::WeightInfo::hrmp_manage())]
898
		pub fn hrmp_manage(
899
			origin: OriginFor<T>,
900
			action: HrmpOperation,
901
			// fee to be used
902
			fee: CurrencyPayment<CurrencyIdOf<T>>,
903
			// weight information to be used
904
			weight_info: TransactWeights,
905
18
		) -> DispatchResult {
906
18
			// WithdrawAsset
907
18
			// BuyExecution
908
18
			// SetAppendix(RefundSurplus, DepositAsset(sov account))
909
18
			// Transact
910
18

            
911
18
			// check permissions
912
18
			match &action {
913
				HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
914
13
					<EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
915
13
						origin,
916
13
					)?;
917
				}
918
				HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
919
5
					T::HrmpManipulatorOrigin::ensure_origin(origin)?;
920
				}
921
			}
922

            
923
			// process action
924
18
			let call_bytes = match action.clone() {
925
8
				HrmpOperation::InitOpen(params) => {
926
8
					Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
927
8
						params.para_id,
928
8
						params.proposed_max_capacity,
929
8
						params.proposed_max_message_size,
930
8
					))
931
				}
932
5
				HrmpOperation::Accept { para_id } => {
933
5
					Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
934
				}
935
4
				HrmpOperation::Close(close_params) => {
936
4
					Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
937
				}
938
				HrmpOperation::Cancel {
939
1
					channel_id,
940
1
					open_requests,
941
1
				} => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
942
1
					channel_id,
943
1
					open_requests,
944
1
				)),
945
			}
946
18
			.map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
947

            
948
18
			let fee_location = Self::currency_to_multilocation(fee.currency)
949
18
				.ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
950

            
951
			// Grab the destination
952
			// For hrmp, it is always parent
953
18
			let destination = Location::parent();
954

            
955
			// Calculate the total weight that the xcm message is going to spend in the
956
			// destination chain
957
18
			let total_weight = weight_info.overall_weight.map_or_else(
958
18
				|| -> Result<_, DispatchError> {
959
					let weight_info = Self::take_weight_from_transact_info(
960
						destination.clone(),
961
						weight_info.transact_required_weight_at_most,
962
						false,
963
					)?;
964
					Ok(WeightLimit::from(Some(weight_info)))
965
18
				},
966
18
				|v| Ok(v),
967
18
			)?;
968

            
969
18
			let total_weight_fee_calculation = match total_weight {
970
				Unlimited => MAX_WEIGHT,
971
18
				Limited(x) => x,
972
			};
973

            
974
18
			let fee = Self::calculate_fee(
975
18
				fee_location,
976
18
				fee.fee_amount,
977
18
				destination.clone(),
978
18
				total_weight_fee_calculation,
979
18
			)?;
980

            
981
18
			ensure!(
982
18
				T::MaxHrmpFee::filter_max_asset_fee(&fee),
983
1
				Error::<T>::TooMuchFeeUsed
984
			);
985

            
986
			// The appendix instruction will be a deposit back to a self location
987
17
			let deposit_appendix =
988
17
				Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
989

            
990
17
			Self::transact_in_dest_chain_asset_non_signed(
991
17
				destination,
992
17
				None,
993
17
				fee,
994
17
				call_bytes.clone(),
995
17
				OriginKind::Native,
996
17
				total_weight,
997
17
				weight_info.transact_required_weight_at_most,
998
17
				Some(vec![RefundSurplus, deposit_appendix]),
999
17
			)?;
15
			Self::deposit_event(Event::HrmpManagementSent { action });
15

            
15
			Ok(())
		}
	}
	impl<T: Config> Pallet<T> {
51
		fn transact_in_dest_chain_asset_non_signed(
51
			dest: Location,
51
			fee_payer: Option<T::AccountId>,
51
			fee: Asset,
51
			call: Vec<u8>,
51
			origin_kind: OriginKind,
51
			total_weight: WeightLimit,
51
			transact_required_weight_at_most: Weight,
51
			with_appendix: Option<Vec<Instruction<()>>>,
51
		) -> DispatchResult {
51
			if let Some(fee_payer) = fee_payer {
				// Convert origin to multilocation
30
				let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
30

            
30
				// Construct the local withdraw message with the previous calculated amount
30
				// This message deducts and burns "amount" from the caller when executed
30
				T::AssetTransactor::withdraw_asset(&fee.clone().into(), &origin_as_mult, None)
30
					.map_err(|_| Error::<T>::UnableToWithdrawAsset)?;
21
			}
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
			// Transact.
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
			// used to pay fees
			// BuyExecution: Buys "execution power" in the destination chain
			// Transact: Issues the transaction
51
			let transact_message: Xcm<()> = Self::transact_message(
51
				dest.clone(),
51
				fee,
51
				total_weight,
51
				call,
51
				transact_required_weight_at_most,
51
				origin_kind,
51
				with_appendix,
51
			)?;
			// Send to sovereign
49
			let (ticket, _price) =
51
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
51
					.map_err(|_| Error::<T>::ErrorValidating)?;
49
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
49
			Ok(())
51
		}
80
		fn transact_in_dest_chain_asset_signed(
80
			dest: Location,
80
			fee_payer: T::AccountId,
80
			fee: Asset,
80
			call: Vec<u8>,
80
			origin_kind: OriginKind,
80
			total_weight: WeightLimit,
80
			transact_required_weight_at_most: Weight,
80
			with_appendix: Option<Vec<Instruction<()>>>,
80
		) -> DispatchResult {
80
			// Convert origin to multilocation
80
			let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
			// Construct the transact message. This is composed of WithdrawAsset||BuyExecution||
			// Transact.
			// WithdrawAsset: Withdraws "amount" from the sovereign account. These tokens will be
			// used to pay fees
			// BuyExecution: Buys "execution power" in the destination chain
			// Transact: Issues the transaction
80
			let mut transact_message: Xcm<()> = Self::transact_message(
80
				dest.clone(),
80
				fee,
80
				total_weight,
80
				call,
80
				transact_required_weight_at_most,
80
				origin_kind,
80
				with_appendix,
80
			)?;
			// We append DescendOrigin as the first instruction in the message
			// The new message looks like DescendOrigin||WithdrawAsset||BuyExecution||
			// Transact.
80
			let interior: Junctions = origin_as_mult
80
				.clone()
80
				.try_into()
80
				.map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
80
			transact_message.0.insert(0, DescendOrigin(interior));
			// Send to destination chain
60
			let (ticket, _price) =
80
				T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
80
					.map_err(|_| Error::<T>::ErrorValidating)?;
60
			T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
60
			Ok(())
80
		}
		/// Calculate the amount of fee based on the multilocation of the fee asset and
		/// the total weight to be spent
137
		fn calculate_fee(
137
			fee_location: Location,
137
			fee_amount: Option<u128>,
137
			destination: Location,
137
			total_weight: Weight,
137
		) -> Result<Asset, DispatchError> {
			// If amount is provided, just use it
			// Else, multiply weight*destination_units_per_second to see how much we should charge for
			// this weight execution
137
			let amount: u128 = fee_amount.map_or_else(
137
				|| {
46
					Self::take_fee_per_second_from_storage(
46
						fee_location.clone(),
46
						destination,
46
						total_weight,
46
					)
137
				},
137
				|v| Ok(v),
137
			)?;
			// Construct Asset
132
			Ok(Asset {
132
				id: AssetId(fee_location),
132
				fun: Fungible(amount),
132
			})
137
		}
		/// Construct the transact xcm message with the provided parameters
131
		fn transact_message(
131
			dest: Location,
131
			asset: Asset,
131
			dest_weight: WeightLimit,
131
			call: Vec<u8>,
131
			dispatch_weight: Weight,
131
			origin_kind: OriginKind,
131
			with_appendix: Option<Vec<Instruction<()>>>,
131
		) -> Result<Xcm<()>, DispatchError> {
131
			let mut instructions = vec![
131
				Self::withdraw_instruction(asset.clone(), &dest)?,
131
				Self::buy_execution(asset, &dest, dest_weight)?,
			];
131
			if let Some(appendix) = with_appendix {
31
				instructions.push(Self::appendix_instruction(appendix)?);
100
			}
131
			instructions.push(Transact {
131
				origin_kind,
131
				fallback_max_weight: Some(dispatch_weight),
131
				call: call.into(),
131
			});
131
			Ok(Xcm(instructions))
131
		}
		/// Construct a buy execution xcm order with the provided parameters
131
		fn buy_execution(
131
			asset: Asset,
131
			at: &Location,
131
			weight: WeightLimit,
131
		) -> Result<Instruction<()>, DispatchError> {
131
			let universal_location = T::UniversalLocation::get();
131
			let fees = asset
131
				.reanchored(at, &universal_location)
131
				.map_err(|_| Error::<T>::CannotReanchor)?;
131
			Ok(BuyExecution {
131
				fees,
131
				weight_limit: weight,
131
			})
131
		}
		/// Construct a withdraw instruction from a sovereign account
131
		fn withdraw_instruction(
131
			asset: Asset,
131
			at: &Location,
131
		) -> Result<Instruction<()>, DispatchError> {
131
			let universal_location = T::UniversalLocation::get();
131
			let fees = asset
131
				.reanchored(at, &universal_location)
131
				.map_err(|_| Error::<T>::CannotReanchor)?;
131
			Ok(WithdrawAsset(fees.into()))
131
		}
		/// Construct a deposit instruction to a sovereign account
31
		fn deposit_instruction(
31
			mut beneficiary: Location,
31
			at: &Location,
31
			max_assets: u32,
31
		) -> Result<Instruction<()>, DispatchError> {
31
			let universal_location = T::UniversalLocation::get();
31
			beneficiary
31
				.reanchor(at, &universal_location)
31
				.map_err(|_| Error::<T>::CannotReanchor)?;
31
			Ok(DepositAsset {
31
				assets: Wild(AllCounted(max_assets)),
31
				beneficiary,
31
			})
31
		}
		/// Construct a withdraw instruction from a sovereign account
31
		fn appendix_instruction(
31
			instructions: Vec<Instruction<()>>,
31
		) -> Result<Instruction<()>, DispatchError> {
31
			Ok(SetAppendix(Xcm(instructions)))
31
		}
		/// Ensure `dest` has chain part and none recipient part.
43
		fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
43
			let chain_location = dest.chain_location();
43
			if *dest == chain_location {
43
				Ok(chain_location)
			} else {
				Err(Error::<T>::InvalidDest.into())
			}
43
		}
		/// Check whether the transfer is allowed.
		///
		/// Returns `Err` if `asset` is not a reserved asset of `dest`,
		/// else returns `dest`, parachain or relay chain location.
43
		fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
43
			let dest = Self::ensure_valid_dest(dest)?;
43
			let self_location = T::SelfLocation::get();
43
			ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
43
			let reserve =
43
				T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
			// We only allow to transact using a reserve asset as fee
43
			ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
41
			Ok(dest)
43
		}
		/// Returns weight of `weight_of_initiate_reserve_withdraw` call.
6
		fn weight_of_initiate_reserve_withdraw() -> Weight {
6
			let dest = Location::parent();
6

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

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

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

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

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