staging_xcm/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Cross-Consensus Message format data structures.
18
19// NOTE, this crate is meant to be used in many different environments, notably wasm, but not
20// necessarily related to FRAME or even Substrate.
21//
22// Hence, `no_std` rather than sp-runtime.
23#![cfg_attr(not(feature = "std"), no_std)]
24// Because of XCMv2.
25#![allow(deprecated)]
26
27extern crate alloc;
28
29use codec::{Decode, DecodeLimit, Encode, Error as CodecError, Input, MaxEncodedLen};
30use derivative::Derivative;
31use scale_info::TypeInfo;
32
33#[deprecated(
34	note = "XCMv2 will be removed once XCMv5 is released. Please use XCMv3 or XCMv4 instead."
35)]
36pub mod v2;
37pub mod v3;
38pub mod v4;
39
40pub mod lts {
41	pub use super::v4::*;
42}
43
44pub mod latest {
45	pub use super::v4::*;
46}
47
48mod double_encoded;
49pub use double_encoded::DoubleEncoded;
50
51#[cfg(test)]
52mod tests;
53
54/// Maximum nesting level for XCM decoding.
55pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
56
57/// A version of XCM.
58pub type Version = u32;
59
60#[derive(Clone, Eq, PartialEq, Debug)]
61pub enum Unsupported {}
62impl Encode for Unsupported {}
63impl Decode for Unsupported {
64	fn decode<I: Input>(_: &mut I) -> Result<Self, CodecError> {
65		Err("Not decodable".into())
66	}
67}
68
69/// Attempt to convert `self` into a particular version of itself.
70pub trait IntoVersion: Sized {
71	/// Consume `self` and return same value expressed in some particular `version` of XCM.
72	fn into_version(self, version: Version) -> Result<Self, ()>;
73
74	/// Consume `self` and return same value expressed the latest version of XCM.
75	fn into_latest(self) -> Result<Self, ()> {
76		self.into_version(latest::VERSION)
77	}
78}
79
80pub trait TryAs<T> {
81	fn try_as(&self) -> Result<&T, ()>;
82}
83
84macro_rules! versioned_type {
85	($(#[$attr:meta])* pub enum $n:ident {
86		$(#[$index3:meta])+
87		V3($v3:ty),
88		$(#[$index4:meta])+
89		V4($v4:ty),
90	}) => {
91		#[derive(Derivative, Encode, Decode, TypeInfo)]
92		#[derivative(
93			Clone(bound = ""),
94			Eq(bound = ""),
95			PartialEq(bound = ""),
96			Debug(bound = "")
97		)]
98		#[codec(encode_bound())]
99		#[codec(decode_bound())]
100		#[scale_info(replace_segment("staging_xcm", "xcm"))]
101		$(#[$attr])*
102		pub enum $n {
103			$(#[$index3])*
104			V3($v3),
105			$(#[$index4])*
106			V4($v4),
107		}
108		impl $n {
109			pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
110				<Self as TryAs<T>>::try_as(&self)
111			}
112		}
113		impl TryAs<$v3> for $n {
114			fn try_as(&self) -> Result<&$v3, ()> {
115				match &self {
116					Self::V3(ref x) => Ok(x),
117					_ => Err(()),
118				}
119			}
120		}
121		impl TryAs<$v4> for $n {
122			fn try_as(&self) -> Result<&$v4, ()> {
123				match &self {
124					Self::V4(ref x) => Ok(x),
125					_ => Err(()),
126				}
127			}
128		}
129		impl IntoVersion for $n {
130			fn into_version(self, n: Version) -> Result<Self, ()> {
131				Ok(match n {
132					3 => Self::V3(self.try_into()?),
133					4 => Self::V4(self.try_into()?),
134					_ => return Err(()),
135				})
136			}
137		}
138		impl From<$v3> for $n {
139			fn from(x: $v3) -> Self {
140				$n::V3(x.into())
141			}
142		}
143		impl From<$v4> for $n {
144			fn from(x: $v4) -> Self {
145				$n::V4(x.into())
146			}
147		}
148		impl TryFrom<$n> for $v3 {
149			type Error = ();
150			fn try_from(x: $n) -> Result<Self, ()> {
151				use $n::*;
152				match x {
153					V3(x) => Ok(x),
154					V4(x) => x.try_into(),
155				}
156			}
157		}
158		impl TryFrom<$n> for $v4 {
159			type Error = ();
160			fn try_from(x: $n) -> Result<Self, ()> {
161				use $n::*;
162				match x {
163					V3(x) => x.try_into().map_err(|_| ()),
164					V4(x) => Ok(x),
165				}
166			}
167		}
168		impl MaxEncodedLen for $n {
169			fn max_encoded_len() -> usize {
170				<$v3>::max_encoded_len()
171			}
172		}
173		impl IdentifyVersion for $n {
174			fn identify_version(&self) -> Version {
175				use $n::*;
176				match self {
177					V3(_) => v3::VERSION,
178					V4(_) => v4::VERSION,
179				}
180			}
181		}
182	};
183
184	($(#[$attr:meta])* pub enum $n:ident {
185		$(#[$index2:meta])+
186		V2($v2:ty),
187		$(#[$index3:meta])+
188		V3($v3:ty),
189		$(#[$index4:meta])+
190		V4($v4:ty),
191	}) => {
192		#[derive(Derivative, Encode, Decode, TypeInfo)]
193		#[derivative(
194			Clone(bound = ""),
195			Eq(bound = ""),
196			PartialEq(bound = ""),
197			Debug(bound = "")
198		)]
199		#[codec(encode_bound())]
200		#[codec(decode_bound())]
201		#[scale_info(replace_segment("staging_xcm", "xcm"))]
202		$(#[$attr])*
203		pub enum $n {
204			$(#[$index2])*
205			V2($v2),
206			$(#[$index3])*
207			V3($v3),
208			$(#[$index4])*
209			V4($v4),
210		}
211		impl $n {
212			pub fn try_as<T>(&self) -> Result<&T, ()> where Self: TryAs<T> {
213				<Self as TryAs<T>>::try_as(&self)
214			}
215		}
216		impl TryAs<$v2> for $n {
217			fn try_as(&self) -> Result<&$v2, ()> {
218				match &self {
219					Self::V2(ref x) => Ok(x),
220					_ => Err(()),
221				}
222			}
223		}
224		impl TryAs<$v3> for $n {
225			fn try_as(&self) -> Result<&$v3, ()> {
226				match &self {
227					Self::V3(ref x) => Ok(x),
228					_ => Err(()),
229				}
230			}
231		}
232		impl TryAs<$v4> for $n {
233			fn try_as(&self) -> Result<&$v4, ()> {
234				match &self {
235					Self::V4(ref x) => Ok(x),
236					_ => Err(()),
237				}
238			}
239		}
240		impl IntoVersion for $n {
241			fn into_version(self, n: Version) -> Result<Self, ()> {
242				Ok(match n {
243					1 | 2 => Self::V2(self.try_into()?),
244					3 => Self::V3(self.try_into()?),
245					4 => Self::V4(self.try_into()?),
246					_ => return Err(()),
247				})
248			}
249		}
250		impl From<$v2> for $n {
251			fn from(x: $v2) -> Self {
252				$n::V2(x)
253			}
254		}
255		impl<T: Into<$v4>> From<T> for $n {
256			fn from(x: T) -> Self {
257				$n::V4(x.into())
258			}
259		}
260		impl TryFrom<$n> for $v2 {
261			type Error = ();
262			fn try_from(x: $n) -> Result<Self, ()> {
263				use $n::*;
264				match x {
265					V2(x) => Ok(x),
266					V3(x) => x.try_into(),
267					V4(x) => {
268						let v3: $v3 = x.try_into().map_err(|_| ())?;
269						v3.try_into()
270					},
271				}
272			}
273		}
274		impl TryFrom<$n> for $v3 {
275			type Error = ();
276			fn try_from(x: $n) -> Result<Self, ()> {
277				use $n::*;
278				match x {
279					V2(x) => x.try_into(),
280					V3(x) => Ok(x),
281					V4(x) => x.try_into().map_err(|_| ()),
282				}
283			}
284		}
285		impl TryFrom<$n> for $v4 {
286			type Error = ();
287			fn try_from(x: $n) -> Result<Self, ()> {
288				use $n::*;
289				match x {
290					V2(x) => {
291						let v3: $v3 = x.try_into().map_err(|_| ())?;
292						v3.try_into().map_err(|_| ())
293					},
294					V3(x) => x.try_into().map_err(|_| ()),
295					V4(x) => Ok(x),
296				}
297			}
298		}
299		impl MaxEncodedLen for $n {
300			fn max_encoded_len() -> usize {
301				<$v3>::max_encoded_len()
302			}
303		}
304		impl IdentifyVersion for $n {
305			fn identify_version(&self) -> Version {
306				use $n::*;
307				match self {
308					V2(_) => v2::VERSION,
309					V3(_) => v3::VERSION,
310					V4(_) => v4::VERSION,
311				}
312			}
313		}
314	};
315}
316
317versioned_type! {
318	/// A single version's `AssetId` value, together with its version code.
319	pub enum VersionedAssetId {
320		#[codec(index = 3)]
321		V3(v3::AssetId),
322		#[codec(index = 4)]
323		V4(v4::AssetId),
324	}
325}
326
327versioned_type! {
328	/// A single version's `Response` value, together with its version code.
329	pub enum VersionedResponse {
330		#[codec(index = 2)]
331		V2(v2::Response),
332		#[codec(index = 3)]
333		V3(v3::Response),
334		#[codec(index = 4)]
335		V4(v4::Response),
336	}
337}
338
339versioned_type! {
340	/// A single `NetworkId` value, together with its version code.
341	pub enum VersionedNetworkId {
342		#[codec(index = 2)]
343		V2(v2::NetworkId),
344		#[codec(index = 3)]
345		V3(v3::NetworkId),
346		#[codec(index = 4)]
347		V4(v4::NetworkId),
348	}
349}
350
351versioned_type! {
352	/// A single `Junction` value, together with its version code.
353	pub enum VersionedJunction {
354		#[codec(index = 2)]
355		V2(v2::Junction),
356		#[codec(index = 3)]
357		V3(v3::Junction),
358		#[codec(index = 4)]
359		V4(v4::Junction),
360	}
361}
362
363versioned_type! {
364	/// A single `Location` value, together with its version code.
365	#[derive(Ord, PartialOrd)]
366	pub enum VersionedLocation {
367		#[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index
368		V2(v2::MultiLocation),
369		#[codec(index = 3)]
370		V3(v3::MultiLocation),
371		#[codec(index = 4)]
372		V4(v4::Location),
373	}
374}
375
376#[deprecated(note = "Use `VersionedLocation` instead")]
377pub type VersionedMultiLocation = VersionedLocation;
378
379versioned_type! {
380	/// A single `InteriorLocation` value, together with its version code.
381	pub enum VersionedInteriorLocation {
382		#[codec(index = 2)] // while this is same as v1::Junctions, VersionedInteriorLocation is introduced in v3
383		V2(v2::InteriorMultiLocation),
384		#[codec(index = 3)]
385		V3(v3::InteriorMultiLocation),
386		#[codec(index = 4)]
387		V4(v4::InteriorLocation),
388	}
389}
390
391#[deprecated(note = "Use `VersionedInteriorLocation` instead")]
392pub type VersionedInteriorMultiLocation = VersionedInteriorLocation;
393
394versioned_type! {
395	/// A single `Asset` value, together with its version code.
396	pub enum VersionedAsset {
397		#[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index
398		V2(v2::MultiAsset),
399		#[codec(index = 3)]
400		V3(v3::MultiAsset),
401		#[codec(index = 4)]
402		V4(v4::Asset),
403	}
404}
405
406#[deprecated(note = "Use `VersionedAsset` instead")]
407pub type VersionedMultiAsset = VersionedAsset;
408
409versioned_type! {
410	/// A single `MultiAssets` value, together with its version code.
411	pub enum VersionedAssets {
412		#[codec(index = 1)] // v2 is same as v1 and therefore re-using the v1 index
413		V2(v2::MultiAssets),
414		#[codec(index = 3)]
415		V3(v3::MultiAssets),
416		#[codec(index = 4)]
417		V4(v4::Assets),
418	}
419}
420
421#[deprecated(note = "Use `VersionedAssets` instead")]
422pub type VersionedMultiAssets = VersionedAssets;
423
424/// A single XCM message, together with its version code.
425#[derive(Derivative, Encode, Decode, TypeInfo)]
426#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
427#[codec(encode_bound())]
428#[codec(decode_bound())]
429#[scale_info(bounds(), skip_type_params(RuntimeCall))]
430#[scale_info(replace_segment("staging_xcm", "xcm"))]
431pub enum VersionedXcm<RuntimeCall> {
432	#[codec(index = 2)]
433	#[deprecated]
434	V2(v2::Xcm<RuntimeCall>),
435	#[codec(index = 3)]
436	V3(v3::Xcm<RuntimeCall>),
437	#[codec(index = 4)]
438	V4(v4::Xcm<RuntimeCall>),
439}
440
441impl<C> IntoVersion for VersionedXcm<C> {
442	fn into_version(self, n: Version) -> Result<Self, ()> {
443		Ok(match n {
444			2 => Self::V2(self.try_into()?),
445			3 => Self::V3(self.try_into()?),
446			4 => Self::V4(self.try_into()?),
447			_ => return Err(()),
448		})
449	}
450}
451
452impl<C> IdentifyVersion for VersionedXcm<C> {
453	fn identify_version(&self) -> Version {
454		match self {
455			Self::V2(_) => v2::VERSION,
456			Self::V3(_) => v3::VERSION,
457			Self::V4(_) => v4::VERSION,
458		}
459	}
460}
461
462impl<C> VersionedXcm<C> {
463	/// Checks that the XCM is decodable with `MAX_XCM_DECODE_DEPTH`. Consequently, it also checks
464	/// all decode implementations and limits, such as MAX_ITEMS_IN_ASSETS or
465	/// MAX_INSTRUCTIONS_TO_DECODE.
466	///
467	/// Note that this uses the limit of the sender - not the receiver. It is a best effort.
468	pub fn validate_xcm_nesting(&self) -> Result<(), ()> {
469		self.using_encoded(|mut enc| {
470			Self::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut enc).map(|_| ())
471		})
472		.map_err(|e| {
473			log::error!(target: "xcm::validate_xcm_nesting", "Decode error: {e:?} for xcm: {self:?}!");
474			()
475		})
476	}
477}
478
479impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
480	fn from(x: v2::Xcm<RuntimeCall>) -> Self {
481		VersionedXcm::V2(x)
482	}
483}
484
485impl<RuntimeCall> From<v3::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
486	fn from(x: v3::Xcm<RuntimeCall>) -> Self {
487		VersionedXcm::V3(x)
488	}
489}
490
491impl<RuntimeCall> From<v4::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
492	fn from(x: v4::Xcm<RuntimeCall>) -> Self {
493		VersionedXcm::V4(x)
494	}
495}
496
497impl<RuntimeCall> TryFrom<VersionedXcm<RuntimeCall>> for v2::Xcm<RuntimeCall> {
498	type Error = ();
499	fn try_from(x: VersionedXcm<RuntimeCall>) -> Result<Self, ()> {
500		use VersionedXcm::*;
501		match x {
502			V2(x) => Ok(x),
503			V3(x) => x.try_into(),
504			V4(x) => {
505				let v3: v3::Xcm<RuntimeCall> = x.try_into()?;
506				v3.try_into()
507			},
508		}
509	}
510}
511
512impl<Call> TryFrom<VersionedXcm<Call>> for v3::Xcm<Call> {
513	type Error = ();
514	fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
515		use VersionedXcm::*;
516		match x {
517			V2(x) => x.try_into(),
518			V3(x) => Ok(x),
519			V4(x) => x.try_into(),
520		}
521	}
522}
523
524impl<Call> TryFrom<VersionedXcm<Call>> for v4::Xcm<Call> {
525	type Error = ();
526	fn try_from(x: VersionedXcm<Call>) -> Result<Self, ()> {
527		use VersionedXcm::*;
528		match x {
529			V2(x) => {
530				let v3: v3::Xcm<Call> = x.try_into()?;
531				v3.try_into()
532			},
533			V3(x) => x.try_into(),
534			V4(x) => Ok(x),
535		}
536	}
537}
538
539/// Convert an `Xcm` datum into a `VersionedXcm`, based on a destination `Location` which will
540/// interpret it.
541pub trait WrapVersion {
542	fn wrap_version<RuntimeCall>(
543		dest: &latest::Location,
544		xcm: impl Into<VersionedXcm<RuntimeCall>>,
545	) -> Result<VersionedXcm<RuntimeCall>, ()>;
546}
547
548/// Used to get the version out of a versioned type.
549// TODO(XCMv5): This could be `GetVersion` and we change the current one to `GetVersionFor`.
550pub trait IdentifyVersion {
551	fn identify_version(&self) -> Version;
552}
553
554/// Check and return the `Version` that should be used for the `Xcm` datum for the destination
555/// `Location`, which will interpret it.
556pub trait GetVersion {
557	fn get_version_for(dest: &latest::Location) -> Option<Version>;
558}
559
560/// `()` implementation does nothing with the XCM, just sending with whatever version it was
561/// authored as.
562impl WrapVersion for () {
563	fn wrap_version<RuntimeCall>(
564		_: &latest::Location,
565		xcm: impl Into<VersionedXcm<RuntimeCall>>,
566	) -> Result<VersionedXcm<RuntimeCall>, ()> {
567		Ok(xcm.into())
568	}
569}
570
571/// `WrapVersion` implementation which attempts to always convert the XCM to version 2 before
572/// wrapping it.
573pub struct AlwaysV2;
574impl WrapVersion for AlwaysV2 {
575	fn wrap_version<RuntimeCall>(
576		_: &latest::Location,
577		xcm: impl Into<VersionedXcm<RuntimeCall>>,
578	) -> Result<VersionedXcm<RuntimeCall>, ()> {
579		Ok(VersionedXcm::<RuntimeCall>::V2(xcm.into().try_into()?))
580	}
581}
582impl GetVersion for AlwaysV2 {
583	fn get_version_for(_dest: &latest::Location) -> Option<Version> {
584		Some(v2::VERSION)
585	}
586}
587
588/// `WrapVersion` implementation which attempts to always convert the XCM to version 3 before
589/// wrapping it.
590pub struct AlwaysV3;
591impl WrapVersion for AlwaysV3 {
592	fn wrap_version<Call>(
593		_: &latest::Location,
594		xcm: impl Into<VersionedXcm<Call>>,
595	) -> Result<VersionedXcm<Call>, ()> {
596		Ok(VersionedXcm::<Call>::V3(xcm.into().try_into()?))
597	}
598}
599impl GetVersion for AlwaysV3 {
600	fn get_version_for(_dest: &latest::Location) -> Option<Version> {
601		Some(v3::VERSION)
602	}
603}
604
605/// `WrapVersion` implementation which attempts to always convert the XCM to version 3 before
606/// wrapping it.
607pub struct AlwaysV4;
608impl WrapVersion for AlwaysV4 {
609	fn wrap_version<Call>(
610		_: &latest::Location,
611		xcm: impl Into<VersionedXcm<Call>>,
612	) -> Result<VersionedXcm<Call>, ()> {
613		Ok(VersionedXcm::<Call>::V4(xcm.into().try_into()?))
614	}
615}
616impl GetVersion for AlwaysV4 {
617	fn get_version_for(_dest: &latest::Location) -> Option<Version> {
618		Some(v4::VERSION)
619	}
620}
621
622/// `WrapVersion` implementation which attempts to always convert the XCM to the latest version
623/// before wrapping it.
624pub type AlwaysLatest = AlwaysV4;
625
626/// `WrapVersion` implementation which attempts to always convert the XCM to the most recent Long-
627/// Term-Support version before wrapping it.
628pub type AlwaysLts = AlwaysV4;
629
630pub mod prelude {
631	pub use super::{
632		latest::prelude::*, AlwaysLatest, AlwaysLts, AlwaysV2, AlwaysV3, AlwaysV4, GetVersion,
633		IdentifyVersion, IntoVersion, Unsupported, Version as XcmVersion, VersionedAsset,
634		VersionedAssetId, VersionedAssets, VersionedInteriorLocation, VersionedLocation,
635		VersionedResponse, VersionedXcm, WrapVersion,
636	};
637}
638
639pub mod opaque {
640	pub mod v2 {
641		// Everything from v2
642		pub use crate::v2::*;
643		// Then override with the opaque types in v2
644		pub use crate::v2::opaque::{Instruction, Xcm};
645	}
646	pub mod v3 {
647		// Everything from v3
648		pub use crate::v3::*;
649		// Then override with the opaque types in v3
650		pub use crate::v3::opaque::{Instruction, Xcm};
651	}
652	pub mod v4 {
653		// Everything from v4
654		pub use crate::v4::*;
655		// Then override with the opaque types in v4
656		pub use crate::v4::opaque::{Instruction, Xcm};
657	}
658
659	pub mod latest {
660		pub use super::v4::*;
661	}
662
663	pub mod lts {
664		pub use super::v4::*;
665	}
666
667	/// The basic `VersionedXcm` type which just uses the `Vec<u8>` as an encoded call.
668	pub type VersionedXcm = super::VersionedXcm<()>;
669}
670
671#[test]
672fn conversion_works() {
673	use latest::prelude::*;
674	let assets: Assets = (Here, 1u128).into();
675	let _: VersionedAssets = assets.into();
676}
677
678#[test]
679fn size_limits() {
680	extern crate std;
681
682	let mut test_failed = false;
683	macro_rules! check_sizes {
684        ($(($kind:ty, $expected:expr),)+) => {
685            $({
686                let s = core::mem::size_of::<$kind>();
687                // Since the types often affect the size of other types in which they're included
688                // it is more convenient to check multiple types at the same time and only fail
689                // the test at the end. For debugging it's also useful to print out all of the sizes,
690                // even if they're within the expected range.
691                if s > $expected {
692                    test_failed = true;
693                    std::eprintln!(
694                        "assertion failed: size of '{}' is {} (which is more than the expected {})",
695                        stringify!($kind),
696                        s,
697                        $expected
698                    );
699                } else {
700                    std::println!(
701                        "type '{}' is of size {} which is within the expected {}",
702                        stringify!($kind),
703                        s,
704                        $expected
705                    );
706                }
707            })+
708        }
709    }
710
711	check_sizes! {
712		(crate::latest::Instruction<()>, 112),
713		(crate::latest::Asset, 80),
714		(crate::latest::Location, 24),
715		(crate::latest::AssetId, 40),
716		(crate::latest::Junctions, 16),
717		(crate::latest::Junction, 88),
718		(crate::latest::Response, 40),
719		(crate::latest::AssetInstance, 48),
720		(crate::latest::NetworkId, 48),
721		(crate::latest::BodyId, 32),
722		(crate::latest::Assets, 24),
723		(crate::latest::BodyPart, 12),
724	}
725	assert!(!test_failed);
726}
727
728#[test]
729fn validate_xcm_nesting_works() {
730	use crate::latest::{
731		prelude::{GeneralIndex, ReserveAssetDeposited, SetAppendix},
732		Assets, Xcm, MAX_INSTRUCTIONS_TO_DECODE, MAX_ITEMS_IN_ASSETS,
733	};
734
735	// closure generates assets of `count`
736	let assets = |count| {
737		let mut assets = Assets::new();
738		for i in 0..count {
739			assets.push((GeneralIndex(i as u128), 100).into());
740		}
741		assets
742	};
743
744	// closer generates `Xcm` with nested instructions of `depth`
745	let with_instr = |depth| {
746		let mut xcm = Xcm::<()>(vec![]);
747		for _ in 0..depth - 1 {
748			xcm = Xcm::<()>(vec![SetAppendix(xcm)]);
749		}
750		xcm
751	};
752
753	// `MAX_INSTRUCTIONS_TO_DECODE` check
754	assert!(VersionedXcm::<()>::from(Xcm(vec![
755		ReserveAssetDeposited(assets(1));
756		(MAX_INSTRUCTIONS_TO_DECODE - 1) as usize
757	]))
758	.validate_xcm_nesting()
759	.is_ok());
760	assert!(VersionedXcm::<()>::from(Xcm(vec![
761		ReserveAssetDeposited(assets(1));
762		MAX_INSTRUCTIONS_TO_DECODE as usize
763	]))
764	.validate_xcm_nesting()
765	.is_ok());
766	assert!(VersionedXcm::<()>::from(Xcm(vec![
767		ReserveAssetDeposited(assets(1));
768		(MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
769	]))
770	.validate_xcm_nesting()
771	.is_err());
772
773	// `MAX_XCM_DECODE_DEPTH` check
774	assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH - 1))
775		.validate_xcm_nesting()
776		.is_ok());
777	assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH))
778		.validate_xcm_nesting()
779		.is_ok());
780	assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH + 1))
781		.validate_xcm_nesting()
782		.is_err());
783
784	// `MAX_ITEMS_IN_ASSETS` check
785	assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
786		MAX_ITEMS_IN_ASSETS
787	))]))
788	.validate_xcm_nesting()
789	.is_ok());
790	assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
791		MAX_ITEMS_IN_ASSETS - 1
792	))]))
793	.validate_xcm_nesting()
794	.is_ok());
795	assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
796		MAX_ITEMS_IN_ASSETS + 1
797	))]))
798	.validate_xcm_nesting()
799	.is_err());
800}