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
31extern crate alloc;
32
33use alloc::{boxed::Box, vec, vec::Vec};
34use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
35use core::{marker::PhantomData, result::Result};
36use frame_support::{
37 dispatch::{
38 DispatchErrorWithPostInfo, GetDispatchInfo, PostDispatchInfo, WithPostDispatchInfo,
39 },
40 pallet_prelude::*,
41 traits::{
42 Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Get, LockableCurrency,
43 OriginTrait, WithdrawReasons,
44 },
45 PalletId,
46};
47use frame_system::pallet_prelude::{BlockNumberFor, *};
48pub use pallet::*;
49use scale_info::TypeInfo;
50use sp_runtime::{
51 traits::{
52 AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
53 Saturating, Zero,
54 },
55 Either, RuntimeDebug,
56};
57use xcm::{latest::QueryResponseInfo, prelude::*};
58use xcm_builder::{
59 ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
60 QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
61};
62use xcm_executor::{
63 traits::{
64 AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
65 DropAssets, MatchesFungible, OnResponse, Properties, QueryHandler, QueryResponseStatus,
66 RecordXcm, TransactAsset, TransferType, VersionChangeNotifier, WeightBounds,
67 XcmAssetTransfers,
68 },
69 AssetsInHolding,
70};
71use xcm_runtime_apis::{
72 dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
73 fees::Error as XcmPaymentApiError,
74};
75
76#[cfg(any(feature = "try-runtime", test))]
77use sp_runtime::TryRuntimeError;
78
79pub trait WeightInfo {
80 fn send() -> Weight;
81 fn teleport_assets() -> Weight;
82 fn reserve_transfer_assets() -> Weight;
83 fn transfer_assets() -> Weight;
84 fn execute() -> Weight;
85 fn force_xcm_version() -> Weight;
86 fn force_default_xcm_version() -> Weight;
87 fn force_subscribe_version_notify() -> Weight;
88 fn force_unsubscribe_version_notify() -> Weight;
89 fn force_suspension() -> Weight;
90 fn migrate_supported_version() -> Weight;
91 fn migrate_version_notifiers() -> Weight;
92 fn already_notified_target() -> Weight;
93 fn notify_current_targets() -> Weight;
94 fn notify_target_migration_fail() -> Weight;
95 fn migrate_version_notify_targets() -> Weight;
96 fn migrate_and_notify_old_targets() -> Weight;
97 fn new_query() -> Weight;
98 fn take_response() -> Weight;
99 fn claim_assets() -> Weight;
100}
101
102pub struct TestWeightInfo;
104impl WeightInfo for TestWeightInfo {
105 fn send() -> Weight {
106 Weight::from_parts(100_000_000, 0)
107 }
108
109 fn teleport_assets() -> Weight {
110 Weight::from_parts(100_000_000, 0)
111 }
112
113 fn reserve_transfer_assets() -> Weight {
114 Weight::from_parts(100_000_000, 0)
115 }
116
117 fn transfer_assets() -> Weight {
118 Weight::from_parts(100_000_000, 0)
119 }
120
121 fn execute() -> Weight {
122 Weight::from_parts(100_000_000, 0)
123 }
124
125 fn force_xcm_version() -> Weight {
126 Weight::from_parts(100_000_000, 0)
127 }
128
129 fn force_default_xcm_version() -> Weight {
130 Weight::from_parts(100_000_000, 0)
131 }
132
133 fn force_subscribe_version_notify() -> Weight {
134 Weight::from_parts(100_000_000, 0)
135 }
136
137 fn force_unsubscribe_version_notify() -> Weight {
138 Weight::from_parts(100_000_000, 0)
139 }
140
141 fn force_suspension() -> Weight {
142 Weight::from_parts(100_000_000, 0)
143 }
144
145 fn migrate_supported_version() -> Weight {
146 Weight::from_parts(100_000_000, 0)
147 }
148
149 fn migrate_version_notifiers() -> Weight {
150 Weight::from_parts(100_000_000, 0)
151 }
152
153 fn already_notified_target() -> Weight {
154 Weight::from_parts(100_000_000, 0)
155 }
156
157 fn notify_current_targets() -> Weight {
158 Weight::from_parts(100_000_000, 0)
159 }
160
161 fn notify_target_migration_fail() -> Weight {
162 Weight::from_parts(100_000_000, 0)
163 }
164
165 fn migrate_version_notify_targets() -> Weight {
166 Weight::from_parts(100_000_000, 0)
167 }
168
169 fn migrate_and_notify_old_targets() -> Weight {
170 Weight::from_parts(100_000_000, 0)
171 }
172
173 fn new_query() -> Weight {
174 Weight::from_parts(100_000_000, 0)
175 }
176
177 fn take_response() -> Weight {
178 Weight::from_parts(100_000_000, 0)
179 }
180
181 fn claim_assets() -> Weight {
182 Weight::from_parts(100_000_000, 0)
183 }
184}
185
186#[frame_support::pallet]
187pub mod pallet {
188 use super::*;
189 use frame_support::{
190 dispatch::{GetDispatchInfo, PostDispatchInfo},
191 parameter_types,
192 };
193 use frame_system::Config as SysConfig;
194 use sp_core::H256;
195 use sp_runtime::traits::Dispatchable;
196 use xcm_executor::traits::{MatchesFungible, WeightBounds};
197
198 parameter_types! {
199 pub const CurrentXcmVersion: u32 = XCM_VERSION;
202 }
203
204 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
205
206 #[pallet::pallet]
207 #[pallet::storage_version(STORAGE_VERSION)]
208 #[pallet::without_storage_info]
209 pub struct Pallet<T>(_);
210
211 pub type BalanceOf<T> =
212 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
213
214 #[pallet::config]
215 pub trait Config: frame_system::Config {
217 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
219
220 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
223
224 type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
226
227 type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
230
231 type XcmRouter: SendXcm;
233
234 type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
238
239 type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
241
242 type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers;
244
245 type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
247
248 type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
251
252 type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
254
255 #[pallet::constant]
257 type UniversalLocation: Get<InteriorLocation>;
258
259 type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
261
262 type RuntimeCall: Parameter
264 + GetDispatchInfo
265 + Dispatchable<
266 RuntimeOrigin = <Self as Config>::RuntimeOrigin,
267 PostInfo = PostDispatchInfo,
268 >;
269
270 const VERSION_DISCOVERY_QUEUE_SIZE: u32;
271
272 #[pallet::constant]
275 type AdvertisedXcmVersion: Get<XcmVersion>;
276
277 type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
279
280 type TrustedLockers: ContainsPair<Location, Asset>;
283
284 type SovereignAccountOf: ConvertLocation<Self::AccountId>;
286
287 #[pallet::constant]
289 type MaxLockers: Get<u32>;
290
291 #[pallet::constant]
293 type MaxRemoteLockConsumers: Get<u32>;
294
295 type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
297
298 type WeightInfo: WeightInfo;
300 }
301
302 impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
303 fn execute() -> Weight {
304 T::WeightInfo::execute()
305 }
306 }
307
308 impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
309 type WeightInfo = Self;
310 fn execute(
311 origin: OriginFor<T>,
312 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
313 max_weight: Weight,
314 ) -> Result<Weight, DispatchErrorWithPostInfo> {
315 log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
316 let outcome = (|| {
317 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
318 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
319 let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
320 let value = (origin_location, message);
321 ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
322 let (origin_location, message) = value;
323 Ok(T::XcmExecutor::prepare_and_execute(
324 origin_location,
325 message,
326 &mut hash,
327 max_weight,
328 max_weight,
329 ))
330 })()
331 .map_err(|e: DispatchError| {
332 e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
333 })?;
334
335 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
336 let weight_used = outcome.weight_used();
337 outcome.ensure_complete().map_err(|error| {
338 log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
339 Error::<T>::LocalExecutionIncomplete.with_weight(
340 weight_used.saturating_add(
341 <Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
342 ),
343 )
344 })?;
345 Ok(weight_used)
346 }
347 }
348
349 impl<T: Config> SendControllerWeightInfo for Pallet<T> {
350 fn send() -> Weight {
351 T::WeightInfo::send()
352 }
353 }
354
355 impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
356 type WeightInfo = Self;
357 fn send(
358 origin: OriginFor<T>,
359 dest: Box<VersionedLocation>,
360 message: Box<VersionedXcm<()>>,
361 ) -> Result<XcmHash, DispatchError> {
362 let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
363 let interior: Junctions =
364 origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
365 let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
366 let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
367
368 let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
369 .map_err(|error| {
370 tracing::error!(target: "xcm::pallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
371 Error::<T>::from(error)
372 })?;
373 let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
374 Self::deposit_event(e);
375 Ok(message_id)
376 }
377 }
378
379 impl<T: Config> QueryControllerWeightInfo for Pallet<T> {
380 fn query() -> Weight {
381 T::WeightInfo::new_query()
382 }
383 fn take_response() -> Weight {
384 T::WeightInfo::take_response()
385 }
386 }
387
388 impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pallet<T> {
389 type WeightInfo = Self;
390
391 fn query(
392 origin: OriginFor<T>,
393 timeout: BlockNumberFor<T>,
394 match_querier: VersionedLocation,
395 ) -> Result<QueryId, DispatchError> {
396 let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
397 let query_id = <Self as QueryHandler>::new_query(
398 responder,
399 timeout,
400 Location::try_from(match_querier)
401 .map_err(|_| Into::<DispatchError>::into(Error::<T>::BadVersion))?,
402 );
403
404 Ok(query_id)
405 }
406 }
407
408 #[pallet::event]
409 #[pallet::generate_deposit(pub(super) fn deposit_event)]
410 pub enum Event<T: Config> {
411 Attempted { outcome: xcm::latest::Outcome },
413 Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
415 UnexpectedResponse { origin: Location, query_id: QueryId },
419 ResponseReady { query_id: QueryId, response: Response },
422 Notified { query_id: QueryId, pallet_index: u8, call_index: u8 },
425 NotifyOverweight {
429 query_id: QueryId,
430 pallet_index: u8,
431 call_index: u8,
432 actual_weight: Weight,
433 max_budgeted_weight: Weight,
434 },
435 NotifyDispatchError { query_id: QueryId, pallet_index: u8, call_index: u8 },
438 NotifyDecodeFailed { query_id: QueryId, pallet_index: u8, call_index: u8 },
442 InvalidResponder {
446 origin: Location,
447 query_id: QueryId,
448 expected_location: Option<Location>,
449 },
450 InvalidResponderVersion { origin: Location, query_id: QueryId },
458 ResponseTaken { query_id: QueryId },
460 AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
462 VersionChangeNotified {
466 destination: Location,
467 result: XcmVersion,
468 cost: Assets,
469 message_id: XcmHash,
470 },
471 SupportedVersionChanged { location: Location, version: XcmVersion },
474 NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
477 NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
480 InvalidQuerierVersion { origin: Location, query_id: QueryId },
488 InvalidQuerier {
492 origin: Location,
493 query_id: QueryId,
494 expected_querier: Location,
495 maybe_actual_querier: Option<Location>,
496 },
497 VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
500 VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
502 VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
505 FeesPaid { paying: Location, fees: Assets },
507 AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
509 VersionMigrationFinished { version: XcmVersion },
511 }
512
513 #[pallet::origin]
514 #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
515 pub enum Origin {
516 Xcm(Location),
518 Response(Location),
520 }
521 impl From<Location> for Origin {
522 fn from(location: Location) -> Origin {
523 Origin::Xcm(location)
524 }
525 }
526
527 #[pallet::error]
528 pub enum Error<T> {
529 Unreachable,
532 SendFailure,
535 Filtered,
537 UnweighableMessage,
539 DestinationNotInvertible,
541 Empty,
543 CannotReanchor,
545 TooManyAssets,
547 InvalidOrigin,
549 BadVersion,
551 BadLocation,
554 NoSubscription,
556 AlreadySubscribed,
558 CannotCheckOutTeleport,
560 LowBalance,
562 TooManyLocks,
564 AccountNotSovereign,
566 FeesNotMet,
568 LockNotFound,
570 InUse,
572 #[codec(index = 21)]
574 InvalidAssetUnknownReserve,
575 #[codec(index = 22)]
577 InvalidAssetUnsupportedReserve,
578 #[codec(index = 23)]
580 TooManyReserves,
581 #[codec(index = 24)]
583 LocalExecutionIncomplete,
584 }
585
586 impl<T: Config> From<SendError> for Error<T> {
587 fn from(e: SendError) -> Self {
588 match e {
589 SendError::Fees => Error::<T>::FeesNotMet,
590 SendError::NotApplicable => Error::<T>::Unreachable,
591 _ => Error::<T>::SendFailure,
592 }
593 }
594 }
595
596 impl<T: Config> From<AssetTransferError> for Error<T> {
597 fn from(e: AssetTransferError) -> Self {
598 match e {
599 AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
600 }
601 }
602 }
603
604 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
606 pub enum QueryStatus<BlockNumber> {
607 Pending {
609 responder: VersionedLocation,
612 maybe_match_querier: Option<VersionedLocation>,
615 maybe_notify: Option<(u8, u8)>,
616 timeout: BlockNumber,
617 },
618 VersionNotifier { origin: VersionedLocation, is_active: bool },
620 Ready { response: VersionedResponse, at: BlockNumber },
622 }
623
624 #[derive(Copy, Clone)]
625 pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
626 impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
627 impl<'a> Encode for LatestVersionedLocation<'a> {
628 fn encode(&self) -> Vec<u8> {
629 let mut r = VersionedLocation::from(Location::default()).encode();
630 r.truncate(1);
631 self.0.using_encoded(|d| r.extend_from_slice(d));
632 r
633 }
634 }
635
636 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
637 pub enum VersionMigrationStage {
638 MigrateSupportedVersion,
639 MigrateVersionNotifiers,
640 NotifyCurrentTargets(Option<Vec<u8>>),
641 MigrateAndNotifyOldTargets,
642 }
643
644 impl Default for VersionMigrationStage {
645 fn default() -> Self {
646 Self::MigrateSupportedVersion
647 }
648 }
649
650 #[pallet::storage]
652 pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
653
654 #[pallet::storage]
656 #[pallet::getter(fn query)]
657 pub(super) type Queries<T: Config> =
658 StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
659
660 #[pallet::storage]
665 #[pallet::getter(fn asset_trap)]
666 pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
667
668 #[pallet::storage]
671 #[pallet::whitelist_storage]
672 pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
673
674 #[pallet::storage]
676 pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
677 _,
678 Twox64Concat,
679 XcmVersion,
680 Blake2_128Concat,
681 VersionedLocation,
682 XcmVersion,
683 OptionQuery,
684 >;
685
686 #[pallet::storage]
688 pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
689 _,
690 Twox64Concat,
691 XcmVersion,
692 Blake2_128Concat,
693 VersionedLocation,
694 QueryId,
695 OptionQuery,
696 >;
697
698 #[pallet::storage]
701 pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
702 _,
703 Twox64Concat,
704 XcmVersion,
705 Blake2_128Concat,
706 VersionedLocation,
707 (QueryId, Weight, XcmVersion),
708 OptionQuery,
709 >;
710
711 pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
712 impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
713 fn get() -> u32 {
714 T::VERSION_DISCOVERY_QUEUE_SIZE
715 }
716 }
717
718 #[pallet::storage]
722 #[pallet::whitelist_storage]
723 pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
724 _,
725 BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
726 ValueQuery,
727 >;
728
729 #[pallet::storage]
731 pub(super) type CurrentMigration<T: Config> =
732 StorageValue<_, VersionMigrationStage, OptionQuery>;
733
734 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
735 #[scale_info(skip_type_params(MaxConsumers))]
736 pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
737 pub amount: u128,
739 pub owner: VersionedLocation,
741 pub locker: VersionedLocation,
743 pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
747 }
748
749 impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
750 pub fn amount_held(&self) -> Option<u128> {
753 self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
754 }
755 }
756
757 #[pallet::storage]
759 pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
760 _,
761 (
762 NMapKey<Twox64Concat, XcmVersion>,
763 NMapKey<Blake2_128Concat, T::AccountId>,
764 NMapKey<Blake2_128Concat, VersionedAssetId>,
765 ),
766 RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
767 OptionQuery,
768 >;
769
770 #[pallet::storage]
772 pub(super) type LockedFungibles<T: Config> = StorageMap<
773 _,
774 Blake2_128Concat,
775 T::AccountId,
776 BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
777 OptionQuery,
778 >;
779
780 #[pallet::storage]
782 pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
783
784 #[pallet::storage]
792 pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
793
794 #[pallet::storage]
801 pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
802
803 #[pallet::genesis_config]
804 pub struct GenesisConfig<T: Config> {
805 #[serde(skip)]
806 pub _config: core::marker::PhantomData<T>,
807 pub safe_xcm_version: Option<XcmVersion>,
809 }
810
811 impl<T: Config> Default for GenesisConfig<T> {
812 fn default() -> Self {
813 Self { safe_xcm_version: Some(XCM_VERSION), _config: Default::default() }
814 }
815 }
816
817 #[pallet::genesis_build]
818 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
819 fn build(&self) {
820 SafeXcmVersion::<T>::set(self.safe_xcm_version);
821 }
822 }
823
824 #[pallet::hooks]
825 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
826 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
827 let mut weight_used = Weight::zero();
828 if let Some(migration) = CurrentMigration::<T>::get() {
829 let max_weight = T::BlockWeights::get().max_block / 10;
831 let (w, maybe_migration) = Self::check_xcm_version_change(migration, max_weight);
832 if maybe_migration.is_none() {
833 Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
834 }
835 CurrentMigration::<T>::set(maybe_migration);
836 weight_used.saturating_accrue(w);
837 }
838
839 let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
842 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
844 q.sort_by_key(|i| i.1);
845 while let Some((versioned_dest, _)) = q.pop() {
846 if let Ok(dest) = Location::try_from(versioned_dest) {
847 if Self::request_version_notify(dest).is_ok() {
848 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
850 break
851 }
852 }
853 }
854 if let Ok(q) = BoundedVec::try_from(q) {
857 VersionDiscoveryQueue::<T>::put(q);
858 }
859 weight_used
860 }
861
862 #[cfg(feature = "try-runtime")]
863 fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
864 Self::do_try_state()
865 }
866 }
867
868 pub mod migrations {
869 use super::*;
870 use frame_support::traits::{PalletInfoAccess, StorageVersion};
871
872 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
873 enum QueryStatusV0<BlockNumber> {
874 Pending {
875 responder: VersionedLocation,
876 maybe_notify: Option<(u8, u8)>,
877 timeout: BlockNumber,
878 },
879 VersionNotifier {
880 origin: VersionedLocation,
881 is_active: bool,
882 },
883 Ready {
884 response: VersionedResponse,
885 at: BlockNumber,
886 },
887 }
888 impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
889 fn from(old: QueryStatusV0<B>) -> Self {
890 use QueryStatusV0::*;
891 match old {
892 Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
893 responder,
894 maybe_notify,
895 timeout,
896 maybe_match_querier: Some(Location::here().into()),
897 },
898 VersionNotifier { origin, is_active } =>
899 QueryStatus::VersionNotifier { origin, is_active },
900 Ready { response, at } => QueryStatus::Ready { response, at },
901 }
902 }
903 }
904
905 pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
906 ) -> frame_support::weights::Weight {
907 let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
908 log::info!(
909 target: "runtime::xcm",
910 "Running migration storage v1 for xcm with storage version {:?}",
911 on_chain_storage_version,
912 );
913
914 if on_chain_storage_version < 1 {
915 let mut count = 0;
916 Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
917 count += 1;
918 Some(value.into())
919 });
920 StorageVersion::new(1).put::<P>();
921 log::info!(
922 target: "runtime::xcm",
923 "Running migration storage v1 for xcm with storage version {:?} was complete",
924 on_chain_storage_version,
925 );
926 T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
928 } else {
929 log::warn!(
930 target: "runtime::xcm",
931 "Attempted to apply migration to v1 but failed because storage version is {:?}",
932 on_chain_storage_version,
933 );
934 T::DbWeight::get().reads(1)
935 }
936 }
937 }
938
939 #[pallet::call(weight(<T as Config>::WeightInfo))]
940 impl<T: Config> Pallet<T> {
941 #[pallet::call_index(0)]
942 pub fn send(
943 origin: OriginFor<T>,
944 dest: Box<VersionedLocation>,
945 message: Box<VersionedXcm<()>>,
946 ) -> DispatchResult {
947 <Self as SendController<_>>::send(origin, dest, message)?;
948 Ok(())
949 }
950
951 #[pallet::call_index(1)]
970 #[allow(deprecated)]
971 #[deprecated(
972 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
973 )]
974 pub fn teleport_assets(
975 origin: OriginFor<T>,
976 dest: Box<VersionedLocation>,
977 beneficiary: Box<VersionedLocation>,
978 assets: Box<VersionedAssets>,
979 fee_asset_item: u32,
980 ) -> DispatchResult {
981 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_item, Unlimited)
982 }
983
984 #[pallet::call_index(2)]
1015 #[allow(deprecated)]
1016 #[deprecated(
1017 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1018 )]
1019 pub fn reserve_transfer_assets(
1020 origin: OriginFor<T>,
1021 dest: Box<VersionedLocation>,
1022 beneficiary: Box<VersionedLocation>,
1023 assets: Box<VersionedAssets>,
1024 fee_asset_item: u32,
1025 ) -> DispatchResult {
1026 Self::do_reserve_transfer_assets(
1027 origin,
1028 dest,
1029 beneficiary,
1030 assets,
1031 fee_asset_item,
1032 Unlimited,
1033 )
1034 }
1035
1036 #[pallet::call_index(3)]
1045 #[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1046 pub fn execute(
1047 origin: OriginFor<T>,
1048 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1049 max_weight: Weight,
1050 ) -> DispatchResultWithPostInfo {
1051 let weight_used =
1052 <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1053 Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1054 }
1055
1056 #[pallet::call_index(4)]
1063 pub fn force_xcm_version(
1064 origin: OriginFor<T>,
1065 location: Box<Location>,
1066 version: XcmVersion,
1067 ) -> DispatchResult {
1068 T::AdminOrigin::ensure_origin(origin)?;
1069 let location = *location;
1070 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1071 Self::deposit_event(Event::SupportedVersionChanged { location, version });
1072 Ok(())
1073 }
1074
1075 #[pallet::call_index(5)]
1081 pub fn force_default_xcm_version(
1082 origin: OriginFor<T>,
1083 maybe_xcm_version: Option<XcmVersion>,
1084 ) -> DispatchResult {
1085 T::AdminOrigin::ensure_origin(origin)?;
1086 SafeXcmVersion::<T>::set(maybe_xcm_version);
1087 Ok(())
1088 }
1089
1090 #[pallet::call_index(6)]
1095 pub fn force_subscribe_version_notify(
1096 origin: OriginFor<T>,
1097 location: Box<VersionedLocation>,
1098 ) -> DispatchResult {
1099 T::AdminOrigin::ensure_origin(origin)?;
1100 let location: Location =
1101 (*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
1102 Self::request_version_notify(location).map_err(|e| {
1103 match e {
1104 XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1105 _ => Error::<T>::InvalidOrigin,
1106 }
1107 .into()
1108 })
1109 }
1110
1111 #[pallet::call_index(7)]
1118 pub fn force_unsubscribe_version_notify(
1119 origin: OriginFor<T>,
1120 location: Box<VersionedLocation>,
1121 ) -> DispatchResult {
1122 T::AdminOrigin::ensure_origin(origin)?;
1123 let location: Location =
1124 (*location).try_into().map_err(|()| Error::<T>::BadLocation)?;
1125 Self::unrequest_version_notify(location).map_err(|e| {
1126 match e {
1127 XcmError::InvalidLocation => Error::<T>::NoSubscription,
1128 _ => Error::<T>::InvalidOrigin,
1129 }
1130 .into()
1131 })
1132 }
1133
1134 #[pallet::call_index(8)]
1165 #[pallet::weight(T::WeightInfo::reserve_transfer_assets())]
1166 pub fn limited_reserve_transfer_assets(
1167 origin: OriginFor<T>,
1168 dest: Box<VersionedLocation>,
1169 beneficiary: Box<VersionedLocation>,
1170 assets: Box<VersionedAssets>,
1171 fee_asset_item: u32,
1172 weight_limit: WeightLimit,
1173 ) -> DispatchResult {
1174 Self::do_reserve_transfer_assets(
1175 origin,
1176 dest,
1177 beneficiary,
1178 assets,
1179 fee_asset_item,
1180 weight_limit,
1181 )
1182 }
1183
1184 #[pallet::call_index(9)]
1203 #[pallet::weight(T::WeightInfo::teleport_assets())]
1204 pub fn limited_teleport_assets(
1205 origin: OriginFor<T>,
1206 dest: Box<VersionedLocation>,
1207 beneficiary: Box<VersionedLocation>,
1208 assets: Box<VersionedAssets>,
1209 fee_asset_item: u32,
1210 weight_limit: WeightLimit,
1211 ) -> DispatchResult {
1212 Self::do_teleport_assets(
1213 origin,
1214 dest,
1215 beneficiary,
1216 assets,
1217 fee_asset_item,
1218 weight_limit,
1219 )
1220 }
1221
1222 #[pallet::call_index(10)]
1227 pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1228 T::AdminOrigin::ensure_origin(origin)?;
1229 XcmExecutionSuspended::<T>::set(suspended);
1230 Ok(())
1231 }
1232
1233 #[pallet::call_index(11)]
1267 pub fn transfer_assets(
1268 origin: OriginFor<T>,
1269 dest: Box<VersionedLocation>,
1270 beneficiary: Box<VersionedLocation>,
1271 assets: Box<VersionedAssets>,
1272 fee_asset_item: u32,
1273 weight_limit: WeightLimit,
1274 ) -> DispatchResult {
1275 let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1276 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1277 let beneficiary: Location =
1278 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1279 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1280 log::debug!(
1281 target: "xcm::pallet_xcm::transfer_assets",
1282 "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}, weight_limit {:?}",
1283 origin, dest, beneficiary, assets, fee_asset_item, weight_limit,
1284 );
1285
1286 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1287 let assets = assets.into_inner();
1288 let fee_asset_item = fee_asset_item as usize;
1289 let (fees_transfer_type, assets_transfer_type) =
1291 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1292
1293 Self::ensure_network_asset_reserve_transfer_allowed(
1297 &assets,
1298 fee_asset_item,
1299 &assets_transfer_type,
1300 &fees_transfer_type,
1301 )?;
1302
1303 Self::do_transfer_assets(
1304 origin,
1305 dest,
1306 Either::Left(beneficiary),
1307 assets,
1308 assets_transfer_type,
1309 fee_asset_item,
1310 fees_transfer_type,
1311 weight_limit,
1312 )
1313 }
1314
1315 #[pallet::call_index(12)]
1322 pub fn claim_assets(
1323 origin: OriginFor<T>,
1324 assets: Box<VersionedAssets>,
1325 beneficiary: Box<VersionedLocation>,
1326 ) -> DispatchResult {
1327 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1328 log::debug!(target: "xcm::pallet_xcm::claim_assets", "origin: {:?}, assets: {:?}, beneficiary: {:?}", origin_location, assets, beneficiary);
1329 let assets_version = assets.identify_version();
1331 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1332 let number_of_assets = assets.len() as u32;
1333 let beneficiary: Location =
1334 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1335 let ticket: Location = GeneralIndex(assets_version as u128).into();
1336 let mut message = Xcm(vec![
1337 ClaimAsset { assets, ticket },
1338 DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1339 ]);
1340 let weight =
1341 T::Weigher::weight(&mut message).map_err(|()| Error::<T>::UnweighableMessage)?;
1342 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
1343 let outcome = T::XcmExecutor::prepare_and_execute(
1344 origin_location,
1345 message,
1346 &mut hash,
1347 weight,
1348 weight,
1349 );
1350 outcome.ensure_complete().map_err(|error| {
1351 log::error!(target: "xcm::pallet_xcm::claim_assets", "XCM execution failed with error: {:?}", error);
1352 Error::<T>::LocalExecutionIncomplete
1353 })?;
1354 Ok(())
1355 }
1356
1357 #[pallet::call_index(13)]
1406 #[pallet::weight(T::WeightInfo::transfer_assets())]
1407 pub fn transfer_assets_using_type_and_then(
1408 origin: OriginFor<T>,
1409 dest: Box<VersionedLocation>,
1410 assets: Box<VersionedAssets>,
1411 assets_transfer_type: Box<TransferType>,
1412 remote_fees_id: Box<VersionedAssetId>,
1413 fees_transfer_type: Box<TransferType>,
1414 custom_xcm_on_dest: Box<VersionedXcm<()>>,
1415 weight_limit: WeightLimit,
1416 ) -> DispatchResult {
1417 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1418 let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1419 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1420 let fees_id: AssetId =
1421 (*remote_fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?;
1422 let remote_xcm: Xcm<()> =
1423 (*custom_xcm_on_dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1424 log::debug!(
1425 target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
1426 "origin {origin_location:?}, dest {dest:?}, assets {assets:?} through {assets_transfer_type:?}, \
1427 remote_fees_id {fees_id:?} through {fees_transfer_type:?}, \
1428 custom_xcm_on_dest {remote_xcm:?}, weight-limit {weight_limit:?}",
1429 );
1430
1431 let assets = assets.into_inner();
1432 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1433
1434 let fee_asset_index =
1435 assets.iter().position(|a| a.id == fees_id).ok_or(Error::<T>::FeesNotMet)?;
1436 Self::do_transfer_assets(
1437 origin_location,
1438 dest,
1439 Either::Right(remote_xcm),
1440 assets,
1441 *assets_transfer_type,
1442 fee_asset_index,
1443 *fees_transfer_type,
1444 weight_limit,
1445 )
1446 }
1447 }
1448}
1449
1450const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1452
1453#[derive(Clone, PartialEq)]
1455enum FeesHandling<T: Config> {
1456 Batched { fees: Asset },
1458 Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1460}
1461
1462impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1463 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1464 match self {
1465 Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1466 Self::Separate { local_xcm, remote_xcm } => write!(
1467 f,
1468 "FeesHandling::Separate(local: {:?}, remote: {:?})",
1469 local_xcm, remote_xcm
1470 ),
1471 }
1472 }
1473}
1474
1475impl<T: Config> QueryHandler for Pallet<T> {
1476 type BlockNumber = BlockNumberFor<T>;
1477 type Error = XcmError;
1478 type UniversalLocation = T::UniversalLocation;
1479
1480 fn new_query(
1482 responder: impl Into<Location>,
1483 timeout: BlockNumberFor<T>,
1484 match_querier: impl Into<Location>,
1485 ) -> QueryId {
1486 Self::do_new_query(responder, None, timeout, match_querier)
1487 }
1488
1489 fn report_outcome(
1492 message: &mut Xcm<()>,
1493 responder: impl Into<Location>,
1494 timeout: Self::BlockNumber,
1495 ) -> Result<QueryId, Self::Error> {
1496 let responder = responder.into();
1497 let destination = Self::UniversalLocation::get()
1498 .invert_target(&responder)
1499 .map_err(|()| XcmError::LocationNotInvertible)?;
1500 let query_id = Self::new_query(responder, timeout, Here);
1501 let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1502 let report_error = Xcm(vec![ReportError(response_info)]);
1503 message.0.insert(0, SetAppendix(report_error));
1504 Ok(query_id)
1505 }
1506
1507 fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1509 match Queries::<T>::get(query_id) {
1510 Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1511 Ok(response) => {
1512 Queries::<T>::remove(query_id);
1513 Self::deposit_event(Event::ResponseTaken { query_id });
1514 QueryResponseStatus::Ready { response, at }
1515 },
1516 Err(_) => QueryResponseStatus::UnexpectedVersion,
1517 },
1518 Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
1519 Some(_) => QueryResponseStatus::UnexpectedVersion,
1520 None => QueryResponseStatus::NotFound,
1521 }
1522 }
1523
1524 #[cfg(feature = "runtime-benchmarks")]
1525 fn expect_response(id: QueryId, response: Response) {
1526 let response = response.into();
1527 Queries::<T>::insert(
1528 id,
1529 QueryStatus::Ready { response, at: frame_system::Pallet::<T>::block_number() },
1530 );
1531 }
1532}
1533
1534impl<T: Config> Pallet<T> {
1535 fn find_fee_and_assets_transfer_types(
1540 assets: &[Asset],
1541 fee_asset_item: usize,
1542 dest: &Location,
1543 ) -> Result<(TransferType, TransferType), Error<T>> {
1544 let mut fees_transfer_type = None;
1545 let mut assets_transfer_type = None;
1546 for (idx, asset) in assets.iter().enumerate() {
1547 if let Fungible(x) = asset.fun {
1548 ensure!(!x.is_zero(), Error::<T>::Empty);
1550 }
1551 let transfer_type =
1552 T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
1553 if idx == fee_asset_item {
1554 fees_transfer_type = Some(transfer_type);
1555 } else {
1556 if let Some(existing) = assets_transfer_type.as_ref() {
1557 ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
1560 } else {
1561 assets_transfer_type = Some(transfer_type);
1563 }
1564 }
1565 }
1566 if assets.len() == 1 {
1568 assets_transfer_type = fees_transfer_type.clone()
1569 }
1570 Ok((
1571 fees_transfer_type.ok_or(Error::<T>::Empty)?,
1572 assets_transfer_type.ok_or(Error::<T>::Empty)?,
1573 ))
1574 }
1575
1576 fn do_reserve_transfer_assets(
1577 origin: OriginFor<T>,
1578 dest: Box<VersionedLocation>,
1579 beneficiary: Box<VersionedLocation>,
1580 assets: Box<VersionedAssets>,
1581 fee_asset_item: u32,
1582 weight_limit: WeightLimit,
1583 ) -> DispatchResult {
1584 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1585 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1586 let beneficiary: Location =
1587 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1588 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1589 log::debug!(
1590 target: "xcm::pallet_xcm::do_reserve_transfer_assets",
1591 "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}",
1592 origin_location, dest, beneficiary, assets, fee_asset_item,
1593 );
1594
1595 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1596 let value = (origin_location, assets.into_inner());
1597 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1598 let (origin, assets) = value;
1599
1600 let fee_asset_item = fee_asset_item as usize;
1601 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1602
1603 let (fees_transfer_type, assets_transfer_type) =
1605 Self::find_fee_and_assets_transfer_types(&assets, fee_asset_item, &dest)?;
1606 ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
1608 ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
1610
1611 Self::ensure_network_asset_reserve_transfer_allowed(
1615 &assets,
1616 fee_asset_item,
1617 &assets_transfer_type,
1618 &fees_transfer_type,
1619 )?;
1620
1621 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1622 origin.clone(),
1623 dest.clone(),
1624 Either::Left(beneficiary),
1625 assets,
1626 assets_transfer_type,
1627 FeesHandling::Batched { fees },
1628 weight_limit,
1629 )?;
1630 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1631 }
1632
1633 fn do_teleport_assets(
1634 origin: OriginFor<T>,
1635 dest: Box<VersionedLocation>,
1636 beneficiary: Box<VersionedLocation>,
1637 assets: Box<VersionedAssets>,
1638 fee_asset_item: u32,
1639 weight_limit: WeightLimit,
1640 ) -> DispatchResult {
1641 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1642 let dest = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
1643 let beneficiary: Location =
1644 (*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
1645 let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
1646 log::debug!(
1647 target: "xcm::pallet_xcm::do_teleport_assets",
1648 "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, fee-idx {:?}, weight_limit {:?}",
1649 origin_location, dest, beneficiary, assets, fee_asset_item, weight_limit,
1650 );
1651
1652 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1653 let value = (origin_location, assets.into_inner());
1654 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
1655 let (origin_location, assets) = value;
1656 for asset in assets.iter() {
1657 let transfer_type =
1658 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
1659 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
1660 }
1661 let fees = assets.get(fee_asset_item as usize).ok_or(Error::<T>::Empty)?.clone();
1662
1663 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1664 origin_location.clone(),
1665 dest.clone(),
1666 Either::Left(beneficiary),
1667 assets,
1668 TransferType::Teleport,
1669 FeesHandling::Batched { fees },
1670 weight_limit,
1671 )?;
1672 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
1673 }
1674
1675 fn do_transfer_assets(
1676 origin: Location,
1677 dest: Location,
1678 beneficiary: Either<Location, Xcm<()>>,
1679 mut assets: Vec<Asset>,
1680 assets_transfer_type: TransferType,
1681 fee_asset_index: usize,
1682 fees_transfer_type: TransferType,
1683 weight_limit: WeightLimit,
1684 ) -> DispatchResult {
1685 let fees = if fees_transfer_type == assets_transfer_type {
1687 let fees = assets.get(fee_asset_index).ok_or(Error::<T>::Empty)?.clone();
1688 FeesHandling::Batched { fees }
1690 } else {
1691 ensure!(
1697 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
1698 Error::<T>::InvalidAssetUnsupportedReserve
1699 );
1700 let weight_limit = weight_limit.clone();
1701 let fees = assets.remove(fee_asset_index);
1704 let (local_xcm, remote_xcm) = match fees_transfer_type {
1705 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
1706 origin.clone(),
1707 dest.clone(),
1708 fees,
1709 weight_limit,
1710 )?,
1711 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
1712 origin.clone(),
1713 dest.clone(),
1714 fees,
1715 weight_limit,
1716 )?,
1717 TransferType::Teleport => Self::teleport_fees_instructions(
1718 origin.clone(),
1719 dest.clone(),
1720 fees,
1721 weight_limit,
1722 )?,
1723 TransferType::RemoteReserve(_) =>
1724 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
1725 };
1726 FeesHandling::Separate { local_xcm, remote_xcm }
1727 };
1728
1729 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
1730 origin.clone(),
1731 dest.clone(),
1732 beneficiary,
1733 assets,
1734 assets_transfer_type,
1735 fees,
1736 weight_limit,
1737 )?;
1738 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
1739 }
1740
1741 fn build_xcm_transfer_type(
1742 origin: Location,
1743 dest: Location,
1744 beneficiary: Either<Location, Xcm<()>>,
1745 assets: Vec<Asset>,
1746 transfer_type: TransferType,
1747 fees: FeesHandling<T>,
1748 weight_limit: WeightLimit,
1749 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
1750 log::debug!(
1751 target: "xcm::pallet_xcm::build_xcm_transfer_type",
1752 "origin {:?}, dest {:?}, beneficiary {:?}, assets {:?}, transfer_type {:?}, \
1753 fees_handling {:?}, weight_limit: {:?}",
1754 origin, dest, beneficiary, assets, transfer_type, fees, weight_limit,
1755 );
1756 match transfer_type {
1757 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
1758 origin.clone(),
1759 dest.clone(),
1760 beneficiary,
1761 assets,
1762 fees,
1763 weight_limit,
1764 )
1765 .map(|(local, remote)| (local, Some(remote))),
1766 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
1767 origin.clone(),
1768 dest.clone(),
1769 beneficiary,
1770 assets,
1771 fees,
1772 weight_limit,
1773 )
1774 .map(|(local, remote)| (local, Some(remote))),
1775 TransferType::RemoteReserve(reserve) => {
1776 let fees = match fees {
1777 FeesHandling::Batched { fees } => fees,
1778 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
1779 };
1780 Self::remote_reserve_transfer_program(
1781 origin.clone(),
1782 reserve.try_into().map_err(|()| Error::<T>::BadVersion)?,
1783 beneficiary,
1784 dest.clone(),
1785 assets,
1786 fees,
1787 weight_limit,
1788 )
1789 .map(|local| (local, None))
1790 },
1791 TransferType::Teleport => Self::teleport_assets_program(
1792 origin.clone(),
1793 dest.clone(),
1794 beneficiary,
1795 assets,
1796 fees,
1797 weight_limit,
1798 )
1799 .map(|(local, remote)| (local, Some(remote))),
1800 }
1801 }
1802
1803 fn execute_xcm_transfer(
1804 origin: Location,
1805 dest: Location,
1806 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
1807 remote_xcm: Option<Xcm<()>>,
1808 ) -> DispatchResult {
1809 log::debug!(
1810 target: "xcm::pallet_xcm::execute_xcm_transfer",
1811 "origin {:?}, dest {:?}, local_xcm {:?}, remote_xcm {:?}",
1812 origin, dest, local_xcm, remote_xcm,
1813 );
1814
1815 let weight =
1816 T::Weigher::weight(&mut local_xcm).map_err(|()| Error::<T>::UnweighableMessage)?;
1817 let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256);
1818 let outcome = T::XcmExecutor::prepare_and_execute(
1819 origin.clone(),
1820 local_xcm,
1821 &mut hash,
1822 weight,
1823 weight,
1824 );
1825 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
1826 outcome.ensure_complete().map_err(|error| {
1827 log::error!(
1828 target: "xcm::pallet_xcm::execute_xcm_transfer",
1829 "XCM execution failed with error {:?}", error
1830 );
1831 Error::<T>::LocalExecutionIncomplete
1832 })?;
1833
1834 if let Some(remote_xcm) = remote_xcm {
1835 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
1836 .map_err(|error| {
1837 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
1838 Error::<T>::from(error)
1839 })?;
1840 if origin != Here.into_location() {
1841 Self::charge_fees(origin.clone(), price).map_err(|error| {
1842 log::error!(
1843 target: "xcm::pallet_xcm::execute_xcm_transfer",
1844 "Unable to charge fee with error {:?}", error
1845 );
1846 Error::<T>::FeesNotMet
1847 })?;
1848 }
1849 let message_id = T::XcmRouter::deliver(ticket)
1850 .map_err(|error| {
1851 tracing::error!(target: "xcm::pallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
1852 Error::<T>::from(error)
1853 })?;
1854
1855 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
1856 Self::deposit_event(e);
1857 }
1858 Ok(())
1859 }
1860
1861 fn add_fees_to_xcm(
1862 dest: Location,
1863 fees: FeesHandling<T>,
1864 weight_limit: WeightLimit,
1865 local: &mut Xcm<<T as Config>::RuntimeCall>,
1866 remote: &mut Xcm<()>,
1867 ) -> Result<(), Error<T>> {
1868 match fees {
1869 FeesHandling::Batched { fees } => {
1870 let context = T::UniversalLocation::get();
1871 let reanchored_fees =
1874 fees.reanchored(&dest, &context).map_err(|_| Error::<T>::CannotReanchor)?;
1875 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
1877 },
1878 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
1879 core::mem::swap(local, &mut local_fees);
1882 core::mem::swap(remote, &mut remote_fees);
1883 local.inner_mut().append(&mut local_fees.into_inner());
1885 remote.inner_mut().append(&mut remote_fees.into_inner());
1886 },
1887 }
1888 Ok(())
1889 }
1890
1891 fn local_reserve_fees_instructions(
1892 origin: Location,
1893 dest: Location,
1894 fees: Asset,
1895 weight_limit: WeightLimit,
1896 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1897 let value = (origin, vec![fees.clone()]);
1898 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1899
1900 let context = T::UniversalLocation::get();
1901 let reanchored_fees = fees
1902 .clone()
1903 .reanchored(&dest, &context)
1904 .map_err(|_| Error::<T>::CannotReanchor)?;
1905
1906 let local_execute_xcm = Xcm(vec![
1907 TransferAsset { assets: fees.into(), beneficiary: dest },
1909 ]);
1910 let xcm_on_dest = Xcm(vec![
1911 ReserveAssetDeposited(reanchored_fees.clone().into()),
1913 BuyExecution { fees: reanchored_fees, weight_limit },
1915 ]);
1916 Ok((local_execute_xcm, xcm_on_dest))
1917 }
1918
1919 fn local_reserve_transfer_programs(
1920 origin: Location,
1921 dest: Location,
1922 beneficiary: Either<Location, Xcm<()>>,
1923 assets: Vec<Asset>,
1924 fees: FeesHandling<T>,
1925 weight_limit: WeightLimit,
1926 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1927 let value = (origin, assets);
1928 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1929 let (_, assets) = value;
1930
1931 let max_assets =
1933 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
1934 let assets: Assets = assets.into();
1935 let context = T::UniversalLocation::get();
1936 let mut reanchored_assets = assets.clone();
1937 reanchored_assets
1938 .reanchor(&dest, &context)
1939 .map_err(|_| Error::<T>::CannotReanchor)?;
1940
1941 let mut local_execute_xcm = Xcm(vec![
1943 TransferAsset { assets, beneficiary: dest.clone() },
1945 ]);
1946 let mut xcm_on_dest = Xcm(vec![
1948 ReserveAssetDeposited(reanchored_assets),
1950 ClearOrigin,
1952 ]);
1953 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
1955
1956 let custom_remote_xcm = match beneficiary {
1958 Either::Right(custom_xcm) => custom_xcm,
1959 Either::Left(beneficiary) => {
1960 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
1962 },
1963 };
1964 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
1965
1966 Ok((local_execute_xcm, xcm_on_dest))
1967 }
1968
1969 fn destination_reserve_fees_instructions(
1970 origin: Location,
1971 dest: Location,
1972 fees: Asset,
1973 weight_limit: WeightLimit,
1974 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
1975 let value = (origin, vec![fees.clone()]);
1976 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
1977 ensure!(
1978 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
1979 Error::<T>::InvalidAssetUnsupportedReserve
1980 );
1981
1982 let context = T::UniversalLocation::get();
1983 let reanchored_fees = fees
1984 .clone()
1985 .reanchored(&dest, &context)
1986 .map_err(|_| Error::<T>::CannotReanchor)?;
1987 let fees: Assets = fees.into();
1988
1989 let local_execute_xcm = Xcm(vec![
1990 WithdrawAsset(fees.clone()),
1992 BurnAsset(fees),
1994 ]);
1995 let xcm_on_dest = Xcm(vec![
1996 WithdrawAsset(reanchored_fees.clone().into()),
1998 BuyExecution { fees: reanchored_fees, weight_limit },
2000 ]);
2001 Ok((local_execute_xcm, xcm_on_dest))
2002 }
2003
2004 fn destination_reserve_transfer_programs(
2005 origin: Location,
2006 dest: Location,
2007 beneficiary: Either<Location, Xcm<()>>,
2008 assets: Vec<Asset>,
2009 fees: FeesHandling<T>,
2010 weight_limit: WeightLimit,
2011 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2012 let value = (origin, assets);
2013 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2014 let (_, assets) = value;
2015 for asset in assets.iter() {
2016 ensure!(
2017 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2018 Error::<T>::InvalidAssetUnsupportedReserve
2019 );
2020 }
2021
2022 let max_assets =
2024 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2025 let assets: Assets = assets.into();
2026 let context = T::UniversalLocation::get();
2027 let mut reanchored_assets = assets.clone();
2028 reanchored_assets
2029 .reanchor(&dest, &context)
2030 .map_err(|_| Error::<T>::CannotReanchor)?;
2031
2032 let mut local_execute_xcm = Xcm(vec![
2034 WithdrawAsset(assets.clone()),
2036 BurnAsset(assets),
2038 ]);
2039 let mut xcm_on_dest = Xcm(vec![
2041 WithdrawAsset(reanchored_assets),
2043 ClearOrigin,
2045 ]);
2046 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2048
2049 let custom_remote_xcm = match beneficiary {
2051 Either::Right(custom_xcm) => custom_xcm,
2052 Either::Left(beneficiary) => {
2053 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2055 },
2056 };
2057 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2058
2059 Ok((local_execute_xcm, xcm_on_dest))
2060 }
2061
2062 fn remote_reserve_transfer_program(
2064 origin: Location,
2065 reserve: Location,
2066 beneficiary: Either<Location, Xcm<()>>,
2067 dest: Location,
2068 assets: Vec<Asset>,
2069 fees: Asset,
2070 weight_limit: WeightLimit,
2071 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2072 let value = (origin, assets);
2073 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2074 let (_, assets) = value;
2075
2076 let max_assets = assets.len() as u32;
2077 let context = T::UniversalLocation::get();
2078 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2081 let reserve_fees = fees_half_1
2083 .reanchored(&reserve, &context)
2084 .map_err(|_| Error::<T>::CannotReanchor)?;
2085 let dest_fees = fees_half_2
2087 .reanchored(&dest, &context)
2088 .map_err(|_| Error::<T>::CannotReanchor)?;
2089 let dest = dest.reanchored(&reserve, &context).map_err(|_| Error::<T>::CannotReanchor)?;
2091 let mut xcm_on_dest =
2093 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2094 let custom_xcm_on_dest = match beneficiary {
2096 Either::Right(custom_xcm) => custom_xcm,
2097 Either::Left(beneficiary) => {
2098 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2100 },
2101 };
2102 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2103 let xcm_on_reserve = Xcm(vec![
2105 BuyExecution { fees: reserve_fees, weight_limit },
2106 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2107 ]);
2108 Ok(Xcm(vec![
2109 WithdrawAsset(assets.into()),
2110 SetFeesMode { jit_withdraw: true },
2111 InitiateReserveWithdraw {
2112 assets: Wild(AllCounted(max_assets)),
2113 reserve,
2114 xcm: xcm_on_reserve,
2115 },
2116 ]))
2117 }
2118
2119 fn teleport_fees_instructions(
2120 origin: Location,
2121 dest: Location,
2122 fees: Asset,
2123 weight_limit: WeightLimit,
2124 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2125 let value = (origin, vec![fees.clone()]);
2126 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2127 ensure!(
2128 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2129 Error::<T>::Filtered
2130 );
2131
2132 let context = T::UniversalLocation::get();
2133 let reanchored_fees = fees
2134 .clone()
2135 .reanchored(&dest, &context)
2136 .map_err(|_| Error::<T>::CannotReanchor)?;
2137
2138 let dummy_context =
2140 XcmContext { origin: None, message_id: Default::default(), topic: None };
2141 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2146 &dest,
2147 &fees,
2148 &dummy_context,
2149 )
2150 .map_err(|_| Error::<T>::CannotCheckOutTeleport)?;
2151 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2154 &dest,
2155 &fees,
2156 &dummy_context,
2157 );
2158
2159 let fees: Assets = fees.into();
2160 let local_execute_xcm = Xcm(vec![
2161 WithdrawAsset(fees.clone()),
2163 BurnAsset(fees),
2165 ]);
2166 let xcm_on_dest = Xcm(vec![
2167 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2169 BuyExecution { fees: reanchored_fees, weight_limit },
2171 ]);
2172 Ok((local_execute_xcm, xcm_on_dest))
2173 }
2174
2175 fn teleport_assets_program(
2176 origin: Location,
2177 dest: Location,
2178 beneficiary: Either<Location, Xcm<()>>,
2179 assets: Vec<Asset>,
2180 fees: FeesHandling<T>,
2181 weight_limit: WeightLimit,
2182 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2183 let value = (origin, assets);
2184 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2185 let (_, assets) = value;
2186 for asset in assets.iter() {
2187 ensure!(
2188 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2189 Error::<T>::Filtered
2190 );
2191 }
2192
2193 let max_assets =
2195 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2196 let context = T::UniversalLocation::get();
2197 let assets: Assets = assets.into();
2198 let mut reanchored_assets = assets.clone();
2199 reanchored_assets
2200 .reanchor(&dest, &context)
2201 .map_err(|_| Error::<T>::CannotReanchor)?;
2202
2203 let dummy_context =
2205 XcmContext { origin: None, message_id: Default::default(), topic: None };
2206 for asset in assets.inner() {
2207 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2212 &dest,
2213 asset,
2214 &dummy_context,
2215 )
2216 .map_err(|_| Error::<T>::CannotCheckOutTeleport)?;
2217 }
2218 for asset in assets.inner() {
2219 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2222 &dest,
2223 asset,
2224 &dummy_context,
2225 );
2226 }
2227
2228 let mut local_execute_xcm = Xcm(vec![
2230 WithdrawAsset(assets.clone()),
2232 BurnAsset(assets),
2234 ]);
2235 let mut xcm_on_dest = Xcm(vec![
2237 ReceiveTeleportedAsset(reanchored_assets),
2239 ClearOrigin,
2241 ]);
2242 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2244
2245 let custom_remote_xcm = match beneficiary {
2247 Either::Right(custom_xcm) => custom_xcm,
2248 Either::Left(beneficiary) => {
2249 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2251 },
2252 };
2253 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2254
2255 Ok((local_execute_xcm, xcm_on_dest))
2256 }
2257
2258 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2260 match fees.fun {
2261 Fungible(amount) => {
2262 let fee1 = amount.saturating_div(2);
2263 let fee2 = amount.saturating_sub(fee1);
2264 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2265 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2266 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2267 },
2268 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2269 }
2270 }
2271
2272 pub(crate) fn check_xcm_version_change(
2275 mut stage: VersionMigrationStage,
2276 weight_cutoff: Weight,
2277 ) -> (Weight, Option<VersionMigrationStage>) {
2278 let mut weight_used = Weight::zero();
2279
2280 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2281 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2282 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2283 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2284 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2285 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2286 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2287
2288 use VersionMigrationStage::*;
2289
2290 if stage == MigrateSupportedVersion {
2291 for v in 0..XCM_VERSION {
2294 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2295 if let Ok(new_key) = old_key.into_latest() {
2296 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2297 }
2298 weight_used.saturating_accrue(sv_migrate_weight);
2299 if weight_used.any_gte(weight_cutoff) {
2300 return (weight_used, Some(stage))
2301 }
2302 }
2303 }
2304 stage = MigrateVersionNotifiers;
2305 }
2306 if stage == MigrateVersionNotifiers {
2307 for v in 0..XCM_VERSION {
2308 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2309 if let Ok(new_key) = old_key.into_latest() {
2310 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2311 }
2312 weight_used.saturating_accrue(vn_migrate_weight);
2313 if weight_used.any_gte(weight_cutoff) {
2314 return (weight_used, Some(stage))
2315 }
2316 }
2317 }
2318 stage = NotifyCurrentTargets(None);
2319 }
2320
2321 let xcm_version = T::AdvertisedXcmVersion::get();
2322
2323 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2324 let mut iter = match maybe_last_raw_key {
2325 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2326 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2327 };
2328 while let Some((key, value)) = iter.next() {
2329 let (query_id, max_weight, target_xcm_version) = value;
2330 let new_key: Location = match key.clone().try_into() {
2331 Ok(k) if target_xcm_version != xcm_version => k,
2332 _ => {
2333 weight_used.saturating_accrue(vnt_already_notified_weight);
2336 continue
2337 },
2338 };
2339 let response = Response::Version(xcm_version);
2340 let message =
2341 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2342 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2343 Ok((message_id, cost)) => {
2344 let value = (query_id, max_weight, xcm_version);
2345 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2346 Event::VersionChangeNotified {
2347 destination: new_key,
2348 result: xcm_version,
2349 cost,
2350 message_id,
2351 }
2352 },
2353 Err(e) => {
2354 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2355 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2356 },
2357 };
2358 Self::deposit_event(event);
2359 weight_used.saturating_accrue(vnt_notify_weight);
2360 if weight_used.any_gte(weight_cutoff) {
2361 let last = Some(iter.last_raw_key().into());
2362 return (weight_used, Some(NotifyCurrentTargets(last)))
2363 }
2364 }
2365 stage = MigrateAndNotifyOldTargets;
2366 }
2367 if stage == MigrateAndNotifyOldTargets {
2368 for v in 0..XCM_VERSION {
2369 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2370 let (query_id, max_weight, target_xcm_version) = value;
2371 let new_key = match Location::try_from(old_key.clone()) {
2372 Ok(k) => k,
2373 Err(()) => {
2374 Self::deposit_event(Event::NotifyTargetMigrationFail {
2375 location: old_key,
2376 query_id: value.0,
2377 });
2378 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2379 if weight_used.any_gte(weight_cutoff) {
2380 return (weight_used, Some(stage))
2381 }
2382 continue
2383 },
2384 };
2385
2386 let versioned_key = LatestVersionedLocation(&new_key);
2387 if target_xcm_version == xcm_version {
2388 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2389 weight_used.saturating_accrue(vnt_migrate_weight);
2390 } else {
2391 let response = Response::Version(xcm_version);
2393 let message = Xcm(vec![QueryResponse {
2394 query_id,
2395 response,
2396 max_weight,
2397 querier: None,
2398 }]);
2399 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2400 Ok((message_id, cost)) => {
2401 VersionNotifyTargets::<T>::insert(
2402 XCM_VERSION,
2403 versioned_key,
2404 (query_id, max_weight, xcm_version),
2405 );
2406 Event::VersionChangeNotified {
2407 destination: new_key,
2408 result: xcm_version,
2409 cost,
2410 message_id,
2411 }
2412 },
2413 Err(e) => Event::NotifyTargetSendFail {
2414 location: new_key,
2415 query_id,
2416 error: e.into(),
2417 },
2418 };
2419 Self::deposit_event(event);
2420 weight_used.saturating_accrue(vnt_notify_migrate_weight);
2421 }
2422 if weight_used.any_gte(weight_cutoff) {
2423 return (weight_used, Some(stage))
2424 }
2425 }
2426 }
2427 }
2428 (weight_used, None)
2429 }
2430
2431 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2433 let dest = dest.into();
2434 let versioned_dest = VersionedLocation::from(dest.clone());
2435 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
2436 ensure!(!already, XcmError::InvalidLocation);
2437 let query_id = QueryCounter::<T>::mutate(|q| {
2438 let r = *q;
2439 q.saturating_inc();
2440 r
2441 });
2442 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
2444 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
2445 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
2446 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
2447 let query_status =
2448 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
2449 Queries::<T>::insert(query_id, query_status);
2450 Ok(())
2451 }
2452
2453 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
2455 let dest = dest.into();
2456 let versioned_dest = LatestVersionedLocation(&dest);
2457 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
2458 .ok_or(XcmError::InvalidLocation)?;
2459 let (message_id, cost) =
2460 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
2461 Self::deposit_event(Event::VersionNotifyUnrequested {
2462 destination: dest,
2463 cost,
2464 message_id,
2465 });
2466 Queries::<T>::remove(query_id);
2467 Ok(())
2468 }
2469
2470 pub fn send_xcm(
2474 interior: impl Into<Junctions>,
2475 dest: impl Into<Location>,
2476 mut message: Xcm<()>,
2477 ) -> Result<XcmHash, SendError> {
2478 let interior = interior.into();
2479 let dest = dest.into();
2480 let maybe_fee_payer = if interior != Junctions::Here {
2481 message.0.insert(0, DescendOrigin(interior.clone()));
2482 Some(interior.into())
2483 } else {
2484 None
2485 };
2486 log::debug!(target: "xcm::send_xcm", "dest: {:?}, message: {:?}", &dest, &message);
2487 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
2488 if let Some(fee_payer) = maybe_fee_payer {
2489 Self::charge_fees(fee_payer, price).map_err(|_| SendError::Fees)?;
2490 }
2491 T::XcmRouter::deliver(ticket)
2492 }
2493
2494 pub fn check_account() -> T::AccountId {
2495 const ID: PalletId = PalletId(*b"py/xcmch");
2496 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
2497 }
2498
2499 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
2505 origin: OriginCaller,
2506 call: RuntimeCall,
2507 result_xcms_version: XcmVersion,
2508 ) -> Result<CallDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2509 where
2510 Runtime: crate::Config,
2511 Router: InspectMessageQueues,
2512 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
2513 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
2514 {
2515 crate::Pallet::<Runtime>::set_record_xcm(true);
2516 Router::clear_messages();
2518 frame_system::Pallet::<Runtime>::reset_events();
2520 let result = call.dispatch(origin.into());
2521 crate::Pallet::<Runtime>::set_record_xcm(false);
2522 let local_xcm = crate::Pallet::<Runtime>::recorded_xcm()
2523 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
2524 .transpose()
2525 .map_err(|()| {
2526 tracing::error!(
2527 target: "xcm::DryRunApi::dry_run_call",
2528 "Local xcm version conversion failed"
2529 );
2530
2531 XcmDryRunApiError::VersionedConversionFailed
2532 })?;
2533
2534 let forwarded_xcms =
2536 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
2537 |error| {
2538 tracing::error!(
2539 target: "xcm::DryRunApi::dry_run_call",
2540 ?error, "Forwarded xcms version conversion failed with error"
2541 );
2542 },
2543 )?;
2544 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2545 frame_system::Pallet::<Runtime>::read_events_no_consensus()
2546 .map(|record| record.event.clone())
2547 .collect();
2548 Ok(CallDryRunEffects {
2549 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
2550 forwarded_xcms,
2551 emitted_events: events,
2552 execution_result: result,
2553 })
2554 }
2555
2556 pub fn dry_run_xcm<Runtime, Router, RuntimeCall, XcmConfig>(
2561 origin_location: VersionedLocation,
2562 xcm: VersionedXcm<RuntimeCall>,
2563 ) -> Result<XcmDryRunEffects<<Runtime as frame_system::Config>::RuntimeEvent>, XcmDryRunApiError>
2564 where
2565 Runtime: frame_system::Config,
2566 Router: InspectMessageQueues,
2567 XcmConfig: xcm_executor::Config<RuntimeCall = RuntimeCall>,
2568 {
2569 let origin_location: Location = origin_location.try_into().map_err(|error| {
2570 log::error!(
2571 target: "xcm::DryRunApi::dry_run_xcm",
2572 "Location version conversion failed with error: {:?}",
2573 error,
2574 );
2575 XcmDryRunApiError::VersionedConversionFailed
2576 })?;
2577 let xcm_version = xcm.identify_version();
2578 let xcm: Xcm<RuntimeCall> = xcm.try_into().map_err(|error| {
2579 log::error!(
2580 target: "xcm::DryRunApi::dry_run_xcm",
2581 "Xcm version conversion failed with error {:?}",
2582 error,
2583 );
2584 XcmDryRunApiError::VersionedConversionFailed
2585 })?;
2586 let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256);
2587
2588 Router::clear_messages();
2590 frame_system::Pallet::<Runtime>::reset_events();
2591
2592 let result = xcm_executor::XcmExecutor::<XcmConfig>::prepare_and_execute(
2593 origin_location,
2594 xcm,
2595 &mut hash,
2596 Weight::MAX, Weight::zero(),
2598 );
2599 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
2600 .inspect_err(|error| {
2601 tracing::error!(
2602 target: "xcm::DryRunApi::dry_run_xcm",
2603 ?error, "Forwarded xcms version conversion failed with error"
2604 );
2605 })?;
2606 let events: Vec<<Runtime as frame_system::Config>::RuntimeEvent> =
2607 frame_system::Pallet::<Runtime>::read_events_no_consensus()
2608 .map(|record| record.event.clone())
2609 .collect();
2610 Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
2611 }
2612
2613 fn convert_xcms(
2614 xcm_version: XcmVersion,
2615 xcms: Vec<VersionedXcm<()>>,
2616 ) -> Result<Vec<VersionedXcm<()>>, ()> {
2617 xcms.into_iter()
2618 .map(|xcm| xcm.into_version(xcm_version))
2619 .collect::<Result<Vec<_>, ()>>()
2620 }
2621
2622 fn convert_forwarded_xcms(
2623 xcm_version: XcmVersion,
2624 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
2625 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
2626 forwarded_xcms
2627 .into_iter()
2628 .map(|(dest, forwarded_xcms)| {
2629 let dest = dest.into_version(xcm_version)?;
2630 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
2631
2632 Ok((dest, forwarded_xcms))
2633 })
2634 .collect::<Result<Vec<_>, ()>>()
2635 .map_err(|()| XcmDryRunApiError::VersionedConversionFailed)
2636 }
2637
2638 pub fn query_acceptable_payment_assets(
2643 version: xcm::Version,
2644 asset_ids: Vec<AssetId>,
2645 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
2646 Ok(asset_ids
2647 .into_iter()
2648 .map(|asset_id| VersionedAssetId::from(asset_id))
2649 .filter_map(|asset_id| asset_id.into_version(version).ok())
2650 .collect())
2651 }
2652
2653 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
2654 let message = Xcm::<()>::try_from(message)
2655 .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
2656
2657 T::Weigher::weight(&mut message.into()).map_err(|()| {
2658 log::error!(target: "xcm::pallet_xcm::query_xcm_weight", "Error when querying XCM weight");
2659 XcmPaymentApiError::WeightNotComputable
2660 })
2661 }
2662
2663 pub fn query_delivery_fees(
2664 destination: VersionedLocation,
2665 message: VersionedXcm<()>,
2666 ) -> Result<VersionedAssets, XcmPaymentApiError> {
2667 let result_version = destination.identify_version().max(message.identify_version());
2668
2669 let destination = destination
2670 .try_into()
2671 .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
2672
2673 let message =
2674 message.try_into().map_err(|_| XcmPaymentApiError::VersionedConversionFailed)?;
2675
2676 let (_, fees) = validate_send::<T::XcmRouter>(destination, message).map_err(|error| {
2677 log::error!(target: "xcm::pallet_xcm::query_delivery_fees", "Error when querying delivery fees: {:?}", error);
2678 XcmPaymentApiError::Unroutable
2679 })?;
2680
2681 VersionedAssets::from(fees)
2682 .into_version(result_version)
2683 .map_err(|_| XcmPaymentApiError::VersionedConversionFailed)
2684 }
2685
2686 fn do_new_query(
2688 responder: impl Into<Location>,
2689 maybe_notify: Option<(u8, u8)>,
2690 timeout: BlockNumberFor<T>,
2691 match_querier: impl Into<Location>,
2692 ) -> u64 {
2693 QueryCounter::<T>::mutate(|q| {
2694 let r = *q;
2695 q.saturating_inc();
2696 Queries::<T>::insert(
2697 r,
2698 QueryStatus::Pending {
2699 responder: responder.into().into(),
2700 maybe_match_querier: Some(match_querier.into().into()),
2701 maybe_notify,
2702 timeout,
2703 },
2704 );
2705 r
2706 })
2707 }
2708
2709 pub fn report_outcome_notify(
2732 message: &mut Xcm<()>,
2733 responder: impl Into<Location>,
2734 notify: impl Into<<T as Config>::RuntimeCall>,
2735 timeout: BlockNumberFor<T>,
2736 ) -> Result<(), XcmError> {
2737 let responder = responder.into();
2738 let destination = T::UniversalLocation::get()
2739 .invert_target(&responder)
2740 .map_err(|()| XcmError::LocationNotInvertible)?;
2741 let notify: <T as Config>::RuntimeCall = notify.into();
2742 let max_weight = notify.get_dispatch_info().weight;
2743 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
2744 let response_info = QueryResponseInfo { destination, query_id, max_weight };
2745 let report_error = Xcm(vec![ReportError(response_info)]);
2746 message.0.insert(0, SetAppendix(report_error));
2747 Ok(())
2748 }
2749
2750 pub fn new_notify_query(
2753 responder: impl Into<Location>,
2754 notify: impl Into<<T as Config>::RuntimeCall>,
2755 timeout: BlockNumberFor<T>,
2756 match_querier: impl Into<Location>,
2757 ) -> u64 {
2758 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
2759 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
2760 );
2761 Self::do_new_query(responder, Some(notify), timeout, match_querier)
2762 }
2763
2764 fn note_unknown_version(dest: &Location) {
2767 log::trace!(
2768 target: "xcm::pallet_xcm::note_unknown_version",
2769 "XCM version is unknown for destination: {:?}",
2770 dest,
2771 );
2772 let versioned_dest = VersionedLocation::from(dest.clone());
2773 VersionDiscoveryQueue::<T>::mutate(|q| {
2774 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
2775 q[index].1.saturating_inc();
2777 } else {
2778 let _ = q.try_push((versioned_dest, 1));
2779 }
2780 });
2781 }
2782
2783 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
2789 T::XcmExecutor::charge_fees(location.clone(), assets.clone())
2790 .map_err(|_| Error::<T>::FeesNotMet)?;
2791 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
2792 Ok(())
2793 }
2794
2795 #[cfg(any(feature = "try-runtime", test))]
2805 pub fn do_try_state() -> Result<(), TryRuntimeError> {
2806 use migration::data::NeedsMigration;
2807
2808 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
2812 {
2813 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
2814 } else {
2815 XCM_VERSION.saturating_sub(1)
2816 };
2817
2818 ensure!(
2820 !Queries::<T>::iter_values()
2821 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
2822 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
2823 );
2824
2825 ensure!(
2827 !LockedFungibles::<T>::iter_values()
2828 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
2829 TryRuntimeError::Other(
2830 "`LockedFungibles` data should be migrated to the higher xcm version!"
2831 )
2832 );
2833
2834 ensure!(
2836 !RemoteLockedFungibles::<T>::iter()
2837 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version) ||
2838 data.needs_migration(minimal_allowed_xcm_version)),
2839 TryRuntimeError::Other(
2840 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
2841 )
2842 );
2843
2844 if CurrentMigration::<T>::exists() {
2847 return Ok(())
2848 }
2849
2850 for v in 0..XCM_VERSION {
2852 ensure!(
2853 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
2854 TryRuntimeError::Other(
2855 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
2856 )
2857 );
2858 ensure!(
2859 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
2860 TryRuntimeError::Other(
2861 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
2862 )
2863 );
2864 ensure!(
2865 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
2866 TryRuntimeError::Other(
2867 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
2868 )
2869 );
2870 }
2871
2872 Ok(())
2873 }
2874}
2875
2876pub struct LockTicket<T: Config> {
2877 sovereign_account: T::AccountId,
2878 amount: BalanceOf<T>,
2879 unlocker: Location,
2880 item_index: Option<usize>,
2881}
2882
2883impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
2884 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
2885 use xcm_executor::traits::LockError::UnexpectedState;
2886 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
2887 match self.item_index {
2888 Some(index) => {
2889 ensure!(locks.len() > index, UnexpectedState);
2890 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
2891 locks[index].0 = locks[index].0.max(self.amount);
2892 },
2893 None => {
2894 locks
2895 .try_push((self.amount, self.unlocker.into()))
2896 .map_err(|(_balance, _location)| UnexpectedState)?;
2897 },
2898 }
2899 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
2900 T::Currency::extend_lock(
2901 *b"py/xcmlk",
2902 &self.sovereign_account,
2903 self.amount,
2904 WithdrawReasons::all(),
2905 );
2906 Ok(())
2907 }
2908}
2909
2910pub struct UnlockTicket<T: Config> {
2911 sovereign_account: T::AccountId,
2912 amount: BalanceOf<T>,
2913 unlocker: Location,
2914}
2915
2916impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
2917 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
2918 use xcm_executor::traits::LockError::UnexpectedState;
2919 let mut locks =
2920 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
2921 let mut maybe_remove_index = None;
2922 let mut locked = BalanceOf::<T>::zero();
2923 let mut found = false;
2924 for (i, x) in locks.iter_mut().enumerate() {
2927 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
2928 x.0 = x.0.saturating_sub(self.amount);
2929 if x.0.is_zero() {
2930 maybe_remove_index = Some(i);
2931 }
2932 found = true;
2933 }
2934 locked = locked.max(x.0);
2935 }
2936 ensure!(found, UnexpectedState);
2937 if let Some(remove_index) = maybe_remove_index {
2938 locks.swap_remove(remove_index);
2939 }
2940 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
2941 let reasons = WithdrawReasons::all();
2942 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
2943 Ok(())
2944 }
2945}
2946
2947pub struct ReduceTicket<T: Config> {
2948 key: (u32, T::AccountId, VersionedAssetId),
2949 amount: u128,
2950 locker: VersionedLocation,
2951 owner: VersionedLocation,
2952}
2953
2954impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
2955 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
2956 use xcm_executor::traits::LockError::UnexpectedState;
2957 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
2958 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
2959 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
2960 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
2961 if new_amount == 0 {
2962 RemoteLockedFungibles::<T>::remove(&self.key);
2963 } else {
2964 record.amount = new_amount;
2965 RemoteLockedFungibles::<T>::insert(&self.key, &record);
2966 }
2967 Ok(())
2968 }
2969}
2970
2971impl<T: Config> xcm_executor::traits::AssetLock for Pallet<T> {
2972 type LockTicket = LockTicket<T>;
2973 type UnlockTicket = UnlockTicket<T>;
2974 type ReduceTicket = ReduceTicket<T>;
2975
2976 fn prepare_lock(
2977 unlocker: Location,
2978 asset: Asset,
2979 owner: Location,
2980 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
2981 use xcm_executor::traits::LockError::*;
2982 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
2983 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
2984 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
2985 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
2986 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
2987 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
2988 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
2989 }
2990
2991 fn prepare_unlock(
2992 unlocker: Location,
2993 asset: Asset,
2994 owner: Location,
2995 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
2996 use xcm_executor::traits::LockError::*;
2997 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
2998 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
2999 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3000 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3001 let item_index =
3002 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3003 ensure!(locks[item_index].0 >= amount, NotLocked);
3004 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3005 }
3006
3007 fn note_unlockable(
3008 locker: Location,
3009 asset: Asset,
3010 mut owner: Location,
3011 ) -> Result<(), xcm_executor::traits::LockError> {
3012 use xcm_executor::traits::LockError::*;
3013 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3014 let amount = match asset.fun {
3015 Fungible(a) => a,
3016 NonFungible(_) => return Err(Unimplemented),
3017 };
3018 owner.remove_network_id();
3019 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3020 let locker = locker.into();
3021 let owner = owner.into();
3022 let id: VersionedAssetId = asset.id.into();
3023 let key = (XCM_VERSION, account, id);
3024 let mut record =
3025 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3026 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3027 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3029 record.consumers = old.consumers;
3030 record.amount = record.amount.max(old.amount);
3031 }
3032 RemoteLockedFungibles::<T>::insert(&key, record);
3033 Ok(())
3034 }
3035
3036 fn prepare_reduce_unlockable(
3037 locker: Location,
3038 asset: Asset,
3039 mut owner: Location,
3040 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3041 use xcm_executor::traits::LockError::*;
3042 let amount = match asset.fun {
3043 Fungible(a) => a,
3044 NonFungible(_) => return Err(Unimplemented),
3045 };
3046 owner.remove_network_id();
3047 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3048 let locker = locker.into();
3049 let owner = owner.into();
3050 let id: VersionedAssetId = asset.id.into();
3051 let key = (XCM_VERSION, sovereign_account, id);
3052
3053 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3054 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3056 ensure!(record.amount >= amount, NotEnoughLocked);
3057 ensure!(
3058 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3059 InUse
3060 );
3061 Ok(ReduceTicket { key, amount, locker, owner })
3062 }
3063}
3064
3065impl<T: Config> WrapVersion for Pallet<T> {
3066 fn wrap_version<RuntimeCall>(
3067 dest: &Location,
3068 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3069 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3070 Self::get_version_for(dest)
3071 .or_else(|| {
3072 Self::note_unknown_version(dest);
3073 SafeXcmVersion::<T>::get()
3074 })
3075 .ok_or_else(|| {
3076 log::trace!(
3077 target: "xcm::pallet_xcm::wrap_version",
3078 "Could not determine a version to wrap XCM for destination: {:?}",
3079 dest,
3080 );
3081 ()
3082 })
3083 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3084 }
3085}
3086
3087impl<T: Config> GetVersion for Pallet<T> {
3088 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3089 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3090 }
3091}
3092
3093impl<T: Config> VersionChangeNotifier for Pallet<T> {
3094 fn start(
3103 dest: &Location,
3104 query_id: QueryId,
3105 max_weight: Weight,
3106 _context: &XcmContext,
3107 ) -> XcmResult {
3108 let versioned_dest = LatestVersionedLocation(dest);
3109 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3110 ensure!(!already, XcmError::InvalidLocation);
3111
3112 let xcm_version = T::AdvertisedXcmVersion::get();
3113 let response = Response::Version(xcm_version);
3114 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3115 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3116 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3117 destination: dest.clone(),
3118 cost,
3119 message_id,
3120 });
3121
3122 let value = (query_id, max_weight, xcm_version);
3123 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3124 Ok(())
3125 }
3126
3127 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3130 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3131 Ok(())
3132 }
3133
3134 fn is_subscribed(dest: &Location) -> bool {
3136 let versioned_dest = LatestVersionedLocation(dest);
3137 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3138 }
3139}
3140
3141impl<T: Config> DropAssets for Pallet<T> {
3142 fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
3143 if assets.is_empty() {
3144 return Weight::zero()
3145 }
3146 let versioned = VersionedAssets::from(Assets::from(assets));
3147 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3148 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3149 Self::deposit_event(Event::AssetsTrapped {
3150 hash,
3151 origin: origin.clone(),
3152 assets: versioned,
3153 });
3154 Weight::zero()
3156 }
3157}
3158
3159impl<T: Config> ClaimAssets for Pallet<T> {
3160 fn claim_assets(
3161 origin: &Location,
3162 ticket: &Location,
3163 assets: &Assets,
3164 _context: &XcmContext,
3165 ) -> bool {
3166 let mut versioned = VersionedAssets::from(assets.clone());
3167 match ticket.unpack() {
3168 (0, [GeneralIndex(i)]) =>
3169 versioned = match versioned.into_version(*i as u32) {
3170 Ok(v) => v,
3171 Err(()) => return false,
3172 },
3173 (0, []) => (),
3174 _ => return false,
3175 };
3176 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3177 match AssetTraps::<T>::get(hash) {
3178 0 => return false,
3179 1 => AssetTraps::<T>::remove(hash),
3180 n => AssetTraps::<T>::insert(hash, n - 1),
3181 }
3182 Self::deposit_event(Event::AssetsClaimed {
3183 hash,
3184 origin: origin.clone(),
3185 assets: versioned,
3186 });
3187 return true
3188 }
3189}
3190
3191impl<T: Config> OnResponse for Pallet<T> {
3192 fn expecting_response(
3193 origin: &Location,
3194 query_id: QueryId,
3195 querier: Option<&Location>,
3196 ) -> bool {
3197 match Queries::<T>::get(query_id) {
3198 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) =>
3199 Location::try_from(responder).map_or(false, |r| origin == &r) &&
3200 maybe_match_querier.map_or(true, |match_querier| {
3201 Location::try_from(match_querier).map_or(false, |match_querier| {
3202 querier.map_or(false, |q| q == &match_querier)
3203 })
3204 }),
3205 Some(QueryStatus::VersionNotifier { origin: r, .. }) =>
3206 Location::try_from(r).map_or(false, |r| origin == &r),
3207 _ => false,
3208 }
3209 }
3210
3211 fn on_response(
3212 origin: &Location,
3213 query_id: QueryId,
3214 querier: Option<&Location>,
3215 response: Response,
3216 max_weight: Weight,
3217 _context: &XcmContext,
3218 ) -> Weight {
3219 let origin = origin.clone();
3220 match (response, Queries::<T>::get(query_id)) {
3221 (
3222 Response::Version(v),
3223 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
3224 ) => {
3225 let origin: Location = match expected_origin.try_into() {
3226 Ok(o) if o == origin => o,
3227 Ok(o) => {
3228 Self::deposit_event(Event::InvalidResponder {
3229 origin: origin.clone(),
3230 query_id,
3231 expected_location: Some(o),
3232 });
3233 return Weight::zero()
3234 },
3235 _ => {
3236 Self::deposit_event(Event::InvalidResponder {
3237 origin: origin.clone(),
3238 query_id,
3239 expected_location: None,
3240 });
3241 return Weight::zero()
3243 },
3244 };
3245 if !is_active {
3247 Queries::<T>::insert(
3248 query_id,
3249 QueryStatus::VersionNotifier {
3250 origin: origin.clone().into(),
3251 is_active: true,
3252 },
3253 );
3254 }
3255 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
3257 Self::deposit_event(Event::SupportedVersionChanged {
3258 location: origin,
3259 version: v,
3260 });
3261 Weight::zero()
3262 },
3263 (
3264 response,
3265 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
3266 ) => {
3267 if let Some(match_querier) = maybe_match_querier {
3268 let match_querier = match Location::try_from(match_querier) {
3269 Ok(mq) => mq,
3270 Err(_) => {
3271 Self::deposit_event(Event::InvalidQuerierVersion {
3272 origin: origin.clone(),
3273 query_id,
3274 });
3275 return Weight::zero()
3276 },
3277 };
3278 if querier.map_or(true, |q| q != &match_querier) {
3279 Self::deposit_event(Event::InvalidQuerier {
3280 origin: origin.clone(),
3281 query_id,
3282 expected_querier: match_querier,
3283 maybe_actual_querier: querier.cloned(),
3284 });
3285 return Weight::zero()
3286 }
3287 }
3288 let responder = match Location::try_from(responder) {
3289 Ok(r) => r,
3290 Err(_) => {
3291 Self::deposit_event(Event::InvalidResponderVersion {
3292 origin: origin.clone(),
3293 query_id,
3294 });
3295 return Weight::zero()
3296 },
3297 };
3298 if origin != responder {
3299 Self::deposit_event(Event::InvalidResponder {
3300 origin: origin.clone(),
3301 query_id,
3302 expected_location: Some(responder),
3303 });
3304 return Weight::zero()
3305 }
3306 match maybe_notify {
3307 Some((pallet_index, call_index)) => {
3308 let bare = (pallet_index, call_index, query_id, response);
3312 if let Ok(call) = bare.using_encoded(|mut bytes| {
3313 <T as Config>::RuntimeCall::decode(&mut bytes)
3314 }) {
3315 Queries::<T>::remove(query_id);
3316 let weight = call.get_dispatch_info().weight;
3317 if weight.any_gt(max_weight) {
3318 let e = Event::NotifyOverweight {
3319 query_id,
3320 pallet_index,
3321 call_index,
3322 actual_weight: weight,
3323 max_budgeted_weight: max_weight,
3324 };
3325 Self::deposit_event(e);
3326 return Weight::zero()
3327 }
3328 let dispatch_origin = Origin::Response(origin.clone()).into();
3329 match call.dispatch(dispatch_origin) {
3330 Ok(post_info) => {
3331 let e = Event::Notified { query_id, pallet_index, call_index };
3332 Self::deposit_event(e);
3333 post_info.actual_weight
3334 },
3335 Err(error_and_info) => {
3336 let e = Event::NotifyDispatchError {
3337 query_id,
3338 pallet_index,
3339 call_index,
3340 };
3341 Self::deposit_event(e);
3342 error_and_info.post_info.actual_weight
3345 },
3346 }
3347 .unwrap_or(weight)
3348 } else {
3349 let e =
3350 Event::NotifyDecodeFailed { query_id, pallet_index, call_index };
3351 Self::deposit_event(e);
3352 Weight::zero()
3353 }
3354 },
3355 None => {
3356 let e = Event::ResponseReady { query_id, response: response.clone() };
3357 Self::deposit_event(e);
3358 let at = frame_system::Pallet::<T>::current_block_number();
3359 let response = response.into();
3360 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
3361 Weight::zero()
3362 },
3363 }
3364 },
3365 _ => {
3366 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
3367 Self::deposit_event(e);
3368 Weight::zero()
3369 },
3370 }
3371 }
3372}
3373
3374impl<T: Config> CheckSuspension for Pallet<T> {
3375 fn is_suspended<Call>(
3376 _origin: &Location,
3377 _instructions: &mut [Instruction<Call>],
3378 _max_weight: Weight,
3379 _properties: &mut Properties,
3380 ) -> bool {
3381 XcmExecutionSuspended::<T>::get()
3382 }
3383}
3384
3385impl<T: Config> RecordXcm for Pallet<T> {
3386 fn should_record() -> bool {
3387 ShouldRecordXcm::<T>::get()
3388 }
3389
3390 fn set_record_xcm(enabled: bool) {
3391 ShouldRecordXcm::<T>::put(enabled);
3392 }
3393
3394 fn recorded_xcm() -> Option<Xcm<()>> {
3395 RecordedXcm::<T>::get()
3396 }
3397
3398 fn record(xcm: Xcm<()>) {
3399 RecordedXcm::<T>::put(xcm);
3400 }
3401}
3402
3403pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3407where
3408 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3409{
3410 match o.into() {
3411 Ok(Origin::Xcm(location)) => Ok(location),
3412 _ => Err(BadOrigin),
3413 }
3414}
3415
3416pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
3420where
3421 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
3422{
3423 match o.into() {
3424 Ok(Origin::Response(location)) => Ok(location),
3425 _ => Err(BadOrigin),
3426 }
3427}
3428
3429pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3434impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
3435 for IsMajorityOfBody<Prefix, Body>
3436{
3437 fn contains(l: &Location) -> bool {
3438 let maybe_suffix = l.match_and_split(&Prefix::get());
3439 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
3440 }
3441}
3442
3443pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
3447impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
3448 fn contains(l: &Location) -> bool {
3449 let maybe_suffix = l.match_and_split(&Prefix::get());
3450 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
3451 }
3452}
3453
3454pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
3457impl<
3458 O: OriginTrait + From<Origin>,
3459 F: Contains<L>,
3460 L: TryFrom<Location> + TryInto<Location> + Clone,
3461 > EnsureOrigin<O> for EnsureXcm<F, L>
3462where
3463 O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
3464{
3465 type Success = L;
3466
3467 fn try_origin(outer: O) -> Result<Self::Success, O> {
3468 outer.try_with_caller(|caller| {
3469 caller.try_into().and_then(|o| match o {
3470 Origin::Xcm(ref location)
3471 if F::contains(&location.clone().try_into().map_err(|_| o.clone().into())?) =>
3472 Ok(location.clone().try_into().map_err(|_| o.clone().into())?),
3473 Origin::Xcm(location) => Err(Origin::Xcm(location).into()),
3474 o => Err(o.into()),
3475 })
3476 })
3477 }
3478
3479 #[cfg(feature = "runtime-benchmarks")]
3480 fn try_successful_origin() -> Result<O, ()> {
3481 Ok(O::from(Origin::Xcm(Here.into())))
3482 }
3483}
3484
3485pub struct EnsureResponse<F>(PhantomData<F>);
3488impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
3489where
3490 O::PalletsOrigin: From<Origin> + TryInto<Origin, Error = O::PalletsOrigin>,
3491{
3492 type Success = Location;
3493
3494 fn try_origin(outer: O) -> Result<Self::Success, O> {
3495 outer.try_with_caller(|caller| {
3496 caller.try_into().and_then(|o| match o {
3497 Origin::Response(responder) => Ok(responder),
3498 o => Err(o.into()),
3499 })
3500 })
3501 }
3502
3503 #[cfg(feature = "runtime-benchmarks")]
3504 fn try_successful_origin() -> Result<O, ()> {
3505 Ok(O::from(Origin::Response(Here.into())))
3506 }
3507}
3508
3509pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
3512impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
3513 for XcmPassthrough<RuntimeOrigin>
3514{
3515 fn convert_origin(
3516 origin: impl Into<Location>,
3517 kind: OriginKind,
3518 ) -> Result<RuntimeOrigin, Location> {
3519 let origin = origin.into();
3520 match kind {
3521 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
3522 _ => Err(origin),
3523 }
3524 }
3525}