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