1#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "runtime-benchmarks")]
22pub mod benchmarking;
23#[cfg(test)]
24mod mock;
25#[cfg(test)]
26mod tests;
27mod transfer_assets_validation;
28
29pub mod migration;
30#[cfg(any(test, feature = "test-utils"))]
31pub mod xcm_helpers;
32
33extern crate alloc;
34
35use alloc::{boxed::Box, vec, vec::Vec};
36use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
37use core::{marker::PhantomData, result::Result};
38use pezframe_support::{
39 dispatch::{
40 DispatchErrorWithPostInfo, GetDispatchInfo, PostDispatchInfo, WithPostDispatchInfo,
41 },
42 pezpallet_prelude::*,
43 traits::{
44 Consideration, Contains, ContainsPair, Currency, Defensive, EnsureOrigin, Footprint, Get,
45 LockableCurrency, OriginTrait, WithdrawReasons,
46 },
47 PalletId,
48};
49use pezframe_system::pezpallet_prelude::{BlockNumberFor, *};
50pub use pezpallet::*;
51use pezsp_core::H256;
52use pezsp_runtime::{
53 traits::{
54 AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
55 Saturating, Zero,
56 },
57 Either, RuntimeDebug, SaturatedConversion,
58};
59use scale_info::TypeInfo;
60use storage::{with_transaction, TransactionOutcome};
61use xcm::{latest::QueryResponseInfo, prelude::*};
62use xcm_builder::{
63 ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController,
64 QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
65};
66use xcm_executor::{
67 traits::{
68 AssetTransferError, CheckSuspension, ClaimAssets, ConvertLocation, ConvertOrigin,
69 DropAssets, EventEmitter, FeeManager, FeeReason, MatchesFungible, OnResponse, Properties,
70 QueryHandler, QueryResponseStatus, RecordXcm, TransactAsset, TransferType,
71 VersionChangeNotifier, WeightBounds, XcmAssetTransfers,
72 },
73 AssetsInHolding,
74};
75use xcm_runtime_pezapis::{
76 authorized_aliases::{Error as AuthorizedAliasersApiError, OriginAliaser},
77 dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects},
78 fees::Error as XcmPaymentApiError,
79 trusted_query::Error as TrustedQueryApiError,
80};
81
82mod errors;
83pub use errors::ExecutionError;
84
85#[cfg(any(feature = "try-runtime", test))]
86use pezsp_runtime::TryRuntimeError;
87
88pub trait WeightInfo {
89 fn send() -> Weight;
90 fn teleport_assets() -> Weight;
91 fn reserve_transfer_assets() -> Weight;
92 fn transfer_assets() -> Weight;
93 fn execute() -> Weight;
94 fn force_xcm_version() -> Weight;
95 fn force_default_xcm_version() -> Weight;
96 fn force_subscribe_version_notify() -> Weight;
97 fn force_unsubscribe_version_notify() -> Weight;
98 fn force_suspension() -> Weight;
99 fn migrate_supported_version() -> Weight;
100 fn migrate_version_notifiers() -> Weight;
101 fn already_notified_target() -> Weight;
102 fn notify_current_targets() -> Weight;
103 fn notify_target_migration_fail() -> Weight;
104 fn migrate_version_notify_targets() -> Weight;
105 fn migrate_and_notify_old_targets() -> Weight;
106 fn new_query() -> Weight;
107 fn take_response() -> Weight;
108 fn claim_assets() -> Weight;
109 fn add_authorized_alias() -> Weight;
110 fn remove_authorized_alias() -> Weight;
111
112 fn weigh_message() -> Weight;
113}
114
115pub struct TestWeightInfo;
117impl WeightInfo for TestWeightInfo {
118 fn send() -> Weight {
119 Weight::from_parts(100_000_000, 0)
120 }
121
122 fn teleport_assets() -> Weight {
123 Weight::from_parts(100_000_000, 0)
124 }
125
126 fn reserve_transfer_assets() -> Weight {
127 Weight::from_parts(100_000_000, 0)
128 }
129
130 fn transfer_assets() -> Weight {
131 Weight::from_parts(100_000_000, 0)
132 }
133
134 fn execute() -> Weight {
135 Weight::from_parts(100_000_000, 0)
136 }
137
138 fn force_xcm_version() -> Weight {
139 Weight::from_parts(100_000_000, 0)
140 }
141
142 fn force_default_xcm_version() -> Weight {
143 Weight::from_parts(100_000_000, 0)
144 }
145
146 fn force_subscribe_version_notify() -> Weight {
147 Weight::from_parts(100_000_000, 0)
148 }
149
150 fn force_unsubscribe_version_notify() -> Weight {
151 Weight::from_parts(100_000_000, 0)
152 }
153
154 fn force_suspension() -> Weight {
155 Weight::from_parts(100_000_000, 0)
156 }
157
158 fn migrate_supported_version() -> Weight {
159 Weight::from_parts(100_000_000, 0)
160 }
161
162 fn migrate_version_notifiers() -> Weight {
163 Weight::from_parts(100_000_000, 0)
164 }
165
166 fn already_notified_target() -> Weight {
167 Weight::from_parts(100_000_000, 0)
168 }
169
170 fn notify_current_targets() -> Weight {
171 Weight::from_parts(100_000_000, 0)
172 }
173
174 fn notify_target_migration_fail() -> Weight {
175 Weight::from_parts(100_000_000, 0)
176 }
177
178 fn migrate_version_notify_targets() -> Weight {
179 Weight::from_parts(100_000_000, 0)
180 }
181
182 fn migrate_and_notify_old_targets() -> Weight {
183 Weight::from_parts(100_000_000, 0)
184 }
185
186 fn new_query() -> Weight {
187 Weight::from_parts(100_000_000, 0)
188 }
189
190 fn take_response() -> Weight {
191 Weight::from_parts(100_000_000, 0)
192 }
193
194 fn claim_assets() -> Weight {
195 Weight::from_parts(100_000_000, 0)
196 }
197
198 fn add_authorized_alias() -> Weight {
199 Weight::from_parts(100_000, 0)
200 }
201
202 fn remove_authorized_alias() -> Weight {
203 Weight::from_parts(100_000, 0)
204 }
205
206 fn weigh_message() -> Weight {
207 Weight::from_parts(100_000, 0)
208 }
209}
210
211#[derive(Clone, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
212pub struct AuthorizedAliasesEntry<Ticket, MAX: Get<u32>> {
213 pub aliasers: BoundedVec<OriginAliaser, MAX>,
214 pub ticket: Ticket,
215}
216
217pub fn aliasers_footprint(aliasers_count: usize) -> Footprint {
218 Footprint::from_parts(aliasers_count, OriginAliaser::max_encoded_len())
219}
220
221#[pezframe_support::pezpallet]
222pub mod pezpallet {
223 use super::*;
224 use pezframe_support::{
225 dispatch::{GetDispatchInfo, PostDispatchInfo},
226 parameter_types,
227 };
228 use pezframe_system::Config as SysConfig;
229 use pezsp_runtime::traits::Dispatchable;
230 use xcm_executor::traits::{MatchesFungible, WeightBounds};
231
232 parameter_types! {
233 pub const CurrentXcmVersion: u32 = XCM_VERSION;
236
237 #[derive(Debug, TypeInfo)]
238 pub const MaxAuthorizedAliases: u32 = 10;
240 }
241
242 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
243
244 #[pezpallet::pezpallet]
245 #[pezpallet::storage_version(STORAGE_VERSION)]
246 #[pezpallet::without_storage_info]
247 pub struct Pezpallet<T>(_);
248
249 pub type BalanceOf<T> =
250 <<T as Config>::Currency as Currency<<T as pezframe_system::Config>::AccountId>>::Balance;
251 pub type TicketOf<T> = <T as Config>::AuthorizedAliasConsideration;
252
253 #[pezpallet::config]
254 pub trait Config: pezframe_system::Config {
256 #[allow(deprecated)]
258 type RuntimeEvent: From<Event<Self>>
259 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
260
261 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>;
264
265 type CurrencyMatcher: MatchesFungible<BalanceOf<Self>>;
267
268 type AuthorizedAliasConsideration: Consideration<Self::AccountId, Footprint>;
270
271 type SendXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
274
275 type XcmRouter: SendXcm;
277
278 type ExecuteXcmOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin, Success = Location>;
282
283 type XcmExecuteFilter: Contains<(Location, Xcm<<Self as Config>::RuntimeCall>)>;
285
286 type XcmExecutor: ExecuteXcm<<Self as Config>::RuntimeCall> + XcmAssetTransfers + FeeManager;
288
289 type XcmTeleportFilter: Contains<(Location, Vec<Asset>)>;
291
292 type XcmReserveTransferFilter: Contains<(Location, Vec<Asset>)>;
295
296 type Weigher: WeightBounds<<Self as Config>::RuntimeCall>;
298
299 #[pezpallet::constant]
301 type UniversalLocation: Get<InteriorLocation>;
302
303 type RuntimeOrigin: From<Origin> + From<<Self as SysConfig>::RuntimeOrigin>;
305
306 type RuntimeCall: Parameter
308 + GetDispatchInfo
309 + Dispatchable<
310 RuntimeOrigin = <Self as Config>::RuntimeOrigin,
311 PostInfo = PostDispatchInfo,
312 >;
313
314 const VERSION_DISCOVERY_QUEUE_SIZE: u32;
315
316 #[pezpallet::constant]
319 type AdvertisedXcmVersion: Get<XcmVersion>;
320
321 type AdminOrigin: EnsureOrigin<<Self as SysConfig>::RuntimeOrigin>;
323
324 type TrustedLockers: ContainsPair<Location, Asset>;
327
328 type SovereignAccountOf: ConvertLocation<Self::AccountId>;
330
331 #[pezpallet::constant]
333 type MaxLockers: Get<u32>;
334
335 #[pezpallet::constant]
337 type MaxRemoteLockConsumers: Get<u32>;
338
339 type RemoteLockConsumerIdentifier: Parameter + Member + MaxEncodedLen + Ord + Copy;
341
342 type WeightInfo: WeightInfo;
344 }
345
346 impl<T: Config> ExecuteControllerWeightInfo for Pezpallet<T> {
347 fn execute() -> Weight {
348 T::WeightInfo::execute()
349 }
350 }
351
352 impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pezpallet<T> {
353 type WeightInfo = Self;
354 fn execute(
355 origin: OriginFor<T>,
356 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
357 max_weight: Weight,
358 ) -> Result<Weight, DispatchErrorWithPostInfo> {
359 tracing::trace!(target: "xcm::pezpallet_xcm::execute", ?message, ?max_weight);
360 let outcome = (|| {
361 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
362 let mut hash = message.using_encoded(pezsp_io::hashing::blake2_256);
363 let message = (*message).try_into().map_err(|()| {
364 tracing::debug!(
365 target: "xcm::pezpallet_xcm::execute", id=?hash,
366 "Failed to convert VersionedXcm to Xcm",
367 );
368 Error::<T>::BadVersion
369 })?;
370 let value = (origin_location, message);
371 ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
372 let (origin_location, message) = value;
373 Ok(T::XcmExecutor::prepare_and_execute(
374 origin_location,
375 message,
376 &mut hash,
377 max_weight,
378 max_weight,
379 ))
380 })()
381 .map_err(|e: DispatchError| {
382 tracing::debug!(
383 target: "xcm::pezpallet_xcm::execute", error=?e,
384 "Failed XCM pre-execution validation or filter",
385 );
386 e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
387 })?;
388
389 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
390 let weight_used = outcome.weight_used();
391 outcome.ensure_complete().map_err(|error| {
392 tracing::error!(target: "xcm::pezpallet_xcm::execute", ?error, "XCM execution failed with error");
393 Error::<T>::LocalExecutionIncompleteWithError {
394 index: error.index,
395 error: error.error.into(),
396 }
397 .with_weight(
398 weight_used.saturating_add(
399 <Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
400 ),
401 )
402 })?;
403 Ok(weight_used)
404 }
405 }
406
407 impl<T: Config> SendControllerWeightInfo for Pezpallet<T> {
408 fn send() -> Weight {
409 T::WeightInfo::send()
410 }
411 }
412
413 impl<T: Config> SendController<OriginFor<T>> for Pezpallet<T> {
414 type WeightInfo = Self;
415 fn send(
416 origin: OriginFor<T>,
417 dest: Box<VersionedLocation>,
418 message: Box<VersionedXcm<()>>,
419 ) -> Result<XcmHash, DispatchError> {
420 let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
421 let interior: Junctions = origin_location.clone().try_into().map_err(|_| {
422 tracing::debug!(
423 target: "xcm::pezpallet_xcm::send",
424 "Failed to convert origin_location to interior Junctions",
425 );
426 Error::<T>::InvalidOrigin
427 })?;
428 let dest = Location::try_from(*dest).map_err(|()| {
429 tracing::debug!(
430 target: "xcm::pezpallet_xcm::send",
431 "Failed to convert destination VersionedLocation to Location",
432 );
433 Error::<T>::BadVersion
434 })?;
435 let message: Xcm<()> = (*message).try_into().map_err(|()| {
436 tracing::debug!(
437 target: "xcm::pezpallet_xcm::send",
438 "Failed to convert VersionedXcm message to Xcm",
439 );
440 Error::<T>::BadVersion
441 })?;
442
443 let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
444 .map_err(|error| {
445 tracing::error!(target: "xcm::pezpallet_xcm::send", ?error, ?dest, ?message, "XCM send failed with error");
446 Error::<T>::from(error)
447 })?;
448 let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
449 Self::deposit_event(e);
450 Ok(message_id)
451 }
452 }
453
454 impl<T: Config> QueryControllerWeightInfo for Pezpallet<T> {
455 fn query() -> Weight {
456 T::WeightInfo::new_query()
457 }
458 fn take_response() -> Weight {
459 T::WeightInfo::take_response()
460 }
461 }
462
463 impl<T: Config> QueryController<OriginFor<T>, BlockNumberFor<T>> for Pezpallet<T> {
464 type WeightInfo = Self;
465
466 fn query(
467 origin: OriginFor<T>,
468 timeout: BlockNumberFor<T>,
469 match_querier: VersionedLocation,
470 ) -> Result<QueryId, DispatchError> {
471 let responder = <T as Config>::ExecuteXcmOrigin::ensure_origin(origin)?;
472 let query_id = <Self as QueryHandler>::new_query(
473 responder,
474 timeout,
475 Location::try_from(match_querier).map_err(|_| {
476 tracing::debug!(
477 target: "xcm::pezpallet_xcm::query",
478 "Failed to convert VersionedLocation for match_querier",
479 );
480 Into::<DispatchError>::into(Error::<T>::BadVersion)
481 })?,
482 );
483
484 Ok(query_id)
485 }
486 }
487
488 impl<T: Config> EventEmitter for Pezpallet<T> {
489 fn emit_sent_event(
490 origin: Location,
491 destination: Location,
492 message: Option<Xcm<()>>,
493 message_id: XcmHash,
494 ) {
495 Self::deposit_event(Event::Sent {
496 origin,
497 destination,
498 message: message.unwrap_or_default(),
499 message_id,
500 });
501 }
502
503 fn emit_send_failure_event(
504 origin: Location,
505 destination: Location,
506 error: SendError,
507 message_id: XcmHash,
508 ) {
509 Self::deposit_event(Event::SendFailed { origin, destination, error, message_id });
510 }
511
512 fn emit_process_failure_event(origin: Location, error: XcmError, message_id: XcmHash) {
513 Self::deposit_event(Event::ProcessXcmError { origin, error, message_id });
514 }
515 }
516
517 #[pezpallet::event]
518 #[pezpallet::generate_deposit(pub(super) fn deposit_event)]
519 pub enum Event<T: Config> {
520 Attempted { outcome: xcm::latest::Outcome },
522 Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash },
524 SendFailed {
526 origin: Location,
527 destination: Location,
528 error: SendError,
529 message_id: XcmHash,
530 },
531 ProcessXcmError { origin: Location, error: XcmError, message_id: XcmHash },
533 UnexpectedResponse { origin: Location, query_id: QueryId },
537 ResponseReady { query_id: QueryId, response: Response },
540 Notified { query_id: QueryId, pezpallet_index: u8, call_index: u8 },
543 NotifyOverweight {
547 query_id: QueryId,
548 pezpallet_index: u8,
549 call_index: u8,
550 actual_weight: Weight,
551 max_budgeted_weight: Weight,
552 },
553 NotifyDispatchError { query_id: QueryId, pezpallet_index: u8, call_index: u8 },
556 NotifyDecodeFailed { query_id: QueryId, pezpallet_index: u8, call_index: u8 },
560 InvalidResponder {
564 origin: Location,
565 query_id: QueryId,
566 expected_location: Option<Location>,
567 },
568 InvalidResponderVersion { origin: Location, query_id: QueryId },
576 ResponseTaken { query_id: QueryId },
578 AssetsTrapped { hash: H256, origin: Location, assets: VersionedAssets },
580 VersionChangeNotified {
584 destination: Location,
585 result: XcmVersion,
586 cost: Assets,
587 message_id: XcmHash,
588 },
589 SupportedVersionChanged { location: Location, version: XcmVersion },
592 NotifyTargetSendFail { location: Location, query_id: QueryId, error: XcmError },
595 NotifyTargetMigrationFail { location: VersionedLocation, query_id: QueryId },
598 InvalidQuerierVersion { origin: Location, query_id: QueryId },
606 InvalidQuerier {
610 origin: Location,
611 query_id: QueryId,
612 expected_querier: Location,
613 maybe_actual_querier: Option<Location>,
614 },
615 VersionNotifyStarted { destination: Location, cost: Assets, message_id: XcmHash },
618 VersionNotifyRequested { destination: Location, cost: Assets, message_id: XcmHash },
620 VersionNotifyUnrequested { destination: Location, cost: Assets, message_id: XcmHash },
623 FeesPaid { paying: Location, fees: Assets },
625 AssetsClaimed { hash: H256, origin: Location, assets: VersionedAssets },
627 VersionMigrationFinished { version: XcmVersion },
629 AliasAuthorized { aliaser: Location, target: Location, expiry: Option<u64> },
632 AliasAuthorizationRemoved { aliaser: Location, target: Location },
634 AliasesAuthorizationsRemoved { target: Location },
636 }
637
638 #[pezpallet::origin]
639 #[derive(
640 PartialEq,
641 Eq,
642 Clone,
643 Encode,
644 Decode,
645 DecodeWithMemTracking,
646 RuntimeDebug,
647 TypeInfo,
648 MaxEncodedLen,
649 )]
650 pub enum Origin {
651 Xcm(Location),
653 Response(Location),
655 }
656 impl From<Location> for Origin {
657 fn from(location: Location) -> Origin {
658 Origin::Xcm(location)
659 }
660 }
661
662 #[pezpallet::composite_enum]
664 pub enum HoldReason {
665 AuthorizeAlias,
667 }
668
669 #[pezpallet::error]
670 pub enum Error<T> {
671 Unreachable,
674 SendFailure,
677 Filtered,
679 UnweighableMessage,
681 DestinationNotInvertible,
683 Empty,
685 CannotReanchor,
687 TooManyAssets,
689 InvalidOrigin,
691 BadVersion,
693 BadLocation,
696 NoSubscription,
698 AlreadySubscribed,
700 CannotCheckOutTeleport,
702 LowBalance,
704 TooManyLocks,
706 AccountNotSovereign,
708 FeesNotMet,
710 LockNotFound,
712 InUse,
714 #[codec(index = 21)]
716 InvalidAssetUnknownReserve,
717 #[codec(index = 22)]
719 InvalidAssetUnsupportedReserve,
720 #[codec(index = 23)]
722 TooManyReserves,
723 #[deprecated(since = "20.0.0", note = "Use `LocalExecutionIncompleteWithError` instead")]
725 #[codec(index = 24)]
726 LocalExecutionIncomplete,
727 #[codec(index = 25)]
729 TooManyAuthorizedAliases,
730 #[codec(index = 26)]
732 ExpiresInPast,
733 #[codec(index = 27)]
735 AliasNotFound,
736 #[codec(index = 28)]
739 LocalExecutionIncompleteWithError { index: InstructionIndex, error: ExecutionError },
740 }
741
742 impl<T: Config> From<SendError> for Error<T> {
743 fn from(e: SendError) -> Self {
744 match e {
745 SendError::Fees => Error::<T>::FeesNotMet,
746 SendError::NotApplicable => Error::<T>::Unreachable,
747 _ => Error::<T>::SendFailure,
748 }
749 }
750 }
751
752 impl<T: Config> From<AssetTransferError> for Error<T> {
753 fn from(e: AssetTransferError) -> Self {
754 match e {
755 AssetTransferError::UnknownReserve => Error::<T>::InvalidAssetUnknownReserve,
756 }
757 }
758 }
759
760 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
762 pub enum QueryStatus<BlockNumber> {
763 Pending {
765 responder: VersionedLocation,
768 maybe_match_querier: Option<VersionedLocation>,
771 maybe_notify: Option<(u8, u8)>,
772 timeout: BlockNumber,
773 },
774 VersionNotifier { origin: VersionedLocation, is_active: bool },
776 Ready { response: VersionedResponse, at: BlockNumber },
778 }
779
780 #[derive(Copy, Clone)]
781 pub(crate) struct LatestVersionedLocation<'a>(pub(crate) &'a Location);
782 impl<'a> EncodeLike<VersionedLocation> for LatestVersionedLocation<'a> {}
783 impl<'a> Encode for LatestVersionedLocation<'a> {
784 fn encode(&self) -> Vec<u8> {
785 let mut r = VersionedLocation::from(Location::default()).encode();
786 r.truncate(1);
787 self.0.using_encoded(|d| r.extend_from_slice(d));
788 r
789 }
790 }
791
792 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo)]
793 pub enum VersionMigrationStage {
794 MigrateSupportedVersion,
795 MigrateVersionNotifiers,
796 NotifyCurrentTargets(Option<Vec<u8>>),
797 MigrateAndNotifyOldTargets,
798 }
799
800 impl Default for VersionMigrationStage {
801 fn default() -> Self {
802 Self::MigrateSupportedVersion
803 }
804 }
805
806 #[pezpallet::storage]
808 pub(super) type QueryCounter<T: Config> = StorageValue<_, QueryId, ValueQuery>;
809
810 #[pezpallet::storage]
812 pub(super) type Queries<T: Config> =
813 StorageMap<_, Blake2_128Concat, QueryId, QueryStatus<BlockNumberFor<T>>, OptionQuery>;
814
815 #[pezpallet::storage]
820 pub(super) type AssetTraps<T: Config> = StorageMap<_, Identity, H256, u32, ValueQuery>;
821
822 #[pezpallet::storage]
825 #[pezpallet::whitelist_storage]
826 pub(super) type SafeXcmVersion<T: Config> = StorageValue<_, XcmVersion, OptionQuery>;
827
828 #[pezpallet::storage]
830 pub(super) type SupportedVersion<T: Config> = StorageDoubleMap<
831 _,
832 Twox64Concat,
833 XcmVersion,
834 Blake2_128Concat,
835 VersionedLocation,
836 XcmVersion,
837 OptionQuery,
838 >;
839
840 #[pezpallet::storage]
842 pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
843 _,
844 Twox64Concat,
845 XcmVersion,
846 Blake2_128Concat,
847 VersionedLocation,
848 QueryId,
849 OptionQuery,
850 >;
851
852 #[pezpallet::storage]
855 pub(super) type VersionNotifyTargets<T: Config> = StorageDoubleMap<
856 _,
857 Twox64Concat,
858 XcmVersion,
859 Blake2_128Concat,
860 VersionedLocation,
861 (QueryId, Weight, XcmVersion),
862 OptionQuery,
863 >;
864
865 pub struct VersionDiscoveryQueueSize<T>(PhantomData<T>);
866 impl<T: Config> Get<u32> for VersionDiscoveryQueueSize<T> {
867 fn get() -> u32 {
868 T::VERSION_DISCOVERY_QUEUE_SIZE
869 }
870 }
871
872 #[pezpallet::storage]
876 #[pezpallet::whitelist_storage]
877 pub(super) type VersionDiscoveryQueue<T: Config> = StorageValue<
878 _,
879 BoundedVec<(VersionedLocation, u32), VersionDiscoveryQueueSize<T>>,
880 ValueQuery,
881 >;
882
883 #[pezpallet::storage]
885 pub(super) type CurrentMigration<T: Config> =
886 StorageValue<_, VersionMigrationStage, OptionQuery>;
887
888 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, TypeInfo, MaxEncodedLen)]
889 #[scale_info(skip_type_params(MaxConsumers))]
890 pub struct RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers: Get<u32>> {
891 pub amount: u128,
893 pub owner: VersionedLocation,
895 pub locker: VersionedLocation,
897 pub consumers: BoundedVec<(ConsumerIdentifier, u128), MaxConsumers>,
901 }
902
903 impl<LockId, MaxConsumers: Get<u32>> RemoteLockedFungibleRecord<LockId, MaxConsumers> {
904 pub fn amount_held(&self) -> Option<u128> {
907 self.consumers.iter().max_by(|x, y| x.1.cmp(&y.1)).map(|max| max.1)
908 }
909 }
910
911 #[pezpallet::storage]
913 pub(super) type RemoteLockedFungibles<T: Config> = StorageNMap<
914 _,
915 (
916 NMapKey<Twox64Concat, XcmVersion>,
917 NMapKey<Blake2_128Concat, T::AccountId>,
918 NMapKey<Blake2_128Concat, VersionedAssetId>,
919 ),
920 RemoteLockedFungibleRecord<T::RemoteLockConsumerIdentifier, T::MaxRemoteLockConsumers>,
921 OptionQuery,
922 >;
923
924 #[pezpallet::storage]
926 pub(super) type LockedFungibles<T: Config> = StorageMap<
927 _,
928 Blake2_128Concat,
929 T::AccountId,
930 BoundedVec<(BalanceOf<T>, VersionedLocation), T::MaxLockers>,
931 OptionQuery,
932 >;
933
934 #[pezpallet::storage]
936 pub(super) type XcmExecutionSuspended<T: Config> = StorageValue<_, bool, ValueQuery>;
937
938 #[pezpallet::storage]
946 pub(crate) type ShouldRecordXcm<T: Config> = StorageValue<_, bool, ValueQuery>;
947
948 #[pezpallet::storage]
955 pub(crate) type RecordedXcm<T: Config> = StorageValue<_, Xcm<()>>;
956
957 #[pezpallet::storage]
961 pub(super) type AuthorizedAliases<T: Config> = StorageMap<
962 _,
963 Blake2_128Concat,
964 VersionedLocation,
965 AuthorizedAliasesEntry<TicketOf<T>, MaxAuthorizedAliases>,
966 OptionQuery,
967 >;
968
969 #[pezpallet::genesis_config]
970 pub struct GenesisConfig<T: Config> {
971 #[serde(skip)]
972 pub _config: core::marker::PhantomData<T>,
973 pub safe_xcm_version: Option<XcmVersion>,
975 pub supported_version: Vec<(Location, XcmVersion)>,
977 }
978
979 impl<T: Config> Default for GenesisConfig<T> {
980 fn default() -> Self {
981 Self {
982 _config: Default::default(),
983 safe_xcm_version: Some(XCM_VERSION),
984 supported_version: Vec::new(),
985 }
986 }
987 }
988
989 #[pezpallet::genesis_build]
990 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
991 fn build(&self) {
992 SafeXcmVersion::<T>::set(self.safe_xcm_version);
993 self.supported_version.iter().for_each(|(location, version)| {
995 SupportedVersion::<T>::insert(
996 XCM_VERSION,
997 LatestVersionedLocation(location),
998 version,
999 );
1000 });
1001 }
1002 }
1003
1004 #[pezpallet::hooks]
1005 impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
1006 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
1007 let mut weight_used = Weight::zero();
1008 if let Some(migration) = CurrentMigration::<T>::get() {
1009 let max_weight = T::BlockWeights::get().max_block / 10;
1011 let (w, maybe_migration) = Self::lazy_migration(migration, max_weight);
1012 if maybe_migration.is_none() {
1013 Self::deposit_event(Event::VersionMigrationFinished { version: XCM_VERSION });
1014 }
1015 CurrentMigration::<T>::set(maybe_migration);
1016 weight_used.saturating_accrue(w);
1017 }
1018
1019 let mut q = VersionDiscoveryQueue::<T>::take().into_inner();
1022 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1024 q.sort_by_key(|i| i.1);
1025 while let Some((versioned_dest, _)) = q.pop() {
1026 if let Ok(dest) = Location::try_from(versioned_dest) {
1027 if Self::request_version_notify(dest).is_ok() {
1028 weight_used.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
1030 break;
1031 }
1032 }
1033 }
1034 if let Ok(q) = BoundedVec::try_from(q) {
1037 VersionDiscoveryQueue::<T>::put(q);
1038 }
1039 weight_used
1040 }
1041
1042 #[cfg(feature = "try-runtime")]
1043 fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
1044 Self::do_try_state()
1045 }
1046 }
1047
1048 pub mod migrations {
1049 use super::*;
1050 use pezframe_support::traits::{PalletInfoAccess, StorageVersion};
1051
1052 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
1053 enum QueryStatusV0<BlockNumber> {
1054 Pending {
1055 responder: VersionedLocation,
1056 maybe_notify: Option<(u8, u8)>,
1057 timeout: BlockNumber,
1058 },
1059 VersionNotifier {
1060 origin: VersionedLocation,
1061 is_active: bool,
1062 },
1063 Ready {
1064 response: VersionedResponse,
1065 at: BlockNumber,
1066 },
1067 }
1068 impl<B> From<QueryStatusV0<B>> for QueryStatus<B> {
1069 fn from(old: QueryStatusV0<B>) -> Self {
1070 use QueryStatusV0::*;
1071 match old {
1072 Pending { responder, maybe_notify, timeout } => QueryStatus::Pending {
1073 responder,
1074 maybe_notify,
1075 timeout,
1076 maybe_match_querier: Some(Location::here().into()),
1077 },
1078 VersionNotifier { origin, is_active } => {
1079 QueryStatus::VersionNotifier { origin, is_active }
1080 },
1081 Ready { response, at } => QueryStatus::Ready { response, at },
1082 }
1083 }
1084 }
1085
1086 pub fn migrate_to_v1<T: Config, P: GetStorageVersion + PalletInfoAccess>(
1087 ) -> pezframe_support::weights::Weight {
1088 let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
1089 tracing::info!(
1090 target: "runtime::xcm",
1091 ?on_chain_storage_version,
1092 "Running migration storage v1 for xcm with storage version",
1093 );
1094
1095 if on_chain_storage_version < 1 {
1096 let mut count = 0;
1097 Queries::<T>::translate::<QueryStatusV0<BlockNumberFor<T>>, _>(|_key, value| {
1098 count += 1;
1099 Some(value.into())
1100 });
1101 StorageVersion::new(1).put::<P>();
1102 tracing::info!(
1103 target: "runtime::xcm",
1104 ?on_chain_storage_version,
1105 "Running migration storage v1 for xcm with storage version was complete",
1106 );
1107 T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1)
1109 } else {
1110 tracing::warn!(
1111 target: "runtime::xcm",
1112 ?on_chain_storage_version,
1113 "Attempted to apply migration to v1 but failed because storage version is",
1114 );
1115 T::DbWeight::get().reads(1)
1116 }
1117 }
1118 }
1119
1120 #[pezpallet::call(weight(<T as Config>::WeightInfo))]
1121 impl<T: Config> Pezpallet<T> {
1122 #[pezpallet::call_index(0)]
1123 pub fn send(
1124 origin: OriginFor<T>,
1125 dest: Box<VersionedLocation>,
1126 message: Box<VersionedXcm<()>>,
1127 ) -> DispatchResult {
1128 <Self as SendController<_>>::send(origin, dest, message)?;
1129 Ok(())
1130 }
1131
1132 #[pezpallet::call_index(1)]
1150 #[allow(deprecated)]
1151 #[deprecated(
1152 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_teleport_assets` or `transfer_assets`"
1153 )]
1154 pub fn teleport_assets(
1155 origin: OriginFor<T>,
1156 dest: Box<VersionedLocation>,
1157 beneficiary: Box<VersionedLocation>,
1158 assets: Box<VersionedAssets>,
1159 fee_asset_id: Box<VersionedAssetId>,
1160 ) -> DispatchResult {
1161 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_id, Unlimited)
1162 }
1163
1164 #[pezpallet::call_index(2)]
1194 #[allow(deprecated)]
1195 #[deprecated(
1196 note = "This extrinsic uses `WeightLimit::Unlimited`, please migrate to `limited_reserve_transfer_assets` or `transfer_assets`"
1197 )]
1198 pub fn reserve_transfer_assets(
1199 origin: OriginFor<T>,
1200 dest: Box<VersionedLocation>,
1201 beneficiary: Box<VersionedLocation>,
1202 assets: Box<VersionedAssets>,
1203 fee_asset_id: Box<VersionedAssetId>,
1204 ) -> DispatchResult {
1205 Self::do_reserve_transfer_assets(
1206 origin,
1207 dest,
1208 beneficiary,
1209 assets,
1210 fee_asset_id,
1211 Unlimited,
1212 )
1213 }
1214
1215 #[pezpallet::call_index(3)]
1224 #[pezpallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
1225 pub fn execute(
1226 origin: OriginFor<T>,
1227 message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
1228 max_weight: Weight,
1229 ) -> DispatchResultWithPostInfo {
1230 let weight_used =
1231 <Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
1232 Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
1233 }
1234
1235 #[pezpallet::call_index(4)]
1242 pub fn force_xcm_version(
1243 origin: OriginFor<T>,
1244 location: Box<Location>,
1245 version: XcmVersion,
1246 ) -> DispatchResult {
1247 T::AdminOrigin::ensure_origin(origin)?;
1248 let location = *location;
1249 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&location), version);
1250 Self::deposit_event(Event::SupportedVersionChanged { location, version });
1251 Ok(())
1252 }
1253
1254 #[pezpallet::call_index(5)]
1260 pub fn force_default_xcm_version(
1261 origin: OriginFor<T>,
1262 maybe_xcm_version: Option<XcmVersion>,
1263 ) -> DispatchResult {
1264 T::AdminOrigin::ensure_origin(origin)?;
1265 SafeXcmVersion::<T>::set(maybe_xcm_version);
1266 Ok(())
1267 }
1268
1269 #[pezpallet::call_index(6)]
1274 pub fn force_subscribe_version_notify(
1275 origin: OriginFor<T>,
1276 location: Box<VersionedLocation>,
1277 ) -> DispatchResult {
1278 T::AdminOrigin::ensure_origin(origin)?;
1279 let location: Location = (*location).try_into().map_err(|()| {
1280 tracing::debug!(
1281 target: "xcm::pezpallet_xcm::force_subscribe_version_notify",
1282 "Failed to convert VersionedLocation for subscription target"
1283 );
1284 Error::<T>::BadLocation
1285 })?;
1286 Self::request_version_notify(location).map_err(|e| {
1287 tracing::debug!(
1288 target: "xcm::pezpallet_xcm::force_subscribe_version_notify", error=?e,
1289 "Failed to subscribe for version notifications for location"
1290 );
1291 match e {
1292 XcmError::InvalidLocation => Error::<T>::AlreadySubscribed,
1293 _ => Error::<T>::InvalidOrigin,
1294 }
1295 .into()
1296 })
1297 }
1298
1299 #[pezpallet::call_index(7)]
1306 pub fn force_unsubscribe_version_notify(
1307 origin: OriginFor<T>,
1308 location: Box<VersionedLocation>,
1309 ) -> DispatchResult {
1310 T::AdminOrigin::ensure_origin(origin)?;
1311 let location: Location = (*location).try_into().map_err(|()| {
1312 tracing::debug!(
1313 target: "xcm::pezpallet_xcm::force_unsubscribe_version_notify",
1314 "Failed to convert VersionedLocation for unsubscription target"
1315 );
1316 Error::<T>::BadLocation
1317 })?;
1318 Self::unrequest_version_notify(location).map_err(|e| {
1319 tracing::debug!(
1320 target: "xcm::pezpallet_xcm::force_unsubscribe_version_notify", error=?e,
1321 "Failed to unsubscribe from version notifications for location"
1322 );
1323 match e {
1324 XcmError::InvalidLocation => Error::<T>::NoSubscription,
1325 _ => Error::<T>::InvalidOrigin,
1326 }
1327 .into()
1328 })
1329 }
1330
1331 #[pezpallet::call_index(8)]
1361 #[pezpallet::weight(T::WeightInfo::reserve_transfer_assets())]
1362 pub fn limited_reserve_transfer_assets(
1363 origin: OriginFor<T>,
1364 dest: Box<VersionedLocation>,
1365 beneficiary: Box<VersionedLocation>,
1366 assets: Box<VersionedAssets>,
1367 fee_asset_id: Box<VersionedAssetId>,
1368 weight_limit: WeightLimit,
1369 ) -> DispatchResult {
1370 Self::do_reserve_transfer_assets(
1371 origin,
1372 dest,
1373 beneficiary,
1374 assets,
1375 fee_asset_id,
1376 weight_limit,
1377 )
1378 }
1379
1380 #[pezpallet::call_index(9)]
1398 #[pezpallet::weight(T::WeightInfo::teleport_assets())]
1399 pub fn limited_teleport_assets(
1400 origin: OriginFor<T>,
1401 dest: Box<VersionedLocation>,
1402 beneficiary: Box<VersionedLocation>,
1403 assets: Box<VersionedAssets>,
1404 fee_asset_id: Box<VersionedAssetId>,
1405 weight_limit: WeightLimit,
1406 ) -> DispatchResult {
1407 Self::do_teleport_assets(origin, dest, beneficiary, assets, fee_asset_id, weight_limit)
1408 }
1409
1410 #[pezpallet::call_index(10)]
1415 pub fn force_suspension(origin: OriginFor<T>, suspended: bool) -> DispatchResult {
1416 T::AdminOrigin::ensure_origin(origin)?;
1417 XcmExecutionSuspended::<T>::set(suspended);
1418 Ok(())
1419 }
1420
1421 #[pezpallet::call_index(11)]
1454 pub fn transfer_assets(
1455 origin: OriginFor<T>,
1456 dest: Box<VersionedLocation>,
1457 beneficiary: Box<VersionedLocation>,
1458 assets: Box<VersionedAssets>,
1459 fee_asset_id: Box<VersionedAssetId>,
1460 weight_limit: WeightLimit,
1461 ) -> DispatchResult {
1462 let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1463 let dest = (*dest).try_into().map_err(|()| {
1464 tracing::debug!(
1465 target: "xcm::pezpallet_xcm::transfer_assets",
1466 "Failed to convert destination VersionedLocation",
1467 );
1468 Error::<T>::BadVersion
1469 })?;
1470 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1471 tracing::debug!(
1472 target: "xcm::pezpallet_xcm::transfer_assets",
1473 "Failed to convert beneficiary VersionedLocation",
1474 );
1475 Error::<T>::BadVersion
1476 })?;
1477 let assets: Assets = (*assets).try_into().map_err(|()| {
1478 tracing::debug!(
1479 target: "xcm::pezpallet_xcm::transfer_assets",
1480 "Failed to convert VersionedAssets",
1481 );
1482 Error::<T>::BadVersion
1483 })?;
1484 let fee_asset_id: AssetId = (*fee_asset_id).try_into().map_err(|()| {
1485 tracing::debug!(
1486 target: "xcm::pezpallet_xcm::transfer_assets",
1487 "Failed to convert VersionedAssetId",
1488 );
1489 Error::<T>::BadVersion
1490 })?;
1491 tracing::debug!(
1492 target: "xcm::pezpallet_xcm::transfer_assets",
1493 ?origin, ?dest, ?beneficiary, ?assets, ?fee_asset_id, ?weight_limit,
1494 );
1495
1496 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1497 let assets = assets.into_inner();
1498 let (fees_transfer_type, assets_transfer_type) =
1500 Self::find_fee_and_assets_transfer_types(&assets, &fee_asset_id, &dest)?;
1501
1502 Self::ensure_network_asset_reserve_transfer_allowed(
1506 &assets,
1507 &fee_asset_id,
1508 &assets_transfer_type,
1509 &fees_transfer_type,
1510 )?;
1511
1512 Self::do_transfer_assets(
1513 origin,
1514 dest,
1515 Either::Left(beneficiary),
1516 assets,
1517 assets_transfer_type,
1518 fee_asset_id,
1519 fees_transfer_type,
1520 weight_limit,
1521 )
1522 }
1523
1524 #[pezpallet::call_index(12)]
1531 pub fn claim_assets(
1532 origin: OriginFor<T>,
1533 assets: Box<VersionedAssets>,
1534 beneficiary: Box<VersionedLocation>,
1535 ) -> DispatchResult {
1536 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1537 tracing::debug!(target: "xcm::pezpallet_xcm::claim_assets", ?origin_location, ?assets, ?beneficiary);
1538 let assets_version = assets.identify_version();
1540 let assets: Assets = (*assets).try_into().map_err(|()| {
1541 tracing::debug!(
1542 target: "xcm::pezpallet_xcm::claim_assets",
1543 "Failed to convert input VersionedAssets",
1544 );
1545 Error::<T>::BadVersion
1546 })?;
1547 let number_of_assets = assets.len() as u32;
1548 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
1549 tracing::debug!(
1550 target: "xcm::pezpallet_xcm::claim_assets",
1551 "Failed to convert beneficiary VersionedLocation",
1552 );
1553 Error::<T>::BadVersion
1554 })?;
1555 let ticket: Location = GeneralIndex(assets_version as u128).into();
1556 let mut message = Xcm(vec![
1557 ClaimAsset { assets, ticket },
1558 DepositAsset { assets: AllCounted(number_of_assets).into(), beneficiary },
1559 ]);
1560 let weight = T::Weigher::weight(&mut message, Weight::MAX).map_err(|error| {
1561 tracing::debug!(target: "xcm::pezpallet_xcm::claim_assets", ?error, "Failed to calculate weight");
1562 Error::<T>::UnweighableMessage
1563 })?;
1564 let mut hash = message.using_encoded(pezsp_io::hashing::blake2_256);
1565 let outcome = T::XcmExecutor::prepare_and_execute(
1566 origin_location,
1567 message,
1568 &mut hash,
1569 weight,
1570 weight,
1571 );
1572 outcome.ensure_complete().map_err(|error| {
1573 tracing::error!(target: "xcm::pezpallet_xcm::claim_assets", ?error, "XCM execution failed with error");
1574 Error::<T>::LocalExecutionIncompleteWithError { index: error.index, error: error.error.into()}
1575 })?;
1576 Ok(())
1577 }
1578
1579 #[pezpallet::call_index(13)]
1628 #[pezpallet::weight(T::WeightInfo::transfer_assets())]
1629 pub fn transfer_assets_using_type_and_then(
1630 origin: OriginFor<T>,
1631 dest: Box<VersionedLocation>,
1632 assets: Box<VersionedAssets>,
1633 assets_transfer_type: Box<TransferType>,
1634 remote_fees_id: Box<VersionedAssetId>,
1635 fees_transfer_type: Box<TransferType>,
1636 custom_xcm_on_dest: Box<VersionedXcm<()>>,
1637 weight_limit: WeightLimit,
1638 ) -> DispatchResult {
1639 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1640 let dest: Location = (*dest).try_into().map_err(|()| {
1641 tracing::debug!(
1642 target: "xcm::pezpallet_xcm::transfer_assets_using_type_and_then",
1643 "Failed to convert destination VersionedLocation",
1644 );
1645 Error::<T>::BadVersion
1646 })?;
1647 let assets: Assets = (*assets).try_into().map_err(|()| {
1648 tracing::debug!(
1649 target: "xcm::pezpallet_xcm::transfer_assets_using_type_and_then",
1650 "Failed to convert VersionedAssets",
1651 );
1652 Error::<T>::BadVersion
1653 })?;
1654 let fees_id: AssetId = (*remote_fees_id).try_into().map_err(|()| {
1655 tracing::debug!(
1656 target: "xcm::pezpallet_xcm::transfer_assets_using_type_and_then",
1657 "Failed to convert remote_fees_id VersionedAssetId",
1658 );
1659 Error::<T>::BadVersion
1660 })?;
1661 let remote_xcm: Xcm<()> = (*custom_xcm_on_dest).try_into().map_err(|()| {
1662 tracing::debug!(
1663 target: "xcm::pezpallet_xcm::transfer_assets_using_type_and_then",
1664 "Failed to convert custom_xcm_on_dest VersionedXcm",
1665 );
1666 Error::<T>::BadVersion
1667 })?;
1668 tracing::debug!(
1669 target: "xcm::pezpallet_xcm::transfer_assets_using_type_and_then",
1670 ?origin_location, ?dest, ?assets, ?assets_transfer_type, ?fees_id, ?fees_transfer_type,
1671 ?remote_xcm, ?weight_limit,
1672 );
1673
1674 let assets = assets.into_inner();
1675 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
1676
1677 Self::do_transfer_assets(
1678 origin_location,
1679 dest,
1680 Either::Right(remote_xcm),
1681 assets,
1682 *assets_transfer_type,
1683 fees_id,
1684 *fees_transfer_type,
1685 weight_limit,
1686 )
1687 }
1688
1689 #[pezpallet::call_index(14)]
1701 pub fn add_authorized_alias(
1702 origin: OriginFor<T>,
1703 aliaser: Box<VersionedLocation>,
1704 expires: Option<u64>,
1705 ) -> DispatchResult {
1706 let signed_origin = ensure_signed(origin.clone())?;
1707 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1708 let new_aliaser: Location = (*aliaser).try_into().map_err(|()| {
1709 tracing::debug!(
1710 target: "xcm::pezpallet_xcm::add_authorized_alias",
1711 "Failed to convert aliaser VersionedLocation",
1712 );
1713 Error::<T>::BadVersion
1714 })?;
1715 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1716 let origin_location = match origin_location.unpack() {
1718 (0, [AccountId32 { network: _, id }]) => {
1719 Location::new(0, [AccountId32 { network: None, id: *id }])
1720 },
1721 _ => return Err(Error::<T>::InvalidOrigin.into()),
1722 };
1723 tracing::debug!(target: "xcm::pezpallet_xcm::add_authorized_alias", ?origin_location, ?new_aliaser, ?expires);
1724 ensure!(origin_location != new_aliaser, Error::<T>::BadLocation);
1725 if let Some(expiry) = expires {
1726 ensure!(
1727 expiry
1728 > pezframe_system::Pezpallet::<T>::current_block_number()
1729 .saturated_into::<u64>(),
1730 Error::<T>::ExpiresInPast
1731 );
1732 }
1733 let versioned_origin = VersionedLocation::from(origin_location.clone());
1734 let versioned_aliaser = VersionedLocation::from(new_aliaser.clone());
1735 let entry = if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1736 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1738 if let Some(aliaser) =
1739 aliasers.iter_mut().find(|aliaser| aliaser.location == versioned_aliaser)
1740 {
1741 aliaser.expiry = expires;
1743 } else {
1744 let aliaser =
1746 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1747 aliasers.try_push(aliaser).map_err(|_| {
1748 tracing::debug!(
1749 target: "xcm::pezpallet_xcm::add_authorized_alias",
1750 "Failed to add new aliaser to existing entry",
1751 );
1752 Error::<T>::TooManyAuthorizedAliases
1753 })?;
1754 ticket = ticket.update(&signed_origin, aliasers_footprint(aliasers.len()))?;
1756 }
1757 AuthorizedAliasesEntry { aliasers, ticket }
1758 } else {
1759 let ticket = TicketOf::<T>::new(&signed_origin, aliasers_footprint(1))?;
1761 let aliaser =
1762 OriginAliaser { location: versioned_aliaser.clone(), expiry: expires };
1763 let mut aliasers = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
1764 aliasers.try_push(aliaser).map_err(|error| {
1765 tracing::debug!(
1766 target: "xcm::pezpallet_xcm::add_authorized_alias", ?error,
1767 "Failed to add first aliaser to new entry",
1768 );
1769 Error::<T>::TooManyAuthorizedAliases
1770 })?;
1771 AuthorizedAliasesEntry { aliasers, ticket }
1772 };
1773 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1775 Self::deposit_event(Event::AliasAuthorized {
1776 aliaser: new_aliaser,
1777 target: origin_location,
1778 expiry: expires,
1779 });
1780 Ok(())
1781 }
1782
1783 #[pezpallet::call_index(15)]
1786 pub fn remove_authorized_alias(
1787 origin: OriginFor<T>,
1788 aliaser: Box<VersionedLocation>,
1789 ) -> DispatchResult {
1790 let signed_origin = ensure_signed(origin.clone())?;
1791 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1792 let to_remove: Location = (*aliaser).try_into().map_err(|()| {
1793 tracing::debug!(
1794 target: "xcm::pezpallet_xcm::remove_authorized_alias",
1795 "Failed to convert aliaser VersionedLocation",
1796 );
1797 Error::<T>::BadVersion
1798 })?;
1799 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1800 let origin_location = match origin_location.unpack() {
1802 (0, [AccountId32 { network: _, id }]) => {
1803 Location::new(0, [AccountId32 { network: None, id: *id }])
1804 },
1805 _ => return Err(Error::<T>::InvalidOrigin.into()),
1806 };
1807 tracing::debug!(target: "xcm::pezpallet_xcm::remove_authorized_alias", ?origin_location, ?to_remove);
1808 ensure!(origin_location != to_remove, Error::<T>::BadLocation);
1809 let versioned_origin = VersionedLocation::from(origin_location.clone());
1811 let versioned_to_remove = VersionedLocation::from(to_remove.clone());
1812 AuthorizedAliases::<T>::get(&versioned_origin)
1813 .ok_or(Error::<T>::AliasNotFound.into())
1814 .and_then(|entry| {
1815 let (mut aliasers, mut ticket) = (entry.aliasers, entry.ticket);
1816 let old_len = aliasers.len();
1817 aliasers.retain(|alias| versioned_to_remove.ne(&alias.location));
1818 let new_len = aliasers.len();
1819 if aliasers.is_empty() {
1820 ticket.drop(&signed_origin)?;
1822 AuthorizedAliases::<T>::remove(&versioned_origin);
1823 Self::deposit_event(Event::AliasAuthorizationRemoved {
1824 aliaser: to_remove,
1825 target: origin_location,
1826 });
1827 Ok(())
1828 } else if old_len != new_len {
1829 ticket = ticket.update(&signed_origin, aliasers_footprint(new_len))?;
1831 let entry = AuthorizedAliasesEntry { aliasers, ticket };
1832 AuthorizedAliases::<T>::insert(&versioned_origin, entry);
1833 Self::deposit_event(Event::AliasAuthorizationRemoved {
1834 aliaser: to_remove,
1835 target: origin_location,
1836 });
1837 Ok(())
1838 } else {
1839 Err(Error::<T>::AliasNotFound.into())
1840 }
1841 })
1842 }
1843
1844 #[pezpallet::call_index(16)]
1847 #[pezpallet::weight(T::WeightInfo::remove_authorized_alias())]
1848 pub fn remove_all_authorized_aliases(origin: OriginFor<T>) -> DispatchResult {
1849 let signed_origin = ensure_signed(origin.clone())?;
1850 let origin_location: Location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
1851 let origin_location = match origin_location.unpack() {
1853 (0, [AccountId32 { network: _, id }]) => {
1854 Location::new(0, [AccountId32 { network: None, id: *id }])
1855 },
1856 _ => return Err(Error::<T>::InvalidOrigin.into()),
1857 };
1858 tracing::debug!(target: "xcm::pezpallet_xcm::remove_all_authorized_aliases", ?origin_location);
1859 let versioned_origin = VersionedLocation::from(origin_location.clone());
1861 if let Some(entry) = AuthorizedAliases::<T>::get(&versioned_origin) {
1862 entry.ticket.drop(&signed_origin)?;
1864 AuthorizedAliases::<T>::remove(&versioned_origin);
1865 Self::deposit_event(Event::AliasesAuthorizationsRemoved {
1866 target: origin_location,
1867 });
1868 Ok(())
1869 } else {
1870 tracing::debug!(target: "xcm::pezpallet_xcm::remove_all_authorized_aliases", "No authorized alias entry found for the origin");
1871 Err(Error::<T>::AliasNotFound.into())
1872 }
1873 }
1874 }
1875}
1876
1877const MAX_ASSETS_FOR_TRANSFER: usize = 2;
1879
1880#[derive(Clone, PartialEq)]
1882enum FeesHandling<T: Config> {
1883 Batched { fees: Asset },
1885 Separate { local_xcm: Xcm<<T as Config>::RuntimeCall>, remote_xcm: Xcm<()> },
1887}
1888
1889impl<T: Config> core::fmt::Debug for FeesHandling<T> {
1890 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1891 match self {
1892 Self::Batched { fees } => write!(f, "FeesHandling::Batched({:?})", fees),
1893 Self::Separate { local_xcm, remote_xcm } => write!(
1894 f,
1895 "FeesHandling::Separate(local: {:?}, remote: {:?})",
1896 local_xcm, remote_xcm
1897 ),
1898 }
1899 }
1900}
1901
1902impl<T: Config> QueryHandler for Pezpallet<T> {
1903 type BlockNumber = BlockNumberFor<T>;
1904 type Error = XcmError;
1905 type UniversalLocation = T::UniversalLocation;
1906
1907 fn new_query(
1909 responder: impl Into<Location>,
1910 timeout: BlockNumberFor<T>,
1911 match_querier: impl Into<Location>,
1912 ) -> QueryId {
1913 Self::do_new_query(responder, None, timeout, match_querier)
1914 }
1915
1916 fn report_outcome(
1919 message: &mut Xcm<()>,
1920 responder: impl Into<Location>,
1921 timeout: Self::BlockNumber,
1922 ) -> Result<QueryId, Self::Error> {
1923 let responder = responder.into();
1924 let destination =
1925 Self::UniversalLocation::get().invert_target(&responder).map_err(|()| {
1926 tracing::debug!(
1927 target: "xcm::pezpallet_xcm::report_outcome",
1928 "Failed to invert responder Location",
1929 );
1930 XcmError::LocationNotInvertible
1931 })?;
1932 let query_id = Self::new_query(responder, timeout, Here);
1933 let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
1934 let report_error = Xcm(vec![ReportError(response_info)]);
1935 message.0.insert(0, SetAppendix(report_error));
1936 Ok(query_id)
1937 }
1938
1939 fn take_response(query_id: QueryId) -> QueryResponseStatus<Self::BlockNumber> {
1941 match Queries::<T>::get(query_id) {
1942 Some(QueryStatus::Ready { response, at }) => match response.try_into() {
1943 Ok(response) => {
1944 Queries::<T>::remove(query_id);
1945 Self::deposit_event(Event::ResponseTaken { query_id });
1946 QueryResponseStatus::Ready { response, at }
1947 },
1948 Err(_) => {
1949 tracing::debug!(
1950 target: "xcm::pezpallet_xcm::take_response", ?query_id,
1951 "Failed to convert VersionedResponse to Response for query",
1952 );
1953 QueryResponseStatus::UnexpectedVersion
1954 },
1955 },
1956 Some(QueryStatus::Pending { timeout, .. }) => QueryResponseStatus::Pending { timeout },
1957 Some(_) => {
1958 tracing::debug!(
1959 target: "xcm::pezpallet_xcm::take_response", ?query_id,
1960 "Unexpected QueryStatus variant for query",
1961 );
1962 QueryResponseStatus::UnexpectedVersion
1963 },
1964 None => {
1965 tracing::debug!(
1966 target: "xcm::pezpallet_xcm::take_response", ?query_id,
1967 "Query ID not found`",
1968 );
1969 QueryResponseStatus::NotFound
1970 },
1971 }
1972 }
1973
1974 #[cfg(feature = "runtime-benchmarks")]
1975 fn expect_response(id: QueryId, response: Response) {
1976 let response = response.into();
1977 Queries::<T>::insert(
1978 id,
1979 QueryStatus::Ready {
1980 response,
1981 at: pezframe_system::Pezpallet::<T>::current_block_number(),
1982 },
1983 );
1984 }
1985}
1986
1987impl<T: Config> Pezpallet<T> {
1988 pub fn query(query_id: &QueryId) -> Option<QueryStatus<BlockNumberFor<T>>> {
1990 Queries::<T>::get(query_id)
1991 }
1992
1993 pub fn asset_trap(trap_id: &H256) -> u32 {
1999 AssetTraps::<T>::get(trap_id)
2000 }
2001
2002 fn find_fee_and_assets_transfer_types(
2007 assets: &[Asset],
2008 fee_asset_id: &AssetId,
2009 dest: &Location,
2010 ) -> Result<(TransferType, TransferType), Error<T>> {
2011 let mut fees_transfer_type = None;
2012 let mut assets_transfer_type = None;
2013 for asset in assets.iter() {
2014 if let Fungible(x) = asset.fun {
2015 ensure!(!x.is_zero(), Error::<T>::Empty);
2017 }
2018 let transfer_type =
2019 T::XcmExecutor::determine_for(&asset, dest).map_err(Error::<T>::from)?;
2020 if asset.id == *fee_asset_id {
2021 fees_transfer_type = Some(transfer_type);
2022 } else {
2023 if let Some(existing) = assets_transfer_type.as_ref() {
2024 ensure!(existing == &transfer_type, Error::<T>::TooManyReserves);
2027 } else {
2028 assets_transfer_type = Some(transfer_type);
2030 }
2031 }
2032 }
2033 if assets.len() == 1 {
2035 assets_transfer_type = fees_transfer_type.clone()
2036 }
2037 Ok((
2038 fees_transfer_type.ok_or(Error::<T>::Empty)?,
2039 assets_transfer_type.ok_or(Error::<T>::Empty)?,
2040 ))
2041 }
2042
2043 fn do_reserve_transfer_assets(
2044 origin: OriginFor<T>,
2045 dest: Box<VersionedLocation>,
2046 beneficiary: Box<VersionedLocation>,
2047 assets: Box<VersionedAssets>,
2048 fee_asset_id: Box<VersionedAssetId>,
2049 weight_limit: WeightLimit,
2050 ) -> DispatchResult {
2051 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2052 let dest = (*dest).try_into().map_err(|()| {
2053 tracing::debug!(
2054 target: "xcm::pezpallet_xcm::do_reserve_transfer_assets",
2055 "Failed to convert destination VersionedLocation",
2056 );
2057 Error::<T>::BadVersion
2058 })?;
2059 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2060 tracing::debug!(
2061 target: "xcm::pezpallet_xcm::do_reserve_transfer_assets",
2062 "Failed to convert beneficiary VersionedLocation",
2063 );
2064 Error::<T>::BadVersion
2065 })?;
2066 let assets: Assets = (*assets).try_into().map_err(|()| {
2067 tracing::debug!(
2068 target: "xcm::pezpallet_xcm::do_reserve_transfer_assets",
2069 "Failed to convert VersionedAssets",
2070 );
2071 Error::<T>::BadVersion
2072 })?;
2073 let fee_asset_id: AssetId = (*fee_asset_id).try_into().map_err(|()| {
2074 tracing::debug!(
2075 target: "xcm::pezpallet_xcm::do_reserve_transfer_assets",
2076 "Failed to convert VersionedAssetId",
2077 );
2078 Error::<T>::BadVersion
2079 })?;
2080 tracing::debug!(
2081 target: "xcm::pezpallet_xcm::do_reserve_transfer_assets",
2082 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_id,
2083 );
2084
2085 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2086 let value = (origin_location, assets.into_inner());
2087 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2088 let (origin, assets) = value;
2089
2090 let fees = assets.iter().find(|a| a.id == fee_asset_id).ok_or(Error::<T>::Empty)?.clone();
2091
2092 let (fees_transfer_type, assets_transfer_type) =
2094 Self::find_fee_and_assets_transfer_types(&assets, &fee_asset_id, &dest)?;
2095 ensure!(assets_transfer_type != TransferType::Teleport, Error::<T>::Filtered);
2097 ensure!(assets_transfer_type == fees_transfer_type, Error::<T>::TooManyReserves);
2099
2100 Self::ensure_network_asset_reserve_transfer_allowed(
2104 &assets,
2105 &fee_asset_id,
2106 &assets_transfer_type,
2107 &fees_transfer_type,
2108 )?;
2109
2110 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2111 origin.clone(),
2112 dest.clone(),
2113 Either::Left(beneficiary),
2114 assets,
2115 assets_transfer_type,
2116 FeesHandling::Batched { fees },
2117 weight_limit,
2118 )?;
2119 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2120 }
2121
2122 fn do_teleport_assets(
2123 origin: OriginFor<T>,
2124 dest: Box<VersionedLocation>,
2125 beneficiary: Box<VersionedLocation>,
2126 assets: Box<VersionedAssets>,
2127 fee_asset_id: Box<VersionedAssetId>,
2128 weight_limit: WeightLimit,
2129 ) -> DispatchResult {
2130 let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
2131 let dest = (*dest).try_into().map_err(|()| {
2132 tracing::debug!(
2133 target: "xcm::pezpallet_xcm::do_teleport_assets",
2134 "Failed to convert destination VersionedLocation",
2135 );
2136 Error::<T>::BadVersion
2137 })?;
2138 let beneficiary: Location = (*beneficiary).try_into().map_err(|()| {
2139 tracing::debug!(
2140 target: "xcm::pezpallet_xcm::do_teleport_assets",
2141 "Failed to convert beneficiary VersionedLocation",
2142 );
2143 Error::<T>::BadVersion
2144 })?;
2145 let assets: Assets = (*assets).try_into().map_err(|()| {
2146 tracing::debug!(
2147 target: "xcm::pezpallet_xcm::do_teleport_assets",
2148 "Failed to convert VersionedAssets",
2149 );
2150 Error::<T>::BadVersion
2151 })?;
2152 let fee_asset_id: AssetId = (*fee_asset_id).try_into().map_err(|()| {
2153 tracing::debug!(
2154 target: "xcm::pezpallet_xcm::do_teleport_assets",
2155 "Failed to convert VersionedAssetId",
2156 );
2157 Error::<T>::BadVersion
2158 })?;
2159
2160 tracing::debug!(
2161 target: "xcm::pezpallet_xcm::do_teleport_assets",
2162 ?origin_location, ?dest, ?beneficiary, ?assets, ?fee_asset_id, ?weight_limit,
2163 );
2164
2165 ensure!(assets.len() <= MAX_ASSETS_FOR_TRANSFER, Error::<T>::TooManyAssets);
2166 let value = (origin_location, assets.into_inner());
2167 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2168 let (origin_location, assets) = value;
2169 let mut maybe_fee_asset = None;
2170 for asset in assets.iter() {
2171 let transfer_type =
2172 T::XcmExecutor::determine_for(asset, &dest).map_err(Error::<T>::from)?;
2173 ensure!(transfer_type == TransferType::Teleport, Error::<T>::Filtered);
2174 if asset.id == fee_asset_id {
2175 maybe_fee_asset = Some(asset)
2176 }
2177 }
2178 let fees = maybe_fee_asset.ok_or(Error::<T>::Empty)?.clone();
2179
2180 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2181 origin_location.clone(),
2182 dest.clone(),
2183 Either::Left(beneficiary),
2184 assets,
2185 TransferType::Teleport,
2186 FeesHandling::Batched { fees },
2187 weight_limit,
2188 )?;
2189 Self::execute_xcm_transfer(origin_location, dest, local_xcm, remote_xcm)
2190 }
2191
2192 fn do_transfer_assets(
2193 origin: Location,
2194 dest: Location,
2195 beneficiary: Either<Location, Xcm<()>>,
2196 mut assets: Vec<Asset>,
2197 assets_transfer_type: TransferType,
2198 fee_asset_id: AssetId,
2199 fees_transfer_type: TransferType,
2200 weight_limit: WeightLimit,
2201 ) -> DispatchResult {
2202 let fees = if fees_transfer_type == assets_transfer_type {
2204 let fees =
2205 assets.iter().find(|a| a.id == fee_asset_id).ok_or(Error::<T>::Empty)?.clone();
2206 FeesHandling::Batched { fees }
2208 } else {
2209 ensure!(
2215 !matches!(assets_transfer_type, TransferType::RemoteReserve(_)),
2216 Error::<T>::InvalidAssetUnsupportedReserve
2217 );
2218 let weight_limit = weight_limit.clone();
2219 let fee_asset_index =
2222 assets.iter().position(|a| a.id == fee_asset_id).ok_or(Error::<T>::FeesNotMet)?;
2223 let fees = assets.remove(fee_asset_index);
2224 let (local_xcm, remote_xcm) = match fees_transfer_type {
2225 TransferType::LocalReserve => Self::local_reserve_fees_instructions(
2226 origin.clone(),
2227 dest.clone(),
2228 fees,
2229 weight_limit,
2230 )?,
2231 TransferType::DestinationReserve => Self::destination_reserve_fees_instructions(
2232 origin.clone(),
2233 dest.clone(),
2234 fees,
2235 weight_limit,
2236 )?,
2237 TransferType::Teleport => Self::teleport_fees_instructions(
2238 origin.clone(),
2239 dest.clone(),
2240 fees,
2241 weight_limit,
2242 )?,
2243 TransferType::RemoteReserve(_) => {
2244 return Err(Error::<T>::InvalidAssetUnsupportedReserve.into())
2245 },
2246 };
2247 FeesHandling::Separate { local_xcm, remote_xcm }
2248 };
2249
2250 let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
2251 origin.clone(),
2252 dest.clone(),
2253 beneficiary,
2254 assets,
2255 assets_transfer_type,
2256 fees,
2257 weight_limit,
2258 )?;
2259 Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm)
2260 }
2261
2262 fn build_xcm_transfer_type(
2263 origin: Location,
2264 dest: Location,
2265 beneficiary: Either<Location, Xcm<()>>,
2266 assets: Vec<Asset>,
2267 transfer_type: TransferType,
2268 fees: FeesHandling<T>,
2269 weight_limit: WeightLimit,
2270 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Option<Xcm<()>>), Error<T>> {
2271 tracing::debug!(
2272 target: "xcm::pezpallet_xcm::build_xcm_transfer_type",
2273 ?origin, ?dest, ?beneficiary, ?assets, ?transfer_type, ?fees, ?weight_limit,
2274 );
2275 match transfer_type {
2276 TransferType::LocalReserve => Self::local_reserve_transfer_programs(
2277 origin.clone(),
2278 dest.clone(),
2279 beneficiary,
2280 assets,
2281 fees,
2282 weight_limit,
2283 )
2284 .map(|(local, remote)| (local, Some(remote))),
2285 TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
2286 origin.clone(),
2287 dest.clone(),
2288 beneficiary,
2289 assets,
2290 fees,
2291 weight_limit,
2292 )
2293 .map(|(local, remote)| (local, Some(remote))),
2294 TransferType::RemoteReserve(reserve) => {
2295 let fees = match fees {
2296 FeesHandling::Batched { fees } => fees,
2297 _ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
2298 };
2299 Self::remote_reserve_transfer_program(
2300 origin.clone(),
2301 reserve.try_into().map_err(|()| {
2302 tracing::debug!(
2303 target: "xcm::pezpallet_xcm::build_xcm_transfer_type",
2304 "Failed to convert remote reserve location",
2305 );
2306 Error::<T>::BadVersion
2307 })?,
2308 beneficiary,
2309 dest.clone(),
2310 assets,
2311 fees,
2312 weight_limit,
2313 )
2314 .map(|local| (local, None))
2315 },
2316 TransferType::Teleport => Self::teleport_assets_program(
2317 origin.clone(),
2318 dest.clone(),
2319 beneficiary,
2320 assets,
2321 fees,
2322 weight_limit,
2323 )
2324 .map(|(local, remote)| (local, Some(remote))),
2325 }
2326 }
2327
2328 fn execute_xcm_transfer(
2329 origin: Location,
2330 dest: Location,
2331 mut local_xcm: Xcm<<T as Config>::RuntimeCall>,
2332 remote_xcm: Option<Xcm<()>>,
2333 ) -> DispatchResult {
2334 tracing::debug!(
2335 target: "xcm::pezpallet_xcm::execute_xcm_transfer",
2336 ?origin, ?dest, ?local_xcm, ?remote_xcm,
2337 );
2338
2339 let weight =
2340 T::Weigher::weight(&mut local_xcm, Weight::MAX).map_err(|error| {
2341 tracing::debug!(target: "xcm::pezpallet_xcm::execute_xcm_transfer", ?error, "Failed to calculate weight");
2342 Error::<T>::UnweighableMessage
2343 })?;
2344 let mut hash = local_xcm.using_encoded(pezsp_io::hashing::blake2_256);
2345 let outcome = T::XcmExecutor::prepare_and_execute(
2346 origin.clone(),
2347 local_xcm,
2348 &mut hash,
2349 weight,
2350 weight,
2351 );
2352 Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
2353 outcome.clone().ensure_complete().map_err(|error| {
2354 tracing::error!(
2355 target: "xcm::pezpallet_xcm::execute_xcm_transfer",
2356 ?error, "XCM execution failed with error with outcome: {:?}", outcome
2357 );
2358 Error::<T>::LocalExecutionIncompleteWithError {
2359 index: error.index,
2360 error: error.error.into(),
2361 }
2362 })?;
2363
2364 if let Some(remote_xcm) = remote_xcm {
2365 let (ticket, price) = validate_send::<T::XcmRouter>(dest.clone(), remote_xcm.clone())
2366 .map_err(|error| {
2367 tracing::error!(target: "xcm::pezpallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM validate_send failed with error");
2368 Error::<T>::from(error)
2369 })?;
2370 if origin != Here.into_location() {
2371 Self::charge_fees(origin.clone(), price.clone()).map_err(|error| {
2372 tracing::error!(
2373 target: "xcm::pezpallet_xcm::execute_xcm_transfer",
2374 ?error, ?price, ?origin, "Unable to charge fee",
2375 );
2376 Error::<T>::FeesNotMet
2377 })?;
2378 }
2379 let message_id = T::XcmRouter::deliver(ticket)
2380 .map_err(|error| {
2381 tracing::error!(target: "xcm::pezpallet_xcm::execute_xcm_transfer", ?error, ?dest, ?remote_xcm, "XCM deliver failed with error");
2382 Error::<T>::from(error)
2383 })?;
2384
2385 let e = Event::Sent { origin, destination: dest, message: remote_xcm, message_id };
2386 Self::deposit_event(e);
2387 }
2388 Ok(())
2389 }
2390
2391 fn add_fees_to_xcm(
2392 dest: Location,
2393 fees: FeesHandling<T>,
2394 weight_limit: WeightLimit,
2395 local: &mut Xcm<<T as Config>::RuntimeCall>,
2396 remote: &mut Xcm<()>,
2397 ) -> Result<(), Error<T>> {
2398 match fees {
2399 FeesHandling::Batched { fees } => {
2400 let context = T::UniversalLocation::get();
2401 let reanchored_fees =
2404 fees.reanchored(&dest, &context).map_err(|e| {
2405 tracing::error!(target: "xcm::pezpallet_xcm::add_fees_to_xcm", ?e, ?dest, ?context, "Failed to re-anchor fees");
2406 Error::<T>::CannotReanchor
2407 })?;
2408 remote.inner_mut().push(BuyExecution { fees: reanchored_fees, weight_limit });
2410 },
2411 FeesHandling::Separate { local_xcm: mut local_fees, remote_xcm: mut remote_fees } => {
2412 core::mem::swap(local, &mut local_fees);
2415 core::mem::swap(remote, &mut remote_fees);
2416 local.inner_mut().append(&mut local_fees.into_inner());
2418 remote.inner_mut().append(&mut remote_fees.into_inner());
2419 },
2420 }
2421 Ok(())
2422 }
2423
2424 fn local_reserve_fees_instructions(
2425 origin: Location,
2426 dest: Location,
2427 fees: Asset,
2428 weight_limit: WeightLimit,
2429 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2430 let value = (origin, vec![fees.clone()]);
2431 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2432
2433 let context = T::UniversalLocation::get();
2434 let reanchored_fees = fees.clone().reanchored(&dest, &context).map_err(|_| {
2435 tracing::debug!(
2436 target: "xcm::pezpallet_xcm::local_reserve_fees_instructions",
2437 "Failed to re-anchor fees",
2438 );
2439 Error::<T>::CannotReanchor
2440 })?;
2441
2442 let local_execute_xcm = Xcm(vec![
2443 TransferAsset { assets: fees.into(), beneficiary: dest },
2445 ]);
2446 let xcm_on_dest = Xcm(vec![
2447 ReserveAssetDeposited(reanchored_fees.clone().into()),
2449 BuyExecution { fees: reanchored_fees, weight_limit },
2451 ]);
2452 Ok((local_execute_xcm, xcm_on_dest))
2453 }
2454
2455 fn local_reserve_transfer_programs(
2456 origin: Location,
2457 dest: Location,
2458 beneficiary: Either<Location, Xcm<()>>,
2459 assets: Vec<Asset>,
2460 fees: FeesHandling<T>,
2461 weight_limit: WeightLimit,
2462 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2463 let value = (origin, assets);
2464 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2465 let (_, assets) = value;
2466
2467 let max_assets =
2469 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2470 let assets: Assets = assets.into();
2471 let context = T::UniversalLocation::get();
2472 let mut reanchored_assets = assets.clone();
2473 reanchored_assets
2474 .reanchor(&dest, &context)
2475 .map_err(|e| {
2476 tracing::error!(target: "xcm::pezpallet_xcm::local_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2477 Error::<T>::CannotReanchor
2478 })?;
2479
2480 let mut local_execute_xcm = Xcm(vec![
2482 TransferAsset { assets, beneficiary: dest.clone() },
2484 ]);
2485 let mut xcm_on_dest = Xcm(vec![
2487 ReserveAssetDeposited(reanchored_assets),
2489 ClearOrigin,
2491 ]);
2492 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2494
2495 let custom_remote_xcm = match beneficiary {
2497 Either::Right(custom_xcm) => custom_xcm,
2498 Either::Left(beneficiary) => {
2499 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2501 },
2502 };
2503 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2504
2505 Ok((local_execute_xcm, xcm_on_dest))
2506 }
2507
2508 fn destination_reserve_fees_instructions(
2509 origin: Location,
2510 dest: Location,
2511 fees: Asset,
2512 weight_limit: WeightLimit,
2513 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2514 let value = (origin, vec![fees.clone()]);
2515 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2516 ensure!(
2517 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&fees, &dest),
2518 Error::<T>::InvalidAssetUnsupportedReserve
2519 );
2520
2521 let context = T::UniversalLocation::get();
2522 let reanchored_fees = fees
2523 .clone()
2524 .reanchored(&dest, &context)
2525 .map_err(|e| {
2526 tracing::error!(target: "xcm::pezpallet_xcm::destination_reserve_fees_instructions", ?e, ?dest,?context, "Failed to re-anchor fees");
2527 Error::<T>::CannotReanchor
2528 })?;
2529 let fees: Assets = fees.into();
2530
2531 let local_execute_xcm = Xcm(vec![
2532 WithdrawAsset(fees.clone()),
2534 BurnAsset(fees),
2536 ]);
2537 let xcm_on_dest = Xcm(vec![
2538 WithdrawAsset(reanchored_fees.clone().into()),
2540 BuyExecution { fees: reanchored_fees, weight_limit },
2542 ]);
2543 Ok((local_execute_xcm, xcm_on_dest))
2544 }
2545
2546 fn destination_reserve_transfer_programs(
2547 origin: Location,
2548 dest: Location,
2549 beneficiary: Either<Location, Xcm<()>>,
2550 assets: Vec<Asset>,
2551 fees: FeesHandling<T>,
2552 weight_limit: WeightLimit,
2553 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2554 let value = (origin, assets);
2555 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2556 let (_, assets) = value;
2557 for asset in assets.iter() {
2558 ensure!(
2559 <T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&asset, &dest),
2560 Error::<T>::InvalidAssetUnsupportedReserve
2561 );
2562 }
2563
2564 let max_assets =
2566 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2567 let assets: Assets = assets.into();
2568 let context = T::UniversalLocation::get();
2569 let mut reanchored_assets = assets.clone();
2570 reanchored_assets
2571 .reanchor(&dest, &context)
2572 .map_err(|e| {
2573 tracing::error!(target: "xcm::pezpallet_xcm::destination_reserve_transfer_programs", ?e, ?dest, ?context, "Failed to re-anchor assets");
2574 Error::<T>::CannotReanchor
2575 })?;
2576
2577 let mut local_execute_xcm = Xcm(vec![
2579 WithdrawAsset(assets.clone()),
2581 BurnAsset(assets),
2583 ]);
2584 let mut xcm_on_dest = Xcm(vec![
2586 WithdrawAsset(reanchored_assets),
2588 ClearOrigin,
2590 ]);
2591 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2593
2594 let custom_remote_xcm = match beneficiary {
2596 Either::Right(custom_xcm) => custom_xcm,
2597 Either::Left(beneficiary) => {
2598 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2600 },
2601 };
2602 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2603
2604 Ok((local_execute_xcm, xcm_on_dest))
2605 }
2606
2607 fn remote_reserve_transfer_program(
2609 origin: Location,
2610 reserve: Location,
2611 beneficiary: Either<Location, Xcm<()>>,
2612 dest: Location,
2613 assets: Vec<Asset>,
2614 fees: Asset,
2615 weight_limit: WeightLimit,
2616 ) -> Result<Xcm<<T as Config>::RuntimeCall>, Error<T>> {
2617 let value = (origin, assets);
2618 ensure!(T::XcmReserveTransferFilter::contains(&value), Error::<T>::Filtered);
2619 let (_, assets) = value;
2620
2621 let max_assets = assets.len() as u32;
2622 let context = T::UniversalLocation::get();
2623 let (fees_half_1, fees_half_2) = Self::halve_fees(fees)?;
2626 let reserve_fees = fees_half_1
2628 .reanchored(&reserve, &context)
2629 .map_err(|e| {
2630 tracing::error!(target: "xcm::pezpallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor reserve_fees");
2631 Error::<T>::CannotReanchor
2632 })?;
2633 let dest_fees = fees_half_2
2635 .reanchored(&dest, &context)
2636 .map_err(|e| {
2637 tracing::error!(target: "xcm::pezpallet_xcm::remote_reserve_transfer_program", ?e, ?dest, ?context, "Failed to re-anchor dest_fees");
2638 Error::<T>::CannotReanchor
2639 })?;
2640 let dest = dest.reanchored(&reserve, &context).map_err(|e| {
2642 tracing::error!(target: "xcm::pezpallet_xcm::remote_reserve_transfer_program", ?e, ?reserve, ?context, "Failed to re-anchor dest");
2643 Error::<T>::CannotReanchor
2644 })?;
2645 let mut xcm_on_dest =
2647 Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
2648 let custom_xcm_on_dest = match beneficiary {
2650 Either::Right(custom_xcm) => custom_xcm,
2651 Either::Left(beneficiary) => {
2652 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2654 },
2655 };
2656 xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
2657 let xcm_on_reserve = Xcm(vec![
2659 BuyExecution { fees: reserve_fees, weight_limit },
2660 DepositReserveAsset { assets: Wild(AllCounted(max_assets)), dest, xcm: xcm_on_dest },
2661 ]);
2662 Ok(Xcm(vec![
2663 WithdrawAsset(assets.into()),
2664 SetFeesMode { jit_withdraw: true },
2665 InitiateReserveWithdraw {
2666 assets: Wild(AllCounted(max_assets)),
2667 reserve,
2668 xcm: xcm_on_reserve,
2669 },
2670 ]))
2671 }
2672
2673 fn teleport_fees_instructions(
2674 origin: Location,
2675 dest: Location,
2676 fees: Asset,
2677 weight_limit: WeightLimit,
2678 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2679 let value = (origin, vec![fees.clone()]);
2680 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2681 ensure!(
2682 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&fees, &dest),
2683 Error::<T>::Filtered
2684 );
2685
2686 let context = T::UniversalLocation::get();
2687 let reanchored_fees = fees
2688 .clone()
2689 .reanchored(&dest, &context)
2690 .map_err(|e| {
2691 tracing::error!(target: "xcm::pezpallet_xcm::teleport_fees_instructions", ?e, ?dest, ?context, "Failed to re-anchor fees");
2692 Error::<T>::CannotReanchor
2693 })?;
2694
2695 let dummy_context =
2697 XcmContext { origin: None, message_id: Default::default(), topic: None };
2698 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2703 &dest,
2704 &fees,
2705 &dummy_context,
2706 )
2707 .map_err(|e| {
2708 tracing::error!(target: "xcm::pezpallet_xcm::teleport_fees_instructions", ?e, ?fees, ?dest, "Failed can_check_out");
2709 Error::<T>::CannotCheckOutTeleport
2710 })?;
2711 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2714 &dest,
2715 &fees,
2716 &dummy_context,
2717 );
2718
2719 let fees: Assets = fees.into();
2720 let local_execute_xcm = Xcm(vec![
2721 WithdrawAsset(fees.clone()),
2723 BurnAsset(fees),
2725 ]);
2726 let xcm_on_dest = Xcm(vec![
2727 ReceiveTeleportedAsset(reanchored_fees.clone().into()),
2729 BuyExecution { fees: reanchored_fees, weight_limit },
2731 ]);
2732 Ok((local_execute_xcm, xcm_on_dest))
2733 }
2734
2735 fn teleport_assets_program(
2736 origin: Location,
2737 dest: Location,
2738 beneficiary: Either<Location, Xcm<()>>,
2739 assets: Vec<Asset>,
2740 fees: FeesHandling<T>,
2741 weight_limit: WeightLimit,
2742 ) -> Result<(Xcm<<T as Config>::RuntimeCall>, Xcm<()>), Error<T>> {
2743 let value = (origin, assets);
2744 ensure!(T::XcmTeleportFilter::contains(&value), Error::<T>::Filtered);
2745 let (_, assets) = value;
2746 for asset in assets.iter() {
2747 ensure!(
2748 <T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&asset, &dest),
2749 Error::<T>::Filtered
2750 );
2751 }
2752
2753 let max_assets =
2755 assets.len() as u32 + if matches!(&fees, FeesHandling::Batched { .. }) { 0 } else { 1 };
2756 let context = T::UniversalLocation::get();
2757 let assets: Assets = assets.into();
2758 let mut reanchored_assets = assets.clone();
2759 reanchored_assets
2760 .reanchor(&dest, &context)
2761 .map_err(|e| {
2762 tracing::error!(target: "xcm::pezpallet_xcm::teleport_assets_program", ?e, ?dest, ?context, "Failed to re-anchor asset");
2763 Error::<T>::CannotReanchor
2764 })?;
2765
2766 let dummy_context =
2768 XcmContext { origin: None, message_id: Default::default(), topic: None };
2769 for asset in assets.inner() {
2770 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::can_check_out(
2775 &dest,
2776 asset,
2777 &dummy_context,
2778 )
2779 .map_err(|e| {
2780 tracing::error!(target: "xcm::pezpallet_xcm::teleport_assets_program", ?e, ?asset, ?dest, "Failed can_check_out asset");
2781 Error::<T>::CannotCheckOutTeleport
2782 })?;
2783 }
2784 for asset in assets.inner() {
2785 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::check_out(
2788 &dest,
2789 asset,
2790 &dummy_context,
2791 );
2792 }
2793
2794 let mut local_execute_xcm = Xcm(vec![
2796 WithdrawAsset(assets.clone()),
2798 BurnAsset(assets),
2800 ]);
2801 let mut xcm_on_dest = Xcm(vec![
2803 ReceiveTeleportedAsset(reanchored_assets),
2805 ClearOrigin,
2807 ]);
2808 Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
2810
2811 let custom_remote_xcm = match beneficiary {
2813 Either::Right(custom_xcm) => custom_xcm,
2814 Either::Left(beneficiary) => {
2815 Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
2817 },
2818 };
2819 xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
2820
2821 Ok((local_execute_xcm, xcm_on_dest))
2822 }
2823
2824 pub(crate) fn halve_fees(fees: Asset) -> Result<(Asset, Asset), Error<T>> {
2826 match fees.fun {
2827 Fungible(amount) => {
2828 let fee1 = amount.saturating_div(2);
2829 let fee2 = amount.saturating_sub(fee1);
2830 ensure!(fee1 > 0, Error::<T>::FeesNotMet);
2831 ensure!(fee2 > 0, Error::<T>::FeesNotMet);
2832 Ok((Asset::from((fees.id.clone(), fee1)), Asset::from((fees.id.clone(), fee2))))
2833 },
2834 NonFungible(_) => Err(Error::<T>::FeesNotMet),
2835 }
2836 }
2837
2838 pub(crate) fn lazy_migration(
2841 mut stage: VersionMigrationStage,
2842 weight_cutoff: Weight,
2843 ) -> (Weight, Option<VersionMigrationStage>) {
2844 let mut weight_used = Weight::zero();
2845
2846 let sv_migrate_weight = T::WeightInfo::migrate_supported_version();
2847 let vn_migrate_weight = T::WeightInfo::migrate_version_notifiers();
2848 let vnt_already_notified_weight = T::WeightInfo::already_notified_target();
2849 let vnt_notify_weight = T::WeightInfo::notify_current_targets();
2850 let vnt_migrate_weight = T::WeightInfo::migrate_version_notify_targets();
2851 let vnt_migrate_fail_weight = T::WeightInfo::notify_target_migration_fail();
2852 let vnt_notify_migrate_weight = T::WeightInfo::migrate_and_notify_old_targets();
2853
2854 use VersionMigrationStage::*;
2855
2856 if stage == MigrateSupportedVersion {
2857 for v in 0..XCM_VERSION {
2860 for (old_key, value) in SupportedVersion::<T>::drain_prefix(v) {
2861 if let Ok(new_key) = old_key.into_latest() {
2862 SupportedVersion::<T>::insert(XCM_VERSION, new_key, value);
2863 }
2864 weight_used.saturating_accrue(sv_migrate_weight);
2865 if weight_used.any_gte(weight_cutoff) {
2866 return (weight_used, Some(stage));
2867 }
2868 }
2869 }
2870 stage = MigrateVersionNotifiers;
2871 }
2872 if stage == MigrateVersionNotifiers {
2873 for v in 0..XCM_VERSION {
2874 for (old_key, value) in VersionNotifiers::<T>::drain_prefix(v) {
2875 if let Ok(new_key) = old_key.into_latest() {
2876 VersionNotifiers::<T>::insert(XCM_VERSION, new_key, value);
2877 }
2878 weight_used.saturating_accrue(vn_migrate_weight);
2879 if weight_used.any_gte(weight_cutoff) {
2880 return (weight_used, Some(stage));
2881 }
2882 }
2883 }
2884 stage = NotifyCurrentTargets(None);
2885 }
2886
2887 let xcm_version = T::AdvertisedXcmVersion::get();
2888
2889 if let NotifyCurrentTargets(maybe_last_raw_key) = stage {
2890 let mut iter = match maybe_last_raw_key {
2891 Some(k) => VersionNotifyTargets::<T>::iter_prefix_from(XCM_VERSION, k),
2892 None => VersionNotifyTargets::<T>::iter_prefix(XCM_VERSION),
2893 };
2894 while let Some((key, value)) = iter.next() {
2895 let (query_id, max_weight, target_xcm_version) = value;
2896 let new_key: Location = match key.clone().try_into() {
2897 Ok(k) if target_xcm_version != xcm_version => k,
2898 _ => {
2899 weight_used.saturating_accrue(vnt_already_notified_weight);
2902 continue;
2903 },
2904 };
2905 let response = Response::Version(xcm_version);
2906 let message =
2907 Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]);
2908 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2909 Ok((message_id, cost)) => {
2910 let value = (query_id, max_weight, xcm_version);
2911 VersionNotifyTargets::<T>::insert(XCM_VERSION, key, value);
2912 Event::VersionChangeNotified {
2913 destination: new_key,
2914 result: xcm_version,
2915 cost,
2916 message_id,
2917 }
2918 },
2919 Err(e) => {
2920 VersionNotifyTargets::<T>::remove(XCM_VERSION, key);
2921 Event::NotifyTargetSendFail { location: new_key, query_id, error: e.into() }
2922 },
2923 };
2924 Self::deposit_event(event);
2925 weight_used.saturating_accrue(vnt_notify_weight);
2926 if weight_used.any_gte(weight_cutoff) {
2927 let last = Some(iter.last_raw_key().into());
2928 return (weight_used, Some(NotifyCurrentTargets(last)));
2929 }
2930 }
2931 stage = MigrateAndNotifyOldTargets;
2932 }
2933 if stage == MigrateAndNotifyOldTargets {
2934 for v in 0..XCM_VERSION {
2935 for (old_key, value) in VersionNotifyTargets::<T>::drain_prefix(v) {
2936 let (query_id, max_weight, target_xcm_version) = value;
2937 let new_key = match Location::try_from(old_key.clone()) {
2938 Ok(k) => k,
2939 Err(()) => {
2940 Self::deposit_event(Event::NotifyTargetMigrationFail {
2941 location: old_key,
2942 query_id: value.0,
2943 });
2944 weight_used.saturating_accrue(vnt_migrate_fail_weight);
2945 if weight_used.any_gte(weight_cutoff) {
2946 return (weight_used, Some(stage));
2947 }
2948 continue;
2949 },
2950 };
2951
2952 let versioned_key = LatestVersionedLocation(&new_key);
2953 if target_xcm_version == xcm_version {
2954 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_key, value);
2955 weight_used.saturating_accrue(vnt_migrate_weight);
2956 } else {
2957 let response = Response::Version(xcm_version);
2959 let message = Xcm(vec![QueryResponse {
2960 query_id,
2961 response,
2962 max_weight,
2963 querier: None,
2964 }]);
2965 let event = match send_xcm::<T::XcmRouter>(new_key.clone(), message) {
2966 Ok((message_id, cost)) => {
2967 VersionNotifyTargets::<T>::insert(
2968 XCM_VERSION,
2969 versioned_key,
2970 (query_id, max_weight, xcm_version),
2971 );
2972 Event::VersionChangeNotified {
2973 destination: new_key,
2974 result: xcm_version,
2975 cost,
2976 message_id,
2977 }
2978 },
2979 Err(e) => Event::NotifyTargetSendFail {
2980 location: new_key,
2981 query_id,
2982 error: e.into(),
2983 },
2984 };
2985 Self::deposit_event(event);
2986 weight_used.saturating_accrue(vnt_notify_migrate_weight);
2987 }
2988 if weight_used.any_gte(weight_cutoff) {
2989 return (weight_used, Some(stage));
2990 }
2991 }
2992 }
2993 }
2994 (weight_used, None)
2995 }
2996
2997 pub fn request_version_notify(dest: impl Into<Location>) -> XcmResult {
2999 let dest = dest.into();
3000 let versioned_dest = VersionedLocation::from(dest.clone());
3001 let already = VersionNotifiers::<T>::contains_key(XCM_VERSION, &versioned_dest);
3002 ensure!(!already, XcmError::InvalidLocation);
3003 let query_id = QueryCounter::<T>::mutate(|q| {
3004 let r = *q;
3005 q.saturating_inc();
3006 r
3007 });
3008 let instruction = SubscribeVersion { query_id, max_response_weight: Weight::zero() };
3010 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3011 Self::deposit_event(Event::VersionNotifyRequested { destination: dest, cost, message_id });
3012 VersionNotifiers::<T>::insert(XCM_VERSION, &versioned_dest, query_id);
3013 let query_status =
3014 QueryStatus::VersionNotifier { origin: versioned_dest, is_active: false };
3015 Queries::<T>::insert(query_id, query_status);
3016 Ok(())
3017 }
3018
3019 pub fn unrequest_version_notify(dest: impl Into<Location>) -> XcmResult {
3021 let dest = dest.into();
3022 let versioned_dest = LatestVersionedLocation(&dest);
3023 let query_id = VersionNotifiers::<T>::take(XCM_VERSION, versioned_dest)
3024 .ok_or(XcmError::InvalidLocation)?;
3025 let (message_id, cost) =
3026 send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![UnsubscribeVersion]))?;
3027 Self::deposit_event(Event::VersionNotifyUnrequested {
3028 destination: dest,
3029 cost,
3030 message_id,
3031 });
3032 Queries::<T>::remove(query_id);
3033 Ok(())
3034 }
3035
3036 pub fn send_xcm(
3040 interior: impl Into<Junctions>,
3041 dest: impl Into<Location>,
3042 mut message: Xcm<()>,
3043 ) -> Result<XcmHash, SendError> {
3044 let interior = interior.into();
3045 let local_origin = interior.clone().into();
3046 let dest = dest.into();
3047 let is_waived =
3048 <T::XcmExecutor as FeeManager>::is_waived(Some(&local_origin), FeeReason::ChargeFees);
3049 if interior != Junctions::Here {
3050 message.0.insert(0, DescendOrigin(interior.clone()));
3051 }
3052 tracing::debug!(target: "xcm::send_xcm", "{:?}, {:?}", dest.clone(), message.clone());
3053 let (ticket, price) = validate_send::<T::XcmRouter>(dest, message)?;
3054 if !is_waived {
3055 Self::charge_fees(local_origin, price).map_err(|e| {
3056 tracing::error!(
3057 target: "xcm::pezpallet_xcm::send_xcm",
3058 ?e,
3059 "Charging fees failed with error",
3060 );
3061 SendError::Fees
3062 })?;
3063 }
3064 T::XcmRouter::deliver(ticket)
3065 }
3066
3067 pub fn check_account() -> T::AccountId {
3068 const ID: PalletId = PalletId(*b"py/xcmch");
3069 AccountIdConversion::<T::AccountId>::into_account_truncating(&ID)
3070 }
3071
3072 pub fn dry_run_call<Runtime, Router, OriginCaller, RuntimeCall>(
3078 origin: OriginCaller,
3079 call: RuntimeCall,
3080 result_xcms_version: XcmVersion,
3081 ) -> Result<
3082 CallDryRunEffects<<Runtime as pezframe_system::Config>::RuntimeEvent>,
3083 XcmDryRunApiError,
3084 >
3085 where
3086 Runtime: crate::Config,
3087 Router: InspectMessageQueues,
3088 RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo>,
3089 <RuntimeCall as Dispatchable>::RuntimeOrigin: From<OriginCaller>,
3090 {
3091 crate::Pezpallet::<Runtime>::set_record_xcm(true);
3092 Router::clear_messages();
3094 pezframe_system::Pezpallet::<Runtime>::reset_events();
3096 let result = call.dispatch(origin.into());
3097 crate::Pezpallet::<Runtime>::set_record_xcm(false);
3098 let local_xcm = crate::Pezpallet::<Runtime>::recorded_xcm()
3099 .map(|xcm| VersionedXcm::<()>::from(xcm).into_version(result_xcms_version))
3100 .transpose()
3101 .map_err(|()| {
3102 tracing::error!(
3103 target: "xcm::DryRunApi::dry_run_call",
3104 "Local xcm version conversion failed"
3105 );
3106
3107 XcmDryRunApiError::VersionedConversionFailed
3108 })?;
3109
3110 let forwarded_xcms =
3112 Self::convert_forwarded_xcms(result_xcms_version, Router::get_messages()).inspect_err(
3113 |error| {
3114 tracing::error!(
3115 target: "xcm::DryRunApi::dry_run_call",
3116 ?error, "Forwarded xcms version conversion failed with error"
3117 );
3118 },
3119 )?;
3120 let events: Vec<<Runtime as pezframe_system::Config>::RuntimeEvent> =
3121 pezframe_system::Pezpallet::<Runtime>::read_events_no_consensus()
3122 .map(|record| record.event.clone())
3123 .collect();
3124 Ok(CallDryRunEffects {
3125 local_xcm: local_xcm.map(VersionedXcm::<()>::from),
3126 forwarded_xcms,
3127 emitted_events: events,
3128 execution_result: result,
3129 })
3130 }
3131
3132 pub fn dry_run_xcm<Router>(
3137 origin_location: VersionedLocation,
3138 xcm: VersionedXcm<<T as Config>::RuntimeCall>,
3139 ) -> Result<XcmDryRunEffects<<T as pezframe_system::Config>::RuntimeEvent>, XcmDryRunApiError>
3140 where
3141 Router: InspectMessageQueues,
3142 {
3143 let origin_location: Location = origin_location.try_into().map_err(|error| {
3144 tracing::error!(
3145 target: "xcm::DryRunApi::dry_run_xcm",
3146 ?error, "Location version conversion failed with error"
3147 );
3148 XcmDryRunApiError::VersionedConversionFailed
3149 })?;
3150 let xcm_version = xcm.identify_version();
3151 let xcm: Xcm<<T as Config>::RuntimeCall> = xcm.try_into().map_err(|error| {
3152 tracing::error!(
3153 target: "xcm::DryRunApi::dry_run_xcm",
3154 ?error, "Xcm version conversion failed with error"
3155 );
3156 XcmDryRunApiError::VersionedConversionFailed
3157 })?;
3158 let mut hash = xcm.using_encoded(pezsp_io::hashing::blake2_256);
3159
3160 Router::clear_messages();
3162 pezframe_system::Pezpallet::<T>::reset_events();
3163
3164 let result = <T as Config>::XcmExecutor::prepare_and_execute(
3165 origin_location,
3166 xcm,
3167 &mut hash,
3168 Weight::MAX, Weight::zero(),
3170 );
3171 let forwarded_xcms = Self::convert_forwarded_xcms(xcm_version, Router::get_messages())
3172 .inspect_err(|error| {
3173 tracing::error!(
3174 target: "xcm::DryRunApi::dry_run_xcm",
3175 ?error, "Forwarded xcms version conversion failed with error"
3176 );
3177 })?;
3178 let events: Vec<<T as pezframe_system::Config>::RuntimeEvent> =
3179 pezframe_system::Pezpallet::<T>::read_events_no_consensus()
3180 .map(|record| record.event.clone())
3181 .collect();
3182 Ok(XcmDryRunEffects { forwarded_xcms, emitted_events: events, execution_result: result })
3183 }
3184
3185 fn convert_xcms(
3186 xcm_version: XcmVersion,
3187 xcms: Vec<VersionedXcm<()>>,
3188 ) -> Result<Vec<VersionedXcm<()>>, ()> {
3189 xcms.into_iter()
3190 .map(|xcm| xcm.into_version(xcm_version))
3191 .collect::<Result<Vec<_>, ()>>()
3192 }
3193
3194 fn convert_forwarded_xcms(
3195 xcm_version: XcmVersion,
3196 forwarded_xcms: Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>,
3197 ) -> Result<Vec<(VersionedLocation, Vec<VersionedXcm<()>>)>, XcmDryRunApiError> {
3198 forwarded_xcms
3199 .into_iter()
3200 .map(|(dest, forwarded_xcms)| {
3201 let dest = dest.into_version(xcm_version)?;
3202 let forwarded_xcms = Self::convert_xcms(xcm_version, forwarded_xcms)?;
3203
3204 Ok((dest, forwarded_xcms))
3205 })
3206 .collect::<Result<Vec<_>, ()>>()
3207 .map_err(|()| {
3208 tracing::debug!(
3209 target: "xcm::pezpallet_xcm::convert_forwarded_xcms",
3210 "Failed to convert VersionedLocation to requested version",
3211 );
3212 XcmDryRunApiError::VersionedConversionFailed
3213 })
3214 }
3215
3216 pub fn query_acceptable_payment_assets(
3221 version: xcm::Version,
3222 asset_ids: Vec<AssetId>,
3223 ) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> {
3224 Ok(asset_ids
3225 .into_iter()
3226 .map(|asset_id| VersionedAssetId::from(asset_id))
3227 .filter_map(|asset_id| asset_id.into_version(version).ok())
3228 .collect())
3229 }
3230
3231 pub fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> {
3232 let message = Xcm::<()>::try_from(message.clone())
3233 .map_err(|e| {
3234 tracing::debug!(target: "xcm::pezpallet_xcm::query_xcm_weight", ?e, ?message, "Failed to convert versioned message");
3235 XcmPaymentApiError::VersionedConversionFailed
3236 })?;
3237
3238 T::Weigher::weight(&mut message.clone().into(), Weight::MAX).map_err(|error| {
3239 tracing::debug!(target: "xcm::pezpallet_xcm::query_xcm_weight", ?error, ?message, "Error when querying XCM weight");
3240 XcmPaymentApiError::WeightNotComputable
3241 })
3242 }
3243
3244 pub fn query_weight_to_asset_fee<Trader: xcm_executor::traits::WeightTrader>(
3261 weight: Weight,
3262 asset: VersionedAssetId,
3263 ) -> Result<u128, XcmPaymentApiError> {
3264 let asset: AssetId = asset.clone().try_into()
3265 .map_err(|e| {
3266 tracing::debug!(target: "xcm::pezpallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to convert versioned asset");
3267 XcmPaymentApiError::VersionedConversionFailed
3268 })?;
3269
3270 let max_amount = u128::MAX / 2;
3271 let max_payment: Asset = (asset.clone(), max_amount).into();
3272 let context = XcmContext::with_message_id(XcmHash::default());
3273
3274 let unspent = with_transaction(|| {
3277 let mut trader = Trader::new();
3278 let result = trader.buy_weight(weight, max_payment.into(), &context)
3279 .map_err(|e| {
3280 tracing::error!(target: "xcm::pezpallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to buy weight");
3281
3282 DispatchError::Other("Failed to buy weight")
3284 });
3285
3286 TransactionOutcome::Rollback(result)
3287 }).map_err(|error| {
3288 tracing::debug!(target: "xcm::pezpallet::query_weight_to_asset_fee", ?error, "Failed to execute transaction");
3289 XcmPaymentApiError::AssetNotFound
3290 })?;
3291
3292 let Some(unspent) = unspent.fungible.get(&asset) else {
3293 tracing::error!(target: "xcm::pezpallet::query_weight_to_asset_fee", ?asset, "The trader didn't return the needed fungible asset");
3294 return Err(XcmPaymentApiError::AssetNotFound);
3295 };
3296
3297 let paid = max_amount - unspent;
3298 Ok(paid)
3299 }
3300
3301 pub fn query_delivery_fees<AssetExchanger: xcm_executor::traits::AssetExchange>(
3308 destination: VersionedLocation,
3309 message: VersionedXcm<()>,
3310 versioned_asset_id: VersionedAssetId,
3311 ) -> Result<VersionedAssets, XcmPaymentApiError> {
3312 let result_version = destination.identify_version().max(message.identify_version());
3313
3314 let destination: Location = destination
3315 .clone()
3316 .try_into()
3317 .map_err(|e| {
3318 tracing::error!(target: "xcm::pezpallet_xcm::query_delivery_fees", ?e, ?destination, "Failed to convert versioned destination");
3319 XcmPaymentApiError::VersionedConversionFailed
3320 })?;
3321
3322 let message: Xcm<()> =
3323 message.clone().try_into().map_err(|e| {
3324 tracing::error!(target: "xcm::pezpallet_xcm::query_delivery_fees", ?e, ?message, "Failed to convert versioned message");
3325 XcmPaymentApiError::VersionedConversionFailed
3326 })?;
3327
3328 let (_, fees) = validate_send::<T::XcmRouter>(destination.clone(), message.clone()).map_err(|error| {
3329 tracing::error!(target: "xcm::pezpallet_xcm::query_delivery_fees", ?error, ?destination, ?message, "Failed to validate send to destination");
3330 XcmPaymentApiError::Unroutable
3331 })?;
3332
3333 if fees.len() != 1 {
3335 return Err(XcmPaymentApiError::Unimplemented);
3336 }
3337
3338 let fee = fees.get(0).ok_or(XcmPaymentApiError::Unimplemented)?;
3339
3340 let asset_id = versioned_asset_id.clone().try_into().map_err(|()| {
3341 tracing::trace!(
3342 target: "xcm::xcm_runtime_pezapis::query_delivery_fees",
3343 "Failed to convert asset id: {versioned_asset_id:?}!"
3344 );
3345 XcmPaymentApiError::VersionedConversionFailed
3346 })?;
3347
3348 let assets_to_pay = if fee.id == asset_id {
3349 fees
3351 } else {
3352 AssetExchanger::quote_exchange_price(
3354 &fees.into(),
3355 &(asset_id, Fungible(1)).into(),
3356 true, )
3358 .ok_or(XcmPaymentApiError::AssetNotFound)?
3359 };
3360
3361 VersionedAssets::from(assets_to_pay).into_version(result_version).map_err(|e| {
3362 tracing::trace!(
3363 target: "xcm::pezpallet_xcm::query_delivery_fees",
3364 ?e,
3365 ?result_version,
3366 "Failed to convert fees into desired version"
3367 );
3368 XcmPaymentApiError::VersionedConversionFailed
3369 })
3370 }
3371
3372 pub fn is_trusted_reserve(
3375 asset: VersionedAsset,
3376 location: VersionedLocation,
3377 ) -> Result<bool, TrustedQueryApiError> {
3378 let location: Location = location.try_into().map_err(|e| {
3379 tracing::debug!(
3380 target: "xcm::pezpallet_xcm::is_trusted_reserve",
3381 ?e, "Failed to convert versioned location",
3382 );
3383 TrustedQueryApiError::VersionedLocationConversionFailed
3384 })?;
3385
3386 let a: Asset = asset.try_into().map_err(|e| {
3387 tracing::debug!(
3388 target: "xcm::pezpallet_xcm::is_trusted_reserve",
3389 ?e, "Failed to convert versioned asset",
3390 );
3391 TrustedQueryApiError::VersionedAssetConversionFailed
3392 })?;
3393
3394 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsReserve::contains(&a, &location))
3395 }
3396
3397 pub fn is_trusted_teleporter(
3399 asset: VersionedAsset,
3400 location: VersionedLocation,
3401 ) -> Result<bool, TrustedQueryApiError> {
3402 let location: Location = location.try_into().map_err(|e| {
3403 tracing::debug!(
3404 target: "xcm::pezpallet_xcm::is_trusted_teleporter",
3405 ?e, "Failed to convert versioned location",
3406 );
3407 TrustedQueryApiError::VersionedLocationConversionFailed
3408 })?;
3409 let a: Asset = asset.try_into().map_err(|e| {
3410 tracing::debug!(
3411 target: "xcm::pezpallet_xcm::is_trusted_teleporter",
3412 ?e, "Failed to convert versioned asset",
3413 );
3414 TrustedQueryApiError::VersionedAssetConversionFailed
3415 })?;
3416 Ok(<T::XcmExecutor as XcmAssetTransfers>::IsTeleporter::contains(&a, &location))
3417 }
3418
3419 pub fn authorized_aliasers(
3421 target: VersionedLocation,
3422 ) -> Result<Vec<OriginAliaser>, AuthorizedAliasersApiError> {
3423 let desired_version = target.identify_version();
3424 let target: VersionedLocation = target.into_version(XCM_VERSION).map_err(|e| {
3426 tracing::debug!(
3427 target: "xcm::pezpallet_xcm::authorized_aliasers",
3428 ?e, "Failed to convert versioned location",
3429 );
3430 AuthorizedAliasersApiError::LocationVersionConversionFailed
3431 })?;
3432 Ok(AuthorizedAliases::<T>::get(&target)
3433 .map(|authorized| {
3434 authorized
3435 .aliasers
3436 .into_iter()
3437 .filter_map(|aliaser| {
3438 let OriginAliaser { location, expiry } = aliaser;
3439 location
3440 .into_version(desired_version)
3441 .map(|location| OriginAliaser { location, expiry })
3442 .ok()
3443 })
3444 .collect()
3445 })
3446 .unwrap_or_default())
3447 }
3448
3449 pub fn is_authorized_alias(
3454 origin: VersionedLocation,
3455 target: VersionedLocation,
3456 ) -> Result<bool, AuthorizedAliasersApiError> {
3457 let desired_version = target.identify_version();
3458 let origin = origin.into_version(desired_version).map_err(|e| {
3459 tracing::debug!(
3460 target: "xcm::pezpallet_xcm::is_authorized_alias",
3461 ?e, "mismatching origin and target versions",
3462 );
3463 AuthorizedAliasersApiError::LocationVersionConversionFailed
3464 })?;
3465 Ok(Self::authorized_aliasers(target)?.into_iter().any(|aliaser| {
3466 aliaser.location == origin
3469 && aliaser
3470 .expiry
3471 .map(|expiry| {
3472 pezframe_system::Pezpallet::<T>::current_block_number()
3473 .saturated_into::<u64>()
3474 < expiry
3475 })
3476 .unwrap_or(true)
3477 }))
3478 }
3479
3480 fn do_new_query(
3482 responder: impl Into<Location>,
3483 maybe_notify: Option<(u8, u8)>,
3484 timeout: BlockNumberFor<T>,
3485 match_querier: impl Into<Location>,
3486 ) -> u64 {
3487 QueryCounter::<T>::mutate(|q| {
3488 let r = *q;
3489 q.saturating_inc();
3490 Queries::<T>::insert(
3491 r,
3492 QueryStatus::Pending {
3493 responder: responder.into().into(),
3494 maybe_match_querier: Some(match_querier.into().into()),
3495 maybe_notify,
3496 timeout,
3497 },
3498 );
3499 r
3500 })
3501 }
3502
3503 pub fn report_outcome_notify(
3526 message: &mut Xcm<()>,
3527 responder: impl Into<Location>,
3528 notify: impl Into<<T as Config>::RuntimeCall>,
3529 timeout: BlockNumberFor<T>,
3530 ) -> Result<(), XcmError> {
3531 let responder = responder.into();
3532 let destination = T::UniversalLocation::get().invert_target(&responder).map_err(|()| {
3533 tracing::debug!(
3534 target: "xcm::pezpallet_xcm::report_outcome_notify",
3535 "Failed to invert responder location to universal location",
3536 );
3537 XcmError::LocationNotInvertible
3538 })?;
3539 let notify: <T as Config>::RuntimeCall = notify.into();
3540 let max_weight = notify.get_dispatch_info().call_weight;
3541 let query_id = Self::new_notify_query(responder, notify, timeout, Here);
3542 let response_info = QueryResponseInfo { destination, query_id, max_weight };
3543 let report_error = Xcm(vec![ReportError(response_info)]);
3544 message.0.insert(0, SetAppendix(report_error));
3545 Ok(())
3546 }
3547
3548 pub fn new_notify_query(
3551 responder: impl Into<Location>,
3552 notify: impl Into<<T as Config>::RuntimeCall>,
3553 timeout: BlockNumberFor<T>,
3554 match_querier: impl Into<Location>,
3555 ) -> u64 {
3556 let notify = notify.into().using_encoded(|mut bytes| Decode::decode(&mut bytes)).expect(
3557 "decode input is output of Call encode; Call guaranteed to have two enums; qed",
3558 );
3559 Self::do_new_query(responder, Some(notify), timeout, match_querier)
3560 }
3561
3562 fn note_unknown_version(dest: &Location) {
3565 tracing::trace!(
3566 target: "xcm::pezpallet_xcm::note_unknown_version",
3567 ?dest, "XCM version is unknown for destination"
3568 );
3569 let versioned_dest = VersionedLocation::from(dest.clone());
3570 VersionDiscoveryQueue::<T>::mutate(|q| {
3571 if let Some(index) = q.iter().position(|i| &i.0 == &versioned_dest) {
3572 q[index].1.saturating_inc();
3574 } else {
3575 let _ = q.try_push((versioned_dest, 1));
3576 }
3577 });
3578 }
3579
3580 fn charge_fees(location: Location, assets: Assets) -> DispatchResult {
3586 T::XcmExecutor::charge_fees(location.clone(), assets.clone()).map_err(|error| {
3587 tracing::debug!(
3588 target: "xcm::pezpallet_xcm::charge_fees", ?error,
3589 "Failed to charge fees for location with assets",
3590 );
3591 Error::<T>::FeesNotMet
3592 })?;
3593 Self::deposit_event(Event::FeesPaid { paying: location, fees: assets });
3594 Ok(())
3595 }
3596
3597 #[cfg(any(feature = "try-runtime", test))]
3607 pub fn do_try_state() -> Result<(), TryRuntimeError> {
3608 use migration::data::NeedsMigration;
3609
3610 let minimal_allowed_xcm_version = if let Some(safe_xcm_version) = SafeXcmVersion::<T>::get()
3614 {
3615 XCM_VERSION.saturating_sub(1).min(safe_xcm_version)
3616 } else {
3617 XCM_VERSION.saturating_sub(1)
3618 };
3619
3620 ensure!(
3622 !Queries::<T>::iter_values()
3623 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3624 TryRuntimeError::Other("`Queries` data should be migrated to the higher xcm version!")
3625 );
3626
3627 ensure!(
3629 !LockedFungibles::<T>::iter_values()
3630 .any(|data| data.needs_migration(minimal_allowed_xcm_version)),
3631 TryRuntimeError::Other(
3632 "`LockedFungibles` data should be migrated to the higher xcm version!"
3633 )
3634 );
3635
3636 ensure!(
3638 !RemoteLockedFungibles::<T>::iter()
3639 .any(|(key, data)| key.needs_migration(minimal_allowed_xcm_version)
3640 || data.needs_migration(minimal_allowed_xcm_version)),
3641 TryRuntimeError::Other(
3642 "`RemoteLockedFungibles` data should be migrated to the higher xcm version!"
3643 )
3644 );
3645
3646 if CurrentMigration::<T>::exists() {
3649 return Ok(());
3650 }
3651
3652 for v in 0..XCM_VERSION {
3654 ensure!(
3655 SupportedVersion::<T>::iter_prefix(v).next().is_none(),
3656 TryRuntimeError::Other(
3657 "`SupportedVersion` data should be migrated to the `XCM_VERSION`!`"
3658 )
3659 );
3660 ensure!(
3661 VersionNotifiers::<T>::iter_prefix(v).next().is_none(),
3662 TryRuntimeError::Other(
3663 "`VersionNotifiers` data should be migrated to the `XCM_VERSION`!`"
3664 )
3665 );
3666 ensure!(
3667 VersionNotifyTargets::<T>::iter_prefix(v).next().is_none(),
3668 TryRuntimeError::Other(
3669 "`VersionNotifyTargets` data should be migrated to the `XCM_VERSION`!`"
3670 )
3671 );
3672 }
3673
3674 Ok(())
3675 }
3676}
3677
3678pub struct LockTicket<T: Config> {
3679 sovereign_account: T::AccountId,
3680 amount: BalanceOf<T>,
3681 unlocker: Location,
3682 item_index: Option<usize>,
3683}
3684
3685impl<T: Config> xcm_executor::traits::Enact for LockTicket<T> {
3686 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3687 use xcm_executor::traits::LockError::UnexpectedState;
3688 let mut locks = LockedFungibles::<T>::get(&self.sovereign_account).unwrap_or_default();
3689 match self.item_index {
3690 Some(index) => {
3691 ensure!(locks.len() > index, UnexpectedState);
3692 ensure!(locks[index].1.try_as::<_>() == Ok(&self.unlocker), UnexpectedState);
3693 locks[index].0 = locks[index].0.max(self.amount);
3694 },
3695 None => {
3696 locks.try_push((self.amount, self.unlocker.into())).map_err(
3697 |(balance, location)| {
3698 tracing::debug!(
3699 target: "xcm::pezpallet_xcm::enact", ?balance, ?location,
3700 "Failed to lock fungibles",
3701 );
3702 UnexpectedState
3703 },
3704 )?;
3705 },
3706 }
3707 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3708 T::Currency::extend_lock(
3709 *b"py/xcmlk",
3710 &self.sovereign_account,
3711 self.amount,
3712 WithdrawReasons::all(),
3713 );
3714 Ok(())
3715 }
3716}
3717
3718pub struct UnlockTicket<T: Config> {
3719 sovereign_account: T::AccountId,
3720 amount: BalanceOf<T>,
3721 unlocker: Location,
3722}
3723
3724impl<T: Config> xcm_executor::traits::Enact for UnlockTicket<T> {
3725 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3726 use xcm_executor::traits::LockError::UnexpectedState;
3727 let mut locks =
3728 LockedFungibles::<T>::get(&self.sovereign_account).ok_or(UnexpectedState)?;
3729 let mut maybe_remove_index = None;
3730 let mut locked = BalanceOf::<T>::zero();
3731 let mut found = false;
3732 for (i, x) in locks.iter_mut().enumerate() {
3735 if x.1.try_as::<_>().defensive() == Ok(&self.unlocker) {
3736 x.0 = x.0.saturating_sub(self.amount);
3737 if x.0.is_zero() {
3738 maybe_remove_index = Some(i);
3739 }
3740 found = true;
3741 }
3742 locked = locked.max(x.0);
3743 }
3744 ensure!(found, UnexpectedState);
3745 if let Some(remove_index) = maybe_remove_index {
3746 locks.swap_remove(remove_index);
3747 }
3748 LockedFungibles::<T>::insert(&self.sovereign_account, locks);
3749 let reasons = WithdrawReasons::all();
3750 T::Currency::set_lock(*b"py/xcmlk", &self.sovereign_account, locked, reasons);
3751 Ok(())
3752 }
3753}
3754
3755pub struct ReduceTicket<T: Config> {
3756 key: (u32, T::AccountId, VersionedAssetId),
3757 amount: u128,
3758 locker: VersionedLocation,
3759 owner: VersionedLocation,
3760}
3761
3762impl<T: Config> xcm_executor::traits::Enact for ReduceTicket<T> {
3763 fn enact(self) -> Result<(), xcm_executor::traits::LockError> {
3764 use xcm_executor::traits::LockError::UnexpectedState;
3765 let mut record = RemoteLockedFungibles::<T>::get(&self.key).ok_or(UnexpectedState)?;
3766 ensure!(self.locker == record.locker && self.owner == record.owner, UnexpectedState);
3767 let new_amount = record.amount.checked_sub(self.amount).ok_or(UnexpectedState)?;
3768 ensure!(record.amount_held().map_or(true, |h| new_amount >= h), UnexpectedState);
3769 if new_amount == 0 {
3770 RemoteLockedFungibles::<T>::remove(&self.key);
3771 } else {
3772 record.amount = new_amount;
3773 RemoteLockedFungibles::<T>::insert(&self.key, &record);
3774 }
3775 Ok(())
3776 }
3777}
3778
3779impl<T: Config> xcm_executor::traits::AssetLock for Pezpallet<T> {
3780 type LockTicket = LockTicket<T>;
3781 type UnlockTicket = UnlockTicket<T>;
3782 type ReduceTicket = ReduceTicket<T>;
3783
3784 fn prepare_lock(
3785 unlocker: Location,
3786 asset: Asset,
3787 owner: Location,
3788 ) -> Result<LockTicket<T>, xcm_executor::traits::LockError> {
3789 use xcm_executor::traits::LockError::*;
3790 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3791 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3792 ensure!(T::Currency::free_balance(&sovereign_account) >= amount, AssetNotOwned);
3793 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3794 let item_index = locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker));
3795 ensure!(item_index.is_some() || locks.len() < T::MaxLockers::get() as usize, NoResources);
3796 Ok(LockTicket { sovereign_account, amount, unlocker, item_index })
3797 }
3798
3799 fn prepare_unlock(
3800 unlocker: Location,
3801 asset: Asset,
3802 owner: Location,
3803 ) -> Result<UnlockTicket<T>, xcm_executor::traits::LockError> {
3804 use xcm_executor::traits::LockError::*;
3805 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3806 let amount = T::CurrencyMatcher::matches_fungible(&asset).ok_or(UnknownAsset)?;
3807 let locks = LockedFungibles::<T>::get(&sovereign_account).unwrap_or_default();
3808 let item_index =
3809 locks.iter().position(|x| x.1.try_as::<_>() == Ok(&unlocker)).ok_or(NotLocked)?;
3810 ensure!(locks[item_index].0 >= amount, NotLocked);
3811 Ok(UnlockTicket { sovereign_account, amount, unlocker })
3812 }
3813
3814 fn note_unlockable(
3815 locker: Location,
3816 asset: Asset,
3817 mut owner: Location,
3818 ) -> Result<(), xcm_executor::traits::LockError> {
3819 use xcm_executor::traits::LockError::*;
3820 ensure!(T::TrustedLockers::contains(&locker, &asset), NotTrusted);
3821 let amount = match asset.fun {
3822 Fungible(a) => a,
3823 NonFungible(_) => return Err(Unimplemented),
3824 };
3825 owner.remove_network_id();
3826 let account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3827 let locker = locker.into();
3828 let owner = owner.into();
3829 let id: VersionedAssetId = asset.id.into();
3830 let key = (XCM_VERSION, account, id);
3831 let mut record =
3832 RemoteLockedFungibleRecord { amount, owner, locker, consumers: BoundedVec::default() };
3833 if let Some(old) = RemoteLockedFungibles::<T>::get(&key) {
3834 ensure!(old.locker == record.locker && old.owner == record.owner, WouldClobber);
3836 record.consumers = old.consumers;
3837 record.amount = record.amount.max(old.amount);
3838 }
3839 RemoteLockedFungibles::<T>::insert(&key, record);
3840 Ok(())
3841 }
3842
3843 fn prepare_reduce_unlockable(
3844 locker: Location,
3845 asset: Asset,
3846 mut owner: Location,
3847 ) -> Result<Self::ReduceTicket, xcm_executor::traits::LockError> {
3848 use xcm_executor::traits::LockError::*;
3849 let amount = match asset.fun {
3850 Fungible(a) => a,
3851 NonFungible(_) => return Err(Unimplemented),
3852 };
3853 owner.remove_network_id();
3854 let sovereign_account = T::SovereignAccountOf::convert_location(&owner).ok_or(BadOwner)?;
3855 let locker = locker.into();
3856 let owner = owner.into();
3857 let id: VersionedAssetId = asset.id.into();
3858 let key = (XCM_VERSION, sovereign_account, id);
3859
3860 let record = RemoteLockedFungibles::<T>::get(&key).ok_or(NotLocked)?;
3861 ensure!(locker == record.locker && owner == record.owner, WouldClobber);
3863 ensure!(record.amount >= amount, NotEnoughLocked);
3864 ensure!(
3865 record.amount_held().map_or(true, |h| record.amount.saturating_sub(amount) >= h),
3866 InUse
3867 );
3868 Ok(ReduceTicket { key, amount, locker, owner })
3869 }
3870}
3871
3872impl<T: Config> WrapVersion for Pezpallet<T> {
3873 fn wrap_version<RuntimeCall: Decode + GetDispatchInfo>(
3874 dest: &Location,
3875 xcm: impl Into<VersionedXcm<RuntimeCall>>,
3876 ) -> Result<VersionedXcm<RuntimeCall>, ()> {
3877 Self::get_version_for(dest)
3878 .or_else(|| {
3879 Self::note_unknown_version(dest);
3880 SafeXcmVersion::<T>::get()
3881 })
3882 .ok_or_else(|| {
3883 tracing::trace!(
3884 target: "xcm::pezpallet_xcm::wrap_version",
3885 ?dest, "Could not determine a version to wrap XCM for destination",
3886 );
3887 ()
3888 })
3889 .and_then(|v| xcm.into().into_version(v.min(XCM_VERSION)))
3890 }
3891}
3892
3893impl<T: Config> GetVersion for Pezpallet<T> {
3894 fn get_version_for(dest: &Location) -> Option<XcmVersion> {
3895 SupportedVersion::<T>::get(XCM_VERSION, LatestVersionedLocation(dest))
3896 }
3897}
3898
3899impl<T: Config> VersionChangeNotifier for Pezpallet<T> {
3900 fn start(
3909 dest: &Location,
3910 query_id: QueryId,
3911 max_weight: Weight,
3912 _context: &XcmContext,
3913 ) -> XcmResult {
3914 let versioned_dest = LatestVersionedLocation(dest);
3915 let already = VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest);
3916 ensure!(!already, XcmError::InvalidLocation);
3917
3918 let xcm_version = T::AdvertisedXcmVersion::get();
3919 let response = Response::Version(xcm_version);
3920 let instruction = QueryResponse { query_id, response, max_weight, querier: None };
3921 let (message_id, cost) = send_xcm::<T::XcmRouter>(dest.clone(), Xcm(vec![instruction]))?;
3922 Self::deposit_event(Event::<T>::VersionNotifyStarted {
3923 destination: dest.clone(),
3924 cost,
3925 message_id,
3926 });
3927
3928 let value = (query_id, max_weight, xcm_version);
3929 VersionNotifyTargets::<T>::insert(XCM_VERSION, versioned_dest, value);
3930 Ok(())
3931 }
3932
3933 fn stop(dest: &Location, _context: &XcmContext) -> XcmResult {
3936 VersionNotifyTargets::<T>::remove(XCM_VERSION, LatestVersionedLocation(dest));
3937 Ok(())
3938 }
3939
3940 fn is_subscribed(dest: &Location) -> bool {
3942 let versioned_dest = LatestVersionedLocation(dest);
3943 VersionNotifyTargets::<T>::contains_key(XCM_VERSION, versioned_dest)
3944 }
3945}
3946
3947impl<T: Config> DropAssets for Pezpallet<T> {
3948 fn drop_assets(origin: &Location, assets: AssetsInHolding, _context: &XcmContext) -> Weight {
3949 if assets.is_empty() {
3950 return Weight::zero();
3951 }
3952 let versioned = VersionedAssets::from(Assets::from(assets));
3953 let hash = BlakeTwo256::hash_of(&(&origin, &versioned));
3954 AssetTraps::<T>::mutate(hash, |n| *n += 1);
3955 Self::deposit_event(Event::AssetsTrapped {
3956 hash,
3957 origin: origin.clone(),
3958 assets: versioned,
3959 });
3960 Weight::zero()
3962 }
3963}
3964
3965impl<T: Config> ClaimAssets for Pezpallet<T> {
3966 fn claim_assets(
3967 origin: &Location,
3968 ticket: &Location,
3969 assets: &Assets,
3970 _context: &XcmContext,
3971 ) -> bool {
3972 let mut versioned = VersionedAssets::from(assets.clone());
3973 match ticket.unpack() {
3974 (0, [GeneralIndex(i)]) => {
3975 versioned = match versioned.into_version(*i as u32) {
3976 Ok(v) => v,
3977 Err(()) => return false,
3978 }
3979 },
3980 (0, []) => (),
3981 _ => return false,
3982 };
3983 let hash = BlakeTwo256::hash_of(&(origin.clone(), versioned.clone()));
3984 match AssetTraps::<T>::get(hash) {
3985 0 => return false,
3986 1 => AssetTraps::<T>::remove(hash),
3987 n => AssetTraps::<T>::insert(hash, n - 1),
3988 }
3989 Self::deposit_event(Event::AssetsClaimed {
3990 hash,
3991 origin: origin.clone(),
3992 assets: versioned,
3993 });
3994 return true;
3995 }
3996}
3997
3998impl<T: Config> OnResponse for Pezpallet<T> {
3999 fn expecting_response(
4000 origin: &Location,
4001 query_id: QueryId,
4002 querier: Option<&Location>,
4003 ) -> bool {
4004 match Queries::<T>::get(query_id) {
4005 Some(QueryStatus::Pending { responder, maybe_match_querier, .. }) => {
4006 Location::try_from(responder).map_or(false, |r| origin == &r)
4007 && maybe_match_querier.map_or(true, |match_querier| {
4008 Location::try_from(match_querier).map_or(false, |match_querier| {
4009 querier.map_or(false, |q| q == &match_querier)
4010 })
4011 })
4012 },
4013 Some(QueryStatus::VersionNotifier { origin: r, .. }) => {
4014 Location::try_from(r).map_or(false, |r| origin == &r)
4015 },
4016 _ => false,
4017 }
4018 }
4019
4020 fn on_response(
4021 origin: &Location,
4022 query_id: QueryId,
4023 querier: Option<&Location>,
4024 response: Response,
4025 max_weight: Weight,
4026 _context: &XcmContext,
4027 ) -> Weight {
4028 let origin = origin.clone();
4029 match (response, Queries::<T>::get(query_id)) {
4030 (
4031 Response::Version(v),
4032 Some(QueryStatus::VersionNotifier { origin: expected_origin, is_active }),
4033 ) => {
4034 let origin: Location = match expected_origin.try_into() {
4035 Ok(o) if o == origin => o,
4036 Ok(o) => {
4037 Self::deposit_event(Event::InvalidResponder {
4038 origin: origin.clone(),
4039 query_id,
4040 expected_location: Some(o),
4041 });
4042 return Weight::zero();
4043 },
4044 _ => {
4045 Self::deposit_event(Event::InvalidResponder {
4046 origin: origin.clone(),
4047 query_id,
4048 expected_location: None,
4049 });
4050 return Weight::zero();
4052 },
4053 };
4054 if !is_active {
4056 Queries::<T>::insert(
4057 query_id,
4058 QueryStatus::VersionNotifier {
4059 origin: origin.clone().into(),
4060 is_active: true,
4061 },
4062 );
4063 }
4064 SupportedVersion::<T>::insert(XCM_VERSION, LatestVersionedLocation(&origin), v);
4066 Self::deposit_event(Event::SupportedVersionChanged {
4067 location: origin,
4068 version: v,
4069 });
4070 Weight::zero()
4071 },
4072 (
4073 response,
4074 Some(QueryStatus::Pending { responder, maybe_notify, maybe_match_querier, .. }),
4075 ) => {
4076 if let Some(match_querier) = maybe_match_querier {
4077 let match_querier = match Location::try_from(match_querier) {
4078 Ok(mq) => mq,
4079 Err(_) => {
4080 Self::deposit_event(Event::InvalidQuerierVersion {
4081 origin: origin.clone(),
4082 query_id,
4083 });
4084 return Weight::zero();
4085 },
4086 };
4087 if querier.map_or(true, |q| q != &match_querier) {
4088 Self::deposit_event(Event::InvalidQuerier {
4089 origin: origin.clone(),
4090 query_id,
4091 expected_querier: match_querier,
4092 maybe_actual_querier: querier.cloned(),
4093 });
4094 return Weight::zero();
4095 }
4096 }
4097 let responder = match Location::try_from(responder) {
4098 Ok(r) => r,
4099 Err(_) => {
4100 Self::deposit_event(Event::InvalidResponderVersion {
4101 origin: origin.clone(),
4102 query_id,
4103 });
4104 return Weight::zero();
4105 },
4106 };
4107 if origin != responder {
4108 Self::deposit_event(Event::InvalidResponder {
4109 origin: origin.clone(),
4110 query_id,
4111 expected_location: Some(responder),
4112 });
4113 return Weight::zero();
4114 }
4115 match maybe_notify {
4116 Some((pezpallet_index, call_index)) => {
4117 let bare = (pezpallet_index, call_index, query_id, response);
4121 if let Ok(call) = bare.using_encoded(|mut bytes| {
4122 <T as Config>::RuntimeCall::decode(&mut bytes)
4123 }) {
4124 Queries::<T>::remove(query_id);
4125 let weight = call.get_dispatch_info().call_weight;
4126 if weight.any_gt(max_weight) {
4127 let e = Event::NotifyOverweight {
4128 query_id,
4129 pezpallet_index,
4130 call_index,
4131 actual_weight: weight,
4132 max_budgeted_weight: max_weight,
4133 };
4134 Self::deposit_event(e);
4135 return Weight::zero();
4136 }
4137 let dispatch_origin = Origin::Response(origin.clone()).into();
4138 match call.dispatch(dispatch_origin) {
4139 Ok(post_info) => {
4140 let e =
4141 Event::Notified { query_id, pezpallet_index, call_index };
4142 Self::deposit_event(e);
4143 post_info.actual_weight
4144 },
4145 Err(error_and_info) => {
4146 let e = Event::NotifyDispatchError {
4147 query_id,
4148 pezpallet_index,
4149 call_index,
4150 };
4151 Self::deposit_event(e);
4152 error_and_info.post_info.actual_weight
4155 },
4156 }
4157 .unwrap_or(weight)
4158 } else {
4159 let e =
4160 Event::NotifyDecodeFailed { query_id, pezpallet_index, call_index };
4161 Self::deposit_event(e);
4162 Weight::zero()
4163 }
4164 },
4165 None => {
4166 let e = Event::ResponseReady { query_id, response: response.clone() };
4167 Self::deposit_event(e);
4168 let at = pezframe_system::Pezpallet::<T>::current_block_number();
4169 let response = response.into();
4170 Queries::<T>::insert(query_id, QueryStatus::Ready { response, at });
4171 Weight::zero()
4172 },
4173 }
4174 },
4175 _ => {
4176 let e = Event::UnexpectedResponse { origin: origin.clone(), query_id };
4177 Self::deposit_event(e);
4178 Weight::zero()
4179 },
4180 }
4181 }
4182}
4183
4184impl<T: Config> CheckSuspension for Pezpallet<T> {
4185 fn is_suspended<Call>(
4186 _origin: &Location,
4187 _instructions: &mut [Instruction<Call>],
4188 _max_weight: Weight,
4189 _properties: &mut Properties,
4190 ) -> bool {
4191 XcmExecutionSuspended::<T>::get()
4192 }
4193}
4194
4195impl<T: Config> RecordXcm for Pezpallet<T> {
4196 fn should_record() -> bool {
4197 ShouldRecordXcm::<T>::get()
4198 }
4199
4200 fn set_record_xcm(enabled: bool) {
4201 ShouldRecordXcm::<T>::put(enabled);
4202 }
4203
4204 fn recorded_xcm() -> Option<Xcm<()>> {
4205 RecordedXcm::<T>::get()
4206 }
4207
4208 fn record(xcm: Xcm<()>) {
4209 RecordedXcm::<T>::put(xcm);
4210 }
4211}
4212
4213pub fn ensure_xcm<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4217where
4218 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4219{
4220 match o.into() {
4221 Ok(Origin::Xcm(location)) => Ok(location),
4222 _ => Err(BadOrigin),
4223 }
4224}
4225
4226pub fn ensure_response<OuterOrigin>(o: OuterOrigin) -> Result<Location, BadOrigin>
4230where
4231 OuterOrigin: Into<Result<Origin, OuterOrigin>>,
4232{
4233 match o.into() {
4234 Ok(Origin::Response(location)) => Ok(location),
4235 _ => Err(BadOrigin),
4236 }
4237}
4238
4239pub struct AuthorizedAliasers<T>(PhantomData<T>);
4245impl<L: Into<VersionedLocation> + Clone, T: Config> ContainsPair<L, L> for AuthorizedAliasers<T> {
4246 fn contains(origin: &L, target: &L) -> bool {
4247 let origin: VersionedLocation = origin.clone().into();
4248 let target: VersionedLocation = target.clone().into();
4249 tracing::trace!(target: "xcm::pezpallet_xcm::AuthorizedAliasers::contains", ?origin, ?target);
4250 Pezpallet::<T>::is_authorized_alias(origin, target).unwrap_or(false)
4253 }
4254}
4255
4256pub struct IsMajorityOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4261impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location>
4262 for IsMajorityOfBody<Prefix, Body>
4263{
4264 fn contains(l: &Location) -> bool {
4265 let maybe_suffix = l.match_and_split(&Prefix::get());
4266 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part.is_majority())
4267 }
4268}
4269
4270pub struct IsVoiceOfBody<Prefix, Body>(PhantomData<(Prefix, Body)>);
4274impl<Prefix: Get<Location>, Body: Get<BodyId>> Contains<Location> for IsVoiceOfBody<Prefix, Body> {
4275 fn contains(l: &Location) -> bool {
4276 let maybe_suffix = l.match_and_split(&Prefix::get());
4277 matches!(maybe_suffix, Some(Plurality { id, part }) if id == &Body::get() && part == &BodyPart::Voice)
4278 }
4279}
4280
4281pub struct EnsureXcm<F, L = Location>(PhantomData<(F, L)>);
4284impl<
4285 O: OriginTrait + From<Origin>,
4286 F: Contains<L>,
4287 L: TryFrom<Location> + TryInto<Location> + Clone,
4288 > EnsureOrigin<O> for EnsureXcm<F, L>
4289where
4290 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4291{
4292 type Success = L;
4293
4294 fn try_origin(outer: O) -> Result<Self::Success, O> {
4295 match outer.caller().try_into() {
4296 Ok(Origin::Xcm(ref location)) => {
4297 if let Ok(location) = location.clone().try_into() {
4298 if F::contains(&location) {
4299 return Ok(location);
4300 }
4301 }
4302 },
4303 _ => (),
4304 }
4305
4306 Err(outer)
4307 }
4308
4309 #[cfg(feature = "runtime-benchmarks")]
4310 fn try_successful_origin() -> Result<O, ()> {
4311 Ok(O::from(Origin::Xcm(Here.into())))
4312 }
4313}
4314
4315pub struct EnsureResponse<F>(PhantomData<F>);
4318impl<O: OriginTrait + From<Origin>, F: Contains<Location>> EnsureOrigin<O> for EnsureResponse<F>
4319where
4320 for<'a> &'a O::PalletsOrigin: TryInto<&'a Origin>,
4321{
4322 type Success = Location;
4323
4324 fn try_origin(outer: O) -> Result<Self::Success, O> {
4325 match outer.caller().try_into() {
4326 Ok(Origin::Response(responder)) => return Ok(responder.clone()),
4327 _ => (),
4328 }
4329
4330 Err(outer)
4331 }
4332
4333 #[cfg(feature = "runtime-benchmarks")]
4334 fn try_successful_origin() -> Result<O, ()> {
4335 Ok(O::from(Origin::Response(Here.into())))
4336 }
4337}
4338
4339pub struct XcmPassthrough<RuntimeOrigin>(PhantomData<RuntimeOrigin>);
4342impl<RuntimeOrigin: From<crate::Origin>> ConvertOrigin<RuntimeOrigin>
4343 for XcmPassthrough<RuntimeOrigin>
4344{
4345 fn convert_origin(
4346 origin: impl Into<Location>,
4347 kind: OriginKind,
4348 ) -> Result<RuntimeOrigin, Location> {
4349 let origin = origin.into();
4350 match kind {
4351 OriginKind::Xcm => Ok(crate::Origin::Xcm(origin).into()),
4352 _ => Err(origin),
4353 }
4354 }
4355}