1pub use super::v3::GetWeight;
20use super::v3::{
21 Instruction as OldInstruction, PalletInfo as OldPalletInfo,
22 QueryResponseInfo as OldQueryResponseInfo, Response as OldResponse, Xcm as OldXcm,
23};
24use crate::DoubleEncoded;
25use alloc::{vec, vec::Vec};
26use bounded_collections::{parameter_types, BoundedVec};
27use codec::{
28 self, decode_vec_with_len, Compact, Decode, Encode, Error as CodecError, Input as CodecInput,
29 MaxEncodedLen,
30};
31use core::{fmt::Debug, result};
32use derivative::Derivative;
33use scale_info::TypeInfo;
34
35mod asset;
36mod junction;
37pub(crate) mod junctions;
38mod location;
39mod traits;
40
41pub use asset::{
42 Asset, AssetFilter, AssetId, AssetInstance, Assets, Fungibility, WildAsset, WildFungibility,
43 MAX_ITEMS_IN_ASSETS,
44};
45pub use junction::{BodyId, BodyPart, Junction, NetworkId};
46pub use junctions::Junctions;
47pub use location::{Ancestor, AncestorThen, InteriorLocation, Location, Parent, ParentThen};
48pub use traits::{
49 send_xcm, validate_send, Error, ExecuteXcm, Outcome, PreparedMessage, Reanchorable, Result,
50 SendError, SendResult, SendXcm, Weight, XcmHash,
51};
52pub use super::v3::{MaybeErrorCode, OriginKind, WeightLimit};
54
55pub const VERSION: super::Version = 4;
57
58pub type QueryId = u64;
60
61#[derive(Derivative, Default, Encode, TypeInfo)]
62#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
63#[codec(encode_bound())]
64#[codec(decode_bound())]
65#[scale_info(bounds(), skip_type_params(Call))]
66pub struct Xcm<Call>(pub Vec<Instruction<Call>>);
67
68pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100;
69
70environmental::environmental!(instructions_count: u8);
71
72impl<Call> Decode for Xcm<Call> {
73 fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
74 instructions_count::using_once(&mut 0, || {
75 let number_of_instructions: u32 = <Compact<u32>>::decode(input)?.into();
76 instructions_count::with(|count| {
77 *count = count.saturating_add(number_of_instructions as u8);
78 if *count > MAX_INSTRUCTIONS_TO_DECODE {
79 return Err(CodecError::from("Max instructions exceeded"))
80 }
81 Ok(())
82 })
83 .expect("Called in `using` context and thus can not return `None`; qed")?;
84 let decoded_instructions = decode_vec_with_len(input, number_of_instructions as usize)?;
85 Ok(Self(decoded_instructions))
86 })
87 }
88}
89
90impl<Call> Xcm<Call> {
91 pub fn new() -> Self {
93 Self(vec![])
94 }
95
96 pub fn is_empty(&self) -> bool {
98 self.0.is_empty()
99 }
100
101 pub fn len(&self) -> usize {
103 self.0.len()
104 }
105
106 pub fn inner(&self) -> &[Instruction<Call>] {
108 &self.0
109 }
110
111 pub fn inner_mut(&mut self) -> &mut Vec<Instruction<Call>> {
113 &mut self.0
114 }
115
116 pub fn into_inner(self) -> Vec<Instruction<Call>> {
118 self.0
119 }
120
121 pub fn iter(&self) -> impl Iterator<Item = &Instruction<Call>> {
123 self.0.iter()
124 }
125
126 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Instruction<Call>> {
128 self.0.iter_mut()
129 }
130
131 pub fn into_iter(self) -> impl Iterator<Item = Instruction<Call>> {
133 self.0.into_iter()
134 }
135
136 pub fn or_else(self, f: impl FnOnce() -> Self) -> Self {
139 if self.0.is_empty() {
140 f()
141 } else {
142 self
143 }
144 }
145
146 pub fn first(&self) -> Option<&Instruction<Call>> {
148 self.0.first()
149 }
150
151 pub fn last(&self) -> Option<&Instruction<Call>> {
153 self.0.last()
154 }
155
156 pub fn only(&self) -> Option<&Instruction<Call>> {
158 if self.0.len() == 1 {
159 self.0.first()
160 } else {
161 None
162 }
163 }
164
165 pub fn into_only(mut self) -> core::result::Result<Instruction<Call>, Self> {
168 if self.0.len() == 1 {
169 self.0.pop().ok_or(self)
170 } else {
171 Err(self)
172 }
173 }
174}
175
176impl<Call> From<Vec<Instruction<Call>>> for Xcm<Call> {
177 fn from(c: Vec<Instruction<Call>>) -> Self {
178 Self(c)
179 }
180}
181
182impl<Call> From<Xcm<Call>> for Vec<Instruction<Call>> {
183 fn from(c: Xcm<Call>) -> Self {
184 c.0
185 }
186}
187
188pub mod prelude {
190 mod contents {
191 pub use super::super::{
192 send_xcm, validate_send, Ancestor, AncestorThen, Asset,
193 AssetFilter::{self, *},
194 AssetId,
195 AssetInstance::{self, *},
196 Assets, BodyId, BodyPart, Error as XcmError, ExecuteXcm,
197 Fungibility::{self, *},
198 Instruction::*,
199 InteriorLocation,
200 Junction::{self, *},
201 Junctions::{self, Here},
202 Location, MaybeErrorCode,
203 NetworkId::{self, *},
204 OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId,
205 QueryResponseInfo, Reanchorable, Response, Result as XcmResult, SendError, SendResult,
206 SendXcm, Weight,
207 WeightLimit::{self, *},
208 WildAsset::{self, *},
209 WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
210 XcmContext, XcmHash, XcmWeightInfo, VERSION as XCM_VERSION,
211 };
212 }
213 pub use super::{Instruction, Xcm};
214 pub use contents::*;
215 pub mod opaque {
216 pub use super::{
217 super::opaque::{Instruction, Xcm},
218 contents::*,
219 };
220 }
221}
222
223parameter_types! {
224 pub MaxPalletNameLen: u32 = 48;
225 pub MaxDispatchErrorLen: u32 = 128;
228 pub MaxPalletsInfo: u32 = 64;
229}
230
231#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
232pub struct PalletInfo {
233 #[codec(compact)]
234 pub index: u32,
235 pub name: BoundedVec<u8, MaxPalletNameLen>,
236 pub module_name: BoundedVec<u8, MaxPalletNameLen>,
237 #[codec(compact)]
238 pub major: u32,
239 #[codec(compact)]
240 pub minor: u32,
241 #[codec(compact)]
242 pub patch: u32,
243}
244
245impl TryInto<OldPalletInfo> for PalletInfo {
246 type Error = ();
247
248 fn try_into(self) -> result::Result<OldPalletInfo, Self::Error> {
249 OldPalletInfo::new(
250 self.index,
251 self.name.into_inner(),
252 self.module_name.into_inner(),
253 self.major,
254 self.minor,
255 self.patch,
256 )
257 .map_err(|_| ())
258 }
259}
260
261impl PalletInfo {
262 pub fn new(
263 index: u32,
264 name: Vec<u8>,
265 module_name: Vec<u8>,
266 major: u32,
267 minor: u32,
268 patch: u32,
269 ) -> result::Result<Self, Error> {
270 let name = BoundedVec::try_from(name).map_err(|_| Error::Overflow)?;
271 let module_name = BoundedVec::try_from(module_name).map_err(|_| Error::Overflow)?;
272
273 Ok(Self { index, name, module_name, major, minor, patch })
274 }
275}
276
277#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
279pub enum Response {
280 Null,
282 Assets(Assets),
284 ExecutionResult(Option<(u32, Error)>),
286 Version(super::Version),
288 PalletsInfo(BoundedVec<PalletInfo, MaxPalletsInfo>),
290 DispatchResult(MaybeErrorCode),
292}
293
294impl Default for Response {
295 fn default() -> Self {
296 Self::Null
297 }
298}
299
300impl TryFrom<OldResponse> for Response {
301 type Error = ();
302
303 fn try_from(old: OldResponse) -> result::Result<Self, Self::Error> {
304 use OldResponse::*;
305 Ok(match old {
306 Null => Self::Null,
307 Assets(assets) => Self::Assets(assets.try_into()?),
308 ExecutionResult(result) =>
309 Self::ExecutionResult(result.map(|(num, old_error)| (num, old_error.into()))),
310 Version(version) => Self::Version(version),
311 PalletsInfo(pallet_info) => {
312 let inner = pallet_info
313 .into_iter()
314 .map(TryInto::try_into)
315 .collect::<result::Result<Vec<_>, _>>()?;
316 Self::PalletsInfo(
317 BoundedVec::<PalletInfo, MaxPalletsInfo>::try_from(inner).map_err(|_| ())?,
318 )
319 },
320 DispatchResult(maybe_error) => Self::DispatchResult(maybe_error),
321 })
322 }
323}
324
325#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
327pub struct QueryResponseInfo {
328 pub destination: Location,
330 #[codec(compact)]
332 pub query_id: QueryId,
333 pub max_weight: Weight,
335}
336
337impl TryFrom<OldQueryResponseInfo> for QueryResponseInfo {
338 type Error = ();
339
340 fn try_from(old: OldQueryResponseInfo) -> result::Result<Self, Self::Error> {
341 Ok(Self {
342 destination: old.destination.try_into()?,
343 query_id: old.query_id,
344 max_weight: old.max_weight,
345 })
346 }
347}
348
349#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
351pub struct XcmContext {
352 pub origin: Option<Location>,
354 pub message_id: XcmHash,
357 pub topic: Option<[u8; 32]>,
359}
360
361impl XcmContext {
362 pub fn with_message_id(message_id: XcmHash) -> XcmContext {
365 XcmContext { origin: None, message_id, topic: None }
366 }
367}
368
369#[derive(
378 Derivative,
379 Encode,
380 Decode,
381 TypeInfo,
382 xcm_procedural::XcmWeightInfoTrait,
383 xcm_procedural::Builder,
384)]
385#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))]
386#[codec(encode_bound())]
387#[codec(decode_bound())]
388#[scale_info(bounds(), skip_type_params(Call))]
389pub enum Instruction<Call> {
390 #[builder(loads_holding)]
399 WithdrawAsset(Assets),
400
401 #[builder(loads_holding)]
413 ReserveAssetDeposited(Assets),
414
415 #[builder(loads_holding)]
427 ReceiveTeleportedAsset(Assets),
428
429 QueryResponse {
447 #[codec(compact)]
448 query_id: QueryId,
449 response: Response,
450 max_weight: Weight,
451 querier: Option<Location>,
452 },
453
454 TransferAsset { assets: Assets, beneficiary: Location },
466
467 TransferReserveAsset { assets: Assets, dest: Location, xcm: Xcm<()> },
486
487 Transact { origin_kind: OriginKind, require_weight_at_most: Weight, call: DoubleEncoded<Call> },
503
504 HrmpNewChannelOpenRequest {
516 #[codec(compact)]
517 sender: u32,
518 #[codec(compact)]
519 max_message_size: u32,
520 #[codec(compact)]
521 max_capacity: u32,
522 },
523
524 HrmpChannelAccepted {
534 #[codec(compact)]
537 recipient: u32,
538 },
539
540 HrmpChannelClosing {
551 #[codec(compact)]
552 initiator: u32,
553 #[codec(compact)]
554 sender: u32,
555 #[codec(compact)]
556 recipient: u32,
557 },
558
559 ClearOrigin,
571
572 DescendOrigin(InteriorLocation),
578
579 ReportError(QueryResponseInfo),
589
590 DepositAsset { assets: AssetFilter, beneficiary: Location },
600
601 DepositReserveAsset { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
618
619 ExchangeAsset { give: AssetFilter, want: Assets, maximal: bool },
635
636 InitiateReserveWithdraw { assets: AssetFilter, reserve: Location, xcm: Xcm<()> },
651
652 InitiateTeleport { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
667
668 ReportHolding { response_info: QueryResponseInfo, assets: AssetFilter },
681
682 BuyExecution { fees: Asset, weight_limit: WeightLimit },
694
695 RefundSurplus,
701
702 SetErrorHandler(Xcm<Call>),
717
718 SetAppendix(Xcm<Call>),
733
734 ClearError,
740
741 #[builder(loads_holding)]
752 ClaimAsset { assets: Assets, ticket: Location },
753
754 Trap(#[codec(compact)] u64),
761
762 SubscribeVersion {
775 #[codec(compact)]
776 query_id: QueryId,
777 max_response_weight: Weight,
778 },
779
780 UnsubscribeVersion,
786
787 BurnAsset(Assets),
797
798 ExpectAsset(Assets),
805
806 ExpectOrigin(Option<Location>),
813
814 ExpectError(Option<(u32, Error)>),
821
822 ExpectTransactStatus(MaybeErrorCode),
831
832 QueryPallet { module_name: Vec<u8>, response_info: QueryResponseInfo },
847
848 ExpectPallet {
867 #[codec(compact)]
868 index: u32,
869 name: Vec<u8>,
870 module_name: Vec<u8>,
871 #[codec(compact)]
872 crate_major: u32,
873 #[codec(compact)]
874 min_crate_minor: u32,
875 },
876
877 ReportTransactStatus(QueryResponseInfo),
889
890 ClearTransactStatus,
898
899 UniversalOrigin(Junction),
913
914 ExportMessage { network: NetworkId, destination: InteriorLocation, xcm: Xcm<()> },
934
935 LockAsset { asset: Asset, unlocker: Location },
950
951 UnlockAsset { asset: Asset, target: Location },
963
964 NoteUnlockable { asset: Asset, owner: Location },
978
979 RequestUnlock { asset: Asset, locker: Location },
992
993 SetFeesMode { jit_withdraw: bool },
1002
1003 SetTopic([u8; 32]),
1015
1016 ClearTopic,
1022
1023 AliasOrigin(Location),
1029
1030 UnpaidExecution { weight_limit: WeightLimit, check_origin: Option<Location> },
1041}
1042
1043impl<Call> Xcm<Call> {
1044 pub fn into<C>(self) -> Xcm<C> {
1045 Xcm::from(self)
1046 }
1047 pub fn from<C>(xcm: Xcm<C>) -> Self {
1048 Self(xcm.0.into_iter().map(Instruction::<Call>::from).collect())
1049 }
1050}
1051
1052impl<Call> Instruction<Call> {
1053 pub fn into<C>(self) -> Instruction<C> {
1054 Instruction::from(self)
1055 }
1056 pub fn from<C>(xcm: Instruction<C>) -> Self {
1057 use Instruction::*;
1058 match xcm {
1059 WithdrawAsset(assets) => WithdrawAsset(assets),
1060 ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets),
1061 ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets),
1062 QueryResponse { query_id, response, max_weight, querier } =>
1063 QueryResponse { query_id, response, max_weight, querier },
1064 TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
1065 TransferReserveAsset { assets, dest, xcm } =>
1066 TransferReserveAsset { assets, dest, xcm },
1067 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1068 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
1069 HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
1070 HrmpChannelClosing { initiator, sender, recipient } =>
1071 HrmpChannelClosing { initiator, sender, recipient },
1072 Transact { origin_kind, require_weight_at_most, call } =>
1073 Transact { origin_kind, require_weight_at_most, call: call.into() },
1074 ReportError(response_info) => ReportError(response_info),
1075 DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary },
1076 DepositReserveAsset { assets, dest, xcm } => DepositReserveAsset { assets, dest, xcm },
1077 ExchangeAsset { give, want, maximal } => ExchangeAsset { give, want, maximal },
1078 InitiateReserveWithdraw { assets, reserve, xcm } =>
1079 InitiateReserveWithdraw { assets, reserve, xcm },
1080 InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm },
1081 ReportHolding { response_info, assets } => ReportHolding { response_info, assets },
1082 BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit },
1083 ClearOrigin => ClearOrigin,
1084 DescendOrigin(who) => DescendOrigin(who),
1085 RefundSurplus => RefundSurplus,
1086 SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
1087 SetAppendix(xcm) => SetAppendix(xcm.into()),
1088 ClearError => ClearError,
1089 ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
1090 Trap(code) => Trap(code),
1091 SubscribeVersion { query_id, max_response_weight } =>
1092 SubscribeVersion { query_id, max_response_weight },
1093 UnsubscribeVersion => UnsubscribeVersion,
1094 BurnAsset(assets) => BurnAsset(assets),
1095 ExpectAsset(assets) => ExpectAsset(assets),
1096 ExpectOrigin(origin) => ExpectOrigin(origin),
1097 ExpectError(error) => ExpectError(error),
1098 ExpectTransactStatus(transact_status) => ExpectTransactStatus(transact_status),
1099 QueryPallet { module_name, response_info } =>
1100 QueryPallet { module_name, response_info },
1101 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1102 ExpectPallet { index, name, module_name, crate_major, min_crate_minor },
1103 ReportTransactStatus(response_info) => ReportTransactStatus(response_info),
1104 ClearTransactStatus => ClearTransactStatus,
1105 UniversalOrigin(j) => UniversalOrigin(j),
1106 ExportMessage { network, destination, xcm } =>
1107 ExportMessage { network, destination, xcm },
1108 LockAsset { asset, unlocker } => LockAsset { asset, unlocker },
1109 UnlockAsset { asset, target } => UnlockAsset { asset, target },
1110 NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner },
1111 RequestUnlock { asset, locker } => RequestUnlock { asset, locker },
1112 SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw },
1113 SetTopic(topic) => SetTopic(topic),
1114 ClearTopic => ClearTopic,
1115 AliasOrigin(location) => AliasOrigin(location),
1116 UnpaidExecution { weight_limit, check_origin } =>
1117 UnpaidExecution { weight_limit, check_origin },
1118 }
1119 }
1120}
1121
1122impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
1124 fn weight(&self) -> Weight {
1125 use Instruction::*;
1126 match self {
1127 WithdrawAsset(assets) => W::withdraw_asset(assets),
1128 ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
1129 ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
1130 QueryResponse { query_id, response, max_weight, querier } =>
1131 W::query_response(query_id, response, max_weight, querier),
1132 TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
1133 TransferReserveAsset { assets, dest, xcm } =>
1134 W::transfer_reserve_asset(&assets, dest, xcm),
1135 Transact { origin_kind, require_weight_at_most, call } =>
1136 W::transact(origin_kind, require_weight_at_most, call),
1137 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1138 W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity),
1139 HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
1140 HrmpChannelClosing { initiator, sender, recipient } =>
1141 W::hrmp_channel_closing(initiator, sender, recipient),
1142 ClearOrigin => W::clear_origin(),
1143 DescendOrigin(who) => W::descend_origin(who),
1144 ReportError(response_info) => W::report_error(&response_info),
1145 DepositAsset { assets, beneficiary } => W::deposit_asset(assets, beneficiary),
1146 DepositReserveAsset { assets, dest, xcm } =>
1147 W::deposit_reserve_asset(assets, dest, xcm),
1148 ExchangeAsset { give, want, maximal } => W::exchange_asset(give, want, maximal),
1149 InitiateReserveWithdraw { assets, reserve, xcm } =>
1150 W::initiate_reserve_withdraw(assets, reserve, xcm),
1151 InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
1152 ReportHolding { response_info, assets } => W::report_holding(&response_info, &assets),
1153 BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
1154 RefundSurplus => W::refund_surplus(),
1155 SetErrorHandler(xcm) => W::set_error_handler(xcm),
1156 SetAppendix(xcm) => W::set_appendix(xcm),
1157 ClearError => W::clear_error(),
1158 ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
1159 Trap(code) => W::trap(code),
1160 SubscribeVersion { query_id, max_response_weight } =>
1161 W::subscribe_version(query_id, max_response_weight),
1162 UnsubscribeVersion => W::unsubscribe_version(),
1163 BurnAsset(assets) => W::burn_asset(assets),
1164 ExpectAsset(assets) => W::expect_asset(assets),
1165 ExpectOrigin(origin) => W::expect_origin(origin),
1166 ExpectError(error) => W::expect_error(error),
1167 ExpectTransactStatus(transact_status) => W::expect_transact_status(transact_status),
1168 QueryPallet { module_name, response_info } =>
1169 W::query_pallet(module_name, response_info),
1170 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1171 W::expect_pallet(index, name, module_name, crate_major, min_crate_minor),
1172 ReportTransactStatus(response_info) => W::report_transact_status(response_info),
1173 ClearTransactStatus => W::clear_transact_status(),
1174 UniversalOrigin(j) => W::universal_origin(j),
1175 ExportMessage { network, destination, xcm } =>
1176 W::export_message(network, destination, xcm),
1177 LockAsset { asset, unlocker } => W::lock_asset(asset, unlocker),
1178 UnlockAsset { asset, target } => W::unlock_asset(asset, target),
1179 NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner),
1180 RequestUnlock { asset, locker } => W::request_unlock(asset, locker),
1181 SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw),
1182 SetTopic(topic) => W::set_topic(topic),
1183 ClearTopic => W::clear_topic(),
1184 AliasOrigin(location) => W::alias_origin(location),
1185 UnpaidExecution { weight_limit, check_origin } =>
1186 W::unpaid_execution(weight_limit, check_origin),
1187 }
1188 }
1189}
1190
1191pub mod opaque {
1192 pub type Xcm = super::Xcm<()>;
1195
1196 pub type Instruction = super::Instruction<()>;
1199}
1200
1201impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
1203 type Error = ();
1204 fn try_from(old_xcm: OldXcm<Call>) -> result::Result<Self, Self::Error> {
1205 Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
1206 }
1207}
1208
1209impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
1211 type Error = ();
1212 fn try_from(old_instruction: OldInstruction<Call>) -> result::Result<Self, Self::Error> {
1213 use OldInstruction::*;
1214 Ok(match old_instruction {
1215 WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
1216 ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
1217 ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
1218 QueryResponse { query_id, response, max_weight, querier: Some(querier) } =>
1219 Self::QueryResponse {
1220 query_id,
1221 querier: querier.try_into()?,
1222 response: response.try_into()?,
1223 max_weight,
1224 },
1225 QueryResponse { query_id, response, max_weight, querier: None } =>
1226 Self::QueryResponse {
1227 query_id,
1228 querier: None,
1229 response: response.try_into()?,
1230 max_weight,
1231 },
1232 TransferAsset { assets, beneficiary } => Self::TransferAsset {
1233 assets: assets.try_into()?,
1234 beneficiary: beneficiary.try_into()?,
1235 },
1236 TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
1237 assets: assets.try_into()?,
1238 dest: dest.try_into()?,
1239 xcm: xcm.try_into()?,
1240 },
1241 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1242 Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
1243 HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
1244 HrmpChannelClosing { initiator, sender, recipient } =>
1245 Self::HrmpChannelClosing { initiator, sender, recipient },
1246 Transact { origin_kind, require_weight_at_most, call } =>
1247 Self::Transact { origin_kind, require_weight_at_most, call: call.into() },
1248 ReportError(response_info) => Self::ReportError(QueryResponseInfo {
1249 query_id: response_info.query_id,
1250 destination: response_info.destination.try_into().map_err(|_| ())?,
1251 max_weight: response_info.max_weight,
1252 }),
1253 DepositAsset { assets, beneficiary } => {
1254 let beneficiary = beneficiary.try_into()?;
1255 let assets = assets.try_into()?;
1256 Self::DepositAsset { assets, beneficiary }
1257 },
1258 DepositReserveAsset { assets, dest, xcm } => {
1259 let dest = dest.try_into()?;
1260 let xcm = xcm.try_into()?;
1261 let assets = assets.try_into()?;
1262 Self::DepositReserveAsset { assets, dest, xcm }
1263 },
1264 ExchangeAsset { give, want, maximal } => {
1265 let give = give.try_into()?;
1266 let want = want.try_into()?;
1267 Self::ExchangeAsset { give, want, maximal }
1268 },
1269 InitiateReserveWithdraw { assets, reserve, xcm } => {
1270 let assets = assets.try_into()?;
1271 let reserve = reserve.try_into()?;
1272 let xcm = xcm.try_into()?;
1273 Self::InitiateReserveWithdraw { assets, reserve, xcm }
1274 },
1275 InitiateTeleport { assets, dest, xcm } => {
1276 let assets = assets.try_into()?;
1277 let dest = dest.try_into()?;
1278 let xcm = xcm.try_into()?;
1279 Self::InitiateTeleport { assets, dest, xcm }
1280 },
1281 ReportHolding { response_info, assets } => {
1282 let response_info = QueryResponseInfo {
1283 destination: response_info.destination.try_into().map_err(|_| ())?,
1284 query_id: response_info.query_id,
1285 max_weight: response_info.max_weight,
1286 };
1287 Self::ReportHolding { response_info, assets: assets.try_into()? }
1288 },
1289 BuyExecution { fees, weight_limit } => {
1290 let fees = fees.try_into()?;
1291 let weight_limit = weight_limit.into();
1292 Self::BuyExecution { fees, weight_limit }
1293 },
1294 ClearOrigin => Self::ClearOrigin,
1295 DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
1296 RefundSurplus => Self::RefundSurplus,
1297 SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
1298 SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
1299 ClearError => Self::ClearError,
1300 ClaimAsset { assets, ticket } => {
1301 let assets = assets.try_into()?;
1302 let ticket = ticket.try_into()?;
1303 Self::ClaimAsset { assets, ticket }
1304 },
1305 Trap(code) => Self::Trap(code),
1306 SubscribeVersion { query_id, max_response_weight } =>
1307 Self::SubscribeVersion { query_id, max_response_weight },
1308 UnsubscribeVersion => Self::UnsubscribeVersion,
1309 BurnAsset(assets) => Self::BurnAsset(assets.try_into()?),
1310 ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?),
1311 ExpectOrigin(maybe_location) => Self::ExpectOrigin(
1312 maybe_location.map(|location| location.try_into()).transpose().map_err(|_| ())?,
1313 ),
1314 ExpectError(maybe_error) => Self::ExpectError(
1315 maybe_error.map(|error| error.try_into()).transpose().map_err(|_| ())?,
1316 ),
1317 ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code),
1318 QueryPallet { module_name, response_info } => Self::QueryPallet {
1319 module_name,
1320 response_info: response_info.try_into().map_err(|_| ())?,
1321 },
1322 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1323 Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor },
1324 ReportTransactStatus(response_info) =>
1325 Self::ReportTransactStatus(response_info.try_into().map_err(|_| ())?),
1326 ClearTransactStatus => Self::ClearTransactStatus,
1327 UniversalOrigin(junction) =>
1328 Self::UniversalOrigin(junction.try_into().map_err(|_| ())?),
1329 ExportMessage { network, destination, xcm } => Self::ExportMessage {
1330 network: network.into(),
1331 destination: destination.try_into().map_err(|_| ())?,
1332 xcm: xcm.try_into().map_err(|_| ())?,
1333 },
1334 LockAsset { asset, unlocker } => Self::LockAsset {
1335 asset: asset.try_into().map_err(|_| ())?,
1336 unlocker: unlocker.try_into().map_err(|_| ())?,
1337 },
1338 UnlockAsset { asset, target } => Self::UnlockAsset {
1339 asset: asset.try_into().map_err(|_| ())?,
1340 target: target.try_into().map_err(|_| ())?,
1341 },
1342 NoteUnlockable { asset, owner } => Self::NoteUnlockable {
1343 asset: asset.try_into().map_err(|_| ())?,
1344 owner: owner.try_into().map_err(|_| ())?,
1345 },
1346 RequestUnlock { asset, locker } => Self::RequestUnlock {
1347 asset: asset.try_into().map_err(|_| ())?,
1348 locker: locker.try_into().map_err(|_| ())?,
1349 },
1350 SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw },
1351 SetTopic(topic) => Self::SetTopic(topic),
1352 ClearTopic => Self::ClearTopic,
1353 AliasOrigin(location) => Self::AliasOrigin(location.try_into().map_err(|_| ())?),
1354 UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution {
1355 weight_limit,
1356 check_origin: check_origin
1357 .map(|location| location.try_into())
1358 .transpose()
1359 .map_err(|_| ())?,
1360 },
1361 })
1362 }
1363}
1364
1365#[cfg(test)]
1366mod tests {
1367 use super::{prelude::*, *};
1368 use crate::v3::{
1369 Junctions::Here as OldHere, MultiAssetFilter as OldMultiAssetFilter,
1370 WildMultiAsset as OldWildMultiAsset,
1371 };
1372
1373 #[test]
1374 fn basic_roundtrip_works() {
1375 let xcm = Xcm::<()>(vec![TransferAsset {
1376 assets: (Here, 1u128).into(),
1377 beneficiary: Here.into(),
1378 }]);
1379 let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset {
1380 assets: (OldHere, 1u128).into(),
1381 beneficiary: OldHere.into(),
1382 }]);
1383 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1384 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1385 assert_eq!(new_xcm, xcm);
1386 }
1387
1388 #[test]
1389 fn teleport_roundtrip_works() {
1390 let xcm = Xcm::<()>(vec![
1391 ReceiveTeleportedAsset((Here, 1u128).into()),
1392 ClearOrigin,
1393 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1394 ]);
1395 let old_xcm: OldXcm<()> = OldXcm::<()>(vec![
1396 OldInstruction::ReceiveTeleportedAsset((OldHere, 1u128).into()),
1397 OldInstruction::ClearOrigin,
1398 OldInstruction::DepositAsset {
1399 assets: crate::v3::MultiAssetFilter::Wild(crate::v3::WildMultiAsset::AllCounted(1)),
1400 beneficiary: OldHere.into(),
1401 },
1402 ]);
1403 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1404 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1405 assert_eq!(new_xcm, xcm);
1406 }
1407
1408 #[test]
1409 fn reserve_deposit_roundtrip_works() {
1410 let xcm = Xcm::<()>(vec![
1411 ReserveAssetDeposited((Here, 1u128).into()),
1412 ClearOrigin,
1413 BuyExecution {
1414 fees: (Here, 1u128).into(),
1415 weight_limit: Some(Weight::from_parts(1, 1)).into(),
1416 },
1417 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1418 ]);
1419 let old_xcm = OldXcm::<()>(vec![
1420 OldInstruction::ReserveAssetDeposited((OldHere, 1u128).into()),
1421 OldInstruction::ClearOrigin,
1422 OldInstruction::BuyExecution {
1423 fees: (OldHere, 1u128).into(),
1424 weight_limit: WeightLimit::Limited(Weight::from_parts(1, 1)),
1425 },
1426 OldInstruction::DepositAsset {
1427 assets: crate::v3::MultiAssetFilter::Wild(crate::v3::WildMultiAsset::AllCounted(1)),
1428 beneficiary: OldHere.into(),
1429 },
1430 ]);
1431 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1432 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1433 assert_eq!(new_xcm, xcm);
1434 }
1435
1436 #[test]
1437 fn deposit_asset_roundtrip_works() {
1438 let xcm = Xcm::<()>(vec![
1439 WithdrawAsset((Here, 1u128).into()),
1440 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1441 ]);
1442 let old_xcm = OldXcm::<()>(vec![
1443 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1444 OldInstruction::DepositAsset {
1445 assets: OldMultiAssetFilter::Wild(OldWildMultiAsset::AllCounted(1)),
1446 beneficiary: OldHere.into(),
1447 },
1448 ]);
1449 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1450 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1451 assert_eq!(new_xcm, xcm);
1452 }
1453
1454 #[test]
1455 fn deposit_reserve_asset_roundtrip_works() {
1456 let xcm = Xcm::<()>(vec![
1457 WithdrawAsset((Here, 1u128).into()),
1458 DepositReserveAsset {
1459 assets: Wild(AllCounted(1)),
1460 dest: Here.into(),
1461 xcm: Xcm::<()>(vec![]),
1462 },
1463 ]);
1464 let old_xcm = OldXcm::<()>(vec![
1465 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1466 OldInstruction::DepositReserveAsset {
1467 assets: OldMultiAssetFilter::Wild(OldWildMultiAsset::AllCounted(1)),
1468 dest: OldHere.into(),
1469 xcm: OldXcm::<()>(vec![]),
1470 },
1471 ]);
1472 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1473 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1474 assert_eq!(new_xcm, xcm);
1475 }
1476
1477 #[test]
1478 fn decoding_respects_limit() {
1479 let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1480 let encoded = max_xcm.encode();
1481 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1482
1483 let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1484 let encoded = big_xcm.encode();
1485 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1486
1487 let nested_xcm = Xcm::<()>(vec![
1488 DepositReserveAsset {
1489 assets: All.into(),
1490 dest: Here.into(),
1491 xcm: max_xcm,
1492 };
1493 (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1494 ]);
1495 let encoded = nested_xcm.encode();
1496 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1497
1498 let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1499 let encoded = even_more_nested_xcm.encode();
1500 assert_eq!(encoded.len(), 342530);
1501 assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1503 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1504 }
1505}