1#![cfg_attr(not(feature = "std"), no_std)]
69
70use frame_support::pallet;
71use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight};
72use sp_runtime::DispatchError;
73use xcm::latest::prelude::Location;
74use xcm_primitives::XcmFeeTrader;
75
76pub use pallet::*;
77
78#[cfg(any(test, feature = "runtime-benchmarks"))]
79mod benchmarks;
80
81#[cfg(test)]
82pub(crate) mod mock;
83#[cfg(test)]
84mod tests;
85
86pub mod encode;
87pub mod migrations;
88pub mod relay_indices;
89pub mod weights;
90pub use crate::weights::WeightInfo;
91
92type CurrencyIdOf<T> = <T as Config>::CurrencyId;
93
94pub const ASSET_HUB_UTILITY_PALLET_INDEX: u8 = 40;
99
100pub const ASSET_HUB_STAKING_PALLET_INDEX: u8 = 89;
105
106#[pallet]
107pub mod pallet {
108
109 use super::*;
110 use crate::relay_indices::RelayChainIndices;
111 use crate::weights::WeightInfo;
112 use crate::CurrencyIdOf;
113 use cumulus_primitives_core::{relay_chain::HrmpChannelId, ParaId};
114 use frame_support::traits::EitherOfDiverse;
115 use frame_support::{dispatch::DispatchResult, pallet_prelude::*};
116 use frame_system::{ensure_signed, pallet_prelude::*};
117 use sp_runtime::traits::{AtLeast32BitUnsigned, Convert};
118 use sp_std::boxed::Box;
119 use sp_std::convert::TryFrom;
120 use sp_std::prelude::*;
121 use sp_std::vec;
122 use sp_std::vec::Vec;
123 use xcm::{latest::prelude::*, VersionedLocation};
124 use xcm_executor::traits::{TransactAsset, WeightBounds};
125 use xcm_primitives::{
126 FilterMaxAssetFee, HrmpAvailableCalls, HrmpEncodeCall, UtilityAvailableCalls,
127 UtilityEncodeCall, XcmTransact,
128 };
129
130 #[pallet::pallet]
131 #[pallet::without_storage_info]
132 pub struct Pallet<T>(pub PhantomData<T>);
133
134 #[pallet::config]
135 pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
136 type Balance: Parameter
138 + Member
139 + AtLeast32BitUnsigned
140 + Default
141 + Copy
142 + MaybeSerializeDeserialize
143 + Into<u128>;
144
145 type CurrencyId: Parameter + Member + Clone;
147
148 type CurrencyIdToLocation: Convert<Self::CurrencyId, Option<Location>>;
150
151 type Transactor: Parameter + Member + Clone + XcmTransact;
154
155 type AssetTransactor: TransactAsset;
158
159 type DerivativeAddressRegistrationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
161
162 type HrmpManipulatorOrigin: EnsureOrigin<Self::RuntimeOrigin>;
164
165 type HrmpOpenOrigin: EnsureOrigin<Self::RuntimeOrigin>;
167
168 type AccountIdToLocation: Convert<Self::AccountId, Location>;
170
171 type Weigher: WeightBounds<Self::RuntimeCall>;
173
174 type UniversalLocation: Get<InteriorLocation>;
176
177 #[pallet::constant]
179 type SelfLocation: Get<Location>;
180
181 type SovereignAccountDispatcherOrigin: EnsureOrigin<Self::RuntimeOrigin>;
183
184 type XcmSender: SendXcm;
186
187 #[pallet::constant]
192 type BaseXcmWeight: Get<Weight>;
193
194 type ReserveProvider: xcm_primitives::Reserve;
197
198 type MaxHrmpFee: FilterMaxAssetFee;
200
201 type FeeTrader: XcmFeeTrader;
204
205 type WeightInfo: WeightInfo;
206 }
207
208 #[derive(
211 Default,
212 Clone,
213 Encode,
214 Decode,
215 MaxEncodedLen,
216 RuntimeDebug,
217 Eq,
218 PartialEq,
219 scale_info::TypeInfo,
220 DecodeWithMemTracking,
221 )]
222 pub struct RemoteTransactInfoWithMaxWeight {
223 pub transact_extra_weight: Weight,
228 pub max_weight: Weight,
230 pub transact_extra_weight_signed: Option<Weight>,
236 }
237
238 #[derive(
240 Clone,
241 Encode,
242 Decode,
243 Eq,
244 PartialEq,
245 RuntimeDebug,
246 scale_info::TypeInfo,
247 DecodeWithMemTracking,
248 )]
249 pub enum Currency<CurrencyId> {
250 AsCurrencyId(CurrencyId),
252 AsMultiLocation(Box<VersionedLocation>),
254 }
255
256 impl<T> Default for Currency<T> {
257 fn default() -> Currency<T> {
258 Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
259 }
260 }
261
262 #[derive(
263 Clone,
264 Encode,
265 Decode,
266 Eq,
267 PartialEq,
268 RuntimeDebug,
269 scale_info::TypeInfo,
270 DecodeWithMemTracking,
271 )]
272 pub struct HrmpInitParams {
273 pub para_id: ParaId,
274 pub proposed_max_capacity: u32,
275 pub proposed_max_message_size: u32,
276 }
277
278 #[derive(
280 Clone,
281 Encode,
282 Decode,
283 Eq,
284 PartialEq,
285 RuntimeDebug,
286 scale_info::TypeInfo,
287 DecodeWithMemTracking,
288 )]
289 pub enum HrmpOperation {
290 InitOpen(HrmpInitParams),
291 Accept {
292 para_id: ParaId,
293 },
294 Close(HrmpChannelId),
295 Cancel {
296 channel_id: HrmpChannelId,
297 open_requests: u32,
298 },
299 }
300
301 #[derive(
302 Default,
303 Clone,
304 Encode,
305 Decode,
306 Eq,
307 PartialEq,
308 RuntimeDebug,
309 MaxEncodedLen,
310 scale_info::TypeInfo,
311 DecodeWithMemTracking,
312 )]
313
314 pub struct CurrencyPayment<CurrencyId> {
323 pub currency: Currency<CurrencyId>,
325 pub fee_amount: Option<u128>,
327 }
328
329 #[derive(
330 Default,
331 Clone,
332 Encode,
333 Decode,
334 RuntimeDebug,
335 PartialEq,
336 scale_info::TypeInfo,
337 DecodeWithMemTracking,
338 )]
339 pub struct TransactWeights {
346 pub transact_required_weight_at_most: Weight,
348 pub overall_weight: Option<WeightLimit>,
352 }
353
354 pub const MAX_WEIGHT: Weight = Weight::from_parts(100_000_000_000, 100_000);
358
359 #[pallet::storage]
363 #[pallet::getter(fn index_to_account)]
364 pub type IndexToAccount<T: Config> = StorageMap<_, Blake2_128Concat, u16, T::AccountId>;
365
366 #[pallet::storage]
370 #[pallet::getter(fn transact_info)]
371 pub type TransactInfoWithWeightLimit<T: Config> =
372 StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
373
374 #[pallet::storage]
376 #[pallet::getter(fn relay_indices)]
377 pub type RelayIndices<T: Config> = StorageValue<_, RelayChainIndices, ValueQuery>;
378
379 #[pallet::error]
381 pub enum Error<T> {
382 IndexAlreadyClaimed,
383 UnclaimedIndex,
384 NotOwner,
385 UnweighableMessage,
386 CannotReanchor,
387 AssetHasNoReserve,
388 InvalidDest,
389 NotCrossChainTransfer,
390 AssetIsNotReserveInDestination,
391 DestinationNotInvertible,
392 ErrorDelivering,
393 DispatchWeightBiggerThanTotalWeight,
394 WeightOverflow,
395 AmountOverflow,
396 TransactorInfoNotSet,
397 NotCrossChainTransferableCurrency,
398 XcmExecuteError,
399 BadVersion,
400 MaxWeightTransactReached,
401 UnableToWithdrawAsset,
402 #[codec(index = 21)]
405 SignedTransactNotAllowedForDestination,
406 #[codec(index = 22)]
407 FailedMultiLocationToJunction,
408 #[codec(index = 23)]
409 HrmpHandlerNotImplemented,
410 #[codec(index = 24)]
411 TooMuchFeeUsed,
412 #[codec(index = 25)]
413 ErrorValidating,
414 #[codec(index = 26)]
415 RefundNotSupportedWithTransactInfo,
416 }
417
418 #[pallet::event]
419 #[pallet::generate_deposit(pub(crate) fn deposit_event)]
420 pub enum Event<T: Config> {
421 TransactedDerivative {
423 account_id: T::AccountId,
424 dest: Location,
425 call: Vec<u8>,
426 index: u16,
427 },
428 TransactedSovereign {
430 fee_payer: Option<T::AccountId>,
431 dest: Location,
432 call: Vec<u8>,
433 },
434 TransactedSigned {
436 fee_payer: T::AccountId,
437 dest: Location,
438 call: Vec<u8>,
439 },
440 RegisteredDerivative {
442 account_id: T::AccountId,
443 index: u16,
444 },
445 DeRegisteredDerivative {
446 index: u16,
447 },
448 TransactFailed {
450 error: XcmError,
451 },
452 TransactInfoChanged {
454 location: Location,
455 remote_info: RemoteTransactInfoWithMaxWeight,
456 },
457 TransactInfoRemoved {
459 location: Location,
460 },
461 #[codec(index = 10)]
467 HrmpManagementSent {
468 action: HrmpOperation,
469 },
470 }
471
472 #[pallet::genesis_config]
473 pub struct GenesisConfig<T> {
474 pub relay_indices: RelayChainIndices,
475 #[serde(skip)]
476 pub _phantom: PhantomData<T>,
477 }
478
479 impl<T> Default for GenesisConfig<T> {
480 fn default() -> Self {
481 Self {
482 relay_indices: RelayChainIndices::default(),
483 _phantom: Default::default(),
484 }
485 }
486 }
487
488 #[pallet::genesis_build]
489 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
490 fn build(&self) {
491 <RelayIndices<T>>::put(self.relay_indices);
492 }
493 }
494
495 #[pallet::call]
496 impl<T: Config> Pallet<T> {
497 #[pallet::call_index(0)]
506 #[pallet::weight(T::WeightInfo::register())]
507 pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
508 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
509
510 ensure!(
511 IndexToAccount::<T>::get(&index).is_none(),
512 Error::<T>::IndexAlreadyClaimed
513 );
514
515 IndexToAccount::<T>::insert(&index, who.clone());
516
517 Self::deposit_event(Event::<T>::RegisteredDerivative {
519 account_id: who,
520 index: index,
521 });
522
523 Ok(())
524 }
525
526 #[pallet::call_index(1)]
529 #[pallet::weight(T::WeightInfo::deregister())]
530 pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
531 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
532
533 IndexToAccount::<T>::remove(&index);
535
536 Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
538
539 Ok(())
540 }
541
542 #[pallet::call_index(2)]
548 #[pallet::weight(
549 Pallet::<T>::weight_of_initiate_reserve_withdraw()
550 .saturating_add(T::WeightInfo::transact_through_derivative())
551 )]
552 pub fn transact_through_derivative(
553 origin: OriginFor<T>,
554 dest: T::Transactor,
556 index: u16,
558 fee: CurrencyPayment<CurrencyIdOf<T>>,
560 inner_call: Vec<u8>,
563 weight_info: TransactWeights,
565 refund: bool,
567 ) -> DispatchResult {
568 let who = ensure_signed(origin)?;
569
570 let fee_location = Self::currency_to_multilocation(fee.currency)
571 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
572
573 let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
575 ensure!(account == who, Error::<T>::NotOwner);
577
578 let call_bytes: Vec<u8> = <Self as UtilityEncodeCall>::encode_call(
581 dest.clone(),
582 UtilityAvailableCalls::AsDerivative(index, inner_call),
583 );
584
585 let dest = dest.destination();
587
588 let total_weight = weight_info.overall_weight.map_or_else(
591 || -> Result<_, DispatchError> {
592 let weight_info = Self::take_weight_from_transact_info(
593 dest.clone(),
594 weight_info.transact_required_weight_at_most,
595 refund,
596 )?;
597 Ok(WeightLimit::from(Some(weight_info)))
598 },
599 |v| Ok(v),
600 )?;
601
602 let total_weight_fee_calculation = match total_weight {
603 Unlimited => MAX_WEIGHT,
604 Limited(x) => x,
605 };
606
607 let fee = Self::calculate_fee(
609 fee_location,
610 fee.fee_amount,
611 dest.clone(),
612 total_weight_fee_calculation,
613 )?;
614
615 let appendix = refund
617 .then(|| -> Result<_, DispatchError> {
618 Ok(vec![
619 RefundSurplus,
620 Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
621 ])
622 })
623 .transpose()?;
624
625 Self::transact_in_dest_chain_asset_non_signed(
626 dest.clone(),
627 Some(who.clone()),
628 fee,
629 call_bytes.clone(),
630 OriginKind::SovereignAccount,
631 total_weight,
632 weight_info.transact_required_weight_at_most,
633 appendix,
634 )?;
635
636 Self::deposit_event(Event::<T>::TransactedDerivative {
638 account_id: who,
639 dest,
640 call: call_bytes,
641 index,
642 });
643
644 Ok(())
645 }
646
647 #[pallet::call_index(3)]
652 #[pallet::weight(
653 Pallet::<T>::weight_of_initiate_reserve_withdraw()
654 .saturating_add(T::WeightInfo::transact_through_sovereign())
655 )]
656 pub fn transact_through_sovereign(
657 origin: OriginFor<T>,
658 dest: Box<VersionedLocation>,
660 fee_payer: Option<T::AccountId>,
662 fee: CurrencyPayment<CurrencyIdOf<T>>,
664 call: Vec<u8>,
666 origin_kind: OriginKind,
668 weight_info: TransactWeights,
670 refund: bool,
672 ) -> DispatchResult {
673 T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
674
675 let fee_location = Self::currency_to_multilocation(fee.currency)
676 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
677
678 let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
679
680 let total_weight = weight_info.overall_weight.map_or_else(
683 || -> Result<_, DispatchError> {
684 let weight_info = Self::take_weight_from_transact_info(
685 dest.clone(),
686 weight_info.transact_required_weight_at_most,
687 refund,
688 )?;
689 Ok(WeightLimit::from(Some(weight_info)))
690 },
691 |v| Ok(v),
692 )?;
693
694 let total_weight_fee_calculation = match total_weight {
695 Unlimited => MAX_WEIGHT,
696 Limited(x) => x,
697 };
698
699 let fee = Self::calculate_fee(
701 fee_location,
702 fee.fee_amount,
703 dest.clone(),
704 total_weight_fee_calculation,
705 )?;
706
707 let appendix = refund
709 .then(|| -> Result<_, DispatchError> {
710 Ok(vec![
711 RefundSurplus,
712 Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
713 ])
714 })
715 .transpose()?;
716
717 Self::transact_in_dest_chain_asset_non_signed(
719 dest.clone(),
720 fee_payer.clone(),
721 fee,
722 call.clone(),
723 origin_kind,
724 total_weight,
725 weight_info.transact_required_weight_at_most,
726 appendix,
727 )?;
728
729 Self::deposit_event(Event::<T>::TransactedSovereign {
731 fee_payer,
732 dest,
733 call,
734 });
735
736 Ok(())
737 }
738
739 #[pallet::call_index(4)]
741 #[pallet::weight(T::WeightInfo::set_transact_info())]
742 pub fn set_transact_info(
743 origin: OriginFor<T>,
744 location: Box<VersionedLocation>,
745 transact_extra_weight: Weight,
746 max_weight: Weight,
747 transact_extra_weight_signed: Option<Weight>,
748 ) -> DispatchResult {
749 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
750 let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
751 let remote_info = RemoteTransactInfoWithMaxWeight {
752 transact_extra_weight,
753 max_weight,
754 transact_extra_weight_signed,
755 };
756
757 TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
758
759 Self::deposit_event(Event::TransactInfoChanged {
760 location,
761 remote_info,
762 });
763 Ok(())
764 }
765
766 #[pallet::call_index(5)]
768 #[pallet::weight(T::WeightInfo::remove_transact_info())]
769 pub fn remove_transact_info(
770 origin: OriginFor<T>,
771 location: Box<VersionedLocation>,
772 ) -> DispatchResult {
773 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
774 let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
775
776 TransactInfoWithWeightLimit::<T>::remove(&location);
778
779 Self::deposit_event(Event::TransactInfoRemoved { location });
780 Ok(())
781 }
782
783 #[pallet::call_index(6)]
789 #[pallet::weight(T::WeightInfo::transact_through_signed())]
790 pub fn transact_through_signed(
791 origin: OriginFor<T>,
792 dest: Box<VersionedLocation>,
794 fee: CurrencyPayment<CurrencyIdOf<T>>,
796 call: Vec<u8>,
798 weight_info: TransactWeights,
800 refund: bool,
802 ) -> DispatchResult {
803 let who = ensure_signed(origin)?;
804
805 let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
806
807 let fee_location = Self::currency_to_multilocation(fee.currency)
808 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
809
810 let total_weight = weight_info.overall_weight.map_or_else(
813 || -> Result<_, DispatchError> {
814 let weight_info = Self::take_weight_from_transact_info_signed(
815 dest.clone(),
816 weight_info.transact_required_weight_at_most,
817 refund,
818 )?;
819 Ok(WeightLimit::from(Some(weight_info)))
820 },
821 |v| Ok(v),
822 )?;
823
824 let total_weight_fee_calculation = match total_weight {
825 Unlimited => MAX_WEIGHT,
826 Limited(x) => x,
827 };
828
829 let fee = Self::calculate_fee(
831 fee_location,
832 fee.fee_amount,
833 dest.clone(),
834 total_weight_fee_calculation,
835 )?;
836
837 let appendix = refund
839 .then(|| -> Result<_, DispatchError> {
840 let sender = T::AccountIdToLocation::convert(who.clone());
841 Ok(vec![
842 RefundSurplus,
843 Self::deposit_instruction(sender, &dest, 1u32)?,
844 ])
845 })
846 .transpose()?;
847
848 Self::transact_in_dest_chain_asset_signed(
850 dest.clone(),
851 who.clone(),
852 fee,
853 call.clone(),
854 OriginKind::SovereignAccount,
855 total_weight,
856 weight_info.transact_required_weight_at_most,
857 appendix,
858 )?;
859
860 Self::deposit_event(Event::<T>::TransactedSigned {
862 fee_payer: who,
863 dest,
864 call,
865 });
866
867 Ok(())
868 }
869
870 #[pallet::call_index(9)]
878 #[pallet::weight(T::WeightInfo::hrmp_manage())]
879 pub fn hrmp_manage(
880 origin: OriginFor<T>,
881 action: HrmpOperation,
882 fee: CurrencyPayment<CurrencyIdOf<T>>,
884 weight_info: TransactWeights,
886 ) -> DispatchResult {
887 match &action {
894 HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
895 <EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
896 origin,
897 )?;
898 }
899 HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
900 T::HrmpManipulatorOrigin::ensure_origin(origin)?;
901 }
902 }
903
904 let call_bytes = match action.clone() {
906 HrmpOperation::InitOpen(params) => {
907 Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
908 params.para_id,
909 params.proposed_max_capacity,
910 params.proposed_max_message_size,
911 ))
912 }
913 HrmpOperation::Accept { para_id } => {
914 Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
915 }
916 HrmpOperation::Close(close_params) => {
917 Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
918 }
919 HrmpOperation::Cancel {
920 channel_id,
921 open_requests,
922 } => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
923 channel_id,
924 open_requests,
925 )),
926 }
927 .map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
928
929 let fee_location = Self::currency_to_multilocation(fee.currency)
930 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
931
932 let destination = Location::parent();
935
936 let total_weight = weight_info.overall_weight.map_or_else(
939 || -> Result<_, DispatchError> {
940 let weight_info = Self::take_weight_from_transact_info(
941 destination.clone(),
942 weight_info.transact_required_weight_at_most,
943 false,
944 )?;
945 Ok(WeightLimit::from(Some(weight_info)))
946 },
947 |v| Ok(v),
948 )?;
949
950 let total_weight_fee_calculation = match total_weight {
951 Unlimited => MAX_WEIGHT,
952 Limited(x) => x,
953 };
954
955 let fee = Self::calculate_fee(
956 fee_location,
957 fee.fee_amount,
958 destination.clone(),
959 total_weight_fee_calculation,
960 )?;
961
962 ensure!(
963 T::MaxHrmpFee::filter_max_asset_fee(&fee),
964 Error::<T>::TooMuchFeeUsed
965 );
966
967 let deposit_appendix =
969 Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
970
971 Self::transact_in_dest_chain_asset_non_signed(
972 destination,
973 None,
974 fee,
975 call_bytes.clone(),
976 OriginKind::Native,
977 total_weight,
978 weight_info.transact_required_weight_at_most,
979 Some(vec![RefundSurplus, deposit_appendix]),
980 )?;
981
982 Self::deposit_event(Event::HrmpManagementSent { action });
983
984 Ok(())
985 }
986 }
987
988 impl<T: Config> Pallet<T> {
989 fn transact_in_dest_chain_asset_non_signed(
990 dest: Location,
991 fee_payer: Option<T::AccountId>,
992 fee: Asset,
993 call: Vec<u8>,
994 origin_kind: OriginKind,
995 total_weight: WeightLimit,
996 transact_required_weight_at_most: Weight,
997 with_appendix: Option<Vec<Instruction<()>>>,
998 ) -> DispatchResult {
999 if let Some(fee_payer) = fee_payer {
1000 let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
1002
1003 T::AssetTransactor::withdraw_asset(&fee.clone().into(), &origin_as_mult, None)
1006 .map_err(|_| Error::<T>::UnableToWithdrawAsset)?;
1007 }
1008
1009 let transact_message: Xcm<()> = Self::transact_message(
1016 dest.clone(),
1017 fee,
1018 total_weight,
1019 call,
1020 transact_required_weight_at_most,
1021 origin_kind,
1022 with_appendix,
1023 )?;
1024
1025 let (ticket, _price) =
1027 T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
1028 .map_err(|_| Error::<T>::ErrorValidating)?;
1029 T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
1030
1031 Ok(())
1032 }
1033
1034 fn transact_in_dest_chain_asset_signed(
1035 dest: Location,
1036 fee_payer: T::AccountId,
1037 fee: Asset,
1038 call: Vec<u8>,
1039 origin_kind: OriginKind,
1040 total_weight: WeightLimit,
1041 transact_required_weight_at_most: Weight,
1042 with_appendix: Option<Vec<Instruction<()>>>,
1043 ) -> DispatchResult {
1044 let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
1046
1047 let mut transact_message: Xcm<()> = Self::transact_message(
1054 dest.clone(),
1055 fee,
1056 total_weight,
1057 call,
1058 transact_required_weight_at_most,
1059 origin_kind,
1060 with_appendix,
1061 )?;
1062
1063 let interior: Junctions = origin_as_mult
1067 .clone()
1068 .try_into()
1069 .map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
1070 transact_message.0.insert(0, DescendOrigin(interior));
1071
1072 let (ticket, _price) =
1074 T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
1075 .map_err(|_| Error::<T>::ErrorValidating)?;
1076 T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
1077
1078 Ok(())
1079 }
1080
1081 fn calculate_fee(
1085 fee_location: Location,
1086 fee_amount: Option<u128>,
1087 destination: Location,
1088 total_weight: Weight,
1089 ) -> Result<Asset, DispatchError> {
1090 if let Some(amount) = fee_amount {
1093 return Ok(Asset {
1094 id: AssetId(fee_location),
1095 fun: Fungible(amount),
1096 });
1097 }
1098
1099 let amount: u128 = T::FeeTrader::compute_fee(total_weight, &fee_location)?;
1102
1103 let asset = Asset {
1104 id: AssetId(fee_location.clone()),
1105 fun: Fungible(amount),
1106 };
1107 let reserve = <T::ReserveProvider as xcm_primitives::Reserve>::reserve(&asset)
1108 .ok_or(Error::<T>::AssetHasNoReserve)?;
1109 if reserve != destination {
1110 return Err(Error::<T>::AssetIsNotReserveInDestination.into());
1111 }
1112
1113 Ok(asset)
1114 }
1115
1116 fn transact_message(
1118 dest: Location,
1119 asset: Asset,
1120 dest_weight: WeightLimit,
1121 call: Vec<u8>,
1122 dispatch_weight: Weight,
1123 origin_kind: OriginKind,
1124 with_appendix: Option<Vec<Instruction<()>>>,
1125 ) -> Result<Xcm<()>, DispatchError> {
1126 let mut instructions = vec![
1127 Self::withdraw_instruction(asset.clone(), &dest)?,
1128 Self::buy_execution(asset, &dest, dest_weight)?,
1129 ];
1130 if let Some(appendix) = with_appendix {
1131 instructions.push(Self::appendix_instruction(appendix)?);
1132 }
1133 instructions.push(Transact {
1134 origin_kind,
1135 fallback_max_weight: Some(dispatch_weight),
1136 call: call.into(),
1137 });
1138 Ok(Xcm(instructions))
1139 }
1140
1141 fn buy_execution(
1143 asset: Asset,
1144 at: &Location,
1145 weight: WeightLimit,
1146 ) -> Result<Instruction<()>, DispatchError> {
1147 let universal_location = T::UniversalLocation::get();
1148 let fees = asset
1149 .reanchored(at, &universal_location)
1150 .map_err(|_| Error::<T>::CannotReanchor)?;
1151
1152 Ok(BuyExecution {
1153 fees,
1154 weight_limit: weight,
1155 })
1156 }
1157
1158 fn withdraw_instruction(
1160 asset: Asset,
1161 at: &Location,
1162 ) -> Result<Instruction<()>, DispatchError> {
1163 let universal_location = T::UniversalLocation::get();
1164 let fees = asset
1165 .reanchored(at, &universal_location)
1166 .map_err(|_| Error::<T>::CannotReanchor)?;
1167
1168 Ok(WithdrawAsset(fees.into()))
1169 }
1170
1171 fn deposit_instruction(
1173 mut beneficiary: Location,
1174 at: &Location,
1175 max_assets: u32,
1176 ) -> Result<Instruction<()>, DispatchError> {
1177 let universal_location = T::UniversalLocation::get();
1178 beneficiary
1179 .reanchor(at, &universal_location)
1180 .map_err(|_| Error::<T>::CannotReanchor)?;
1181 Ok(DepositAsset {
1182 assets: Wild(AllCounted(max_assets)),
1183 beneficiary,
1184 })
1185 }
1186
1187 fn appendix_instruction(
1189 instructions: Vec<Instruction<()>>,
1190 ) -> Result<Instruction<()>, DispatchError> {
1191 Ok(SetAppendix(Xcm(instructions)))
1192 }
1193
1194 fn weight_of_initiate_reserve_withdraw() -> Weight {
1196 let dest = Location::parent();
1197
1198 let asset = Location::parent();
1200
1201 let fee = Asset {
1203 id: AssetId(asset.clone()),
1204 fun: Fungible(0),
1205 };
1206
1207 let xcm: Xcm<()> = Xcm(vec![
1208 WithdrawAsset(fee.into()),
1209 InitiateReserveWithdraw {
1210 assets: AssetFilter::Wild(All),
1211 reserve: dest.clone(),
1212 xcm: Xcm(vec![]),
1213 },
1214 ]);
1215 T::Weigher::weight(&mut xcm.into(), Weight::MAX)
1216 .map_or(Weight::MAX, |w| T::BaseXcmWeight::get().saturating_add(w))
1217 }
1218
1219 pub fn take_weight_from_transact_info(
1222 dest: Location,
1223 dest_weight: Weight,
1224 refund: bool,
1225 ) -> Result<Weight, DispatchError> {
1226 ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
1229 let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
1231 .ok_or(Error::<T>::TransactorInfoNotSet)?;
1232
1233 let total_weight = dest_weight
1234 .checked_add(&transactor_info.transact_extra_weight)
1235 .ok_or(Error::<T>::WeightOverflow)?;
1236
1237 ensure!(
1238 total_weight.all_lte(transactor_info.max_weight),
1239 Error::<T>::MaxWeightTransactReached
1240 );
1241 Ok(total_weight)
1242 }
1243
1244 pub fn take_weight_from_transact_info_signed(
1247 dest: Location,
1248 dest_weight: Weight,
1249 refund: bool,
1250 ) -> Result<Weight, DispatchError> {
1251 ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
1254 let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
1256 .ok_or(Error::<T>::TransactorInfoNotSet)?;
1257
1258 let transact_in_dest_as_signed_weight = transactor_info
1261 .transact_extra_weight_signed
1262 .ok_or(Error::<T>::SignedTransactNotAllowedForDestination)?;
1263
1264 let total_weight = dest_weight
1265 .checked_add(&transact_in_dest_as_signed_weight)
1266 .ok_or(Error::<T>::WeightOverflow)?;
1267
1268 ensure!(
1269 total_weight.all_lte(transactor_info.max_weight),
1270 Error::<T>::MaxWeightTransactReached
1271 );
1272 Ok(total_weight)
1273 }
1274
1275 pub fn dest_asset_fee_per_second(location: &Location) -> Option<u128> {
1281 let one_second = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0);
1282 T::FeeTrader::compute_fee(one_second, location).ok()
1283 }
1284
1285 pub fn currency_to_multilocation(currency: Currency<CurrencyIdOf<T>>) -> Option<Location> {
1287 match currency {
1288 Currency::AsCurrencyId(id) => T::CurrencyIdToLocation::convert(id),
1289 Currency::AsMultiLocation(multiloc) => Location::try_from(*multiloc).ok(),
1290 }
1291 }
1292 }
1293}