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