1#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "runtime-benchmarks")]
22pub mod benchmarking;
23#[cfg(test)]
24mod mock;
25#[cfg(test)]
26mod tests;
27
28pub mod migration;
29
30extern crate alloc;
31
32use alloc::{boxed::Box, vec, vec::Vec};
33use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
34use core::{marker::PhantomData, result::Result};
35use frame_support::{
36 dispatch::{
37 DispatchErrorWithPostInfo, GetDispatchInfo, PostDispatchInfo, WithPostDispatchInfo,
38 },
39 pallet_prelude::*,
40 traits::{
41 Consideration, Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Footprint, Get,
42 LockableCurrency, OriginTrait, WithdrawReasons,
43 },
44 PalletId,
45};
46use frame_system::pallet_prelude::{BlockNumberFor, *};
47pub use pallet::*;
48use scale_info::TypeInfo;
49use sp_core::H256;
50use sp_runtime::{
51 traits::{
52 AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
53 Saturating, Zero,
54 },
55 Either, RuntimeDebug, SaturatedConversion,
56};
57use xcm::{latest::QueryResponseInfo, prelude::*};
58use xcm_builder::{
59 ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
60 QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
61};
62use xcm_executor::{
63 traits::{
64 AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
65 DropAssets, EventEmitter, FeeManager, FeeReason, MatchesFungible, OnResponse, Properties,
66 QueryHandler, QueryResponseStatus, RecordXcm, TransactAsset, TransferType,
67 VersionChangeNotifier, WeightBounds, XcmAssetTransfers,
68 },
69 AssetsInHolding,
70};
71use xcm_runtime_apis::{
72 authorized_aliases::{Error as AuthorizedAliasersApiError, OriginAliaser},
73 dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
74 fees::Error as XcmPaymentApiError,
75 trusted_query::Error as TrustedQueryApiError,
76};
77
78#[cfg(any(feature = "try-runtime", test))]
79use sp_runtime::TryRuntimeError;
80
81pub trait WeightInfo {
82 fn send() -> Weight;
83 fn teleport_assets() -> Weight;
84 fn reserve_transfer_assets() -> Weight;
85 fn transfer_assets() -> Weight;
86 fn execute() -> Weight;
87 fn force_xcm_version() -> Weight;
88 fn force_default_xcm_version() -> Weight;
89 fn force_subscribe_version_notify() -> Weight;
90 fn force_unsubscribe_version_notify() -> Weight;
91 fn force_suspension() -> Weight;
92 fn migrate_supported_version() -> Weight;
93 fn migrate_version_notifiers() -> Weight;
94 fn already_notified_target() -> Weight;
95 fn notify_current_targets() -> Weight;
96 fn notify_target_migration_fail() -> Weight;
97 fn migrate_version_notify_targets() -> Weight;
98 fn migrate_and_notify_old_targets() -> Weight;
99 fn new_query() -> Weight;
100 fn take_response() -> Weight;
101 fn claim_assets() -> Weight;
102 fn add_authorized_alias() -> Weight;
103 fn remove_authorized_alias() -> Weight;
104}
105
106pub struct TestWeightInfo;
108impl WeightInfo for TestWeightInfo {
109 fn send() -> Weight {
110 Weight::from_parts(100_000_000, 0)
111 }
112
113 fn teleport_assets() -> Weight {
114 Weight::from_parts(100_000_000, 0)
115 }
116
117 fn reserve_transfer_assets() -> Weight {
118 Weight::from_parts(100_000_000, 0)
119 }
120
121 fn transfer_assets() -> Weight {
122 Weight::from_parts(100_000_000, 0)
123 }
124
125 fn execute() -> Weight {
126 Weight::from_parts(100_000_000, 0)
127 }
128
129 fn force_xcm_version() -> Weight {
130 Weight::from_parts(100_000_000, 0)
131 }
132
133 fn force_default_xcm_version() -> Weight {
134 Weight::from_parts(100_000_000, 0)
135 }
136
137 fn force_subscribe_version_notify() -> Weight {
138 Weight::from_parts(100_000_000, 0)
139 }
140
141 fn force_unsubscribe_version_notify() -> Weight {
142 Weight::from_parts(100_000_000, 0)
143 }
144
145 fn force_suspension() -> Weight {
146 Weight::from_parts(100_000_000, 0)
147 }
148
149 fn migrate_supported_version() -> Weight {
150 Weight::from_parts(100_000_000, 0)
151 }
152
153 fn migrate_version_notifiers() -> Weight {
154 Weight::from_parts(100_000_000, 0)
155 }
156
157 fn already_notified_target() -> Weight {
158 Weight::from_parts(100_000_000, 0)
159 }
160
161 fn notify_current_targets() -> Weight {
162 Weight::from_parts(100_000_000, 0)
163 }
164
165 fn notify_target_migration_fail() -> Weight {
166 Weight::from_parts(100_000_000, 0)
167 }
168
169 fn migrate_version_notify_targets() -> Weight {
170 Weight::from_parts(100_000_000, 0)
171 }
172
173 fn migrate_and_notify_old_targets() -> Weight {
174 Weight::from_parts(100_000_000, 0)
175 }
176
177 fn new_query() -> Weight {
178 Weight::from_parts(100_000_000, 0)
179 }
180
181 fn take_response() -> Weight {
182 Weight::from_parts(100_000_000, 0)
183 }
184
185 fn claim_assets() -> Weight {
186 Weight::from_parts(100_000_000, 0)
187 }
188
189 fn add_authorized_alias() -> Weight {
190 Weight::from_parts(100_000, 0)
191 }
192
193 fn remove_authorized_alias() -> Weight {
194 Weight::from_parts(100_000, 0)
195 }
196}
197
198#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
199pub struct AuthorizedAliasesEntry<Ticket, MAX: Get<u32>> {
200 pub aliasers: BoundedVec<OriginAliaser, MAX>,
201 pub ticket: Ticket,
202}
203
204pub fn aliasers_footprint(aliasers_count: usize) -> Footprint {
205 Footprint::from_parts(aliasers_count, OriginAliaser::max_encoded_len())
206}
207
208#[frame_support::pallet]
209pub mod pallet {
210 use super::*;
211 use frame_support::{
212 dispatch::{GetDispatchInfo, PostDispatchInfo},
213 parameter_types,
214 };
215 use frame_system::Config as SysConfig;
216 use sp_runtime::traits::Dispatchable;
217 use xcm_executor::traits::{MatchesFungible, WeightBounds};
218
219 parameter_types! {
220 pub const CurrentXcmVersion: u32 = XCM_VERSION;
223
224 #[derive(Debug, TypeInfo)]
225 pub const MaxAuthorizedAliases: u32 = 10;
227 }
228
229 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
230
231 #[pallet::pallet]
232 #[pallet::storage_version(STORAGE_VERSION)]
233 #[pallet::without_storage_info]
234 pub struct Pallet<T>(_);
235
236 pub type BalanceOf<T> =
237 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
238 pub type TicketOf<T> = <T as Config>::AuthorizedAliasConsideration;
239
240 #[pallet::config]
241 pub trait Config: frame_system::Config {
243 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
245
246 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
249
250 type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
252
253 type AuthorizedAliasConsideration: Consideration<Self::AccountId, Footprint>;
255
256 type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
259
260 type XcmRouter: SendXcm;
262
263 type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
267
268 type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
270
271 type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers + FeeManager;
273
274 type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
276
277 type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
280
281 type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
283
284 type UniversalLocation: Get<InteriorLocation>;
286
287 type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
289
290 type RuntimeCall: Parameter
292 + GetDispatchInfo
293 + Dispatchable<
294 RuntimeOrigin = <Self as Config>::RuntimeOrigin,
295 PostInfo = PostDispatchInfo,
296 >;
297
298 const VERSION_DISCOVERY_QUEUE_SIZE: u32;
299
300 type AdvertisedXcmVersion: Get<XcmVersion>;
303
304 type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
306
307 type TrustedLockers: ContainsPair<Location, Asset>;
310
311 type SovereignAccountOf: ConvertLocation<Self::AccountId>;
313
314 type MaxLockers: Get<u32>;
316
317 type MaxRemoteLockConsumers: Get<u32>;
319
320 type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
322
323 type WeightInfo: WeightInfo;
325 }
326
327 impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
328 fn execute() -> Weight {
329 T::WeightInfo::execute()
330 }
331 }
332
333 impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
334 type WeightInfo = Self;
335 fn execute(
336 origin: OriginFor<T>,
337 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
338 max_weight: Weight,
339 ) -> Result<Weight, DispatchErrorWithPostInfo> {
340 tracing::trace!(target: "xcm::pallet_xcm::execute", ?message, ?max_weight);
341 let outcome = (|| {
342 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
343 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
344 let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
345 let value = (origin_location, message);
346 ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
347 let (origin_location, message) = value;
348 Ok(T::XcmExecutor::prepare_and_execute(
349 origin_location,
350 message,
351 &mut hash,
352 max_weight,
353 max_weight,
354 ))
355 })()
356 .map_err(|e: DispatchError| {
357 e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
358 })?;
359
360 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
361 let weight_used = outcome.weight_used();
362 outcome.ensure_complete().map_err(|error| {
363 tracing::error!(target: "xcm::pallet_xcm::execute", ?error, "XCM execution failed with error");
364 Error::<T>::LocalExecutionIncomplete.with_weight(
365 weight_used.saturating_add(
366 <Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
367 ),
368 )
369 })?;
370 Ok(weight_used)
371 }
372 }
373
374 impl<T: Config> SendControllerWeightInfo for Pallet<T> {
375 fn send() -> Weight {
376 T::WeightInfo::send()
377 }
378 }
379
380 impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
381 type WeightInfo = Self;
382 fn send(
383 origin: OriginFor<T>,
384 dest: Box<VersionedLocation>,
385 message: Box<VersionedXcm<()>>,
386 ) -> Result<XcmHash, DispatchError> {
387 let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
388 let interior: Junctions =
389 origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
390 let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
391 let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
392
393 let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
394 .map_err(|error| {
395 tracing::error!(target: "xcm::pallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
396 Error::<T>::from(error)
397 })?;
398 let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
399 Self::deposit_event(e);
400 Ok(message_id)
401 }
402 }
403
404 impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
405 fn query() -> Weight {
406 T::WeightInfo::new_query()
407 }
408 fn take_response() -> Weight {
409 T::WeightInfo::take_response()
410 }
411 }
412
413 impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
414 type WeightInfo = Self;
415
416 fn query(
417 origin: OriginFor<T>,
418 timeout: BlockNumberFor<T>,
419 match_querier: VersionedLocation,
420 ) -> Result<QueryId, DispatchError> {
421 let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
422 let query_id = <Self as QueryHandler>::new_query(
423 responder,
424 timeout,
425 Location::try_from(match_querier)
426 .map_err(|_| Into::<DispatchError>::into(Error::<T>::BadVersion))?,
427 );
428
429 Ok(query_id)
430 }
431 }
432
433 impl<T: Config> EventEmitter for Pallet<T> {
434 fn emit_sent_event(
435 origin: Location,
436 destination: Location,
437 message: Option<Xcm<()>>,
438 message_id: XcmHash,
439 ) {
440 Self::deposit_event(Event::Sent {
441 origin,
442 destination,
443 message: message.unwrap_or_default(),
444 message_id,
445 });
446 }
447
448 fn emit_send_failure_event(
449 origin: Location,
450 destination: Location,
451 error: SendError,
452 message_id: XcmHash,
453 ) {
454 Self::deposit_event(Event::SendFailed { origin, destination, error, message_id });
455 }
456
457 fn emit_process_failure_event(origin: Location, error: XcmError, message_id: XcmHash) {
458 Self::deposit_event(Event::ProcessXcmError { origin, error, message_id });
459 }
460 }
461
462 #[pallet::event]
463 #[pallet::generate_deposit(pub(super) fn deposit_event)]
464 pub enum Event<T: Config> {
465 Attempted { outcome: xcm::latest::Outcome },
467 Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
469 SendFailed {
471 origin: Location,
472 destination: Location,
473 error: SendError,
474 message_id: XcmHash,
475 },
476 ProcessXcmError { origin: Location, error: XcmError, message_id: XcmHash },
478 UnexpectedResponse { origin: Location, query_id: QueryId },
482 ResponseReady { query_id: QueryId, response: Response },
485 Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
488 NotifyOverweight {
492 query_id: QueryId,
493 pallet_index: u8,
494 call_index: u8,
495 actual_weight: Weight,
496 max_budgeted_weight: Weight,
497 },
498 NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
501 NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
505 InvalidResponder {
509 origin: Location,
510 query_id: QueryId,
511 expected_location: Option<Location>,
512 },
513 InvalidResponderVersion { origin: Location, query_id: QueryId },
521 ResponseTaken { query_id: QueryId },
523 AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
525 VersionChangeNotified {
529 destination: Location,
530 result: XcmVersion,
531 cost: Assets,
532 message_id: XcmHash,
533 },
534 SupportedVersionChanged { location: Location, version: XcmVersion },
537 NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
540 NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
543 InvalidQuerierVersion { origin: Location, query_id: QueryId },
551 InvalidQuerier {
555 origin: Location,
556 query_id: QueryId,
557 expected_querier: Location,
558 maybe_actual_querier: Option<Location>,
559 },
560 VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
563 VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
565 VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
568 FeesPaid { paying: Location, fees: Assets },
570 AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
572 VersionMigrationFinished { version: XcmVersion },
574 AliasAuthorized { aliaser: Location, target: Location, expiry: Option<u64> },
577 AliasAuthorizationRemoved { aliaser: Location, target: Location },
579 AliasesAuthorizationsRemoved { target: Location },
581 }
582
583 #[pallet::origin]
584 #[derive(
585 PartialEq,
586 Eq,
587 Clone,
588 Encode,
589 Decode,
590 DecodeWithMemTracking,
591 RuntimeDebug,
592 TypeInfo,
593 MaxEncodedLen,
594 )]
595 pub enum Origin {
596 Xcm(Location),
598 Response(Location),
600 }
601 impl From<Location> for Origin {
602 fn from(location: Location) -> Origin {
603 Origin::Xcm(location)
604 }
605 }
606
607 #[pallet::composite_enum]
609 pub enum HoldReason {
610 AuthorizeAlias,
612 }
613
614 #[pallet::error]
615 pub enum Error<T> {
616 Unreachable,
619 SendFailure,
622 Filtered,
624 UnweighableMessage,
626 DestinationNotInvertible,
628 Empty,
630 CannotReanchor,
632 TooManyAssets,
634 InvalidOrigin,
636 BadVersion,
638 BadLocation,
641 NoSubscription,
643 AlreadySubscribed,
645 CannotCheckOutTeleport,
647 LowBalance,
649 TooManyLocks,
651 AccountNotSovereign,
653 FeesNotMet,
655 LockNotFound,
657 InUse,
659 #[codec(index = 21)]
661 InvalidAssetUnknownReserve,
662 #[codec(index = 22)]
664 InvalidAssetUnsupportedReserve,
665 #[codec(index = 23)]
667 TooManyReserves,
668 #[codec(index = 24)]
670 LocalExecutionIncomplete,
671 #[codec(index = 25)]
673 TooManyAuthorizedAliases,
674 #[codec(index = 26)]
676 ExpiresInPast,
677 #[codec(index = 27)]
679 AliasNotFound,
680 }
681
682 impl<T: Config> From<SendError> for Error<T> {
683 fn from(e: SendError) -> Self {
684 match e {
685 SendError::Fees => Error::<T>::FeesNotMet,
686 SendError::NotApplicable => Error::<T>::Unreachable,
687 _ => Error::<T>::SendFailure,
688 }
689 }
690 }
691
692 impl<T: Config> From<AssetTransferError> for Error<T> {
693 fn from(e: AssetTransferError) -> Self {
694 match e {
695 AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
696 }
697 }
698 }
699
700 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
702 pub enum QueryStatus<BlockNumber> {
703 Pending {
705 responder: VersionedLocation,
708 maybe_match_querier: Option<VersionedLocation>,
711 maybe_notify: Option<(u8, u8)>,
712 timeout: BlockNumber,
713 },
714 VersionNotifier { origin: VersionedLocation, is_active: bool },
716 Ready { response: VersionedResponse, at: BlockNumber },
718 }
719
720 #[derive(Copy, Clone)]
721 pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
722 impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
723 impl<'a> Encode for LatestVersionedLocation<'a> {
724 fn encode(&self) -> Vec<u8> {
725 let mut r = VersionedLocation::from(Location::default()).encode();
726 r.truncate(1);
727 self.0.using_encoded(|d| r.extend_from_slice(d));
728 r
729 }
730 }
731
732 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
733 pub enum VersionMigrationStage {
734 MigrateSupportedVersion,
735 MigrateVersionNotifiers,
736 NotifyCurrentTargets(Option<Vec<u8>>),
737 MigrateAndNotifyOldTargets,
738 }
739
740 impl Default for VersionMigrationStage {
741 fn default() -> Self {
742 Self::MigrateSupportedVersion
743 }
744 }
745
746 #[pallet::storage]
748 pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
749
750 #[pallet::storage]
752 pub(super) type Queries<T: Config> =
753 StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
754
755 #[pallet::storage]
760 pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
761
762 #[pallet::storage]
765 #[pallet::whitelist_storage]
766 pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
767
768 #[pallet::storage]
770 pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
771 _,
772 Twox64Concat,
773 XcmVersion,
774 Blake2_128Concat,
775 VersionedLocation,
776 XcmVersion,
777 OptionQuery,
778 >;
779
780 #[pallet::storage]
782 pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
783 _,
784 Twox64Concat,
785 XcmVersion,
786 Blake2_128Concat,
787 VersionedLocation,
788 QueryId,
789 OptionQuery,
790 >;
791
792 #[pallet::storage]
795 pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
796 _,
797 Twox64Concat,
798 XcmVersion,
799 Blake2_128Concat,
800 VersionedLocation,
801 (QueryId, Weight, XcmVersion),
802 OptionQuery,
803 >;
804
805 pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
806 impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
807 fn get() -> u32 {
808 T::VERSION_DISCOVERY_QUEUE_SIZE
809 }
810 }
811
812 #[pallet::storage]
816 #[pallet::whitelist_storage]
817 pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
818 _,
819 BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
820 ValueQuery,
821 >;
822
823 #[pallet::storage]
825 pub(super) type CurrentMigration<T: Config> =
826 StorageValue<_, VersionMigrationStage, OptionQuery>;
827
828 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
829 #[scale_info(skip_type_params(MaxConsumers))]
830 pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
831 pub amount: u128,
833 pub owner: VersionedLocation,
835 pub locker: VersionedLocation,
837 pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
841 }
842
843 impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
844 pub fn amount_held(&self) -> Option<u128> {
847 self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
848 }
849 }
850
851 #[pallet::storage]
853 pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
854 _,
855 (
856 NMapKey<Twox64Concat, XcmVersion>,
857 NMapKey<Blake2_128Concat, T::AccountId>,
858 NMapKey<Blake2_128Concat, VersionedAssetId>,
859 ),
860 RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
861 OptionQuery,
862 >;
863
864 #[pallet::storage]
866 pub(super) type LockedFungibles<T: Config> = StorageMap<
867 _,
868 Blake2_128Concat,
869 T::AccountId,
870 BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
871 OptionQuery,
872 >;
873
874 #[pallet::storage]
876 pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
877
878 #[pallet::storage]
886 pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
887
888 #[pallet::storage]
895 pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
896
897 #[pallet::storage]
901 pub(super) type AuthorizedAliases<T: Config> = StorageMap<
902 _,
903 Blake2_128Concat,
904 VersionedLocation,
905 AuthorizedAliasesEntry<TicketOf<T>, MaxAuthorizedAliases>,
906 OptionQuery,
907 >;
908
909 #[pallet::genesis_config]
910 pub struct GenesisConfig<T: Config> {
911 #[serde(skip)]
912 pub _config: core::marker::PhantomData<T>,
913 pub safe_xcm_version: Option<XcmVersion>,
915 }
916
917 impl<T: Config> Default for GenesisConfig<T> {
918 fn default() -> Self {
919 Self { safe_xcm_version: Some(XCM_VERSION), _config: Default::default() }
920 }
921 }
922
923 #[pallet::genesis_build]
924 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
925 fn build(&self) {
926 SafeXcmVersion::<T>::set(self.safe_xcm_version);
927 }
928 }
929
930 #[pallet::hooks]
931 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
932 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
933 let mut weight_used = Weight::zero();
934 if let Some(migration) = CurrentMigration::<T>::get() {
935 let max_weight = T::BlockWeights::get().max_block / 10;
937 let (w, maybe_migration) = Self::lazy_migration(migration, max_weight);
938 if maybe_migration.is_none() {
939 Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
940 }
941 CurrentMigration::<T>::set(maybe_migration);
942 weight_used.saturating_accrue(w);
943 }
944
945 let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
948 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
950 q.sort_by_key(|i| i.1);
951 while let Some((versioned_dest, _)) = q.pop() {
952 if let Ok(dest) = Location::try_from(versioned_dest) {
953 if Self::request_version_notify(dest).is_ok() {
954 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
956 break
957 }
958 }
959 }
960 if let Ok(q) = BoundedVec::try_from(q) {
963 VersionDiscoveryQueue::<T>::put(q);
964 }
965 weight_used
966 }
967
968 #[cfg(feature = "try-runtime")]
969 fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
970 Self::do_try_state()
971 }
972 }
973
974 pub mod migrations {
975 use super::*;
976 use frame_support::traits::{PalletInfoAccess, StorageVersion};
977
978 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
979 enum QueryStatusV0<BlockNumber> {
980 Pending {
981 responder: VersionedLocation,
982 maybe_notify: Option<(u8, u8)>,
983 timeout: BlockNumber,
984 },
985 VersionNotifier {
986 origin: VersionedLocation,
987 is_active: bool,
988 },
989 Ready {
990 response: VersionedResponse,
991 at: BlockNumber,
992 },
993 }
994 impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
995 fn from(old: QueryStatusV0<B>) -> Self {
996 use QueryStatusV0::*;
997 match old {
998 Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
999 responder,
1000 maybe_notify,
1001 timeout,
1002 maybe_match_querier: Some(Location::here().into()),
1003 },
1004 VersionNotifier { origin, is_active } =>
1005 QueryStatus::VersionNotifier { origin, is_active },
1006 Ready { response, at } => QueryStatus::Ready { response, at },
1007 }
1008 }
1009 }
1010
1011 pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
1012 ) -> frame_support::weights::Weight {
1013 let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
1014 tracing::info!(
1015 target: "runtime::xcm",
1016 ?on_chain_storage_version,
1017 "Running migration storage v1 for xcm with storage version",
1018 );
1019
1020 if on_chain_storage_version < 1 {
1021 let mut count = 0;
1022 Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
1023 count += 1;
1024 Some(value.into())
1025 });
1026 StorageVersion::new(1).put::<P>();
1027 tracing::info!(
1028 target: "runtime::xcm",
1029 ?on_chain_storage_version,
1030 "Running migration storage v1 for xcm with storage version was complete",
1031 );
1032 T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
1034 } else {
1035 tracing::warn!(
1036 target: "runtime::xcm",
1037 ?on_chain_storage_version,
1038 "Attempted to apply migration to v1 but failed because storage version is",
1039 );
1040 T::DbWeight::get().reads(1)
1041 }
1042 }
1043 }
1044
1045 #[pallet::call(weight(<T as Config>::WeightInfo))]
1046 impl<T: Config> Pallet<T> {
1047 #[pallet::call_index(0)]
1048 pub fn send(
1049 origin: OriginFor<T>,
1050 dest: Box<VersionedLocation>,
1051 message: Box<VersionedXcm<()>>,
1052 ) -> DispatchResult {
1053 <Self as SendController<_>>::send(origin, dest, message)?;
1054 Ok(())
1055 }
1056
1057 #[pallet::call_index(1)]
1076 #[allow(deprecated)]
1077 #[deprecated(
1078 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
1079 )]
1080 pub fn teleport_assets(
1081 origin: OriginFor<T>,
1082 dest: Box<VersionedLocation>,
1083 beneficiary: Box<VersionedLocation>,
1084 assets: Box<VersionedAssets>,
1085 fee_asset_item: u32,
1086 ) -> DispatchResult {
1087 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
1088 }
1089
1090 #[pallet::call_index(2)]
1121 #[allow(deprecated)]
1122 #[deprecated(
1123 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1124 )]
1125 pub fn reserve_transfer_assets(
1126 origin: OriginFor<T>,
1127 dest: Box<VersionedLocation>,
1128 beneficiary: Box<VersionedLocation>,
1129 assets: Box<VersionedAssets>,
1130 fee_asset_item: u32,
1131 ) -> DispatchResult {
1132 Self::do_reserve_transfer_assets(
1133 origin,
1134 dest,
1135 beneficiary,
1136 assets,
1137 fee_asset_item,
1138 Unlimited,
1139 )
1140 }
1141
1142 #[pallet::call_index(3)]
1151 #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1152 pub fn execute(
1153 origin: OriginFor<T>,
1154 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1155 max_weight: Weight,
1156 ) -> DispatchResultWithPostInfo {
1157 let weight_used =
1158 <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1159 Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1160 }
1161
1162 #[pallet::call_index(4)]
1169 pub fn force_xcm_version(
1170 origin: OriginFor<T>,
1171 location: Box<Location>,
1172 version: XcmVersion,
1173 ) -> DispatchResult {
1174 T::AdminOrigin::ensure_origin(origin)?;
1175 let location = *location;
1176 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1177 Self::deposit_event(Event::SupportedVersionChanged { location, version });
1178 Ok(())
1179 }
1180
1181 #[pallet::call_index(5)]
1187 pub fn force_default_xcm_version(
1188 origin: OriginFor<T>,
1189 maybe_xcm_version: Option<XcmVersion>,
1190 ) -> DispatchResult {
1191 T::AdminOrigin::ensure_origin(origin)?;
1192 SafeXcmVersion::<T>::set(maybe_xcm_version);
1193 Ok(())
1194 }
1195
1196 #[pallet::call_index(6)]
1201 pub fn force_subscribe_version_notify(
1202 origin: OriginFor<T>,
1203 location: Box<VersionedLocation>,
1204 ) -> DispatchResult {
1205 T::AdminOrigin::ensure_origin(origin)?;
1206 let location: Location =
1207 (*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
1208 Self::request_version_notify(location).map_err(|e| {
1209 match e {
1210 XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1211 _ => Error::<T>::InvalidOrigin,
1212 }
1213 .into()
1214 })
1215 }
1216
1217 #[pallet::call_index(7)]
1224 pub fn force_unsubscribe_version_notify(
1225 origin: OriginFor<T>,
1226 location: Box<VersionedLocation>,
1227 ) -> DispatchResult {
1228 T::AdminOrigin::ensure_origin(origin)?;
1229 let location: Location =
1230 (*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
1231 Self::unrequest_version_notify(location).map_err(|e| {
1232 match e {
1233 XcmError::InvalidLocation => Error::<T>::NoSubscription,
1234 _ => Error::<T>::InvalidOrigin,
1235 }
1236 .into()
1237 })
1238 }
1239
1240 #[pallet::call_index(8)]
1271 #[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
1272 pub fn limited_reserve_transfer_assets(
1273 origin: OriginFor<T>,
1274 dest: Box<VersionedLocation>,
1275 beneficiary: Box<VersionedLocation>,
1276 assets: Box<VersionedAssets>,
1277 fee_asset_item: u32,
1278 weight_limit: WeightLimit,
1279 ) -> DispatchResult {
1280 Self::do_reserve_transfer_assets(
1281 origin,
1282 dest,
1283 beneficiary,
1284 assets,
1285 fee_asset_item,
1286 weight_limit,
1287 )
1288 }
1289
1290 #[pallet::call_index(9)]
1309 #[pallet::weight(T::WeightInfo::teleport_assets())]
1310 pub fn limited_teleport_assets(
1311 origin: OriginFor<T>,
1312 dest: Box<VersionedLocation>,
1313 beneficiary: Box<VersionedLocation>,
1314 assets: Box<VersionedAssets>,
1315 fee_asset_item: u32,
1316 weight_limit: WeightLimit,
1317 ) -> DispatchResult {
1318 Self::do_teleport_assets(
1319 origin,
1320 dest,
1321 beneficiary,
1322 assets,
1323 fee_asset_item,
1324 weight_limit,
1325 )
1326 }
1327
1328 #[pallet::call_index(10)]
1333 pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1334 T::AdminOrigin::ensure_origin(origin)?;
1335 XcmExecutionSuspended::<T>::set(suspended);
1336 Ok(())
1337 }
1338
1339 #[pallet::call_index(11)]
1373 pub fn transfer_assets(
1374 origin: OriginFor<T>,
1375 dest: Box<VersionedLocation>,
1376 beneficiary: Box<VersionedLocation>,
1377 assets: Box<VersionedAssets>,
1378 fee_asset_item: u32,
1379 weight_limit: WeightLimit,
1380 ) -> DispatchResult {
1381 let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1382 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1383 let beneficiary: Location =
1384 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1385 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1386 tracing::debug!(
1387 target: "xcm::pallet_xcm::transfer_assets",
1388 ?origin, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1389 );
1390
1391 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1392 let assets = assets.into_inner();
1393 let fee_asset_item = fee_asset_item as usize;
1394 let (fees_transfer_type, assets_transfer_type) =
1396 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1397
1398 Self::do_transfer_assets(
1399 origin,
1400 dest,
1401 Either::Left(beneficiary),
1402 assets,
1403 assets_transfer_type,
1404 fee_asset_item,
1405 fees_transfer_type,
1406 weight_limit,
1407 )
1408 }
1409
1410 #[pallet::call_index(12)]
1417 pub fn claim_assets(
1418 origin: OriginFor<T>,
1419 assets: Box<VersionedAssets>,
1420 beneficiary: Box<VersionedLocation>,
1421 ) -> DispatchResult {
1422 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1423 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?origin_location, ?assets, ?beneficiary);
1424 let assets_version = assets.identify_version();
1426 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1427 let number_of_assets = assets.len() as u32;
1428 let beneficiary: Location =
1429 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1430 let ticket: Location = GeneralIndex(assets_version as u128).into();
1431 let mut message = Xcm(vec![
1432 ClaimAsset { assets, ticket },
1433 DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1434 ]);
1435 let weight =
1436 T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
1437 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
1438 let outcome = T::XcmExecutor::prepare_and_execute(
1439 origin_location,
1440 message,
1441 &mut hash,
1442 weight,
1443 weight,
1444 );
1445 outcome.ensure_complete().map_err(|error| {
1446 tracing::error!(target: "xcm::pallet_xcm::claim_assets", ?error, "XCM execution failed with error");
1447 Error::<T>::LocalExecutionIncomplete
1448 })?;
1449 Ok(())
1450 }
1451
1452 #[pallet::call_index(13)]
1501 #[pallet::weight(T::WeightInfo::transfer_assets())]
1502 pub fn transfer_assets_using_type_and_then(
1503 origin: OriginFor<T>,
1504 dest: Box<VersionedLocation>,
1505 assets: Box<VersionedAssets>,
1506 assets_transfer_type: Box<TransferType>,
1507 remote_fees_id: Box<VersionedAssetId>,
1508 fees_transfer_type: Box<TransferType>,
1509 custom_xcm_on_dest: Box<VersionedXcm<()>>,
1510 weight_limit: WeightLimit,
1511 ) -> DispatchResult {
1512 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1513 let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1514 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1515 let fees_id: AssetId =
1516 (*remote_fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?;
1517 let remote_xcm: Xcm<()> =
1518 (*custom_xcm_on_dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1519 tracing::debug!(
1520 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1521 ?origin_location, ?dest, ?assets, ?assets_transfer_type, ?fees_id, ?fees_transfer_type,
1522 ?remote_xcm, ?weight_limit,
1523 );
1524
1525 let assets = assets.into_inner();
1526 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1527
1528 let fee_asset_index =
1529 assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
1530 Self::do_transfer_assets(
1531 origin_location,
1532 dest,
1533 Either::Right(remote_xcm),
1534 assets,
1535 *assets_transfer_type,
1536 fee_asset_index,
1537 *fees_transfer_type,
1538 weight_limit,
1539 )
1540 }
1541
1542 #[pallet::call_index(14)]
1554 pub fn add_authorized_alias(
1555 origin: OriginFor<T>,
1556 aliaser: Box<VersionedLocation>,
1557 expires: Option<u64>,
1558 ) -> DispatchResult {
1559 let signed_origin = ensure_signed(origin.clone())?;
1560 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1561 let new_aliaser: Location =
1562 (*aliaser).try_into().map_err(|()| Error::<T>::BadVersion)?;
1563 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1564 let origin_location = match origin_location.unpack() {
1566 (0, [AccountId32 { network: _, id }]) =>
1567 Location::new(0, [AccountId32 { network: None, id: *id }]),
1568 _ => return Err(Error::<T>::InvalidOrigin.into()),
1569 };
1570 tracing::debug!(target: "xcm::pallet_xcm::add_authorized_alias", ?origin_location, ?new_aliaser, ?expires);
1571 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1572 if let Some(expiry) = expires {
1573 ensure!(
1574 expiry >
1575 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>(),
1576 Error::<T>::ExpiresInPast
1577 );
1578 }
1579 let versioned_origin = VersionedLocation::from(origin_location.clone());
1580 let versioned_aliaser = VersionedLocation::from(new_aliaser.clone());
1581 let entry = if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1582 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1584 if let Some(aliaser) =
1585 aliasers.iter_mut().find(|aliaser| aliaser.location == versioned_aliaser)
1586 {
1587 aliaser.expiry = expires;
1589 } else {
1590 let aliaser =
1592 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1593 aliasers.try_push(aliaser).map_err(|_| Error::<T>::TooManyAuthorizedAliases)?;
1594 ticket = ticket.update(&signed_origin, aliasers_footprint(aliasers.len()))?;
1596 }
1597 AuthorizedAliasesEntry { aliasers, ticket }
1598 } else {
1599 let ticket = TicketOf::<T>::new(&signed_origin, aliasers_footprint(1))?;
1601 let aliaser =
1602 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1603 let mut aliasers = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
1604 aliasers.try_push(aliaser).map_err(|_| Error::<T>::TooManyAuthorizedAliases)?;
1605 AuthorizedAliasesEntry { aliasers, ticket }
1606 };
1607 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1609 Self::deposit_event(Event::AliasAuthorized {
1610 aliaser: new_aliaser,
1611 target: origin_location,
1612 expiry: expires,
1613 });
1614 Ok(())
1615 }
1616
1617 #[pallet::call_index(15)]
1620 pub fn remove_authorized_alias(
1621 origin: OriginFor<T>,
1622 aliaser: Box<VersionedLocation>,
1623 ) -> DispatchResult {
1624 let signed_origin = ensure_signed(origin.clone())?;
1625 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1626 let to_remove: Location = (*aliaser).try_into().map_err(|()| Error::<T>::BadVersion)?;
1627 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1628 let origin_location = match origin_location.unpack() {
1630 (0, [AccountId32 { network: _, id }]) =>
1631 Location::new(0, [AccountId32 { network: None, id: *id }]),
1632 _ => return Err(Error::<T>::InvalidOrigin.into()),
1633 };
1634 tracing::debug!(target: "xcm::pallet_xcm::remove_authorized_alias", ?origin_location, ?to_remove);
1635 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1636 let versioned_origin = VersionedLocation::from(origin_location.clone());
1638 let versioned_to_remove = VersionedLocation::from(to_remove.clone());
1639 AuthorizedAliases::<T>::get(&versioned_origin)
1640 .ok_or(Error::<T>::AliasNotFound.into())
1641 .and_then(|entry| {
1642 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1643 let old_len = aliasers.len();
1644 aliasers.retain(|alias| versioned_to_remove.ne(&alias.location));
1645 let new_len = aliasers.len();
1646 if aliasers.is_empty() {
1647 ticket.drop(&signed_origin)?;
1649 AuthorizedAliases::<T>::remove(&versioned_origin);
1650 Self::deposit_event(Event::AliasAuthorizationRemoved {
1651 aliaser: to_remove,
1652 target: origin_location,
1653 });
1654 Ok(())
1655 } else if old_len != new_len {
1656 ticket = ticket.update(&signed_origin, aliasers_footprint(new_len))?;
1658 let entry = AuthorizedAliasesEntry { aliasers, ticket };
1659 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1660 Self::deposit_event(Event::AliasAuthorizationRemoved {
1661 aliaser: to_remove,
1662 target: origin_location,
1663 });
1664 Ok(())
1665 } else {
1666 Err(Error::<T>::AliasNotFound.into())
1667 }
1668 })
1669 }
1670
1671 #[pallet::call_index(16)]
1674 #[pallet::weight(T::WeightInfo::remove_authorized_alias())]
1675 pub fn remove_all_authorized_aliases(origin: OriginFor<T>) -> DispatchResult {
1676 let signed_origin = ensure_signed(origin.clone())?;
1677 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1678 let origin_location = match origin_location.unpack() {
1680 (0, [AccountId32 { network: _, id }]) =>
1681 Location::new(0, [AccountId32 { network: None, id: *id }]),
1682 _ => return Err(Error::<T>::InvalidOrigin.into()),
1683 };
1684 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", ?origin_location);
1685 let versioned_origin = VersionedLocation::from(origin_location.clone());
1687 if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1688 entry.ticket.drop(&signed_origin)?;
1690 AuthorizedAliases::<T>::remove(&versioned_origin);
1691 Self::deposit_event(Event::AliasesAuthorizationsRemoved {
1692 target: origin_location,
1693 });
1694 Ok(())
1695 } else {
1696 Err(Error::<T>::AliasNotFound.into())
1697 }
1698 }
1699 }
1700}
1701
1702const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1704
1705#[derive(Clone, PartialEq)]
1707enum FeesHandling<T: Config> {
1708 Batched { fees: Asset },
1710 Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1712}
1713
1714impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1715 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1716 match self {
1717 Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1718 Self::Separate { local_xcm, remote_xcm } => write!(
1719 f,
1720 "FeesHandling::Separate(local: {:?}, remote: {:?})",
1721 local_xcm, remote_xcm
1722 ),
1723 }
1724 }
1725}
1726
1727impl<T: Config> QueryHandler for Pallet<T> {
1728 type BlockNumber = BlockNumberFor<T>;
1729 type Error = XcmError;
1730 type UniversalLocation = T::UniversalLocation;
1731
1732 fn new_query(
1734 responder: impl Into<Location>,
1735 timeout: BlockNumberFor<T>,
1736 match_querier: impl Into<Location>,
1737 ) -> QueryId {
1738 Self::do_new_query(responder, None, timeout, match_querier)
1739 }
1740
1741 fn report_outcome(
1744 message: &mut Xcm<()>,
1745 responder: impl Into<Location>,
1746 timeout: Self::BlockNumber,
1747 ) -> Result<QueryId, Self::Error> {
1748 let responder = responder.into();
1749 let destination = Self::UniversalLocation::get()
1750 .invert_target(&responder)
1751 .map_err(|()| XcmError::LocationNotInvertible)?;
1752 let query_id = Self::new_query(responder, timeout, Here);
1753 let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1754 let report_error = Xcm(vec![ReportError(response_info)]);
1755 message.0.insert(0, SetAppendix(report_error));
1756 Ok(query_id)
1757 }
1758
1759 fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1761 match Queries::<T>::get(query_id) {
1762 Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1763 Ok(response) => {
1764 Queries::<T>::remove(query_id);
1765 Self::deposit_event(Event::ResponseTaken { query_id });
1766 QueryResponseStatus::Ready { response, at }
1767 },
1768 Err(_) => QueryResponseStatus::UnexpectedVersion,
1769 },
1770 Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
1771 Some(_) => QueryResponseStatus::UnexpectedVersion,
1772 None => QueryResponseStatus::NotFound,
1773 }
1774 }
1775
1776 #[cfg(feature = "runtime-benchmarks")]
1777 fn expect_response(id: QueryId, response: Response) {
1778 let response = response.into();
1779 Queries::<T>::insert(
1780 id,
1781 QueryStatus::Ready { response, at: frame_system::Pallet::<T>::current_block_number() },
1782 );
1783 }
1784}
1785
1786impl<T: Config> Pallet<T> {
1787 pub fn query(query_id: &QueryId) -> Option<QueryStatus<BlockNumberFor<T>>> {
1789 Queries::<T>::get(query_id)
1790 }
1791
1792 pub fn asset_trap(trap_id: &H256) -> u32 {
1798 AssetTraps::<T>::get(trap_id)
1799 }
1800
1801 fn find_fee_and_assets_transfer_types(
1806 assets: &[Asset],
1807 fee_asset_item: usize,
1808 dest: &Location,
1809 ) -> Result<(TransferType, TransferType), Error<T>> {
1810 let mut fees_transfer_type = None;
1811 let mut assets_transfer_type = None;
1812 for (idx, asset) in assets.iter().enumerate() {
1813 if let Fungible(x) = asset.fun {
1814 ensure!(!x.is_zero(), Error::<T>::Empty);
1816 }
1817 let transfer_type =
1818 T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
1819 if idx == fee_asset_item {
1820 fees_transfer_type = Some(transfer_type);
1821 } else {
1822 if let Some(existing) = assets_transfer_type.as_ref() {
1823 ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
1826 } else {
1827 assets_transfer_type = Some(transfer_type);
1829 }
1830 }
1831 }
1832 if assets.len() == 1 {
1834 assets_transfer_type = fees_transfer_type.clone()
1835 }
1836 Ok((
1837 fees_transfer_type.ok_or(Error::<T>::Empty)?,
1838 assets_transfer_type.ok_or(Error::<T>::Empty)?,
1839 ))
1840 }
1841
1842 fn do_reserve_transfer_assets(
1843 origin: OriginFor<T>,
1844 dest: Box<VersionedLocation>,
1845 beneficiary: Box<VersionedLocation>,
1846 assets: Box<VersionedAssets>,
1847 fee_asset_item: u32,
1848 weight_limit: WeightLimit,
1849 ) -> DispatchResult {
1850 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1851 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1852 let beneficiary: Location =
1853 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1854 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1855 tracing::debug!(
1856 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
1857 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item,
1858 );
1859
1860 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1861 let value = (origin_location, assets.into_inner());
1862 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1863 let (origin, assets) = value;
1864
1865 let fee_asset_item = fee_asset_item as usize;
1866 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1867
1868 let (fees_transfer_type, assets_transfer_type) =
1870 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1871 ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
1873 ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
1875
1876 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1877 origin.clone(),
1878 dest.clone(),
1879 Either::Left(beneficiary),
1880 assets,
1881 assets_transfer_type,
1882 FeesHandling::Batched { fees },
1883 weight_limit,
1884 )?;
1885 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1886 }
1887
1888 fn do_teleport_assets(
1889 origin: OriginFor<T>,
1890 dest: Box<VersionedLocation>,
1891 beneficiary: Box<VersionedLocation>,
1892 assets: Box<VersionedAssets>,
1893 fee_asset_item: u32,
1894 weight_limit: WeightLimit,
1895 ) -> DispatchResult {
1896 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1897 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1898 let beneficiary: Location =
1899 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1900 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1901 tracing::debug!(
1902 target: "xcm::pallet_xcm::do_teleport_assets",
1903 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1904 );
1905
1906 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1907 let value = (origin_location, assets.into_inner());
1908 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
1909 let (origin_location, assets) = value;
1910 for asset in assets.iter() {
1911 let transfer_type =
1912 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
1913 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
1914 }
1915 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1916
1917 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1918 origin_location.clone(),
1919 dest.clone(),
1920 Either::Left(beneficiary),
1921 assets,
1922 TransferType::Teleport,
1923 FeesHandling::Batched { fees },
1924 weight_limit,
1925 )?;
1926 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
1927 }
1928
1929 fn do_transfer_assets(
1930 origin: Location,
1931 dest: Location,
1932 beneficiary: Either<Location, Xcm<()>>,
1933 mut assets: Vec<Asset>,
1934 assets_transfer_type: TransferType,
1935 fee_asset_index: usize,
1936 fees_transfer_type: TransferType,
1937 weight_limit: WeightLimit,
1938 ) -> DispatchResult {
1939 let fees = if fees_transfer_type == assets_transfer_type {
1941 let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
1942 FeesHandling::Batched { fees }
1944 } else {
1945 ensure!(
1951 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
1952 Error::<T>::InvalidAssetUnsupportedReserve
1953 );
1954 let weight_limit = weight_limit.clone();
1955 let fees = assets.remove(fee_asset_index);
1958 let (local_xcm, remote_xcm) = match fees_transfer_type {
1959 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
1960 origin.clone(),
1961 dest.clone(),
1962 fees,
1963 weight_limit,
1964 )?,
1965 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
1966 origin.clone(),
1967 dest.clone(),
1968 fees,
1969 weight_limit,
1970 )?,
1971 TransferType::Teleport => Self::teleport_fees_instructions(
1972 origin.clone(),
1973 dest.clone(),
1974 fees,
1975 weight_limit,
1976 )?,
1977 TransferType::RemoteReserve(_) =>
1978 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
1979 };
1980 FeesHandling::Separate { local_xcm, remote_xcm }
1981 };
1982
1983 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1984 origin.clone(),
1985 dest.clone(),
1986 beneficiary,
1987 assets,
1988 assets_transfer_type,
1989 fees,
1990 weight_limit,
1991 )?;
1992 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1993 }
1994
1995 fn build_xcm_transfer_type(
1996 origin: Location,
1997 dest: Location,
1998 beneficiary: Either<Location, Xcm<()>>,
1999 assets: Vec<Asset>,
2000 transfer_type: TransferType,
2001 fees: FeesHandling<T>,
2002 weight_limit: WeightLimit,
2003 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
2004 tracing::debug!(
2005 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2006 ?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
2007 );
2008 match transfer_type {
2009 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
2010 origin.clone(),
2011 dest.clone(),
2012 beneficiary,
2013 assets,
2014 fees,
2015 weight_limit,
2016 )
2017 .map(|(local, remote)| (local, Some(remote))),
2018 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
2019 origin.clone(),
2020 dest.clone(),
2021 beneficiary,
2022 assets,
2023 fees,
2024 weight_limit,
2025 )
2026 .map(|(local, remote)| (local, Some(remote))),
2027 TransferType::RemoteReserve(reserve) => {
2028 let fees = match fees {
2029 FeesHandling::Batched { fees } => fees,
2030 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2031 };
2032 Self::remote_reserve_transfer_program(
2033 origin.clone(),
2034 reserve.try_into().map_err(|()| Error::<T>::BadVersion)?,
2035 beneficiary,
2036 dest.clone(),
2037 assets,
2038 fees,
2039 weight_limit,
2040 )
2041 .map(|local| (local, None))
2042 },
2043 TransferType::Teleport => Self::teleport_assets_program(
2044 origin.clone(),
2045 dest.clone(),
2046 beneficiary,
2047 assets,
2048 fees,
2049 weight_limit,
2050 )
2051 .map(|(local, remote)| (local, Some(remote))),
2052 }
2053 }
2054
2055 fn execute_xcm_transfer(
2056 origin: Location,
2057 dest: Location,
2058 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
2059 remote_xcm: Option<Xcm<()>>,
2060 ) -> DispatchResult {
2061 tracing::debug!(
2062 target: "xcm::pallet_xcm::execute_xcm_transfer",
2063 ?origin, ?dest, ?local_xcm, ?remote_xcm,
2064 );
2065
2066 let weight =
2067 T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?;
2068 let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
2069 let outcome = T::XcmExecutor::prepare_and_execute(
2070 origin.clone(),
2071 local_xcm,
2072 &mut hash,
2073 weight,
2074 weight,
2075 );
2076 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
2077 outcome.clone().ensure_complete().map_err(|error| {
2078 tracing::error!(
2079 target: "xcm::pallet_xcm::execute_xcm_transfer",
2080 ?error, "XCM execution failed with error with outcome: {:?}", outcome
2081 );
2082 Error::<T>::LocalExecutionIncomplete
2083 })?;
2084
2085 if let Some(remote_xcm) = remote_xcm {
2086 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
2087 .map_err(|error| {
2088 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
2089 Error::<T>::from(error)
2090 })?;
2091 if origin != Here.into_location() {
2092 Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
2093 tracing::error!(
2094 target: "xcm::pallet_xcm::execute_xcm_transfer",
2095 ?error, ?price, ?origin, "Unable to charge fee",
2096 );
2097 Error::<T>::FeesNotMet
2098 })?;
2099 }
2100 let message_id = T::XcmRouter::deliver(ticket)
2101 .map_err(|error| {
2102 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
2103 Error::<T>::from(error)
2104 })?;
2105
2106 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
2107 Self::deposit_event(e);
2108 }
2109 Ok(())
2110 }
2111
2112 fn add_fees_to_xcm(
2113 dest: Location,
2114 fees: FeesHandling<T>,
2115 weight_limit: WeightLimit,
2116 local: &mut Xcm<<T as Config>::RuntimeCall>,
2117 remote: &mut Xcm<()>,
2118 ) -> Result<(), Error<T>> {
2119 match fees {
2120 FeesHandling::Batched { fees } => {
2121 let context = T::UniversalLocation::get();
2122 let reanchored_fees =
2125 fees.reanchored(&dest, &context).map_err(|e| {
2126 tracing::error!(target: "xcm::pallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
2127 Error::<T>::CannotReanchor
2128 })?;
2129 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
2131 },
2132 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
2133 core::mem::swap(local, &mut local_fees);
2136 core::mem::swap(remote, &mut remote_fees);
2137 local.inner_mut().append(&mut local_fees.into_inner());
2139 remote.inner_mut().append(&mut remote_fees.into_inner());
2140 },
2141 }
2142 Ok(())
2143 }
2144
2145 fn local_reserve_fees_instructions(
2146 origin: Location,
2147 dest: Location,
2148 fees: Asset,
2149 weight_limit: WeightLimit,
2150 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2151 let value = (origin, vec![fees.clone()]);
2152 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2153
2154 let context = T::UniversalLocation::get();
2155 let reanchored_fees = fees
2156 .clone()
2157 .reanchored(&dest, &context)
2158 .map_err(|_| Error::<T>::CannotReanchor)?;
2159
2160 let local_execute_xcm = Xcm(vec![
2161 TransferAsset { assets: fees.into(), beneficiary: dest },
2163 ]);
2164 let xcm_on_dest = Xcm(vec![
2165 ReserveAssetDeposited(reanchored_fees.clone().into()),
2167 BuyExecution { fees: reanchored_fees, weight_limit },
2169 ]);
2170 Ok((local_execute_xcm, xcm_on_dest))
2171 }
2172
2173 fn local_reserve_transfer_programs(
2174 origin: Location,
2175 dest: Location,
2176 beneficiary: Either<Location, Xcm<()>>,
2177 assets: Vec<Asset>,
2178 fees: FeesHandling<T>,
2179 weight_limit: WeightLimit,
2180 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2181 let value = (origin, assets);
2182 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2183 let (_, assets) = value;
2184
2185 let max_assets =
2187 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2188 let assets: Assets = assets.into();
2189 let context = T::UniversalLocation::get();
2190 let mut reanchored_assets = assets.clone();
2191 reanchored_assets
2192 .reanchor(&dest, &context)
2193 .map_err(|e| {
2194 tracing::error!(target: "xcm::pallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2195 Error::<T>::CannotReanchor
2196 })?;
2197
2198 let mut local_execute_xcm = Xcm(vec![
2200 TransferAsset { assets, beneficiary: dest.clone() },
2202 ]);
2203 let mut xcm_on_dest = Xcm(vec![
2205 ReserveAssetDeposited(reanchored_assets),
2207 ClearOrigin,
2209 ]);
2210 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2212
2213 let custom_remote_xcm = match beneficiary {
2215 Either::Right(custom_xcm) => custom_xcm,
2216 Either::Left(beneficiary) => {
2217 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2219 },
2220 };
2221 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2222
2223 Ok((local_execute_xcm, xcm_on_dest))
2224 }
2225
2226 fn destination_reserve_fees_instructions(
2227 origin: Location,
2228 dest: Location,
2229 fees: Asset,
2230 weight_limit: WeightLimit,
2231 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2232 let value = (origin, vec![fees.clone()]);
2233 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2234 ensure!(
2235 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
2236 Error::<T>::InvalidAssetUnsupportedReserve
2237 );
2238
2239 let context = T::UniversalLocation::get();
2240 let reanchored_fees = fees
2241 .clone()
2242 .reanchored(&dest, &context)
2243 .map_err(|e| {
2244 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
2245 Error::<T>::CannotReanchor
2246 })?;
2247 let fees: Assets = fees.into();
2248
2249 let local_execute_xcm = Xcm(vec![
2250 WithdrawAsset(fees.clone()),
2252 BurnAsset(fees),
2254 ]);
2255 let xcm_on_dest = Xcm(vec![
2256 WithdrawAsset(reanchored_fees.clone().into()),
2258 BuyExecution { fees: reanchored_fees, weight_limit },
2260 ]);
2261 Ok((local_execute_xcm, xcm_on_dest))
2262 }
2263
2264 fn destination_reserve_transfer_programs(
2265 origin: Location,
2266 dest: Location,
2267 beneficiary: Either<Location, Xcm<()>>,
2268 assets: Vec<Asset>,
2269 fees: FeesHandling<T>,
2270 weight_limit: WeightLimit,
2271 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2272 let value = (origin, assets);
2273 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2274 let (_, assets) = value;
2275 for asset in assets.iter() {
2276 ensure!(
2277 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2278 Error::<T>::InvalidAssetUnsupportedReserve
2279 );
2280 }
2281
2282 let max_assets =
2284 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2285 let assets: Assets = assets.into();
2286 let context = T::UniversalLocation::get();
2287 let mut reanchored_assets = assets.clone();
2288 reanchored_assets
2289 .reanchor(&dest, &context)
2290 .map_err(|e| {
2291 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2292 Error::<T>::CannotReanchor
2293 })?;
2294
2295 let mut local_execute_xcm = Xcm(vec![
2297 WithdrawAsset(assets.clone()),
2299 BurnAsset(assets),
2301 ]);
2302 let mut xcm_on_dest = Xcm(vec![
2304 WithdrawAsset(reanchored_assets),
2306 ClearOrigin,
2308 ]);
2309 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2311
2312 let custom_remote_xcm = match beneficiary {
2314 Either::Right(custom_xcm) => custom_xcm,
2315 Either::Left(beneficiary) => {
2316 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2318 },
2319 };
2320 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2321
2322 Ok((local_execute_xcm, xcm_on_dest))
2323 }
2324
2325 fn remote_reserve_transfer_program(
2327 origin: Location,
2328 reserve: Location,
2329 beneficiary: Either<Location, Xcm<()>>,
2330 dest: Location,
2331 assets: Vec<Asset>,
2332 fees: Asset,
2333 weight_limit: WeightLimit,
2334 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2335 let value = (origin, assets);
2336 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2337 let (_, assets) = value;
2338
2339 let max_assets = assets.len() as u32;
2340 let context = T::UniversalLocation::get();
2341 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2344 let reserve_fees = fees_half_1
2346 .reanchored(&reserve, &context)
2347 .map_err(|e| {
2348 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2349 Error::<T>::CannotReanchor
2350 })?;
2351 let dest_fees = fees_half_2
2353 .reanchored(&dest, &context)
2354 .map_err(|e| {
2355 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2356 Error::<T>::CannotReanchor
2357 })?;
2358 let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2360 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2361 Error::<T>::CannotReanchor
2362 })?;
2363 let mut xcm_on_dest =
2365 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2366 let custom_xcm_on_dest = match beneficiary {
2368 Either::Right(custom_xcm) => custom_xcm,
2369 Either::Left(beneficiary) => {
2370 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2372 },
2373 };
2374 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2375 let xcm_on_reserve = Xcm(vec![
2377 BuyExecution { fees: reserve_fees, weight_limit },
2378 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2379 ]);
2380 Ok(Xcm(vec![
2381 WithdrawAsset(assets.into()),
2382 SetFeesMode { jit_withdraw: true },
2383 InitiateReserveWithdraw {
2384 assets: Wild(AllCounted(max_assets)),
2385 reserve,
2386 xcm: xcm_on_reserve,
2387 },
2388 ]))
2389 }
2390
2391 fn teleport_fees_instructions(
2392 origin: Location,
2393 dest: Location,
2394 fees: Asset,
2395 weight_limit: WeightLimit,
2396 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2397 let value = (origin, vec![fees.clone()]);
2398 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2399 ensure!(
2400 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2401 Error::<T>::Filtered
2402 );
2403
2404 let context = T::UniversalLocation::get();
2405 let reanchored_fees = fees
2406 .clone()
2407 .reanchored(&dest, &context)
2408 .map_err(|e| {
2409 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2410 Error::<T>::CannotReanchor
2411 })?;
2412
2413 let dummy_context =
2415 XcmContext { origin: None, message_id: Default::default(), topic: None };
2416 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2421 &dest,
2422 &fees,
2423 &dummy_context,
2424 )
2425 .map_err(|e| {
2426 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2427 Error::<T>::CannotCheckOutTeleport
2428 })?;
2429 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2432 &dest,
2433 &fees,
2434 &dummy_context,
2435 );
2436
2437 let fees: Assets = fees.into();
2438 let local_execute_xcm = Xcm(vec![
2439 WithdrawAsset(fees.clone()),
2441 BurnAsset(fees),
2443 ]);
2444 let xcm_on_dest = Xcm(vec![
2445 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2447 BuyExecution { fees: reanchored_fees, weight_limit },
2449 ]);
2450 Ok((local_execute_xcm, xcm_on_dest))
2451 }
2452
2453 fn teleport_assets_program(
2454 origin: Location,
2455 dest: Location,
2456 beneficiary: Either<Location, Xcm<()>>,
2457 assets: Vec<Asset>,
2458 fees: FeesHandling<T>,
2459 weight_limit: WeightLimit,
2460 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2461 let value = (origin, assets);
2462 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2463 let (_, assets) = value;
2464 for asset in assets.iter() {
2465 ensure!(
2466 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2467 Error::<T>::Filtered
2468 );
2469 }
2470
2471 let max_assets =
2473 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2474 let context = T::UniversalLocation::get();
2475 let assets: Assets = assets.into();
2476 let mut reanchored_assets = assets.clone();
2477 reanchored_assets
2478 .reanchor(&dest, &context)
2479 .map_err(|e| {
2480 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2481 Error::<T>::CannotReanchor
2482 })?;
2483
2484 let dummy_context =
2486 XcmContext { origin: None, message_id: Default::default(), topic: None };
2487 for asset in assets.inner() {
2488 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2493 &dest,
2494 asset,
2495 &dummy_context,
2496 )
2497 .map_err(|e| {
2498 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2499 Error::<T>::CannotCheckOutTeleport
2500 })?;
2501 }
2502 for asset in assets.inner() {
2503 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2506 &dest,
2507 asset,
2508 &dummy_context,
2509 );
2510 }
2511
2512 let mut local_execute_xcm = Xcm(vec![
2514 WithdrawAsset(assets.clone()),
2516 BurnAsset(assets),
2518 ]);
2519 let mut xcm_on_dest = Xcm(vec![
2521 ReceiveTeleportedAsset(reanchored_assets),
2523 ClearOrigin,
2525 ]);
2526 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2528
2529 let custom_remote_xcm = match beneficiary {
2531 Either::Right(custom_xcm) => custom_xcm,
2532 Either::Left(beneficiary) => {
2533 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2535 },
2536 };
2537 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2538
2539 Ok((local_execute_xcm, xcm_on_dest))
2540 }
2541
2542 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2544 match fees.fun {
2545 Fungible(amount) => {
2546 let fee1 = amount.saturating_div(2);
2547 let fee2 = amount.saturating_sub(fee1);
2548 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2549 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2550 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2551 },
2552 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2553 }
2554 }
2555
2556 pub(crate) fn lazy_migration(
2559 mut stage: VersionMigrationStage,
2560 weight_cutoff: Weight,
2561 ) -> (Weight, Option<VersionMigrationStage>) {
2562 let mut weight_used = Weight::zero();
2563
2564 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2565 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2566 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2567 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2568 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2569 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2570 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2571
2572 use VersionMigrationStage::*;
2573
2574 if stage == MigrateSupportedVersion {
2575 for v in 0..XCM_VERSION {
2578 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2579 if let Ok(new_key) = old_key.into_latest() {
2580 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2581 }
2582 weight_used.saturating_accrue(sv_migrate_weight);
2583 if weight_used.any_gte(weight_cutoff) {
2584 return (weight_used, Some(stage))
2585 }
2586 }
2587 }
2588 stage = MigrateVersionNotifiers;
2589 }
2590 if stage == MigrateVersionNotifiers {
2591 for v in 0..XCM_VERSION {
2592 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2593 if let Ok(new_key) = old_key.into_latest() {
2594 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2595 }
2596 weight_used.saturating_accrue(vn_migrate_weight);
2597 if weight_used.any_gte(weight_cutoff) {
2598 return (weight_used, Some(stage))
2599 }
2600 }
2601 }
2602 stage = NotifyCurrentTargets(None);
2603 }
2604
2605 let xcm_version = T::AdvertisedXcmVersion::get();
2606
2607 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2608 let mut iter = match maybe_last_raw_key {
2609 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2610 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2611 };
2612 while let Some((key, value)) = iter.next() {
2613 let (query_id, max_weight, target_xcm_version) = value;
2614 let new_key: Location = match key.clone().try_into() {
2615 Ok(k) if target_xcm_version != xcm_version => k,
2616 _ => {
2617 weight_used.saturating_accrue(vnt_already_notified_weight);
2620 continue
2621 },
2622 };
2623 let response = Response::Version(xcm_version);
2624 let message =
2625 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2626 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2627 Ok((message_id, cost)) => {
2628 let value = (query_id, max_weight, xcm_version);
2629 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2630 Event::VersionChangeNotified {
2631 destination: new_key,
2632 result: xcm_version,
2633 cost,
2634 message_id,
2635 }
2636 },
2637 Err(e) => {
2638 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2639 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2640 },
2641 };
2642 Self::deposit_event(event);
2643 weight_used.saturating_accrue(vnt_notify_weight);
2644 if weight_used.any_gte(weight_cutoff) {
2645 let last = Some(iter.last_raw_key().into());
2646 return (weight_used, Some(NotifyCurrentTargets(last)))
2647 }
2648 }
2649 stage = MigrateAndNotifyOldTargets;
2650 }
2651 if stage == MigrateAndNotifyOldTargets {
2652 for v in 0..XCM_VERSION {
2653 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2654 let (query_id, max_weight, target_xcm_version) = value;
2655 let new_key = match Location::try_from(old_key.clone()) {
2656 Ok(k) => k,
2657 Err(()) => {
2658 Self::deposit_event(Event::NotifyTargetMigrationFail {
2659 location: old_key,
2660 query_id: value.0,
2661 });
2662 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2663 if weight_used.any_gte(weight_cutoff) {
2664 return (weight_used, Some(stage))
2665 }
2666 continue
2667 },
2668 };
2669
2670 let versioned_key = LatestVersionedLocation(&new_key);
2671 if target_xcm_version == xcm_version {
2672 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2673 weight_used.saturating_accrue(vnt_migrate_weight);
2674 } else {
2675 let response = Response::Version(xcm_version);
2677 let message = Xcm(vec![QueryResponse {
2678 query_id,
2679 response,
2680 max_weight,
2681 querier: None,
2682 }]);
2683 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2684 Ok((message_id, cost)) => {
2685 VersionNotifyTargets::<T>::insert(
2686 XCM_VERSION,
2687 versioned_key,
2688 (query_id, max_weight, xcm_version),
2689 );
2690 Event::VersionChangeNotified {
2691 destination: new_key,
2692 result: xcm_version,
2693 cost,
2694 message_id,
2695 }
2696 },
2697 Err(e) => Event::NotifyTargetSendFail {
2698 location: new_key,
2699 query_id,
2700 error: e.into(),
2701 },
2702 };
2703 Self::deposit_event(event);
2704 weight_used.saturating_accrue(vnt_notify_migrate_weight);
2705 }
2706 if weight_used.any_gte(weight_cutoff) {
2707 return (weight_used, Some(stage))
2708 }
2709 }
2710 }
2711 }
2712 (weight_used, None)
2713 }
2714
2715 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2717 let dest = dest.into();
2718 let versioned_dest = VersionedLocation::from(dest.clone());
2719 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
2720 ensure!(!already, XcmError::InvalidLocation);
2721 let query_id = QueryCounter::<T>::mutate(|q| {
2722 let r = *q;
2723 q.saturating_inc();
2724 r
2725 });
2726 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
2728 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
2729 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
2730 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
2731 let query_status =
2732 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
2733 Queries::<T>::insert(query_id, query_status);
2734 Ok(())
2735 }
2736
2737 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
2739 let dest = dest.into();
2740 let versioned_dest = LatestVersionedLocation(&dest);
2741 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
2742 .ok_or(XcmError::InvalidLocation)?;
2743 let (message_id, cost) =
2744 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
2745 Self::deposit_event(Event::VersionNotifyUnrequested {
2746 destination: dest,
2747 cost,
2748 message_id,
2749 });
2750 Queries::<T>::remove(query_id);
2751 Ok(())
2752 }
2753
2754 pub fn send_xcm(
2758 interior: impl Into<Junctions>,
2759 dest: impl Into<Location>,
2760 mut message: Xcm<()>,
2761 ) -> Result<XcmHash, SendError> {
2762 let interior = interior.into();
2763 let local_origin = interior.clone().into();
2764 let dest = dest.into();
2765 let is_waived =
2766 <T::XcmExecutor as FeeManager>::is_waived(Some(&local_origin), FeeReason::ChargeFees);
2767 if interior != Junctions::Here {
2768 message.0.insert(0, DescendOrigin(interior.clone()));
2769 }
2770 tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
2771 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
2772 if !is_waived {
2773 Self::charge_fees(local_origin, price).map_err(|e| {
2774 tracing::error!(
2775 target: "xcm::pallet_xcm::send_xcm",
2776 ?e,
2777 "Charging fees failed with error",
2778 );
2779 SendError::Fees
2780 })?;
2781 }
2782 T::XcmRouter::deliver(ticket)
2783 }
2784
2785 pub fn check_account() -> T::AccountId {
2786 const ID: PalletId = PalletId(*b"py/xcmch");
2787 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
2788 }
2789
2790 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
2796 origin: OriginCaller,
2797 call: RuntimeCall,
2798 result_xcms_version: XcmVersion,
2799 ) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2800 where
2801 Runtime: crate::Config,
2802 Router: InspectMessageQueues,
2803 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
2804 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
2805 {
2806 crate::Pallet::<Runtime>::set_record_xcm(true);
2807 Router::clear_messages();
2809 frame_system::Pallet::<Runtime>::reset_events();
2811 let result = call.dispatch(origin.into());
2812 crate::Pallet::<Runtime>::set_record_xcm(false);
2813 let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
2814 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
2815 .transpose()
2816 .map_err(|()| {
2817 tracing::error!(
2818 target: "xcm::DryRunApi::dry_run_call",
2819 "Local xcm version conversion failed"
2820 );
2821
2822 XcmDryRunApiError::VersionedConversionFailed
2823 })?;
2824
2825 let forwarded_xcms =
2827 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
2828 |error| {
2829 tracing::error!(
2830 target: "xcm::DryRunApi::dry_run_call",
2831 ?error, "Forwarded xcms version conversion failed with error"
2832 );
2833 },
2834 )?;
2835 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2836 frame_system::Pallet::<Runtime>::read_events_no_consensus()
2837 .map(|record| record.event.clone())
2838 .collect();
2839 Ok(CallDryRunEffects {
2840 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
2841 forwarded_xcms,
2842 emitted_events: events,
2843 execution_result: result,
2844 })
2845 }
2846
2847 pub fn dry_run_xcm<Runtime, Router, RuntimeCall: Decode + GetDispatchInfo, XcmConfig>(
2852 origin_location: VersionedLocation,
2853 xcm: VersionedXcm<RuntimeCall>,
2854 ) -> Result<XcmDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2855 where
2856 Runtime: frame_system::Config,
2857 Router: InspectMessageQueues,
2858 XcmConfig: xcm_executor::Config<RuntimeCall = RuntimeCall>,
2859 {
2860 let origin_location: Location = origin_location.try_into().map_err(|error| {
2861 tracing::error!(
2862 target: "xcm::DryRunApi::dry_run_xcm",
2863 ?error, "Location version conversion failed with error"
2864 );
2865 XcmDryRunApiError::VersionedConversionFailed
2866 })?;
2867 let xcm_version = xcm.identify_version();
2868 let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
2869 tracing::error!(
2870 target: "xcm::DryRunApi::dry_run_xcm",
2871 ?error, "Xcm version conversion failed with error"
2872 );
2873 XcmDryRunApiError::VersionedConversionFailed
2874 })?;
2875 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
2876
2877 Router::clear_messages();
2879 frame_system::Pallet::<Runtime>::reset_events();
2880
2881 let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
2882 origin_location,
2883 xcm,
2884 &mut hash,
2885 Weight::MAX, Weight::zero(),
2887 );
2888 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
2889 .inspect_err(|error| {
2890 tracing::error!(
2891 target: "xcm::DryRunApi::dry_run_xcm",
2892 ?error, "Forwarded xcms version conversion failed with error"
2893 );
2894 })?;
2895 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2896 frame_system::Pallet::<Runtime>::read_events_no_consensus()
2897 .map(|record| record.event.clone())
2898 .collect();
2899 Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
2900 }
2901
2902 fn convert_xcms(
2903 xcm_version: XcmVersion,
2904 xcms: Vec<VersionedXcm<()>>,
2905 ) -> Result<Vec<VersionedXcm<()>>, ()> {
2906 xcms.into_iter()
2907 .map(|xcm| xcm.into_version(xcm_version))
2908 .collect::<Result<Vec<_>, ()>>()
2909 }
2910
2911 fn convert_forwarded_xcms(
2912 xcm_version: XcmVersion,
2913 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
2914 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
2915 forwarded_xcms
2916 .into_iter()
2917 .map(|(dest, forwarded_xcms)| {
2918 let dest = dest.into_version(xcm_version)?;
2919 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
2920
2921 Ok((dest, forwarded_xcms))
2922 })
2923 .collect::<Result<Vec<_>, ()>>()
2924 .map_err(|()| XcmDryRunApiError::VersionedConversionFailed)
2925 }
2926
2927 pub fn query_acceptable_payment_assets(
2932 version: xcm::Version,
2933 asset_ids: Vec<AssetId>,
2934 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
2935 Ok(asset_ids
2936 .into_iter()
2937 .map(|asset_id| VersionedAssetId::from(asset_id))
2938 .filter_map(|asset_id| asset_id.into_version(version).ok())
2939 .collect())
2940 }
2941
2942 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
2943 let message = Xcm::<()>::try_from(message.clone())
2944 .map_err(|e| {
2945 tracing::error!(target: "xcm::pallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
2946 XcmPaymentApiError::VersionedConversionFailed
2947 })?;
2948
2949 T::Weigher::weight(&mut message.clone().into()).map_err(|()| {
2950 tracing::error!(target: "xcm::pallet_xcm::query_xcm_weight", ?message, "Error when querying XCM weight");
2951 XcmPaymentApiError::WeightNotComputable
2952 })
2953 }
2954
2955 pub fn query_delivery_fees(
2957 destination: VersionedLocation,
2958 message: VersionedXcm<()>,
2959 ) -> Result<VersionedAssets, XcmPaymentApiError> {
2960 let result_version = destination.identify_version().max(message.identify_version());
2961
2962 let destination: Location = destination
2963 .clone()
2964 .try_into()
2965 .map_err(|e| {
2966 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
2967 XcmPaymentApiError::VersionedConversionFailed
2968 })?;
2969
2970 let message: Xcm<()> =
2971 message.clone().try_into().map_err(|e| {
2972 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
2973 XcmPaymentApiError::VersionedConversionFailed
2974 })?;
2975
2976 let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
2977 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
2978 XcmPaymentApiError::Unroutable
2979 })?;
2980
2981 VersionedAssets::from(fees)
2982 .into_version(result_version)
2983 .map_err(|e| {
2984 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?result_version, "Failed to convert fees into version");
2985 XcmPaymentApiError::VersionedConversionFailed
2986 })
2987 }
2988
2989 pub fn is_trusted_reserve(
2992 asset: VersionedAsset,
2993 location: VersionedLocation,
2994 ) -> Result<bool, TrustedQueryApiError> {
2995 let location: Location = location.try_into().map_err(|e| {
2996 tracing::debug!(
2997 target: "xcm::pallet_xcm::is_trusted_reserve",
2998 ?e, "Failed to convert versioned location",
2999 );
3000 TrustedQueryApiError::VersionedLocationConversionFailed
3001 })?;
3002
3003 let a: Asset = asset.try_into().map_err(|e| {
3004 tracing::debug!(
3005 target: "xcm::pallet_xcm::is_trusted_reserve",
3006 ?e, "Failed to convert versioned asset",
3007 );
3008 TrustedQueryApiError::VersionedAssetConversionFailed
3009 })?;
3010
3011 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
3012 }
3013
3014 pub fn is_trusted_teleporter(
3016 asset: VersionedAsset,
3017 location: VersionedLocation,
3018 ) -> Result<bool, TrustedQueryApiError> {
3019 let location: Location = location.try_into().map_err(|e| {
3020 tracing::debug!(
3021 target: "xcm::pallet_xcm::is_trusted_teleporter",
3022 ?e, "Failed to convert versioned location",
3023 );
3024 TrustedQueryApiError::VersionedLocationConversionFailed
3025 })?;
3026 let a: Asset = asset.try_into().map_err(|e| {
3027 tracing::debug!(
3028 target: "xcm::pallet_xcm::is_trusted_teleporter",
3029 ?e, "Failed to convert versioned asset",
3030 );
3031 TrustedQueryApiError::VersionedAssetConversionFailed
3032 })?;
3033 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
3034 }
3035
3036 pub fn authorized_aliasers(
3038 target: VersionedLocation,
3039 ) -> Result<Vec<OriginAliaser>, AuthorizedAliasersApiError> {
3040 let desired_version = target.identify_version();
3041 let target: VersionedLocation = target.into_version(XCM_VERSION).map_err(|e| {
3043 tracing::debug!(
3044 target: "xcm::pallet_xcm::authorized_aliasers",
3045 ?e, "Failed to convert versioned location",
3046 );
3047 AuthorizedAliasersApiError::LocationVersionConversionFailed
3048 })?;
3049 Ok(AuthorizedAliases::<T>::get(&target)
3050 .map(|authorized| {
3051 authorized
3052 .aliasers
3053 .into_iter()
3054 .filter_map(|aliaser| {
3055 let OriginAliaser { location, expiry } = aliaser;
3056 location
3057 .into_version(desired_version)
3058 .map(|location| OriginAliaser { location, expiry })
3059 .ok()
3060 })
3061 .collect()
3062 })
3063 .unwrap_or_default())
3064 }
3065
3066 pub fn is_authorized_alias(
3071 origin: VersionedLocation,
3072 target: VersionedLocation,
3073 ) -> Result<bool, AuthorizedAliasersApiError> {
3074 let desired_version = target.identify_version();
3075 let origin = origin.into_version(desired_version).map_err(|e| {
3076 tracing::debug!(
3077 target: "xcm::pallet_xcm::is_authorized_alias",
3078 ?e, "mismatching origin and target versions",
3079 );
3080 AuthorizedAliasersApiError::LocationVersionConversionFailed
3081 })?;
3082 Ok(Self::authorized_aliasers(target)?.into_iter().any(|aliaser| {
3083 aliaser.location == origin &&
3086 aliaser
3087 .expiry
3088 .map(|expiry| {
3089 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>() <
3090 expiry
3091 })
3092 .unwrap_or(true)
3093 }))
3094 }
3095
3096 fn do_new_query(
3098 responder: impl Into<Location>,
3099 maybe_notify: Option<(u8, u8)>,
3100 timeout: BlockNumberFor<T>,
3101 match_querier: impl Into<Location>,
3102 ) -> u64 {
3103 QueryCounter::<T>::mutate(|q| {
3104 let r = *q;
3105 q.saturating_inc();
3106 Queries::<T>::insert(
3107 r,
3108 QueryStatus::Pending {
3109 responder: responder.into().into(),
3110 maybe_match_querier: Some(match_querier.into().into()),
3111 maybe_notify,
3112 timeout,
3113 },
3114 );
3115 r
3116 })
3117 }
3118
3119 pub fn report_outcome_notify(
3142 message: &mut Xcm<()>,
3143 responder: impl Into<Location>,
3144 notify: impl Into<<T as Config>::RuntimeCall>,
3145 timeout: BlockNumberFor<T>,
3146 ) -> Result<(), XcmError> {
3147 let responder = responder.into();
3148 let destination = T::UniversalLocation::get()
3149 .invert_target(&responder)
3150 .map_err(|()| XcmError::LocationNotInvertible)?;
3151 let notify: <T as Config>::RuntimeCall = notify.into();
3152 let max_weight = notify.get_dispatch_info().call_weight;
3153 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
3154 let response_info = QueryResponseInfo { destination, query_id, max_weight };
3155 let report_error = Xcm(vec![ReportError(response_info)]);
3156 message.0.insert(0, SetAppendix(report_error));
3157 Ok(())
3158 }
3159
3160 pub fn new_notify_query(
3163 responder: impl Into<Location>,
3164 notify: impl Into<<T as Config>::RuntimeCall>,
3165 timeout: BlockNumberFor<T>,
3166 match_querier: impl Into<Location>,
3167 ) -> u64 {
3168 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
3169 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
3170 );
3171 Self::do_new_query(responder, Some(notify), timeout, match_querier)
3172 }
3173
3174 fn note_unknown_version(dest: &Location) {
3177 tracing::trace!(
3178 target: "xcm::pallet_xcm::note_unknown_version",
3179 ?dest, "XCM version is unknown for destination"
3180 );
3181 let versioned_dest = VersionedLocation::from(dest.clone());
3182 VersionDiscoveryQueue::<T>::mutate(|q| {
3183 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
3184 q[index].1.saturating_inc();
3186 } else {
3187 let _ = q.try_push((versioned_dest, 1));
3188 }
3189 });
3190 }
3191
3192 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
3198 T::XcmExecutor::charge_fees(location.clone(), assets.clone())
3199 .map_err(|_| Error::<T>::FeesNotMet)?;
3200 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
3201 Ok(())
3202 }
3203
3204 #[cfg(any(feature = "try-runtime", test))]
3214 pub fn do_try_state() -> Result<(), TryRuntimeError> {
3215 use migration::data::NeedsMigration;
3216
3217 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
3221 {
3222 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
3223 } else {
3224 XCM_VERSION.saturating_sub(1)
3225 };
3226
3227 ensure!(
3229 !Queries::<T>::iter_values()
3230 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3231 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
3232 );
3233
3234 ensure!(
3236 !LockedFungibles::<T>::iter_values()
3237 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3238 TryRuntimeError::Other(
3239 "`LockedFungibles` data should be migrated to the higher xcm version!"
3240 )
3241 );
3242
3243 ensure!(
3245 !RemoteLockedFungibles::<T>::iter()
3246 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
3247 data.needs_migration(minimal_allowed_xcm_version)),
3248 TryRuntimeError::Other(
3249 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
3250 )
3251 );
3252
3253 if CurrentMigration::<T>::exists() {
3256 return Ok(())
3257 }
3258
3259 for v in 0..XCM_VERSION {
3261 ensure!(
3262 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
3263 TryRuntimeError::Other(
3264 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
3265 )
3266 );
3267 ensure!(
3268 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
3269 TryRuntimeError::Other(
3270 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
3271 )
3272 );
3273 ensure!(
3274 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
3275 TryRuntimeError::Other(
3276 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
3277 )
3278 );
3279 }
3280
3281 Ok(())
3282 }
3283}
3284
3285pub struct LockTicket<T: Config> {
3286 sovereign_account: T::AccountId,
3287 amount: BalanceOf<T>,
3288 unlocker: Location,
3289 item_index: Option<usize>,
3290}
3291
3292impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
3293 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3294 use xcm_executor::traits::LockError::UnexpectedState;
3295 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
3296 match self.item_index {
3297 Some(index) => {
3298 ensure!(locks.len() > index, UnexpectedState);
3299 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
3300 locks[index].0 = locks[index].0.max(self.amount);
3301 },
3302 None => {
3303 locks
3304 .try_push((self.amount, self.unlocker.into()))
3305 .map_err(|(_balance, _location)| UnexpectedState)?;
3306 },
3307 }
3308 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3309 T::Currency::extend_lock(
3310 *b"py/xcmlk",
3311 &self.sovereign_account,
3312 self.amount,
3313 WithdrawReasons::all(),
3314 );
3315 Ok(())
3316 }
3317}
3318
3319pub struct UnlockTicket<T: Config> {
3320 sovereign_account: T::AccountId,
3321 amount: BalanceOf<T>,
3322 unlocker: Location,
3323}
3324
3325impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
3326 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3327 use xcm_executor::traits::LockError::UnexpectedState;
3328 let mut locks =
3329 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
3330 let mut maybe_remove_index = None;
3331 let mut locked = BalanceOf::<T>::zero();
3332 let mut found = false;
3333 for (i, x) in locks.iter_mut().enumerate() {
3336 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
3337 x.0 = x.0.saturating_sub(self.amount);
3338 if x.0.is_zero() {
3339 maybe_remove_index = Some(i);
3340 }
3341 found = true;
3342 }
3343 locked = locked.max(x.0);
3344 }
3345 ensure!(found, UnexpectedState);
3346 if let Some(remove_index) = maybe_remove_index {
3347 locks.swap_remove(remove_index);
3348 }
3349 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3350 let reasons = WithdrawReasons::all();
3351 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3352 Ok(())
3353 }
3354}
3355
3356pub struct ReduceTicket<T: Config> {
3357 key: (u32, T::AccountId, VersionedAssetId),
3358 amount: u128,
3359 locker: VersionedLocation,
3360 owner: VersionedLocation,
3361}
3362
3363impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3364 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3365 use xcm_executor::traits::LockError::UnexpectedState;
3366 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3367 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3368 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3369 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3370 if new_amount == 0 {
3371 RemoteLockedFungibles::<T>::remove(&self.key);
3372 } else {
3373 record.amount = new_amount;
3374 RemoteLockedFungibles::<T>::insert(&self.key, &record);
3375 }
3376 Ok(())
3377 }
3378}
3379
3380impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
3381 type LockTicket = LockTicket<T>;
3382 type UnlockTicket = UnlockTicket<T>;
3383 type ReduceTicket = ReduceTicket<T>;
3384
3385 fn prepare_lock(
3386 unlocker: Location,
3387 asset: Asset,
3388 owner: Location,
3389 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3390 use xcm_executor::traits::LockError::*;
3391 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3392 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3393 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3394 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3395 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3396 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3397 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3398 }
3399
3400 fn prepare_unlock(
3401 unlocker: Location,
3402 asset: Asset,
3403 owner: Location,
3404 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3405 use xcm_executor::traits::LockError::*;
3406 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3407 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3408 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3409 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3410 let item_index =
3411 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3412 ensure!(locks[item_index].0 >= amount, NotLocked);
3413 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3414 }
3415
3416 fn note_unlockable(
3417 locker: Location,
3418 asset: Asset,
3419 mut owner: Location,
3420 ) -> Result<(), xcm_executor::traits::LockError> {
3421 use xcm_executor::traits::LockError::*;
3422 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3423 let amount = match asset.fun {
3424 Fungible(a) => a,
3425 NonFungible(_) => return Err(Unimplemented),
3426 };
3427 owner.remove_network_id();
3428 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3429 let locker = locker.into();
3430 let owner = owner.into();
3431 let id: VersionedAssetId = asset.id.into();
3432 let key = (XCM_VERSION, account, id);
3433 let mut record =
3434 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3435 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3436 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3438 record.consumers = old.consumers;
3439 record.amount = record.amount.max(old.amount);
3440 }
3441 RemoteLockedFungibles::<T>::insert(&key, record);
3442 Ok(())
3443 }
3444
3445 fn prepare_reduce_unlockable(
3446 locker: Location,
3447 asset: Asset,
3448 mut owner: Location,
3449 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3450 use xcm_executor::traits::LockError::*;
3451 let amount = match asset.fun {
3452 Fungible(a) => a,
3453 NonFungible(_) => return Err(Unimplemented),
3454 };
3455 owner.remove_network_id();
3456 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3457 let locker = locker.into();
3458 let owner = owner.into();
3459 let id: VersionedAssetId = asset.id.into();
3460 let key = (XCM_VERSION, sovereign_account, id);
3461
3462 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3463 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3465 ensure!(record.amount >= amount, NotEnoughLocked);
3466 ensure!(
3467 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3468 InUse
3469 );
3470 Ok(ReduceTicket { key, amount, locker, owner })
3471 }
3472}
3473
3474impl<T: Config> WrapVersion for Pallet<T> {
3475 fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3476 dest: &Location,
3477 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3478 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3479 Self::get_version_for(dest)
3480 .or_else(|| {
3481 Self::note_unknown_version(dest);
3482 SafeXcmVersion::<T>::get()
3483 })
3484 .ok_or_else(|| {
3485 tracing::trace!(
3486 target: "xcm::pallet_xcm::wrap_version",
3487 ?dest, "Could not determine a version to wrap XCM for destination",
3488 );
3489 ()
3490 })
3491 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3492 }
3493}
3494
3495impl<T: Config> GetVersion for Pallet<T> {
3496 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3497 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3498 }
3499}
3500
3501impl<T: Config> VersionChangeNotifier for Pallet<T> {
3502 fn start(
3511 dest: &Location,
3512 query_id: QueryId,
3513 max_weight: Weight,
3514 _context: &XcmContext,
3515 ) -> XcmResult {
3516 let versioned_dest = LatestVersionedLocation(dest);
3517 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3518 ensure!(!already, XcmError::InvalidLocation);
3519
3520 let xcm_version = T::AdvertisedXcmVersion::get();
3521 let response = Response::Version(xcm_version);
3522 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3523 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3524 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3525 destination: dest.clone(),
3526 cost,
3527 message_id,
3528 });
3529
3530 let value = (query_id, max_weight, xcm_version);
3531 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3532 Ok(())
3533 }
3534
3535 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3538 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3539 Ok(())
3540 }
3541
3542 fn is_subscribed(dest: &Location) -> bool {
3544 let versioned_dest = LatestVersionedLocation(dest);
3545 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3546 }
3547}
3548
3549impl<T: Config> DropAssets for Pallet<T> {
3550 fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
3551 if assets.is_empty() {
3552 return Weight::zero()
3553 }
3554 let versioned = VersionedAssets::from(Assets::from(assets));
3555 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3556 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3557 Self::deposit_event(Event::AssetsTrapped {
3558 hash,
3559 origin: origin.clone(),
3560 assets: versioned,
3561 });
3562 Weight::zero()
3564 }
3565}
3566
3567impl<T: Config> ClaimAssets for Pallet<T> {
3568 fn claim_assets(
3569 origin: &Location,
3570 ticket: &Location,
3571 assets: &Assets,
3572 _context: &XcmContext,
3573 ) -> bool {
3574 let mut versioned = VersionedAssets::from(assets.clone());
3575 match ticket.unpack() {
3576 (0, [GeneralIndex(i)]) =>
3577 versioned = match versioned.into_version(*i as u32) {
3578 Ok(v) => v,
3579 Err(()) => return false,
3580 },
3581 (0, []) => (),
3582 _ => return false,
3583 };
3584 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3585 match AssetTraps::<T>::get(hash) {
3586 0 => return false,
3587 1 => AssetTraps::<T>::remove(hash),
3588 n => AssetTraps::<T>::insert(hash, n - 1),
3589 }
3590 Self::deposit_event(Event::AssetsClaimed {
3591 hash,
3592 origin: origin.clone(),
3593 assets: versioned,
3594 });
3595 return true
3596 }
3597}
3598
3599impl<T: Config> OnResponse for Pallet<T> {
3600 fn expecting_response(
3601 origin: &Location,
3602 query_id: QueryId,
3603 querier: Option<&Location>,
3604 ) -> bool {
3605 match Queries::<T>::get(query_id) {
3606 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) =>
3607 Location::try_from(responder).map_or(false, |r| origin == &r) &&
3608 maybe_match_querier.map_or(true, |match_querier| {
3609 Location::try_from(match_querier).map_or(false, |match_querier| {
3610 querier.map_or(false, |q| q == &match_querier)
3611 })
3612 }),
3613 Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
3614 Location::try_from(r).map_or(false, |r| origin == &r),
3615 _ => false,
3616 }
3617 }
3618
3619 fn on_response(
3620 origin: &Location,
3621 query_id: QueryId,
3622 querier: Option<&Location>,
3623 response: Response,
3624 max_weight: Weight,
3625 _context: &XcmContext,
3626 ) -> Weight {
3627 let origin = origin.clone();
3628 match (response, Queries::<T>::get(query_id)) {
3629 (
3630 Response::Version(v),
3631 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
3632 ) => {
3633 let origin: Location = match expected_origin.try_into() {
3634 Ok(o) if o == origin => o,
3635 Ok(o) => {
3636 Self::deposit_event(Event::InvalidResponder {
3637 origin: origin.clone(),
3638 query_id,
3639 expected_location: Some(o),
3640 });
3641 return Weight::zero()
3642 },
3643 _ => {
3644 Self::deposit_event(Event::InvalidResponder {
3645 origin: origin.clone(),
3646 query_id,
3647 expected_location: None,
3648 });
3649 return Weight::zero()
3651 },
3652 };
3653 if !is_active {
3655 Queries::<T>::insert(
3656 query_id,
3657 QueryStatus::VersionNotifier {
3658 origin: origin.clone().into(),
3659 is_active: true,
3660 },
3661 );
3662 }
3663 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
3665 Self::deposit_event(Event::SupportedVersionChanged {
3666 location: origin,
3667 version: v,
3668 });
3669 Weight::zero()
3670 },
3671 (
3672 response,
3673 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
3674 ) => {
3675 if let Some(match_querier) = maybe_match_querier {
3676 let match_querier = match Location::try_from(match_querier) {
3677 Ok(mq) => mq,
3678 Err(_) => {
3679 Self::deposit_event(Event::InvalidQuerierVersion {
3680 origin: origin.clone(),
3681 query_id,
3682 });
3683 return Weight::zero()
3684 },
3685 };
3686 if querier.map_or(true, |q| q != &match_querier) {
3687 Self::deposit_event(Event::InvalidQuerier {
3688 origin: origin.clone(),
3689 query_id,
3690 expected_querier: match_querier,
3691 maybe_actual_querier: querier.cloned(),
3692 });
3693 return Weight::zero()
3694 }
3695 }
3696 let responder = match Location::try_from(responder) {
3697 Ok(r) => r,
3698 Err(_) => {
3699 Self::deposit_event(Event::InvalidResponderVersion {
3700 origin: origin.clone(),
3701 query_id,
3702 });
3703 return Weight::zero()
3704 },
3705 };
3706 if origin != responder {
3707 Self::deposit_event(Event::InvalidResponder {
3708 origin: origin.clone(),
3709 query_id,
3710 expected_location: Some(responder),
3711 });
3712 return Weight::zero()
3713 }
3714 match maybe_notify {
3715 Some((pallet_index, call_index)) => {
3716 let bare = (pallet_index, call_index, query_id, response);
3720 if let Ok(call) = bare.using_encoded(|mut bytes| {
3721 <T as Config>::RuntimeCall::decode(&mut bytes)
3722 }) {
3723 Queries::<T>::remove(query_id);
3724 let weight = call.get_dispatch_info().call_weight;
3725 if weight.any_gt(max_weight) {
3726 let e = Event::NotifyOverweight {
3727 query_id,
3728 pallet_index,
3729 call_index,
3730 actual_weight: weight,
3731 max_budgeted_weight: max_weight,
3732 };
3733 Self::deposit_event(e);
3734 return Weight::zero()
3735 }
3736 let dispatch_origin = Origin::Response(origin.clone()).into();
3737 match call.dispatch(dispatch_origin) {
3738 Ok(post_info) => {
3739 let e = Event::Notified { query_id, pallet_index, call_index };
3740 Self::deposit_event(e);
3741 post_info.actual_weight
3742 },
3743 Err(error_and_info) => {
3744 let e = Event::NotifyDispatchError {
3745 query_id,
3746 pallet_index,
3747 call_index,
3748 };
3749 Self::deposit_event(e);
3750 error_and_info.post_info.actual_weight
3753 },
3754 }
3755 .unwrap_or(weight)
3756 } else {
3757 let e =
3758 Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
3759 Self::deposit_event(e);
3760 Weight::zero()
3761 }
3762 },
3763 None => {
3764 let e = Event::ResponseReady { query_id, response: response.clone() };
3765 Self::deposit_event(e);
3766 let at = frame_system::Pallet::<T>::current_block_number();
3767 let response = response.into();
3768 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
3769 Weight::zero()
3770 },
3771 }
3772 },
3773 _ => {
3774 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
3775 Self::deposit_event(e);
3776 Weight::zero()
3777 },
3778 }
3779 }
3780}
3781
3782impl<T: Config> CheckSuspension for Pallet<T> {
3783 fn is_suspended<Call>(
3784 _origin: &Location,
3785 _instructions: &mut [Instruction<Call>],
3786 _max_weight: Weight,
3787 _properties: &mut Properties,
3788 ) -> bool {
3789 XcmExecutionSuspended::<T>::get()
3790 }
3791}
3792
3793impl<T: Config> RecordXcm for Pallet<T> {
3794 fn should_record() -> bool {
3795 ShouldRecordXcm::<T>::get()
3796 }
3797
3798 fn set_record_xcm(enabled: bool) {
3799 ShouldRecordXcm::<T>::put(enabled);
3800 }
3801
3802 fn recorded_xcm() -> Option<Xcm<()>> {
3803 RecordedXcm::<T>::get()
3804 }
3805
3806 fn record(xcm: Xcm<()>) {
3807 RecordedXcm::<T>::put(xcm);
3808 }
3809}
3810
3811pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3815where
3816 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3817{
3818 match o.into() {
3819 Ok(Origin::Xcm(location)) => Ok(location),
3820 _ => Err(BadOrigin),
3821 }
3822}
3823
3824pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3828where
3829 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3830{
3831 match o.into() {
3832 Ok(Origin::Response(location)) => Ok(location),
3833 _ => Err(BadOrigin),
3834 }
3835}
3836
3837pub struct AuthorizedAliasers<T>(PhantomData<T>);
3843impl<L: Into<VersionedLocation> + Clone, T: Config> ContainsPair<L, L> for AuthorizedAliasers<T> {
3844 fn contains(origin: &L, target: &L) -> bool {
3845 let origin: VersionedLocation = origin.clone().into();
3846 let target: VersionedLocation = target.clone().into();
3847 tracing::trace!(target: "xcm::pallet_xcm::AuthorizedAliasers::contains", ?origin, ?target);
3848 Pallet::<T>::is_authorized_alias(origin, target).unwrap_or(false)
3851 }
3852}
3853
3854pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3859impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
3860 for IsMajorityOfBody<Prefix, Body>
3861{
3862 fn contains(l: &Location) -> bool {
3863 let maybe_suffix = l.match_and_split(&Prefix::get());
3864 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
3865 }
3866}
3867
3868pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3872impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
3873 fn contains(l: &Location) -> bool {
3874 let maybe_suffix = l.match_and_split(&Prefix::get());
3875 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
3876 }
3877}
3878
3879pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
3882impl<
3883 O: OriginTrait + From<Origin>,
3884 F: Contains<L>,
3885 L: TryFrom<Location> + TryInto<Location> + Clone,
3886 > EnsureOrigin<O> for EnsureXcm<F, L>
3887where
3888 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
3889{
3890 type Success = L;
3891
3892 fn try_origin(outer: O) -> Result<Self::Success, O> {
3893 match outer.caller().try_into() {
3894 Ok(Origin::Xcm(ref location)) =>
3895 if let Ok(location) = location.clone().try_into() {
3896 if F::contains(&location) {
3897 return Ok(location);
3898 }
3899 },
3900 _ => (),
3901 }
3902
3903 Err(outer)
3904 }
3905
3906 #[cfg(feature = "runtime-benchmarks")]
3907 fn try_successful_origin() -> Result<O, ()> {
3908 Ok(O::from(Origin::Xcm(Here.into())))
3909 }
3910}
3911
3912pub struct EnsureResponse<F>(PhantomData<F>);
3915impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
3916where
3917 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
3918{
3919 type Success = Location;
3920
3921 fn try_origin(outer: O) -> Result<Self::Success, O> {
3922 match outer.caller().try_into() {
3923 Ok(Origin::Response(responder)) => return Ok(responder.clone()),
3924 _ => (),
3925 }
3926
3927 Err(outer)
3928 }
3929
3930 #[cfg(feature = "runtime-benchmarks")]
3931 fn try_successful_origin() -> Result<O, ()> {
3932 Ok(O::from(Origin::Response(Here.into())))
3933 }
3934}
3935
3936pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
3939impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
3940 for XcmPassthrough<RuntimeOrigin>
3941{
3942 fn convert_origin(
3943 origin: impl Into<Location>,
3944 kind: OriginKind,
3945 ) -> Result<RuntimeOrigin, Location> {
3946 let origin = origin.into();
3947 match kind {
3948 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
3949 _ => Err(origin),
3950 }
3951 }
3952}