1#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "runtime-benchmarks")]
22pub mod benchmarking;
23#[cfg(test)]
24mod mock;
25#[cfg(test)]
26mod tests;
27mod transfer_assets_validation;
28
29pub mod migration;
30#[cfg(any(test, feature = "test-utils"))]
31pub mod xcm_helpers;
32
33extern crate alloc;
34
35use alloc::{boxed::Box, vec, vec::Vec};
36use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
37use core::{marker::PhantomData, result::Result};
38use frame_support::{
39 dispatch::{
40 DispatchErrorWithPostInfo, GetDispatchInfo, PostDispatchInfo, WithPostDispatchInfo,
41 },
42 pallet_prelude::*,
43 traits::{
44 Consideration, Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Footprint, Get,
45 LockableCurrency, OriginTrait, WithdrawReasons,
46 },
47 PalletId,
48};
49use frame_system::pallet_prelude::{BlockNumberFor, *};
50pub use pallet::*;
51use scale_info::TypeInfo;
52use sp_core::H256;
53use sp_runtime::{
54 traits::{
55 AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
56 Saturating, Zero,
57 },
58 Debug, Either, SaturatedConversion,
59};
60use xcm::{latest::QueryResponseInfo, prelude::*};
61use xcm_builder::{
62 ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
63 QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
64};
65use xcm_executor::{
66 traits::{
67 AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
68 DropAssets, EventEmitter, FeeManager, FeeReason, MatchesFungible, OnResponse, Properties,
69 QueryHandler, QueryResponseStatus, RecordXcm, TransactAsset, TransferType,
70 VersionChangeNotifier, WeightBounds, XcmAssetTransfers,
71 },
72 AssetsInHolding,
73};
74use xcm_runtime_apis::{
75 authorized_aliases::{Error as AuthorizedAliasersApiError, OriginAliaser},
76 dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
77 fees::Error as XcmPaymentApiError,
78 trusted_query::Error as TrustedQueryApiError,
79};
80
81mod errors;
82pub use errors::ExecutionError;
83
84#[cfg(any(feature = "try-runtime", test))]
85use sp_runtime::TryRuntimeError;
86
87pub trait WeightInfo {
88 fn send() -> Weight;
89 fn teleport_assets() -> Weight;
90 fn reserve_transfer_assets() -> Weight;
91 fn transfer_assets() -> Weight;
92 fn execute() -> Weight;
93 fn force_xcm_version() -> Weight;
94 fn force_default_xcm_version() -> Weight;
95 fn force_subscribe_version_notify() -> Weight;
96 fn force_unsubscribe_version_notify() -> Weight;
97 fn force_suspension() -> Weight;
98 fn migrate_supported_version() -> Weight;
99 fn migrate_version_notifiers() -> Weight;
100 fn already_notified_target() -> Weight;
101 fn notify_current_targets() -> Weight;
102 fn notify_target_migration_fail() -> Weight;
103 fn migrate_version_notify_targets() -> Weight;
104 fn migrate_and_notify_old_targets() -> Weight;
105 fn new_query() -> Weight;
106 fn take_response() -> Weight;
107 fn claim_assets() -> Weight;
108 fn add_authorized_alias() -> Weight;
109 fn remove_authorized_alias() -> Weight;
110
111 fn weigh_message() -> Weight;
112
113 fn weigh_message_by_size(n: u32) -> Weight {
125 let _ = n;
126 Self::weigh_message()
127 }
128 fn decode_xcm(n: u32) -> Weight {
134 let _ = n;
135 Self::weigh_message()
136 }
137 fn claim_assets_by_size(n: u32) -> Weight {
142 let _ = n;
143 Self::claim_assets()
144 }
145}
146
147pub struct TestWeightInfo;
149impl WeightInfo for TestWeightInfo {
150 fn send() -> Weight {
151 Weight::from_parts(100_000_000, 0)
152 }
153
154 fn teleport_assets() -> Weight {
155 Weight::from_parts(100_000_000, 0)
156 }
157
158 fn reserve_transfer_assets() -> Weight {
159 Weight::from_parts(100_000_000, 0)
160 }
161
162 fn transfer_assets() -> Weight {
163 Weight::from_parts(100_000_000, 0)
164 }
165
166 fn execute() -> Weight {
167 Weight::from_parts(100_000_000, 0)
168 }
169
170 fn force_xcm_version() -> Weight {
171 Weight::from_parts(100_000_000, 0)
172 }
173
174 fn force_default_xcm_version() -> Weight {
175 Weight::from_parts(100_000_000, 0)
176 }
177
178 fn force_subscribe_version_notify() -> Weight {
179 Weight::from_parts(100_000_000, 0)
180 }
181
182 fn force_unsubscribe_version_notify() -> Weight {
183 Weight::from_parts(100_000_000, 0)
184 }
185
186 fn force_suspension() -> Weight {
187 Weight::from_parts(100_000_000, 0)
188 }
189
190 fn migrate_supported_version() -> Weight {
191 Weight::from_parts(100_000_000, 0)
192 }
193
194 fn migrate_version_notifiers() -> Weight {
195 Weight::from_parts(100_000_000, 0)
196 }
197
198 fn already_notified_target() -> Weight {
199 Weight::from_parts(100_000_000, 0)
200 }
201
202 fn notify_current_targets() -> Weight {
203 Weight::from_parts(100_000_000, 0)
204 }
205
206 fn notify_target_migration_fail() -> Weight {
207 Weight::from_parts(100_000_000, 0)
208 }
209
210 fn migrate_version_notify_targets() -> Weight {
211 Weight::from_parts(100_000_000, 0)
212 }
213
214 fn migrate_and_notify_old_targets() -> Weight {
215 Weight::from_parts(100_000_000, 0)
216 }
217
218 fn new_query() -> Weight {
219 Weight::from_parts(100_000_000, 0)
220 }
221
222 fn take_response() -> Weight {
223 Weight::from_parts(100_000_000, 0)
224 }
225
226 fn claim_assets() -> Weight {
227 Weight::from_parts(100_000_000, 0)
228 }
229
230 fn add_authorized_alias() -> Weight {
231 Weight::from_parts(100_000, 0)
232 }
233
234 fn remove_authorized_alias() -> Weight {
235 Weight::from_parts(100_000, 0)
236 }
237
238 fn weigh_message() -> Weight {
239 Weight::from_parts(100_000, 0)
240 }
241
242 fn weigh_message_by_size(n: u32) -> Weight {
243 Weight::from_parts(100_000, 0)
244 .saturating_add(Weight::from_parts(100_000, 0).saturating_mul(n.into()))
245 }
246
247 fn decode_xcm(n: u32) -> Weight {
248 Weight::from_parts(100_000, 0)
249 .saturating_add(Weight::from_parts(20_000, 0).saturating_mul(n.into()))
250 }
251
252 fn claim_assets_by_size(n: u32) -> Weight {
253 Weight::from_parts(100_000_000, 0)
254 .saturating_add(Weight::from_parts(10_000_000, 0).saturating_mul(n.into()))
255 }
256}
257
258#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
259pub struct AuthorizedAliasesEntry<Ticket, MAX: Get<u32>> {
260 pub aliasers: BoundedVec<OriginAliaser, MAX>,
261 pub ticket: Ticket,
262}
263
264pub fn aliasers_footprint(aliasers_count: usize) -> Footprint {
265 Footprint::from_parts(aliasers_count, OriginAliaser::max_encoded_len())
266}
267
268#[frame_support::pallet]
269pub mod pallet {
270 use super::*;
271 use frame_support::{
272 dispatch::{GetDispatchInfo, PostDispatchInfo},
273 parameter_types,
274 };
275 use frame_system::Config as SysConfig;
276 use sp_runtime::traits::Dispatchable;
277 use xcm_executor::traits::{MatchesFungible, WeightBounds};
278
279 parameter_types! {
280 pub const CurrentXcmVersion: u32 = XCM_VERSION;
283
284 #[derive(Debug, TypeInfo)]
285 pub const MaxAuthorizedAliases: u32 = 10;
287 }
288
289 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
290
291 #[pallet::pallet]
292 #[pallet::storage_version(STORAGE_VERSION)]
293 #[pallet::without_storage_info]
294 pub struct Pallet<T>(_);
295
296 pub type BalanceOf<T> =
297 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
298 pub type TicketOf<T> = <T as Config>::AuthorizedAliasConsideration;
299
300 #[pallet::config]
301 pub trait Config: frame_system::Config {
303 #[allow(deprecated)]
305 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
306
307 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
310
311 type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
313
314 type AuthorizedAliasConsideration: Consideration<Self::AccountId, Footprint>;
316
317 type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
320
321 type XcmRouter: SendXcm;
323
324 type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
328
329 type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
331
332 type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers + FeeManager;
334
335 type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
337
338 type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
341
342 type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
344
345 #[pallet::constant]
347 type UniversalLocation: Get<InteriorLocation>;
348
349 type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
351
352 type RuntimeCall: Parameter
354 + GetDispatchInfo
355 + Dispatchable<
356 RuntimeOrigin = <Self as Config>::RuntimeOrigin,
357 PostInfo = PostDispatchInfo,
358 >;
359
360 const VERSION_DISCOVERY_QUEUE_SIZE: u32;
361
362 #[pallet::constant]
365 type AdvertisedXcmVersion: Get<XcmVersion>;
366
367 type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
369
370 type TrustedLockers: ContainsPair<Location, Asset>;
373
374 type SovereignAccountOf: ConvertLocation<Self::AccountId>;
376
377 #[pallet::constant]
379 type MaxLockers: Get<u32>;
380
381 #[pallet::constant]
383 type MaxRemoteLockConsumers: Get<u32>;
384
385 type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
387
388 type WeightInfo: WeightInfo;
390 }
391
392 impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
393 fn execute() -> Weight {
394 T::WeightInfo::execute()
395 }
396 }
397
398 impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
399 type WeightInfo = Self;
400 fn execute(
401 origin: OriginFor<T>,
402 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
403 max_weight: Weight,
404 ) -> Result<Weight, DispatchErrorWithPostInfo> {
405 tracing::trace!(target: "xcm::pallet_xcm::execute", ?message, ?max_weight);
406 let outcome = (|| {
407 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
408 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
409 let message = (*message).try_into().map_err(|()| {
410 tracing::debug!(
411 target: "xcm::pallet_xcm::execute", id=?hash,
412 "Failed to convert VersionedXcm to Xcm",
413 );
414 Error::<T>::BadVersion
415 })?;
416 let value = (origin_location, message);
417 ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
418 let (origin_location, message) = value;
419 Ok(T::XcmExecutor::prepare_and_execute(
420 origin_location,
421 message,
422 &mut hash,
423 max_weight,
424 max_weight,
425 ))
426 })()
427 .map_err(|e: DispatchError| {
428 tracing::debug!(
429 target: "xcm::pallet_xcm::execute", error=?e,
430 "Failed XCM pre-execution validation or filter",
431 );
432 e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
433 })?;
434
435 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
436 let weight_used = outcome.weight_used();
437 outcome.ensure_complete().map_err(|error| {
438 tracing::error!(target: "xcm::pallet_xcm::execute", ?error, "XCM execution failed with error");
439 Error::<T>::LocalExecutionIncompleteWithError {
440 index: error.index,
441 error: error.error.into(),
442 }
443 .with_weight(
444 weight_used.saturating_add(
445 <Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
446 ),
447 )
448 })?;
449 Ok(weight_used)
450 }
451 }
452
453 impl<T: Config> SendControllerWeightInfo for Pallet<T> {
454 fn send() -> Weight {
455 T::WeightInfo::send()
456 }
457 }
458
459 impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
460 type WeightInfo = Self;
461 fn send(
462 origin: OriginFor<T>,
463 dest: Box<VersionedLocation>,
464 message: Box<VersionedXcm<()>>,
465 ) -> Result<XcmHash, DispatchError> {
466 let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
467 let interior: Junctions = origin_location.clone().try_into().map_err(|_| {
468 tracing::debug!(
469 target: "xcm::pallet_xcm::send",
470 "Failed to convert origin_location to interior Junctions",
471 );
472 Error::<T>::InvalidOrigin
473 })?;
474 let dest = Location::try_from(*dest).map_err(|()| {
475 tracing::debug!(
476 target: "xcm::pallet_xcm::send",
477 "Failed to convert destination VersionedLocation to Location",
478 );
479 Error::<T>::BadVersion
480 })?;
481 let message: Xcm<()> = (*message).try_into().map_err(|()| {
482 tracing::debug!(
483 target: "xcm::pallet_xcm::send",
484 "Failed to convert VersionedXcm message to Xcm",
485 );
486 Error::<T>::BadVersion
487 })?;
488
489 let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
490 .map_err(|error| {
491 tracing::error!(target: "xcm::pallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
492 Error::<T>::from(error)
493 })?;
494 let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
495 Self::deposit_event(e);
496 Ok(message_id)
497 }
498 }
499
500 impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
501 fn query() -> Weight {
502 T::WeightInfo::new_query()
503 }
504 fn take_response() -> Weight {
505 T::WeightInfo::take_response()
506 }
507 }
508
509 impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
510 type WeightInfo = Self;
511
512 fn query(
513 origin: OriginFor<T>,
514 timeout: BlockNumberFor<T>,
515 match_querier: VersionedLocation,
516 ) -> Result<QueryId, DispatchError> {
517 let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
518 let query_id = <Self as QueryHandler>::new_query(
519 responder,
520 timeout,
521 Location::try_from(match_querier).map_err(|_| {
522 tracing::debug!(
523 target: "xcm::pallet_xcm::query",
524 "Failed to convert VersionedLocation for match_querier",
525 );
526 Into::<DispatchError>::into(Error::<T>::BadVersion)
527 })?,
528 );
529
530 Ok(query_id)
531 }
532 }
533
534 impl<T: Config> EventEmitter for Pallet<T> {
535 fn emit_sent_event(
536 origin: Location,
537 destination: Location,
538 message: Option<Xcm<()>>,
539 message_id: XcmHash,
540 ) {
541 Self::deposit_event(Event::Sent {
542 origin,
543 destination,
544 message: message.unwrap_or_default(),
545 message_id,
546 });
547 }
548
549 fn emit_send_failure_event(
550 origin: Location,
551 destination: Location,
552 error: SendError,
553 message_id: XcmHash,
554 ) {
555 Self::deposit_event(Event::SendFailed { origin, destination, error, message_id });
556 }
557
558 fn emit_process_failure_event(origin: Location, error: XcmError, message_id: XcmHash) {
559 Self::deposit_event(Event::ProcessXcmError { origin, error, message_id });
560 }
561 }
562
563 #[pallet::event]
564 #[pallet::generate_deposit(pub(super) fn deposit_event)]
565 pub enum Event<T: Config> {
566 Attempted { outcome: xcm::latest::Outcome },
568 Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
570 SendFailed {
572 origin: Location,
573 destination: Location,
574 error: SendError,
575 message_id: XcmHash,
576 },
577 ProcessXcmError { origin: Location, error: XcmError, message_id: XcmHash },
579 UnexpectedResponse { origin: Location, query_id: QueryId },
583 ResponseReady { query_id: QueryId, response: Response },
586 Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
589 NotifyOverweight {
593 query_id: QueryId,
594 pallet_index: u8,
595 call_index: u8,
596 actual_weight: Weight,
597 max_budgeted_weight: Weight,
598 },
599 NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
602 NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
606 InvalidResponder {
610 origin: Location,
611 query_id: QueryId,
612 expected_location: Option<Location>,
613 },
614 InvalidResponderVersion { origin: Location, query_id: QueryId },
622 ResponseTaken { query_id: QueryId },
624 AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
626 VersionChangeNotified {
630 destination: Location,
631 result: XcmVersion,
632 cost: Assets,
633 message_id: XcmHash,
634 },
635 SupportedVersionChanged { location: Location, version: XcmVersion },
638 NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
641 NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
644 InvalidQuerierVersion { origin: Location, query_id: QueryId },
652 InvalidQuerier {
656 origin: Location,
657 query_id: QueryId,
658 expected_querier: Location,
659 maybe_actual_querier: Option<Location>,
660 },
661 VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
664 VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
666 VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
669 FeesPaid { paying: Location, fees: Assets },
671 AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
673 VersionMigrationFinished { version: XcmVersion },
675 AliasAuthorized { aliaser: Location, target: Location, expiry: Option<u64> },
678 AliasAuthorizationRemoved { aliaser: Location, target: Location },
680 AliasesAuthorizationsRemoved { target: Location },
682 }
683
684 #[pallet::origin]
685 #[derive(
686 PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen,
687 )]
688 pub enum Origin {
689 Xcm(Location),
691 Response(Location),
693 }
694 impl From<Location> for Origin {
695 fn from(location: Location) -> Origin {
696 Origin::Xcm(location)
697 }
698 }
699
700 #[pallet::composite_enum]
702 pub enum HoldReason {
703 AuthorizeAlias,
705 }
706
707 #[pallet::error]
708 pub enum Error<T> {
709 Unreachable,
712 SendFailure,
715 Filtered,
717 UnweighableMessage,
719 DestinationNotInvertible,
721 Empty,
723 CannotReanchor,
725 TooManyAssets,
727 InvalidOrigin,
729 BadVersion,
731 BadLocation,
734 NoSubscription,
736 AlreadySubscribed,
738 CannotCheckOutTeleport,
740 LowBalance,
742 TooManyLocks,
744 AccountNotSovereign,
746 FeesNotMet,
748 LockNotFound,
750 InUse,
752 #[codec(index = 21)]
754 InvalidAssetUnknownReserve,
755 #[codec(index = 22)]
757 InvalidAssetUnsupportedReserve,
758 #[codec(index = 23)]
760 TooManyReserves,
761 #[deprecated(since = "20.0.0", note = "Use `LocalExecutionIncompleteWithError` instead")]
763 #[codec(index = 24)]
764 LocalExecutionIncomplete,
765 #[codec(index = 25)]
767 TooManyAuthorizedAliases,
768 #[codec(index = 26)]
770 ExpiresInPast,
771 #[codec(index = 27)]
773 AliasNotFound,
774 #[codec(index = 28)]
777 LocalExecutionIncompleteWithError { index: InstructionIndex, error: ExecutionError },
778 }
779
780 impl<T: Config> From<SendError> for Error<T> {
781 fn from(e: SendError) -> Self {
782 match e {
783 SendError::Fees => Error::<T>::FeesNotMet,
784 SendError::NotApplicable => Error::<T>::Unreachable,
785 _ => Error::<T>::SendFailure,
786 }
787 }
788 }
789
790 impl<T: Config> From<AssetTransferError> for Error<T> {
791 fn from(e: AssetTransferError) -> Self {
792 match e {
793 AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
794 }
795 }
796 }
797
798 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
800 pub enum QueryStatus<BlockNumber> {
801 Pending {
803 responder: VersionedLocation,
806 maybe_match_querier: Option<VersionedLocation>,
809 maybe_notify: Option<(u8, u8)>,
810 timeout: BlockNumber,
811 },
812 VersionNotifier { origin: VersionedLocation, is_active: bool },
814 Ready { response: VersionedResponse, at: BlockNumber },
816 }
817
818 #[derive(Copy, Clone)]
819 pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
820 impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
821 impl<'a> Encode for LatestVersionedLocation<'a> {
822 fn encode(&self) -> Vec<u8> {
823 let mut r = VersionedLocation::from(Location::default()).encode();
824 r.truncate(1);
825 self.0.using_encoded(|d| r.extend_from_slice(d));
826 r
827 }
828 }
829
830 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
831 pub enum VersionMigrationStage {
832 MigrateSupportedVersion,
833 MigrateVersionNotifiers,
834 NotifyCurrentTargets(Option<Vec<u8>>),
835 MigrateAndNotifyOldTargets,
836 }
837
838 impl Default for VersionMigrationStage {
839 fn default() -> Self {
840 Self::MigrateSupportedVersion
841 }
842 }
843
844 #[pallet::storage]
846 pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
847
848 #[pallet::storage]
850 pub(super) type Queries<T: Config> =
851 StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
852
853 #[pallet::storage]
858 pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
859
860 #[pallet::storage]
863 #[pallet::whitelist_storage]
864 pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
865
866 #[pallet::storage]
868 pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
869 _,
870 Twox64Concat,
871 XcmVersion,
872 Blake2_128Concat,
873 VersionedLocation,
874 XcmVersion,
875 OptionQuery,
876 >;
877
878 #[pallet::storage]
880 pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
881 _,
882 Twox64Concat,
883 XcmVersion,
884 Blake2_128Concat,
885 VersionedLocation,
886 QueryId,
887 OptionQuery,
888 >;
889
890 #[pallet::storage]
893 pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
894 _,
895 Twox64Concat,
896 XcmVersion,
897 Blake2_128Concat,
898 VersionedLocation,
899 (QueryId, Weight, XcmVersion),
900 OptionQuery,
901 >;
902
903 pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
904 impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
905 fn get() -> u32 {
906 T::VERSION_DISCOVERY_QUEUE_SIZE
907 }
908 }
909
910 #[pallet::storage]
914 #[pallet::whitelist_storage]
915 pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
916 _,
917 BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
918 ValueQuery,
919 >;
920
921 #[pallet::storage]
923 pub(super) type CurrentMigration<T: Config> =
924 StorageValue<_, VersionMigrationStage, OptionQuery>;
925
926 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
927 #[scale_info(skip_type_params(MaxConsumers))]
928 pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
929 pub amount: u128,
931 pub owner: VersionedLocation,
933 pub locker: VersionedLocation,
935 pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
939 }
940
941 impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
942 pub fn amount_held(&self) -> Option<u128> {
945 self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
946 }
947 }
948
949 #[pallet::storage]
951 pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
952 _,
953 (
954 NMapKey<Twox64Concat, XcmVersion>,
955 NMapKey<Blake2_128Concat, T::AccountId>,
956 NMapKey<Blake2_128Concat, VersionedAssetId>,
957 ),
958 RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
959 OptionQuery,
960 >;
961
962 #[pallet::storage]
964 pub(super) type LockedFungibles<T: Config> = StorageMap<
965 _,
966 Blake2_128Concat,
967 T::AccountId,
968 BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
969 OptionQuery,
970 >;
971
972 #[pallet::storage]
974 pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
975
976 #[pallet::storage]
984 pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
985
986 #[pallet::storage]
993 pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
994
995 #[pallet::storage]
999 pub(super) type AuthorizedAliases<T: Config> = StorageMap<
1000 _,
1001 Blake2_128Concat,
1002 VersionedLocation,
1003 AuthorizedAliasesEntry<TicketOf<T>, MaxAuthorizedAliases>,
1004 OptionQuery,
1005 >;
1006
1007 #[pallet::genesis_config]
1008 pub struct GenesisConfig<T: Config> {
1009 #[serde(skip)]
1010 pub _config: core::marker::PhantomData<T>,
1011 pub safe_xcm_version: Option<XcmVersion>,
1013 pub supported_version: Vec<(Location, XcmVersion)>,
1015 }
1016
1017 impl<T: Config> Default for GenesisConfig<T> {
1018 fn default() -> Self {
1019 Self {
1020 _config: Default::default(),
1021 safe_xcm_version: Some(XCM_VERSION),
1022 supported_version: Vec::new(),
1023 }
1024 }
1025 }
1026
1027 #[pallet::genesis_build]
1028 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1029 fn build(&self) {
1030 SafeXcmVersion::<T>::set(self.safe_xcm_version);
1031 self.supported_version.iter().for_each(|(location, version)| {
1033 SupportedVersion::<T>::insert(
1034 XCM_VERSION,
1035 LatestVersionedLocation(location),
1036 version,
1037 );
1038 });
1039 }
1040 }
1041
1042 #[pallet::hooks]
1043 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1044 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
1045 let mut weight_used = Weight::zero();
1046 if let Some(migration) = CurrentMigration::<T>::get() {
1047 let max_weight = T::BlockWeights::get().max_block / 10;
1049 let (w, maybe_migration) = Self::lazy_migration(migration, max_weight);
1050 if maybe_migration.is_none() {
1051 Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
1052 }
1053 CurrentMigration::<T>::set(maybe_migration);
1054 weight_used.saturating_accrue(w);
1055 }
1056
1057 let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
1060 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1062 q.sort_by_key(|i| i.1);
1063 while let Some((versioned_dest, _)) = q.pop() {
1064 if let Ok(dest) = Location::try_from(versioned_dest) {
1065 if Self::request_version_notify(dest).is_ok() {
1066 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1068 break;
1069 }
1070 }
1071 }
1072 if let Ok(q) = BoundedVec::try_from(q) {
1075 VersionDiscoveryQueue::<T>::put(q);
1076 }
1077 weight_used
1078 }
1079
1080 #[cfg(feature = "try-runtime")]
1081 fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
1082 Self::do_try_state()
1083 }
1084 }
1085
1086 pub mod migrations {
1087 use super::*;
1088 use frame_support::traits::{PalletInfoAccess, StorageVersion};
1089
1090 #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
1091 enum QueryStatusV0<BlockNumber> {
1092 Pending {
1093 responder: VersionedLocation,
1094 maybe_notify: Option<(u8, u8)>,
1095 timeout: BlockNumber,
1096 },
1097 VersionNotifier {
1098 origin: VersionedLocation,
1099 is_active: bool,
1100 },
1101 Ready {
1102 response: VersionedResponse,
1103 at: BlockNumber,
1104 },
1105 }
1106 impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
1107 fn from(old: QueryStatusV0<B>) -> Self {
1108 use QueryStatusV0::*;
1109 match old {
1110 Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
1111 responder,
1112 maybe_notify,
1113 timeout,
1114 maybe_match_querier: Some(Location::here().into()),
1115 },
1116 VersionNotifier { origin, is_active } => {
1117 QueryStatus::VersionNotifier { origin, is_active }
1118 },
1119 Ready { response, at } => QueryStatus::Ready { response, at },
1120 }
1121 }
1122 }
1123
1124 pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
1125 ) -> frame_support::weights::Weight {
1126 let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
1127 tracing::info!(
1128 target: "runtime::xcm",
1129 ?on_chain_storage_version,
1130 "Running migration storage v1 for xcm with storage version",
1131 );
1132
1133 if on_chain_storage_version < 1 {
1134 let mut count = 0;
1135 Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
1136 count += 1;
1137 Some(value.into())
1138 });
1139 StorageVersion::new(1).put::<P>();
1140 tracing::info!(
1141 target: "runtime::xcm",
1142 ?on_chain_storage_version,
1143 "Running migration storage v1 for xcm with storage version was complete",
1144 );
1145 T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
1147 } else {
1148 tracing::warn!(
1149 target: "runtime::xcm",
1150 ?on_chain_storage_version,
1151 "Attempted to apply migration to v1 but failed because storage version is",
1152 );
1153 T::DbWeight::get().reads(1)
1154 }
1155 }
1156 }
1157
1158 #[pallet::call(weight(<T as Config>::WeightInfo))]
1159 impl<T: Config> Pallet<T> {
1160 #[pallet::call_index(0)]
1161 pub fn send(
1162 origin: OriginFor<T>,
1163 dest: Box<VersionedLocation>,
1164 message: Box<VersionedXcm<()>>,
1165 ) -> DispatchResult {
1166 <Self as SendController<_>>::send(origin, dest, message)?;
1167 Ok(())
1168 }
1169
1170 #[pallet::call_index(1)]
1189 #[allow(deprecated)]
1190 #[deprecated(
1191 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
1192 )]
1193 pub fn teleport_assets(
1194 origin: OriginFor<T>,
1195 dest: Box<VersionedLocation>,
1196 beneficiary: Box<VersionedLocation>,
1197 assets: Box<VersionedAssets>,
1198 fee_asset_item: u32,
1199 ) -> DispatchResult {
1200 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
1201 }
1202
1203 #[pallet::call_index(2)]
1234 #[allow(deprecated)]
1235 #[deprecated(
1236 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1237 )]
1238 pub fn reserve_transfer_assets(
1239 origin: OriginFor<T>,
1240 dest: Box<VersionedLocation>,
1241 beneficiary: Box<VersionedLocation>,
1242 assets: Box<VersionedAssets>,
1243 fee_asset_item: u32,
1244 ) -> DispatchResult {
1245 Self::do_reserve_transfer_assets(
1246 origin,
1247 dest,
1248 beneficiary,
1249 assets,
1250 fee_asset_item,
1251 Unlimited,
1252 )
1253 }
1254
1255 #[pallet::call_index(3)]
1264 #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1265 pub fn execute(
1266 origin: OriginFor<T>,
1267 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1268 max_weight: Weight,
1269 ) -> DispatchResultWithPostInfo {
1270 let weight_used =
1271 <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1272 Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1273 }
1274
1275 #[pallet::call_index(4)]
1282 pub fn force_xcm_version(
1283 origin: OriginFor<T>,
1284 location: Box<Location>,
1285 version: XcmVersion,
1286 ) -> DispatchResult {
1287 T::AdminOrigin::ensure_origin(origin)?;
1288 let location = *location;
1289 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1290 Self::deposit_event(Event::SupportedVersionChanged { location, version });
1291 Ok(())
1292 }
1293
1294 #[pallet::call_index(5)]
1300 pub fn force_default_xcm_version(
1301 origin: OriginFor<T>,
1302 maybe_xcm_version: Option<XcmVersion>,
1303 ) -> DispatchResult {
1304 T::AdminOrigin::ensure_origin(origin)?;
1305 SafeXcmVersion::<T>::set(maybe_xcm_version);
1306 Ok(())
1307 }
1308
1309 #[pallet::call_index(6)]
1314 pub fn force_subscribe_version_notify(
1315 origin: OriginFor<T>,
1316 location: Box<VersionedLocation>,
1317 ) -> DispatchResult {
1318 T::AdminOrigin::ensure_origin(origin)?;
1319 let location: Location = (*location).try_into().map_err(|()| {
1320 tracing::debug!(
1321 target: "xcm::pallet_xcm::force_subscribe_version_notify",
1322 "Failed to convert VersionedLocation for subscription target"
1323 );
1324 Error::<T>::BadLocation
1325 })?;
1326 Self::request_version_notify(location).map_err(|e| {
1327 tracing::debug!(
1328 target: "xcm::pallet_xcm::force_subscribe_version_notify", error=?e,
1329 "Failed to subscribe for version notifications for location"
1330 );
1331 match e {
1332 XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1333 _ => Error::<T>::InvalidOrigin,
1334 }
1335 .into()
1336 })
1337 }
1338
1339 #[pallet::call_index(7)]
1346 pub fn force_unsubscribe_version_notify(
1347 origin: OriginFor<T>,
1348 location: Box<VersionedLocation>,
1349 ) -> DispatchResult {
1350 T::AdminOrigin::ensure_origin(origin)?;
1351 let location: Location = (*location).try_into().map_err(|()| {
1352 tracing::debug!(
1353 target: "xcm::pallet_xcm::force_unsubscribe_version_notify",
1354 "Failed to convert VersionedLocation for unsubscription target"
1355 );
1356 Error::<T>::BadLocation
1357 })?;
1358 Self::unrequest_version_notify(location).map_err(|e| {
1359 tracing::debug!(
1360 target: "xcm::pallet_xcm::force_unsubscribe_version_notify", error=?e,
1361 "Failed to unsubscribe from version notifications for location"
1362 );
1363 match e {
1364 XcmError::InvalidLocation => Error::<T>::NoSubscription,
1365 _ => Error::<T>::InvalidOrigin,
1366 }
1367 .into()
1368 })
1369 }
1370
1371 #[pallet::call_index(8)]
1402 #[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
1403 pub fn limited_reserve_transfer_assets(
1404 origin: OriginFor<T>,
1405 dest: Box<VersionedLocation>,
1406 beneficiary: Box<VersionedLocation>,
1407 assets: Box<VersionedAssets>,
1408 fee_asset_item: u32,
1409 weight_limit: WeightLimit,
1410 ) -> DispatchResult {
1411 Self::do_reserve_transfer_assets(
1412 origin,
1413 dest,
1414 beneficiary,
1415 assets,
1416 fee_asset_item,
1417 weight_limit,
1418 )
1419 }
1420
1421 #[pallet::call_index(9)]
1440 #[pallet::weight(T::WeightInfo::teleport_assets())]
1441 pub fn limited_teleport_assets(
1442 origin: OriginFor<T>,
1443 dest: Box<VersionedLocation>,
1444 beneficiary: Box<VersionedLocation>,
1445 assets: Box<VersionedAssets>,
1446 fee_asset_item: u32,
1447 weight_limit: WeightLimit,
1448 ) -> DispatchResult {
1449 Self::do_teleport_assets(
1450 origin,
1451 dest,
1452 beneficiary,
1453 assets,
1454 fee_asset_item,
1455 weight_limit,
1456 )
1457 }
1458
1459 #[pallet::call_index(10)]
1464 pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1465 T::AdminOrigin::ensure_origin(origin)?;
1466 XcmExecutionSuspended::<T>::set(suspended);
1467 Ok(())
1468 }
1469
1470 #[pallet::call_index(11)]
1504 pub fn transfer_assets(
1505 origin: OriginFor<T>,
1506 dest: Box<VersionedLocation>,
1507 beneficiary: Box<VersionedLocation>,
1508 assets: Box<VersionedAssets>,
1509 fee_asset_item: u32,
1510 weight_limit: WeightLimit,
1511 ) -> DispatchResult {
1512 let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1513 let dest = (*dest).try_into().map_err(|()| {
1514 tracing::debug!(
1515 target: "xcm::pallet_xcm::transfer_assets",
1516 "Failed to convert destination VersionedLocation",
1517 );
1518 Error::<T>::BadVersion
1519 })?;
1520 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1521 tracing::debug!(
1522 target: "xcm::pallet_xcm::transfer_assets",
1523 "Failed to convert beneficiary VersionedLocation",
1524 );
1525 Error::<T>::BadVersion
1526 })?;
1527 let assets: Assets = (*assets).try_into().map_err(|()| {
1528 tracing::debug!(
1529 target: "xcm::pallet_xcm::transfer_assets",
1530 "Failed to convert VersionedAssets",
1531 );
1532 Error::<T>::BadVersion
1533 })?;
1534 tracing::debug!(
1535 target: "xcm::pallet_xcm::transfer_assets",
1536 ?origin, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
1537 );
1538
1539 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1540 let assets = assets.into_inner();
1541 let fee_asset_item = fee_asset_item as usize;
1542 let (fees_transfer_type, assets_transfer_type) =
1544 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1545
1546 Self::ensure_network_asset_reserve_transfer_allowed(
1550 &assets,
1551 fee_asset_item,
1552 &assets_transfer_type,
1553 &fees_transfer_type,
1554 )?;
1555
1556 Self::do_transfer_assets(
1557 origin,
1558 dest,
1559 Either::Left(beneficiary),
1560 assets,
1561 assets_transfer_type,
1562 fee_asset_item,
1563 fees_transfer_type,
1564 weight_limit,
1565 )
1566 }
1567
1568 #[pallet::call_index(12)]
1577 #[pallet::weight(T::WeightInfo::claim_assets_by_size(assets.len() as u32))]
1578 pub fn claim_assets(
1579 origin: OriginFor<T>,
1580 assets: Box<VersionedAssets>,
1581 beneficiary: Box<VersionedLocation>,
1582 ) -> DispatchResult {
1583 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1584 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?origin_location, ?assets, ?beneficiary);
1585 let assets_version = assets.identify_version();
1587 let assets: Assets = (*assets).try_into().map_err(|()| {
1588 tracing::debug!(
1589 target: "xcm::pallet_xcm::claim_assets",
1590 "Failed to convert input VersionedAssets",
1591 );
1592 Error::<T>::BadVersion
1593 })?;
1594 let number_of_assets = assets.len() as u32;
1595 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1596 tracing::debug!(
1597 target: "xcm::pallet_xcm::claim_assets",
1598 "Failed to convert beneficiary VersionedLocation",
1599 );
1600 Error::<T>::BadVersion
1601 })?;
1602 let ticket: Location = GeneralIndex(assets_version as u128).into();
1603 let mut message = Xcm(vec![
1604 ClaimAsset { assets, ticket },
1605 DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1606 ]);
1607 let weight = T::Weigher::weight(&mut message, Weight::MAX).map_err(|error| {
1608 tracing::debug!(target: "xcm::pallet_xcm::claim_assets", ?error, "Failed to calculate weight");
1609 Error::<T>::UnweighableMessage
1610 })?;
1611 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
1612 let outcome = T::XcmExecutor::prepare_and_execute(
1613 origin_location,
1614 message,
1615 &mut hash,
1616 weight,
1617 weight,
1618 );
1619 outcome.ensure_complete().map_err(|error| {
1620 tracing::error!(target: "xcm::pallet_xcm::claim_assets", ?error, "XCM execution failed with error");
1621 Error::<T>::LocalExecutionIncompleteWithError { index: error.index, error: error.error.into()}
1622 })?;
1623 Ok(())
1624 }
1625
1626 #[pallet::call_index(13)]
1675 #[pallet::weight(T::WeightInfo::transfer_assets())]
1676 pub fn transfer_assets_using_type_and_then(
1677 origin: OriginFor<T>,
1678 dest: Box<VersionedLocation>,
1679 assets: Box<VersionedAssets>,
1680 assets_transfer_type: Box<TransferType>,
1681 remote_fees_id: Box<VersionedAssetId>,
1682 fees_transfer_type: Box<TransferType>,
1683 custom_xcm_on_dest: Box<VersionedXcm<()>>,
1684 weight_limit: WeightLimit,
1685 ) -> DispatchResult {
1686 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1687 let dest: Location = (*dest).try_into().map_err(|()| {
1688 tracing::debug!(
1689 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1690 "Failed to convert destination VersionedLocation",
1691 );
1692 Error::<T>::BadVersion
1693 })?;
1694 let assets: Assets = (*assets).try_into().map_err(|()| {
1695 tracing::debug!(
1696 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1697 "Failed to convert VersionedAssets",
1698 );
1699 Error::<T>::BadVersion
1700 })?;
1701 let fees_id: AssetId = (*remote_fees_id).try_into().map_err(|()| {
1702 tracing::debug!(
1703 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1704 "Failed to convert remote_fees_id VersionedAssetId",
1705 );
1706 Error::<T>::BadVersion
1707 })?;
1708 let remote_xcm: Xcm<()> = (*custom_xcm_on_dest).try_into().map_err(|()| {
1709 tracing::debug!(
1710 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1711 "Failed to convert custom_xcm_on_dest VersionedXcm",
1712 );
1713 Error::<T>::BadVersion
1714 })?;
1715 tracing::debug!(
1716 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1717 ?origin_location, ?dest, ?assets, ?assets_transfer_type, ?fees_id, ?fees_transfer_type,
1718 ?remote_xcm, ?weight_limit,
1719 );
1720
1721 let assets = assets.into_inner();
1722 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1723
1724 let fee_asset_index =
1725 assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
1726 Self::do_transfer_assets(
1727 origin_location,
1728 dest,
1729 Either::Right(remote_xcm),
1730 assets,
1731 *assets_transfer_type,
1732 fee_asset_index,
1733 *fees_transfer_type,
1734 weight_limit,
1735 )
1736 }
1737
1738 #[pallet::call_index(14)]
1750 pub fn add_authorized_alias(
1751 origin: OriginFor<T>,
1752 aliaser: Box<VersionedLocation>,
1753 expires: Option<u64>,
1754 ) -> DispatchResult {
1755 let signed_origin = ensure_signed(origin.clone())?;
1756 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1757 let new_aliaser: Location = (*aliaser).try_into().map_err(|()| {
1758 tracing::debug!(
1759 target: "xcm::pallet_xcm::add_authorized_alias",
1760 "Failed to convert aliaser VersionedLocation",
1761 );
1762 Error::<T>::BadVersion
1763 })?;
1764 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1765 let origin_location = match origin_location.unpack() {
1767 (0, [AccountId32 { network: _, id }]) => {
1768 Location::new(0, [AccountId32 { network: None, id: *id }])
1769 },
1770 _ => return Err(Error::<T>::InvalidOrigin.into()),
1771 };
1772 tracing::debug!(target: "xcm::pallet_xcm::add_authorized_alias", ?origin_location, ?new_aliaser, ?expires);
1773 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1774 if let Some(expiry) = expires {
1775 ensure!(
1776 expiry >
1777 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>(),
1778 Error::<T>::ExpiresInPast
1779 );
1780 }
1781 let versioned_origin = VersionedLocation::from(origin_location.clone());
1782 let versioned_aliaser = VersionedLocation::from(new_aliaser.clone());
1783 let entry = if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1784 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1786 if let Some(aliaser) =
1787 aliasers.iter_mut().find(|aliaser| aliaser.location == versioned_aliaser)
1788 {
1789 aliaser.expiry = expires;
1791 } else {
1792 let aliaser =
1794 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1795 aliasers.try_push(aliaser).map_err(|_| {
1796 tracing::debug!(
1797 target: "xcm::pallet_xcm::add_authorized_alias",
1798 "Failed to add new aliaser to existing entry",
1799 );
1800 Error::<T>::TooManyAuthorizedAliases
1801 })?;
1802 ticket = ticket.update(&signed_origin, aliasers_footprint(aliasers.len()))?;
1804 }
1805 AuthorizedAliasesEntry { aliasers, ticket }
1806 } else {
1807 let ticket = TicketOf::<T>::new(&signed_origin, aliasers_footprint(1))?;
1809 let aliaser =
1810 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1811 let mut aliasers = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
1812 aliasers.try_push(aliaser).map_err(|error| {
1813 tracing::debug!(
1814 target: "xcm::pallet_xcm::add_authorized_alias", ?error,
1815 "Failed to add first aliaser to new entry",
1816 );
1817 Error::<T>::TooManyAuthorizedAliases
1818 })?;
1819 AuthorizedAliasesEntry { aliasers, ticket }
1820 };
1821 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1823 Self::deposit_event(Event::AliasAuthorized {
1824 aliaser: new_aliaser,
1825 target: origin_location,
1826 expiry: expires,
1827 });
1828 Ok(())
1829 }
1830
1831 #[pallet::call_index(15)]
1834 pub fn remove_authorized_alias(
1835 origin: OriginFor<T>,
1836 aliaser: Box<VersionedLocation>,
1837 ) -> DispatchResult {
1838 let signed_origin = ensure_signed(origin.clone())?;
1839 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1840 let to_remove: Location = (*aliaser).try_into().map_err(|()| {
1841 tracing::debug!(
1842 target: "xcm::pallet_xcm::remove_authorized_alias",
1843 "Failed to convert aliaser VersionedLocation",
1844 );
1845 Error::<T>::BadVersion
1846 })?;
1847 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1848 let origin_location = match origin_location.unpack() {
1850 (0, [AccountId32 { network: _, id }]) => {
1851 Location::new(0, [AccountId32 { network: None, id: *id }])
1852 },
1853 _ => return Err(Error::<T>::InvalidOrigin.into()),
1854 };
1855 tracing::debug!(target: "xcm::pallet_xcm::remove_authorized_alias", ?origin_location, ?to_remove);
1856 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1857 let versioned_origin = VersionedLocation::from(origin_location.clone());
1859 let versioned_to_remove = VersionedLocation::from(to_remove.clone());
1860 AuthorizedAliases::<T>::get(&versioned_origin)
1861 .ok_or(Error::<T>::AliasNotFound.into())
1862 .and_then(|entry| {
1863 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1864 let old_len = aliasers.len();
1865 aliasers.retain(|alias| versioned_to_remove.ne(&alias.location));
1866 let new_len = aliasers.len();
1867 if aliasers.is_empty() {
1868 ticket.drop(&signed_origin)?;
1870 AuthorizedAliases::<T>::remove(&versioned_origin);
1871 Self::deposit_event(Event::AliasAuthorizationRemoved {
1872 aliaser: to_remove,
1873 target: origin_location,
1874 });
1875 Ok(())
1876 } else if old_len != new_len {
1877 ticket = ticket.update(&signed_origin, aliasers_footprint(new_len))?;
1879 let entry = AuthorizedAliasesEntry { aliasers, ticket };
1880 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1881 Self::deposit_event(Event::AliasAuthorizationRemoved {
1882 aliaser: to_remove,
1883 target: origin_location,
1884 });
1885 Ok(())
1886 } else {
1887 Err(Error::<T>::AliasNotFound.into())
1888 }
1889 })
1890 }
1891
1892 #[pallet::call_index(16)]
1895 #[pallet::weight(T::WeightInfo::remove_authorized_alias())]
1896 pub fn remove_all_authorized_aliases(origin: OriginFor<T>) -> DispatchResult {
1897 let signed_origin = ensure_signed(origin.clone())?;
1898 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1899 let origin_location = match origin_location.unpack() {
1901 (0, [AccountId32 { network: _, id }]) => {
1902 Location::new(0, [AccountId32 { network: None, id: *id }])
1903 },
1904 _ => return Err(Error::<T>::InvalidOrigin.into()),
1905 };
1906 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", ?origin_location);
1907 let versioned_origin = VersionedLocation::from(origin_location.clone());
1909 if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1910 entry.ticket.drop(&signed_origin)?;
1912 AuthorizedAliases::<T>::remove(&versioned_origin);
1913 Self::deposit_event(Event::AliasesAuthorizationsRemoved {
1914 target: origin_location,
1915 });
1916 Ok(())
1917 } else {
1918 tracing::debug!(target: "xcm::pallet_xcm::remove_all_authorized_aliases", "No authorized alias entry found for the origin");
1919 Err(Error::<T>::AliasNotFound.into())
1920 }
1921 }
1922 }
1923}
1924
1925const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1927
1928#[derive(Clone, PartialEq)]
1930enum FeesHandling<T: Config> {
1931 Batched { fees: Asset },
1933 Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1935}
1936
1937impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1938 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1939 match self {
1940 Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1941 Self::Separate { local_xcm, remote_xcm } => write!(
1942 f,
1943 "FeesHandling::Separate(local: {:?}, remote: {:?})",
1944 local_xcm, remote_xcm
1945 ),
1946 }
1947 }
1948}
1949
1950impl<T: Config> QueryHandler for Pallet<T> {
1951 type BlockNumber = BlockNumberFor<T>;
1952 type Error = XcmError;
1953 type UniversalLocation = T::UniversalLocation;
1954
1955 fn new_query(
1957 responder: impl Into<Location>,
1958 timeout: BlockNumberFor<T>,
1959 match_querier: impl Into<Location>,
1960 ) -> QueryId {
1961 Self::do_new_query(responder, None, timeout, match_querier)
1962 }
1963
1964 fn report_outcome(
1967 message: &mut Xcm<()>,
1968 responder: impl Into<Location>,
1969 timeout: Self::BlockNumber,
1970 ) -> Result<QueryId, Self::Error> {
1971 let responder = responder.into();
1972 let destination =
1973 Self::UniversalLocation::get().invert_target(&responder).map_err(|()| {
1974 tracing::debug!(
1975 target: "xcm::pallet_xcm::report_outcome",
1976 "Failed to invert responder Location",
1977 );
1978 XcmError::LocationNotInvertible
1979 })?;
1980 let query_id = Self::new_query(responder, timeout, Here);
1981 let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1982 let report_error = Xcm(vec![ReportError(response_info)]);
1983 message.0.insert(0, SetAppendix(report_error));
1984 Ok(query_id)
1985 }
1986
1987 fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1989 match Queries::<T>::get(query_id) {
1990 Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1991 Ok(response) => {
1992 Queries::<T>::remove(query_id);
1993 Self::deposit_event(Event::ResponseTaken { query_id });
1994 QueryResponseStatus::Ready { response, at }
1995 },
1996 Err(_) => {
1997 tracing::debug!(
1998 target: "xcm::pallet_xcm::take_response", ?query_id,
1999 "Failed to convert VersionedResponse to Response for query",
2000 );
2001 QueryResponseStatus::UnexpectedVersion
2002 },
2003 },
2004 Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
2005 Some(_) => {
2006 tracing::debug!(
2007 target: "xcm::pallet_xcm::take_response", ?query_id,
2008 "Unexpected QueryStatus variant for query",
2009 );
2010 QueryResponseStatus::UnexpectedVersion
2011 },
2012 None => {
2013 tracing::debug!(
2014 target: "xcm::pallet_xcm::take_response", ?query_id,
2015 "Query ID not found`",
2016 );
2017 QueryResponseStatus::NotFound
2018 },
2019 }
2020 }
2021
2022 #[cfg(feature = "runtime-benchmarks")]
2023 fn expect_response(id: QueryId, response: Response) {
2024 let response = response.into();
2025 Queries::<T>::insert(
2026 id,
2027 QueryStatus::Ready { response, at: frame_system::Pallet::<T>::current_block_number() },
2028 );
2029 }
2030}
2031
2032impl<T: Config> Pallet<T> {
2033 pub fn query(query_id: &QueryId) -> Option<QueryStatus<BlockNumberFor<T>>> {
2035 Queries::<T>::get(query_id)
2036 }
2037
2038 pub fn asset_trap(trap_id: &H256) -> u32 {
2044 AssetTraps::<T>::get(trap_id)
2045 }
2046
2047 fn find_fee_and_assets_transfer_types(
2052 assets: &[Asset],
2053 fee_asset_item: usize,
2054 dest: &Location,
2055 ) -> Result<(TransferType, TransferType), Error<T>> {
2056 let mut fees_transfer_type = None;
2057 let mut assets_transfer_type = None;
2058 for (idx, asset) in assets.iter().enumerate() {
2059 if let Fungible(x) = asset.fun {
2060 ensure!(!x.is_zero(), Error::<T>::Empty);
2062 }
2063 let transfer_type =
2064 T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
2065 if idx == fee_asset_item {
2066 fees_transfer_type = Some(transfer_type);
2067 } else {
2068 if let Some(existing) = assets_transfer_type.as_ref() {
2069 ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
2072 } else {
2073 assets_transfer_type = Some(transfer_type);
2075 }
2076 }
2077 }
2078 if assets.len() == 1 {
2080 assets_transfer_type = fees_transfer_type.clone()
2081 }
2082 Ok((
2083 fees_transfer_type.ok_or(Error::<T>::Empty)?,
2084 assets_transfer_type.ok_or(Error::<T>::Empty)?,
2085 ))
2086 }
2087
2088 fn do_reserve_transfer_assets(
2089 origin: OriginFor<T>,
2090 dest: Box<VersionedLocation>,
2091 beneficiary: Box<VersionedLocation>,
2092 assets: Box<VersionedAssets>,
2093 fee_asset_item: u32,
2094 weight_limit: WeightLimit,
2095 ) -> DispatchResult {
2096 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2097 let dest = (*dest).try_into().map_err(|()| {
2098 tracing::debug!(
2099 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2100 "Failed to convert destination VersionedLocation",
2101 );
2102 Error::<T>::BadVersion
2103 })?;
2104 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2105 tracing::debug!(
2106 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2107 "Failed to convert beneficiary VersionedLocation",
2108 );
2109 Error::<T>::BadVersion
2110 })?;
2111 let assets: Assets = (*assets).try_into().map_err(|()| {
2112 tracing::debug!(
2113 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2114 "Failed to convert VersionedAssets",
2115 );
2116 Error::<T>::BadVersion
2117 })?;
2118 tracing::debug!(
2119 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
2120 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item,
2121 );
2122
2123 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2124 let value = (origin_location, assets.into_inner());
2125 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2126 let (origin, assets) = value;
2127
2128 let fee_asset_item = fee_asset_item as usize;
2129 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
2130
2131 let (fees_transfer_type, assets_transfer_type) =
2133 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
2134 ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
2136 ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
2138
2139 Self::ensure_network_asset_reserve_transfer_allowed(
2143 &assets,
2144 fee_asset_item,
2145 &assets_transfer_type,
2146 &fees_transfer_type,
2147 )?;
2148
2149 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2150 origin.clone(),
2151 dest.clone(),
2152 Either::Left(beneficiary),
2153 assets,
2154 assets_transfer_type,
2155 FeesHandling::Batched { fees },
2156 weight_limit,
2157 )?;
2158 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2159 }
2160
2161 fn do_teleport_assets(
2162 origin: OriginFor<T>,
2163 dest: Box<VersionedLocation>,
2164 beneficiary: Box<VersionedLocation>,
2165 assets: Box<VersionedAssets>,
2166 fee_asset_item: u32,
2167 weight_limit: WeightLimit,
2168 ) -> DispatchResult {
2169 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2170 let dest = (*dest).try_into().map_err(|()| {
2171 tracing::debug!(
2172 target: "xcm::pallet_xcm::do_teleport_assets",
2173 "Failed to convert destination VersionedLocation",
2174 );
2175 Error::<T>::BadVersion
2176 })?;
2177 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2178 tracing::debug!(
2179 target: "xcm::pallet_xcm::do_teleport_assets",
2180 "Failed to convert beneficiary VersionedLocation",
2181 );
2182 Error::<T>::BadVersion
2183 })?;
2184 let assets: Assets = (*assets).try_into().map_err(|()| {
2185 tracing::debug!(
2186 target: "xcm::pallet_xcm::do_teleport_assets",
2187 "Failed to convert VersionedAssets",
2188 );
2189 Error::<T>::BadVersion
2190 })?;
2191 tracing::debug!(
2192 target: "xcm::pallet_xcm::do_teleport_assets",
2193 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_item, ?weight_limit,
2194 );
2195
2196 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2197 let value = (origin_location, assets.into_inner());
2198 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2199 let (origin_location, assets) = value;
2200 for asset in assets.iter() {
2201 let transfer_type =
2202 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
2203 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
2204 }
2205 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
2206
2207 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2208 origin_location.clone(),
2209 dest.clone(),
2210 Either::Left(beneficiary),
2211 assets,
2212 TransferType::Teleport,
2213 FeesHandling::Batched { fees },
2214 weight_limit,
2215 )?;
2216 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
2217 }
2218
2219 fn do_transfer_assets(
2220 origin: Location,
2221 dest: Location,
2222 beneficiary: Either<Location, Xcm<()>>,
2223 mut assets: Vec<Asset>,
2224 assets_transfer_type: TransferType,
2225 fee_asset_index: usize,
2226 fees_transfer_type: TransferType,
2227 weight_limit: WeightLimit,
2228 ) -> DispatchResult {
2229 let fees = if fees_transfer_type == assets_transfer_type {
2231 let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
2232 FeesHandling::Batched { fees }
2234 } else {
2235 ensure!(
2241 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
2242 Error::<T>::InvalidAssetUnsupportedReserve
2243 );
2244 let weight_limit = weight_limit.clone();
2245 let fees = assets.remove(fee_asset_index);
2248 let (local_xcm, remote_xcm) = match fees_transfer_type {
2249 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
2250 origin.clone(),
2251 dest.clone(),
2252 fees,
2253 weight_limit,
2254 )?,
2255 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
2256 origin.clone(),
2257 dest.clone(),
2258 fees,
2259 weight_limit,
2260 )?,
2261 TransferType::Teleport => Self::teleport_fees_instructions(
2262 origin.clone(),
2263 dest.clone(),
2264 fees,
2265 weight_limit,
2266 )?,
2267 TransferType::RemoteReserve(_) => {
2268 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into())
2269 },
2270 };
2271 FeesHandling::Separate { local_xcm, remote_xcm }
2272 };
2273
2274 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2275 origin.clone(),
2276 dest.clone(),
2277 beneficiary,
2278 assets,
2279 assets_transfer_type,
2280 fees,
2281 weight_limit,
2282 )?;
2283 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2284 }
2285
2286 fn build_xcm_transfer_type(
2287 origin: Location,
2288 dest: Location,
2289 beneficiary: Either<Location, Xcm<()>>,
2290 assets: Vec<Asset>,
2291 transfer_type: TransferType,
2292 fees: FeesHandling<T>,
2293 weight_limit: WeightLimit,
2294 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
2295 tracing::debug!(
2296 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2297 ?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
2298 );
2299 match transfer_type {
2300 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
2301 origin.clone(),
2302 dest.clone(),
2303 beneficiary,
2304 assets,
2305 fees,
2306 weight_limit,
2307 )
2308 .map(|(local, remote)| (local, Some(remote))),
2309 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
2310 origin.clone(),
2311 dest.clone(),
2312 beneficiary,
2313 assets,
2314 fees,
2315 weight_limit,
2316 )
2317 .map(|(local, remote)| (local, Some(remote))),
2318 TransferType::RemoteReserve(reserve) => {
2319 let fees = match fees {
2320 FeesHandling::Batched { fees } => fees,
2321 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2322 };
2323 Self::remote_reserve_transfer_program(
2324 origin.clone(),
2325 reserve.try_into().map_err(|()| {
2326 tracing::debug!(
2327 target: "xcm::pallet_xcm::build_xcm_transfer_type",
2328 "Failed to convert remote reserve location",
2329 );
2330 Error::<T>::BadVersion
2331 })?,
2332 beneficiary,
2333 dest.clone(),
2334 assets,
2335 fees,
2336 weight_limit,
2337 )
2338 .map(|local| (local, None))
2339 },
2340 TransferType::Teleport => Self::teleport_assets_program(
2341 origin.clone(),
2342 dest.clone(),
2343 beneficiary,
2344 assets,
2345 fees,
2346 weight_limit,
2347 )
2348 .map(|(local, remote)| (local, Some(remote))),
2349 }
2350 }
2351
2352 fn execute_xcm_transfer(
2353 origin: Location,
2354 dest: Location,
2355 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
2356 remote_xcm: Option<Xcm<()>>,
2357 ) -> DispatchResult {
2358 tracing::debug!(
2359 target: "xcm::pallet_xcm::execute_xcm_transfer",
2360 ?origin, ?dest, ?local_xcm, ?remote_xcm,
2361 );
2362
2363 let weight =
2364 T::Weigher::weight(&mut local_xcm, Weight::MAX).map_err(|error| {
2365 tracing::debug!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, "Failed to calculate weight");
2366 Error::<T>::UnweighableMessage
2367 })?;
2368 let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
2369 let outcome = T::XcmExecutor::prepare_and_execute(
2370 origin.clone(),
2371 local_xcm,
2372 &mut hash,
2373 weight,
2374 weight,
2375 );
2376 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
2377 outcome.clone().ensure_complete().map_err(|error| {
2378 tracing::error!(
2379 target: "xcm::pallet_xcm::execute_xcm_transfer",
2380 ?error, "XCM execution failed with error with outcome: {:?}", outcome
2381 );
2382 Error::<T>::LocalExecutionIncompleteWithError {
2383 index: error.index,
2384 error: error.error.into(),
2385 }
2386 })?;
2387
2388 if let Some(remote_xcm) = remote_xcm {
2389 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
2390 .map_err(|error| {
2391 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
2392 Error::<T>::from(error)
2393 })?;
2394 if origin != Here.into_location() {
2395 Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
2396 tracing::error!(
2397 target: "xcm::pallet_xcm::execute_xcm_transfer",
2398 ?error, ?price, ?origin, "Unable to charge fee",
2399 );
2400 Error::<T>::FeesNotMet
2401 })?;
2402 }
2403 let message_id = T::XcmRouter::deliver(ticket)
2404 .map_err(|error| {
2405 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
2406 Error::<T>::from(error)
2407 })?;
2408
2409 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
2410 Self::deposit_event(e);
2411 }
2412 Ok(())
2413 }
2414
2415 fn add_fees_to_xcm(
2416 dest: Location,
2417 fees: FeesHandling<T>,
2418 weight_limit: WeightLimit,
2419 local: &mut Xcm<<T as Config>::RuntimeCall>,
2420 remote: &mut Xcm<()>,
2421 ) -> Result<(), Error<T>> {
2422 match fees {
2423 FeesHandling::Batched { fees } => {
2424 let context = T::UniversalLocation::get();
2425 let reanchored_fees =
2428 fees.reanchored(&dest, &context).map_err(|e| {
2429 tracing::error!(target: "xcm::pallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
2430 Error::<T>::CannotReanchor
2431 })?;
2432 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
2434 },
2435 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
2436 core::mem::swap(local, &mut local_fees);
2439 core::mem::swap(remote, &mut remote_fees);
2440 local.inner_mut().append(&mut local_fees.into_inner());
2442 remote.inner_mut().append(&mut remote_fees.into_inner());
2443 },
2444 }
2445 Ok(())
2446 }
2447
2448 fn local_reserve_fees_instructions(
2449 origin: Location,
2450 dest: Location,
2451 fees: Asset,
2452 weight_limit: WeightLimit,
2453 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2454 let value = (origin, vec![fees.clone()]);
2455 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2456
2457 let context = T::UniversalLocation::get();
2458 let reanchored_fees = fees.clone().reanchored(&dest, &context).map_err(|_| {
2459 tracing::debug!(
2460 target: "xcm::pallet_xcm::local_reserve_fees_instructions",
2461 "Failed to re-anchor fees",
2462 );
2463 Error::<T>::CannotReanchor
2464 })?;
2465
2466 let local_execute_xcm = Xcm(vec![
2467 TransferAsset { assets: fees.into(), beneficiary: dest },
2469 ]);
2470 let xcm_on_dest = Xcm(vec![
2471 ReserveAssetDeposited(reanchored_fees.clone().into()),
2473 BuyExecution { fees: reanchored_fees, weight_limit },
2475 ]);
2476 Ok((local_execute_xcm, xcm_on_dest))
2477 }
2478
2479 fn local_reserve_transfer_programs(
2480 origin: Location,
2481 dest: Location,
2482 beneficiary: Either<Location, Xcm<()>>,
2483 assets: Vec<Asset>,
2484 fees: FeesHandling<T>,
2485 weight_limit: WeightLimit,
2486 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2487 let value = (origin, assets);
2488 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2489 let (_, assets) = value;
2490
2491 let max_assets =
2493 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2494 let assets: Assets = assets.into();
2495 let context = T::UniversalLocation::get();
2496 let mut reanchored_assets = assets.clone();
2497 reanchored_assets
2498 .reanchor(&dest, &context)
2499 .map_err(|e| {
2500 tracing::error!(target: "xcm::pallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2501 Error::<T>::CannotReanchor
2502 })?;
2503
2504 let mut local_execute_xcm = Xcm(vec![
2506 TransferAsset { assets, beneficiary: dest.clone() },
2508 ]);
2509 let mut xcm_on_dest = Xcm(vec![
2511 ReserveAssetDeposited(reanchored_assets),
2513 ClearOrigin,
2515 ]);
2516 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2518
2519 let custom_remote_xcm = match beneficiary {
2521 Either::Right(custom_xcm) => custom_xcm,
2522 Either::Left(beneficiary) => {
2523 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2525 },
2526 };
2527 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2528
2529 Ok((local_execute_xcm, xcm_on_dest))
2530 }
2531
2532 fn destination_reserve_fees_instructions(
2533 origin: Location,
2534 dest: Location,
2535 fees: Asset,
2536 weight_limit: WeightLimit,
2537 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2538 let value = (origin, vec![fees.clone()]);
2539 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2540 ensure!(
2541 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
2542 Error::<T>::InvalidAssetUnsupportedReserve
2543 );
2544
2545 let context = T::UniversalLocation::get();
2546 let reanchored_fees = fees
2547 .clone()
2548 .reanchored(&dest, &context)
2549 .map_err(|e| {
2550 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
2551 Error::<T>::CannotReanchor
2552 })?;
2553 let fees: Assets = fees.into();
2554
2555 let local_execute_xcm = Xcm(vec![
2556 WithdrawAsset(fees.clone()),
2558 BurnAsset(fees),
2560 ]);
2561 let xcm_on_dest = Xcm(vec![
2562 WithdrawAsset(reanchored_fees.clone().into()),
2564 BuyExecution { fees: reanchored_fees, weight_limit },
2566 ]);
2567 Ok((local_execute_xcm, xcm_on_dest))
2568 }
2569
2570 fn destination_reserve_transfer_programs(
2571 origin: Location,
2572 dest: Location,
2573 beneficiary: Either<Location, Xcm<()>>,
2574 assets: Vec<Asset>,
2575 fees: FeesHandling<T>,
2576 weight_limit: WeightLimit,
2577 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2578 let value = (origin, assets);
2579 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2580 let (_, assets) = value;
2581 for asset in assets.iter() {
2582 ensure!(
2583 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2584 Error::<T>::InvalidAssetUnsupportedReserve
2585 );
2586 }
2587
2588 let max_assets =
2590 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2591 let assets: Assets = assets.into();
2592 let context = T::UniversalLocation::get();
2593 let mut reanchored_assets = assets.clone();
2594 reanchored_assets
2595 .reanchor(&dest, &context)
2596 .map_err(|e| {
2597 tracing::error!(target: "xcm::pallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2598 Error::<T>::CannotReanchor
2599 })?;
2600
2601 let mut local_execute_xcm = Xcm(vec![
2603 WithdrawAsset(assets.clone()),
2605 BurnAsset(assets),
2607 ]);
2608 let mut xcm_on_dest = Xcm(vec![
2610 WithdrawAsset(reanchored_assets),
2612 ClearOrigin,
2614 ]);
2615 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2617
2618 let custom_remote_xcm = match beneficiary {
2620 Either::Right(custom_xcm) => custom_xcm,
2621 Either::Left(beneficiary) => {
2622 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2624 },
2625 };
2626 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2627
2628 Ok((local_execute_xcm, xcm_on_dest))
2629 }
2630
2631 fn remote_reserve_transfer_program(
2633 origin: Location,
2634 reserve: Location,
2635 beneficiary: Either<Location, Xcm<()>>,
2636 dest: Location,
2637 assets: Vec<Asset>,
2638 fees: Asset,
2639 weight_limit: WeightLimit,
2640 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2641 let value = (origin, assets);
2642 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2643 let (_, assets) = value;
2644
2645 let max_assets = assets.len() as u32;
2646 let context = T::UniversalLocation::get();
2647 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2650 let reserve_fees = fees_half_1
2652 .reanchored(&reserve, &context)
2653 .map_err(|e| {
2654 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2655 Error::<T>::CannotReanchor
2656 })?;
2657 let dest_fees = fees_half_2
2659 .reanchored(&dest, &context)
2660 .map_err(|e| {
2661 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2662 Error::<T>::CannotReanchor
2663 })?;
2664 let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2666 tracing::error!(target: "xcm::pallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2667 Error::<T>::CannotReanchor
2668 })?;
2669 let mut xcm_on_dest =
2671 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2672 let custom_xcm_on_dest = match beneficiary {
2674 Either::Right(custom_xcm) => custom_xcm,
2675 Either::Left(beneficiary) => {
2676 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2678 },
2679 };
2680 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2681 let xcm_on_reserve = Xcm(vec![
2683 BuyExecution { fees: reserve_fees, weight_limit },
2684 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2685 ]);
2686 Ok(Xcm(vec![
2687 WithdrawAsset(assets.into()),
2688 SetFeesMode { jit_withdraw: true },
2689 InitiateReserveWithdraw {
2690 assets: Wild(AllCounted(max_assets)),
2691 reserve,
2692 xcm: xcm_on_reserve,
2693 },
2694 ]))
2695 }
2696
2697 fn teleport_fees_instructions(
2698 origin: Location,
2699 dest: Location,
2700 fees: Asset,
2701 weight_limit: WeightLimit,
2702 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2703 let value = (origin, vec![fees.clone()]);
2704 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2705 ensure!(
2706 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2707 Error::<T>::Filtered
2708 );
2709
2710 let context = T::UniversalLocation::get();
2711 let reanchored_fees = fees
2712 .clone()
2713 .reanchored(&dest, &context)
2714 .map_err(|e| {
2715 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2716 Error::<T>::CannotReanchor
2717 })?;
2718
2719 let dummy_context =
2721 XcmContext { origin: None, message_id: Default::default(), topic: None };
2722 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2727 &dest,
2728 &fees,
2729 &dummy_context,
2730 )
2731 .map_err(|e| {
2732 tracing::error!(target: "xcm::pallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2733 Error::<T>::CannotCheckOutTeleport
2734 })?;
2735 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2738 &dest,
2739 &fees,
2740 &dummy_context,
2741 );
2742
2743 let fees: Assets = fees.into();
2744 let local_execute_xcm = Xcm(vec![
2745 WithdrawAsset(fees.clone()),
2747 BurnAsset(fees),
2749 ]);
2750 let xcm_on_dest = Xcm(vec![
2751 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2753 BuyExecution { fees: reanchored_fees, weight_limit },
2755 ]);
2756 Ok((local_execute_xcm, xcm_on_dest))
2757 }
2758
2759 fn teleport_assets_program(
2760 origin: Location,
2761 dest: Location,
2762 beneficiary: Either<Location, Xcm<()>>,
2763 assets: Vec<Asset>,
2764 fees: FeesHandling<T>,
2765 weight_limit: WeightLimit,
2766 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2767 let value = (origin, assets);
2768 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2769 let (_, assets) = value;
2770 for asset in assets.iter() {
2771 ensure!(
2772 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2773 Error::<T>::Filtered
2774 );
2775 }
2776
2777 let max_assets =
2779 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2780 let context = T::UniversalLocation::get();
2781 let assets: Assets = assets.into();
2782 let mut reanchored_assets = assets.clone();
2783 reanchored_assets
2784 .reanchor(&dest, &context)
2785 .map_err(|e| {
2786 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2787 Error::<T>::CannotReanchor
2788 })?;
2789
2790 let dummy_context =
2792 XcmContext { origin: None, message_id: Default::default(), topic: None };
2793 for asset in assets.inner() {
2794 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2799 &dest,
2800 asset,
2801 &dummy_context,
2802 )
2803 .map_err(|e| {
2804 tracing::error!(target: "xcm::pallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2805 Error::<T>::CannotCheckOutTeleport
2806 })?;
2807 }
2808 for asset in assets.inner() {
2809 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2812 &dest,
2813 asset,
2814 &dummy_context,
2815 );
2816 }
2817
2818 let mut local_execute_xcm = Xcm(vec![
2820 WithdrawAsset(assets.clone()),
2822 BurnAsset(assets),
2824 ]);
2825 let mut xcm_on_dest = Xcm(vec![
2827 ReceiveTeleportedAsset(reanchored_assets),
2829 ClearOrigin,
2831 ]);
2832 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2834
2835 let custom_remote_xcm = match beneficiary {
2837 Either::Right(custom_xcm) => custom_xcm,
2838 Either::Left(beneficiary) => {
2839 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2841 },
2842 };
2843 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2844
2845 Ok((local_execute_xcm, xcm_on_dest))
2846 }
2847
2848 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2850 match fees.fun {
2851 Fungible(amount) => {
2852 let fee1 = amount.saturating_div(2);
2853 let fee2 = amount.saturating_sub(fee1);
2854 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2855 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2856 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2857 },
2858 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2859 }
2860 }
2861
2862 pub(crate) fn lazy_migration(
2865 mut stage: VersionMigrationStage,
2866 weight_cutoff: Weight,
2867 ) -> (Weight, Option<VersionMigrationStage>) {
2868 let mut weight_used = Weight::zero();
2869
2870 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2871 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2872 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2873 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2874 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2875 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2876 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2877
2878 use VersionMigrationStage::*;
2879
2880 if stage == MigrateSupportedVersion {
2881 for v in 0..XCM_VERSION {
2884 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2885 if let Ok(new_key) = old_key.into_latest() {
2886 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2887 }
2888 weight_used.saturating_accrue(sv_migrate_weight);
2889 if weight_used.any_gte(weight_cutoff) {
2890 return (weight_used, Some(stage));
2891 }
2892 }
2893 }
2894 stage = MigrateVersionNotifiers;
2895 }
2896 if stage == MigrateVersionNotifiers {
2897 for v in 0..XCM_VERSION {
2898 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2899 if let Ok(new_key) = old_key.into_latest() {
2900 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2901 }
2902 weight_used.saturating_accrue(vn_migrate_weight);
2903 if weight_used.any_gte(weight_cutoff) {
2904 return (weight_used, Some(stage));
2905 }
2906 }
2907 }
2908 stage = NotifyCurrentTargets(None);
2909 }
2910
2911 let xcm_version = T::AdvertisedXcmVersion::get();
2912
2913 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2914 let mut iter = match maybe_last_raw_key {
2915 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2916 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2917 };
2918 while let Some((key, value)) = iter.next() {
2919 let (query_id, max_weight, target_xcm_version) = value;
2920 let new_key: Location = match key.clone().try_into() {
2921 Ok(k) if target_xcm_version != xcm_version => k,
2922 _ => {
2923 weight_used.saturating_accrue(vnt_already_notified_weight);
2926 continue;
2927 },
2928 };
2929 let response = Response::Version(xcm_version);
2930 let message =
2931 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2932 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2933 Ok((message_id, cost)) => {
2934 let value = (query_id, max_weight, xcm_version);
2935 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2936 Event::VersionChangeNotified {
2937 destination: new_key,
2938 result: xcm_version,
2939 cost,
2940 message_id,
2941 }
2942 },
2943 Err(e) => {
2944 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2945 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2946 },
2947 };
2948 Self::deposit_event(event);
2949 weight_used.saturating_accrue(vnt_notify_weight);
2950 if weight_used.any_gte(weight_cutoff) {
2951 let last = Some(iter.last_raw_key().into());
2952 return (weight_used, Some(NotifyCurrentTargets(last)));
2953 }
2954 }
2955 stage = MigrateAndNotifyOldTargets;
2956 }
2957 if stage == MigrateAndNotifyOldTargets {
2958 for v in 0..XCM_VERSION {
2959 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2960 let (query_id, max_weight, target_xcm_version) = value;
2961 let new_key = match Location::try_from(old_key.clone()) {
2962 Ok(k) => k,
2963 Err(()) => {
2964 Self::deposit_event(Event::NotifyTargetMigrationFail {
2965 location: old_key,
2966 query_id: value.0,
2967 });
2968 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2969 if weight_used.any_gte(weight_cutoff) {
2970 return (weight_used, Some(stage));
2971 }
2972 continue;
2973 },
2974 };
2975
2976 let versioned_key = LatestVersionedLocation(&new_key);
2977 if target_xcm_version == xcm_version {
2978 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2979 weight_used.saturating_accrue(vnt_migrate_weight);
2980 } else {
2981 let response = Response::Version(xcm_version);
2983 let message = Xcm(vec![QueryResponse {
2984 query_id,
2985 response,
2986 max_weight,
2987 querier: None,
2988 }]);
2989 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2990 Ok((message_id, cost)) => {
2991 VersionNotifyTargets::<T>::insert(
2992 XCM_VERSION,
2993 versioned_key,
2994 (query_id, max_weight, xcm_version),
2995 );
2996 Event::VersionChangeNotified {
2997 destination: new_key,
2998 result: xcm_version,
2999 cost,
3000 message_id,
3001 }
3002 },
3003 Err(e) => Event::NotifyTargetSendFail {
3004 location: new_key,
3005 query_id,
3006 error: e.into(),
3007 },
3008 };
3009 Self::deposit_event(event);
3010 weight_used.saturating_accrue(vnt_notify_migrate_weight);
3011 }
3012 if weight_used.any_gte(weight_cutoff) {
3013 return (weight_used, Some(stage));
3014 }
3015 }
3016 }
3017 }
3018 (weight_used, None)
3019 }
3020
3021 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
3023 let dest = dest.into();
3024 let versioned_dest = VersionedLocation::from(dest.clone());
3025 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
3026 ensure!(!already, XcmError::InvalidLocation);
3027 let query_id = QueryCounter::<T>::mutate(|q| {
3028 let r = *q;
3029 q.saturating_inc();
3030 r
3031 });
3032 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
3034 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3035 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
3036 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
3037 let query_status =
3038 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
3039 Queries::<T>::insert(query_id, query_status);
3040 Ok(())
3041 }
3042
3043 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
3045 let dest = dest.into();
3046 let versioned_dest = LatestVersionedLocation(&dest);
3047 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
3048 .ok_or(XcmError::InvalidLocation)?;
3049 let (message_id, cost) =
3050 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
3051 Self::deposit_event(Event::VersionNotifyUnrequested {
3052 destination: dest,
3053 cost,
3054 message_id,
3055 });
3056 Queries::<T>::remove(query_id);
3057 Ok(())
3058 }
3059
3060 pub fn send_xcm(
3064 interior: impl Into<Junctions>,
3065 dest: impl Into<Location>,
3066 mut message: Xcm<()>,
3067 ) -> Result<XcmHash, SendError> {
3068 let interior = interior.into();
3069 let local_origin = interior.clone().into();
3070 let dest = dest.into();
3071 let is_waived =
3072 <T::XcmExecutor as FeeManager>::is_waived(Some(&local_origin), FeeReason::ChargeFees);
3073 if interior != Junctions::Here {
3074 message.0.insert(0, DescendOrigin(interior.clone()));
3075 }
3076 tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
3077 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
3078 if !is_waived {
3079 Self::charge_fees(local_origin, price).map_err(|e| {
3080 tracing::error!(
3081 target: "xcm::pallet_xcm::send_xcm",
3082 ?e,
3083 "Charging fees failed with error",
3084 );
3085 SendError::Fees
3086 })?;
3087 }
3088 T::XcmRouter::deliver(ticket)
3089 }
3090
3091 pub fn check_account() -> T::AccountId {
3092 const ID: PalletId = PalletId(*b"py/xcmch");
3093 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
3094 }
3095
3096 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
3102 origin: OriginCaller,
3103 call: RuntimeCall,
3104 result_xcms_version: XcmVersion,
3105 ) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3106 where
3107 Runtime: crate::Config,
3108 Router: InspectMessageQueues,
3109 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
3110 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
3111 {
3112 crate::Pallet::<Runtime>::set_record_xcm(true);
3113 Router::clear_messages();
3115 frame_system::Pallet::<Runtime>::reset_events();
3117 let result = call.dispatch(origin.into());
3118 crate::Pallet::<Runtime>::set_record_xcm(false);
3119 let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
3120 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
3121 .transpose()
3122 .map_err(|()| {
3123 tracing::debug!(
3124 target: "xcm::DryRunApi::dry_run_call",
3125 "Local xcm version conversion failed"
3126 );
3127
3128 XcmDryRunApiError::VersionedConversionFailed
3129 })?;
3130
3131 let forwarded_xcms =
3133 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
3134 |error| {
3135 tracing::debug!(
3136 target: "xcm::DryRunApi::dry_run_call",
3137 ?error, "Forwarded xcms version conversion failed with error"
3138 );
3139 },
3140 )?;
3141 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
3142 frame_system::Pallet::<Runtime>::read_events_no_consensus()
3143 .map(|record| record.event.clone())
3144 .collect();
3145 Ok(CallDryRunEffects {
3146 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
3147 forwarded_xcms,
3148 emitted_events: events,
3149 execution_result: result,
3150 })
3151 }
3152
3153 pub fn dry_run_xcm<Router>(
3158 origin_location: VersionedLocation,
3159 xcm: VersionedXcm<<T as Config>::RuntimeCall>,
3160 ) -> Result<XcmDryRunEffects<<T as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3161 where
3162 Router: InspectMessageQueues,
3163 {
3164 let origin_location: Location = origin_location.try_into().map_err(|error| {
3165 tracing::debug!(
3166 target: "xcm::DryRunApi::dry_run_xcm",
3167 ?error, "Location version conversion failed with error"
3168 );
3169 XcmDryRunApiError::VersionedConversionFailed
3170 })?;
3171 let xcm_version = xcm.identify_version();
3172 let xcm: Xcm<<T as Config>::RuntimeCall> = xcm.try_into().map_err(|error| {
3173 tracing::debug!(
3174 target: "xcm::DryRunApi::dry_run_xcm",
3175 ?error, "Xcm version conversion failed with error"
3176 );
3177 XcmDryRunApiError::VersionedConversionFailed
3178 })?;
3179 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
3180
3181 Router::clear_messages();
3183 frame_system::Pallet::<T>::reset_events();
3184
3185 let result = <T as Config>::XcmExecutor::prepare_and_execute(
3186 origin_location,
3187 xcm,
3188 &mut hash,
3189 Weight::MAX, Weight::zero(),
3191 );
3192 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
3193 .inspect_err(|error| {
3194 tracing::debug!(
3195 target: "xcm::DryRunApi::dry_run_xcm",
3196 ?error, "Forwarded xcms version conversion failed with error"
3197 );
3198 })?;
3199 let events: Vec<<T as frame_system::Config>::RuntimeEvent> =
3200 frame_system::Pallet::<T>::read_events_no_consensus()
3201 .map(|record| record.event.clone())
3202 .collect();
3203 Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
3204 }
3205
3206 fn convert_xcms(
3207 xcm_version: XcmVersion,
3208 xcms: Vec<VersionedXcm<()>>,
3209 ) -> Result<Vec<VersionedXcm<()>>, ()> {
3210 xcms.into_iter()
3211 .map(|xcm| xcm.into_version(xcm_version))
3212 .collect::<Result<Vec<_>, ()>>()
3213 }
3214
3215 fn convert_forwarded_xcms(
3216 xcm_version: XcmVersion,
3217 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
3218 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
3219 forwarded_xcms
3220 .into_iter()
3221 .map(|(dest, forwarded_xcms)| {
3222 let dest = dest.into_version(xcm_version)?;
3223 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
3224
3225 Ok((dest, forwarded_xcms))
3226 })
3227 .collect::<Result<Vec<_>, ()>>()
3228 .map_err(|()| {
3229 tracing::debug!(
3230 target: "xcm::pallet_xcm::convert_forwarded_xcms",
3231 "Failed to convert VersionedLocation to requested version",
3232 );
3233 XcmDryRunApiError::VersionedConversionFailed
3234 })
3235 }
3236
3237 pub fn query_acceptable_payment_assets(
3242 version: xcm::Version,
3243 asset_ids: Vec<AssetId>,
3244 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
3245 Ok(asset_ids
3246 .into_iter()
3247 .map(|asset_id| VersionedAssetId::from(asset_id))
3248 .filter_map(|asset_id| asset_id.into_version(version).ok())
3249 .collect())
3250 }
3251
3252 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
3253 let message = Xcm::<()>::try_from(message.clone())
3254 .map_err(|e| {
3255 tracing::debug!(target: "xcm::pallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
3256 XcmPaymentApiError::VersionedConversionFailed
3257 })?;
3258
3259 T::Weigher::weight(&mut message.clone().into(), Weight::MAX).map_err(|error| {
3260 tracing::debug!(target: "xcm::pallet_xcm::query_xcm_weight", ?error, ?message, "Error when querying XCM weight");
3261 XcmPaymentApiError::WeightNotComputable
3262 })
3263 }
3264
3265 pub fn query_weight_to_asset_fee<Trader: xcm_executor::traits::WeightTrader>(
3282 weight: Weight,
3283 asset_id: VersionedAssetId,
3284 ) -> Result<u128, XcmPaymentApiError> {
3285 let asset_id: AssetId = asset_id.clone().try_into()
3286 .map_err(|e| {
3287 tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset_id, "Failed to convert versioned asset");
3288 XcmPaymentApiError::VersionedConversionFailed
3289 })?;
3290
3291 let context = XcmContext::with_message_id(XcmHash::default());
3292
3293 let mut trader = Trader::new();
3294 let required = trader.quote_weight(weight, asset_id.clone(), &context)
3295 .map_err(|e| {
3296 tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset_id, "Failed to quote weight");
3297 XcmPaymentApiError::AssetNotFound
3298 })?;
3299 match (required.id, required.fun) {
3300 (required_id, Fungible(required_amount)) if required_id.eq(&asset_id) => {
3301 Ok(required_amount)
3302 },
3303 _ => Err(XcmPaymentApiError::AssetNotFound),
3304 }
3305 }
3306
3307 pub fn query_delivery_fees<AssetExchanger: xcm_executor::traits::AssetExchange>(
3314 destination: VersionedLocation,
3315 message: VersionedXcm<()>,
3316 versioned_asset_id: VersionedAssetId,
3317 ) -> Result<VersionedAssets, XcmPaymentApiError> {
3318 let result_version = destination.identify_version().max(message.identify_version());
3319
3320 let destination: Location = destination
3321 .clone()
3322 .try_into()
3323 .map_err(|e| {
3324 tracing::debug!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
3325 XcmPaymentApiError::VersionedConversionFailed
3326 })?;
3327
3328 let message: Xcm<()> =
3329 message.clone().try_into().map_err(|e| {
3330 tracing::debug!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
3331 XcmPaymentApiError::VersionedConversionFailed
3332 })?;
3333
3334 let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
3335 tracing::debug!(target: "xcm::pallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
3336 XcmPaymentApiError::Unroutable
3337 })?;
3338
3339 if fees.len() != 1 {
3341 return Err(XcmPaymentApiError::Unimplemented);
3342 }
3343
3344 let fee = fees.get(0).ok_or(XcmPaymentApiError::Unimplemented)?;
3345
3346 let asset_id = versioned_asset_id.clone().try_into().map_err(|()| {
3347 tracing::trace!(
3348 target: "xcm::xcm_runtime_apis::query_delivery_fees",
3349 "Failed to convert asset id: {versioned_asset_id:?}!"
3350 );
3351 XcmPaymentApiError::VersionedConversionFailed
3352 })?;
3353
3354 let assets_to_pay = if fee.id == asset_id {
3355 fees
3357 } else {
3358 AssetExchanger::quote_exchange_price(
3360 &fees.into(),
3361 &(asset_id, Fungible(1)).into(),
3362 true, )
3364 .ok_or(XcmPaymentApiError::AssetNotFound)?
3365 };
3366
3367 VersionedAssets::from(assets_to_pay).into_version(result_version).map_err(|e| {
3368 tracing::trace!(
3369 target: "xcm::pallet_xcm::query_delivery_fees",
3370 ?e,
3371 ?result_version,
3372 "Failed to convert fees into desired version"
3373 );
3374 XcmPaymentApiError::VersionedConversionFailed
3375 })
3376 }
3377
3378 pub fn is_trusted_reserve(
3381 asset: VersionedAsset,
3382 location: VersionedLocation,
3383 ) -> Result<bool, TrustedQueryApiError> {
3384 let location: Location = location.try_into().map_err(|e| {
3385 tracing::debug!(
3386 target: "xcm::pallet_xcm::is_trusted_reserve",
3387 ?e, "Failed to convert versioned location",
3388 );
3389 TrustedQueryApiError::VersionedLocationConversionFailed
3390 })?;
3391
3392 let a: Asset = asset.try_into().map_err(|e| {
3393 tracing::debug!(
3394 target: "xcm::pallet_xcm::is_trusted_reserve",
3395 ?e, "Failed to convert versioned asset",
3396 );
3397 TrustedQueryApiError::VersionedAssetConversionFailed
3398 })?;
3399
3400 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
3401 }
3402
3403 pub fn is_trusted_teleporter(
3405 asset: VersionedAsset,
3406 location: VersionedLocation,
3407 ) -> Result<bool, TrustedQueryApiError> {
3408 let location: Location = location.try_into().map_err(|e| {
3409 tracing::debug!(
3410 target: "xcm::pallet_xcm::is_trusted_teleporter",
3411 ?e, "Failed to convert versioned location",
3412 );
3413 TrustedQueryApiError::VersionedLocationConversionFailed
3414 })?;
3415 let a: Asset = asset.try_into().map_err(|e| {
3416 tracing::debug!(
3417 target: "xcm::pallet_xcm::is_trusted_teleporter",
3418 ?e, "Failed to convert versioned asset",
3419 );
3420 TrustedQueryApiError::VersionedAssetConversionFailed
3421 })?;
3422 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
3423 }
3424
3425 pub fn authorized_aliasers(
3427 target: VersionedLocation,
3428 ) -> Result<Vec<OriginAliaser>, AuthorizedAliasersApiError> {
3429 let desired_version = target.identify_version();
3430 let target: VersionedLocation = target.into_version(XCM_VERSION).map_err(|e| {
3432 tracing::debug!(
3433 target: "xcm::pallet_xcm::authorized_aliasers",
3434 ?e, "Failed to convert versioned location",
3435 );
3436 AuthorizedAliasersApiError::LocationVersionConversionFailed
3437 })?;
3438 Ok(AuthorizedAliases::<T>::get(&target)
3439 .map(|authorized| {
3440 authorized
3441 .aliasers
3442 .into_iter()
3443 .filter_map(|aliaser| {
3444 let OriginAliaser { location, expiry } = aliaser;
3445 location
3446 .into_version(desired_version)
3447 .map(|location| OriginAliaser { location, expiry })
3448 .ok()
3449 })
3450 .collect()
3451 })
3452 .unwrap_or_default())
3453 }
3454
3455 pub fn is_authorized_alias(
3460 origin: VersionedLocation,
3461 target: VersionedLocation,
3462 ) -> Result<bool, AuthorizedAliasersApiError> {
3463 let desired_version = target.identify_version();
3464 let origin = origin.into_version(desired_version).map_err(|e| {
3465 tracing::debug!(
3466 target: "xcm::pallet_xcm::is_authorized_alias",
3467 ?e, "mismatching origin and target versions",
3468 );
3469 AuthorizedAliasersApiError::LocationVersionConversionFailed
3470 })?;
3471 Ok(Self::authorized_aliasers(target)?.into_iter().any(|aliaser| {
3472 aliaser.location == origin &&
3475 aliaser
3476 .expiry
3477 .map(|expiry| {
3478 frame_system::Pallet::<T>::current_block_number().saturated_into::<u64>() <
3479 expiry
3480 })
3481 .unwrap_or(true)
3482 }))
3483 }
3484
3485 fn do_new_query(
3487 responder: impl Into<Location>,
3488 maybe_notify: Option<(u8, u8)>,
3489 timeout: BlockNumberFor<T>,
3490 match_querier: impl Into<Location>,
3491 ) -> u64 {
3492 QueryCounter::<T>::mutate(|q| {
3493 let r = *q;
3494 q.saturating_inc();
3495 Queries::<T>::insert(
3496 r,
3497 QueryStatus::Pending {
3498 responder: responder.into().into(),
3499 maybe_match_querier: Some(match_querier.into().into()),
3500 maybe_notify,
3501 timeout,
3502 },
3503 );
3504 r
3505 })
3506 }
3507
3508 pub fn report_outcome_notify(
3531 message: &mut Xcm<()>,
3532 responder: impl Into<Location>,
3533 notify: impl Into<<T as Config>::RuntimeCall>,
3534 timeout: BlockNumberFor<T>,
3535 ) -> Result<(), XcmError> {
3536 let responder = responder.into();
3537 let destination = T::UniversalLocation::get().invert_target(&responder).map_err(|()| {
3538 tracing::debug!(
3539 target: "xcm::pallet_xcm::report_outcome_notify",
3540 "Failed to invert responder location to universal location",
3541 );
3542 XcmError::LocationNotInvertible
3543 })?;
3544 let notify: <T as Config>::RuntimeCall = notify.into();
3545 let max_weight = notify.get_dispatch_info().call_weight;
3546 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
3547 let response_info = QueryResponseInfo { destination, query_id, max_weight };
3548 let report_error = Xcm(vec![ReportError(response_info)]);
3549 message.0.insert(0, SetAppendix(report_error));
3550 Ok(())
3551 }
3552
3553 pub fn new_notify_query(
3556 responder: impl Into<Location>,
3557 notify: impl Into<<T as Config>::RuntimeCall>,
3558 timeout: BlockNumberFor<T>,
3559 match_querier: impl Into<Location>,
3560 ) -> u64 {
3561 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
3562 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
3563 );
3564 Self::do_new_query(responder, Some(notify), timeout, match_querier)
3565 }
3566
3567 fn note_unknown_version(dest: &Location) {
3570 tracing::trace!(
3571 target: "xcm::pallet_xcm::note_unknown_version",
3572 ?dest, "XCM version is unknown for destination"
3573 );
3574 let versioned_dest = VersionedLocation::from(dest.clone());
3575 VersionDiscoveryQueue::<T>::mutate(|q| {
3576 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
3577 q[index].1.saturating_inc();
3579 } else {
3580 let _ = q.try_push((versioned_dest, 1));
3581 }
3582 });
3583 }
3584
3585 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
3591 T::XcmExecutor::charge_fees(location.clone(), assets.clone()).map_err(|error| {
3592 tracing::debug!(
3593 target: "xcm::pallet_xcm::charge_fees", ?error,
3594 "Failed to charge fees for location with assets",
3595 );
3596 Error::<T>::FeesNotMet
3597 })?;
3598 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
3599 Ok(())
3600 }
3601
3602 #[cfg(any(feature = "try-runtime", test))]
3612 pub fn do_try_state() -> Result<(), TryRuntimeError> {
3613 use migration::data::NeedsMigration;
3614
3615 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
3619 {
3620 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
3621 } else {
3622 XCM_VERSION.saturating_sub(1)
3623 };
3624
3625 ensure!(
3627 !Queries::<T>::iter_values()
3628 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3629 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
3630 );
3631
3632 ensure!(
3634 !LockedFungibles::<T>::iter_values()
3635 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3636 TryRuntimeError::Other(
3637 "`LockedFungibles` data should be migrated to the higher xcm version!"
3638 )
3639 );
3640
3641 ensure!(
3643 !RemoteLockedFungibles::<T>::iter()
3644 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
3645 data.needs_migration(minimal_allowed_xcm_version)),
3646 TryRuntimeError::Other(
3647 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
3648 )
3649 );
3650
3651 if CurrentMigration::<T>::exists() {
3654 return Ok(());
3655 }
3656
3657 for v in 0..XCM_VERSION {
3659 ensure!(
3660 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
3661 TryRuntimeError::Other(
3662 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
3663 )
3664 );
3665 ensure!(
3666 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
3667 TryRuntimeError::Other(
3668 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
3669 )
3670 );
3671 ensure!(
3672 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
3673 TryRuntimeError::Other(
3674 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
3675 )
3676 );
3677 }
3678
3679 Ok(())
3680 }
3681}
3682
3683pub struct LockTicket<T: Config> {
3684 sovereign_account: T::AccountId,
3685 amount: BalanceOf<T>,
3686 unlocker: Location,
3687 item_index: Option<usize>,
3688}
3689
3690impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
3691 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3692 use xcm_executor::traits::LockError::UnexpectedState;
3693 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
3694 match self.item_index {
3695 Some(index) => {
3696 ensure!(locks.len() > index, UnexpectedState);
3697 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
3698 locks[index].0 = locks[index].0.max(self.amount);
3699 },
3700 None => {
3701 locks.try_push((self.amount, self.unlocker.into())).map_err(
3702 |(balance, location)| {
3703 tracing::debug!(
3704 target: "xcm::pallet_xcm::enact", ?balance, ?location,
3705 "Failed to lock fungibles",
3706 );
3707 UnexpectedState
3708 },
3709 )?;
3710 },
3711 }
3712 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3713 T::Currency::extend_lock(
3714 *b"py/xcmlk",
3715 &self.sovereign_account,
3716 self.amount,
3717 WithdrawReasons::all(),
3718 );
3719 Ok(())
3720 }
3721}
3722
3723pub struct UnlockTicket<T: Config> {
3724 sovereign_account: T::AccountId,
3725 amount: BalanceOf<T>,
3726 unlocker: Location,
3727}
3728
3729impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
3730 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3731 use xcm_executor::traits::LockError::UnexpectedState;
3732 let mut locks =
3733 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
3734 let mut maybe_remove_index = None;
3735 let mut locked = BalanceOf::<T>::zero();
3736 let mut found = false;
3737 for (i, x) in locks.iter_mut().enumerate() {
3740 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
3741 x.0 = x.0.saturating_sub(self.amount);
3742 if x.0.is_zero() {
3743 maybe_remove_index = Some(i);
3744 }
3745 found = true;
3746 }
3747 locked = locked.max(x.0);
3748 }
3749 ensure!(found, UnexpectedState);
3750 if let Some(remove_index) = maybe_remove_index {
3751 locks.swap_remove(remove_index);
3752 }
3753 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3754 let reasons = WithdrawReasons::all();
3755 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3756 Ok(())
3757 }
3758}
3759
3760pub struct ReduceTicket<T: Config> {
3761 key: (u32, T::AccountId, VersionedAssetId),
3762 amount: u128,
3763 locker: VersionedLocation,
3764 owner: VersionedLocation,
3765}
3766
3767impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3768 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3769 use xcm_executor::traits::LockError::UnexpectedState;
3770 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3771 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3772 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3773 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3774 if new_amount == 0 {
3775 RemoteLockedFungibles::<T>::remove(&self.key);
3776 } else {
3777 record.amount = new_amount;
3778 RemoteLockedFungibles::<T>::insert(&self.key, &record);
3779 }
3780 Ok(())
3781 }
3782}
3783
3784impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
3785 type LockTicket = LockTicket<T>;
3786 type UnlockTicket = UnlockTicket<T>;
3787 type ReduceTicket = ReduceTicket<T>;
3788
3789 fn prepare_lock(
3790 unlocker: Location,
3791 asset: Asset,
3792 owner: Location,
3793 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3794 use xcm_executor::traits::LockError::*;
3795 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3796 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3797 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3798 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3799 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3800 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3801 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3802 }
3803
3804 fn prepare_unlock(
3805 unlocker: Location,
3806 asset: Asset,
3807 owner: Location,
3808 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3809 use xcm_executor::traits::LockError::*;
3810 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3811 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3812 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3813 let item_index =
3814 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3815 ensure!(locks[item_index].0 >= amount, NotLocked);
3816 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3817 }
3818
3819 fn note_unlockable(
3820 locker: Location,
3821 asset: Asset,
3822 mut owner: Location,
3823 ) -> Result<(), xcm_executor::traits::LockError> {
3824 use xcm_executor::traits::LockError::*;
3825 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3826 let amount = match asset.fun {
3827 Fungible(a) => a,
3828 NonFungible(_) => return Err(Unimplemented),
3829 };
3830 owner.remove_network_id();
3831 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3832 let locker = locker.into();
3833 let owner = owner.into();
3834 let id: VersionedAssetId = asset.id.into();
3835 let key = (XCM_VERSION, account, id);
3836 let mut record =
3837 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3838 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3839 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3841 record.consumers = old.consumers;
3842 record.amount = record.amount.max(old.amount);
3843 }
3844 RemoteLockedFungibles::<T>::insert(&key, record);
3845 Ok(())
3846 }
3847
3848 fn prepare_reduce_unlockable(
3849 locker: Location,
3850 asset: Asset,
3851 mut owner: Location,
3852 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3853 use xcm_executor::traits::LockError::*;
3854 let amount = match asset.fun {
3855 Fungible(a) => a,
3856 NonFungible(_) => return Err(Unimplemented),
3857 };
3858 owner.remove_network_id();
3859 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3860 let locker = locker.into();
3861 let owner = owner.into();
3862 let id: VersionedAssetId = asset.id.into();
3863 let key = (XCM_VERSION, sovereign_account, id);
3864
3865 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3866 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3868 ensure!(record.amount >= amount, NotEnoughLocked);
3869 ensure!(
3870 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3871 InUse
3872 );
3873 Ok(ReduceTicket { key, amount, locker, owner })
3874 }
3875}
3876
3877impl<T: Config> WrapVersion for Pallet<T> {
3878 fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3879 dest: &Location,
3880 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3881 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3882 Self::get_version_for(dest)
3883 .or_else(|| {
3884 Self::note_unknown_version(dest);
3885 SafeXcmVersion::<T>::get()
3886 })
3887 .ok_or_else(|| {
3888 tracing::trace!(
3889 target: "xcm::pallet_xcm::wrap_version",
3890 ?dest, "Could not determine a version to wrap XCM for destination",
3891 );
3892 ()
3893 })
3894 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3895 }
3896}
3897
3898impl<T: Config> GetVersion for Pallet<T> {
3899 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3900 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3901 }
3902}
3903
3904impl<T: Config> VersionChangeNotifier for Pallet<T> {
3905 fn start(
3914 dest: &Location,
3915 query_id: QueryId,
3916 max_weight: Weight,
3917 _context: &XcmContext,
3918 ) -> XcmResult {
3919 let versioned_dest = LatestVersionedLocation(dest);
3920 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3921 ensure!(!already, XcmError::InvalidLocation);
3922
3923 let xcm_version = T::AdvertisedXcmVersion::get();
3924 let response = Response::Version(xcm_version);
3925 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3926 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3927 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3928 destination: dest.clone(),
3929 cost,
3930 message_id,
3931 });
3932
3933 let value = (query_id, max_weight, xcm_version);
3934 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3935 Ok(())
3936 }
3937
3938 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3941 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3942 Ok(())
3943 }
3944
3945 fn is_subscribed(dest: &Location) -> bool {
3947 let versioned_dest = LatestVersionedLocation(dest);
3948 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3949 }
3950}
3951
3952impl<T: Config> DropAssets for Pallet<T> {
3953 fn drop_assets(origin: &Location, holding: AssetsInHolding, _context: &XcmContext) -> Weight {
3954 if holding.is_empty() {
3955 return Weight::zero();
3956 }
3957 let assets: Vec<Asset> = holding.assets_iter().collect();
3958 holding.fungible.into_iter().for_each(|(_, mut accounting)| {
3963 accounting.forget_imbalance();
3964 });
3965 let versioned = VersionedAssets::from(Assets::from(assets));
3966 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3967 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3968 Self::deposit_event(Event::AssetsTrapped {
3969 hash,
3970 origin: origin.clone(),
3971 assets: versioned,
3972 });
3973 Weight::zero()
3975 }
3976}
3977
3978impl<T: Config> ClaimAssets for Pallet<T> {
3979 fn claim_assets(
3980 origin: &Location,
3981 ticket: &Location,
3982 assets: &Assets,
3983 context: &XcmContext,
3984 ) -> Option<AssetsInHolding> {
3985 let mut versioned = VersionedAssets::from(assets.clone());
3986 match ticket.unpack() {
3987 (0, [GeneralIndex(i)]) => {
3988 versioned = match versioned.into_version(*i as u32) {
3989 Ok(v) => v,
3990 Err(()) => return None,
3991 }
3992 },
3993 (0, []) => (),
3994 _ => return None,
3995 };
3996 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3997 match AssetTraps::<T>::get(hash) {
3998 0 => return None,
3999 1 => AssetTraps::<T>::remove(hash),
4000 n => AssetTraps::<T>::insert(hash, n - 1),
4001 }
4002 let mut claimed = AssetsInHolding::new();
4003 for asset in assets.inner() {
4004 match <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(asset, context)
4005 {
4006 Ok(minted) => {
4007 minted.fungible.iter().for_each(|(_, imbalance)| {
4018 let to_resolve = imbalance.unsafe_clone();
4019 core::mem::drop(to_resolve);
4020 });
4021 claimed.subsume_assets(minted)
4022 },
4023 Err(error) => tracing::debug!(
4024 target: "xcm::pallet_xcm::claim_assets",
4025 ?asset, ?error, "Asset claimed from trap but unable to mint."
4026 ),
4027 }
4028 }
4029 Self::deposit_event(Event::AssetsClaimed {
4030 hash,
4031 origin: origin.clone(),
4032 assets: versioned,
4033 });
4034 Some(claimed)
4035 }
4036}
4037
4038impl<T: Config> OnResponse for Pallet<T> {
4039 fn expecting_response(
4040 origin: &Location,
4041 query_id: QueryId,
4042 querier: Option<&Location>,
4043 ) -> bool {
4044 match Queries::<T>::get(query_id) {
4045 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) => {
4046 Location::try_from(responder).map_or(false, |r| origin == &r) &&
4047 maybe_match_querier.map_or(true, |match_querier| {
4048 Location::try_from(match_querier).map_or(false, |match_querier| {
4049 querier.map_or(false, |q| q == &match_querier)
4050 })
4051 })
4052 },
4053 Some(QueryStatus::VersionNotifier { origin: r, .. }) => {
4054 Location::try_from(r).map_or(false, |r| origin == &r)
4055 },
4056 _ => false,
4057 }
4058 }
4059
4060 fn on_response(
4061 origin: &Location,
4062 query_id: QueryId,
4063 querier: Option<&Location>,
4064 response: Response,
4065 max_weight: Weight,
4066 _context: &XcmContext,
4067 ) -> Weight {
4068 let origin = origin.clone();
4069 match (response, Queries::<T>::get(query_id)) {
4070 (
4071 Response::Version(v),
4072 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
4073 ) => {
4074 let origin: Location = match expected_origin.try_into() {
4075 Ok(o) if o == origin => o,
4076 Ok(o) => {
4077 Self::deposit_event(Event::InvalidResponder {
4078 origin: origin.clone(),
4079 query_id,
4080 expected_location: Some(o),
4081 });
4082 return Weight::zero();
4083 },
4084 _ => {
4085 Self::deposit_event(Event::InvalidResponder {
4086 origin: origin.clone(),
4087 query_id,
4088 expected_location: None,
4089 });
4090 return Weight::zero();
4092 },
4093 };
4094 if !is_active {
4096 Queries::<T>::insert(
4097 query_id,
4098 QueryStatus::VersionNotifier {
4099 origin: origin.clone().into(),
4100 is_active: true,
4101 },
4102 );
4103 }
4104 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
4106 Self::deposit_event(Event::SupportedVersionChanged {
4107 location: origin,
4108 version: v,
4109 });
4110 Weight::zero()
4111 },
4112 (
4113 response,
4114 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
4115 ) => {
4116 if let Some(match_querier) = maybe_match_querier {
4117 let match_querier = match Location::try_from(match_querier) {
4118 Ok(mq) => mq,
4119 Err(_) => {
4120 Self::deposit_event(Event::InvalidQuerierVersion {
4121 origin: origin.clone(),
4122 query_id,
4123 });
4124 return Weight::zero();
4125 },
4126 };
4127 if querier.map_or(true, |q| q != &match_querier) {
4128 Self::deposit_event(Event::InvalidQuerier {
4129 origin: origin.clone(),
4130 query_id,
4131 expected_querier: match_querier,
4132 maybe_actual_querier: querier.cloned(),
4133 });
4134 return Weight::zero();
4135 }
4136 }
4137 let responder = match Location::try_from(responder) {
4138 Ok(r) => r,
4139 Err(_) => {
4140 Self::deposit_event(Event::InvalidResponderVersion {
4141 origin: origin.clone(),
4142 query_id,
4143 });
4144 return Weight::zero();
4145 },
4146 };
4147 if origin != responder {
4148 Self::deposit_event(Event::InvalidResponder {
4149 origin: origin.clone(),
4150 query_id,
4151 expected_location: Some(responder),
4152 });
4153 return Weight::zero();
4154 }
4155 match maybe_notify {
4156 Some((pallet_index, call_index)) => {
4157 let bare = (pallet_index, call_index, query_id, response);
4161 if let Ok(call) = bare.using_encoded(|mut bytes| {
4162 <T as Config>::RuntimeCall::decode(&mut bytes)
4163 }) {
4164 Queries::<T>::remove(query_id);
4165 let weight = call.get_dispatch_info().call_weight;
4166 if weight.any_gt(max_weight) {
4167 let e = Event::NotifyOverweight {
4168 query_id,
4169 pallet_index,
4170 call_index,
4171 actual_weight: weight,
4172 max_budgeted_weight: max_weight,
4173 };
4174 Self::deposit_event(e);
4175 return Weight::zero();
4176 }
4177 let dispatch_origin = Origin::Response(origin.clone()).into();
4178 match call.dispatch(dispatch_origin) {
4179 Ok(post_info) => {
4180 let e = Event::Notified { query_id, pallet_index, call_index };
4181 Self::deposit_event(e);
4182 post_info.actual_weight
4183 },
4184 Err(error_and_info) => {
4185 let e = Event::NotifyDispatchError {
4186 query_id,
4187 pallet_index,
4188 call_index,
4189 };
4190 Self::deposit_event(e);
4191 error_and_info.post_info.actual_weight
4194 },
4195 }
4196 .unwrap_or(weight)
4197 } else {
4198 let e =
4199 Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
4200 Self::deposit_event(e);
4201 Weight::zero()
4202 }
4203 },
4204 None => {
4205 let e = Event::ResponseReady { query_id, response: response.clone() };
4206 Self::deposit_event(e);
4207 let at = frame_system::Pallet::<T>::current_block_number();
4208 let response = response.into();
4209 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
4210 Weight::zero()
4211 },
4212 }
4213 },
4214 _ => {
4215 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
4216 Self::deposit_event(e);
4217 Weight::zero()
4218 },
4219 }
4220 }
4221}
4222
4223impl<T: Config> CheckSuspension for Pallet<T> {
4224 fn is_suspended<Call>(
4225 _origin: &Location,
4226 _instructions: &mut [Instruction<Call>],
4227 _max_weight: Weight,
4228 _properties: &mut Properties,
4229 ) -> bool {
4230 XcmExecutionSuspended::<T>::get()
4231 }
4232}
4233
4234impl<T: Config> RecordXcm for Pallet<T> {
4235 fn should_record() -> bool {
4236 ShouldRecordXcm::<T>::get()
4237 }
4238
4239 fn set_record_xcm(enabled: bool) {
4240 ShouldRecordXcm::<T>::put(enabled);
4241 }
4242
4243 fn recorded_xcm() -> Option<Xcm<()>> {
4244 RecordedXcm::<T>::get()
4245 }
4246
4247 fn record(xcm: Xcm<()>) {
4248 RecordedXcm::<T>::put(xcm);
4249 }
4250}
4251
4252pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4256where
4257 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4258{
4259 match o.into() {
4260 Ok(Origin::Xcm(location)) => Ok(location),
4261 _ => Err(BadOrigin),
4262 }
4263}
4264
4265pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4269where
4270 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4271{
4272 match o.into() {
4273 Ok(Origin::Response(location)) => Ok(location),
4274 _ => Err(BadOrigin),
4275 }
4276}
4277
4278pub struct AuthorizedAliasers<T>(PhantomData<T>);
4284impl<L: Into<VersionedLocation> + Clone, T: Config> ContainsPair<L, L> for AuthorizedAliasers<T> {
4285 fn contains(origin: &L, target: &L) -> bool {
4286 let origin: VersionedLocation = origin.clone().into();
4287 let target: VersionedLocation = target.clone().into();
4288 tracing::trace!(target: "xcm::pallet_xcm::AuthorizedAliasers::contains", ?origin, ?target);
4289 Pallet::<T>::is_authorized_alias(origin, target).unwrap_or(false)
4292 }
4293}
4294
4295pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4300impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
4301 for IsMajorityOfBody<Prefix, Body>
4302{
4303 fn contains(l: &Location) -> bool {
4304 let maybe_suffix = l.match_and_split(&Prefix::get());
4305 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
4306 }
4307}
4308
4309pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4313impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
4314 fn contains(l: &Location) -> bool {
4315 let maybe_suffix = l.match_and_split(&Prefix::get());
4316 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
4317 }
4318}
4319
4320pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
4323impl<
4324 O: OriginTrait + From<Origin>,
4325 F: Contains<L>,
4326 L: TryFrom<Location> + TryInto<Location> + Clone,
4327 > EnsureOrigin<O> for EnsureXcm<F, L>
4328where
4329 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4330{
4331 type Success = L;
4332
4333 fn try_origin(outer: O) -> Result<Self::Success, O> {
4334 match outer.caller().try_into() {
4335 Ok(Origin::Xcm(ref location)) => {
4336 if let Ok(location) = location.clone().try_into() {
4337 if F::contains(&location) {
4338 return Ok(location);
4339 }
4340 }
4341 },
4342 _ => (),
4343 }
4344
4345 Err(outer)
4346 }
4347
4348 #[cfg(feature = "runtime-benchmarks")]
4349 fn try_successful_origin() -> Result<O, ()> {
4350 Ok(O::from(Origin::Xcm(Here.into())))
4351 }
4352}
4353
4354pub struct EnsureResponse<F>(PhantomData<F>);
4357impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
4358where
4359 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4360{
4361 type Success = Location;
4362
4363 fn try_origin(outer: O) -> Result<Self::Success, O> {
4364 match outer.caller().try_into() {
4365 Ok(Origin::Response(responder)) => return Ok(responder.clone()),
4366 _ => (),
4367 }
4368
4369 Err(outer)
4370 }
4371
4372 #[cfg(feature = "runtime-benchmarks")]
4373 fn try_successful_origin() -> Result<O, ()> {
4374 Ok(O::from(Origin::Response(Here.into())))
4375 }
4376}
4377
4378pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
4381impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
4382 for XcmPassthrough<RuntimeOrigin>
4383{
4384 fn convert_origin(
4385 origin: impl Into<Location>,
4386 kind: OriginKind,
4387 ) -> Result<RuntimeOrigin, Location> {
4388 let origin = origin.into();
4389 match kind {
4390 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
4391 _ => Err(origin),
4392 }
4393 }
4394}