1#![cfg_attr(not(feature = "std"), no_std)]
69
70use frame_support::pallet;
71
72pub use pallet::*;
73
74#[cfg(any(test, feature = "runtime-benchmarks"))]
75mod benchmarks;
76
77#[cfg(test)]
78pub(crate) mod mock;
79#[cfg(test)]
80mod tests;
81
82pub mod encode;
83pub mod migrations;
84pub mod relay_indices;
85pub mod weights;
86pub use crate::weights::WeightInfo;
87
88type CurrencyIdOf<T> = <T as Config>::CurrencyId;
89
90pub const ASSET_HUB_UTILITY_PALLET_INDEX: u8 = 40;
95
96pub const ASSET_HUB_STAKING_PALLET_INDEX: u8 = 89;
101
102#[pallet]
103pub mod pallet {
104
105 use super::*;
106 use crate::relay_indices::RelayChainIndices;
107 use crate::weights::WeightInfo;
108 use crate::CurrencyIdOf;
109 use cumulus_primitives_core::{relay_chain::HrmpChannelId, ParaId};
110 use frame_support::traits::EitherOfDiverse;
111 use frame_support::{
112 dispatch::DispatchResult, pallet_prelude::*, weights::constants::WEIGHT_REF_TIME_PER_SECOND,
113 };
114 use frame_system::{ensure_signed, pallet_prelude::*};
115 use sp_runtime::traits::{AtLeast32BitUnsigned, Convert};
116 use sp_std::boxed::Box;
117 use sp_std::convert::TryFrom;
118 use sp_std::prelude::*;
119 use sp_std::vec;
120 use sp_std::vec::Vec;
121 use xcm::{latest::prelude::*, VersionedLocation};
122 use xcm_executor::traits::{TransactAsset, WeightBounds};
123 use xcm_primitives::{
124 FilterMaxAssetFee, HrmpAvailableCalls, HrmpEncodeCall, Reserve, UtilityAvailableCalls,
125 UtilityEncodeCall, XcmTransact,
126 };
127
128 #[pallet::pallet]
129 #[pallet::without_storage_info]
130 pub struct Pallet<T>(pub PhantomData<T>);
131
132 #[pallet::config]
133 pub trait Config: frame_system::Config<RuntimeEvent: From<Event<Self>>> {
134 type Balance: Parameter
136 + Member
137 + AtLeast32BitUnsigned
138 + Default
139 + Copy
140 + MaybeSerializeDeserialize
141 + Into<u128>;
142
143 type CurrencyId: Parameter + Member + Clone;
145
146 type CurrencyIdToLocation: Convert<Self::CurrencyId, Option<Location>>;
148
149 type Transactor: Parameter + Member + Clone + XcmTransact;
152
153 type AssetTransactor: TransactAsset;
156
157 type DerivativeAddressRegistrationOrigin: EnsureOrigin<Self::RuntimeOrigin>;
159
160 type HrmpManipulatorOrigin: EnsureOrigin<Self::RuntimeOrigin>;
162
163 type HrmpOpenOrigin: EnsureOrigin<Self::RuntimeOrigin>;
165
166 type AccountIdToLocation: Convert<Self::AccountId, Location>;
168
169 type Weigher: WeightBounds<Self::RuntimeCall>;
171
172 type UniversalLocation: Get<InteriorLocation>;
174
175 #[pallet::constant]
177 type SelfLocation: Get<Location>;
178
179 type SovereignAccountDispatcherOrigin: EnsureOrigin<Self::RuntimeOrigin>;
181
182 type XcmSender: SendXcm;
184
185 #[pallet::constant]
190 type BaseXcmWeight: Get<Weight>;
191
192 type ReserveProvider: xcm_primitives::Reserve;
195
196 type MaxHrmpFee: FilterMaxAssetFee;
198
199 type WeightInfo: WeightInfo;
200 }
201
202 #[derive(
205 Default,
206 Clone,
207 Encode,
208 Decode,
209 MaxEncodedLen,
210 RuntimeDebug,
211 Eq,
212 PartialEq,
213 scale_info::TypeInfo,
214 DecodeWithMemTracking,
215 )]
216 pub struct RemoteTransactInfoWithMaxWeight {
217 pub transact_extra_weight: Weight,
222 pub max_weight: Weight,
224 pub transact_extra_weight_signed: Option<Weight>,
230 }
231
232 #[derive(
234 Clone,
235 Encode,
236 Decode,
237 Eq,
238 PartialEq,
239 RuntimeDebug,
240 scale_info::TypeInfo,
241 DecodeWithMemTracking,
242 )]
243 pub enum Currency<CurrencyId> {
244 AsCurrencyId(CurrencyId),
246 AsMultiLocation(Box<VersionedLocation>),
248 }
249
250 impl<T> Default for Currency<T> {
251 fn default() -> Currency<T> {
252 Currency::<T>::AsMultiLocation(Box::new(Location::default().into()))
253 }
254 }
255
256 #[derive(
257 Clone,
258 Encode,
259 Decode,
260 Eq,
261 PartialEq,
262 RuntimeDebug,
263 scale_info::TypeInfo,
264 DecodeWithMemTracking,
265 )]
266 pub struct HrmpInitParams {
267 pub para_id: ParaId,
268 pub proposed_max_capacity: u32,
269 pub proposed_max_message_size: u32,
270 }
271
272 #[derive(
274 Clone,
275 Encode,
276 Decode,
277 Eq,
278 PartialEq,
279 RuntimeDebug,
280 scale_info::TypeInfo,
281 DecodeWithMemTracking,
282 )]
283 pub enum HrmpOperation {
284 InitOpen(HrmpInitParams),
285 Accept {
286 para_id: ParaId,
287 },
288 Close(HrmpChannelId),
289 Cancel {
290 channel_id: HrmpChannelId,
291 open_requests: u32,
292 },
293 }
294
295 #[derive(
296 Default,
297 Clone,
298 Encode,
299 Decode,
300 Eq,
301 PartialEq,
302 RuntimeDebug,
303 MaxEncodedLen,
304 scale_info::TypeInfo,
305 DecodeWithMemTracking,
306 )]
307
308 pub struct CurrencyPayment<CurrencyId> {
317 pub currency: Currency<CurrencyId>,
319 pub fee_amount: Option<u128>,
321 }
322
323 #[derive(
324 Default,
325 Clone,
326 Encode,
327 Decode,
328 RuntimeDebug,
329 PartialEq,
330 scale_info::TypeInfo,
331 DecodeWithMemTracking,
332 )]
333 pub struct TransactWeights {
340 pub transact_required_weight_at_most: Weight,
342 pub overall_weight: Option<WeightLimit>,
346 }
347
348 pub const MAX_WEIGHT: Weight = Weight::from_parts(100_000_000_000, 100_000);
352
353 #[pallet::storage]
357 #[pallet::getter(fn index_to_account)]
358 pub type IndexToAccount<T: Config> = StorageMap<_, Blake2_128Concat, u16, T::AccountId>;
359
360 #[pallet::storage]
364 #[pallet::getter(fn transact_info)]
365 pub type TransactInfoWithWeightLimit<T: Config> =
366 StorageMap<_, Blake2_128Concat, Location, RemoteTransactInfoWithMaxWeight>;
367
368 #[pallet::storage]
371 #[pallet::getter(fn dest_asset_fee_per_second)]
372 pub type DestinationAssetFeePerSecond<T: Config> = StorageMap<_, Twox64Concat, Location, u128>;
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 FeePerSecondNotSet,
403 SignedTransactNotAllowedForDestination,
404 FailedMultiLocationToJunction,
405 HrmpHandlerNotImplemented,
406 TooMuchFeeUsed,
407 ErrorValidating,
408 RefundNotSupportedWithTransactInfo,
409 }
410
411 #[pallet::event]
412 #[pallet::generate_deposit(pub(crate) fn deposit_event)]
413 pub enum Event<T: Config> {
414 TransactedDerivative {
416 account_id: T::AccountId,
417 dest: Location,
418 call: Vec<u8>,
419 index: u16,
420 },
421 TransactedSovereign {
423 fee_payer: Option<T::AccountId>,
424 dest: Location,
425 call: Vec<u8>,
426 },
427 TransactedSigned {
429 fee_payer: T::AccountId,
430 dest: Location,
431 call: Vec<u8>,
432 },
433 RegisteredDerivative {
435 account_id: T::AccountId,
436 index: u16,
437 },
438 DeRegisteredDerivative {
439 index: u16,
440 },
441 TransactFailed {
443 error: XcmError,
444 },
445 TransactInfoChanged {
447 location: Location,
448 remote_info: RemoteTransactInfoWithMaxWeight,
449 },
450 TransactInfoRemoved {
452 location: Location,
453 },
454 DestFeePerSecondChanged {
456 location: Location,
457 fee_per_second: u128,
458 },
459 DestFeePerSecondRemoved {
461 location: Location,
462 },
463 HrmpManagementSent {
465 action: HrmpOperation,
466 },
467 }
468
469 #[pallet::genesis_config]
470 pub struct GenesisConfig<T> {
471 pub relay_indices: RelayChainIndices,
472 #[serde(skip)]
473 pub _phantom: PhantomData<T>,
474 }
475
476 impl<T> Default for GenesisConfig<T> {
477 fn default() -> Self {
478 Self {
479 relay_indices: RelayChainIndices::default(),
480 _phantom: Default::default(),
481 }
482 }
483 }
484
485 #[pallet::genesis_build]
486 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
487 fn build(&self) {
488 <RelayIndices<T>>::put(self.relay_indices);
489 }
490 }
491
492 #[pallet::call]
493 impl<T: Config> Pallet<T> {
494 #[pallet::call_index(0)]
503 #[pallet::weight(T::WeightInfo::register())]
504 pub fn register(origin: OriginFor<T>, who: T::AccountId, index: u16) -> DispatchResult {
505 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
506
507 ensure!(
508 IndexToAccount::<T>::get(&index).is_none(),
509 Error::<T>::IndexAlreadyClaimed
510 );
511
512 IndexToAccount::<T>::insert(&index, who.clone());
513
514 Self::deposit_event(Event::<T>::RegisteredDerivative {
516 account_id: who,
517 index: index,
518 });
519
520 Ok(())
521 }
522
523 #[pallet::call_index(1)]
526 #[pallet::weight(T::WeightInfo::deregister())]
527 pub fn deregister(origin: OriginFor<T>, index: u16) -> DispatchResult {
528 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
529
530 IndexToAccount::<T>::remove(&index);
532
533 Self::deposit_event(Event::<T>::DeRegisteredDerivative { index });
535
536 Ok(())
537 }
538
539 #[pallet::call_index(2)]
545 #[pallet::weight(
546 Pallet::<T>::weight_of_initiate_reserve_withdraw()
547 .saturating_add(T::WeightInfo::transact_through_derivative())
548 )]
549 pub fn transact_through_derivative(
550 origin: OriginFor<T>,
551 dest: T::Transactor,
553 index: u16,
555 fee: CurrencyPayment<CurrencyIdOf<T>>,
557 inner_call: Vec<u8>,
560 weight_info: TransactWeights,
562 refund: bool,
564 ) -> DispatchResult {
565 let who = ensure_signed(origin)?;
566
567 let fee_location = Self::currency_to_multilocation(fee.currency)
568 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
569
570 let account = IndexToAccount::<T>::get(index).ok_or(Error::<T>::UnclaimedIndex)?;
572 ensure!(account == who, Error::<T>::NotOwner);
574
575 let call_bytes: Vec<u8> = <Self as UtilityEncodeCall>::encode_call(
578 dest.clone(),
579 UtilityAvailableCalls::AsDerivative(index, inner_call),
580 );
581
582 let dest = dest.destination();
584
585 let total_weight = weight_info.overall_weight.map_or_else(
588 || -> Result<_, DispatchError> {
589 let weight_info = Self::take_weight_from_transact_info(
590 dest.clone(),
591 weight_info.transact_required_weight_at_most,
592 refund,
593 )?;
594 Ok(WeightLimit::from(Some(weight_info)))
595 },
596 |v| Ok(v),
597 )?;
598
599 let total_weight_fee_calculation = match total_weight {
600 Unlimited => MAX_WEIGHT,
601 Limited(x) => x,
602 };
603
604 let fee = Self::calculate_fee(
606 fee_location,
607 fee.fee_amount,
608 dest.clone(),
609 total_weight_fee_calculation,
610 )?;
611
612 let appendix = refund
614 .then(|| -> Result<_, DispatchError> {
615 Ok(vec![
616 RefundSurplus,
617 Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
618 ])
619 })
620 .transpose()?;
621
622 Self::transact_in_dest_chain_asset_non_signed(
623 dest.clone(),
624 Some(who.clone()),
625 fee,
626 call_bytes.clone(),
627 OriginKind::SovereignAccount,
628 total_weight,
629 weight_info.transact_required_weight_at_most,
630 appendix,
631 )?;
632
633 Self::deposit_event(Event::<T>::TransactedDerivative {
635 account_id: who,
636 dest,
637 call: call_bytes,
638 index,
639 });
640
641 Ok(())
642 }
643
644 #[pallet::call_index(3)]
649 #[pallet::weight(
650 Pallet::<T>::weight_of_initiate_reserve_withdraw()
651 .saturating_add(T::WeightInfo::transact_through_sovereign())
652 )]
653 pub fn transact_through_sovereign(
654 origin: OriginFor<T>,
655 dest: Box<VersionedLocation>,
657 fee_payer: Option<T::AccountId>,
659 fee: CurrencyPayment<CurrencyIdOf<T>>,
661 call: Vec<u8>,
663 origin_kind: OriginKind,
665 weight_info: TransactWeights,
667 refund: bool,
669 ) -> DispatchResult {
670 T::SovereignAccountDispatcherOrigin::ensure_origin(origin)?;
671
672 let fee_location = Self::currency_to_multilocation(fee.currency)
673 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
674
675 let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
676
677 let total_weight = weight_info.overall_weight.map_or_else(
680 || -> Result<_, DispatchError> {
681 let weight_info = Self::take_weight_from_transact_info(
682 dest.clone(),
683 weight_info.transact_required_weight_at_most,
684 refund,
685 )?;
686 Ok(WeightLimit::from(Some(weight_info)))
687 },
688 |v| Ok(v),
689 )?;
690
691 let total_weight_fee_calculation = match total_weight {
692 Unlimited => MAX_WEIGHT,
693 Limited(x) => x,
694 };
695
696 let fee = Self::calculate_fee(
698 fee_location,
699 fee.fee_amount,
700 dest.clone(),
701 total_weight_fee_calculation,
702 )?;
703
704 let appendix = refund
706 .then(|| -> Result<_, DispatchError> {
707 Ok(vec![
708 RefundSurplus,
709 Self::deposit_instruction(T::SelfLocation::get(), &dest, 1u32)?,
710 ])
711 })
712 .transpose()?;
713
714 Self::transact_in_dest_chain_asset_non_signed(
716 dest.clone(),
717 fee_payer.clone(),
718 fee,
719 call.clone(),
720 origin_kind,
721 total_weight,
722 weight_info.transact_required_weight_at_most,
723 appendix,
724 )?;
725
726 Self::deposit_event(Event::<T>::TransactedSovereign {
728 fee_payer,
729 dest,
730 call,
731 });
732
733 Ok(())
734 }
735
736 #[pallet::call_index(4)]
738 #[pallet::weight(T::WeightInfo::set_transact_info())]
739 pub fn set_transact_info(
740 origin: OriginFor<T>,
741 location: Box<VersionedLocation>,
742 transact_extra_weight: Weight,
743 max_weight: Weight,
744 transact_extra_weight_signed: Option<Weight>,
745 ) -> DispatchResult {
746 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
747 let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
748 let remote_info = RemoteTransactInfoWithMaxWeight {
749 transact_extra_weight,
750 max_weight,
751 transact_extra_weight_signed,
752 };
753
754 TransactInfoWithWeightLimit::<T>::insert(&location, &remote_info);
755
756 Self::deposit_event(Event::TransactInfoChanged {
757 location,
758 remote_info,
759 });
760 Ok(())
761 }
762
763 #[pallet::call_index(5)]
765 #[pallet::weight(T::WeightInfo::remove_transact_info())]
766 pub fn remove_transact_info(
767 origin: OriginFor<T>,
768 location: Box<VersionedLocation>,
769 ) -> DispatchResult {
770 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
771 let location = Location::try_from(*location).map_err(|()| Error::<T>::BadVersion)?;
772
773 TransactInfoWithWeightLimit::<T>::remove(&location);
775
776 Self::deposit_event(Event::TransactInfoRemoved { location });
777 Ok(())
778 }
779
780 #[pallet::call_index(6)]
786 #[pallet::weight(T::WeightInfo::transact_through_signed())]
787 pub fn transact_through_signed(
788 origin: OriginFor<T>,
789 dest: Box<VersionedLocation>,
791 fee: CurrencyPayment<CurrencyIdOf<T>>,
793 call: Vec<u8>,
795 weight_info: TransactWeights,
797 refund: bool,
799 ) -> DispatchResult {
800 let who = ensure_signed(origin)?;
801
802 let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
803
804 let fee_location = Self::currency_to_multilocation(fee.currency)
805 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
806
807 let total_weight = weight_info.overall_weight.map_or_else(
810 || -> Result<_, DispatchError> {
811 let weight_info = Self::take_weight_from_transact_info_signed(
812 dest.clone(),
813 weight_info.transact_required_weight_at_most,
814 refund,
815 )?;
816 Ok(WeightLimit::from(Some(weight_info)))
817 },
818 |v| Ok(v),
819 )?;
820
821 let total_weight_fee_calculation = match total_weight {
822 Unlimited => MAX_WEIGHT,
823 Limited(x) => x,
824 };
825
826 let fee = Self::calculate_fee(
828 fee_location,
829 fee.fee_amount,
830 dest.clone(),
831 total_weight_fee_calculation,
832 )?;
833
834 let appendix = refund
836 .then(|| -> Result<_, DispatchError> {
837 let sender = T::AccountIdToLocation::convert(who.clone());
838 Ok(vec![
839 RefundSurplus,
840 Self::deposit_instruction(sender, &dest, 1u32)?,
841 ])
842 })
843 .transpose()?;
844
845 Self::transact_in_dest_chain_asset_signed(
847 dest.clone(),
848 who.clone(),
849 fee,
850 call.clone(),
851 OriginKind::SovereignAccount,
852 total_weight,
853 weight_info.transact_required_weight_at_most,
854 appendix,
855 )?;
856
857 Self::deposit_event(Event::<T>::TransactedSigned {
859 fee_payer: who,
860 dest,
861 call,
862 });
863
864 Ok(())
865 }
866
867 #[pallet::call_index(7)]
869 #[pallet::weight(T::WeightInfo::set_fee_per_second())]
870 pub fn set_fee_per_second(
871 origin: OriginFor<T>,
872 asset_location: Box<VersionedLocation>,
873 fee_per_second: u128,
874 ) -> DispatchResult {
875 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
876 let asset_location =
877 Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
878
879 DestinationAssetFeePerSecond::<T>::insert(&asset_location, &fee_per_second);
880
881 Self::deposit_event(Event::DestFeePerSecondChanged {
882 location: asset_location,
883 fee_per_second,
884 });
885 Ok(())
886 }
887
888 #[pallet::call_index(8)]
890 #[pallet::weight(T::WeightInfo::set_fee_per_second())]
891 pub fn remove_fee_per_second(
892 origin: OriginFor<T>,
893 asset_location: Box<VersionedLocation>,
894 ) -> DispatchResult {
895 T::DerivativeAddressRegistrationOrigin::ensure_origin(origin)?;
896 let asset_location =
897 Location::try_from(*asset_location).map_err(|()| Error::<T>::BadVersion)?;
898
899 DestinationAssetFeePerSecond::<T>::remove(&asset_location);
900
901 Self::deposit_event(Event::DestFeePerSecondRemoved {
902 location: asset_location,
903 });
904 Ok(())
905 }
906
907 #[pallet::call_index(9)]
909 #[pallet::weight(T::WeightInfo::hrmp_manage())]
910 pub fn hrmp_manage(
911 origin: OriginFor<T>,
912 action: HrmpOperation,
913 fee: CurrencyPayment<CurrencyIdOf<T>>,
915 weight_info: TransactWeights,
917 ) -> DispatchResult {
918 match &action {
925 HrmpOperation::InitOpen(_) | HrmpOperation::Accept { .. } => {
926 <EitherOfDiverse<T::HrmpManipulatorOrigin, T::HrmpOpenOrigin>>::ensure_origin(
927 origin,
928 )?;
929 }
930 HrmpOperation::Close(_) | HrmpOperation::Cancel { .. } => {
931 T::HrmpManipulatorOrigin::ensure_origin(origin)?;
932 }
933 }
934
935 let call_bytes = match action.clone() {
937 HrmpOperation::InitOpen(params) => {
938 Self::hrmp_encode_call(HrmpAvailableCalls::InitOpenChannel(
939 params.para_id,
940 params.proposed_max_capacity,
941 params.proposed_max_message_size,
942 ))
943 }
944 HrmpOperation::Accept { para_id } => {
945 Self::hrmp_encode_call(HrmpAvailableCalls::AcceptOpenChannel(para_id))
946 }
947 HrmpOperation::Close(close_params) => {
948 Self::hrmp_encode_call(HrmpAvailableCalls::CloseChannel(close_params))
949 }
950 HrmpOperation::Cancel {
951 channel_id,
952 open_requests,
953 } => Self::hrmp_encode_call(HrmpAvailableCalls::CancelOpenRequest(
954 channel_id,
955 open_requests,
956 )),
957 }
958 .map_err(|_| Error::<T>::HrmpHandlerNotImplemented)?;
959
960 let fee_location = Self::currency_to_multilocation(fee.currency)
961 .ok_or(Error::<T>::NotCrossChainTransferableCurrency)?;
962
963 let destination = Location::parent();
966
967 let total_weight = weight_info.overall_weight.map_or_else(
970 || -> Result<_, DispatchError> {
971 let weight_info = Self::take_weight_from_transact_info(
972 destination.clone(),
973 weight_info.transact_required_weight_at_most,
974 false,
975 )?;
976 Ok(WeightLimit::from(Some(weight_info)))
977 },
978 |v| Ok(v),
979 )?;
980
981 let total_weight_fee_calculation = match total_weight {
982 Unlimited => MAX_WEIGHT,
983 Limited(x) => x,
984 };
985
986 let fee = Self::calculate_fee(
987 fee_location,
988 fee.fee_amount,
989 destination.clone(),
990 total_weight_fee_calculation,
991 )?;
992
993 ensure!(
994 T::MaxHrmpFee::filter_max_asset_fee(&fee),
995 Error::<T>::TooMuchFeeUsed
996 );
997
998 let deposit_appendix =
1000 Self::deposit_instruction(T::SelfLocation::get(), &destination, 1u32)?;
1001
1002 Self::transact_in_dest_chain_asset_non_signed(
1003 destination,
1004 None,
1005 fee,
1006 call_bytes.clone(),
1007 OriginKind::Native,
1008 total_weight,
1009 weight_info.transact_required_weight_at_most,
1010 Some(vec![RefundSurplus, deposit_appendix]),
1011 )?;
1012
1013 Self::deposit_event(Event::HrmpManagementSent { action });
1014
1015 Ok(())
1016 }
1017 }
1018
1019 impl<T: Config> Pallet<T> {
1020 fn transact_in_dest_chain_asset_non_signed(
1021 dest: Location,
1022 fee_payer: Option<T::AccountId>,
1023 fee: Asset,
1024 call: Vec<u8>,
1025 origin_kind: OriginKind,
1026 total_weight: WeightLimit,
1027 transact_required_weight_at_most: Weight,
1028 with_appendix: Option<Vec<Instruction<()>>>,
1029 ) -> DispatchResult {
1030 if let Some(fee_payer) = fee_payer {
1031 let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
1033
1034 T::AssetTransactor::withdraw_asset(&fee.clone().into(), &origin_as_mult, None)
1037 .map_err(|_| Error::<T>::UnableToWithdrawAsset)?;
1038 }
1039
1040 let transact_message: Xcm<()> = Self::transact_message(
1047 dest.clone(),
1048 fee,
1049 total_weight,
1050 call,
1051 transact_required_weight_at_most,
1052 origin_kind,
1053 with_appendix,
1054 )?;
1055
1056 let (ticket, _price) =
1058 T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
1059 .map_err(|_| Error::<T>::ErrorValidating)?;
1060 T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
1061
1062 Ok(())
1063 }
1064
1065 fn transact_in_dest_chain_asset_signed(
1066 dest: Location,
1067 fee_payer: T::AccountId,
1068 fee: Asset,
1069 call: Vec<u8>,
1070 origin_kind: OriginKind,
1071 total_weight: WeightLimit,
1072 transact_required_weight_at_most: Weight,
1073 with_appendix: Option<Vec<Instruction<()>>>,
1074 ) -> DispatchResult {
1075 let origin_as_mult = T::AccountIdToLocation::convert(fee_payer);
1077
1078 let mut transact_message: Xcm<()> = Self::transact_message(
1085 dest.clone(),
1086 fee,
1087 total_weight,
1088 call,
1089 transact_required_weight_at_most,
1090 origin_kind,
1091 with_appendix,
1092 )?;
1093
1094 let interior: Junctions = origin_as_mult
1098 .clone()
1099 .try_into()
1100 .map_err(|_| Error::<T>::FailedMultiLocationToJunction)?;
1101 transact_message.0.insert(0, DescendOrigin(interior));
1102
1103 let (ticket, _price) =
1105 T::XcmSender::validate(&mut Some(dest), &mut Some(transact_message))
1106 .map_err(|_| Error::<T>::ErrorValidating)?;
1107 T::XcmSender::deliver(ticket).map_err(|_| Error::<T>::ErrorDelivering)?;
1108
1109 Ok(())
1110 }
1111
1112 fn calculate_fee(
1115 fee_location: Location,
1116 fee_amount: Option<u128>,
1117 destination: Location,
1118 total_weight: Weight,
1119 ) -> Result<Asset, DispatchError> {
1120 let amount: u128 = fee_amount.map_or_else(
1124 || {
1125 Self::take_fee_per_second_from_storage(
1126 fee_location.clone(),
1127 destination,
1128 total_weight,
1129 )
1130 },
1131 |v| Ok(v),
1132 )?;
1133
1134 Ok(Asset {
1136 id: AssetId(fee_location),
1137 fun: Fungible(amount),
1138 })
1139 }
1140
1141 fn transact_message(
1143 dest: Location,
1144 asset: Asset,
1145 dest_weight: WeightLimit,
1146 call: Vec<u8>,
1147 dispatch_weight: Weight,
1148 origin_kind: OriginKind,
1149 with_appendix: Option<Vec<Instruction<()>>>,
1150 ) -> Result<Xcm<()>, DispatchError> {
1151 let mut instructions = vec![
1152 Self::withdraw_instruction(asset.clone(), &dest)?,
1153 Self::buy_execution(asset, &dest, dest_weight)?,
1154 ];
1155 if let Some(appendix) = with_appendix {
1156 instructions.push(Self::appendix_instruction(appendix)?);
1157 }
1158 instructions.push(Transact {
1159 origin_kind,
1160 fallback_max_weight: Some(dispatch_weight),
1161 call: call.into(),
1162 });
1163 Ok(Xcm(instructions))
1164 }
1165
1166 fn buy_execution(
1168 asset: Asset,
1169 at: &Location,
1170 weight: WeightLimit,
1171 ) -> Result<Instruction<()>, DispatchError> {
1172 let universal_location = T::UniversalLocation::get();
1173 let fees = asset
1174 .reanchored(at, &universal_location)
1175 .map_err(|_| Error::<T>::CannotReanchor)?;
1176
1177 Ok(BuyExecution {
1178 fees,
1179 weight_limit: weight,
1180 })
1181 }
1182
1183 fn withdraw_instruction(
1185 asset: Asset,
1186 at: &Location,
1187 ) -> Result<Instruction<()>, DispatchError> {
1188 let universal_location = T::UniversalLocation::get();
1189 let fees = asset
1190 .reanchored(at, &universal_location)
1191 .map_err(|_| Error::<T>::CannotReanchor)?;
1192
1193 Ok(WithdrawAsset(fees.into()))
1194 }
1195
1196 fn deposit_instruction(
1198 mut beneficiary: Location,
1199 at: &Location,
1200 max_assets: u32,
1201 ) -> Result<Instruction<()>, DispatchError> {
1202 let universal_location = T::UniversalLocation::get();
1203 beneficiary
1204 .reanchor(at, &universal_location)
1205 .map_err(|_| Error::<T>::CannotReanchor)?;
1206 Ok(DepositAsset {
1207 assets: Wild(AllCounted(max_assets)),
1208 beneficiary,
1209 })
1210 }
1211
1212 fn appendix_instruction(
1214 instructions: Vec<Instruction<()>>,
1215 ) -> Result<Instruction<()>, DispatchError> {
1216 Ok(SetAppendix(Xcm(instructions)))
1217 }
1218
1219 fn ensure_valid_dest(dest: &Location) -> Result<Location, DispatchError> {
1221 let chain_location = dest.chain_location();
1222 if *dest == chain_location {
1223 Ok(chain_location)
1224 } else {
1225 Err(Error::<T>::InvalidDest.into())
1226 }
1227 }
1228
1229 fn transfer_allowed(asset: &Asset, dest: &Location) -> Result<Location, DispatchError> {
1234 let dest = Self::ensure_valid_dest(dest)?;
1235
1236 let self_location = T::SelfLocation::get();
1237 ensure!(dest != self_location, Error::<T>::NotCrossChainTransfer);
1238
1239 let reserve =
1240 T::ReserveProvider::reserve(asset).ok_or(Error::<T>::AssetHasNoReserve)?;
1241
1242 ensure!(reserve == dest, Error::<T>::AssetIsNotReserveInDestination);
1244
1245 Ok(dest)
1246 }
1247
1248 fn weight_of_initiate_reserve_withdraw() -> Weight {
1250 let dest = Location::parent();
1251
1252 let asset = Location::parent();
1254
1255 let fee = Asset {
1257 id: AssetId(asset.clone()),
1258 fun: Fungible(0),
1259 };
1260
1261 let xcm: Xcm<()> = Xcm(vec![
1262 WithdrawAsset(fee.into()),
1263 InitiateReserveWithdraw {
1264 assets: AssetFilter::Wild(All),
1265 reserve: dest.clone(),
1266 xcm: Xcm(vec![]),
1267 },
1268 ]);
1269 T::Weigher::weight(&mut xcm.into(), Weight::MAX)
1270 .map_or(Weight::MAX, |w| T::BaseXcmWeight::get().saturating_add(w))
1271 }
1272
1273 pub fn calculate_fee_per_second(weight: Weight, fee_per_second: u128) -> u128 {
1276 let weight_per_second_u128 = WEIGHT_REF_TIME_PER_SECOND as u128;
1278
1279 let fee_mul_rounded_up = (fee_per_second.saturating_mul(weight.ref_time() as u128))
1282 .saturating_add(weight_per_second_u128 - 1);
1283
1284 fee_mul_rounded_up / weight_per_second_u128
1285 }
1286
1287 pub fn take_weight_from_transact_info(
1290 dest: Location,
1291 dest_weight: Weight,
1292 refund: bool,
1293 ) -> Result<Weight, DispatchError> {
1294 ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
1297 let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
1299 .ok_or(Error::<T>::TransactorInfoNotSet)?;
1300
1301 let total_weight = dest_weight
1302 .checked_add(&transactor_info.transact_extra_weight)
1303 .ok_or(Error::<T>::WeightOverflow)?;
1304
1305 ensure!(
1306 total_weight.all_lte(transactor_info.max_weight),
1307 Error::<T>::MaxWeightTransactReached
1308 );
1309 Ok(total_weight)
1310 }
1311
1312 pub fn take_weight_from_transact_info_signed(
1315 dest: Location,
1316 dest_weight: Weight,
1317 refund: bool,
1318 ) -> Result<Weight, DispatchError> {
1319 ensure!(!refund, Error::<T>::RefundNotSupportedWithTransactInfo);
1322 let transactor_info = TransactInfoWithWeightLimit::<T>::get(&dest)
1324 .ok_or(Error::<T>::TransactorInfoNotSet)?;
1325
1326 let transact_in_dest_as_signed_weight = transactor_info
1329 .transact_extra_weight_signed
1330 .ok_or(Error::<T>::SignedTransactNotAllowedForDestination)?;
1331
1332 let total_weight = dest_weight
1333 .checked_add(&transact_in_dest_as_signed_weight)
1334 .ok_or(Error::<T>::WeightOverflow)?;
1335
1336 ensure!(
1337 total_weight.all_lte(transactor_info.max_weight),
1338 Error::<T>::MaxWeightTransactReached
1339 );
1340 Ok(total_weight)
1341 }
1342
1343 pub fn take_fee_per_second_from_storage(
1346 fee_location: Location,
1347 destination: Location,
1348 total_weight: Weight,
1349 ) -> Result<u128, DispatchError> {
1350 let fee_per_second = DestinationAssetFeePerSecond::<T>::get(&fee_location)
1351 .ok_or(Error::<T>::FeePerSecondNotSet)?;
1352
1353 Self::transfer_allowed(&(fee_location, fee_per_second).into(), &destination)?;
1357
1358 Ok(Self::calculate_fee_per_second(total_weight, fee_per_second))
1359 }
1360
1361 pub fn currency_to_multilocation(currency: Currency<CurrencyIdOf<T>>) -> Option<Location> {
1363 match currency {
1364 Currency::AsCurrencyId(id) => T::CurrencyIdToLocation::convert(id),
1365 Currency::AsMultiLocation(multiloc) => Location::try_from(*multiloc).ok(),
1366 }
1367 }
1368 }
1369}