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 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1894 origin.clone(),
1895 dest.clone(),
1896 Either::Left(beneficiary),
1897 assets,
1898 assets_transfer_type,
1899 FeesHandling::Batched { fees },
1900 weight_limit,
1901 )?;
1902 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1903 }
1904
1905 fn do_teleport_assets(
1906 origin: OriginFor<T>,
1907 dest: Box<VersionedLocation>,
1908 beneficiary: Box<VersionedLocation>,
1909 assets: Box<VersionedAssets>,
1910 fee_asset_item: u32,
1911 weight_limit: WeightLimit,
1912 ) -> DispatchResult {
1913 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1914 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1915 let beneficiary: Location =
1916 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1917 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1918 tracing::debug!(
1919 target: "xcm::pallet_xcm::do_teleport_assets",
1920 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1921 );
1922
1923 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1924 let value = (origin_location, assets.into_inner());
1925 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
1926 let (origin_location, assets) = value;
1927 for asset in assets.iter() {
1928 let transfer_type =
1929 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
1930 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
1931 }
1932 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1933
1934 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1935 origin_location.clone(),
1936 dest.clone(),
1937 Either::Left(beneficiary),
1938 assets,
1939 TransferType::Teleport,
1940 FeesHandling::Batched { fees },
1941 weight_limit,
1942 )?;
1943 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
1944 }
1945
1946 fn do_transfer_assets(
1947 origin: Location,
1948 dest: Location,
1949 beneficiary: Either<Location, Xcm<()>>,
1950 mut assets: Vec<Asset>,
1951 assets_transfer_type: TransferType,
1952 fee_asset_index: usize,
1953 fees_transfer_type: TransferType,
1954 weight_limit: WeightLimit,
1955 ) -> DispatchResult {
1956 let fees = if fees_transfer_type == assets_transfer_type {
1958 let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
1959 FeesHandling::Batched { fees }
1961 } else {
1962 ensure!(
1968 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
1969 Error::<T>::InvalidAssetUnsupportedReserve
1970 );
1971 let weight_limit = weight_limit.clone();
1972 let fees = assets.remove(fee_asset_index);
1975 let (local_xcm, remote_xcm) = match fees_transfer_type {
1976 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
1977 origin.clone(),
1978 dest.clone(),
1979 fees,
1980 weight_limit,
1981 )?,
1982 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
1983 origin.clone(),
1984 dest.clone(),
1985 fees,
1986 weight_limit,
1987 )?,
1988 TransferType::Teleport => Self::teleport_fees_instructions(
1989 origin.clone(),
1990 dest.clone(),
1991 fees,
1992 weight_limit,
1993 )?,
1994 TransferType::RemoteReserve(_) =>
1995 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
1996 };
1997 FeesHandling::Separate { local_xcm, remote_xcm }
1998 };
1999
2000 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2001 origin.clone(),
2002 dest.clone(),
2003 beneficiary,
2004 assets,
2005 assets_transfer_type,
2006 fees,
2007 weight_limit,
2008 )?;
2009 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2010 }
2011
2012 fn build_xcm_transfer_type(
2013 origin: Location,
2014 dest: Location,
2015 beneficiary: Either<Location, Xcm<()>>,
2016 assets: Vec<Asset>,
2017 transfer_type: TransferType,
2018 fees: FeesHandling<T>,
2019 weight_limit: WeightLimit,
2020 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
2021 tracing::debug!(
2022 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2023 ?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
2024 );
2025 match transfer_type {
2026 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
2027 origin.clone(),
2028 dest.clone(),
2029 beneficiary,
2030 assets,
2031 fees,
2032 weight_limit,
2033 )
2034 .map(|(local, remote)| (local, Some(remote))),
2035 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
2036 origin.clone(),
2037 dest.clone(),
2038 beneficiary,
2039 assets,
2040 fees,
2041 weight_limit,
2042 )
2043 .map(|(local, remote)| (local, Some(remote))),
2044 TransferType::RemoteReserve(reserve) => {
2045 let fees = match fees {
2046 FeesHandling::Batched { fees } => fees,
2047 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2048 };
2049 Self::remote_reserve_transfer_program(
2050 origin.clone(),
2051 reserve.try_into().map_err(|()| Error::<T>::BadVersion)?,
2052 beneficiary,
2053 dest.clone(),
2054 assets,
2055 fees,
2056 weight_limit,
2057 )
2058 .map(|local| (local, None))
2059 },
2060 TransferType::Teleport => Self::teleport_assets_program(
2061 origin.clone(),
2062 dest.clone(),
2063 beneficiary,
2064 assets,
2065 fees,
2066 weight_limit,
2067 )
2068 .map(|(local, remote)| (local, Some(remote))),
2069 }
2070 }
2071
2072 fn execute_xcm_transfer(
2073 origin: Location,
2074 dest: Location,
2075 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
2076 remote_xcm: Option<Xcm<()>>,
2077 ) -> DispatchResult {
2078 tracing::debug!(
2079 target: "xcm::pallet_xcm::execute_xcm_transfer",
2080 ?origin, ?dest, ?local_xcm, ?remote_xcm,
2081 );
2082
2083 let weight =
2084 T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?;
2085 let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
2086 let outcome = T::XcmExecutor::prepare_and_execute(
2087 origin.clone(),
2088 local_xcm,
2089 &mut hash,
2090 weight,
2091 weight,
2092 );
2093 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
2094 outcome.clone().ensure_complete().map_err(|error| {
2095 tracing::error!(
2096 target: "xcm::pallet_xcm::execute_xcm_transfer",
2097 ?error, "XCM execution failed with error with outcome: {:?}", outcome
2098 );
2099 Error::<T>::LocalExecutionIncomplete
2100 })?;
2101
2102 if let Some(remote_xcm) = remote_xcm {
2103 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
2104 .map_err(|error| {
2105 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
2106 Error::<T>::from(error)
2107 })?;
2108 if origin != Here.into_location() {
2109 Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
2110 tracing::error!(
2111 target: "xcm::pallet_xcm::execute_xcm_transfer",
2112 ?error, ?price, ?origin, "Unable to charge fee",
2113 );
2114 Error::<T>::FeesNotMet
2115 })?;
2116 }
2117 let message_id = T::XcmRouter::deliver(ticket)
2118 .map_err(|error| {
2119 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
2120 Error::<T>::from(error)
2121 })?;
2122
2123 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
2124 Self::deposit_event(e);
2125 }
2126 Ok(())
2127 }
2128
2129 fn add_fees_to_xcm(
2130 dest: Location,
2131 fees: FeesHandling<T>,
2132 weight_limit: WeightLimit,
2133 local: &mut Xcm<<T as Config>::RuntimeCall>,
2134 remote: &mut Xcm<()>,
2135 ) -> Result<(), Error<T>> {
2136 match fees {
2137 FeesHandling::Batched { fees } => {
2138 let context = T::UniversalLocation::get();
2139 let reanchored_fees =
2142 fees.reanchored(&dest, &context).map_err(|e| {
2143 tracing::error!(target: "xcm::pallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
2144 Error::<T>::CannotReanchor
2145 })?;
2146 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
2148 },
2149 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
2150 core::mem::swap(local, &mut local_fees);
2153 core::mem::swap(remote, &mut remote_fees);
2154 local.inner_mut().append(&mut local_fees.into_inner());
2156 remote.inner_mut().append(&mut remote_fees.into_inner());
2157 },
2158 }
2159 Ok(())
2160 }
2161
2162 fn local_reserve_fees_instructions(
2163 origin: Location,
2164 dest: Location,
2165 fees: Asset,
2166 weight_limit: WeightLimit,
2167 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2168 let value = (origin, vec![fees.clone()]);
2169 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2170
2171 let context = T::UniversalLocation::get();
2172 let reanchored_fees = fees
2173 .clone()
2174 .reanchored(&dest, &context)
2175 .map_err(|_| Error::<T>::CannotReanchor)?;
2176
2177 let local_execute_xcm = Xcm(vec![
2178 TransferAsset { assets: fees.into(), beneficiary: dest },
2180 ]);
2181 let xcm_on_dest = Xcm(vec![
2182 ReserveAssetDeposited(reanchored_fees.clone().into()),
2184 BuyExecution { fees: reanchored_fees, weight_limit },
2186 ]);
2187 Ok((local_execute_xcm, xcm_on_dest))
2188 }
2189
2190 fn local_reserve_transfer_programs(
2191 origin: Location,
2192 dest: Location,
2193 beneficiary: Either<Location, Xcm<()>>,
2194 assets: Vec<Asset>,
2195 fees: FeesHandling<T>,
2196 weight_limit: WeightLimit,
2197 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2198 let value = (origin, assets);
2199 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2200 let (_, assets) = value;
2201
2202 let max_assets =
2204 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2205 let assets: Assets = assets.into();
2206 let context = T::UniversalLocation::get();
2207 let mut reanchored_assets = assets.clone();
2208 reanchored_assets
2209 .reanchor(&dest, &context)
2210 .map_err(|e| {
2211 tracing::error!(target: "xcm::pallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2212 Error::<T>::CannotReanchor
2213 })?;
2214
2215 let mut local_execute_xcm = Xcm(vec![
2217 TransferAsset { assets, beneficiary: dest.clone() },
2219 ]);
2220 let mut xcm_on_dest = Xcm(vec![
2222 ReserveAssetDeposited(reanchored_assets),
2224 ClearOrigin,
2226 ]);
2227 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2229
2230 let custom_remote_xcm = match beneficiary {
2232 Either::Right(custom_xcm) => custom_xcm,
2233 Either::Left(beneficiary) => {
2234 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2236 },
2237 };
2238 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2239
2240 Ok((local_execute_xcm, xcm_on_dest))
2241 }
2242
2243 fn destination_reserve_fees_instructions(
2244 origin: Location,
2245 dest: Location,
2246 fees: Asset,
2247 weight_limit: WeightLimit,
2248 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2249 let value = (origin, vec![fees.clone()]);
2250 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2251 ensure!(
2252 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
2253 Error::<T>::InvalidAssetUnsupportedReserve
2254 );
2255
2256 let context = T::UniversalLocation::get();
2257 let reanchored_fees = fees
2258 .clone()
2259 .reanchored(&dest, &context)
2260 .map_err(|e| {
2261 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
2262 Error::<T>::CannotReanchor
2263 })?;
2264 let fees: Assets = fees.into();
2265
2266 let local_execute_xcm = Xcm(vec![
2267 WithdrawAsset(fees.clone()),
2269 BurnAsset(fees),
2271 ]);
2272 let xcm_on_dest = Xcm(vec![
2273 WithdrawAsset(reanchored_fees.clone().into()),
2275 BuyExecution { fees: reanchored_fees, weight_limit },
2277 ]);
2278 Ok((local_execute_xcm, xcm_on_dest))
2279 }
2280
2281 fn destination_reserve_transfer_programs(
2282 origin: Location,
2283 dest: Location,
2284 beneficiary: Either<Location, Xcm<()>>,
2285 assets: Vec<Asset>,
2286 fees: FeesHandling<T>,
2287 weight_limit: WeightLimit,
2288 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2289 let value = (origin, assets);
2290 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2291 let (_, assets) = value;
2292 for asset in assets.iter() {
2293 ensure!(
2294 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2295 Error::<T>::InvalidAssetUnsupportedReserve
2296 );
2297 }
2298
2299 let max_assets =
2301 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2302 let assets: Assets = assets.into();
2303 let context = T::UniversalLocation::get();
2304 let mut reanchored_assets = assets.clone();
2305 reanchored_assets
2306 .reanchor(&dest, &context)
2307 .map_err(|e| {
2308 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2309 Error::<T>::CannotReanchor
2310 })?;
2311
2312 let mut local_execute_xcm = Xcm(vec![
2314 WithdrawAsset(assets.clone()),
2316 BurnAsset(assets),
2318 ]);
2319 let mut xcm_on_dest = Xcm(vec![
2321 WithdrawAsset(reanchored_assets),
2323 ClearOrigin,
2325 ]);
2326 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2328
2329 let custom_remote_xcm = match beneficiary {
2331 Either::Right(custom_xcm) => custom_xcm,
2332 Either::Left(beneficiary) => {
2333 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2335 },
2336 };
2337 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2338
2339 Ok((local_execute_xcm, xcm_on_dest))
2340 }
2341
2342 fn remote_reserve_transfer_program(
2344 origin: Location,
2345 reserve: Location,
2346 beneficiary: Either<Location, Xcm<()>>,
2347 dest: Location,
2348 assets: Vec<Asset>,
2349 fees: Asset,
2350 weight_limit: WeightLimit,
2351 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2352 let value = (origin, assets);
2353 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2354 let (_, assets) = value;
2355
2356 let max_assets = assets.len() as u32;
2357 let context = T::UniversalLocation::get();
2358 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2361 let reserve_fees = fees_half_1
2363 .reanchored(&reserve, &context)
2364 .map_err(|e| {
2365 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2366 Error::<T>::CannotReanchor
2367 })?;
2368 let dest_fees = fees_half_2
2370 .reanchored(&dest, &context)
2371 .map_err(|e| {
2372 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2373 Error::<T>::CannotReanchor
2374 })?;
2375 let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2377 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2378 Error::<T>::CannotReanchor
2379 })?;
2380 let mut xcm_on_dest =
2382 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2383 let custom_xcm_on_dest = match beneficiary {
2385 Either::Right(custom_xcm) => custom_xcm,
2386 Either::Left(beneficiary) => {
2387 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2389 },
2390 };
2391 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2392 let xcm_on_reserve = Xcm(vec![
2394 BuyExecution { fees: reserve_fees, weight_limit },
2395 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2396 ]);
2397 Ok(Xcm(vec![
2398 WithdrawAsset(assets.into()),
2399 SetFeesMode { jit_withdraw: true },
2400 InitiateReserveWithdraw {
2401 assets: Wild(AllCounted(max_assets)),
2402 reserve,
2403 xcm: xcm_on_reserve,
2404 },
2405 ]))
2406 }
2407
2408 fn teleport_fees_instructions(
2409 origin: Location,
2410 dest: Location,
2411 fees: Asset,
2412 weight_limit: WeightLimit,
2413 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2414 let value = (origin, vec![fees.clone()]);
2415 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2416 ensure!(
2417 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2418 Error::<T>::Filtered
2419 );
2420
2421 let context = T::UniversalLocation::get();
2422 let reanchored_fees = fees
2423 .clone()
2424 .reanchored(&dest, &context)
2425 .map_err(|e| {
2426 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2427 Error::<T>::CannotReanchor
2428 })?;
2429
2430 let dummy_context =
2432 XcmContext { origin: None, message_id: Default::default(), topic: None };
2433 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2438 &dest,
2439 &fees,
2440 &dummy_context,
2441 )
2442 .map_err(|e| {
2443 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2444 Error::<T>::CannotCheckOutTeleport
2445 })?;
2446 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2449 &dest,
2450 &fees,
2451 &dummy_context,
2452 );
2453
2454 let fees: Assets = fees.into();
2455 let local_execute_xcm = Xcm(vec![
2456 WithdrawAsset(fees.clone()),
2458 BurnAsset(fees),
2460 ]);
2461 let xcm_on_dest = Xcm(vec![
2462 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2464 BuyExecution { fees: reanchored_fees, weight_limit },
2466 ]);
2467 Ok((local_execute_xcm, xcm_on_dest))
2468 }
2469
2470 fn teleport_assets_program(
2471 origin: Location,
2472 dest: Location,
2473 beneficiary: Either<Location, Xcm<()>>,
2474 assets: Vec<Asset>,
2475 fees: FeesHandling<T>,
2476 weight_limit: WeightLimit,
2477 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2478 let value = (origin, assets);
2479 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2480 let (_, assets) = value;
2481 for asset in assets.iter() {
2482 ensure!(
2483 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2484 Error::<T>::Filtered
2485 );
2486 }
2487
2488 let max_assets =
2490 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2491 let context = T::UniversalLocation::get();
2492 let assets: Assets = assets.into();
2493 let mut reanchored_assets = assets.clone();
2494 reanchored_assets
2495 .reanchor(&dest, &context)
2496 .map_err(|e| {
2497 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2498 Error::<T>::CannotReanchor
2499 })?;
2500
2501 let dummy_context =
2503 XcmContext { origin: None, message_id: Default::default(), topic: None };
2504 for asset in assets.inner() {
2505 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2510 &dest,
2511 asset,
2512 &dummy_context,
2513 )
2514 .map_err(|e| {
2515 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2516 Error::<T>::CannotCheckOutTeleport
2517 })?;
2518 }
2519 for asset in assets.inner() {
2520 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2523 &dest,
2524 asset,
2525 &dummy_context,
2526 );
2527 }
2528
2529 let mut local_execute_xcm = Xcm(vec![
2531 WithdrawAsset(assets.clone()),
2533 BurnAsset(assets),
2535 ]);
2536 let mut xcm_on_dest = Xcm(vec![
2538 ReceiveTeleportedAsset(reanchored_assets),
2540 ClearOrigin,
2542 ]);
2543 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2545
2546 let custom_remote_xcm = match beneficiary {
2548 Either::Right(custom_xcm) => custom_xcm,
2549 Either::Left(beneficiary) => {
2550 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2552 },
2553 };
2554 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2555
2556 Ok((local_execute_xcm, xcm_on_dest))
2557 }
2558
2559 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2561 match fees.fun {
2562 Fungible(amount) => {
2563 let fee1 = amount.saturating_div(2);
2564 let fee2 = amount.saturating_sub(fee1);
2565 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2566 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2567 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2568 },
2569 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2570 }
2571 }
2572
2573 pub(crate) fn lazy_migration(
2576 mut stage: VersionMigrationStage,
2577 weight_cutoff: Weight,
2578 ) -> (Weight, Option<VersionMigrationStage>) {
2579 let mut weight_used = Weight::zero();
2580
2581 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2582 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2583 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2584 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2585 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2586 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2587 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2588
2589 use VersionMigrationStage::*;
2590
2591 if stage == MigrateSupportedVersion {
2592 for v in 0..XCM_VERSION {
2595 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2596 if let Ok(new_key) = old_key.into_latest() {
2597 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2598 }
2599 weight_used.saturating_accrue(sv_migrate_weight);
2600 if weight_used.any_gte(weight_cutoff) {
2601 return (weight_used, Some(stage))
2602 }
2603 }
2604 }
2605 stage = MigrateVersionNotifiers;
2606 }
2607 if stage == MigrateVersionNotifiers {
2608 for v in 0..XCM_VERSION {
2609 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2610 if let Ok(new_key) = old_key.into_latest() {
2611 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2612 }
2613 weight_used.saturating_accrue(vn_migrate_weight);
2614 if weight_used.any_gte(weight_cutoff) {
2615 return (weight_used, Some(stage))
2616 }
2617 }
2618 }
2619 stage = NotifyCurrentTargets(None);
2620 }
2621
2622 let xcm_version = T::AdvertisedXcmVersion::get();
2623
2624 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2625 let mut iter = match maybe_last_raw_key {
2626 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2627 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2628 };
2629 while let Some((key, value)) = iter.next() {
2630 let (query_id, max_weight, target_xcm_version) = value;
2631 let new_key: Location = match key.clone().try_into() {
2632 Ok(k) if target_xcm_version != xcm_version => k,
2633 _ => {
2634 weight_used.saturating_accrue(vnt_already_notified_weight);
2637 continue
2638 },
2639 };
2640 let response = Response::Version(xcm_version);
2641 let message =
2642 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2643 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2644 Ok((message_id, cost)) => {
2645 let value = (query_id, max_weight, xcm_version);
2646 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2647 Event::VersionChangeNotified {
2648 destination: new_key,
2649 result: xcm_version,
2650 cost,
2651 message_id,
2652 }
2653 },
2654 Err(e) => {
2655 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2656 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2657 },
2658 };
2659 Self::deposit_event(event);
2660 weight_used.saturating_accrue(vnt_notify_weight);
2661 if weight_used.any_gte(weight_cutoff) {
2662 let last = Some(iter.last_raw_key().into());
2663 return (weight_used, Some(NotifyCurrentTargets(last)))
2664 }
2665 }
2666 stage = MigrateAndNotifyOldTargets;
2667 }
2668 if stage == MigrateAndNotifyOldTargets {
2669 for v in 0..XCM_VERSION {
2670 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2671 let (query_id, max_weight, target_xcm_version) = value;
2672 let new_key = match Location::try_from(old_key.clone()) {
2673 Ok(k) => k,
2674 Err(()) => {
2675 Self::deposit_event(Event::NotifyTargetMigrationFail {
2676 location: old_key,
2677 query_id: value.0,
2678 });
2679 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2680 if weight_used.any_gte(weight_cutoff) {
2681 return (weight_used, Some(stage))
2682 }
2683 continue
2684 },
2685 };
2686
2687 let versioned_key = LatestVersionedLocation(&new_key);
2688 if target_xcm_version == xcm_version {
2689 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2690 weight_used.saturating_accrue(vnt_migrate_weight);
2691 } else {
2692 let response = Response::Version(xcm_version);
2694 let message = Xcm(vec![QueryResponse {
2695 query_id,
2696 response,
2697 max_weight,
2698 querier: None,
2699 }]);
2700 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2701 Ok((message_id, cost)) => {
2702 VersionNotifyTargets::<T>::insert(
2703 XCM_VERSION,
2704 versioned_key,
2705 (query_id, max_weight, xcm_version),
2706 );
2707 Event::VersionChangeNotified {
2708 destination: new_key,
2709 result: xcm_version,
2710 cost,
2711 message_id,
2712 }
2713 },
2714 Err(e) => Event::NotifyTargetSendFail {
2715 location: new_key,
2716 query_id,
2717 error: e.into(),
2718 },
2719 };
2720 Self::deposit_event(event);
2721 weight_used.saturating_accrue(vnt_notify_migrate_weight);
2722 }
2723 if weight_used.any_gte(weight_cutoff) {
2724 return (weight_used, Some(stage))
2725 }
2726 }
2727 }
2728 }
2729 (weight_used, None)
2730 }
2731
2732 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2734 let dest = dest.into();
2735 let versioned_dest = VersionedLocation::from(dest.clone());
2736 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
2737 ensure!(!already, XcmError::InvalidLocation);
2738 let query_id = QueryCounter::<T>::mutate(|q| {
2739 let r = *q;
2740 q.saturating_inc();
2741 r
2742 });
2743 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
2745 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
2746 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
2747 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
2748 let query_status =
2749 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
2750 Queries::<T>::insert(query_id, query_status);
2751 Ok(())
2752 }
2753
2754 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
2756 let dest = dest.into();
2757 let versioned_dest = LatestVersionedLocation(&dest);
2758 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
2759 .ok_or(XcmError::InvalidLocation)?;
2760 let (message_id, cost) =
2761 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
2762 Self::deposit_event(Event::VersionNotifyUnrequested {
2763 destination: dest,
2764 cost,
2765 message_id,
2766 });
2767 Queries::<T>::remove(query_id);
2768 Ok(())
2769 }
2770
2771 pub fn send_xcm(
2775 interior: impl Into<Junctions>,
2776 dest: impl Into<Location>,
2777 mut message: Xcm<()>,
2778 ) -> Result<XcmHash, SendError> {
2779 let interior = interior.into();
2780 let local_origin = interior.clone().into();
2781 let dest = dest.into();
2782 let is_waived =
2783 <T::XcmExecutor as FeeManager>::is_waived(Some(&local_origin), FeeReason::ChargeFees);
2784 if interior != Junctions::Here {
2785 message.0.insert(0, DescendOrigin(interior.clone()));
2786 }
2787 tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
2788 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
2789 if !is_waived {
2790 Self::charge_fees(local_origin, price).map_err(|e| {
2791 tracing::error!(
2792 target: "xcm::pallet_xcm::send_xcm",
2793 ?e,
2794 "Charging fees failed with error",
2795 );
2796 SendError::Fees
2797 })?;
2798 }
2799 T::XcmRouter::deliver(ticket)
2800 }
2801
2802 pub fn check_account() -> T::AccountId {
2803 const ID: PalletId = PalletId(*b"py/xcmch");
2804 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
2805 }
2806
2807 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
2813 origin: OriginCaller,
2814 call: RuntimeCall,
2815 result_xcms_version: XcmVersion,
2816 ) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2817 where
2818 Runtime: crate::Config,
2819 Router: InspectMessageQueues,
2820 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
2821 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
2822 {
2823 crate::Pallet::<Runtime>::set_record_xcm(true);
2824 Router::clear_messages();
2826 frame_system::Pallet::<Runtime>::reset_events();
2828 let result = call.dispatch(origin.into());
2829 crate::Pallet::<Runtime>::set_record_xcm(false);
2830 let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
2831 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
2832 .transpose()
2833 .map_err(|()| {
2834 tracing::error!(
2835 target: "xcm::DryRunApi::dry_run_call",
2836 "Local xcm version conversion failed"
2837 );
2838
2839 XcmDryRunApiError::VersionedConversionFailed
2840 })?;
2841
2842 let forwarded_xcms =
2844 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
2845 |error| {
2846 tracing::error!(
2847 target: "xcm::DryRunApi::dry_run_call",
2848 ?error, "Forwarded xcms version conversion failed with error"
2849 );
2850 },
2851 )?;
2852 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2853 frame_system::Pallet::<Runtime>::read_events_no_consensus()
2854 .map(|record| record.event.clone())
2855 .collect();
2856 Ok(CallDryRunEffects {
2857 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
2858 forwarded_xcms,
2859 emitted_events: events,
2860 execution_result: result,
2861 })
2862 }
2863
2864 pub fn dry_run_xcm<Runtime, Router, RuntimeCall: Decode + GetDispatchInfo, XcmConfig>(
2869 origin_location: VersionedLocation,
2870 xcm: VersionedXcm<RuntimeCall>,
2871 ) -> Result<XcmDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2872 where
2873 Runtime: frame_system::Config,
2874 Router: InspectMessageQueues,
2875 XcmConfig: xcm_executor::Config<RuntimeCall = RuntimeCall>,
2876 {
2877 let origin_location: Location = origin_location.try_into().map_err(|error| {
2878 tracing::error!(
2879 target: "xcm::DryRunApi::dry_run_xcm",
2880 ?error, "Location version conversion failed with error"
2881 );
2882 XcmDryRunApiError::VersionedConversionFailed
2883 })?;
2884 let xcm_version = xcm.identify_version();
2885 let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
2886 tracing::error!(
2887 target: "xcm::DryRunApi::dry_run_xcm",
2888 ?error, "Xcm version conversion failed with error"
2889 );
2890 XcmDryRunApiError::VersionedConversionFailed
2891 })?;
2892 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
2893
2894 Router::clear_messages();
2896 frame_system::Pallet::<Runtime>::reset_events();
2897
2898 let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
2899 origin_location,
2900 xcm,
2901 &mut hash,
2902 Weight::MAX, Weight::zero(),
2904 );
2905 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
2906 .inspect_err(|error| {
2907 tracing::error!(
2908 target: "xcm::DryRunApi::dry_run_xcm",
2909 ?error, "Forwarded xcms version conversion failed with error"
2910 );
2911 })?;
2912 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2913 frame_system::Pallet::<Runtime>::read_events_no_consensus()
2914 .map(|record| record.event.clone())
2915 .collect();
2916 Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
2917 }
2918
2919 fn convert_xcms(
2920 xcm_version: XcmVersion,
2921 xcms: Vec<VersionedXcm<()>>,
2922 ) -> Result<Vec<VersionedXcm<()>>, ()> {
2923 xcms.into_iter()
2924 .map(|xcm| xcm.into_version(xcm_version))
2925 .collect::<Result<Vec<_>, ()>>()
2926 }
2927
2928 fn convert_forwarded_xcms(
2929 xcm_version: XcmVersion,
2930 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
2931 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
2932 forwarded_xcms
2933 .into_iter()
2934 .map(|(dest, forwarded_xcms)| {
2935 let dest = dest.into_version(xcm_version)?;
2936 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
2937
2938 Ok((dest, forwarded_xcms))
2939 })
2940 .collect::<Result<Vec<_>, ()>>()
2941 .map_err(|()| XcmDryRunApiError::VersionedConversionFailed)
2942 }
2943
2944 pub fn query_acceptable_payment_assets(
2949 version: xcm::Version,
2950 asset_ids: Vec<AssetId>,
2951 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
2952 Ok(asset_ids
2953 .into_iter()
2954 .map(|asset_id| VersionedAssetId::from(asset_id))
2955 .filter_map(|asset_id| asset_id.into_version(version).ok())
2956 .collect())
2957 }
2958
2959 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
2960 let message = Xcm::<()>::try_from(message.clone())
2961 .map_err(|e| {
2962 tracing::error!(target: "xcm::pallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
2963 XcmPaymentApiError::VersionedConversionFailed
2964 })?;
2965
2966 T::Weigher::weight(&mut message.clone().into()).map_err(|()| {
2967 tracing::error!(target: "xcm::pallet_xcm::query_xcm_weight", ?message, "Error when querying XCM weight");
2968 XcmPaymentApiError::WeightNotComputable
2969 })
2970 }
2971
2972 pub fn query_delivery_fees(
2974 destination: VersionedLocation,
2975 message: VersionedXcm<()>,
2976 ) -> Result<VersionedAssets, XcmPaymentApiError> {
2977 let result_version = destination.identify_version().max(message.identify_version());
2978
2979 let destination: Location = destination
2980 .clone()
2981 .try_into()
2982 .map_err(|e| {
2983 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
2984 XcmPaymentApiError::VersionedConversionFailed
2985 })?;
2986
2987 let message: Xcm<()> =
2988 message.clone().try_into().map_err(|e| {
2989 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
2990 XcmPaymentApiError::VersionedConversionFailed
2991 })?;
2992
2993 let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
2994 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
2995 XcmPaymentApiError::Unroutable
2996 })?;
2997
2998 VersionedAssets::from(fees)
2999 .into_version(result_version)
3000 .map_err(|e| {
3001 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?result_version, "Failed to convert fees into version");
3002 XcmPaymentApiError::VersionedConversionFailed
3003 })
3004 }
3005
3006 pub fn is_trusted_reserve(
3009 asset: VersionedAsset,
3010 location: VersionedLocation,
3011 ) -> Result<bool, TrustedQueryApiError> {
3012 let location: Location = location.try_into().map_err(|e| {
3013 tracing::debug!(
3014 target: "xcm::pallet_xcm::is_trusted_reserve",
3015 ?e, "Failed to convert versioned location",
3016 );
3017 TrustedQueryApiError::VersionedLocationConversionFailed
3018 })?;
3019
3020 let a: Asset = asset.try_into().map_err(|e| {
3021 tracing::debug!(
3022 target: "xcm::pallet_xcm::is_trusted_reserve",
3023 ?e, "Failed to convert versioned asset",
3024 );
3025 TrustedQueryApiError::VersionedAssetConversionFailed
3026 })?;
3027
3028 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
3029 }
3030
3031 pub fn is_trusted_teleporter(
3033 asset: VersionedAsset,
3034 location: VersionedLocation,
3035 ) -> Result<bool, TrustedQueryApiError> {
3036 let location: Location = location.try_into().map_err(|e| {
3037 tracing::debug!(
3038 target: "xcm::pallet_xcm::is_trusted_teleporter",
3039 ?e, "Failed to convert versioned location",
3040 );
3041 TrustedQueryApiError::VersionedLocationConversionFailed
3042 })?;
3043 let a: Asset = asset.try_into().map_err(|e| {
3044 tracing::debug!(
3045 target: "xcm::pallet_xcm::is_trusted_teleporter",
3046 ?e, "Failed to convert versioned asset",
3047 );
3048 TrustedQueryApiError::VersionedAssetConversionFailed
3049 })?;
3050 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
3051 }
3052
3053 pub fn authorized_aliasers(
3055 target: VersionedLocation,
3056 ) -> Result<Vec<OriginAliaser>, AuthorizedAliasersApiError> {
3057 let desired_version = target.identify_version();
3058 let target: VersionedLocation = target.into_version(XCM_VERSION).map_err(|e| {
3060 tracing::debug!(
3061 target: "xcm::pallet_xcm::authorized_aliasers",
3062 ?e, "Failed to convert versioned location",
3063 );
3064 AuthorizedAliasersApiError::LocationVersionConversionFailed
3065 })?;
3066 Ok(AuthorizedAliases::<T>::get(&target)
3067 .map(|authorized| {
3068 authorized
3069 .aliasers
3070 .into_iter()
3071 .filter_map(|aliaser| {
3072 let OriginAliaser { location, expiry } = aliaser;
3073 location
3074 .into_version(desired_version)
3075 .map(|location| OriginAliaser { location, expiry })
3076 .ok()
3077 })
3078 .collect()
3079 })
3080 .unwrap_or_default())
3081 }
3082
3083 pub fn is_authorized_alias(
3088 origin: VersionedLocation,
3089 target: VersionedLocation,
3090 ) -> Result<bool, AuthorizedAliasersApiError> {
3091 let desired_version = target.identify_version();
3092 let origin = origin.into_version(desired_version).map_err(|e| {
3093 tracing::debug!(
3094 target: "xcm::pallet_xcm::is_authorized_alias",
3095 ?e, "mismatching origin and target versions",
3096 );
3097 AuthorizedAliasersApiError::LocationVersionConversionFailed
3098 })?;
3099 Ok(Self::authorized_aliasers(target)?.into_iter().any(|aliaser| {
3100 aliaser.location == origin &&
3103 aliaser
3104 .expiry
3105 .map(|expiry| {
3106 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>() <
3107 expiry
3108 })
3109 .unwrap_or(true)
3110 }))
3111 }
3112
3113 fn do_new_query(
3115 responder: impl Into<Location>,
3116 maybe_notify: Option<(u8, u8)>,
3117 timeout: BlockNumberFor<T>,
3118 match_querier: impl Into<Location>,
3119 ) -> u64 {
3120 QueryCounter::<T>::mutate(|q| {
3121 let r = *q;
3122 q.saturating_inc();
3123 Queries::<T>::insert(
3124 r,
3125 QueryStatus::Pending {
3126 responder: responder.into().into(),
3127 maybe_match_querier: Some(match_querier.into().into()),
3128 maybe_notify,
3129 timeout,
3130 },
3131 );
3132 r
3133 })
3134 }
3135
3136 pub fn report_outcome_notify(
3159 message: &mut Xcm<()>,
3160 responder: impl Into<Location>,
3161 notify: impl Into<<T as Config>::RuntimeCall>,
3162 timeout: BlockNumberFor<T>,
3163 ) -> Result<(), XcmError> {
3164 let responder = responder.into();
3165 let destination = T::UniversalLocation::get()
3166 .invert_target(&responder)
3167 .map_err(|()| XcmError::LocationNotInvertible)?;
3168 let notify: <T as Config>::RuntimeCall = notify.into();
3169 let max_weight = notify.get_dispatch_info().call_weight;
3170 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
3171 let response_info = QueryResponseInfo { destination, query_id, max_weight };
3172 let report_error = Xcm(vec![ReportError(response_info)]);
3173 message.0.insert(0, SetAppendix(report_error));
3174 Ok(())
3175 }
3176
3177 pub fn new_notify_query(
3180 responder: impl Into<Location>,
3181 notify: impl Into<<T as Config>::RuntimeCall>,
3182 timeout: BlockNumberFor<T>,
3183 match_querier: impl Into<Location>,
3184 ) -> u64 {
3185 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
3186 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
3187 );
3188 Self::do_new_query(responder, Some(notify), timeout, match_querier)
3189 }
3190
3191 fn note_unknown_version(dest: &Location) {
3194 tracing::trace!(
3195 target: "xcm::pallet_xcm::note_unknown_version",
3196 ?dest, "XCM version is unknown for destination"
3197 );
3198 let versioned_dest = VersionedLocation::from(dest.clone());
3199 VersionDiscoveryQueue::<T>::mutate(|q| {
3200 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
3201 q[index].1.saturating_inc();
3203 } else {
3204 let _ = q.try_push((versioned_dest, 1));
3205 }
3206 });
3207 }
3208
3209 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
3215 T::XcmExecutor::charge_fees(location.clone(), assets.clone())
3216 .map_err(|_| Error::<T>::FeesNotMet)?;
3217 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
3218 Ok(())
3219 }
3220
3221 #[cfg(any(feature = "try-runtime", test))]
3231 pub fn do_try_state() -> Result<(), TryRuntimeError> {
3232 use migration::data::NeedsMigration;
3233
3234 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
3238 {
3239 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
3240 } else {
3241 XCM_VERSION.saturating_sub(1)
3242 };
3243
3244 ensure!(
3246 !Queries::<T>::iter_values()
3247 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3248 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
3249 );
3250
3251 ensure!(
3253 !LockedFungibles::<T>::iter_values()
3254 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3255 TryRuntimeError::Other(
3256 "`LockedFungibles` data should be migrated to the higher xcm version!"
3257 )
3258 );
3259
3260 ensure!(
3262 !RemoteLockedFungibles::<T>::iter()
3263 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
3264 data.needs_migration(minimal_allowed_xcm_version)),
3265 TryRuntimeError::Other(
3266 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
3267 )
3268 );
3269
3270 if CurrentMigration::<T>::exists() {
3273 return Ok(())
3274 }
3275
3276 for v in 0..XCM_VERSION {
3278 ensure!(
3279 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
3280 TryRuntimeError::Other(
3281 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
3282 )
3283 );
3284 ensure!(
3285 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
3286 TryRuntimeError::Other(
3287 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
3288 )
3289 );
3290 ensure!(
3291 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
3292 TryRuntimeError::Other(
3293 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
3294 )
3295 );
3296 }
3297
3298 Ok(())
3299 }
3300}
3301
3302pub struct LockTicket<T: Config> {
3303 sovereign_account: T::AccountId,
3304 amount: BalanceOf<T>,
3305 unlocker: Location,
3306 item_index: Option<usize>,
3307}
3308
3309impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
3310 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3311 use xcm_executor::traits::LockError::UnexpectedState;
3312 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
3313 match self.item_index {
3314 Some(index) => {
3315 ensure!(locks.len() > index, UnexpectedState);
3316 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
3317 locks[index].0 = locks[index].0.max(self.amount);
3318 },
3319 None => {
3320 locks
3321 .try_push((self.amount, self.unlocker.into()))
3322 .map_err(|(_balance, _location)| UnexpectedState)?;
3323 },
3324 }
3325 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3326 T::Currency::extend_lock(
3327 *b"py/xcmlk",
3328 &self.sovereign_account,
3329 self.amount,
3330 WithdrawReasons::all(),
3331 );
3332 Ok(())
3333 }
3334}
3335
3336pub struct UnlockTicket<T: Config> {
3337 sovereign_account: T::AccountId,
3338 amount: BalanceOf<T>,
3339 unlocker: Location,
3340}
3341
3342impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
3343 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3344 use xcm_executor::traits::LockError::UnexpectedState;
3345 let mut locks =
3346 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
3347 let mut maybe_remove_index = None;
3348 let mut locked = BalanceOf::<T>::zero();
3349 let mut found = false;
3350 for (i, x) in locks.iter_mut().enumerate() {
3353 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
3354 x.0 = x.0.saturating_sub(self.amount);
3355 if x.0.is_zero() {
3356 maybe_remove_index = Some(i);
3357 }
3358 found = true;
3359 }
3360 locked = locked.max(x.0);
3361 }
3362 ensure!(found, UnexpectedState);
3363 if let Some(remove_index) = maybe_remove_index {
3364 locks.swap_remove(remove_index);
3365 }
3366 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3367 let reasons = WithdrawReasons::all();
3368 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3369 Ok(())
3370 }
3371}
3372
3373pub struct ReduceTicket<T: Config> {
3374 key: (u32, T::AccountId, VersionedAssetId),
3375 amount: u128,
3376 locker: VersionedLocation,
3377 owner: VersionedLocation,
3378}
3379
3380impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3381 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3382 use xcm_executor::traits::LockError::UnexpectedState;
3383 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3384 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3385 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3386 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3387 if new_amount == 0 {
3388 RemoteLockedFungibles::<T>::remove(&self.key);
3389 } else {
3390 record.amount = new_amount;
3391 RemoteLockedFungibles::<T>::insert(&self.key, &record);
3392 }
3393 Ok(())
3394 }
3395}
3396
3397impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
3398 type LockTicket = LockTicket<T>;
3399 type UnlockTicket = UnlockTicket<T>;
3400 type ReduceTicket = ReduceTicket<T>;
3401
3402 fn prepare_lock(
3403 unlocker: Location,
3404 asset: Asset,
3405 owner: Location,
3406 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3407 use xcm_executor::traits::LockError::*;
3408 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3409 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3410 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3411 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3412 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3413 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3414 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3415 }
3416
3417 fn prepare_unlock(
3418 unlocker: Location,
3419 asset: Asset,
3420 owner: Location,
3421 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3422 use xcm_executor::traits::LockError::*;
3423 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3424 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3425 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3426 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3427 let item_index =
3428 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3429 ensure!(locks[item_index].0 >= amount, NotLocked);
3430 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3431 }
3432
3433 fn note_unlockable(
3434 locker: Location,
3435 asset: Asset,
3436 mut owner: Location,
3437 ) -> Result<(), xcm_executor::traits::LockError> {
3438 use xcm_executor::traits::LockError::*;
3439 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3440 let amount = match asset.fun {
3441 Fungible(a) => a,
3442 NonFungible(_) => return Err(Unimplemented),
3443 };
3444 owner.remove_network_id();
3445 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3446 let locker = locker.into();
3447 let owner = owner.into();
3448 let id: VersionedAssetId = asset.id.into();
3449 let key = (XCM_VERSION, account, id);
3450 let mut record =
3451 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3452 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3453 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3455 record.consumers = old.consumers;
3456 record.amount = record.amount.max(old.amount);
3457 }
3458 RemoteLockedFungibles::<T>::insert(&key, record);
3459 Ok(())
3460 }
3461
3462 fn prepare_reduce_unlockable(
3463 locker: Location,
3464 asset: Asset,
3465 mut owner: Location,
3466 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3467 use xcm_executor::traits::LockError::*;
3468 let amount = match asset.fun {
3469 Fungible(a) => a,
3470 NonFungible(_) => return Err(Unimplemented),
3471 };
3472 owner.remove_network_id();
3473 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3474 let locker = locker.into();
3475 let owner = owner.into();
3476 let id: VersionedAssetId = asset.id.into();
3477 let key = (XCM_VERSION, sovereign_account, id);
3478
3479 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3480 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3482 ensure!(record.amount >= amount, NotEnoughLocked);
3483 ensure!(
3484 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3485 InUse
3486 );
3487 Ok(ReduceTicket { key, amount, locker, owner })
3488 }
3489}
3490
3491impl<T: Config> WrapVersion for Pallet<T> {
3492 fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3493 dest: &Location,
3494 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3495 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3496 Self::get_version_for(dest)
3497 .or_else(|| {
3498 Self::note_unknown_version(dest);
3499 SafeXcmVersion::<T>::get()
3500 })
3501 .ok_or_else(|| {
3502 tracing::trace!(
3503 target: "xcm::pallet_xcm::wrap_version",
3504 ?dest, "Could not determine a version to wrap XCM for destination",
3505 );
3506 ()
3507 })
3508 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3509 }
3510}
3511
3512impl<T: Config> GetVersion for Pallet<T> {
3513 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3514 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3515 }
3516}
3517
3518impl<T: Config> VersionChangeNotifier for Pallet<T> {
3519 fn start(
3528 dest: &Location,
3529 query_id: QueryId,
3530 max_weight: Weight,
3531 _context: &XcmContext,
3532 ) -> XcmResult {
3533 let versioned_dest = LatestVersionedLocation(dest);
3534 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3535 ensure!(!already, XcmError::InvalidLocation);
3536
3537 let xcm_version = T::AdvertisedXcmVersion::get();
3538 let response = Response::Version(xcm_version);
3539 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3540 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3541 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3542 destination: dest.clone(),
3543 cost,
3544 message_id,
3545 });
3546
3547 let value = (query_id, max_weight, xcm_version);
3548 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3549 Ok(())
3550 }
3551
3552 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3555 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3556 Ok(())
3557 }
3558
3559 fn is_subscribed(dest: &Location) -> bool {
3561 let versioned_dest = LatestVersionedLocation(dest);
3562 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3563 }
3564}
3565
3566impl<T: Config> DropAssets for Pallet<T> {
3567 fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
3568 if assets.is_empty() {
3569 return Weight::zero()
3570 }
3571 let versioned = VersionedAssets::from(Assets::from(assets));
3572 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3573 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3574 Self::deposit_event(Event::AssetsTrapped {
3575 hash,
3576 origin: origin.clone(),
3577 assets: versioned,
3578 });
3579 Weight::zero()
3581 }
3582}
3583
3584impl<T: Config> ClaimAssets for Pallet<T> {
3585 fn claim_assets(
3586 origin: &Location,
3587 ticket: &Location,
3588 assets: &Assets,
3589 _context: &XcmContext,
3590 ) -> bool {
3591 let mut versioned = VersionedAssets::from(assets.clone());
3592 match ticket.unpack() {
3593 (0, [GeneralIndex(i)]) =>
3594 versioned = match versioned.into_version(*i as u32) {
3595 Ok(v) => v,
3596 Err(()) => return false,
3597 },
3598 (0, []) => (),
3599 _ => return false,
3600 };
3601 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3602 match AssetTraps::<T>::get(hash) {
3603 0 => return false,
3604 1 => AssetTraps::<T>::remove(hash),
3605 n => AssetTraps::<T>::insert(hash, n - 1),
3606 }
3607 Self::deposit_event(Event::AssetsClaimed {
3608 hash,
3609 origin: origin.clone(),
3610 assets: versioned,
3611 });
3612 return true
3613 }
3614}
3615
3616impl<T: Config> OnResponse for Pallet<T> {
3617 fn expecting_response(
3618 origin: &Location,
3619 query_id: QueryId,
3620 querier: Option<&Location>,
3621 ) -> bool {
3622 match Queries::<T>::get(query_id) {
3623 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) =>
3624 Location::try_from(responder).map_or(false, |r| origin == &r) &&
3625 maybe_match_querier.map_or(true, |match_querier| {
3626 Location::try_from(match_querier).map_or(false, |match_querier| {
3627 querier.map_or(false, |q| q == &match_querier)
3628 })
3629 }),
3630 Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
3631 Location::try_from(r).map_or(false, |r| origin == &r),
3632 _ => false,
3633 }
3634 }
3635
3636 fn on_response(
3637 origin: &Location,
3638 query_id: QueryId,
3639 querier: Option<&Location>,
3640 response: Response,
3641 max_weight: Weight,
3642 _context: &XcmContext,
3643 ) -> Weight {
3644 let origin = origin.clone();
3645 match (response, Queries::<T>::get(query_id)) {
3646 (
3647 Response::Version(v),
3648 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
3649 ) => {
3650 let origin: Location = match expected_origin.try_into() {
3651 Ok(o) if o == origin => o,
3652 Ok(o) => {
3653 Self::deposit_event(Event::InvalidResponder {
3654 origin: origin.clone(),
3655 query_id,
3656 expected_location: Some(o),
3657 });
3658 return Weight::zero()
3659 },
3660 _ => {
3661 Self::deposit_event(Event::InvalidResponder {
3662 origin: origin.clone(),
3663 query_id,
3664 expected_location: None,
3665 });
3666 return Weight::zero()
3668 },
3669 };
3670 if !is_active {
3672 Queries::<T>::insert(
3673 query_id,
3674 QueryStatus::VersionNotifier {
3675 origin: origin.clone().into(),
3676 is_active: true,
3677 },
3678 );
3679 }
3680 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
3682 Self::deposit_event(Event::SupportedVersionChanged {
3683 location: origin,
3684 version: v,
3685 });
3686 Weight::zero()
3687 },
3688 (
3689 response,
3690 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
3691 ) => {
3692 if let Some(match_querier) = maybe_match_querier {
3693 let match_querier = match Location::try_from(match_querier) {
3694 Ok(mq) => mq,
3695 Err(_) => {
3696 Self::deposit_event(Event::InvalidQuerierVersion {
3697 origin: origin.clone(),
3698 query_id,
3699 });
3700 return Weight::zero()
3701 },
3702 };
3703 if querier.map_or(true, |q| q != &match_querier) {
3704 Self::deposit_event(Event::InvalidQuerier {
3705 origin: origin.clone(),
3706 query_id,
3707 expected_querier: match_querier,
3708 maybe_actual_querier: querier.cloned(),
3709 });
3710 return Weight::zero()
3711 }
3712 }
3713 let responder = match Location::try_from(responder) {
3714 Ok(r) => r,
3715 Err(_) => {
3716 Self::deposit_event(Event::InvalidResponderVersion {
3717 origin: origin.clone(),
3718 query_id,
3719 });
3720 return Weight::zero()
3721 },
3722 };
3723 if origin != responder {
3724 Self::deposit_event(Event::InvalidResponder {
3725 origin: origin.clone(),
3726 query_id,
3727 expected_location: Some(responder),
3728 });
3729 return Weight::zero()
3730 }
3731 match maybe_notify {
3732 Some((pallet_index, call_index)) => {
3733 let bare = (pallet_index, call_index, query_id, response);
3737 if let Ok(call) = bare.using_encoded(|mut bytes| {
3738 <T as Config>::RuntimeCall::decode(&mut bytes)
3739 }) {
3740 Queries::<T>::remove(query_id);
3741 let weight = call.get_dispatch_info().call_weight;
3742 if weight.any_gt(max_weight) {
3743 let e = Event::NotifyOverweight {
3744 query_id,
3745 pallet_index,
3746 call_index,
3747 actual_weight: weight,
3748 max_budgeted_weight: max_weight,
3749 };
3750 Self::deposit_event(e);
3751 return Weight::zero()
3752 }
3753 let dispatch_origin = Origin::Response(origin.clone()).into();
3754 match call.dispatch(dispatch_origin) {
3755 Ok(post_info) => {
3756 let e = Event::Notified { query_id, pallet_index, call_index };
3757 Self::deposit_event(e);
3758 post_info.actual_weight
3759 },
3760 Err(error_and_info) => {
3761 let e = Event::NotifyDispatchError {
3762 query_id,
3763 pallet_index,
3764 call_index,
3765 };
3766 Self::deposit_event(e);
3767 error_and_info.post_info.actual_weight
3770 },
3771 }
3772 .unwrap_or(weight)
3773 } else {
3774 let e =
3775 Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
3776 Self::deposit_event(e);
3777 Weight::zero()
3778 }
3779 },
3780 None => {
3781 let e = Event::ResponseReady { query_id, response: response.clone() };
3782 Self::deposit_event(e);
3783 let at = frame_system::Pallet::<T>::current_block_number();
3784 let response = response.into();
3785 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
3786 Weight::zero()
3787 },
3788 }
3789 },
3790 _ => {
3791 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
3792 Self::deposit_event(e);
3793 Weight::zero()
3794 },
3795 }
3796 }
3797}
3798
3799impl<T: Config> CheckSuspension for Pallet<T> {
3800 fn is_suspended<Call>(
3801 _origin: &Location,
3802 _instructions: &mut [Instruction<Call>],
3803 _max_weight: Weight,
3804 _properties: &mut Properties,
3805 ) -> bool {
3806 XcmExecutionSuspended::<T>::get()
3807 }
3808}
3809
3810impl<T: Config> RecordXcm for Pallet<T> {
3811 fn should_record() -> bool {
3812 ShouldRecordXcm::<T>::get()
3813 }
3814
3815 fn set_record_xcm(enabled: bool) {
3816 ShouldRecordXcm::<T>::put(enabled);
3817 }
3818
3819 fn recorded_xcm() -> Option<Xcm<()>> {
3820 RecordedXcm::<T>::get()
3821 }
3822
3823 fn record(xcm: Xcm<()>) {
3824 RecordedXcm::<T>::put(xcm);
3825 }
3826}
3827
3828pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3832where
3833 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3834{
3835 match o.into() {
3836 Ok(Origin::Xcm(location)) => Ok(location),
3837 _ => Err(BadOrigin),
3838 }
3839}
3840
3841pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3845where
3846 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3847{
3848 match o.into() {
3849 Ok(Origin::Response(location)) => Ok(location),
3850 _ => Err(BadOrigin),
3851 }
3852}
3853
3854pub struct AuthorizedAliasers<T>(PhantomData<T>);
3860impl<L: Into<VersionedLocation> + Clone, T: Config> ContainsPair<L, L> for AuthorizedAliasers<T> {
3861 fn contains(origin: &L, target: &L) -> bool {
3862 let origin: VersionedLocation = origin.clone().into();
3863 let target: VersionedLocation = target.clone().into();
3864 tracing::trace!(target: "xcm::pallet_xcm::AuthorizedAliasers::contains", ?origin, ?target);
3865 Pallet::<T>::is_authorized_alias(origin, target).unwrap_or(false)
3868 }
3869}
3870
3871pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3876impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
3877 for IsMajorityOfBody<Prefix, Body>
3878{
3879 fn contains(l: &Location) -> bool {
3880 let maybe_suffix = l.match_and_split(&Prefix::get());
3881 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
3882 }
3883}
3884
3885pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3889impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
3890 fn contains(l: &Location) -> bool {
3891 let maybe_suffix = l.match_and_split(&Prefix::get());
3892 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
3893 }
3894}
3895
3896pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
3899impl<
3900 O: OriginTrait + From<Origin>,
3901 F: Contains<L>,
3902 L: TryFrom<Location> + TryInto<Location> + Clone,
3903 > EnsureOrigin<O> for EnsureXcm<F, L>
3904where
3905 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
3906{
3907 type Success = L;
3908
3909 fn try_origin(outer: O) -> Result<Self::Success, O> {
3910 match outer.caller().try_into() {
3911 Ok(Origin::Xcm(ref location)) =>
3912 if let Ok(location) = location.clone().try_into() {
3913 if F::contains(&location) {
3914 return Ok(location);
3915 }
3916 },
3917 _ => (),
3918 }
3919
3920 Err(outer)
3921 }
3922
3923 #[cfg(feature = "runtime-benchmarks")]
3924 fn try_successful_origin() -> Result<O, ()> {
3925 Ok(O::from(Origin::Xcm(Here.into())))
3926 }
3927}
3928
3929pub struct EnsureResponse<F>(PhantomData<F>);
3932impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
3933where
3934 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
3935{
3936 type Success = Location;
3937
3938 fn try_origin(outer: O) -> Result<Self::Success, O> {
3939 match outer.caller().try_into() {
3940 Ok(Origin::Response(responder)) => return Ok(responder.clone()),
3941 _ => (),
3942 }
3943
3944 Err(outer)
3945 }
3946
3947 #[cfg(feature = "runtime-benchmarks")]
3948 fn try_successful_origin() -> Result<O, ()> {
3949 Ok(O::from(Origin::Response(Here.into())))
3950 }
3951}
3952
3953pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
3956impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
3957 for XcmPassthrough<RuntimeOrigin>
3958{
3959 fn convert_origin(
3960 origin: impl Into<Location>,
3961 kind: OriginKind,
3962 ) -> Result<RuntimeOrigin, Location> {
3963 let origin = origin.into();
3964 match kind {
3965 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
3966 _ => Err(origin),
3967 }
3968 }
3969}