1#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "runtime-benchmarks")]
22pub mod benchmarking;
23#[cfg(test)]
24mod mock;
25#[cfg(test)]
26mod tests;
27mod transfer_assets_validation;
28
29pub mod migration;
30#[cfg(any(test, feature = "test-utils"))]
31pub mod xcm_helpers;
32
33extern crate alloc;
34
35use alloc::{boxed::Box, vec, vec::Vec};
36use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
37use core::{marker::PhantomData, result::Result};
38use frame_support::{
39 dispatch::{
40 DispatchErrorWithPostInfo, GetDispatchInfo, PostDispatchInfo, WithPostDispatchInfo,
41 },
42 pallet_prelude::*,
43 storage::with_transaction,
44 traits::{
45 Consideration, Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Footprint, Get,
46 LockableCurrency, OriginTrait, WithdrawReasons,
47 },
48 PalletId,
49};
50use frame_system::pallet_prelude::{BlockNumberFor, *};
51pub use pallet::*;
52use scale_info::TypeInfo;
53use sp_core::H256;
54use sp_runtime::{
55 traits::{
56 AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
57 Saturating, Zero,
58 },
59 Debug, Either, SaturatedConversion, TransactionOutcome,
60};
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(n: u32) -> Weight;
109 fn add_authorized_alias() -> Weight;
110 fn remove_authorized_alias() -> Weight;
111
112 fn weigh_message(n: u32) -> Weight;
121 fn decode_xcm(n: u32) -> Weight;
125}
126
127pub struct TestWeightInfo;
129impl WeightInfo for TestWeightInfo {
130 fn send() -> Weight {
131 Weight::from_parts(100_000_000, 0)
132 }
133
134 fn teleport_assets() -> Weight {
135 Weight::from_parts(100_000_000, 0)
136 }
137
138 fn reserve_transfer_assets() -> Weight {
139 Weight::from_parts(100_000_000, 0)
140 }
141
142 fn transfer_assets() -> Weight {
143 Weight::from_parts(100_000_000, 0)
144 }
145
146 fn execute() -> Weight {
147 Weight::from_parts(100_000_000, 0)
148 }
149
150 fn force_xcm_version() -> Weight {
151 Weight::from_parts(100_000_000, 0)
152 }
153
154 fn force_default_xcm_version() -> Weight {
155 Weight::from_parts(100_000_000, 0)
156 }
157
158 fn force_subscribe_version_notify() -> Weight {
159 Weight::from_parts(100_000_000, 0)
160 }
161
162 fn force_unsubscribe_version_notify() -> Weight {
163 Weight::from_parts(100_000_000, 0)
164 }
165
166 fn force_suspension() -> Weight {
167 Weight::from_parts(100_000_000, 0)
168 }
169
170 fn migrate_supported_version() -> Weight {
171 Weight::from_parts(100_000_000, 0)
172 }
173
174 fn migrate_version_notifiers() -> Weight {
175 Weight::from_parts(100_000_000, 0)
176 }
177
178 fn already_notified_target() -> Weight {
179 Weight::from_parts(100_000_000, 0)
180 }
181
182 fn notify_current_targets() -> Weight {
183 Weight::from_parts(100_000_000, 0)
184 }
185
186 fn notify_target_migration_fail() -> Weight {
187 Weight::from_parts(100_000_000, 0)
188 }
189
190 fn migrate_version_notify_targets() -> Weight {
191 Weight::from_parts(100_000_000, 0)
192 }
193
194 fn migrate_and_notify_old_targets() -> Weight {
195 Weight::from_parts(100_000_000, 0)
196 }
197
198 fn new_query() -> Weight {
199 Weight::from_parts(100_000_000, 0)
200 }
201
202 fn take_response() -> Weight {
203 Weight::from_parts(100_000_000, 0)
204 }
205
206 fn claim_assets(n: u32) -> Weight {
207 Weight::from_parts(100_000_000, 0)
208 .saturating_add(Weight::from_parts(10_000_000, 0).saturating_mul(n.into()))
209 }
210
211 fn add_authorized_alias() -> Weight {
212 Weight::from_parts(100_000, 0)
213 }
214
215 fn remove_authorized_alias() -> Weight {
216 Weight::from_parts(100_000, 0)
217 }
218
219 fn weigh_message(n: u32) -> Weight {
220 Weight::from_parts(100_000, 0)
221 .saturating_add(Weight::from_parts(100_000, 0).saturating_mul(n.into()))
222 }
223
224 fn decode_xcm(n: u32) -> Weight {
225 Weight::from_parts(100_000, 0)
226 .saturating_add(Weight::from_parts(20_000, 0).saturating_mul(n.into()))
227 }
228}
229
230#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
231pub struct AuthorizedAliasesEntry<Ticket, MAX: Get<u32>> {
232 pub aliasers: BoundedVec<OriginAliaser, MAX>,
233 pub ticket: Ticket,
234}
235
236pub fn aliasers_footprint(aliasers_count: usize) -> Footprint {
237 Footprint::from_parts(aliasers_count, OriginAliaser::max_encoded_len())
238}
239
240#[frame_support::pallet]
241pub mod pallet {
242 use super::*;
243 use frame_support::{
244 dispatch::{GetDispatchInfo, PostDispatchInfo},
245 parameter_types,
246 };
247 use frame_system::Config as SysConfig;
248 use sp_runtime::traits::Dispatchable;
249 use xcm_executor::traits::{MatchesFungible, WeightBounds};
250
251 parameter_types! {
252 pub const CurrentXcmVersion: u32 = XCM_VERSION;
255
256 #[derive(Debug, TypeInfo)]
257 pub const MaxAuthorizedAliases: u32 = 10;
259 }
260
261 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
262
263 #[pallet::pallet]
264 #[pallet::storage_version(STORAGE_VERSION)]
265 #[pallet::without_storage_info]
266 pub struct Pallet<T>(_);
267
268 pub type BalanceOf<T> =
269 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
270 pub type TicketOf<T> = <T as Config>::AuthorizedAliasConsideration;
271
272 #[pallet::config]
273 pub trait Config: frame_system::Config {
275 #[allow(deprecated)]
277 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
278
279 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
282
283 type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
285
286 type AuthorizedAliasConsideration: Consideration<Self::AccountId, Footprint>;
288
289 type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
292
293 type XcmRouter: SendXcm;
295
296 type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
300
301 type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
303
304 type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers + FeeManager;
306
307 type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
309
310 type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
313
314 type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
316
317 #[pallet::constant]
319 type UniversalLocation: Get<InteriorLocation>;
320
321 type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
323
324 type RuntimeCall: Parameter
326 + GetDispatchInfo
327 + Dispatchable<
328 RuntimeOrigin = <Self as Config>::RuntimeOrigin,
329 PostInfo = PostDispatchInfo,
330 >;
331
332 const VERSION_DISCOVERY_QUEUE_SIZE: u32;
333
334 #[pallet::constant]
337 type AdvertisedXcmVersion: Get<XcmVersion>;
338
339 type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
341
342 type TrustedLockers: ContainsPair<Location, Asset>;
345
346 type SovereignAccountOf: ConvertLocation<Self::AccountId>;
348
349 #[pallet::constant]
351 type MaxLockers: Get<u32>;
352
353 #[pallet::constant]
355 type MaxRemoteLockConsumers: Get<u32>;
356
357 type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
359
360 type WeightInfo: WeightInfo;
362 }
363
364 impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
365 fn execute() -> Weight {
366 T::WeightInfo::execute()
367 }
368 }
369
370 impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
371 type WeightInfo = Self;
372 fn execute(
373 origin: OriginFor<T>,
374 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
375 max_weight: Weight,
376 ) -> Result<Weight, DispatchErrorWithPostInfo> {
377 tracing::trace!(target: "xcm::pallet_xcm::execute", ?message, ?max_weight);
378 let outcome = (|| {
379 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
380 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
381 let message = (*message).try_into().map_err(|()| {
382 tracing::debug!(
383 target: "xcm::pallet_xcm::execute", id=?hash,
384 "Failed to convert VersionedXcm to Xcm",
385 );
386 Error::<T>::BadVersion
387 })?;
388 let value = (origin_location, message);
389 ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
390 let (origin_location, message) = value;
391 Ok(T::XcmExecutor::prepare_and_execute(
392 origin_location,
393 message,
394 &mut hash,
395 max_weight,
396 max_weight,
397 ))
398 })()
399 .map_err(|e: DispatchError| {
400 tracing::debug!(
401 target: "xcm::pallet_xcm::execute", error=?e,
402 "Failed XCM pre-execution validation or filter",
403 );
404 e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
405 })?;
406
407 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
408 let weight_used = outcome.weight_used();
409 outcome.ensure_complete().map_err(|error| {
410 tracing::error!(target: "xcm::pallet_xcm::execute", ?error, "XCM execution failed with error");
411 Error::<T>::LocalExecutionIncompleteWithError {
412 index: error.index,
413 error: error.error.into(),
414 }
415 .with_weight(
416 weight_used.saturating_add(
417 <Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
418 ),
419 )
420 })?;
421 Ok(weight_used)
422 }
423 }
424
425 impl<T: Config> SendControllerWeightInfo for Pallet<T> {
426 fn send() -> Weight {
427 T::WeightInfo::send()
428 }
429 }
430
431 impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
432 type WeightInfo = Self;
433 fn send(
434 origin: OriginFor<T>,
435 dest: Box<VersionedLocation>,
436 message: Box<VersionedXcm<()>>,
437 ) -> Result<XcmHash, DispatchError> {
438 let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
439 let interior: Junctions = origin_location.clone().try_into().map_err(|_| {
440 tracing::debug!(
441 target: "xcm::pallet_xcm::send",
442 "Failed to convert origin_location to interior Junctions",
443 );
444 Error::<T>::InvalidOrigin
445 })?;
446 let dest = Location::try_from(*dest).map_err(|()| {
447 tracing::debug!(
448 target: "xcm::pallet_xcm::send",
449 "Failed to convert destination VersionedLocation to Location",
450 );
451 Error::<T>::BadVersion
452 })?;
453 let message: Xcm<()> = (*message).try_into().map_err(|()| {
454 tracing::debug!(
455 target: "xcm::pallet_xcm::send",
456 "Failed to convert VersionedXcm message to Xcm",
457 );
458 Error::<T>::BadVersion
459 })?;
460
461 let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
462 .map_err(|error| {
463 tracing::error!(target: "xcm::pallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
464 Error::<T>::from(error)
465 })?;
466 let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
467 Self::deposit_event(e);
468 Ok(message_id)
469 }
470 }
471
472 impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
473 fn query() -> Weight {
474 T::WeightInfo::new_query()
475 }
476 fn take_response() -> Weight {
477 T::WeightInfo::take_response()
478 }
479 }
480
481 impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
482 type WeightInfo = Self;
483
484 fn query(
485 origin: OriginFor<T>,
486 timeout: BlockNumberFor<T>,
487 match_querier: VersionedLocation,
488 ) -> Result<QueryId, DispatchError> {
489 let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
490 let query_id = <Self as QueryHandler>::new_query(
491 responder,
492 timeout,
493 Location::try_from(match_querier).map_err(|_| {
494 tracing::debug!(
495 target: "xcm::pallet_xcm::query",
496 "Failed to convert VersionedLocation for match_querier",
497 );
498 Into::<DispatchError>::into(Error::<T>::BadVersion)
499 })?,
500 );
501
502 Ok(query_id)
503 }
504 }
505
506 impl<T: Config> EventEmitter for Pallet<T> {
507 fn emit_sent_event(
508 origin: Location,
509 destination: Location,
510 message: Option<Xcm<()>>,
511 message_id: XcmHash,
512 ) {
513 Self::deposit_event(Event::Sent {
514 origin,
515 destination,
516 message: message.unwrap_or_default(),
517 message_id,
518 });
519 }
520
521 fn emit_send_failure_event(
522 origin: Location,
523 destination: Location,
524 error: SendError,
525 message_id: XcmHash,
526 ) {
527 Self::deposit_event(Event::SendFailed { origin, destination, error, message_id });
528 }
529
530 fn emit_process_failure_event(origin: Location, error: XcmError, message_id: XcmHash) {
531 Self::deposit_event(Event::ProcessXcmError { origin, error, message_id });
532 }
533 }
534
535 #[pallet::event]
536 #[pallet::generate_deposit(pub(super) fn deposit_event)]
537 pub enum Event<T: Config> {
538 Attempted { outcome: xcm::latest::Outcome },
540 Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
542 SendFailed {
544 origin: Location,
545 destination: Location,
546 error: SendError,
547 message_id: XcmHash,
548 },
549 ProcessXcmError { origin: Location, error: XcmError, message_id: XcmHash },
551 UnexpectedResponse { origin: Location, query_id: QueryId },
555 ResponseReady { query_id: QueryId, response: Response },
558 Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
561 NotifyOverweight {
565 query_id: QueryId,
566 pallet_index: u8,
567 call_index: u8,
568 actual_weight: Weight,
569 max_budgeted_weight: Weight,
570 },
571 NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
574 NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
578 InvalidResponder {
582 origin: Location,
583 query_id: QueryId,
584 expected_location: Option<Location>,
585 },
586 InvalidResponderVersion { origin: Location, query_id: QueryId },
594 ResponseTaken { query_id: QueryId },
596 AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
598 VersionChangeNotified {
602 destination: Location,
603 result: XcmVersion,
604 cost: Assets,
605 message_id: XcmHash,
606 },
607 SupportedVersionChanged { location: Location, version: XcmVersion },
610 NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
613 NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
616 InvalidQuerierVersion { origin: Location, query_id: QueryId },
624 InvalidQuerier {
628 origin: Location,
629 query_id: QueryId,
630 expected_querier: Location,
631 maybe_actual_querier: Option<Location>,
632 },
633 VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
636 VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
638 VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
641 FeesPaid { paying: Location, fees: Assets },
643 AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
645 VersionMigrationFinished { version: XcmVersion },
647 AliasAuthorized { aliaser: Location, target: Location, expiry: Option<u64> },
650 AliasAuthorizationRemoved { aliaser: Location, target: Location },
652 AliasesAuthorizationsRemoved { target: Location },
654 }
655
656 #[pallet::origin]
657 #[derive(
658 PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen,
659 )]
660 pub enum Origin {
661 Xcm(Location),
663 Response(Location),
665 }
666 impl From<Location> for Origin {
667 fn from(location: Location) -> Origin {
668 Origin::Xcm(location)
669 }
670 }
671
672 #[pallet::composite_enum]
674 pub enum HoldReason {
675 AuthorizeAlias,
677 }
678
679 #[pallet::error]
680 pub enum Error<T> {
681 Unreachable,
684 SendFailure,
687 Filtered,
689 UnweighableMessage,
691 DestinationNotInvertible,
693 Empty,
695 CannotReanchor,
697 TooManyAssets,
699 InvalidOrigin,
701 BadVersion,
703 BadLocation,
706 NoSubscription,
708 AlreadySubscribed,
710 CannotCheckOutTeleport,
712 LowBalance,
714 TooManyLocks,
716 AccountNotSovereign,
718 FeesNotMet,
720 LockNotFound,
722 InUse,
724 #[codec(index = 21)]
726 InvalidAssetUnknownReserve,
727 #[codec(index = 22)]
729 InvalidAssetUnsupportedReserve,
730 #[codec(index = 23)]
732 TooManyReserves,
733 #[deprecated(since = "20.0.0", note = "Use `LocalExecutionIncompleteWithError` instead")]
735 #[codec(index = 24)]
736 LocalExecutionIncomplete,
737 #[codec(index = 25)]
739 TooManyAuthorizedAliases,
740 #[codec(index = 26)]
742 ExpiresInPast,
743 #[codec(index = 27)]
745 AliasNotFound,
746 #[codec(index = 28)]
749 LocalExecutionIncompleteWithError { index: InstructionIndex, error: ExecutionError },
750 }
751
752 impl<T: Config> From<SendError> for Error<T> {
753 fn from(e: SendError) -> Self {
754 match e {
755 SendError::Fees => Error::<T>::FeesNotMet,
756 SendError::NotApplicable => Error::<T>::Unreachable,
757 _ => Error::<T>::SendFailure,
758 }
759 }
760 }
761
762 impl<T: Config> From<AssetTransferError> for Error<T> {
763 fn from(e: AssetTransferError) -> Self {
764 match e {
765 AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
766 }
767 }
768 }
769
770 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
772 pub enum QueryStatus<BlockNumber> {
773 Pending {
775 responder: VersionedLocation,
778 maybe_match_querier: Option<VersionedLocation>,
781 maybe_notify: Option<(u8, u8)>,
782 timeout: BlockNumber,
783 },
784 VersionNotifier { origin: VersionedLocation, is_active: bool },
786 Ready { response: VersionedResponse, at: BlockNumber },
788 }
789
790 #[derive(Copy, Clone)]
791 pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
792 impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
793 impl<'a> Encode for LatestVersionedLocation<'a> {
794 fn encode(&self) -> Vec<u8> {
795 let mut r = VersionedLocation::from(Location::default()).encode();
796 r.truncate(1);
797 self.0.using_encoded(|d| r.extend_from_slice(d));
798 r
799 }
800 }
801
802 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
803 pub enum VersionMigrationStage {
804 MigrateSupportedVersion,
805 MigrateVersionNotifiers,
806 NotifyCurrentTargets(Option<Vec<u8>>),
807 MigrateAndNotifyOldTargets,
808 }
809
810 impl Default for VersionMigrationStage {
811 fn default() -> Self {
812 Self::MigrateSupportedVersion
813 }
814 }
815
816 #[pallet::storage]
818 pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
819
820 #[pallet::storage]
822 pub(super) type Queries<T: Config> =
823 StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
824
825 #[pallet::storage]
830 pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
831
832 #[pallet::storage]
835 #[pallet::whitelist_storage]
836 pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
837
838 #[pallet::storage]
840 pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
841 _,
842 Twox64Concat,
843 XcmVersion,
844 Blake2_128Concat,
845 VersionedLocation,
846 XcmVersion,
847 OptionQuery,
848 >;
849
850 #[pallet::storage]
852 pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
853 _,
854 Twox64Concat,
855 XcmVersion,
856 Blake2_128Concat,
857 VersionedLocation,
858 QueryId,
859 OptionQuery,
860 >;
861
862 #[pallet::storage]
865 pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
866 _,
867 Twox64Concat,
868 XcmVersion,
869 Blake2_128Concat,
870 VersionedLocation,
871 (QueryId, Weight, XcmVersion),
872 OptionQuery,
873 >;
874
875 pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
876 impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
877 fn get() -> u32 {
878 T::VERSION_DISCOVERY_QUEUE_SIZE
879 }
880 }
881
882 #[pallet::storage]
886 #[pallet::whitelist_storage]
887 pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
888 _,
889 BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
890 ValueQuery,
891 >;
892
893 #[pallet::storage]
895 pub(super) type CurrentMigration<T: Config> =
896 StorageValue<_, VersionMigrationStage, OptionQuery>;
897
898 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
899 #[scale_info(skip_type_params(MaxConsumers))]
900 pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
901 pub amount: u128,
903 pub owner: VersionedLocation,
905 pub locker: VersionedLocation,
907 pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
911 }
912
913 impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
914 pub fn amount_held(&self) -> Option<u128> {
917 self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
918 }
919 }
920
921 #[pallet::storage]
923 pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
924 _,
925 (
926 NMapKey<Twox64Concat, XcmVersion>,
927 NMapKey<Blake2_128Concat, T::AccountId>,
928 NMapKey<Blake2_128Concat, VersionedAssetId>,
929 ),
930 RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
931 OptionQuery,
932 >;
933
934 #[pallet::storage]
936 pub(super) type LockedFungibles<T: Config> = StorageMap<
937 _,
938 Blake2_128Concat,
939 T::AccountId,
940 BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
941 OptionQuery,
942 >;
943
944 #[pallet::storage]
946 pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
947
948 #[pallet::storage]
956 pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
957
958 #[pallet::storage]
965 pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
966
967 #[pallet::storage]
971 pub(super) type AuthorizedAliases<T: Config> = StorageMap<
972 _,
973 Blake2_128Concat,
974 VersionedLocation,
975 AuthorizedAliasesEntry<TicketOf<T>, MaxAuthorizedAliases>,
976 OptionQuery,
977 >;
978
979 #[pallet::genesis_config]
980 pub struct GenesisConfig<T: Config> {
981 #[serde(skip)]
982 pub _config: core::marker::PhantomData<T>,
983 pub safe_xcm_version: Option<XcmVersion>,
985 pub supported_version: Vec<(Location, XcmVersion)>,
987 }
988
989 impl<T: Config> Default for GenesisConfig<T> {
990 fn default() -> Self {
991 Self {
992 _config: Default::default(),
993 safe_xcm_version: Some(XCM_VERSION),
994 supported_version: Vec::new(),
995 }
996 }
997 }
998
999 #[pallet::genesis_build]
1000 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1001 fn build(&self) {
1002 SafeXcmVersion::<T>::set(self.safe_xcm_version);
1003 self.supported_version.iter().for_each(|(location, version)| {
1005 SupportedVersion::<T>::insert(
1006 XCM_VERSION,
1007 LatestVersionedLocation(location),
1008 version,
1009 );
1010 });
1011 }
1012 }
1013
1014 #[pallet::hooks]
1015 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1016 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
1017 let mut weight_used = Weight::zero();
1018 if let Some(migration) = CurrentMigration::<T>::get() {
1019 let max_weight = T::BlockWeights::get().max_block / 10;
1021 let (w, maybe_migration) = Self::lazy_migration(migration, max_weight);
1022 if maybe_migration.is_none() {
1023 Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
1024 }
1025 CurrentMigration::<T>::set(maybe_migration);
1026 weight_used.saturating_accrue(w);
1027 }
1028
1029 let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
1032 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1034 q.sort_by_key(|i| i.1);
1035 while let Some((versioned_dest, _)) = q.pop() {
1036 if let Ok(dest) = Location::try_from(versioned_dest) {
1037 if Self::request_version_notify(dest).is_ok() {
1038 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1040 break;
1041 }
1042 }
1043 }
1044 if let Ok(q) = BoundedVec::try_from(q) {
1047 VersionDiscoveryQueue::<T>::put(q);
1048 }
1049 weight_used
1050 }
1051
1052 #[cfg(feature = "try-runtime")]
1053 fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
1054 Self::do_try_state()
1055 }
1056 }
1057
1058 pub mod migrations {
1059 use super::*;
1060 use frame_support::traits::{PalletInfoAccess, StorageVersion};
1061
1062 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
1063 enum QueryStatusV0<BlockNumber> {
1064 Pending {
1065 responder: VersionedLocation,
1066 maybe_notify: Option<(u8, u8)>,
1067 timeout: BlockNumber,
1068 },
1069 VersionNotifier {
1070 origin: VersionedLocation,
1071 is_active: bool,
1072 },
1073 Ready {
1074 response: VersionedResponse,
1075 at: BlockNumber,
1076 },
1077 }
1078 impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
1079 fn from(old: QueryStatusV0<B>) -> Self {
1080 use QueryStatusV0::*;
1081 match old {
1082 Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
1083 responder,
1084 maybe_notify,
1085 timeout,
1086 maybe_match_querier: Some(Location::here().into()),
1087 },
1088 VersionNotifier { origin, is_active } => {
1089 QueryStatus::VersionNotifier { origin, is_active }
1090 },
1091 Ready { response, at } => QueryStatus::Ready { response, at },
1092 }
1093 }
1094 }
1095
1096 pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
1097 ) -> frame_support::weights::Weight {
1098 let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
1099 tracing::info!(
1100 target: "runtime::xcm",
1101 ?on_chain_storage_version,
1102 "Running migration storage v1 for xcm with storage version",
1103 );
1104
1105 if on_chain_storage_version < 1 {
1106 let mut count = 0;
1107 Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
1108 count += 1;
1109 Some(value.into())
1110 });
1111 StorageVersion::new(1).put::<P>();
1112 tracing::info!(
1113 target: "runtime::xcm",
1114 ?on_chain_storage_version,
1115 "Running migration storage v1 for xcm with storage version was complete",
1116 );
1117 T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
1119 } else {
1120 tracing::warn!(
1121 target: "runtime::xcm",
1122 ?on_chain_storage_version,
1123 "Attempted to apply migration to v1 but failed because storage version is",
1124 );
1125 T::DbWeight::get().reads(1)
1126 }
1127 }
1128 }
1129
1130 #[pallet::call(weight(<T as Config>::WeightInfo))]
1131 impl<T: Config> Pallet<T> {
1132 #[pallet::call_index(0)]
1133 pub fn send(
1134 origin: OriginFor<T>,
1135 dest: Box<VersionedLocation>,
1136 message: Box<VersionedXcm<()>>,
1137 ) -> DispatchResult {
1138 <Self as SendController<_>>::send(origin, dest, message)?;
1139 Ok(())
1140 }
1141
1142 #[pallet::call_index(1)]
1161 #[allow(deprecated)]
1162 #[deprecated(
1163 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
1164 )]
1165 pub fn teleport_assets(
1166 origin: OriginFor<T>,
1167 dest: Box<VersionedLocation>,
1168 beneficiary: Box<VersionedLocation>,
1169 assets: Box<VersionedAssets>,
1170 fee_asset_item: u32,
1171 ) -> DispatchResult {
1172 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
1173 }
1174
1175 #[pallet::call_index(2)]
1206 #[allow(deprecated)]
1207 #[deprecated(
1208 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1209 )]
1210 pub fn reserve_transfer_assets(
1211 origin: OriginFor<T>,
1212 dest: Box<VersionedLocation>,
1213 beneficiary: Box<VersionedLocation>,
1214 assets: Box<VersionedAssets>,
1215 fee_asset_item: u32,
1216 ) -> DispatchResult {
1217 Self::do_reserve_transfer_assets(
1218 origin,
1219 dest,
1220 beneficiary,
1221 assets,
1222 fee_asset_item,
1223 Unlimited,
1224 )
1225 }
1226
1227 #[pallet::call_index(3)]
1236 #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1237 pub fn execute(
1238 origin: OriginFor<T>,
1239 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1240 max_weight: Weight,
1241 ) -> DispatchResultWithPostInfo {
1242 let weight_used =
1243 <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1244 Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1245 }
1246
1247 #[pallet::call_index(4)]
1254 pub fn force_xcm_version(
1255 origin: OriginFor<T>,
1256 location: Box<Location>,
1257 version: XcmVersion,
1258 ) -> DispatchResult {
1259 T::AdminOrigin::ensure_origin(origin)?;
1260 let location = *location;
1261 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1262 Self::deposit_event(Event::SupportedVersionChanged { location, version });
1263 Ok(())
1264 }
1265
1266 #[pallet::call_index(5)]
1272 pub fn force_default_xcm_version(
1273 origin: OriginFor<T>,
1274 maybe_xcm_version: Option<XcmVersion>,
1275 ) -> DispatchResult {
1276 T::AdminOrigin::ensure_origin(origin)?;
1277 SafeXcmVersion::<T>::set(maybe_xcm_version);
1278 Ok(())
1279 }
1280
1281 #[pallet::call_index(6)]
1286 pub fn force_subscribe_version_notify(
1287 origin: OriginFor<T>,
1288 location: Box<VersionedLocation>,
1289 ) -> DispatchResult {
1290 T::AdminOrigin::ensure_origin(origin)?;
1291 let location: Location = (*location).try_into().map_err(|()| {
1292 tracing::debug!(
1293 target: "xcm::pallet_xcm::force_subscribe_version_notify",
1294 "Failed to convert VersionedLocation for subscription target"
1295 );
1296 Error::<T>::BadLocation
1297 })?;
1298 Self::request_version_notify(location).map_err(|e| {
1299 tracing::debug!(
1300 target: "xcm::pallet_xcm::force_subscribe_version_notify", error=?e,
1301 "Failed to subscribe for version notifications for location"
1302 );
1303 match e {
1304 XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1305 _ => Error::<T>::InvalidOrigin,
1306 }
1307 .into()
1308 })
1309 }
1310
1311 #[pallet::call_index(7)]
1318 pub fn force_unsubscribe_version_notify(
1319 origin: OriginFor<T>,
1320 location: Box<VersionedLocation>,
1321 ) -> DispatchResult {
1322 T::AdminOrigin::ensure_origin(origin)?;
1323 let location: Location = (*location).try_into().map_err(|()| {
1324 tracing::debug!(
1325 target: "xcm::pallet_xcm::force_unsubscribe_version_notify",
1326 "Failed to convert VersionedLocation for unsubscription target"
1327 );
1328 Error::<T>::BadLocation
1329 })?;
1330 Self::unrequest_version_notify(location).map_err(|e| {
1331 tracing::debug!(
1332 target: "xcm::pallet_xcm::force_unsubscribe_version_notify", error=?e,
1333 "Failed to unsubscribe from version notifications for location"
1334 );
1335 match e {
1336 XcmError::InvalidLocation => Error::<T>::NoSubscription,
1337 _ => Error::<T>::InvalidOrigin,
1338 }
1339 .into()
1340 })
1341 }
1342
1343 #[pallet::call_index(8)]
1374 #[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
1375 pub fn limited_reserve_transfer_assets(
1376 origin: OriginFor<T>,
1377 dest: Box<VersionedLocation>,
1378 beneficiary: Box<VersionedLocation>,
1379 assets: Box<VersionedAssets>,
1380 fee_asset_item: u32,
1381 weight_limit: WeightLimit,
1382 ) -> DispatchResult {
1383 Self::do_reserve_transfer_assets(
1384 origin,
1385 dest,
1386 beneficiary,
1387 assets,
1388 fee_asset_item,
1389 weight_limit,
1390 )
1391 }
1392
1393 #[pallet::call_index(9)]
1412 #[pallet::weight(T::WeightInfo::teleport_assets())]
1413 pub fn limited_teleport_assets(
1414 origin: OriginFor<T>,
1415 dest: Box<VersionedLocation>,
1416 beneficiary: Box<VersionedLocation>,
1417 assets: Box<VersionedAssets>,
1418 fee_asset_item: u32,
1419 weight_limit: WeightLimit,
1420 ) -> DispatchResult {
1421 Self::do_teleport_assets(
1422 origin,
1423 dest,
1424 beneficiary,
1425 assets,
1426 fee_asset_item,
1427 weight_limit,
1428 )
1429 }
1430
1431 #[pallet::call_index(10)]
1436 pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1437 T::AdminOrigin::ensure_origin(origin)?;
1438 XcmExecutionSuspended::<T>::set(suspended);
1439 Ok(())
1440 }
1441
1442 #[pallet::call_index(11)]
1476 pub fn transfer_assets(
1477 origin: OriginFor<T>,
1478 dest: Box<VersionedLocation>,
1479 beneficiary: Box<VersionedLocation>,
1480 assets: Box<VersionedAssets>,
1481 fee_asset_item: u32,
1482 weight_limit: WeightLimit,
1483 ) -> DispatchResult {
1484 let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1485 let dest = (*dest).try_into().map_err(|()| {
1486 tracing::debug!(
1487 target: "xcm::pallet_xcm::transfer_assets",
1488 "Failed to convert destination VersionedLocation",
1489 );
1490 Error::<T>::BadVersion
1491 })?;
1492 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1493 tracing::debug!(
1494 target: "xcm::pallet_xcm::transfer_assets",
1495 "Failed to convert beneficiary VersionedLocation",
1496 );
1497 Error::<T>::BadVersion
1498 })?;
1499 let assets: Assets = (*assets).try_into().map_err(|()| {
1500 tracing::debug!(
1501 target: "xcm::pallet_xcm::transfer_assets",
1502 "Failed to convert VersionedAssets",
1503 );
1504 Error::<T>::BadVersion
1505 })?;
1506 tracing::debug!(
1507 target: "xcm::pallet_xcm::transfer_assets",
1508 ?origin, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1509 );
1510
1511 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1512 let assets = assets.into_inner();
1513 let fee_asset_item = fee_asset_item as usize;
1514 let (fees_transfer_type, assets_transfer_type) =
1516 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1517
1518 Self::ensure_network_asset_reserve_transfer_allowed(
1522 &assets,
1523 fee_asset_item,
1524 &assets_transfer_type,
1525 &fees_transfer_type,
1526 )?;
1527
1528 Self::do_transfer_assets(
1529 origin,
1530 dest,
1531 Either::Left(beneficiary),
1532 assets,
1533 assets_transfer_type,
1534 fee_asset_item,
1535 fees_transfer_type,
1536 weight_limit,
1537 )
1538 }
1539
1540 #[pallet::call_index(12)]
1549 #[pallet::weight(T::WeightInfo::claim_assets(assets.len() as u32))]
1550 pub fn claim_assets(
1551 origin: OriginFor<T>,
1552 assets: Box<VersionedAssets>,
1553 beneficiary: Box<VersionedLocation>,
1554 ) -> DispatchResult {
1555 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1556 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?origin_location, ?assets, ?beneficiary);
1557 let assets_version = assets.identify_version();
1559 let assets: Assets = (*assets).try_into().map_err(|()| {
1560 tracing::debug!(
1561 target: "xcm::pallet_xcm::claim_assets",
1562 "Failed to convert input VersionedAssets",
1563 );
1564 Error::<T>::BadVersion
1565 })?;
1566 let number_of_assets = assets.len() as u32;
1567 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1568 tracing::debug!(
1569 target: "xcm::pallet_xcm::claim_assets",
1570 "Failed to convert beneficiary VersionedLocation",
1571 );
1572 Error::<T>::BadVersion
1573 })?;
1574 let ticket: Location = GeneralIndex(assets_version as u128).into();
1575 let mut message = Xcm(vec![
1576 ClaimAsset { assets, ticket },
1577 DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1578 ]);
1579 let weight = T::Weigher::weight(&mut message, Weight::MAX).map_err(|error| {
1580 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?error, "Failed to calculate weight");
1581 Error::<T>::UnweighableMessage
1582 })?;
1583 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
1584 let outcome = T::XcmExecutor::prepare_and_execute(
1585 origin_location,
1586 message,
1587 &mut hash,
1588 weight,
1589 weight,
1590 );
1591 outcome.ensure_complete().map_err(|error| {
1592 tracing::error!(target: "xcm::pallet_xcm::claim_assets", ?error, "XCM execution failed with error");
1593 Error::<T>::LocalExecutionIncompleteWithError { index: error.index, error: error.error.into()}
1594 })?;
1595 Ok(())
1596 }
1597
1598 #[pallet::call_index(13)]
1647 #[pallet::weight(T::WeightInfo::transfer_assets())]
1648 pub fn transfer_assets_using_type_and_then(
1649 origin: OriginFor<T>,
1650 dest: Box<VersionedLocation>,
1651 assets: Box<VersionedAssets>,
1652 assets_transfer_type: Box<TransferType>,
1653 remote_fees_id: Box<VersionedAssetId>,
1654 fees_transfer_type: Box<TransferType>,
1655 custom_xcm_on_dest: Box<VersionedXcm<()>>,
1656 weight_limit: WeightLimit,
1657 ) -> DispatchResult {
1658 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1659 let dest: Location = (*dest).try_into().map_err(|()| {
1660 tracing::debug!(
1661 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1662 "Failed to convert destination VersionedLocation",
1663 );
1664 Error::<T>::BadVersion
1665 })?;
1666 let assets: Assets = (*assets).try_into().map_err(|()| {
1667 tracing::debug!(
1668 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1669 "Failed to convert VersionedAssets",
1670 );
1671 Error::<T>::BadVersion
1672 })?;
1673 let fees_id: AssetId = (*remote_fees_id).try_into().map_err(|()| {
1674 tracing::debug!(
1675 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1676 "Failed to convert remote_fees_id VersionedAssetId",
1677 );
1678 Error::<T>::BadVersion
1679 })?;
1680 let remote_xcm: Xcm<()> = (*custom_xcm_on_dest).try_into().map_err(|()| {
1681 tracing::debug!(
1682 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1683 "Failed to convert custom_xcm_on_dest VersionedXcm",
1684 );
1685 Error::<T>::BadVersion
1686 })?;
1687 tracing::debug!(
1688 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1689 ?origin_location, ?dest, ?assets, ?assets_transfer_type, ?fees_id, ?fees_transfer_type,
1690 ?remote_xcm, ?weight_limit,
1691 );
1692
1693 let assets = assets.into_inner();
1694 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1695
1696 let fee_asset_index =
1697 assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
1698 Self::do_transfer_assets(
1699 origin_location,
1700 dest,
1701 Either::Right(remote_xcm),
1702 assets,
1703 *assets_transfer_type,
1704 fee_asset_index,
1705 *fees_transfer_type,
1706 weight_limit,
1707 )
1708 }
1709
1710 #[pallet::call_index(14)]
1722 pub fn add_authorized_alias(
1723 origin: OriginFor<T>,
1724 aliaser: Box<VersionedLocation>,
1725 expires: Option<u64>,
1726 ) -> DispatchResult {
1727 let signed_origin = ensure_signed(origin.clone())?;
1728 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1729 let new_aliaser: Location = (*aliaser).try_into().map_err(|()| {
1730 tracing::debug!(
1731 target: "xcm::pallet_xcm::add_authorized_alias",
1732 "Failed to convert aliaser VersionedLocation",
1733 );
1734 Error::<T>::BadVersion
1735 })?;
1736 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1737 let origin_location = match origin_location.unpack() {
1739 (0, [AccountId32 { network: _, id }]) => {
1740 Location::new(0, [AccountId32 { network: None, id: *id }])
1741 },
1742 _ => return Err(Error::<T>::InvalidOrigin.into()),
1743 };
1744 tracing::debug!(target: "xcm::pallet_xcm::add_authorized_alias", ?origin_location, ?new_aliaser, ?expires);
1745 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1746 if let Some(expiry) = expires {
1747 ensure!(
1748 expiry >
1749 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>(),
1750 Error::<T>::ExpiresInPast
1751 );
1752 }
1753 let versioned_origin = VersionedLocation::from(origin_location.clone());
1754 let versioned_aliaser = VersionedLocation::from(new_aliaser.clone());
1755 let entry = if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1756 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1758 if let Some(aliaser) =
1759 aliasers.iter_mut().find(|aliaser| aliaser.location == versioned_aliaser)
1760 {
1761 aliaser.expiry = expires;
1763 } else {
1764 let aliaser =
1766 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1767 aliasers.try_push(aliaser).map_err(|_| {
1768 tracing::debug!(
1769 target: "xcm::pallet_xcm::add_authorized_alias",
1770 "Failed to add new aliaser to existing entry",
1771 );
1772 Error::<T>::TooManyAuthorizedAliases
1773 })?;
1774 ticket = ticket.update(&signed_origin, aliasers_footprint(aliasers.len()))?;
1776 }
1777 AuthorizedAliasesEntry { aliasers, ticket }
1778 } else {
1779 let ticket = TicketOf::<T>::new(&signed_origin, aliasers_footprint(1))?;
1781 let aliaser =
1782 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1783 let mut aliasers = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
1784 aliasers.try_push(aliaser).map_err(|error| {
1785 tracing::debug!(
1786 target: "xcm::pallet_xcm::add_authorized_alias", ?error,
1787 "Failed to add first aliaser to new entry",
1788 );
1789 Error::<T>::TooManyAuthorizedAliases
1790 })?;
1791 AuthorizedAliasesEntry { aliasers, ticket }
1792 };
1793 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1795 Self::deposit_event(Event::AliasAuthorized {
1796 aliaser: new_aliaser,
1797 target: origin_location,
1798 expiry: expires,
1799 });
1800 Ok(())
1801 }
1802
1803 #[pallet::call_index(15)]
1806 pub fn remove_authorized_alias(
1807 origin: OriginFor<T>,
1808 aliaser: Box<VersionedLocation>,
1809 ) -> DispatchResult {
1810 let signed_origin = ensure_signed(origin.clone())?;
1811 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1812 let to_remove: Location = (*aliaser).try_into().map_err(|()| {
1813 tracing::debug!(
1814 target: "xcm::pallet_xcm::remove_authorized_alias",
1815 "Failed to convert aliaser VersionedLocation",
1816 );
1817 Error::<T>::BadVersion
1818 })?;
1819 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1820 let origin_location = match origin_location.unpack() {
1822 (0, [AccountId32 { network: _, id }]) => {
1823 Location::new(0, [AccountId32 { network: None, id: *id }])
1824 },
1825 _ => return Err(Error::<T>::InvalidOrigin.into()),
1826 };
1827 tracing::debug!(target: "xcm::pallet_xcm::remove_authorized_alias", ?origin_location, ?to_remove);
1828 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1829 let versioned_origin = VersionedLocation::from(origin_location.clone());
1831 let versioned_to_remove = VersionedLocation::from(to_remove.clone());
1832 AuthorizedAliases::<T>::get(&versioned_origin)
1833 .ok_or(Error::<T>::AliasNotFound.into())
1834 .and_then(|entry| {
1835 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1836 let old_len = aliasers.len();
1837 aliasers.retain(|alias| versioned_to_remove.ne(&alias.location));
1838 let new_len = aliasers.len();
1839 if aliasers.is_empty() {
1840 ticket.drop(&signed_origin)?;
1842 AuthorizedAliases::<T>::remove(&versioned_origin);
1843 Self::deposit_event(Event::AliasAuthorizationRemoved {
1844 aliaser: to_remove,
1845 target: origin_location,
1846 });
1847 Ok(())
1848 } else if old_len != new_len {
1849 ticket = ticket.update(&signed_origin, aliasers_footprint(new_len))?;
1851 let entry = AuthorizedAliasesEntry { aliasers, ticket };
1852 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1853 Self::deposit_event(Event::AliasAuthorizationRemoved {
1854 aliaser: to_remove,
1855 target: origin_location,
1856 });
1857 Ok(())
1858 } else {
1859 Err(Error::<T>::AliasNotFound.into())
1860 }
1861 })
1862 }
1863
1864 #[pallet::call_index(16)]
1867 #[pallet::weight(T::WeightInfo::remove_authorized_alias())]
1868 pub fn remove_all_authorized_aliases(origin: OriginFor<T>) -> DispatchResult {
1869 let signed_origin = ensure_signed(origin.clone())?;
1870 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1871 let origin_location = match origin_location.unpack() {
1873 (0, [AccountId32 { network: _, id }]) => {
1874 Location::new(0, [AccountId32 { network: None, id: *id }])
1875 },
1876 _ => return Err(Error::<T>::InvalidOrigin.into()),
1877 };
1878 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", ?origin_location);
1879 let versioned_origin = VersionedLocation::from(origin_location.clone());
1881 if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1882 entry.ticket.drop(&signed_origin)?;
1884 AuthorizedAliases::<T>::remove(&versioned_origin);
1885 Self::deposit_event(Event::AliasesAuthorizationsRemoved {
1886 target: origin_location,
1887 });
1888 Ok(())
1889 } else {
1890 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", "No authorized alias entry found for the origin");
1891 Err(Error::<T>::AliasNotFound.into())
1892 }
1893 }
1894 }
1895}
1896
1897const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1899
1900#[derive(Clone, PartialEq)]
1902enum FeesHandling<T: Config> {
1903 Batched { fees: Asset },
1905 Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1907}
1908
1909impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1910 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1911 match self {
1912 Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1913 Self::Separate { local_xcm, remote_xcm } => write!(
1914 f,
1915 "FeesHandling::Separate(local: {:?}, remote: {:?})",
1916 local_xcm, remote_xcm
1917 ),
1918 }
1919 }
1920}
1921
1922impl<T: Config> QueryHandler for Pallet<T> {
1923 type BlockNumber = BlockNumberFor<T>;
1924 type Error = XcmError;
1925 type UniversalLocation = T::UniversalLocation;
1926
1927 fn new_query(
1929 responder: impl Into<Location>,
1930 timeout: BlockNumberFor<T>,
1931 match_querier: impl Into<Location>,
1932 ) -> QueryId {
1933 Self::do_new_query(responder, None, timeout, match_querier)
1934 }
1935
1936 fn report_outcome(
1939 message: &mut Xcm<()>,
1940 responder: impl Into<Location>,
1941 timeout: Self::BlockNumber,
1942 ) -> Result<QueryId, Self::Error> {
1943 let responder = responder.into();
1944 let destination =
1945 Self::UniversalLocation::get().invert_target(&responder).map_err(|()| {
1946 tracing::debug!(
1947 target: "xcm::pallet_xcm::report_outcome",
1948 "Failed to invert responder Location",
1949 );
1950 XcmError::LocationNotInvertible
1951 })?;
1952 let query_id = Self::new_query(responder, timeout, Here);
1953 let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1954 let report_error = Xcm(vec![ReportError(response_info)]);
1955 message.0.insert(0, SetAppendix(report_error));
1956 Ok(query_id)
1957 }
1958
1959 fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1961 match Queries::<T>::get(query_id) {
1962 Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1963 Ok(response) => {
1964 Queries::<T>::remove(query_id);
1965 Self::deposit_event(Event::ResponseTaken { query_id });
1966 QueryResponseStatus::Ready { response, at }
1967 },
1968 Err(_) => {
1969 tracing::debug!(
1970 target: "xcm::pallet_xcm::take_response", ?query_id,
1971 "Failed to convert VersionedResponse to Response for query",
1972 );
1973 QueryResponseStatus::UnexpectedVersion
1974 },
1975 },
1976 Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
1977 Some(_) => {
1978 tracing::debug!(
1979 target: "xcm::pallet_xcm::take_response", ?query_id,
1980 "Unexpected QueryStatus variant for query",
1981 );
1982 QueryResponseStatus::UnexpectedVersion
1983 },
1984 None => {
1985 tracing::debug!(
1986 target: "xcm::pallet_xcm::take_response", ?query_id,
1987 "Query ID not found`",
1988 );
1989 QueryResponseStatus::NotFound
1990 },
1991 }
1992 }
1993
1994 #[cfg(feature = "runtime-benchmarks")]
1995 fn expect_response(id: QueryId, response: Response) {
1996 let response = response.into();
1997 Queries::<T>::insert(
1998 id,
1999 QueryStatus::Ready { response, at: frame_system::Pallet::<T>::current_block_number() },
2000 );
2001 }
2002}
2003
2004impl<T: Config> Pallet<T> {
2005 pub fn query(query_id: &QueryId) -> Option<QueryStatus<BlockNumberFor<T>>> {
2007 Queries::<T>::get(query_id)
2008 }
2009
2010 pub fn asset_trap(trap_id: &H256) -> u32 {
2016 AssetTraps::<T>::get(trap_id)
2017 }
2018
2019 fn find_fee_and_assets_transfer_types(
2024 assets: &[Asset],
2025 fee_asset_item: usize,
2026 dest: &Location,
2027 ) -> Result<(TransferType, TransferType), Error<T>> {
2028 let mut fees_transfer_type = None;
2029 let mut assets_transfer_type = None;
2030 for (idx, asset) in assets.iter().enumerate() {
2031 if let Fungible(x) = asset.fun {
2032 ensure!(!x.is_zero(), Error::<T>::Empty);
2034 }
2035 let transfer_type =
2036 T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
2037 if idx == fee_asset_item {
2038 fees_transfer_type = Some(transfer_type);
2039 } else {
2040 if let Some(existing) = assets_transfer_type.as_ref() {
2041 ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
2044 } else {
2045 assets_transfer_type = Some(transfer_type);
2047 }
2048 }
2049 }
2050 if assets.len() == 1 {
2052 assets_transfer_type = fees_transfer_type.clone()
2053 }
2054 Ok((
2055 fees_transfer_type.ok_or(Error::<T>::Empty)?,
2056 assets_transfer_type.ok_or(Error::<T>::Empty)?,
2057 ))
2058 }
2059
2060 fn do_reserve_transfer_assets(
2061 origin: OriginFor<T>,
2062 dest: Box<VersionedLocation>,
2063 beneficiary: Box<VersionedLocation>,
2064 assets: Box<VersionedAssets>,
2065 fee_asset_item: u32,
2066 weight_limit: WeightLimit,
2067 ) -> DispatchResult {
2068 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2069 let dest = (*dest).try_into().map_err(|()| {
2070 tracing::debug!(
2071 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2072 "Failed to convert destination VersionedLocation",
2073 );
2074 Error::<T>::BadVersion
2075 })?;
2076 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2077 tracing::debug!(
2078 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2079 "Failed to convert beneficiary VersionedLocation",
2080 );
2081 Error::<T>::BadVersion
2082 })?;
2083 let assets: Assets = (*assets).try_into().map_err(|()| {
2084 tracing::debug!(
2085 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2086 "Failed to convert VersionedAssets",
2087 );
2088 Error::<T>::BadVersion
2089 })?;
2090 tracing::debug!(
2091 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2092 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item,
2093 );
2094
2095 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2096 let value = (origin_location, assets.into_inner());
2097 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2098 let (origin, assets) = value;
2099
2100 let fee_asset_item = fee_asset_item as usize;
2101 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
2102
2103 let (fees_transfer_type, assets_transfer_type) =
2105 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
2106 ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
2108 ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
2110
2111 Self::ensure_network_asset_reserve_transfer_allowed(
2115 &assets,
2116 fee_asset_item,
2117 &assets_transfer_type,
2118 &fees_transfer_type,
2119 )?;
2120
2121 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2122 origin.clone(),
2123 dest.clone(),
2124 Either::Left(beneficiary),
2125 assets,
2126 assets_transfer_type,
2127 FeesHandling::Batched { fees },
2128 weight_limit,
2129 )?;
2130 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2131 }
2132
2133 fn do_teleport_assets(
2134 origin: OriginFor<T>,
2135 dest: Box<VersionedLocation>,
2136 beneficiary: Box<VersionedLocation>,
2137 assets: Box<VersionedAssets>,
2138 fee_asset_item: u32,
2139 weight_limit: WeightLimit,
2140 ) -> DispatchResult {
2141 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2142 let dest = (*dest).try_into().map_err(|()| {
2143 tracing::debug!(
2144 target: "xcm::pallet_xcm::do_teleport_assets",
2145 "Failed to convert destination VersionedLocation",
2146 );
2147 Error::<T>::BadVersion
2148 })?;
2149 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2150 tracing::debug!(
2151 target: "xcm::pallet_xcm::do_teleport_assets",
2152 "Failed to convert beneficiary VersionedLocation",
2153 );
2154 Error::<T>::BadVersion
2155 })?;
2156 let assets: Assets = (*assets).try_into().map_err(|()| {
2157 tracing::debug!(
2158 target: "xcm::pallet_xcm::do_teleport_assets",
2159 "Failed to convert VersionedAssets",
2160 );
2161 Error::<T>::BadVersion
2162 })?;
2163 tracing::debug!(
2164 target: "xcm::pallet_xcm::do_teleport_assets",
2165 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
2166 );
2167
2168 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2169 let value = (origin_location, assets.into_inner());
2170 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2171 let (origin_location, assets) = value;
2172 for asset in assets.iter() {
2173 let transfer_type =
2174 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
2175 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
2176 }
2177 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
2178
2179 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2180 origin_location.clone(),
2181 dest.clone(),
2182 Either::Left(beneficiary),
2183 assets,
2184 TransferType::Teleport,
2185 FeesHandling::Batched { fees },
2186 weight_limit,
2187 )?;
2188 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
2189 }
2190
2191 fn do_transfer_assets(
2192 origin: Location,
2193 dest: Location,
2194 beneficiary: Either<Location, Xcm<()>>,
2195 mut assets: Vec<Asset>,
2196 assets_transfer_type: TransferType,
2197 fee_asset_index: usize,
2198 fees_transfer_type: TransferType,
2199 weight_limit: WeightLimit,
2200 ) -> DispatchResult {
2201 let fees = if fees_transfer_type == assets_transfer_type {
2203 let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
2204 FeesHandling::Batched { fees }
2206 } else {
2207 ensure!(
2213 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
2214 Error::<T>::InvalidAssetUnsupportedReserve
2215 );
2216 let weight_limit = weight_limit.clone();
2217 let fees = assets.remove(fee_asset_index);
2220 let (local_xcm, remote_xcm) = match fees_transfer_type {
2221 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
2222 origin.clone(),
2223 dest.clone(),
2224 fees,
2225 weight_limit,
2226 )?,
2227 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
2228 origin.clone(),
2229 dest.clone(),
2230 fees,
2231 weight_limit,
2232 )?,
2233 TransferType::Teleport => Self::teleport_fees_instructions(
2234 origin.clone(),
2235 dest.clone(),
2236 fees,
2237 weight_limit,
2238 )?,
2239 TransferType::RemoteReserve(_) => {
2240 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into())
2241 },
2242 };
2243 FeesHandling::Separate { local_xcm, remote_xcm }
2244 };
2245
2246 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2247 origin.clone(),
2248 dest.clone(),
2249 beneficiary,
2250 assets,
2251 assets_transfer_type,
2252 fees,
2253 weight_limit,
2254 )?;
2255 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2256 }
2257
2258 fn build_xcm_transfer_type(
2259 origin: Location,
2260 dest: Location,
2261 beneficiary: Either<Location, Xcm<()>>,
2262 assets: Vec<Asset>,
2263 transfer_type: TransferType,
2264 fees: FeesHandling<T>,
2265 weight_limit: WeightLimit,
2266 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
2267 tracing::debug!(
2268 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2269 ?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
2270 );
2271 match transfer_type {
2272 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
2273 origin.clone(),
2274 dest.clone(),
2275 beneficiary,
2276 assets,
2277 fees,
2278 weight_limit,
2279 )
2280 .map(|(local, remote)| (local, Some(remote))),
2281 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
2282 origin.clone(),
2283 dest.clone(),
2284 beneficiary,
2285 assets,
2286 fees,
2287 weight_limit,
2288 )
2289 .map(|(local, remote)| (local, Some(remote))),
2290 TransferType::RemoteReserve(reserve) => {
2291 let fees = match fees {
2292 FeesHandling::Batched { fees } => fees,
2293 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2294 };
2295 Self::remote_reserve_transfer_program(
2296 origin.clone(),
2297 reserve.try_into().map_err(|()| {
2298 tracing::debug!(
2299 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2300 "Failed to convert remote reserve location",
2301 );
2302 Error::<T>::BadVersion
2303 })?,
2304 beneficiary,
2305 dest.clone(),
2306 assets,
2307 fees,
2308 weight_limit,
2309 )
2310 .map(|local| (local, None))
2311 },
2312 TransferType::Teleport => Self::teleport_assets_program(
2313 origin.clone(),
2314 dest.clone(),
2315 beneficiary,
2316 assets,
2317 fees,
2318 weight_limit,
2319 )
2320 .map(|(local, remote)| (local, Some(remote))),
2321 }
2322 }
2323
2324 fn execute_xcm_transfer(
2325 origin: Location,
2326 dest: Location,
2327 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
2328 remote_xcm: Option<Xcm<()>>,
2329 ) -> DispatchResult {
2330 tracing::debug!(
2331 target: "xcm::pallet_xcm::execute_xcm_transfer",
2332 ?origin, ?dest, ?local_xcm, ?remote_xcm,
2333 );
2334
2335 let weight =
2336 T::Weigher::weight(&mut local_xcm, Weight::MAX).map_err(|error| {
2337 tracing::debug!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, "Failed to calculate weight");
2338 Error::<T>::UnweighableMessage
2339 })?;
2340 let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
2341 let outcome = T::XcmExecutor::prepare_and_execute(
2342 origin.clone(),
2343 local_xcm,
2344 &mut hash,
2345 weight,
2346 weight,
2347 );
2348 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
2349 outcome.clone().ensure_complete().map_err(|error| {
2350 tracing::error!(
2351 target: "xcm::pallet_xcm::execute_xcm_transfer",
2352 ?error, "XCM execution failed with error with outcome: {:?}", outcome
2353 );
2354 Error::<T>::LocalExecutionIncompleteWithError {
2355 index: error.index,
2356 error: error.error.into(),
2357 }
2358 })?;
2359
2360 if let Some(remote_xcm) = remote_xcm {
2361 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
2362 .map_err(|error| {
2363 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
2364 Error::<T>::from(error)
2365 })?;
2366 if origin != Here.into_location() {
2367 Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
2368 tracing::error!(
2369 target: "xcm::pallet_xcm::execute_xcm_transfer",
2370 ?error, ?price, ?origin, "Unable to charge fee",
2371 );
2372 Error::<T>::FeesNotMet
2373 })?;
2374 }
2375 let message_id = T::XcmRouter::deliver(ticket)
2376 .map_err(|error| {
2377 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
2378 Error::<T>::from(error)
2379 })?;
2380
2381 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
2382 Self::deposit_event(e);
2383 }
2384 Ok(())
2385 }
2386
2387 fn add_fees_to_xcm(
2388 dest: Location,
2389 fees: FeesHandling<T>,
2390 weight_limit: WeightLimit,
2391 local: &mut Xcm<<T as Config>::RuntimeCall>,
2392 remote: &mut Xcm<()>,
2393 ) -> Result<(), Error<T>> {
2394 match fees {
2395 FeesHandling::Batched { fees } => {
2396 let context = T::UniversalLocation::get();
2397 let reanchored_fees =
2400 fees.reanchored(&dest, &context).map_err(|e| {
2401 tracing::error!(target: "xcm::pallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
2402 Error::<T>::CannotReanchor
2403 })?;
2404 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
2406 },
2407 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
2408 core::mem::swap(local, &mut local_fees);
2411 core::mem::swap(remote, &mut remote_fees);
2412 local.inner_mut().append(&mut local_fees.into_inner());
2414 remote.inner_mut().append(&mut remote_fees.into_inner());
2415 },
2416 }
2417 Ok(())
2418 }
2419
2420 fn local_reserve_fees_instructions(
2421 origin: Location,
2422 dest: Location,
2423 fees: Asset,
2424 weight_limit: WeightLimit,
2425 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2426 let value = (origin, vec![fees.clone()]);
2427 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2428
2429 let context = T::UniversalLocation::get();
2430 let reanchored_fees = fees.clone().reanchored(&dest, &context).map_err(|_| {
2431 tracing::debug!(
2432 target: "xcm::pallet_xcm::local_reserve_fees_instructions",
2433 "Failed to re-anchor fees",
2434 );
2435 Error::<T>::CannotReanchor
2436 })?;
2437
2438 let local_execute_xcm = Xcm(vec![
2439 TransferAsset { assets: fees.into(), beneficiary: dest },
2441 ]);
2442 let xcm_on_dest = Xcm(vec![
2443 ReserveAssetDeposited(reanchored_fees.clone().into()),
2445 BuyExecution { fees: reanchored_fees, weight_limit },
2447 ]);
2448 Ok((local_execute_xcm, xcm_on_dest))
2449 }
2450
2451 fn local_reserve_transfer_programs(
2452 origin: Location,
2453 dest: Location,
2454 beneficiary: Either<Location, Xcm<()>>,
2455 assets: Vec<Asset>,
2456 fees: FeesHandling<T>,
2457 weight_limit: WeightLimit,
2458 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2459 let value = (origin, assets);
2460 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2461 let (_, assets) = value;
2462
2463 let max_assets =
2465 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2466 let assets: Assets = assets.into();
2467 let context = T::UniversalLocation::get();
2468 let mut reanchored_assets = assets.clone();
2469 reanchored_assets
2470 .reanchor(&dest, &context)
2471 .map_err(|e| {
2472 tracing::error!(target: "xcm::pallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2473 Error::<T>::CannotReanchor
2474 })?;
2475
2476 let mut local_execute_xcm = Xcm(vec![
2478 TransferAsset { assets, beneficiary: dest.clone() },
2480 ]);
2481 let mut xcm_on_dest = Xcm(vec![
2483 ReserveAssetDeposited(reanchored_assets),
2485 ClearOrigin,
2487 ]);
2488 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2490
2491 let custom_remote_xcm = match beneficiary {
2493 Either::Right(custom_xcm) => custom_xcm,
2494 Either::Left(beneficiary) => {
2495 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2497 },
2498 };
2499 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2500
2501 Ok((local_execute_xcm, xcm_on_dest))
2502 }
2503
2504 fn destination_reserve_fees_instructions(
2505 origin: Location,
2506 dest: Location,
2507 fees: Asset,
2508 weight_limit: WeightLimit,
2509 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2510 let value = (origin, vec![fees.clone()]);
2511 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2512 ensure!(
2513 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
2514 Error::<T>::InvalidAssetUnsupportedReserve
2515 );
2516
2517 let context = T::UniversalLocation::get();
2518 let reanchored_fees = fees
2519 .clone()
2520 .reanchored(&dest, &context)
2521 .map_err(|e| {
2522 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
2523 Error::<T>::CannotReanchor
2524 })?;
2525 let fees: Assets = fees.into();
2526
2527 let local_execute_xcm = Xcm(vec![
2528 WithdrawAsset(fees.clone()),
2530 BurnAsset(fees),
2532 ]);
2533 let xcm_on_dest = Xcm(vec![
2534 WithdrawAsset(reanchored_fees.clone().into()),
2536 BuyExecution { fees: reanchored_fees, weight_limit },
2538 ]);
2539 Ok((local_execute_xcm, xcm_on_dest))
2540 }
2541
2542 fn destination_reserve_transfer_programs(
2543 origin: Location,
2544 dest: Location,
2545 beneficiary: Either<Location, Xcm<()>>,
2546 assets: Vec<Asset>,
2547 fees: FeesHandling<T>,
2548 weight_limit: WeightLimit,
2549 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2550 let value = (origin, assets);
2551 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2552 let (_, assets) = value;
2553 for asset in assets.iter() {
2554 ensure!(
2555 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2556 Error::<T>::InvalidAssetUnsupportedReserve
2557 );
2558 }
2559
2560 let max_assets =
2562 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2563 let assets: Assets = assets.into();
2564 let context = T::UniversalLocation::get();
2565 let mut reanchored_assets = assets.clone();
2566 reanchored_assets
2567 .reanchor(&dest, &context)
2568 .map_err(|e| {
2569 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2570 Error::<T>::CannotReanchor
2571 })?;
2572
2573 let mut local_execute_xcm = Xcm(vec![
2575 WithdrawAsset(assets.clone()),
2577 BurnAsset(assets),
2579 ]);
2580 let mut xcm_on_dest = Xcm(vec![
2582 WithdrawAsset(reanchored_assets),
2584 ClearOrigin,
2586 ]);
2587 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2589
2590 let custom_remote_xcm = match beneficiary {
2592 Either::Right(custom_xcm) => custom_xcm,
2593 Either::Left(beneficiary) => {
2594 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2596 },
2597 };
2598 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2599
2600 Ok((local_execute_xcm, xcm_on_dest))
2601 }
2602
2603 fn remote_reserve_transfer_program(
2605 origin: Location,
2606 reserve: Location,
2607 beneficiary: Either<Location, Xcm<()>>,
2608 dest: Location,
2609 assets: Vec<Asset>,
2610 fees: Asset,
2611 weight_limit: WeightLimit,
2612 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2613 let value = (origin, assets);
2614 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2615 let (_, assets) = value;
2616
2617 let max_assets = assets.len() as u32;
2618 let context = T::UniversalLocation::get();
2619 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2622 let reserve_fees = fees_half_1
2624 .reanchored(&reserve, &context)
2625 .map_err(|e| {
2626 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2627 Error::<T>::CannotReanchor
2628 })?;
2629 let dest_fees = fees_half_2
2631 .reanchored(&dest, &context)
2632 .map_err(|e| {
2633 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2634 Error::<T>::CannotReanchor
2635 })?;
2636 let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2638 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2639 Error::<T>::CannotReanchor
2640 })?;
2641 let mut xcm_on_dest =
2643 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2644 let custom_xcm_on_dest = match beneficiary {
2646 Either::Right(custom_xcm) => custom_xcm,
2647 Either::Left(beneficiary) => {
2648 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2650 },
2651 };
2652 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2653 let xcm_on_reserve = Xcm(vec![
2655 BuyExecution { fees: reserve_fees, weight_limit },
2656 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2657 ]);
2658 Ok(Xcm(vec![
2659 WithdrawAsset(assets.into()),
2660 SetFeesMode { jit_withdraw: true },
2661 InitiateReserveWithdraw {
2662 assets: Wild(AllCounted(max_assets)),
2663 reserve,
2664 xcm: xcm_on_reserve,
2665 },
2666 ]))
2667 }
2668
2669 fn teleport_fees_instructions(
2670 origin: Location,
2671 dest: Location,
2672 fees: Asset,
2673 weight_limit: WeightLimit,
2674 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2675 let value = (origin, vec![fees.clone()]);
2676 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2677 ensure!(
2678 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2679 Error::<T>::Filtered
2680 );
2681
2682 let context = T::UniversalLocation::get();
2683 let reanchored_fees = fees
2684 .clone()
2685 .reanchored(&dest, &context)
2686 .map_err(|e| {
2687 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2688 Error::<T>::CannotReanchor
2689 })?;
2690
2691 let dummy_context =
2693 XcmContext { origin: None, message_id: Default::default(), topic: None };
2694 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2699 &dest,
2700 &fees,
2701 &dummy_context,
2702 )
2703 .map_err(|e| {
2704 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2705 Error::<T>::CannotCheckOutTeleport
2706 })?;
2707 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2710 &dest,
2711 &fees,
2712 &dummy_context,
2713 );
2714
2715 let fees: Assets = fees.into();
2716 let local_execute_xcm = Xcm(vec![
2717 WithdrawAsset(fees.clone()),
2719 BurnAsset(fees),
2721 ]);
2722 let xcm_on_dest = Xcm(vec![
2723 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2725 BuyExecution { fees: reanchored_fees, weight_limit },
2727 ]);
2728 Ok((local_execute_xcm, xcm_on_dest))
2729 }
2730
2731 fn teleport_assets_program(
2732 origin: Location,
2733 dest: Location,
2734 beneficiary: Either<Location, Xcm<()>>,
2735 assets: Vec<Asset>,
2736 fees: FeesHandling<T>,
2737 weight_limit: WeightLimit,
2738 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2739 let value = (origin, assets);
2740 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2741 let (_, assets) = value;
2742 for asset in assets.iter() {
2743 ensure!(
2744 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2745 Error::<T>::Filtered
2746 );
2747 }
2748
2749 let max_assets =
2751 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2752 let context = T::UniversalLocation::get();
2753 let assets: Assets = assets.into();
2754 let mut reanchored_assets = assets.clone();
2755 reanchored_assets
2756 .reanchor(&dest, &context)
2757 .map_err(|e| {
2758 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2759 Error::<T>::CannotReanchor
2760 })?;
2761
2762 let dummy_context =
2764 XcmContext { origin: None, message_id: Default::default(), topic: None };
2765 for asset in assets.inner() {
2766 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2771 &dest,
2772 asset,
2773 &dummy_context,
2774 )
2775 .map_err(|e| {
2776 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2777 Error::<T>::CannotCheckOutTeleport
2778 })?;
2779 }
2780 for asset in assets.inner() {
2781 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2784 &dest,
2785 asset,
2786 &dummy_context,
2787 );
2788 }
2789
2790 let mut local_execute_xcm = Xcm(vec![
2792 WithdrawAsset(assets.clone()),
2794 BurnAsset(assets),
2796 ]);
2797 let mut xcm_on_dest = Xcm(vec![
2799 ReceiveTeleportedAsset(reanchored_assets),
2801 ClearOrigin,
2803 ]);
2804 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2806
2807 let custom_remote_xcm = match beneficiary {
2809 Either::Right(custom_xcm) => custom_xcm,
2810 Either::Left(beneficiary) => {
2811 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2813 },
2814 };
2815 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2816
2817 Ok((local_execute_xcm, xcm_on_dest))
2818 }
2819
2820 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2822 match fees.fun {
2823 Fungible(amount) => {
2824 let fee1 = amount.saturating_div(2);
2825 let fee2 = amount.saturating_sub(fee1);
2826 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2827 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2828 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2829 },
2830 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2831 }
2832 }
2833
2834 pub(crate) fn lazy_migration(
2837 mut stage: VersionMigrationStage,
2838 weight_cutoff: Weight,
2839 ) -> (Weight, Option<VersionMigrationStage>) {
2840 let mut weight_used = Weight::zero();
2841
2842 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2843 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2844 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2845 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2846 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2847 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2848 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2849
2850 use VersionMigrationStage::*;
2851
2852 if stage == MigrateSupportedVersion {
2853 for v in 0..XCM_VERSION {
2856 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2857 if let Ok(new_key) = old_key.into_latest() {
2858 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2859 }
2860 weight_used.saturating_accrue(sv_migrate_weight);
2861 if weight_used.any_gte(weight_cutoff) {
2862 return (weight_used, Some(stage));
2863 }
2864 }
2865 }
2866 stage = MigrateVersionNotifiers;
2867 }
2868 if stage == MigrateVersionNotifiers {
2869 for v in 0..XCM_VERSION {
2870 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2871 if let Ok(new_key) = old_key.into_latest() {
2872 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2873 }
2874 weight_used.saturating_accrue(vn_migrate_weight);
2875 if weight_used.any_gte(weight_cutoff) {
2876 return (weight_used, Some(stage));
2877 }
2878 }
2879 }
2880 stage = NotifyCurrentTargets(None);
2881 }
2882
2883 let xcm_version = T::AdvertisedXcmVersion::get();
2884
2885 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2886 let mut iter = match maybe_last_raw_key {
2887 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2888 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2889 };
2890 while let Some((key, value)) = iter.next() {
2891 let (query_id, max_weight, target_xcm_version) = value;
2892 let new_key: Location = match key.clone().try_into() {
2893 Ok(k) if target_xcm_version != xcm_version => k,
2894 _ => {
2895 weight_used.saturating_accrue(vnt_already_notified_weight);
2898 continue;
2899 },
2900 };
2901 let response = Response::Version(xcm_version);
2902 let message =
2903 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2904 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2905 Ok((message_id, cost)) => {
2906 let value = (query_id, max_weight, xcm_version);
2907 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2908 Event::VersionChangeNotified {
2909 destination: new_key,
2910 result: xcm_version,
2911 cost,
2912 message_id,
2913 }
2914 },
2915 Err(e) => {
2916 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2917 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2918 },
2919 };
2920 Self::deposit_event(event);
2921 weight_used.saturating_accrue(vnt_notify_weight);
2922 if weight_used.any_gte(weight_cutoff) {
2923 let last = Some(iter.last_raw_key().into());
2924 return (weight_used, Some(NotifyCurrentTargets(last)));
2925 }
2926 }
2927 stage = MigrateAndNotifyOldTargets;
2928 }
2929 if stage == MigrateAndNotifyOldTargets {
2930 for v in 0..XCM_VERSION {
2931 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2932 let (query_id, max_weight, target_xcm_version) = value;
2933 let new_key = match Location::try_from(old_key.clone()) {
2934 Ok(k) => k,
2935 Err(()) => {
2936 Self::deposit_event(Event::NotifyTargetMigrationFail {
2937 location: old_key,
2938 query_id: value.0,
2939 });
2940 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2941 if weight_used.any_gte(weight_cutoff) {
2942 return (weight_used, Some(stage));
2943 }
2944 continue;
2945 },
2946 };
2947
2948 let versioned_key = LatestVersionedLocation(&new_key);
2949 if target_xcm_version == xcm_version {
2950 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2951 weight_used.saturating_accrue(vnt_migrate_weight);
2952 } else {
2953 let response = Response::Version(xcm_version);
2955 let message = Xcm(vec![QueryResponse {
2956 query_id,
2957 response,
2958 max_weight,
2959 querier: None,
2960 }]);
2961 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2962 Ok((message_id, cost)) => {
2963 VersionNotifyTargets::<T>::insert(
2964 XCM_VERSION,
2965 versioned_key,
2966 (query_id, max_weight, xcm_version),
2967 );
2968 Event::VersionChangeNotified {
2969 destination: new_key,
2970 result: xcm_version,
2971 cost,
2972 message_id,
2973 }
2974 },
2975 Err(e) => Event::NotifyTargetSendFail {
2976 location: new_key,
2977 query_id,
2978 error: e.into(),
2979 },
2980 };
2981 Self::deposit_event(event);
2982 weight_used.saturating_accrue(vnt_notify_migrate_weight);
2983 }
2984 if weight_used.any_gte(weight_cutoff) {
2985 return (weight_used, Some(stage));
2986 }
2987 }
2988 }
2989 }
2990 (weight_used, None)
2991 }
2992
2993 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2995 let dest = dest.into();
2996 let versioned_dest = VersionedLocation::from(dest.clone());
2997 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
2998 ensure!(!already, XcmError::InvalidLocation);
2999 let query_id = QueryCounter::<T>::mutate(|q| {
3000 let r = *q;
3001 q.saturating_inc();
3002 r
3003 });
3004 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
3006 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3007 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
3008 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
3009 let query_status =
3010 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
3011 Queries::<T>::insert(query_id, query_status);
3012 Ok(())
3013 }
3014
3015 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
3017 let dest = dest.into();
3018 let versioned_dest = LatestVersionedLocation(&dest);
3019 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
3020 .ok_or(XcmError::InvalidLocation)?;
3021 let (message_id, cost) =
3022 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
3023 Self::deposit_event(Event::VersionNotifyUnrequested {
3024 destination: dest,
3025 cost,
3026 message_id,
3027 });
3028 Queries::<T>::remove(query_id);
3029 Ok(())
3030 }
3031
3032 pub fn send_xcm(
3036 interior: impl Into<Junctions>,
3037 dest: impl Into<Location>,
3038 mut message: Xcm<()>,
3039 ) -> Result<XcmHash, SendError> {
3040 let interior = interior.into();
3041 let local_origin = interior.clone().into();
3042 let dest = dest.into();
3043 let is_waived =
3044 <T::XcmExecutor as FeeManager>::is_waived(Some(&local_origin), FeeReason::ChargeFees);
3045 if interior != Junctions::Here {
3046 message.0.insert(0, DescendOrigin(interior.clone()));
3047 }
3048 tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
3049 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
3050 if !is_waived {
3051 Self::charge_fees(local_origin, price).map_err(|e| {
3052 tracing::error!(
3053 target: "xcm::pallet_xcm::send_xcm",
3054 ?e,
3055 "Charging fees failed with error",
3056 );
3057 SendError::Fees
3058 })?;
3059 }
3060 T::XcmRouter::deliver(ticket)
3061 }
3062
3063 pub fn check_account() -> T::AccountId {
3064 const ID: PalletId = PalletId(*b"py/xcmch");
3065 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
3066 }
3067
3068 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
3074 origin: OriginCaller,
3075 call: RuntimeCall,
3076 result_xcms_version: XcmVersion,
3077 ) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3078 where
3079 Runtime: crate::Config,
3080 Router: InspectMessageQueues,
3081 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
3082 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
3083 {
3084 with_transaction(|| {
3087 crate::Pallet::<Runtime>::set_record_xcm(true);
3088 Router::clear_messages();
3090 frame_system::Pallet::<Runtime>::reset_events();
3092 let result = call.dispatch(origin.into());
3093 crate::Pallet::<Runtime>::set_record_xcm(false);
3094 let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
3095 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
3096 .transpose()
3097 .map_err(|()| {
3098 tracing::debug!(
3099 target: "xcm::DryRunApi::dry_run_call",
3100 "Local xcm version conversion failed"
3101 );
3102
3103 XcmDryRunApiError::VersionedConversionFailed
3104 });
3105
3106 let forwarded_xcms =
3108 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages())
3109 .inspect_err(|error| {
3110 tracing::debug!(
3111 target: "xcm::DryRunApi::dry_run_call",
3112 ?error, "Forwarded xcms version conversion failed with error"
3113 );
3114 });
3115 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
3116 frame_system::Pallet::<Runtime>::read_events_no_consensus()
3117 .map(|record| record.event.clone())
3118 .collect();
3119
3120 let outcome = local_xcm.and_then(|local_xcm| {
3121 forwarded_xcms.map(|forwarded_xcms| CallDryRunEffects {
3122 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
3123 forwarded_xcms,
3124 emitted_events: events,
3125 execution_result: result,
3126 })
3127 });
3128 TransactionOutcome::Rollback(Ok::<_, DispatchError>(outcome))
3129 })
3130 .expect("always Ok; qed")
3131 }
3132
3133 pub fn dry_run_xcm<Router>(
3138 origin_location: VersionedLocation,
3139 xcm: VersionedXcm<<T as Config>::RuntimeCall>,
3140 ) -> Result<XcmDryRunEffects<<T as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3141 where
3142 Router: InspectMessageQueues,
3143 {
3144 let origin_location: Location = origin_location.try_into().map_err(|error| {
3146 tracing::debug!(
3147 target: "xcm::DryRunApi::dry_run_xcm",
3148 ?error, "Location version conversion failed with error"
3149 );
3150 XcmDryRunApiError::VersionedConversionFailed
3151 })?;
3152 let xcm_version = xcm.identify_version();
3153 let xcm: Xcm<<T as Config>::RuntimeCall> = xcm.try_into().map_err(|error| {
3154 tracing::debug!(
3155 target: "xcm::DryRunApi::dry_run_xcm",
3156 ?error, "Xcm version conversion failed with error"
3157 );
3158 XcmDryRunApiError::VersionedConversionFailed
3159 })?;
3160 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
3161
3162 with_transaction(|| {
3165 Router::clear_messages();
3167 frame_system::Pallet::<T>::reset_events();
3168
3169 let result = <T as Config>::XcmExecutor::prepare_and_execute(
3170 origin_location,
3171 xcm,
3172 &mut hash,
3173 Weight::MAX, Weight::zero(),
3175 );
3176 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
3177 .inspect_err(|error| {
3178 tracing::debug!(
3179 target: "xcm::DryRunApi::dry_run_xcm",
3180 ?error, "Forwarded xcms version conversion failed with error"
3181 );
3182 });
3183 let events: Vec<<T as frame_system::Config>::RuntimeEvent> =
3184 frame_system::Pallet::<T>::read_events_no_consensus()
3185 .map(|record| record.event.clone())
3186 .collect();
3187
3188 let outcome = forwarded_xcms.map(|forwarded_xcms| XcmDryRunEffects {
3189 forwarded_xcms,
3190 emitted_events: events,
3191 execution_result: result,
3192 });
3193 TransactionOutcome::Rollback(Ok::<_, DispatchError>(outcome))
3194 })
3195 .expect("always Ok; qed")
3196 }
3197
3198 fn convert_xcms(
3199 xcm_version: XcmVersion,
3200 xcms: Vec<VersionedXcm<()>>,
3201 ) -> Result<Vec<VersionedXcm<()>>, ()> {
3202 xcms.into_iter()
3203 .map(|xcm| xcm.into_version(xcm_version))
3204 .collect::<Result<Vec<_>, ()>>()
3205 }
3206
3207 fn convert_forwarded_xcms(
3208 xcm_version: XcmVersion,
3209 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
3210 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
3211 forwarded_xcms
3212 .into_iter()
3213 .map(|(dest, forwarded_xcms)| {
3214 let dest = dest.into_version(xcm_version)?;
3215 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
3216
3217 Ok((dest, forwarded_xcms))
3218 })
3219 .collect::<Result<Vec<_>, ()>>()
3220 .map_err(|()| {
3221 tracing::debug!(
3222 target: "xcm::pallet_xcm::convert_forwarded_xcms",
3223 "Failed to convert VersionedLocation to requested version",
3224 );
3225 XcmDryRunApiError::VersionedConversionFailed
3226 })
3227 }
3228
3229 pub fn query_acceptable_payment_assets(
3234 version: xcm::Version,
3235 asset_ids: Vec<AssetId>,
3236 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
3237 Ok(asset_ids
3238 .into_iter()
3239 .map(|asset_id| VersionedAssetId::from(asset_id))
3240 .filter_map(|asset_id| asset_id.into_version(version).ok())
3241 .collect())
3242 }
3243
3244 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
3245 let message = Xcm::<()>::try_from(message.clone())
3246 .map_err(|e| {
3247 tracing::debug!(target: "xcm::pallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
3248 XcmPaymentApiError::VersionedConversionFailed
3249 })?;
3250
3251 T::Weigher::weight(&mut message.clone().into(), Weight::MAX).map_err(|error| {
3252 tracing::debug!(target: "xcm::pallet_xcm::query_xcm_weight", ?error, ?message, "Error when querying XCM weight");
3253 XcmPaymentApiError::WeightNotComputable
3254 })
3255 }
3256
3257 pub fn query_weight_to_asset_fee<Trader: xcm_executor::traits::WeightTrader>(
3274 weight: Weight,
3275 asset_id: VersionedAssetId,
3276 ) -> Result<u128, XcmPaymentApiError> {
3277 let asset_id: AssetId = asset_id.clone().try_into()
3278 .map_err(|e| {
3279 tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset_id, "Failed to convert versioned asset");
3280 XcmPaymentApiError::VersionedConversionFailed
3281 })?;
3282
3283 let context = XcmContext::with_message_id(XcmHash::default());
3284
3285 let mut trader = Trader::new();
3286 let required = trader.quote_weight(weight, asset_id.clone(), &context)
3287 .map_err(|e| {
3288 tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset_id, "Failed to quote weight");
3289 XcmPaymentApiError::AssetNotFound
3290 })?;
3291 match (required.id, required.fun) {
3292 (required_id, Fungible(required_amount)) if required_id.eq(&asset_id) => {
3293 Ok(required_amount)
3294 },
3295 _ => Err(XcmPaymentApiError::AssetNotFound),
3296 }
3297 }
3298
3299 pub fn query_delivery_fees<AssetExchanger: xcm_executor::traits::AssetExchange>(
3306 destination: VersionedLocation,
3307 message: VersionedXcm<()>,
3308 versioned_asset_id: VersionedAssetId,
3309 ) -> Result<VersionedAssets, XcmPaymentApiError> {
3310 let result_version = destination.identify_version().max(message.identify_version());
3311
3312 let destination: Location = destination
3313 .clone()
3314 .try_into()
3315 .map_err(|e| {
3316 tracing::debug!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
3317 XcmPaymentApiError::VersionedConversionFailed
3318 })?;
3319
3320 let message: Xcm<()> =
3321 message.clone().try_into().map_err(|e| {
3322 tracing::debug!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
3323 XcmPaymentApiError::VersionedConversionFailed
3324 })?;
3325
3326 let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
3327 tracing::debug!(target: "xcm::pallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
3328 XcmPaymentApiError::Unroutable
3329 })?;
3330
3331 if fees.len() != 1 {
3333 return Err(XcmPaymentApiError::Unimplemented);
3334 }
3335
3336 let fee = fees.get(0).ok_or(XcmPaymentApiError::Unimplemented)?;
3337
3338 let asset_id = versioned_asset_id.clone().try_into().map_err(|()| {
3339 tracing::trace!(
3340 target: "xcm::xcm_runtime_apis::query_delivery_fees",
3341 "Failed to convert asset id: {versioned_asset_id:?}!"
3342 );
3343 XcmPaymentApiError::VersionedConversionFailed
3344 })?;
3345
3346 let assets_to_pay = if fee.id == asset_id {
3347 fees
3349 } else {
3350 AssetExchanger::quote_exchange_price(
3352 &fees.into(),
3353 &(asset_id, Fungible(1)).into(),
3354 true, )
3356 .ok_or(XcmPaymentApiError::AssetNotFound)?
3357 };
3358
3359 VersionedAssets::from(assets_to_pay).into_version(result_version).map_err(|e| {
3360 tracing::trace!(
3361 target: "xcm::pallet_xcm::query_delivery_fees",
3362 ?e,
3363 ?result_version,
3364 "Failed to convert fees into desired version"
3365 );
3366 XcmPaymentApiError::VersionedConversionFailed
3367 })
3368 }
3369
3370 pub fn is_trusted_reserve(
3373 asset: VersionedAsset,
3374 location: VersionedLocation,
3375 ) -> Result<bool, TrustedQueryApiError> {
3376 let location: Location = location.try_into().map_err(|e| {
3377 tracing::debug!(
3378 target: "xcm::pallet_xcm::is_trusted_reserve",
3379 ?e, "Failed to convert versioned location",
3380 );
3381 TrustedQueryApiError::VersionedLocationConversionFailed
3382 })?;
3383
3384 let a: Asset = asset.try_into().map_err(|e| {
3385 tracing::debug!(
3386 target: "xcm::pallet_xcm::is_trusted_reserve",
3387 ?e, "Failed to convert versioned asset",
3388 );
3389 TrustedQueryApiError::VersionedAssetConversionFailed
3390 })?;
3391
3392 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
3393 }
3394
3395 pub fn is_trusted_teleporter(
3397 asset: VersionedAsset,
3398 location: VersionedLocation,
3399 ) -> Result<bool, TrustedQueryApiError> {
3400 let location: Location = location.try_into().map_err(|e| {
3401 tracing::debug!(
3402 target: "xcm::pallet_xcm::is_trusted_teleporter",
3403 ?e, "Failed to convert versioned location",
3404 );
3405 TrustedQueryApiError::VersionedLocationConversionFailed
3406 })?;
3407 let a: Asset = asset.try_into().map_err(|e| {
3408 tracing::debug!(
3409 target: "xcm::pallet_xcm::is_trusted_teleporter",
3410 ?e, "Failed to convert versioned asset",
3411 );
3412 TrustedQueryApiError::VersionedAssetConversionFailed
3413 })?;
3414 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
3415 }
3416
3417 pub fn authorized_aliasers(
3419 target: VersionedLocation,
3420 ) -> Result<Vec<OriginAliaser>, AuthorizedAliasersApiError> {
3421 let desired_version = target.identify_version();
3422 let target: VersionedLocation = target.into_version(XCM_VERSION).map_err(|e| {
3424 tracing::debug!(
3425 target: "xcm::pallet_xcm::authorized_aliasers",
3426 ?e, "Failed to convert versioned location",
3427 );
3428 AuthorizedAliasersApiError::LocationVersionConversionFailed
3429 })?;
3430 Ok(AuthorizedAliases::<T>::get(&target)
3431 .map(|authorized| {
3432 authorized
3433 .aliasers
3434 .into_iter()
3435 .filter_map(|aliaser| {
3436 let OriginAliaser { location, expiry } = aliaser;
3437 location
3438 .into_version(desired_version)
3439 .map(|location| OriginAliaser { location, expiry })
3440 .ok()
3441 })
3442 .collect()
3443 })
3444 .unwrap_or_default())
3445 }
3446
3447 pub fn is_authorized_alias(
3452 origin: VersionedLocation,
3453 target: VersionedLocation,
3454 ) -> Result<bool, AuthorizedAliasersApiError> {
3455 let desired_version = target.identify_version();
3456 let origin = origin.into_version(desired_version).map_err(|e| {
3457 tracing::debug!(
3458 target: "xcm::pallet_xcm::is_authorized_alias",
3459 ?e, "mismatching origin and target versions",
3460 );
3461 AuthorizedAliasersApiError::LocationVersionConversionFailed
3462 })?;
3463 Ok(Self::authorized_aliasers(target)?.into_iter().any(|aliaser| {
3464 aliaser.location == origin &&
3467 aliaser
3468 .expiry
3469 .map(|expiry| {
3470 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>() <
3471 expiry
3472 })
3473 .unwrap_or(true)
3474 }))
3475 }
3476
3477 fn do_new_query(
3479 responder: impl Into<Location>,
3480 maybe_notify: Option<(u8, u8)>,
3481 timeout: BlockNumberFor<T>,
3482 match_querier: impl Into<Location>,
3483 ) -> u64 {
3484 QueryCounter::<T>::mutate(|q| {
3485 let r = *q;
3486 q.saturating_inc();
3487 Queries::<T>::insert(
3488 r,
3489 QueryStatus::Pending {
3490 responder: responder.into().into(),
3491 maybe_match_querier: Some(match_querier.into().into()),
3492 maybe_notify,
3493 timeout,
3494 },
3495 );
3496 r
3497 })
3498 }
3499
3500 pub fn report_outcome_notify(
3523 message: &mut Xcm<()>,
3524 responder: impl Into<Location>,
3525 notify: impl Into<<T as Config>::RuntimeCall>,
3526 timeout: BlockNumberFor<T>,
3527 ) -> Result<(), XcmError> {
3528 let responder = responder.into();
3529 let destination = T::UniversalLocation::get().invert_target(&responder).map_err(|()| {
3530 tracing::debug!(
3531 target: "xcm::pallet_xcm::report_outcome_notify",
3532 "Failed to invert responder location to universal location",
3533 );
3534 XcmError::LocationNotInvertible
3535 })?;
3536 let notify: <T as Config>::RuntimeCall = notify.into();
3537 let max_weight = notify.get_dispatch_info().call_weight;
3538 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
3539 let response_info = QueryResponseInfo { destination, query_id, max_weight };
3540 let report_error = Xcm(vec![ReportError(response_info)]);
3541 message.0.insert(0, SetAppendix(report_error));
3542 Ok(())
3543 }
3544
3545 pub fn new_notify_query(
3548 responder: impl Into<Location>,
3549 notify: impl Into<<T as Config>::RuntimeCall>,
3550 timeout: BlockNumberFor<T>,
3551 match_querier: impl Into<Location>,
3552 ) -> u64 {
3553 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
3554 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
3555 );
3556 Self::do_new_query(responder, Some(notify), timeout, match_querier)
3557 }
3558
3559 fn note_unknown_version(dest: &Location) {
3562 tracing::trace!(
3563 target: "xcm::pallet_xcm::note_unknown_version",
3564 ?dest, "XCM version is unknown for destination"
3565 );
3566 let versioned_dest = VersionedLocation::from(dest.clone());
3567 VersionDiscoveryQueue::<T>::mutate(|q| {
3568 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
3569 q[index].1.saturating_inc();
3571 } else {
3572 let _ = q.try_push((versioned_dest, 1));
3573 }
3574 });
3575 }
3576
3577 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
3583 T::XcmExecutor::charge_fees(location.clone(), assets.clone()).map_err(|error| {
3584 tracing::debug!(
3585 target: "xcm::pallet_xcm::charge_fees", ?error,
3586 "Failed to charge fees for location with assets",
3587 );
3588 Error::<T>::FeesNotMet
3589 })?;
3590 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
3591 Ok(())
3592 }
3593
3594 #[cfg(any(feature = "try-runtime", test))]
3604 pub fn do_try_state() -> Result<(), TryRuntimeError> {
3605 use migration::data::NeedsMigration;
3606
3607 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
3611 {
3612 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
3613 } else {
3614 XCM_VERSION.saturating_sub(1)
3615 };
3616
3617 ensure!(
3619 !Queries::<T>::iter_values()
3620 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3621 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
3622 );
3623
3624 ensure!(
3626 !LockedFungibles::<T>::iter_values()
3627 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3628 TryRuntimeError::Other(
3629 "`LockedFungibles` data should be migrated to the higher xcm version!"
3630 )
3631 );
3632
3633 ensure!(
3635 !RemoteLockedFungibles::<T>::iter()
3636 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
3637 data.needs_migration(minimal_allowed_xcm_version)),
3638 TryRuntimeError::Other(
3639 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
3640 )
3641 );
3642
3643 if CurrentMigration::<T>::exists() {
3646 return Ok(());
3647 }
3648
3649 for v in 0..XCM_VERSION {
3651 ensure!(
3652 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
3653 TryRuntimeError::Other(
3654 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
3655 )
3656 );
3657 ensure!(
3658 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
3659 TryRuntimeError::Other(
3660 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
3661 )
3662 );
3663 ensure!(
3664 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
3665 TryRuntimeError::Other(
3666 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
3667 )
3668 );
3669 }
3670
3671 Ok(())
3672 }
3673}
3674
3675pub struct LockTicket<T: Config> {
3676 sovereign_account: T::AccountId,
3677 amount: BalanceOf<T>,
3678 unlocker: Location,
3679 item_index: Option<usize>,
3680}
3681
3682impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
3683 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3684 use xcm_executor::traits::LockError::UnexpectedState;
3685 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
3686 match self.item_index {
3687 Some(index) => {
3688 ensure!(locks.len() > index, UnexpectedState);
3689 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
3690 locks[index].0 = locks[index].0.max(self.amount);
3691 },
3692 None => {
3693 locks.try_push((self.amount, self.unlocker.into())).map_err(
3694 |(balance, location)| {
3695 tracing::debug!(
3696 target: "xcm::pallet_xcm::enact", ?balance, ?location,
3697 "Failed to lock fungibles",
3698 );
3699 UnexpectedState
3700 },
3701 )?;
3702 },
3703 }
3704 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3705 T::Currency::extend_lock(
3706 *b"py/xcmlk",
3707 &self.sovereign_account,
3708 self.amount,
3709 WithdrawReasons::all(),
3710 );
3711 Ok(())
3712 }
3713}
3714
3715pub struct UnlockTicket<T: Config> {
3716 sovereign_account: T::AccountId,
3717 amount: BalanceOf<T>,
3718 unlocker: Location,
3719}
3720
3721impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
3722 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3723 use xcm_executor::traits::LockError::UnexpectedState;
3724 let mut locks =
3725 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
3726 let mut maybe_remove_index = None;
3727 let mut locked = BalanceOf::<T>::zero();
3728 let mut found = false;
3729 for (i, x) in locks.iter_mut().enumerate() {
3732 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
3733 x.0 = x.0.saturating_sub(self.amount);
3734 if x.0.is_zero() {
3735 maybe_remove_index = Some(i);
3736 }
3737 found = true;
3738 }
3739 locked = locked.max(x.0);
3740 }
3741 ensure!(found, UnexpectedState);
3742 if let Some(remove_index) = maybe_remove_index {
3743 locks.swap_remove(remove_index);
3744 }
3745 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3746 let reasons = WithdrawReasons::all();
3747 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3748 Ok(())
3749 }
3750}
3751
3752pub struct ReduceTicket<T: Config> {
3753 key: (u32, T::AccountId, VersionedAssetId),
3754 amount: u128,
3755 locker: VersionedLocation,
3756 owner: VersionedLocation,
3757}
3758
3759impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3760 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3761 use xcm_executor::traits::LockError::UnexpectedState;
3762 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3763 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3764 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3765 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3766 if new_amount == 0 {
3767 RemoteLockedFungibles::<T>::remove(&self.key);
3768 } else {
3769 record.amount = new_amount;
3770 RemoteLockedFungibles::<T>::insert(&self.key, &record);
3771 }
3772 Ok(())
3773 }
3774}
3775
3776impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
3777 type LockTicket = LockTicket<T>;
3778 type UnlockTicket = UnlockTicket<T>;
3779 type ReduceTicket = ReduceTicket<T>;
3780
3781 fn prepare_lock(
3782 unlocker: Location,
3783 asset: Asset,
3784 owner: Location,
3785 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3786 use xcm_executor::traits::LockError::*;
3787 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3788 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3789 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3790 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3791 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3792 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3793 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3794 }
3795
3796 fn prepare_unlock(
3797 unlocker: Location,
3798 asset: Asset,
3799 owner: Location,
3800 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3801 use xcm_executor::traits::LockError::*;
3802 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3803 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3804 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3805 let item_index =
3806 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3807 ensure!(locks[item_index].0 >= amount, NotLocked);
3808 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3809 }
3810
3811 fn note_unlockable(
3812 locker: Location,
3813 asset: Asset,
3814 mut owner: Location,
3815 ) -> Result<(), xcm_executor::traits::LockError> {
3816 use xcm_executor::traits::LockError::*;
3817 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3818 let amount = match asset.fun {
3819 Fungible(a) => a,
3820 NonFungible(_) => return Err(Unimplemented),
3821 };
3822 owner.remove_network_id();
3823 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3824 let locker = locker.into();
3825 let owner = owner.into();
3826 let id: VersionedAssetId = asset.id.into();
3827 let key = (XCM_VERSION, account, id);
3828 let mut record =
3829 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3830 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3831 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3833 record.consumers = old.consumers;
3834 record.amount = record.amount.max(old.amount);
3835 }
3836 RemoteLockedFungibles::<T>::insert(&key, record);
3837 Ok(())
3838 }
3839
3840 fn prepare_reduce_unlockable(
3841 locker: Location,
3842 asset: Asset,
3843 mut owner: Location,
3844 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3845 use xcm_executor::traits::LockError::*;
3846 let amount = match asset.fun {
3847 Fungible(a) => a,
3848 NonFungible(_) => return Err(Unimplemented),
3849 };
3850 owner.remove_network_id();
3851 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3852 let locker = locker.into();
3853 let owner = owner.into();
3854 let id: VersionedAssetId = asset.id.into();
3855 let key = (XCM_VERSION, sovereign_account, id);
3856
3857 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3858 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3860 ensure!(record.amount >= amount, NotEnoughLocked);
3861 ensure!(
3862 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3863 InUse
3864 );
3865 Ok(ReduceTicket { key, amount, locker, owner })
3866 }
3867}
3868
3869impl<T: Config> WrapVersion for Pallet<T> {
3870 fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3871 dest: &Location,
3872 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3873 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3874 Self::get_version_for(dest)
3875 .or_else(|| {
3876 Self::note_unknown_version(dest);
3877 SafeXcmVersion::<T>::get()
3878 })
3879 .ok_or_else(|| {
3880 tracing::trace!(
3881 target: "xcm::pallet_xcm::wrap_version",
3882 ?dest, "Could not determine a version to wrap XCM for destination",
3883 );
3884 ()
3885 })
3886 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3887 }
3888}
3889
3890impl<T: Config> GetVersion for Pallet<T> {
3891 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3892 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3893 }
3894}
3895
3896impl<T: Config> VersionChangeNotifier for Pallet<T> {
3897 fn start(
3906 dest: &Location,
3907 query_id: QueryId,
3908 max_weight: Weight,
3909 _context: &XcmContext,
3910 ) -> XcmResult {
3911 let versioned_dest = LatestVersionedLocation(dest);
3912 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3913 ensure!(!already, XcmError::InvalidLocation);
3914
3915 let xcm_version = T::AdvertisedXcmVersion::get();
3916 let response = Response::Version(xcm_version);
3917 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3918 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3919 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3920 destination: dest.clone(),
3921 cost,
3922 message_id,
3923 });
3924
3925 let value = (query_id, max_weight, xcm_version);
3926 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3927 Ok(())
3928 }
3929
3930 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3933 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3934 Ok(())
3935 }
3936
3937 fn is_subscribed(dest: &Location) -> bool {
3939 let versioned_dest = LatestVersionedLocation(dest);
3940 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3941 }
3942}
3943
3944impl<T: Config> DropAssets for Pallet<T> {
3945 fn drop_assets(origin: &Location, holding: AssetsInHolding, _context: &XcmContext) -> Weight {
3946 if holding.is_empty() {
3947 return Weight::zero();
3948 }
3949 let assets: Vec<Asset> = holding.assets_iter().collect();
3950 holding.fungible.into_iter().for_each(|(_, mut accounting)| {
3955 accounting.forget_imbalance();
3956 });
3957 let versioned = VersionedAssets::from(Assets::from(assets));
3958 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3959 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3960 Self::deposit_event(Event::AssetsTrapped {
3961 hash,
3962 origin: origin.clone(),
3963 assets: versioned,
3964 });
3965 Weight::zero()
3967 }
3968}
3969
3970impl<T: Config> ClaimAssets for Pallet<T> {
3971 fn claim_assets(
3972 origin: &Location,
3973 ticket: &Location,
3974 assets: &Assets,
3975 context: &XcmContext,
3976 ) -> Option<AssetsInHolding> {
3977 let mut versioned = VersionedAssets::from(assets.clone());
3978 match ticket.unpack() {
3979 (0, [GeneralIndex(i)]) => {
3980 versioned = match versioned.into_version(*i as u32) {
3981 Ok(v) => v,
3982 Err(()) => return None,
3983 }
3984 },
3985 (0, []) => (),
3986 _ => return None,
3987 };
3988 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3989 match AssetTraps::<T>::get(hash) {
3990 0 => return None,
3991 1 => AssetTraps::<T>::remove(hash),
3992 n => AssetTraps::<T>::insert(hash, n - 1),
3993 }
3994 let mut claimed = AssetsInHolding::new();
3995 for asset in assets.inner() {
3996 match <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(asset, context)
3997 {
3998 Ok(minted) => {
3999 minted.fungible.iter().for_each(|(_, imbalance)| {
4010 let to_resolve = imbalance.unsafe_clone();
4011 core::mem::drop(to_resolve);
4012 });
4013 claimed.subsume_assets(minted)
4014 },
4015 Err(error) => tracing::debug!(
4016 target: "xcm::pallet_xcm::claim_assets",
4017 ?asset, ?error, "Asset claimed from trap but unable to mint."
4018 ),
4019 }
4020 }
4021 Self::deposit_event(Event::AssetsClaimed {
4022 hash,
4023 origin: origin.clone(),
4024 assets: versioned,
4025 });
4026 Some(claimed)
4027 }
4028}
4029
4030impl<T: Config> OnResponse for Pallet<T> {
4031 fn expecting_response(
4032 origin: &Location,
4033 query_id: QueryId,
4034 querier: Option<&Location>,
4035 ) -> bool {
4036 match Queries::<T>::get(query_id) {
4037 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) => {
4038 Location::try_from(responder).map_or(false, |r| origin == &r) &&
4039 maybe_match_querier.map_or(true, |match_querier| {
4040 Location::try_from(match_querier).map_or(false, |match_querier| {
4041 querier.map_or(false, |q| q == &match_querier)
4042 })
4043 })
4044 },
4045 Some(QueryStatus::VersionNotifier { origin: r, .. }) => {
4046 Location::try_from(r).map_or(false, |r| origin == &r)
4047 },
4048 _ => false,
4049 }
4050 }
4051
4052 fn on_response(
4053 origin: &Location,
4054 query_id: QueryId,
4055 querier: Option<&Location>,
4056 response: Response,
4057 max_weight: Weight,
4058 _context: &XcmContext,
4059 ) -> Weight {
4060 let origin = origin.clone();
4061 match (response, Queries::<T>::get(query_id)) {
4062 (
4063 Response::Version(v),
4064 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
4065 ) => {
4066 let origin: Location = match expected_origin.try_into() {
4067 Ok(o) if o == origin => o,
4068 Ok(o) => {
4069 Self::deposit_event(Event::InvalidResponder {
4070 origin: origin.clone(),
4071 query_id,
4072 expected_location: Some(o),
4073 });
4074 return Weight::zero();
4075 },
4076 _ => {
4077 Self::deposit_event(Event::InvalidResponder {
4078 origin: origin.clone(),
4079 query_id,
4080 expected_location: None,
4081 });
4082 return Weight::zero();
4084 },
4085 };
4086 if !is_active {
4088 Queries::<T>::insert(
4089 query_id,
4090 QueryStatus::VersionNotifier {
4091 origin: origin.clone().into(),
4092 is_active: true,
4093 },
4094 );
4095 }
4096 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
4098 Self::deposit_event(Event::SupportedVersionChanged {
4099 location: origin,
4100 version: v,
4101 });
4102 Weight::zero()
4103 },
4104 (
4105 response,
4106 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
4107 ) => {
4108 if let Some(match_querier) = maybe_match_querier {
4109 let match_querier = match Location::try_from(match_querier) {
4110 Ok(mq) => mq,
4111 Err(_) => {
4112 Self::deposit_event(Event::InvalidQuerierVersion {
4113 origin: origin.clone(),
4114 query_id,
4115 });
4116 return Weight::zero();
4117 },
4118 };
4119 if querier.map_or(true, |q| q != &match_querier) {
4120 Self::deposit_event(Event::InvalidQuerier {
4121 origin: origin.clone(),
4122 query_id,
4123 expected_querier: match_querier,
4124 maybe_actual_querier: querier.cloned(),
4125 });
4126 return Weight::zero();
4127 }
4128 }
4129 let responder = match Location::try_from(responder) {
4130 Ok(r) => r,
4131 Err(_) => {
4132 Self::deposit_event(Event::InvalidResponderVersion {
4133 origin: origin.clone(),
4134 query_id,
4135 });
4136 return Weight::zero();
4137 },
4138 };
4139 if origin != responder {
4140 Self::deposit_event(Event::InvalidResponder {
4141 origin: origin.clone(),
4142 query_id,
4143 expected_location: Some(responder),
4144 });
4145 return Weight::zero();
4146 }
4147 match maybe_notify {
4148 Some((pallet_index, call_index)) => {
4149 let bare = (pallet_index, call_index, query_id, response);
4153 if let Ok(call) = bare.using_encoded(|mut bytes| {
4154 <T as Config>::RuntimeCall::decode(&mut bytes)
4155 }) {
4156 Queries::<T>::remove(query_id);
4157 let weight = call.get_dispatch_info().call_weight;
4158 if weight.any_gt(max_weight) {
4159 let e = Event::NotifyOverweight {
4160 query_id,
4161 pallet_index,
4162 call_index,
4163 actual_weight: weight,
4164 max_budgeted_weight: max_weight,
4165 };
4166 Self::deposit_event(e);
4167 return Weight::zero();
4168 }
4169 let dispatch_origin = Origin::Response(origin.clone()).into();
4170 match call.dispatch(dispatch_origin) {
4171 Ok(post_info) => {
4172 let e = Event::Notified { query_id, pallet_index, call_index };
4173 Self::deposit_event(e);
4174 post_info.actual_weight
4175 },
4176 Err(error_and_info) => {
4177 let e = Event::NotifyDispatchError {
4178 query_id,
4179 pallet_index,
4180 call_index,
4181 };
4182 Self::deposit_event(e);
4183 error_and_info.post_info.actual_weight
4186 },
4187 }
4188 .unwrap_or(weight)
4189 } else {
4190 let e =
4191 Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
4192 Self::deposit_event(e);
4193 Weight::zero()
4194 }
4195 },
4196 None => {
4197 let e = Event::ResponseReady { query_id, response: response.clone() };
4198 Self::deposit_event(e);
4199 let at = frame_system::Pallet::<T>::current_block_number();
4200 let response = response.into();
4201 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
4202 Weight::zero()
4203 },
4204 }
4205 },
4206 _ => {
4207 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
4208 Self::deposit_event(e);
4209 Weight::zero()
4210 },
4211 }
4212 }
4213}
4214
4215impl<T: Config> CheckSuspension for Pallet<T> {
4216 fn is_suspended<Call>(
4217 _origin: &Location,
4218 _instructions: &mut [Instruction<Call>],
4219 _max_weight: Weight,
4220 _properties: &mut Properties,
4221 ) -> bool {
4222 XcmExecutionSuspended::<T>::get()
4223 }
4224}
4225
4226impl<T: Config> RecordXcm for Pallet<T> {
4227 fn should_record() -> bool {
4228 ShouldRecordXcm::<T>::get()
4229 }
4230
4231 fn set_record_xcm(enabled: bool) {
4232 ShouldRecordXcm::<T>::put(enabled);
4233 }
4234
4235 fn recorded_xcm() -> Option<Xcm<()>> {
4236 RecordedXcm::<T>::get()
4237 }
4238
4239 fn record(xcm: Xcm<()>) {
4240 RecordedXcm::<T>::put(xcm);
4241 }
4242}
4243
4244pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4248where
4249 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4250{
4251 match o.into() {
4252 Ok(Origin::Xcm(location)) => Ok(location),
4253 _ => Err(BadOrigin),
4254 }
4255}
4256
4257pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4261where
4262 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4263{
4264 match o.into() {
4265 Ok(Origin::Response(location)) => Ok(location),
4266 _ => Err(BadOrigin),
4267 }
4268}
4269
4270pub struct AuthorizedAliasers<T>(PhantomData<T>);
4276impl<L: Into<VersionedLocation> + Clone, T: Config> ContainsPair<L, L> for AuthorizedAliasers<T> {
4277 fn contains(origin: &L, target: &L) -> bool {
4278 let origin: VersionedLocation = origin.clone().into();
4279 let target: VersionedLocation = target.clone().into();
4280 tracing::trace!(target: "xcm::pallet_xcm::AuthorizedAliasers::contains", ?origin, ?target);
4281 Pallet::<T>::is_authorized_alias(origin, target).unwrap_or(false)
4284 }
4285}
4286
4287pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4292impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
4293 for IsMajorityOfBody<Prefix, Body>
4294{
4295 fn contains(l: &Location) -> bool {
4296 let maybe_suffix = l.match_and_split(&Prefix::get());
4297 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
4298 }
4299}
4300
4301pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4305impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
4306 fn contains(l: &Location) -> bool {
4307 let maybe_suffix = l.match_and_split(&Prefix::get());
4308 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
4309 }
4310}
4311
4312pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
4315impl<
4316 O: OriginTrait + From<Origin>,
4317 F: Contains<L>,
4318 L: TryFrom<Location> + TryInto<Location> + Clone,
4319 > EnsureOrigin<O> for EnsureXcm<F, L>
4320where
4321 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4322{
4323 type Success = L;
4324
4325 fn try_origin(outer: O) -> Result<Self::Success, O> {
4326 match outer.caller().try_into() {
4327 Ok(Origin::Xcm(ref location)) => {
4328 if let Ok(location) = location.clone().try_into() {
4329 if F::contains(&location) {
4330 return Ok(location);
4331 }
4332 }
4333 },
4334 _ => (),
4335 }
4336
4337 Err(outer)
4338 }
4339
4340 #[cfg(feature = "runtime-benchmarks")]
4341 fn try_successful_origin() -> Result<O, ()> {
4342 Ok(O::from(Origin::Xcm(Here.into())))
4343 }
4344}
4345
4346pub struct EnsureResponse<F>(PhantomData<F>);
4349impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
4350where
4351 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4352{
4353 type Success = Location;
4354
4355 fn try_origin(outer: O) -> Result<Self::Success, O> {
4356 match outer.caller().try_into() {
4357 Ok(Origin::Response(responder)) => return Ok(responder.clone()),
4358 _ => (),
4359 }
4360
4361 Err(outer)
4362 }
4363
4364 #[cfg(feature = "runtime-benchmarks")]
4365 fn try_successful_origin() -> Result<O, ()> {
4366 Ok(O::from(Origin::Response(Here.into())))
4367 }
4368}
4369
4370pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
4373impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
4374 for XcmPassthrough<RuntimeOrigin>
4375{
4376 fn convert_origin(
4377 origin: impl Into<Location>,
4378 kind: OriginKind,
4379 ) -> Result<RuntimeOrigin, Location> {
4380 let origin = origin.into();
4381 match kind {
4382 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
4383 _ => Err(origin),
4384 }
4385 }
4386}