1#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "runtime-benchmarks")]
22pub mod benchmarking;
23#[cfg(test)]
24mod mock;
25pub mod precompiles;
26#[cfg(test)]
27mod tests;
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 storage::{with_transaction, TransactionOutcome};
61use xcm::{latest::QueryResponseInfo, prelude::*};
62use xcm_builder::{
63 ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
64 QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
65};
66use xcm_executor::{
67 traits::{
68 AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
69 DropAssets, EventEmitter, FeeManager, FeeReason, MatchesFungible, OnResponse, Properties,
70 QueryHandler, QueryResponseStatus, RecordXcm, TransactAsset, TransferType,
71 VersionChangeNotifier, WeightBounds, XcmAssetTransfers,
72 },
73 AssetsInHolding,
74};
75use xcm_runtime_apis::{
76 authorized_aliases::{Error as AuthorizedAliasersApiError, OriginAliaser},
77 dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
78 fees::Error as XcmPaymentApiError,
79 trusted_query::Error as TrustedQueryApiError,
80};
81
82mod errors;
83pub use errors::ExecutionError;
84
85#[cfg(any(feature = "try-runtime", test))]
86use sp_runtime::TryRuntimeError;
87
88pub trait WeightInfo {
89 fn send() -> Weight;
90 fn teleport_assets() -> Weight;
91 fn reserve_transfer_assets() -> Weight;
92 fn transfer_assets() -> Weight;
93 fn execute() -> Weight;
94 fn force_xcm_version() -> Weight;
95 fn force_default_xcm_version() -> Weight;
96 fn force_subscribe_version_notify() -> Weight;
97 fn force_unsubscribe_version_notify() -> Weight;
98 fn force_suspension() -> Weight;
99 fn migrate_supported_version() -> Weight;
100 fn migrate_version_notifiers() -> Weight;
101 fn already_notified_target() -> Weight;
102 fn notify_current_targets() -> Weight;
103 fn notify_target_migration_fail() -> Weight;
104 fn migrate_version_notify_targets() -> Weight;
105 fn migrate_and_notify_old_targets() -> Weight;
106 fn new_query() -> Weight;
107 fn take_response() -> Weight;
108 fn claim_assets() -> Weight;
109 fn add_authorized_alias() -> Weight;
110 fn remove_authorized_alias() -> Weight;
111
112 fn weigh_message() -> Weight;
113}
114
115pub struct TestWeightInfo;
117impl WeightInfo for TestWeightInfo {
118 fn send() -> Weight {
119 Weight::from_parts(100_000_000, 0)
120 }
121
122 fn teleport_assets() -> Weight {
123 Weight::from_parts(100_000_000, 0)
124 }
125
126 fn reserve_transfer_assets() -> Weight {
127 Weight::from_parts(100_000_000, 0)
128 }
129
130 fn transfer_assets() -> Weight {
131 Weight::from_parts(100_000_000, 0)
132 }
133
134 fn execute() -> Weight {
135 Weight::from_parts(100_000_000, 0)
136 }
137
138 fn force_xcm_version() -> Weight {
139 Weight::from_parts(100_000_000, 0)
140 }
141
142 fn force_default_xcm_version() -> Weight {
143 Weight::from_parts(100_000_000, 0)
144 }
145
146 fn force_subscribe_version_notify() -> Weight {
147 Weight::from_parts(100_000_000, 0)
148 }
149
150 fn force_unsubscribe_version_notify() -> Weight {
151 Weight::from_parts(100_000_000, 0)
152 }
153
154 fn force_suspension() -> Weight {
155 Weight::from_parts(100_000_000, 0)
156 }
157
158 fn migrate_supported_version() -> Weight {
159 Weight::from_parts(100_000_000, 0)
160 }
161
162 fn migrate_version_notifiers() -> Weight {
163 Weight::from_parts(100_000_000, 0)
164 }
165
166 fn already_notified_target() -> Weight {
167 Weight::from_parts(100_000_000, 0)
168 }
169
170 fn notify_current_targets() -> Weight {
171 Weight::from_parts(100_000_000, 0)
172 }
173
174 fn notify_target_migration_fail() -> Weight {
175 Weight::from_parts(100_000_000, 0)
176 }
177
178 fn migrate_version_notify_targets() -> Weight {
179 Weight::from_parts(100_000_000, 0)
180 }
181
182 fn migrate_and_notify_old_targets() -> Weight {
183 Weight::from_parts(100_000_000, 0)
184 }
185
186 fn new_query() -> Weight {
187 Weight::from_parts(100_000_000, 0)
188 }
189
190 fn take_response() -> Weight {
191 Weight::from_parts(100_000_000, 0)
192 }
193
194 fn claim_assets() -> Weight {
195 Weight::from_parts(100_000_000, 0)
196 }
197
198 fn add_authorized_alias() -> Weight {
199 Weight::from_parts(100_000, 0)
200 }
201
202 fn remove_authorized_alias() -> Weight {
203 Weight::from_parts(100_000, 0)
204 }
205
206 fn weigh_message() -> Weight {
207 Weight::from_parts(100_000, 0)
208 }
209}
210
211#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
212pub struct AuthorizedAliasesEntry<Ticket, MAX: Get<u32>> {
213 pub aliasers: BoundedVec<OriginAliaser, MAX>,
214 pub ticket: Ticket,
215}
216
217pub fn aliasers_footprint(aliasers_count: usize) -> Footprint {
218 Footprint::from_parts(aliasers_count, OriginAliaser::max_encoded_len())
219}
220
221#[frame_support::pallet]
222pub mod pallet {
223 use super::*;
224 use frame_support::{
225 dispatch::{GetDispatchInfo, PostDispatchInfo},
226 parameter_types,
227 };
228 use frame_system::Config as SysConfig;
229 use sp_runtime::traits::Dispatchable;
230 use xcm_executor::traits::{MatchesFungible, WeightBounds};
231
232 parameter_types! {
233 pub const CurrentXcmVersion: u32 = XCM_VERSION;
236
237 #[derive(Debug, TypeInfo)]
238 pub const MaxAuthorizedAliases: u32 = 10;
240 }
241
242 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
243
244 #[pallet::pallet]
245 #[pallet::storage_version(STORAGE_VERSION)]
246 #[pallet::without_storage_info]
247 pub struct Pallet<T>(_);
248
249 pub type BalanceOf<T> =
250 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
251 pub type TicketOf<T> = <T as Config>::AuthorizedAliasConsideration;
252
253 #[pallet::config]
254 pub trait Config: frame_system::Config {
256 #[allow(deprecated)]
258 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
259
260 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
263
264 type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
266
267 type AuthorizedAliasConsideration: Consideration<Self::AccountId, Footprint>;
269
270 type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
273
274 type XcmRouter: SendXcm;
276
277 type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
281
282 type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
284
285 type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers + FeeManager;
287
288 type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
290
291 type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
294
295 type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
297
298 #[pallet::constant]
300 type UniversalLocation: Get<InteriorLocation>;
301
302 type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
304
305 type RuntimeCall: Parameter
307 + GetDispatchInfo
308 + Dispatchable<
309 RuntimeOrigin = <Self as Config>::RuntimeOrigin,
310 PostInfo = PostDispatchInfo,
311 >;
312
313 const VERSION_DISCOVERY_QUEUE_SIZE: u32;
314
315 #[pallet::constant]
318 type AdvertisedXcmVersion: Get<XcmVersion>;
319
320 type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
322
323 type TrustedLockers: ContainsPair<Location, Asset>;
326
327 type SovereignAccountOf: ConvertLocation<Self::AccountId>;
329
330 #[pallet::constant]
332 type MaxLockers: Get<u32>;
333
334 #[pallet::constant]
336 type MaxRemoteLockConsumers: Get<u32>;
337
338 type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
340
341 type WeightInfo: WeightInfo;
343 }
344
345 impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
346 fn execute() -> Weight {
347 T::WeightInfo::execute()
348 }
349 }
350
351 impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
352 type WeightInfo = Self;
353 fn execute(
354 origin: OriginFor<T>,
355 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
356 max_weight: Weight,
357 ) -> Result<Weight, DispatchErrorWithPostInfo> {
358 tracing::trace!(target: "xcm::pallet_xcm::execute", ?message, ?max_weight);
359 let outcome = (|| {
360 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
361 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
362 let message = (*message).try_into().map_err(|()| {
363 tracing::debug!(
364 target: "xcm::pallet_xcm::execute", id=?hash,
365 "Failed to convert VersionedXcm to Xcm",
366 );
367 Error::<T>::BadVersion
368 })?;
369 let value = (origin_location, message);
370 ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
371 let (origin_location, message) = value;
372 Ok(T::XcmExecutor::prepare_and_execute(
373 origin_location,
374 message,
375 &mut hash,
376 max_weight,
377 max_weight,
378 ))
379 })()
380 .map_err(|e: DispatchError| {
381 tracing::debug!(
382 target: "xcm::pallet_xcm::execute", error=?e,
383 "Failed XCM pre-execution validation or filter",
384 );
385 e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
386 })?;
387
388 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
389 let weight_used = outcome.weight_used();
390 outcome.ensure_complete().map_err(|error| {
391 tracing::error!(target: "xcm::pallet_xcm::execute", ?error, "XCM execution failed with error");
392 Error::<T>::LocalExecutionIncompleteWithError {
393 index: error.index,
394 error: error.error.into(),
395 }
396 .with_weight(
397 weight_used.saturating_add(
398 <Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
399 ),
400 )
401 })?;
402 Ok(weight_used)
403 }
404 }
405
406 impl<T: Config> SendControllerWeightInfo for Pallet<T> {
407 fn send() -> Weight {
408 T::WeightInfo::send()
409 }
410 }
411
412 impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
413 type WeightInfo = Self;
414 fn send(
415 origin: OriginFor<T>,
416 dest: Box<VersionedLocation>,
417 message: Box<VersionedXcm<()>>,
418 ) -> Result<XcmHash, DispatchError> {
419 let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
420 let interior: Junctions = origin_location.clone().try_into().map_err(|_| {
421 tracing::debug!(
422 target: "xcm::pallet_xcm::send",
423 "Failed to convert origin_location to interior Junctions",
424 );
425 Error::<T>::InvalidOrigin
426 })?;
427 let dest = Location::try_from(*dest).map_err(|()| {
428 tracing::debug!(
429 target: "xcm::pallet_xcm::send",
430 "Failed to convert destination VersionedLocation to Location",
431 );
432 Error::<T>::BadVersion
433 })?;
434 let message: Xcm<()> = (*message).try_into().map_err(|()| {
435 tracing::debug!(
436 target: "xcm::pallet_xcm::send",
437 "Failed to convert VersionedXcm message to Xcm",
438 );
439 Error::<T>::BadVersion
440 })?;
441
442 let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
443 .map_err(|error| {
444 tracing::error!(target: "xcm::pallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
445 Error::<T>::from(error)
446 })?;
447 let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
448 Self::deposit_event(e);
449 Ok(message_id)
450 }
451 }
452
453 impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
454 fn query() -> Weight {
455 T::WeightInfo::new_query()
456 }
457 fn take_response() -> Weight {
458 T::WeightInfo::take_response()
459 }
460 }
461
462 impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
463 type WeightInfo = Self;
464
465 fn query(
466 origin: OriginFor<T>,
467 timeout: BlockNumberFor<T>,
468 match_querier: VersionedLocation,
469 ) -> Result<QueryId, DispatchError> {
470 let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
471 let query_id = <Self as QueryHandler>::new_query(
472 responder,
473 timeout,
474 Location::try_from(match_querier).map_err(|_| {
475 tracing::debug!(
476 target: "xcm::pallet_xcm::query",
477 "Failed to convert VersionedLocation for match_querier",
478 );
479 Into::<DispatchError>::into(Error::<T>::BadVersion)
480 })?,
481 );
482
483 Ok(query_id)
484 }
485 }
486
487 impl<T: Config> EventEmitter for Pallet<T> {
488 fn emit_sent_event(
489 origin: Location,
490 destination: Location,
491 message: Option<Xcm<()>>,
492 message_id: XcmHash,
493 ) {
494 Self::deposit_event(Event::Sent {
495 origin,
496 destination,
497 message: message.unwrap_or_default(),
498 message_id,
499 });
500 }
501
502 fn emit_send_failure_event(
503 origin: Location,
504 destination: Location,
505 error: SendError,
506 message_id: XcmHash,
507 ) {
508 Self::deposit_event(Event::SendFailed { origin, destination, error, message_id });
509 }
510
511 fn emit_process_failure_event(origin: Location, error: XcmError, message_id: XcmHash) {
512 Self::deposit_event(Event::ProcessXcmError { origin, error, message_id });
513 }
514 }
515
516 #[pallet::event]
517 #[pallet::generate_deposit(pub(super) fn deposit_event)]
518 pub enum Event<T: Config> {
519 Attempted { outcome: xcm::latest::Outcome },
521 Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
523 SendFailed {
525 origin: Location,
526 destination: Location,
527 error: SendError,
528 message_id: XcmHash,
529 },
530 ProcessXcmError { origin: Location, error: XcmError, message_id: XcmHash },
532 UnexpectedResponse { origin: Location, query_id: QueryId },
536 ResponseReady { query_id: QueryId, response: Response },
539 Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
542 NotifyOverweight {
546 query_id: QueryId,
547 pallet_index: u8,
548 call_index: u8,
549 actual_weight: Weight,
550 max_budgeted_weight: Weight,
551 },
552 NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
555 NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
559 InvalidResponder {
563 origin: Location,
564 query_id: QueryId,
565 expected_location: Option<Location>,
566 },
567 InvalidResponderVersion { origin: Location, query_id: QueryId },
575 ResponseTaken { query_id: QueryId },
577 AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
579 VersionChangeNotified {
583 destination: Location,
584 result: XcmVersion,
585 cost: Assets,
586 message_id: XcmHash,
587 },
588 SupportedVersionChanged { location: Location, version: XcmVersion },
591 NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
594 NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
597 InvalidQuerierVersion { origin: Location, query_id: QueryId },
605 InvalidQuerier {
609 origin: Location,
610 query_id: QueryId,
611 expected_querier: Location,
612 maybe_actual_querier: Option<Location>,
613 },
614 VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
617 VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
619 VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
622 FeesPaid { paying: Location, fees: Assets },
624 AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
626 VersionMigrationFinished { version: XcmVersion },
628 AliasAuthorized { aliaser: Location, target: Location, expiry: Option<u64> },
631 AliasAuthorizationRemoved { aliaser: Location, target: Location },
633 AliasesAuthorizationsRemoved { target: Location },
635 }
636
637 #[pallet::origin]
638 #[derive(
639 PartialEq,
640 Eq,
641 Clone,
642 Encode,
643 Decode,
644 DecodeWithMemTracking,
645 RuntimeDebug,
646 TypeInfo,
647 MaxEncodedLen,
648 )]
649 pub enum Origin {
650 Xcm(Location),
652 Response(Location),
654 }
655 impl From<Location> for Origin {
656 fn from(location: Location) -> Origin {
657 Origin::Xcm(location)
658 }
659 }
660
661 #[pallet::composite_enum]
663 pub enum HoldReason {
664 AuthorizeAlias,
666 }
667
668 #[pallet::error]
669 pub enum Error<T> {
670 Unreachable,
673 SendFailure,
676 Filtered,
678 UnweighableMessage,
680 DestinationNotInvertible,
682 Empty,
684 CannotReanchor,
686 TooManyAssets,
688 InvalidOrigin,
690 BadVersion,
692 BadLocation,
695 NoSubscription,
697 AlreadySubscribed,
699 CannotCheckOutTeleport,
701 LowBalance,
703 TooManyLocks,
705 AccountNotSovereign,
707 FeesNotMet,
709 LockNotFound,
711 InUse,
713 #[codec(index = 21)]
715 InvalidAssetUnknownReserve,
716 #[codec(index = 22)]
718 InvalidAssetUnsupportedReserve,
719 #[codec(index = 23)]
721 TooManyReserves,
722 #[deprecated(since = "20.0.0", note = "Use `LocalExecutionIncompleteWithError` instead")]
724 #[codec(index = 24)]
725 LocalExecutionIncomplete,
726 #[codec(index = 25)]
728 TooManyAuthorizedAliases,
729 #[codec(index = 26)]
731 ExpiresInPast,
732 #[codec(index = 27)]
734 AliasNotFound,
735 #[codec(index = 28)]
738 LocalExecutionIncompleteWithError { index: InstructionIndex, error: ExecutionError },
739 }
740
741 impl<T: Config> From<SendError> for Error<T> {
742 fn from(e: SendError) -> Self {
743 match e {
744 SendError::Fees => Error::<T>::FeesNotMet,
745 SendError::NotApplicable => Error::<T>::Unreachable,
746 _ => Error::<T>::SendFailure,
747 }
748 }
749 }
750
751 impl<T: Config> From<AssetTransferError> for Error<T> {
752 fn from(e: AssetTransferError) -> Self {
753 match e {
754 AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
755 }
756 }
757 }
758
759 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
761 pub enum QueryStatus<BlockNumber> {
762 Pending {
764 responder: VersionedLocation,
767 maybe_match_querier: Option<VersionedLocation>,
770 maybe_notify: Option<(u8, u8)>,
771 timeout: BlockNumber,
772 },
773 VersionNotifier { origin: VersionedLocation, is_active: bool },
775 Ready { response: VersionedResponse, at: BlockNumber },
777 }
778
779 #[derive(Copy, Clone)]
780 pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
781 impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
782 impl<'a> Encode for LatestVersionedLocation<'a> {
783 fn encode(&self) -> Vec<u8> {
784 let mut r = VersionedLocation::from(Location::default()).encode();
785 r.truncate(1);
786 self.0.using_encoded(|d| r.extend_from_slice(d));
787 r
788 }
789 }
790
791 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
792 pub enum VersionMigrationStage {
793 MigrateSupportedVersion,
794 MigrateVersionNotifiers,
795 NotifyCurrentTargets(Option<Vec<u8>>),
796 MigrateAndNotifyOldTargets,
797 }
798
799 impl Default for VersionMigrationStage {
800 fn default() -> Self {
801 Self::MigrateSupportedVersion
802 }
803 }
804
805 #[pallet::storage]
807 pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
808
809 #[pallet::storage]
811 pub(super) type Queries<T: Config> =
812 StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
813
814 #[pallet::storage]
819 pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
820
821 #[pallet::storage]
824 #[pallet::whitelist_storage]
825 pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
826
827 #[pallet::storage]
829 pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
830 _,
831 Twox64Concat,
832 XcmVersion,
833 Blake2_128Concat,
834 VersionedLocation,
835 XcmVersion,
836 OptionQuery,
837 >;
838
839 #[pallet::storage]
841 pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
842 _,
843 Twox64Concat,
844 XcmVersion,
845 Blake2_128Concat,
846 VersionedLocation,
847 QueryId,
848 OptionQuery,
849 >;
850
851 #[pallet::storage]
854 pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
855 _,
856 Twox64Concat,
857 XcmVersion,
858 Blake2_128Concat,
859 VersionedLocation,
860 (QueryId, Weight, XcmVersion),
861 OptionQuery,
862 >;
863
864 pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
865 impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
866 fn get() -> u32 {
867 T::VERSION_DISCOVERY_QUEUE_SIZE
868 }
869 }
870
871 #[pallet::storage]
875 #[pallet::whitelist_storage]
876 pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
877 _,
878 BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
879 ValueQuery,
880 >;
881
882 #[pallet::storage]
884 pub(super) type CurrentMigration<T: Config> =
885 StorageValue<_, VersionMigrationStage, OptionQuery>;
886
887 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
888 #[scale_info(skip_type_params(MaxConsumers))]
889 pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
890 pub amount: u128,
892 pub owner: VersionedLocation,
894 pub locker: VersionedLocation,
896 pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
900 }
901
902 impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
903 pub fn amount_held(&self) -> Option<u128> {
906 self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
907 }
908 }
909
910 #[pallet::storage]
912 pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
913 _,
914 (
915 NMapKey<Twox64Concat, XcmVersion>,
916 NMapKey<Blake2_128Concat, T::AccountId>,
917 NMapKey<Blake2_128Concat, VersionedAssetId>,
918 ),
919 RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
920 OptionQuery,
921 >;
922
923 #[pallet::storage]
925 pub(super) type LockedFungibles<T: Config> = StorageMap<
926 _,
927 Blake2_128Concat,
928 T::AccountId,
929 BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
930 OptionQuery,
931 >;
932
933 #[pallet::storage]
935 pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
936
937 #[pallet::storage]
945 pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
946
947 #[pallet::storage]
954 pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
955
956 #[pallet::storage]
960 pub(super) type AuthorizedAliases<T: Config> = StorageMap<
961 _,
962 Blake2_128Concat,
963 VersionedLocation,
964 AuthorizedAliasesEntry<TicketOf<T>, MaxAuthorizedAliases>,
965 OptionQuery,
966 >;
967
968 #[pallet::genesis_config]
969 pub struct GenesisConfig<T: Config> {
970 #[serde(skip)]
971 pub _config: core::marker::PhantomData<T>,
972 pub safe_xcm_version: Option<XcmVersion>,
974 }
975
976 impl<T: Config> Default for GenesisConfig<T> {
977 fn default() -> Self {
978 Self { safe_xcm_version: Some(XCM_VERSION), _config: Default::default() }
979 }
980 }
981
982 #[pallet::genesis_build]
983 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
984 fn build(&self) {
985 SafeXcmVersion::<T>::set(self.safe_xcm_version);
986 }
987 }
988
989 #[pallet::hooks]
990 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
991 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
992 let mut weight_used = Weight::zero();
993 if let Some(migration) = CurrentMigration::<T>::get() {
994 let max_weight = T::BlockWeights::get().max_block / 10;
996 let (w, maybe_migration) = Self::lazy_migration(migration, max_weight);
997 if maybe_migration.is_none() {
998 Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
999 }
1000 CurrentMigration::<T>::set(maybe_migration);
1001 weight_used.saturating_accrue(w);
1002 }
1003
1004 let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
1007 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1009 q.sort_by_key(|i| i.1);
1010 while let Some((versioned_dest, _)) = q.pop() {
1011 if let Ok(dest) = Location::try_from(versioned_dest) {
1012 if Self::request_version_notify(dest).is_ok() {
1013 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1015 break
1016 }
1017 }
1018 }
1019 if let Ok(q) = BoundedVec::try_from(q) {
1022 VersionDiscoveryQueue::<T>::put(q);
1023 }
1024 weight_used
1025 }
1026
1027 #[cfg(feature = "try-runtime")]
1028 fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
1029 Self::do_try_state()
1030 }
1031 }
1032
1033 pub mod migrations {
1034 use super::*;
1035 use frame_support::traits::{PalletInfoAccess, StorageVersion};
1036
1037 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
1038 enum QueryStatusV0<BlockNumber> {
1039 Pending {
1040 responder: VersionedLocation,
1041 maybe_notify: Option<(u8, u8)>,
1042 timeout: BlockNumber,
1043 },
1044 VersionNotifier {
1045 origin: VersionedLocation,
1046 is_active: bool,
1047 },
1048 Ready {
1049 response: VersionedResponse,
1050 at: BlockNumber,
1051 },
1052 }
1053 impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
1054 fn from(old: QueryStatusV0<B>) -> Self {
1055 use QueryStatusV0::*;
1056 match old {
1057 Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
1058 responder,
1059 maybe_notify,
1060 timeout,
1061 maybe_match_querier: Some(Location::here().into()),
1062 },
1063 VersionNotifier { origin, is_active } =>
1064 QueryStatus::VersionNotifier { origin, is_active },
1065 Ready { response, at } => QueryStatus::Ready { response, at },
1066 }
1067 }
1068 }
1069
1070 pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
1071 ) -> frame_support::weights::Weight {
1072 let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
1073 tracing::info!(
1074 target: "runtime::xcm",
1075 ?on_chain_storage_version,
1076 "Running migration storage v1 for xcm with storage version",
1077 );
1078
1079 if on_chain_storage_version < 1 {
1080 let mut count = 0;
1081 Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
1082 count += 1;
1083 Some(value.into())
1084 });
1085 StorageVersion::new(1).put::<P>();
1086 tracing::info!(
1087 target: "runtime::xcm",
1088 ?on_chain_storage_version,
1089 "Running migration storage v1 for xcm with storage version was complete",
1090 );
1091 T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
1093 } else {
1094 tracing::warn!(
1095 target: "runtime::xcm",
1096 ?on_chain_storage_version,
1097 "Attempted to apply migration to v1 but failed because storage version is",
1098 );
1099 T::DbWeight::get().reads(1)
1100 }
1101 }
1102 }
1103
1104 #[pallet::call(weight(<T as Config>::WeightInfo))]
1105 impl<T: Config> Pallet<T> {
1106 #[pallet::call_index(0)]
1107 pub fn send(
1108 origin: OriginFor<T>,
1109 dest: Box<VersionedLocation>,
1110 message: Box<VersionedXcm<()>>,
1111 ) -> DispatchResult {
1112 <Self as SendController<_>>::send(origin, dest, message)?;
1113 Ok(())
1114 }
1115
1116 #[pallet::call_index(1)]
1135 #[allow(deprecated)]
1136 #[deprecated(
1137 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
1138 )]
1139 pub fn teleport_assets(
1140 origin: OriginFor<T>,
1141 dest: Box<VersionedLocation>,
1142 beneficiary: Box<VersionedLocation>,
1143 assets: Box<VersionedAssets>,
1144 fee_asset_item: u32,
1145 ) -> DispatchResult {
1146 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
1147 }
1148
1149 #[pallet::call_index(2)]
1180 #[allow(deprecated)]
1181 #[deprecated(
1182 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1183 )]
1184 pub fn reserve_transfer_assets(
1185 origin: OriginFor<T>,
1186 dest: Box<VersionedLocation>,
1187 beneficiary: Box<VersionedLocation>,
1188 assets: Box<VersionedAssets>,
1189 fee_asset_item: u32,
1190 ) -> DispatchResult {
1191 Self::do_reserve_transfer_assets(
1192 origin,
1193 dest,
1194 beneficiary,
1195 assets,
1196 fee_asset_item,
1197 Unlimited,
1198 )
1199 }
1200
1201 #[pallet::call_index(3)]
1210 #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1211 pub fn execute(
1212 origin: OriginFor<T>,
1213 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1214 max_weight: Weight,
1215 ) -> DispatchResultWithPostInfo {
1216 let weight_used =
1217 <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1218 Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1219 }
1220
1221 #[pallet::call_index(4)]
1228 pub fn force_xcm_version(
1229 origin: OriginFor<T>,
1230 location: Box<Location>,
1231 version: XcmVersion,
1232 ) -> DispatchResult {
1233 T::AdminOrigin::ensure_origin(origin)?;
1234 let location = *location;
1235 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1236 Self::deposit_event(Event::SupportedVersionChanged { location, version });
1237 Ok(())
1238 }
1239
1240 #[pallet::call_index(5)]
1246 pub fn force_default_xcm_version(
1247 origin: OriginFor<T>,
1248 maybe_xcm_version: Option<XcmVersion>,
1249 ) -> DispatchResult {
1250 T::AdminOrigin::ensure_origin(origin)?;
1251 SafeXcmVersion::<T>::set(maybe_xcm_version);
1252 Ok(())
1253 }
1254
1255 #[pallet::call_index(6)]
1260 pub fn force_subscribe_version_notify(
1261 origin: OriginFor<T>,
1262 location: Box<VersionedLocation>,
1263 ) -> DispatchResult {
1264 T::AdminOrigin::ensure_origin(origin)?;
1265 let location: Location = (*location).try_into().map_err(|()| {
1266 tracing::debug!(
1267 target: "xcm::pallet_xcm::force_subscribe_version_notify",
1268 "Failed to convert VersionedLocation for subscription target"
1269 );
1270 Error::<T>::BadLocation
1271 })?;
1272 Self::request_version_notify(location).map_err(|e| {
1273 tracing::debug!(
1274 target: "xcm::pallet_xcm::force_subscribe_version_notify", error=?e,
1275 "Failed to subscribe for version notifications for location"
1276 );
1277 match e {
1278 XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1279 _ => Error::<T>::InvalidOrigin,
1280 }
1281 .into()
1282 })
1283 }
1284
1285 #[pallet::call_index(7)]
1292 pub fn force_unsubscribe_version_notify(
1293 origin: OriginFor<T>,
1294 location: Box<VersionedLocation>,
1295 ) -> DispatchResult {
1296 T::AdminOrigin::ensure_origin(origin)?;
1297 let location: Location = (*location).try_into().map_err(|()| {
1298 tracing::debug!(
1299 target: "xcm::pallet_xcm::force_unsubscribe_version_notify",
1300 "Failed to convert VersionedLocation for unsubscription target"
1301 );
1302 Error::<T>::BadLocation
1303 })?;
1304 Self::unrequest_version_notify(location).map_err(|e| {
1305 tracing::debug!(
1306 target: "xcm::pallet_xcm::force_unsubscribe_version_notify", error=?e,
1307 "Failed to unsubscribe from version notifications for location"
1308 );
1309 match e {
1310 XcmError::InvalidLocation => Error::<T>::NoSubscription,
1311 _ => Error::<T>::InvalidOrigin,
1312 }
1313 .into()
1314 })
1315 }
1316
1317 #[pallet::call_index(8)]
1348 #[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
1349 pub fn limited_reserve_transfer_assets(
1350 origin: OriginFor<T>,
1351 dest: Box<VersionedLocation>,
1352 beneficiary: Box<VersionedLocation>,
1353 assets: Box<VersionedAssets>,
1354 fee_asset_item: u32,
1355 weight_limit: WeightLimit,
1356 ) -> DispatchResult {
1357 Self::do_reserve_transfer_assets(
1358 origin,
1359 dest,
1360 beneficiary,
1361 assets,
1362 fee_asset_item,
1363 weight_limit,
1364 )
1365 }
1366
1367 #[pallet::call_index(9)]
1386 #[pallet::weight(T::WeightInfo::teleport_assets())]
1387 pub fn limited_teleport_assets(
1388 origin: OriginFor<T>,
1389 dest: Box<VersionedLocation>,
1390 beneficiary: Box<VersionedLocation>,
1391 assets: Box<VersionedAssets>,
1392 fee_asset_item: u32,
1393 weight_limit: WeightLimit,
1394 ) -> DispatchResult {
1395 Self::do_teleport_assets(
1396 origin,
1397 dest,
1398 beneficiary,
1399 assets,
1400 fee_asset_item,
1401 weight_limit,
1402 )
1403 }
1404
1405 #[pallet::call_index(10)]
1410 pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1411 T::AdminOrigin::ensure_origin(origin)?;
1412 XcmExecutionSuspended::<T>::set(suspended);
1413 Ok(())
1414 }
1415
1416 #[pallet::call_index(11)]
1450 pub fn transfer_assets(
1451 origin: OriginFor<T>,
1452 dest: Box<VersionedLocation>,
1453 beneficiary: Box<VersionedLocation>,
1454 assets: Box<VersionedAssets>,
1455 fee_asset_item: u32,
1456 weight_limit: WeightLimit,
1457 ) -> DispatchResult {
1458 let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1459 let dest = (*dest).try_into().map_err(|()| {
1460 tracing::debug!(
1461 target: "xcm::pallet_xcm::transfer_assets",
1462 "Failed to convert destination VersionedLocation",
1463 );
1464 Error::<T>::BadVersion
1465 })?;
1466 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1467 tracing::debug!(
1468 target: "xcm::pallet_xcm::transfer_assets",
1469 "Failed to convert beneficiary VersionedLocation",
1470 );
1471 Error::<T>::BadVersion
1472 })?;
1473 let assets: Assets = (*assets).try_into().map_err(|()| {
1474 tracing::debug!(
1475 target: "xcm::pallet_xcm::transfer_assets",
1476 "Failed to convert VersionedAssets",
1477 );
1478 Error::<T>::BadVersion
1479 })?;
1480 tracing::debug!(
1481 target: "xcm::pallet_xcm::transfer_assets",
1482 ?origin, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1483 );
1484
1485 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1486 let assets = assets.into_inner();
1487 let fee_asset_item = fee_asset_item as usize;
1488 let (fees_transfer_type, assets_transfer_type) =
1490 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1491
1492 Self::do_transfer_assets(
1493 origin,
1494 dest,
1495 Either::Left(beneficiary),
1496 assets,
1497 assets_transfer_type,
1498 fee_asset_item,
1499 fees_transfer_type,
1500 weight_limit,
1501 )
1502 }
1503
1504 #[pallet::call_index(12)]
1511 pub fn claim_assets(
1512 origin: OriginFor<T>,
1513 assets: Box<VersionedAssets>,
1514 beneficiary: Box<VersionedLocation>,
1515 ) -> DispatchResult {
1516 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1517 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?origin_location, ?assets, ?beneficiary);
1518 let assets_version = assets.identify_version();
1520 let assets: Assets = (*assets).try_into().map_err(|()| {
1521 tracing::debug!(
1522 target: "xcm::pallet_xcm::claim_assets",
1523 "Failed to convert input VersionedAssets",
1524 );
1525 Error::<T>::BadVersion
1526 })?;
1527 let number_of_assets = assets.len() as u32;
1528 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1529 tracing::debug!(
1530 target: "xcm::pallet_xcm::claim_assets",
1531 "Failed to convert beneficiary VersionedLocation",
1532 );
1533 Error::<T>::BadVersion
1534 })?;
1535 let ticket: Location = GeneralIndex(assets_version as u128).into();
1536 let mut message = Xcm(vec![
1537 ClaimAsset { assets, ticket },
1538 DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1539 ]);
1540 let weight = T::Weigher::weight(&mut message, Weight::MAX).map_err(|error| {
1541 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?error, "Failed to calculate weight");
1542 Error::<T>::UnweighableMessage
1543 })?;
1544 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
1545 let outcome = T::XcmExecutor::prepare_and_execute(
1546 origin_location,
1547 message,
1548 &mut hash,
1549 weight,
1550 weight,
1551 );
1552 outcome.ensure_complete().map_err(|error| {
1553 tracing::error!(target: "xcm::pallet_xcm::claim_assets", ?error, "XCM execution failed with error");
1554 Error::<T>::LocalExecutionIncompleteWithError { index: error.index, error: error.error.into()}
1555 })?;
1556 Ok(())
1557 }
1558
1559 #[pallet::call_index(13)]
1608 #[pallet::weight(T::WeightInfo::transfer_assets())]
1609 pub fn transfer_assets_using_type_and_then(
1610 origin: OriginFor<T>,
1611 dest: Box<VersionedLocation>,
1612 assets: Box<VersionedAssets>,
1613 assets_transfer_type: Box<TransferType>,
1614 remote_fees_id: Box<VersionedAssetId>,
1615 fees_transfer_type: Box<TransferType>,
1616 custom_xcm_on_dest: Box<VersionedXcm<()>>,
1617 weight_limit: WeightLimit,
1618 ) -> DispatchResult {
1619 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1620 let dest: Location = (*dest).try_into().map_err(|()| {
1621 tracing::debug!(
1622 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1623 "Failed to convert destination VersionedLocation",
1624 );
1625 Error::<T>::BadVersion
1626 })?;
1627 let assets: Assets = (*assets).try_into().map_err(|()| {
1628 tracing::debug!(
1629 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1630 "Failed to convert VersionedAssets",
1631 );
1632 Error::<T>::BadVersion
1633 })?;
1634 let fees_id: AssetId = (*remote_fees_id).try_into().map_err(|()| {
1635 tracing::debug!(
1636 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1637 "Failed to convert remote_fees_id VersionedAssetId",
1638 );
1639 Error::<T>::BadVersion
1640 })?;
1641 let remote_xcm: Xcm<()> = (*custom_xcm_on_dest).try_into().map_err(|()| {
1642 tracing::debug!(
1643 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1644 "Failed to convert custom_xcm_on_dest VersionedXcm",
1645 );
1646 Error::<T>::BadVersion
1647 })?;
1648 tracing::debug!(
1649 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1650 ?origin_location, ?dest, ?assets, ?assets_transfer_type, ?fees_id, ?fees_transfer_type,
1651 ?remote_xcm, ?weight_limit,
1652 );
1653
1654 let assets = assets.into_inner();
1655 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1656
1657 let fee_asset_index =
1658 assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
1659 Self::do_transfer_assets(
1660 origin_location,
1661 dest,
1662 Either::Right(remote_xcm),
1663 assets,
1664 *assets_transfer_type,
1665 fee_asset_index,
1666 *fees_transfer_type,
1667 weight_limit,
1668 )
1669 }
1670
1671 #[pallet::call_index(14)]
1683 pub fn add_authorized_alias(
1684 origin: OriginFor<T>,
1685 aliaser: Box<VersionedLocation>,
1686 expires: Option<u64>,
1687 ) -> DispatchResult {
1688 let signed_origin = ensure_signed(origin.clone())?;
1689 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1690 let new_aliaser: Location = (*aliaser).try_into().map_err(|()| {
1691 tracing::debug!(
1692 target: "xcm::pallet_xcm::add_authorized_alias",
1693 "Failed to convert aliaser VersionedLocation",
1694 );
1695 Error::<T>::BadVersion
1696 })?;
1697 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1698 let origin_location = match origin_location.unpack() {
1700 (0, [AccountId32 { network: _, id }]) =>
1701 Location::new(0, [AccountId32 { network: None, id: *id }]),
1702 _ => return Err(Error::<T>::InvalidOrigin.into()),
1703 };
1704 tracing::debug!(target: "xcm::pallet_xcm::add_authorized_alias", ?origin_location, ?new_aliaser, ?expires);
1705 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1706 if let Some(expiry) = expires {
1707 ensure!(
1708 expiry >
1709 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>(),
1710 Error::<T>::ExpiresInPast
1711 );
1712 }
1713 let versioned_origin = VersionedLocation::from(origin_location.clone());
1714 let versioned_aliaser = VersionedLocation::from(new_aliaser.clone());
1715 let entry = if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1716 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1718 if let Some(aliaser) =
1719 aliasers.iter_mut().find(|aliaser| aliaser.location == versioned_aliaser)
1720 {
1721 aliaser.expiry = expires;
1723 } else {
1724 let aliaser =
1726 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1727 aliasers.try_push(aliaser).map_err(|_| {
1728 tracing::debug!(
1729 target: "xcm::pallet_xcm::add_authorized_alias",
1730 "Failed to add new aliaser to existing entry",
1731 );
1732 Error::<T>::TooManyAuthorizedAliases
1733 })?;
1734 ticket = ticket.update(&signed_origin, aliasers_footprint(aliasers.len()))?;
1736 }
1737 AuthorizedAliasesEntry { aliasers, ticket }
1738 } else {
1739 let ticket = TicketOf::<T>::new(&signed_origin, aliasers_footprint(1))?;
1741 let aliaser =
1742 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1743 let mut aliasers = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
1744 aliasers.try_push(aliaser).map_err(|error| {
1745 tracing::debug!(
1746 target: "xcm::pallet_xcm::add_authorized_alias", ?error,
1747 "Failed to add first aliaser to new entry",
1748 );
1749 Error::<T>::TooManyAuthorizedAliases
1750 })?;
1751 AuthorizedAliasesEntry { aliasers, ticket }
1752 };
1753 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1755 Self::deposit_event(Event::AliasAuthorized {
1756 aliaser: new_aliaser,
1757 target: origin_location,
1758 expiry: expires,
1759 });
1760 Ok(())
1761 }
1762
1763 #[pallet::call_index(15)]
1766 pub fn remove_authorized_alias(
1767 origin: OriginFor<T>,
1768 aliaser: Box<VersionedLocation>,
1769 ) -> DispatchResult {
1770 let signed_origin = ensure_signed(origin.clone())?;
1771 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1772 let to_remove: Location = (*aliaser).try_into().map_err(|()| {
1773 tracing::debug!(
1774 target: "xcm::pallet_xcm::remove_authorized_alias",
1775 "Failed to convert aliaser VersionedLocation",
1776 );
1777 Error::<T>::BadVersion
1778 })?;
1779 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1780 let origin_location = match origin_location.unpack() {
1782 (0, [AccountId32 { network: _, id }]) =>
1783 Location::new(0, [AccountId32 { network: None, id: *id }]),
1784 _ => return Err(Error::<T>::InvalidOrigin.into()),
1785 };
1786 tracing::debug!(target: "xcm::pallet_xcm::remove_authorized_alias", ?origin_location, ?to_remove);
1787 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1788 let versioned_origin = VersionedLocation::from(origin_location.clone());
1790 let versioned_to_remove = VersionedLocation::from(to_remove.clone());
1791 AuthorizedAliases::<T>::get(&versioned_origin)
1792 .ok_or(Error::<T>::AliasNotFound.into())
1793 .and_then(|entry| {
1794 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1795 let old_len = aliasers.len();
1796 aliasers.retain(|alias| versioned_to_remove.ne(&alias.location));
1797 let new_len = aliasers.len();
1798 if aliasers.is_empty() {
1799 ticket.drop(&signed_origin)?;
1801 AuthorizedAliases::<T>::remove(&versioned_origin);
1802 Self::deposit_event(Event::AliasAuthorizationRemoved {
1803 aliaser: to_remove,
1804 target: origin_location,
1805 });
1806 Ok(())
1807 } else if old_len != new_len {
1808 ticket = ticket.update(&signed_origin, aliasers_footprint(new_len))?;
1810 let entry = AuthorizedAliasesEntry { aliasers, ticket };
1811 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1812 Self::deposit_event(Event::AliasAuthorizationRemoved {
1813 aliaser: to_remove,
1814 target: origin_location,
1815 });
1816 Ok(())
1817 } else {
1818 Err(Error::<T>::AliasNotFound.into())
1819 }
1820 })
1821 }
1822
1823 #[pallet::call_index(16)]
1826 #[pallet::weight(T::WeightInfo::remove_authorized_alias())]
1827 pub fn remove_all_authorized_aliases(origin: OriginFor<T>) -> DispatchResult {
1828 let signed_origin = ensure_signed(origin.clone())?;
1829 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1830 let origin_location = match origin_location.unpack() {
1832 (0, [AccountId32 { network: _, id }]) =>
1833 Location::new(0, [AccountId32 { network: None, id: *id }]),
1834 _ => return Err(Error::<T>::InvalidOrigin.into()),
1835 };
1836 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", ?origin_location);
1837 let versioned_origin = VersionedLocation::from(origin_location.clone());
1839 if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1840 entry.ticket.drop(&signed_origin)?;
1842 AuthorizedAliases::<T>::remove(&versioned_origin);
1843 Self::deposit_event(Event::AliasesAuthorizationsRemoved {
1844 target: origin_location,
1845 });
1846 Ok(())
1847 } else {
1848 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", "No authorized alias entry found for the origin");
1849 Err(Error::<T>::AliasNotFound.into())
1850 }
1851 }
1852 }
1853}
1854
1855const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1857
1858#[derive(Clone, PartialEq)]
1860enum FeesHandling<T: Config> {
1861 Batched { fees: Asset },
1863 Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1865}
1866
1867impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1868 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1869 match self {
1870 Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1871 Self::Separate { local_xcm, remote_xcm } => write!(
1872 f,
1873 "FeesHandling::Separate(local: {:?}, remote: {:?})",
1874 local_xcm, remote_xcm
1875 ),
1876 }
1877 }
1878}
1879
1880impl<T: Config> QueryHandler for Pallet<T> {
1881 type BlockNumber = BlockNumberFor<T>;
1882 type Error = XcmError;
1883 type UniversalLocation = T::UniversalLocation;
1884
1885 fn new_query(
1887 responder: impl Into<Location>,
1888 timeout: BlockNumberFor<T>,
1889 match_querier: impl Into<Location>,
1890 ) -> QueryId {
1891 Self::do_new_query(responder, None, timeout, match_querier)
1892 }
1893
1894 fn report_outcome(
1897 message: &mut Xcm<()>,
1898 responder: impl Into<Location>,
1899 timeout: Self::BlockNumber,
1900 ) -> Result<QueryId, Self::Error> {
1901 let responder = responder.into();
1902 let destination =
1903 Self::UniversalLocation::get().invert_target(&responder).map_err(|()| {
1904 tracing::debug!(
1905 target: "xcm::pallet_xcm::report_outcome",
1906 "Failed to invert responder Location",
1907 );
1908 XcmError::LocationNotInvertible
1909 })?;
1910 let query_id = Self::new_query(responder, timeout, Here);
1911 let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1912 let report_error = Xcm(vec![ReportError(response_info)]);
1913 message.0.insert(0, SetAppendix(report_error));
1914 Ok(query_id)
1915 }
1916
1917 fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1919 match Queries::<T>::get(query_id) {
1920 Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1921 Ok(response) => {
1922 Queries::<T>::remove(query_id);
1923 Self::deposit_event(Event::ResponseTaken { query_id });
1924 QueryResponseStatus::Ready { response, at }
1925 },
1926 Err(_) => {
1927 tracing::debug!(
1928 target: "xcm::pallet_xcm::take_response", ?query_id,
1929 "Failed to convert VersionedResponse to Response for query",
1930 );
1931 QueryResponseStatus::UnexpectedVersion
1932 },
1933 },
1934 Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
1935 Some(_) => {
1936 tracing::debug!(
1937 target: "xcm::pallet_xcm::take_response", ?query_id,
1938 "Unexpected QueryStatus variant for query",
1939 );
1940 QueryResponseStatus::UnexpectedVersion
1941 },
1942 None => {
1943 tracing::debug!(
1944 target: "xcm::pallet_xcm::take_response", ?query_id,
1945 "Query ID not found`",
1946 );
1947 QueryResponseStatus::NotFound
1948 },
1949 }
1950 }
1951
1952 #[cfg(feature = "runtime-benchmarks")]
1953 fn expect_response(id: QueryId, response: Response) {
1954 let response = response.into();
1955 Queries::<T>::insert(
1956 id,
1957 QueryStatus::Ready { response, at: frame_system::Pallet::<T>::current_block_number() },
1958 );
1959 }
1960}
1961
1962impl<T: Config> Pallet<T> {
1963 pub fn query(query_id: &QueryId) -> Option<QueryStatus<BlockNumberFor<T>>> {
1965 Queries::<T>::get(query_id)
1966 }
1967
1968 pub fn asset_trap(trap_id: &H256) -> u32 {
1974 AssetTraps::<T>::get(trap_id)
1975 }
1976
1977 fn find_fee_and_assets_transfer_types(
1982 assets: &[Asset],
1983 fee_asset_item: usize,
1984 dest: &Location,
1985 ) -> Result<(TransferType, TransferType), Error<T>> {
1986 let mut fees_transfer_type = None;
1987 let mut assets_transfer_type = None;
1988 for (idx, asset) in assets.iter().enumerate() {
1989 if let Fungible(x) = asset.fun {
1990 ensure!(!x.is_zero(), Error::<T>::Empty);
1992 }
1993 let transfer_type =
1994 T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
1995 if idx == fee_asset_item {
1996 fees_transfer_type = Some(transfer_type);
1997 } else {
1998 if let Some(existing) = assets_transfer_type.as_ref() {
1999 ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
2002 } else {
2003 assets_transfer_type = Some(transfer_type);
2005 }
2006 }
2007 }
2008 if assets.len() == 1 {
2010 assets_transfer_type = fees_transfer_type.clone()
2011 }
2012 Ok((
2013 fees_transfer_type.ok_or(Error::<T>::Empty)?,
2014 assets_transfer_type.ok_or(Error::<T>::Empty)?,
2015 ))
2016 }
2017
2018 fn do_reserve_transfer_assets(
2019 origin: OriginFor<T>,
2020 dest: Box<VersionedLocation>,
2021 beneficiary: Box<VersionedLocation>,
2022 assets: Box<VersionedAssets>,
2023 fee_asset_item: u32,
2024 weight_limit: WeightLimit,
2025 ) -> DispatchResult {
2026 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2027 let dest = (*dest).try_into().map_err(|()| {
2028 tracing::debug!(
2029 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2030 "Failed to convert destination VersionedLocation",
2031 );
2032 Error::<T>::BadVersion
2033 })?;
2034 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2035 tracing::debug!(
2036 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2037 "Failed to convert beneficiary VersionedLocation",
2038 );
2039 Error::<T>::BadVersion
2040 })?;
2041 let assets: Assets = (*assets).try_into().map_err(|()| {
2042 tracing::debug!(
2043 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2044 "Failed to convert VersionedAssets",
2045 );
2046 Error::<T>::BadVersion
2047 })?;
2048 tracing::debug!(
2049 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2050 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item,
2051 );
2052
2053 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2054 let value = (origin_location, assets.into_inner());
2055 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2056 let (origin, assets) = value;
2057
2058 let fee_asset_item = fee_asset_item as usize;
2059 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
2060
2061 let (fees_transfer_type, assets_transfer_type) =
2063 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
2064 ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
2066 ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
2068
2069 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2070 origin.clone(),
2071 dest.clone(),
2072 Either::Left(beneficiary),
2073 assets,
2074 assets_transfer_type,
2075 FeesHandling::Batched { fees },
2076 weight_limit,
2077 )?;
2078 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2079 }
2080
2081 fn do_teleport_assets(
2082 origin: OriginFor<T>,
2083 dest: Box<VersionedLocation>,
2084 beneficiary: Box<VersionedLocation>,
2085 assets: Box<VersionedAssets>,
2086 fee_asset_item: u32,
2087 weight_limit: WeightLimit,
2088 ) -> DispatchResult {
2089 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2090 let dest = (*dest).try_into().map_err(|()| {
2091 tracing::debug!(
2092 target: "xcm::pallet_xcm::do_teleport_assets",
2093 "Failed to convert destination VersionedLocation",
2094 );
2095 Error::<T>::BadVersion
2096 })?;
2097 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2098 tracing::debug!(
2099 target: "xcm::pallet_xcm::do_teleport_assets",
2100 "Failed to convert beneficiary VersionedLocation",
2101 );
2102 Error::<T>::BadVersion
2103 })?;
2104 let assets: Assets = (*assets).try_into().map_err(|()| {
2105 tracing::debug!(
2106 target: "xcm::pallet_xcm::do_teleport_assets",
2107 "Failed to convert VersionedAssets",
2108 );
2109 Error::<T>::BadVersion
2110 })?;
2111 tracing::debug!(
2112 target: "xcm::pallet_xcm::do_teleport_assets",
2113 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
2114 );
2115
2116 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2117 let value = (origin_location, assets.into_inner());
2118 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2119 let (origin_location, assets) = value;
2120 for asset in assets.iter() {
2121 let transfer_type =
2122 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
2123 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
2124 }
2125 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
2126
2127 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2128 origin_location.clone(),
2129 dest.clone(),
2130 Either::Left(beneficiary),
2131 assets,
2132 TransferType::Teleport,
2133 FeesHandling::Batched { fees },
2134 weight_limit,
2135 )?;
2136 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
2137 }
2138
2139 fn do_transfer_assets(
2140 origin: Location,
2141 dest: Location,
2142 beneficiary: Either<Location, Xcm<()>>,
2143 mut assets: Vec<Asset>,
2144 assets_transfer_type: TransferType,
2145 fee_asset_index: usize,
2146 fees_transfer_type: TransferType,
2147 weight_limit: WeightLimit,
2148 ) -> DispatchResult {
2149 let fees = if fees_transfer_type == assets_transfer_type {
2151 let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
2152 FeesHandling::Batched { fees }
2154 } else {
2155 ensure!(
2161 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
2162 Error::<T>::InvalidAssetUnsupportedReserve
2163 );
2164 let weight_limit = weight_limit.clone();
2165 let fees = assets.remove(fee_asset_index);
2168 let (local_xcm, remote_xcm) = match fees_transfer_type {
2169 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
2170 origin.clone(),
2171 dest.clone(),
2172 fees,
2173 weight_limit,
2174 )?,
2175 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
2176 origin.clone(),
2177 dest.clone(),
2178 fees,
2179 weight_limit,
2180 )?,
2181 TransferType::Teleport => Self::teleport_fees_instructions(
2182 origin.clone(),
2183 dest.clone(),
2184 fees,
2185 weight_limit,
2186 )?,
2187 TransferType::RemoteReserve(_) =>
2188 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2189 };
2190 FeesHandling::Separate { local_xcm, remote_xcm }
2191 };
2192
2193 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2194 origin.clone(),
2195 dest.clone(),
2196 beneficiary,
2197 assets,
2198 assets_transfer_type,
2199 fees,
2200 weight_limit,
2201 )?;
2202 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2203 }
2204
2205 fn build_xcm_transfer_type(
2206 origin: Location,
2207 dest: Location,
2208 beneficiary: Either<Location, Xcm<()>>,
2209 assets: Vec<Asset>,
2210 transfer_type: TransferType,
2211 fees: FeesHandling<T>,
2212 weight_limit: WeightLimit,
2213 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
2214 tracing::debug!(
2215 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2216 ?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
2217 );
2218 match transfer_type {
2219 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
2220 origin.clone(),
2221 dest.clone(),
2222 beneficiary,
2223 assets,
2224 fees,
2225 weight_limit,
2226 )
2227 .map(|(local, remote)| (local, Some(remote))),
2228 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
2229 origin.clone(),
2230 dest.clone(),
2231 beneficiary,
2232 assets,
2233 fees,
2234 weight_limit,
2235 )
2236 .map(|(local, remote)| (local, Some(remote))),
2237 TransferType::RemoteReserve(reserve) => {
2238 let fees = match fees {
2239 FeesHandling::Batched { fees } => fees,
2240 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2241 };
2242 Self::remote_reserve_transfer_program(
2243 origin.clone(),
2244 reserve.try_into().map_err(|()| {
2245 tracing::debug!(
2246 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2247 "Failed to convert remote reserve location",
2248 );
2249 Error::<T>::BadVersion
2250 })?,
2251 beneficiary,
2252 dest.clone(),
2253 assets,
2254 fees,
2255 weight_limit,
2256 )
2257 .map(|local| (local, None))
2258 },
2259 TransferType::Teleport => Self::teleport_assets_program(
2260 origin.clone(),
2261 dest.clone(),
2262 beneficiary,
2263 assets,
2264 fees,
2265 weight_limit,
2266 )
2267 .map(|(local, remote)| (local, Some(remote))),
2268 }
2269 }
2270
2271 fn execute_xcm_transfer(
2272 origin: Location,
2273 dest: Location,
2274 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
2275 remote_xcm: Option<Xcm<()>>,
2276 ) -> DispatchResult {
2277 tracing::debug!(
2278 target: "xcm::pallet_xcm::execute_xcm_transfer",
2279 ?origin, ?dest, ?local_xcm, ?remote_xcm,
2280 );
2281
2282 let weight =
2283 T::Weigher::weight(&mut local_xcm, Weight::MAX).map_err(|error| {
2284 tracing::debug!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, "Failed to calculate weight");
2285 Error::<T>::UnweighableMessage
2286 })?;
2287 let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
2288 let outcome = T::XcmExecutor::prepare_and_execute(
2289 origin.clone(),
2290 local_xcm,
2291 &mut hash,
2292 weight,
2293 weight,
2294 );
2295 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
2296 outcome.clone().ensure_complete().map_err(|error| {
2297 tracing::error!(
2298 target: "xcm::pallet_xcm::execute_xcm_transfer",
2299 ?error, "XCM execution failed with error with outcome: {:?}", outcome
2300 );
2301 Error::<T>::LocalExecutionIncompleteWithError {
2302 index: error.index,
2303 error: error.error.into(),
2304 }
2305 })?;
2306
2307 if let Some(remote_xcm) = remote_xcm {
2308 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
2309 .map_err(|error| {
2310 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
2311 Error::<T>::from(error)
2312 })?;
2313 if origin != Here.into_location() {
2314 Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
2315 tracing::error!(
2316 target: "xcm::pallet_xcm::execute_xcm_transfer",
2317 ?error, ?price, ?origin, "Unable to charge fee",
2318 );
2319 Error::<T>::FeesNotMet
2320 })?;
2321 }
2322 let message_id = T::XcmRouter::deliver(ticket)
2323 .map_err(|error| {
2324 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
2325 Error::<T>::from(error)
2326 })?;
2327
2328 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
2329 Self::deposit_event(e);
2330 }
2331 Ok(())
2332 }
2333
2334 fn add_fees_to_xcm(
2335 dest: Location,
2336 fees: FeesHandling<T>,
2337 weight_limit: WeightLimit,
2338 local: &mut Xcm<<T as Config>::RuntimeCall>,
2339 remote: &mut Xcm<()>,
2340 ) -> Result<(), Error<T>> {
2341 match fees {
2342 FeesHandling::Batched { fees } => {
2343 let context = T::UniversalLocation::get();
2344 let reanchored_fees =
2347 fees.reanchored(&dest, &context).map_err(|e| {
2348 tracing::error!(target: "xcm::pallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
2349 Error::<T>::CannotReanchor
2350 })?;
2351 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
2353 },
2354 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
2355 core::mem::swap(local, &mut local_fees);
2358 core::mem::swap(remote, &mut remote_fees);
2359 local.inner_mut().append(&mut local_fees.into_inner());
2361 remote.inner_mut().append(&mut remote_fees.into_inner());
2362 },
2363 }
2364 Ok(())
2365 }
2366
2367 fn local_reserve_fees_instructions(
2368 origin: Location,
2369 dest: Location,
2370 fees: Asset,
2371 weight_limit: WeightLimit,
2372 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2373 let value = (origin, vec![fees.clone()]);
2374 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2375
2376 let context = T::UniversalLocation::get();
2377 let reanchored_fees = fees.clone().reanchored(&dest, &context).map_err(|_| {
2378 tracing::debug!(
2379 target: "xcm::pallet_xcm::local_reserve_fees_instructions",
2380 "Failed to re-anchor fees",
2381 );
2382 Error::<T>::CannotReanchor
2383 })?;
2384
2385 let local_execute_xcm = Xcm(vec![
2386 TransferAsset { assets: fees.into(), beneficiary: dest },
2388 ]);
2389 let xcm_on_dest = Xcm(vec![
2390 ReserveAssetDeposited(reanchored_fees.clone().into()),
2392 BuyExecution { fees: reanchored_fees, weight_limit },
2394 ]);
2395 Ok((local_execute_xcm, xcm_on_dest))
2396 }
2397
2398 fn local_reserve_transfer_programs(
2399 origin: Location,
2400 dest: Location,
2401 beneficiary: Either<Location, Xcm<()>>,
2402 assets: Vec<Asset>,
2403 fees: FeesHandling<T>,
2404 weight_limit: WeightLimit,
2405 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2406 let value = (origin, assets);
2407 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2408 let (_, assets) = value;
2409
2410 let max_assets =
2412 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2413 let assets: Assets = assets.into();
2414 let context = T::UniversalLocation::get();
2415 let mut reanchored_assets = assets.clone();
2416 reanchored_assets
2417 .reanchor(&dest, &context)
2418 .map_err(|e| {
2419 tracing::error!(target: "xcm::pallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2420 Error::<T>::CannotReanchor
2421 })?;
2422
2423 let mut local_execute_xcm = Xcm(vec![
2425 TransferAsset { assets, beneficiary: dest.clone() },
2427 ]);
2428 let mut xcm_on_dest = Xcm(vec![
2430 ReserveAssetDeposited(reanchored_assets),
2432 ClearOrigin,
2434 ]);
2435 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2437
2438 let custom_remote_xcm = match beneficiary {
2440 Either::Right(custom_xcm) => custom_xcm,
2441 Either::Left(beneficiary) => {
2442 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2444 },
2445 };
2446 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2447
2448 Ok((local_execute_xcm, xcm_on_dest))
2449 }
2450
2451 fn destination_reserve_fees_instructions(
2452 origin: Location,
2453 dest: Location,
2454 fees: Asset,
2455 weight_limit: WeightLimit,
2456 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2457 let value = (origin, vec![fees.clone()]);
2458 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2459 ensure!(
2460 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
2461 Error::<T>::InvalidAssetUnsupportedReserve
2462 );
2463
2464 let context = T::UniversalLocation::get();
2465 let reanchored_fees = fees
2466 .clone()
2467 .reanchored(&dest, &context)
2468 .map_err(|e| {
2469 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
2470 Error::<T>::CannotReanchor
2471 })?;
2472 let fees: Assets = fees.into();
2473
2474 let local_execute_xcm = Xcm(vec![
2475 WithdrawAsset(fees.clone()),
2477 BurnAsset(fees),
2479 ]);
2480 let xcm_on_dest = Xcm(vec![
2481 WithdrawAsset(reanchored_fees.clone().into()),
2483 BuyExecution { fees: reanchored_fees, weight_limit },
2485 ]);
2486 Ok((local_execute_xcm, xcm_on_dest))
2487 }
2488
2489 fn destination_reserve_transfer_programs(
2490 origin: Location,
2491 dest: Location,
2492 beneficiary: Either<Location, Xcm<()>>,
2493 assets: Vec<Asset>,
2494 fees: FeesHandling<T>,
2495 weight_limit: WeightLimit,
2496 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2497 let value = (origin, assets);
2498 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2499 let (_, assets) = value;
2500 for asset in assets.iter() {
2501 ensure!(
2502 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2503 Error::<T>::InvalidAssetUnsupportedReserve
2504 );
2505 }
2506
2507 let max_assets =
2509 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2510 let assets: Assets = assets.into();
2511 let context = T::UniversalLocation::get();
2512 let mut reanchored_assets = assets.clone();
2513 reanchored_assets
2514 .reanchor(&dest, &context)
2515 .map_err(|e| {
2516 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2517 Error::<T>::CannotReanchor
2518 })?;
2519
2520 let mut local_execute_xcm = Xcm(vec![
2522 WithdrawAsset(assets.clone()),
2524 BurnAsset(assets),
2526 ]);
2527 let mut xcm_on_dest = Xcm(vec![
2529 WithdrawAsset(reanchored_assets),
2531 ClearOrigin,
2533 ]);
2534 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2536
2537 let custom_remote_xcm = match beneficiary {
2539 Either::Right(custom_xcm) => custom_xcm,
2540 Either::Left(beneficiary) => {
2541 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2543 },
2544 };
2545 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2546
2547 Ok((local_execute_xcm, xcm_on_dest))
2548 }
2549
2550 fn remote_reserve_transfer_program(
2552 origin: Location,
2553 reserve: Location,
2554 beneficiary: Either<Location, Xcm<()>>,
2555 dest: Location,
2556 assets: Vec<Asset>,
2557 fees: Asset,
2558 weight_limit: WeightLimit,
2559 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2560 let value = (origin, assets);
2561 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2562 let (_, assets) = value;
2563
2564 let max_assets = assets.len() as u32;
2565 let context = T::UniversalLocation::get();
2566 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2569 let reserve_fees = fees_half_1
2571 .reanchored(&reserve, &context)
2572 .map_err(|e| {
2573 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2574 Error::<T>::CannotReanchor
2575 })?;
2576 let dest_fees = fees_half_2
2578 .reanchored(&dest, &context)
2579 .map_err(|e| {
2580 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2581 Error::<T>::CannotReanchor
2582 })?;
2583 let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2585 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2586 Error::<T>::CannotReanchor
2587 })?;
2588 let mut xcm_on_dest =
2590 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2591 let custom_xcm_on_dest = match beneficiary {
2593 Either::Right(custom_xcm) => custom_xcm,
2594 Either::Left(beneficiary) => {
2595 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2597 },
2598 };
2599 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2600 let xcm_on_reserve = Xcm(vec![
2602 BuyExecution { fees: reserve_fees, weight_limit },
2603 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2604 ]);
2605 Ok(Xcm(vec![
2606 WithdrawAsset(assets.into()),
2607 SetFeesMode { jit_withdraw: true },
2608 InitiateReserveWithdraw {
2609 assets: Wild(AllCounted(max_assets)),
2610 reserve,
2611 xcm: xcm_on_reserve,
2612 },
2613 ]))
2614 }
2615
2616 fn teleport_fees_instructions(
2617 origin: Location,
2618 dest: Location,
2619 fees: Asset,
2620 weight_limit: WeightLimit,
2621 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2622 let value = (origin, vec![fees.clone()]);
2623 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2624 ensure!(
2625 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2626 Error::<T>::Filtered
2627 );
2628
2629 let context = T::UniversalLocation::get();
2630 let reanchored_fees = fees
2631 .clone()
2632 .reanchored(&dest, &context)
2633 .map_err(|e| {
2634 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2635 Error::<T>::CannotReanchor
2636 })?;
2637
2638 let dummy_context =
2640 XcmContext { origin: None, message_id: Default::default(), topic: None };
2641 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2646 &dest,
2647 &fees,
2648 &dummy_context,
2649 )
2650 .map_err(|e| {
2651 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2652 Error::<T>::CannotCheckOutTeleport
2653 })?;
2654 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2657 &dest,
2658 &fees,
2659 &dummy_context,
2660 );
2661
2662 let fees: Assets = fees.into();
2663 let local_execute_xcm = Xcm(vec![
2664 WithdrawAsset(fees.clone()),
2666 BurnAsset(fees),
2668 ]);
2669 let xcm_on_dest = Xcm(vec![
2670 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2672 BuyExecution { fees: reanchored_fees, weight_limit },
2674 ]);
2675 Ok((local_execute_xcm, xcm_on_dest))
2676 }
2677
2678 fn teleport_assets_program(
2679 origin: Location,
2680 dest: Location,
2681 beneficiary: Either<Location, Xcm<()>>,
2682 assets: Vec<Asset>,
2683 fees: FeesHandling<T>,
2684 weight_limit: WeightLimit,
2685 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2686 let value = (origin, assets);
2687 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2688 let (_, assets) = value;
2689 for asset in assets.iter() {
2690 ensure!(
2691 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2692 Error::<T>::Filtered
2693 );
2694 }
2695
2696 let max_assets =
2698 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2699 let context = T::UniversalLocation::get();
2700 let assets: Assets = assets.into();
2701 let mut reanchored_assets = assets.clone();
2702 reanchored_assets
2703 .reanchor(&dest, &context)
2704 .map_err(|e| {
2705 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2706 Error::<T>::CannotReanchor
2707 })?;
2708
2709 let dummy_context =
2711 XcmContext { origin: None, message_id: Default::default(), topic: None };
2712 for asset in assets.inner() {
2713 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2718 &dest,
2719 asset,
2720 &dummy_context,
2721 )
2722 .map_err(|e| {
2723 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2724 Error::<T>::CannotCheckOutTeleport
2725 })?;
2726 }
2727 for asset in assets.inner() {
2728 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2731 &dest,
2732 asset,
2733 &dummy_context,
2734 );
2735 }
2736
2737 let mut local_execute_xcm = Xcm(vec![
2739 WithdrawAsset(assets.clone()),
2741 BurnAsset(assets),
2743 ]);
2744 let mut xcm_on_dest = Xcm(vec![
2746 ReceiveTeleportedAsset(reanchored_assets),
2748 ClearOrigin,
2750 ]);
2751 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2753
2754 let custom_remote_xcm = match beneficiary {
2756 Either::Right(custom_xcm) => custom_xcm,
2757 Either::Left(beneficiary) => {
2758 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2760 },
2761 };
2762 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2763
2764 Ok((local_execute_xcm, xcm_on_dest))
2765 }
2766
2767 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2769 match fees.fun {
2770 Fungible(amount) => {
2771 let fee1 = amount.saturating_div(2);
2772 let fee2 = amount.saturating_sub(fee1);
2773 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2774 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2775 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2776 },
2777 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2778 }
2779 }
2780
2781 pub(crate) fn lazy_migration(
2784 mut stage: VersionMigrationStage,
2785 weight_cutoff: Weight,
2786 ) -> (Weight, Option<VersionMigrationStage>) {
2787 let mut weight_used = Weight::zero();
2788
2789 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2790 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2791 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2792 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2793 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2794 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2795 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2796
2797 use VersionMigrationStage::*;
2798
2799 if stage == MigrateSupportedVersion {
2800 for v in 0..XCM_VERSION {
2803 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2804 if let Ok(new_key) = old_key.into_latest() {
2805 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2806 }
2807 weight_used.saturating_accrue(sv_migrate_weight);
2808 if weight_used.any_gte(weight_cutoff) {
2809 return (weight_used, Some(stage))
2810 }
2811 }
2812 }
2813 stage = MigrateVersionNotifiers;
2814 }
2815 if stage == MigrateVersionNotifiers {
2816 for v in 0..XCM_VERSION {
2817 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2818 if let Ok(new_key) = old_key.into_latest() {
2819 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2820 }
2821 weight_used.saturating_accrue(vn_migrate_weight);
2822 if weight_used.any_gte(weight_cutoff) {
2823 return (weight_used, Some(stage))
2824 }
2825 }
2826 }
2827 stage = NotifyCurrentTargets(None);
2828 }
2829
2830 let xcm_version = T::AdvertisedXcmVersion::get();
2831
2832 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2833 let mut iter = match maybe_last_raw_key {
2834 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2835 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2836 };
2837 while let Some((key, value)) = iter.next() {
2838 let (query_id, max_weight, target_xcm_version) = value;
2839 let new_key: Location = match key.clone().try_into() {
2840 Ok(k) if target_xcm_version != xcm_version => k,
2841 _ => {
2842 weight_used.saturating_accrue(vnt_already_notified_weight);
2845 continue
2846 },
2847 };
2848 let response = Response::Version(xcm_version);
2849 let message =
2850 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2851 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2852 Ok((message_id, cost)) => {
2853 let value = (query_id, max_weight, xcm_version);
2854 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2855 Event::VersionChangeNotified {
2856 destination: new_key,
2857 result: xcm_version,
2858 cost,
2859 message_id,
2860 }
2861 },
2862 Err(e) => {
2863 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2864 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2865 },
2866 };
2867 Self::deposit_event(event);
2868 weight_used.saturating_accrue(vnt_notify_weight);
2869 if weight_used.any_gte(weight_cutoff) {
2870 let last = Some(iter.last_raw_key().into());
2871 return (weight_used, Some(NotifyCurrentTargets(last)))
2872 }
2873 }
2874 stage = MigrateAndNotifyOldTargets;
2875 }
2876 if stage == MigrateAndNotifyOldTargets {
2877 for v in 0..XCM_VERSION {
2878 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2879 let (query_id, max_weight, target_xcm_version) = value;
2880 let new_key = match Location::try_from(old_key.clone()) {
2881 Ok(k) => k,
2882 Err(()) => {
2883 Self::deposit_event(Event::NotifyTargetMigrationFail {
2884 location: old_key,
2885 query_id: value.0,
2886 });
2887 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2888 if weight_used.any_gte(weight_cutoff) {
2889 return (weight_used, Some(stage))
2890 }
2891 continue
2892 },
2893 };
2894
2895 let versioned_key = LatestVersionedLocation(&new_key);
2896 if target_xcm_version == xcm_version {
2897 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2898 weight_used.saturating_accrue(vnt_migrate_weight);
2899 } else {
2900 let response = Response::Version(xcm_version);
2902 let message = Xcm(vec![QueryResponse {
2903 query_id,
2904 response,
2905 max_weight,
2906 querier: None,
2907 }]);
2908 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2909 Ok((message_id, cost)) => {
2910 VersionNotifyTargets::<T>::insert(
2911 XCM_VERSION,
2912 versioned_key,
2913 (query_id, max_weight, xcm_version),
2914 );
2915 Event::VersionChangeNotified {
2916 destination: new_key,
2917 result: xcm_version,
2918 cost,
2919 message_id,
2920 }
2921 },
2922 Err(e) => Event::NotifyTargetSendFail {
2923 location: new_key,
2924 query_id,
2925 error: e.into(),
2926 },
2927 };
2928 Self::deposit_event(event);
2929 weight_used.saturating_accrue(vnt_notify_migrate_weight);
2930 }
2931 if weight_used.any_gte(weight_cutoff) {
2932 return (weight_used, Some(stage))
2933 }
2934 }
2935 }
2936 }
2937 (weight_used, None)
2938 }
2939
2940 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2942 let dest = dest.into();
2943 let versioned_dest = VersionedLocation::from(dest.clone());
2944 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
2945 ensure!(!already, XcmError::InvalidLocation);
2946 let query_id = QueryCounter::<T>::mutate(|q| {
2947 let r = *q;
2948 q.saturating_inc();
2949 r
2950 });
2951 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
2953 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
2954 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
2955 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
2956 let query_status =
2957 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
2958 Queries::<T>::insert(query_id, query_status);
2959 Ok(())
2960 }
2961
2962 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
2964 let dest = dest.into();
2965 let versioned_dest = LatestVersionedLocation(&dest);
2966 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
2967 .ok_or(XcmError::InvalidLocation)?;
2968 let (message_id, cost) =
2969 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
2970 Self::deposit_event(Event::VersionNotifyUnrequested {
2971 destination: dest,
2972 cost,
2973 message_id,
2974 });
2975 Queries::<T>::remove(query_id);
2976 Ok(())
2977 }
2978
2979 pub fn send_xcm(
2983 interior: impl Into<Junctions>,
2984 dest: impl Into<Location>,
2985 mut message: Xcm<()>,
2986 ) -> Result<XcmHash, SendError> {
2987 let interior = interior.into();
2988 let local_origin = interior.clone().into();
2989 let dest = dest.into();
2990 let is_waived =
2991 <T::XcmExecutor as FeeManager>::is_waived(Some(&local_origin), FeeReason::ChargeFees);
2992 if interior != Junctions::Here {
2993 message.0.insert(0, DescendOrigin(interior.clone()));
2994 }
2995 tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
2996 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
2997 if !is_waived {
2998 Self::charge_fees(local_origin, price).map_err(|e| {
2999 tracing::error!(
3000 target: "xcm::pallet_xcm::send_xcm",
3001 ?e,
3002 "Charging fees failed with error",
3003 );
3004 SendError::Fees
3005 })?;
3006 }
3007 T::XcmRouter::deliver(ticket)
3008 }
3009
3010 pub fn check_account() -> T::AccountId {
3011 const ID: PalletId = PalletId(*b"py/xcmch");
3012 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
3013 }
3014
3015 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
3021 origin: OriginCaller,
3022 call: RuntimeCall,
3023 result_xcms_version: XcmVersion,
3024 ) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3025 where
3026 Runtime: crate::Config,
3027 Router: InspectMessageQueues,
3028 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
3029 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
3030 {
3031 crate::Pallet::<Runtime>::set_record_xcm(true);
3032 Router::clear_messages();
3034 frame_system::Pallet::<Runtime>::reset_events();
3036 let result = call.dispatch(origin.into());
3037 crate::Pallet::<Runtime>::set_record_xcm(false);
3038 let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
3039 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
3040 .transpose()
3041 .map_err(|()| {
3042 tracing::error!(
3043 target: "xcm::DryRunApi::dry_run_call",
3044 "Local xcm version conversion failed"
3045 );
3046
3047 XcmDryRunApiError::VersionedConversionFailed
3048 })?;
3049
3050 let forwarded_xcms =
3052 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
3053 |error| {
3054 tracing::error!(
3055 target: "xcm::DryRunApi::dry_run_call",
3056 ?error, "Forwarded xcms version conversion failed with error"
3057 );
3058 },
3059 )?;
3060 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
3061 frame_system::Pallet::<Runtime>::read_events_no_consensus()
3062 .map(|record| record.event.clone())
3063 .collect();
3064 Ok(CallDryRunEffects {
3065 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
3066 forwarded_xcms,
3067 emitted_events: events,
3068 execution_result: result,
3069 })
3070 }
3071
3072 pub fn dry_run_xcm<Runtime, Router, RuntimeCall: Decode + GetDispatchInfo, XcmConfig>(
3077 origin_location: VersionedLocation,
3078 xcm: VersionedXcm<RuntimeCall>,
3079 ) -> Result<XcmDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3080 where
3081 Runtime: frame_system::Config,
3082 Router: InspectMessageQueues,
3083 XcmConfig: xcm_executor::Config<RuntimeCall = RuntimeCall>,
3084 {
3085 let origin_location: Location = origin_location.try_into().map_err(|error| {
3086 tracing::error!(
3087 target: "xcm::DryRunApi::dry_run_xcm",
3088 ?error, "Location version conversion failed with error"
3089 );
3090 XcmDryRunApiError::VersionedConversionFailed
3091 })?;
3092 let xcm_version = xcm.identify_version();
3093 let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
3094 tracing::error!(
3095 target: "xcm::DryRunApi::dry_run_xcm",
3096 ?error, "Xcm version conversion failed with error"
3097 );
3098 XcmDryRunApiError::VersionedConversionFailed
3099 })?;
3100 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
3101
3102 Router::clear_messages();
3104 frame_system::Pallet::<Runtime>::reset_events();
3105
3106 let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
3107 origin_location,
3108 xcm,
3109 &mut hash,
3110 Weight::MAX, Weight::zero(),
3112 );
3113 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
3114 .inspect_err(|error| {
3115 tracing::error!(
3116 target: "xcm::DryRunApi::dry_run_xcm",
3117 ?error, "Forwarded xcms version conversion failed with error"
3118 );
3119 })?;
3120 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
3121 frame_system::Pallet::<Runtime>::read_events_no_consensus()
3122 .map(|record| record.event.clone())
3123 .collect();
3124 Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
3125 }
3126
3127 fn convert_xcms(
3128 xcm_version: XcmVersion,
3129 xcms: Vec<VersionedXcm<()>>,
3130 ) -> Result<Vec<VersionedXcm<()>>, ()> {
3131 xcms.into_iter()
3132 .map(|xcm| xcm.into_version(xcm_version))
3133 .collect::<Result<Vec<_>, ()>>()
3134 }
3135
3136 fn convert_forwarded_xcms(
3137 xcm_version: XcmVersion,
3138 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
3139 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
3140 forwarded_xcms
3141 .into_iter()
3142 .map(|(dest, forwarded_xcms)| {
3143 let dest = dest.into_version(xcm_version)?;
3144 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
3145
3146 Ok((dest, forwarded_xcms))
3147 })
3148 .collect::<Result<Vec<_>, ()>>()
3149 .map_err(|()| {
3150 tracing::debug!(
3151 target: "xcm::pallet_xcm::convert_forwarded_xcms",
3152 "Failed to convert VersionedLocation to requested version",
3153 );
3154 XcmDryRunApiError::VersionedConversionFailed
3155 })
3156 }
3157
3158 pub fn query_acceptable_payment_assets(
3163 version: xcm::Version,
3164 asset_ids: Vec<AssetId>,
3165 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
3166 Ok(asset_ids
3167 .into_iter()
3168 .map(|asset_id| VersionedAssetId::from(asset_id))
3169 .filter_map(|asset_id| asset_id.into_version(version).ok())
3170 .collect())
3171 }
3172
3173 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
3174 let message = Xcm::<()>::try_from(message.clone())
3175 .map_err(|e| {
3176 tracing::debug!(target: "xcm::pallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
3177 XcmPaymentApiError::VersionedConversionFailed
3178 })?;
3179
3180 T::Weigher::weight(&mut message.clone().into(), Weight::MAX).map_err(|error| {
3181 tracing::debug!(target: "xcm::pallet_xcm::query_xcm_weight", ?error, ?message, "Error when querying XCM weight");
3182 XcmPaymentApiError::WeightNotComputable
3183 })
3184 }
3185
3186 pub fn query_weight_to_asset_fee<Trader: xcm_executor::traits::WeightTrader>(
3203 weight: Weight,
3204 asset: VersionedAssetId,
3205 ) -> Result<u128, XcmPaymentApiError> {
3206 let asset: AssetId = asset.clone().try_into()
3207 .map_err(|e| {
3208 tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to convert versioned asset");
3209 XcmPaymentApiError::VersionedConversionFailed
3210 })?;
3211
3212 let max_amount = u128::MAX / 2;
3213 let max_payment: Asset = (asset.clone(), max_amount).into();
3214 let context = XcmContext::with_message_id(XcmHash::default());
3215
3216 let unspent = with_transaction(|| {
3219 let mut trader = Trader::new();
3220 let result = trader.buy_weight(weight, max_payment.into(), &context)
3221 .map_err(|e| {
3222 tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to buy weight");
3223
3224 DispatchError::Other("Failed to buy weight")
3226 });
3227
3228 TransactionOutcome::Rollback(result)
3229 }).map_err(|error| {
3230 tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?error, "Failed to execute transaction");
3231 XcmPaymentApiError::AssetNotFound
3232 })?;
3233
3234 let Some(unspent) = unspent.fungible.get(&asset) else {
3235 tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?asset, "The trader didn't return the needed fungible asset");
3236 return Err(XcmPaymentApiError::AssetNotFound);
3237 };
3238
3239 let paid = max_amount - unspent;
3240 Ok(paid)
3241 }
3242
3243 pub fn query_delivery_fees(
3245 destination: VersionedLocation,
3246 message: VersionedXcm<()>,
3247 ) -> Result<VersionedAssets, XcmPaymentApiError> {
3248 let result_version = destination.identify_version().max(message.identify_version());
3249
3250 let destination: Location = destination
3251 .clone()
3252 .try_into()
3253 .map_err(|e| {
3254 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
3255 XcmPaymentApiError::VersionedConversionFailed
3256 })?;
3257
3258 let message: Xcm<()> =
3259 message.clone().try_into().map_err(|e| {
3260 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
3261 XcmPaymentApiError::VersionedConversionFailed
3262 })?;
3263
3264 let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
3265 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
3266 XcmPaymentApiError::Unroutable
3267 })?;
3268
3269 VersionedAssets::from(fees)
3270 .into_version(result_version)
3271 .map_err(|e| {
3272 tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?result_version, "Failed to convert fees into version");
3273 XcmPaymentApiError::VersionedConversionFailed
3274 })
3275 }
3276
3277 pub fn is_trusted_reserve(
3280 asset: VersionedAsset,
3281 location: VersionedLocation,
3282 ) -> Result<bool, TrustedQueryApiError> {
3283 let location: Location = location.try_into().map_err(|e| {
3284 tracing::debug!(
3285 target: "xcm::pallet_xcm::is_trusted_reserve",
3286 ?e, "Failed to convert versioned location",
3287 );
3288 TrustedQueryApiError::VersionedLocationConversionFailed
3289 })?;
3290
3291 let a: Asset = asset.try_into().map_err(|e| {
3292 tracing::debug!(
3293 target: "xcm::pallet_xcm::is_trusted_reserve",
3294 ?e, "Failed to convert versioned asset",
3295 );
3296 TrustedQueryApiError::VersionedAssetConversionFailed
3297 })?;
3298
3299 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
3300 }
3301
3302 pub fn is_trusted_teleporter(
3304 asset: VersionedAsset,
3305 location: VersionedLocation,
3306 ) -> Result<bool, TrustedQueryApiError> {
3307 let location: Location = location.try_into().map_err(|e| {
3308 tracing::debug!(
3309 target: "xcm::pallet_xcm::is_trusted_teleporter",
3310 ?e, "Failed to convert versioned location",
3311 );
3312 TrustedQueryApiError::VersionedLocationConversionFailed
3313 })?;
3314 let a: Asset = asset.try_into().map_err(|e| {
3315 tracing::debug!(
3316 target: "xcm::pallet_xcm::is_trusted_teleporter",
3317 ?e, "Failed to convert versioned asset",
3318 );
3319 TrustedQueryApiError::VersionedAssetConversionFailed
3320 })?;
3321 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
3322 }
3323
3324 pub fn authorized_aliasers(
3326 target: VersionedLocation,
3327 ) -> Result<Vec<OriginAliaser>, AuthorizedAliasersApiError> {
3328 let desired_version = target.identify_version();
3329 let target: VersionedLocation = target.into_version(XCM_VERSION).map_err(|e| {
3331 tracing::debug!(
3332 target: "xcm::pallet_xcm::authorized_aliasers",
3333 ?e, "Failed to convert versioned location",
3334 );
3335 AuthorizedAliasersApiError::LocationVersionConversionFailed
3336 })?;
3337 Ok(AuthorizedAliases::<T>::get(&target)
3338 .map(|authorized| {
3339 authorized
3340 .aliasers
3341 .into_iter()
3342 .filter_map(|aliaser| {
3343 let OriginAliaser { location, expiry } = aliaser;
3344 location
3345 .into_version(desired_version)
3346 .map(|location| OriginAliaser { location, expiry })
3347 .ok()
3348 })
3349 .collect()
3350 })
3351 .unwrap_or_default())
3352 }
3353
3354 pub fn is_authorized_alias(
3359 origin: VersionedLocation,
3360 target: VersionedLocation,
3361 ) -> Result<bool, AuthorizedAliasersApiError> {
3362 let desired_version = target.identify_version();
3363 let origin = origin.into_version(desired_version).map_err(|e| {
3364 tracing::debug!(
3365 target: "xcm::pallet_xcm::is_authorized_alias",
3366 ?e, "mismatching origin and target versions",
3367 );
3368 AuthorizedAliasersApiError::LocationVersionConversionFailed
3369 })?;
3370 Ok(Self::authorized_aliasers(target)?.into_iter().any(|aliaser| {
3371 aliaser.location == origin &&
3374 aliaser
3375 .expiry
3376 .map(|expiry| {
3377 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>() <
3378 expiry
3379 })
3380 .unwrap_or(true)
3381 }))
3382 }
3383
3384 fn do_new_query(
3386 responder: impl Into<Location>,
3387 maybe_notify: Option<(u8, u8)>,
3388 timeout: BlockNumberFor<T>,
3389 match_querier: impl Into<Location>,
3390 ) -> u64 {
3391 QueryCounter::<T>::mutate(|q| {
3392 let r = *q;
3393 q.saturating_inc();
3394 Queries::<T>::insert(
3395 r,
3396 QueryStatus::Pending {
3397 responder: responder.into().into(),
3398 maybe_match_querier: Some(match_querier.into().into()),
3399 maybe_notify,
3400 timeout,
3401 },
3402 );
3403 r
3404 })
3405 }
3406
3407 pub fn report_outcome_notify(
3430 message: &mut Xcm<()>,
3431 responder: impl Into<Location>,
3432 notify: impl Into<<T as Config>::RuntimeCall>,
3433 timeout: BlockNumberFor<T>,
3434 ) -> Result<(), XcmError> {
3435 let responder = responder.into();
3436 let destination = T::UniversalLocation::get().invert_target(&responder).map_err(|()| {
3437 tracing::debug!(
3438 target: "xcm::pallet_xcm::report_outcome_notify",
3439 "Failed to invert responder location to universal location",
3440 );
3441 XcmError::LocationNotInvertible
3442 })?;
3443 let notify: <T as Config>::RuntimeCall = notify.into();
3444 let max_weight = notify.get_dispatch_info().call_weight;
3445 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
3446 let response_info = QueryResponseInfo { destination, query_id, max_weight };
3447 let report_error = Xcm(vec![ReportError(response_info)]);
3448 message.0.insert(0, SetAppendix(report_error));
3449 Ok(())
3450 }
3451
3452 pub fn new_notify_query(
3455 responder: impl Into<Location>,
3456 notify: impl Into<<T as Config>::RuntimeCall>,
3457 timeout: BlockNumberFor<T>,
3458 match_querier: impl Into<Location>,
3459 ) -> u64 {
3460 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
3461 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
3462 );
3463 Self::do_new_query(responder, Some(notify), timeout, match_querier)
3464 }
3465
3466 fn note_unknown_version(dest: &Location) {
3469 tracing::trace!(
3470 target: "xcm::pallet_xcm::note_unknown_version",
3471 ?dest, "XCM version is unknown for destination"
3472 );
3473 let versioned_dest = VersionedLocation::from(dest.clone());
3474 VersionDiscoveryQueue::<T>::mutate(|q| {
3475 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
3476 q[index].1.saturating_inc();
3478 } else {
3479 let _ = q.try_push((versioned_dest, 1));
3480 }
3481 });
3482 }
3483
3484 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
3490 T::XcmExecutor::charge_fees(location.clone(), assets.clone()).map_err(|error| {
3491 tracing::debug!(
3492 target: "xcm::pallet_xcm::charge_fees", ?error,
3493 "Failed to charge fees for location with assets",
3494 );
3495 Error::<T>::FeesNotMet
3496 })?;
3497 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
3498 Ok(())
3499 }
3500
3501 #[cfg(any(feature = "try-runtime", test))]
3511 pub fn do_try_state() -> Result<(), TryRuntimeError> {
3512 use migration::data::NeedsMigration;
3513
3514 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
3518 {
3519 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
3520 } else {
3521 XCM_VERSION.saturating_sub(1)
3522 };
3523
3524 ensure!(
3526 !Queries::<T>::iter_values()
3527 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3528 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
3529 );
3530
3531 ensure!(
3533 !LockedFungibles::<T>::iter_values()
3534 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3535 TryRuntimeError::Other(
3536 "`LockedFungibles` data should be migrated to the higher xcm version!"
3537 )
3538 );
3539
3540 ensure!(
3542 !RemoteLockedFungibles::<T>::iter()
3543 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
3544 data.needs_migration(minimal_allowed_xcm_version)),
3545 TryRuntimeError::Other(
3546 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
3547 )
3548 );
3549
3550 if CurrentMigration::<T>::exists() {
3553 return Ok(())
3554 }
3555
3556 for v in 0..XCM_VERSION {
3558 ensure!(
3559 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
3560 TryRuntimeError::Other(
3561 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
3562 )
3563 );
3564 ensure!(
3565 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
3566 TryRuntimeError::Other(
3567 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
3568 )
3569 );
3570 ensure!(
3571 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
3572 TryRuntimeError::Other(
3573 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
3574 )
3575 );
3576 }
3577
3578 Ok(())
3579 }
3580}
3581
3582pub struct LockTicket<T: Config> {
3583 sovereign_account: T::AccountId,
3584 amount: BalanceOf<T>,
3585 unlocker: Location,
3586 item_index: Option<usize>,
3587}
3588
3589impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
3590 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3591 use xcm_executor::traits::LockError::UnexpectedState;
3592 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
3593 match self.item_index {
3594 Some(index) => {
3595 ensure!(locks.len() > index, UnexpectedState);
3596 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
3597 locks[index].0 = locks[index].0.max(self.amount);
3598 },
3599 None => {
3600 locks.try_push((self.amount, self.unlocker.into())).map_err(
3601 |(balance, location)| {
3602 tracing::debug!(
3603 target: "xcm::pallet_xcm::enact", ?balance, ?location,
3604 "Failed to lock fungibles",
3605 );
3606 UnexpectedState
3607 },
3608 )?;
3609 },
3610 }
3611 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3612 T::Currency::extend_lock(
3613 *b"py/xcmlk",
3614 &self.sovereign_account,
3615 self.amount,
3616 WithdrawReasons::all(),
3617 );
3618 Ok(())
3619 }
3620}
3621
3622pub struct UnlockTicket<T: Config> {
3623 sovereign_account: T::AccountId,
3624 amount: BalanceOf<T>,
3625 unlocker: Location,
3626}
3627
3628impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
3629 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3630 use xcm_executor::traits::LockError::UnexpectedState;
3631 let mut locks =
3632 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
3633 let mut maybe_remove_index = None;
3634 let mut locked = BalanceOf::<T>::zero();
3635 let mut found = false;
3636 for (i, x) in locks.iter_mut().enumerate() {
3639 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
3640 x.0 = x.0.saturating_sub(self.amount);
3641 if x.0.is_zero() {
3642 maybe_remove_index = Some(i);
3643 }
3644 found = true;
3645 }
3646 locked = locked.max(x.0);
3647 }
3648 ensure!(found, UnexpectedState);
3649 if let Some(remove_index) = maybe_remove_index {
3650 locks.swap_remove(remove_index);
3651 }
3652 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3653 let reasons = WithdrawReasons::all();
3654 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3655 Ok(())
3656 }
3657}
3658
3659pub struct ReduceTicket<T: Config> {
3660 key: (u32, T::AccountId, VersionedAssetId),
3661 amount: u128,
3662 locker: VersionedLocation,
3663 owner: VersionedLocation,
3664}
3665
3666impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3667 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3668 use xcm_executor::traits::LockError::UnexpectedState;
3669 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3670 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3671 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3672 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3673 if new_amount == 0 {
3674 RemoteLockedFungibles::<T>::remove(&self.key);
3675 } else {
3676 record.amount = new_amount;
3677 RemoteLockedFungibles::<T>::insert(&self.key, &record);
3678 }
3679 Ok(())
3680 }
3681}
3682
3683impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
3684 type LockTicket = LockTicket<T>;
3685 type UnlockTicket = UnlockTicket<T>;
3686 type ReduceTicket = ReduceTicket<T>;
3687
3688 fn prepare_lock(
3689 unlocker: Location,
3690 asset: Asset,
3691 owner: Location,
3692 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3693 use xcm_executor::traits::LockError::*;
3694 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3695 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3696 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3697 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3698 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3699 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3700 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3701 }
3702
3703 fn prepare_unlock(
3704 unlocker: Location,
3705 asset: Asset,
3706 owner: Location,
3707 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3708 use xcm_executor::traits::LockError::*;
3709 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3710 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3711 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3712 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3713 let item_index =
3714 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3715 ensure!(locks[item_index].0 >= amount, NotLocked);
3716 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3717 }
3718
3719 fn note_unlockable(
3720 locker: Location,
3721 asset: Asset,
3722 mut owner: Location,
3723 ) -> Result<(), xcm_executor::traits::LockError> {
3724 use xcm_executor::traits::LockError::*;
3725 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3726 let amount = match asset.fun {
3727 Fungible(a) => a,
3728 NonFungible(_) => return Err(Unimplemented),
3729 };
3730 owner.remove_network_id();
3731 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3732 let locker = locker.into();
3733 let owner = owner.into();
3734 let id: VersionedAssetId = asset.id.into();
3735 let key = (XCM_VERSION, account, id);
3736 let mut record =
3737 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3738 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3739 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3741 record.consumers = old.consumers;
3742 record.amount = record.amount.max(old.amount);
3743 }
3744 RemoteLockedFungibles::<T>::insert(&key, record);
3745 Ok(())
3746 }
3747
3748 fn prepare_reduce_unlockable(
3749 locker: Location,
3750 asset: Asset,
3751 mut owner: Location,
3752 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3753 use xcm_executor::traits::LockError::*;
3754 let amount = match asset.fun {
3755 Fungible(a) => a,
3756 NonFungible(_) => return Err(Unimplemented),
3757 };
3758 owner.remove_network_id();
3759 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3760 let locker = locker.into();
3761 let owner = owner.into();
3762 let id: VersionedAssetId = asset.id.into();
3763 let key = (XCM_VERSION, sovereign_account, id);
3764
3765 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3766 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3768 ensure!(record.amount >= amount, NotEnoughLocked);
3769 ensure!(
3770 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3771 InUse
3772 );
3773 Ok(ReduceTicket { key, amount, locker, owner })
3774 }
3775}
3776
3777impl<T: Config> WrapVersion for Pallet<T> {
3778 fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3779 dest: &Location,
3780 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3781 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3782 Self::get_version_for(dest)
3783 .or_else(|| {
3784 Self::note_unknown_version(dest);
3785 SafeXcmVersion::<T>::get()
3786 })
3787 .ok_or_else(|| {
3788 tracing::trace!(
3789 target: "xcm::pallet_xcm::wrap_version",
3790 ?dest, "Could not determine a version to wrap XCM for destination",
3791 );
3792 ()
3793 })
3794 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3795 }
3796}
3797
3798impl<T: Config> GetVersion for Pallet<T> {
3799 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3800 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3801 }
3802}
3803
3804impl<T: Config> VersionChangeNotifier for Pallet<T> {
3805 fn start(
3814 dest: &Location,
3815 query_id: QueryId,
3816 max_weight: Weight,
3817 _context: &XcmContext,
3818 ) -> XcmResult {
3819 let versioned_dest = LatestVersionedLocation(dest);
3820 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3821 ensure!(!already, XcmError::InvalidLocation);
3822
3823 let xcm_version = T::AdvertisedXcmVersion::get();
3824 let response = Response::Version(xcm_version);
3825 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3826 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3827 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3828 destination: dest.clone(),
3829 cost,
3830 message_id,
3831 });
3832
3833 let value = (query_id, max_weight, xcm_version);
3834 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3835 Ok(())
3836 }
3837
3838 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3841 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3842 Ok(())
3843 }
3844
3845 fn is_subscribed(dest: &Location) -> bool {
3847 let versioned_dest = LatestVersionedLocation(dest);
3848 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3849 }
3850}
3851
3852impl<T: Config> DropAssets for Pallet<T> {
3853 fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
3854 if assets.is_empty() {
3855 return Weight::zero()
3856 }
3857 let versioned = VersionedAssets::from(Assets::from(assets));
3858 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3859 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3860 Self::deposit_event(Event::AssetsTrapped {
3861 hash,
3862 origin: origin.clone(),
3863 assets: versioned,
3864 });
3865 Weight::zero()
3867 }
3868}
3869
3870impl<T: Config> ClaimAssets for Pallet<T> {
3871 fn claim_assets(
3872 origin: &Location,
3873 ticket: &Location,
3874 assets: &Assets,
3875 _context: &XcmContext,
3876 ) -> bool {
3877 let mut versioned = VersionedAssets::from(assets.clone());
3878 match ticket.unpack() {
3879 (0, [GeneralIndex(i)]) =>
3880 versioned = match versioned.into_version(*i as u32) {
3881 Ok(v) => v,
3882 Err(()) => return false,
3883 },
3884 (0, []) => (),
3885 _ => return false,
3886 };
3887 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3888 match AssetTraps::<T>::get(hash) {
3889 0 => return false,
3890 1 => AssetTraps::<T>::remove(hash),
3891 n => AssetTraps::<T>::insert(hash, n - 1),
3892 }
3893 Self::deposit_event(Event::AssetsClaimed {
3894 hash,
3895 origin: origin.clone(),
3896 assets: versioned,
3897 });
3898 return true
3899 }
3900}
3901
3902impl<T: Config> OnResponse for Pallet<T> {
3903 fn expecting_response(
3904 origin: &Location,
3905 query_id: QueryId,
3906 querier: Option<&Location>,
3907 ) -> bool {
3908 match Queries::<T>::get(query_id) {
3909 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) =>
3910 Location::try_from(responder).map_or(false, |r| origin == &r) &&
3911 maybe_match_querier.map_or(true, |match_querier| {
3912 Location::try_from(match_querier).map_or(false, |match_querier| {
3913 querier.map_or(false, |q| q == &match_querier)
3914 })
3915 }),
3916 Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
3917 Location::try_from(r).map_or(false, |r| origin == &r),
3918 _ => false,
3919 }
3920 }
3921
3922 fn on_response(
3923 origin: &Location,
3924 query_id: QueryId,
3925 querier: Option<&Location>,
3926 response: Response,
3927 max_weight: Weight,
3928 _context: &XcmContext,
3929 ) -> Weight {
3930 let origin = origin.clone();
3931 match (response, Queries::<T>::get(query_id)) {
3932 (
3933 Response::Version(v),
3934 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
3935 ) => {
3936 let origin: Location = match expected_origin.try_into() {
3937 Ok(o) if o == origin => o,
3938 Ok(o) => {
3939 Self::deposit_event(Event::InvalidResponder {
3940 origin: origin.clone(),
3941 query_id,
3942 expected_location: Some(o),
3943 });
3944 return Weight::zero()
3945 },
3946 _ => {
3947 Self::deposit_event(Event::InvalidResponder {
3948 origin: origin.clone(),
3949 query_id,
3950 expected_location: None,
3951 });
3952 return Weight::zero()
3954 },
3955 };
3956 if !is_active {
3958 Queries::<T>::insert(
3959 query_id,
3960 QueryStatus::VersionNotifier {
3961 origin: origin.clone().into(),
3962 is_active: true,
3963 },
3964 );
3965 }
3966 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
3968 Self::deposit_event(Event::SupportedVersionChanged {
3969 location: origin,
3970 version: v,
3971 });
3972 Weight::zero()
3973 },
3974 (
3975 response,
3976 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
3977 ) => {
3978 if let Some(match_querier) = maybe_match_querier {
3979 let match_querier = match Location::try_from(match_querier) {
3980 Ok(mq) => mq,
3981 Err(_) => {
3982 Self::deposit_event(Event::InvalidQuerierVersion {
3983 origin: origin.clone(),
3984 query_id,
3985 });
3986 return Weight::zero()
3987 },
3988 };
3989 if querier.map_or(true, |q| q != &match_querier) {
3990 Self::deposit_event(Event::InvalidQuerier {
3991 origin: origin.clone(),
3992 query_id,
3993 expected_querier: match_querier,
3994 maybe_actual_querier: querier.cloned(),
3995 });
3996 return Weight::zero()
3997 }
3998 }
3999 let responder = match Location::try_from(responder) {
4000 Ok(r) => r,
4001 Err(_) => {
4002 Self::deposit_event(Event::InvalidResponderVersion {
4003 origin: origin.clone(),
4004 query_id,
4005 });
4006 return Weight::zero()
4007 },
4008 };
4009 if origin != responder {
4010 Self::deposit_event(Event::InvalidResponder {
4011 origin: origin.clone(),
4012 query_id,
4013 expected_location: Some(responder),
4014 });
4015 return Weight::zero()
4016 }
4017 match maybe_notify {
4018 Some((pallet_index, call_index)) => {
4019 let bare = (pallet_index, call_index, query_id, response);
4023 if let Ok(call) = bare.using_encoded(|mut bytes| {
4024 <T as Config>::RuntimeCall::decode(&mut bytes)
4025 }) {
4026 Queries::<T>::remove(query_id);
4027 let weight = call.get_dispatch_info().call_weight;
4028 if weight.any_gt(max_weight) {
4029 let e = Event::NotifyOverweight {
4030 query_id,
4031 pallet_index,
4032 call_index,
4033 actual_weight: weight,
4034 max_budgeted_weight: max_weight,
4035 };
4036 Self::deposit_event(e);
4037 return Weight::zero()
4038 }
4039 let dispatch_origin = Origin::Response(origin.clone()).into();
4040 match call.dispatch(dispatch_origin) {
4041 Ok(post_info) => {
4042 let e = Event::Notified { query_id, pallet_index, call_index };
4043 Self::deposit_event(e);
4044 post_info.actual_weight
4045 },
4046 Err(error_and_info) => {
4047 let e = Event::NotifyDispatchError {
4048 query_id,
4049 pallet_index,
4050 call_index,
4051 };
4052 Self::deposit_event(e);
4053 error_and_info.post_info.actual_weight
4056 },
4057 }
4058 .unwrap_or(weight)
4059 } else {
4060 let e =
4061 Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
4062 Self::deposit_event(e);
4063 Weight::zero()
4064 }
4065 },
4066 None => {
4067 let e = Event::ResponseReady { query_id, response: response.clone() };
4068 Self::deposit_event(e);
4069 let at = frame_system::Pallet::<T>::current_block_number();
4070 let response = response.into();
4071 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
4072 Weight::zero()
4073 },
4074 }
4075 },
4076 _ => {
4077 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
4078 Self::deposit_event(e);
4079 Weight::zero()
4080 },
4081 }
4082 }
4083}
4084
4085impl<T: Config> CheckSuspension for Pallet<T> {
4086 fn is_suspended<Call>(
4087 _origin: &Location,
4088 _instructions: &mut [Instruction<Call>],
4089 _max_weight: Weight,
4090 _properties: &mut Properties,
4091 ) -> bool {
4092 XcmExecutionSuspended::<T>::get()
4093 }
4094}
4095
4096impl<T: Config> RecordXcm for Pallet<T> {
4097 fn should_record() -> bool {
4098 ShouldRecordXcm::<T>::get()
4099 }
4100
4101 fn set_record_xcm(enabled: bool) {
4102 ShouldRecordXcm::<T>::put(enabled);
4103 }
4104
4105 fn recorded_xcm() -> Option<Xcm<()>> {
4106 RecordedXcm::<T>::get()
4107 }
4108
4109 fn record(xcm: Xcm<()>) {
4110 RecordedXcm::<T>::put(xcm);
4111 }
4112}
4113
4114pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4118where
4119 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4120{
4121 match o.into() {
4122 Ok(Origin::Xcm(location)) => Ok(location),
4123 _ => Err(BadOrigin),
4124 }
4125}
4126
4127pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4131where
4132 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4133{
4134 match o.into() {
4135 Ok(Origin::Response(location)) => Ok(location),
4136 _ => Err(BadOrigin),
4137 }
4138}
4139
4140pub struct AuthorizedAliasers<T>(PhantomData<T>);
4146impl<L: Into<VersionedLocation> + Clone, T: Config> ContainsPair<L, L> for AuthorizedAliasers<T> {
4147 fn contains(origin: &L, target: &L) -> bool {
4148 let origin: VersionedLocation = origin.clone().into();
4149 let target: VersionedLocation = target.clone().into();
4150 tracing::trace!(target: "xcm::pallet_xcm::AuthorizedAliasers::contains", ?origin, ?target);
4151 Pallet::<T>::is_authorized_alias(origin, target).unwrap_or(false)
4154 }
4155}
4156
4157pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4162impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
4163 for IsMajorityOfBody<Prefix, Body>
4164{
4165 fn contains(l: &Location) -> bool {
4166 let maybe_suffix = l.match_and_split(&Prefix::get());
4167 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
4168 }
4169}
4170
4171pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4175impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
4176 fn contains(l: &Location) -> bool {
4177 let maybe_suffix = l.match_and_split(&Prefix::get());
4178 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
4179 }
4180}
4181
4182pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
4185impl<
4186 O: OriginTrait + From<Origin>,
4187 F: Contains<L>,
4188 L: TryFrom<Location> + TryInto<Location> + Clone,
4189 > EnsureOrigin<O> for EnsureXcm<F, L>
4190where
4191 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4192{
4193 type Success = L;
4194
4195 fn try_origin(outer: O) -> Result<Self::Success, O> {
4196 match outer.caller().try_into() {
4197 Ok(Origin::Xcm(ref location)) =>
4198 if let Ok(location) = location.clone().try_into() {
4199 if F::contains(&location) {
4200 return Ok(location);
4201 }
4202 },
4203 _ => (),
4204 }
4205
4206 Err(outer)
4207 }
4208
4209 #[cfg(feature = "runtime-benchmarks")]
4210 fn try_successful_origin() -> Result<O, ()> {
4211 Ok(O::from(Origin::Xcm(Here.into())))
4212 }
4213}
4214
4215pub struct EnsureResponse<F>(PhantomData<F>);
4218impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
4219where
4220 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4221{
4222 type Success = Location;
4223
4224 fn try_origin(outer: O) -> Result<Self::Success, O> {
4225 match outer.caller().try_into() {
4226 Ok(Origin::Response(responder)) => return Ok(responder.clone()),
4227 _ => (),
4228 }
4229
4230 Err(outer)
4231 }
4232
4233 #[cfg(feature = "runtime-benchmarks")]
4234 fn try_successful_origin() -> Result<O, ()> {
4235 Ok(O::from(Origin::Response(Here.into())))
4236 }
4237}
4238
4239pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
4242impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
4243 for XcmPassthrough<RuntimeOrigin>
4244{
4245 fn convert_origin(
4246 origin: impl Into<Location>,
4247 kind: OriginKind,
4248 ) -> Result<RuntimeOrigin, Location> {
4249 let origin = origin.into();
4250 match kind {
4251 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
4252 _ => Err(origin),
4253 }
4254 }
4255}