referrerpolicy=no-referrer-when-downgrade

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