1pub use super::v3::GetWeight;
20use super::v4::{
21 Instruction as OldInstruction, PalletInfo as OldPalletInfo,
22 QueryResponseInfo as OldQueryResponseInfo, Response as OldResponse, Xcm as OldXcm,
23};
24use crate::{utils::decode_xcm_instructions, DoubleEncoded};
25use alloc::{vec, vec::Vec};
26use bounded_collections::{parameter_types, BoundedVec};
27use codec::{
28 self, Decode, DecodeWithMemTracking, Encode, Error as CodecError, Input as CodecInput,
29 MaxEncodedLen,
30};
31use core::{fmt::Debug, result};
32use derive_where::derive_where;
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, AssetTransferFilter, Assets, Fungibility,
43 WildAsset, WildFungibility, MAX_ITEMS_IN_ASSETS,
44};
45pub use junction::{
46 BodyId, BodyPart, Junction, NetworkId, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH,
47};
48pub use junctions::Junctions;
49pub use location::{Ancestor, AncestorThen, InteriorLocation, Location, Parent, ParentThen};
50pub use traits::{
51 send_xcm, validate_send, Error, ExecuteXcm, InstructionError, InstructionIndex, Outcome,
52 PreparedMessage, Reanchorable, Result, SendError, SendResult, SendXcm, Weight, XcmHash,
53};
54pub use super::v4::{MaxDispatchErrorLen, MaybeErrorCode, OriginKind, WeightLimit};
56
57pub const VERSION: super::Version = 5;
58
59pub type QueryId = u64;
61
62#[derive(Default, DecodeWithMemTracking, Encode, TypeInfo)]
63#[derive_where(Clone, Eq, PartialEq, Debug)]
64#[codec(encode_bound())]
65#[codec(decode_bound())]
66#[scale_info(bounds(), skip_type_params(Call))]
67pub struct Xcm<Call>(pub Vec<Instruction<Call>>);
68
69impl<Call> Decode for Xcm<Call> {
70 fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
71 Ok(Xcm(decode_xcm_instructions(input)?))
72 }
73}
74
75impl<Call> Xcm<Call> {
76 pub fn new() -> Self {
78 Self(vec![])
79 }
80
81 pub fn is_empty(&self) -> bool {
83 self.0.is_empty()
84 }
85
86 pub fn len(&self) -> usize {
88 self.0.len()
89 }
90
91 pub fn inner(&self) -> &[Instruction<Call>] {
93 &self.0
94 }
95
96 pub fn inner_mut(&mut self) -> &mut Vec<Instruction<Call>> {
98 &mut self.0
99 }
100
101 pub fn into_inner(self) -> Vec<Instruction<Call>> {
103 self.0
104 }
105
106 pub fn iter(&self) -> impl Iterator<Item = &Instruction<Call>> {
108 self.0.iter()
109 }
110
111 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Instruction<Call>> {
113 self.0.iter_mut()
114 }
115
116 pub fn into_iter(self) -> impl Iterator<Item = Instruction<Call>> {
118 self.0.into_iter()
119 }
120
121 pub fn or_else(self, f: impl FnOnce() -> Self) -> Self {
124 if self.0.is_empty() {
125 f()
126 } else {
127 self
128 }
129 }
130
131 pub fn first(&self) -> Option<&Instruction<Call>> {
133 self.0.first()
134 }
135
136 pub fn last(&self) -> Option<&Instruction<Call>> {
138 self.0.last()
139 }
140
141 pub fn only(&self) -> Option<&Instruction<Call>> {
143 if self.0.len() == 1 {
144 self.0.first()
145 } else {
146 None
147 }
148 }
149
150 pub fn into_only(mut self) -> core::result::Result<Instruction<Call>, Self> {
153 if self.0.len() == 1 {
154 self.0.pop().ok_or(self)
155 } else {
156 Err(self)
157 }
158 }
159}
160
161impl<Call> From<Vec<Instruction<Call>>> for Xcm<Call> {
162 fn from(c: Vec<Instruction<Call>>) -> Self {
163 Self(c)
164 }
165}
166
167impl<Call> From<Xcm<Call>> for Vec<Instruction<Call>> {
168 fn from(c: Xcm<Call>) -> Self {
169 c.0
170 }
171}
172
173pub mod prelude {
175 mod contents {
176 pub use super::super::{
177 send_xcm, validate_send, Ancestor, AncestorThen, Asset,
178 AssetFilter::{self, *},
179 AssetId,
180 AssetInstance::{self, *},
181 Assets, BodyId, BodyPart, Error as XcmError, ExecuteXcm,
182 Fungibility::{self, *},
183 Hint::{self, *},
184 HintNumVariants,
185 Instruction::*,
186 InstructionError, InstructionIndex, InteriorLocation,
187 Junction::{self, *},
188 Junctions::{self, Here},
189 Location, MaxAssetTransferFilters, MaybeErrorCode,
190 NetworkId::{self, *},
191 OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId,
192 QueryResponseInfo, Reanchorable, Response, Result as XcmResult, SendError, SendResult,
193 SendXcm, Weight,
194 WeightLimit::{self, *},
195 WildAsset::{self, *},
196 WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
197 XcmContext, XcmHash, XcmWeightInfo, VERSION as XCM_VERSION,
198 };
199 }
200 pub use super::{Instruction, Xcm};
201 pub use contents::*;
202 pub mod opaque {
203 pub use super::{
204 super::opaque::{Instruction, Xcm},
205 contents::*,
206 };
207 }
208}
209
210parameter_types! {
211 pub MaxPalletNameLen: u32 = 48;
212 pub MaxPalletsInfo: u32 = 64;
213 pub MaxAssetTransferFilters: u32 = 6;
214}
215
216#[derive(
217 Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen,
218)]
219pub struct PalletInfo {
220 #[codec(compact)]
221 pub index: u32,
222 pub name: BoundedVec<u8, MaxPalletNameLen>,
223 pub module_name: BoundedVec<u8, MaxPalletNameLen>,
224 #[codec(compact)]
225 pub major: u32,
226 #[codec(compact)]
227 pub minor: u32,
228 #[codec(compact)]
229 pub patch: u32,
230}
231
232impl TryInto<OldPalletInfo> for PalletInfo {
233 type Error = ();
234
235 fn try_into(self) -> result::Result<OldPalletInfo, Self::Error> {
236 OldPalletInfo::new(
237 self.index,
238 self.name.into_inner(),
239 self.module_name.into_inner(),
240 self.major,
241 self.minor,
242 self.patch,
243 )
244 .map_err(|_| ())
245 }
246}
247
248impl PalletInfo {
249 pub fn new(
250 index: u32,
251 name: Vec<u8>,
252 module_name: Vec<u8>,
253 major: u32,
254 minor: u32,
255 patch: u32,
256 ) -> result::Result<Self, Error> {
257 let name = BoundedVec::try_from(name).map_err(|_| Error::Overflow)?;
258 let module_name = BoundedVec::try_from(module_name).map_err(|_| Error::Overflow)?;
259
260 Ok(Self { index, name, module_name, major, minor, patch })
261 }
262}
263
264#[derive(
266 Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen,
267)]
268pub enum Response {
269 Null,
271 Assets(Assets),
273 ExecutionResult(Option<(u32, Error)>),
275 Version(super::Version),
277 PalletsInfo(BoundedVec<PalletInfo, MaxPalletsInfo>),
279 DispatchResult(MaybeErrorCode),
281}
282
283impl Default for Response {
284 fn default() -> Self {
285 Self::Null
286 }
287}
288
289impl TryFrom<OldResponse> for Response {
290 type Error = ();
291
292 fn try_from(old: OldResponse) -> result::Result<Self, Self::Error> {
293 use OldResponse::*;
294 Ok(match old {
295 Null => Self::Null,
296 Assets(assets) => Self::Assets(assets.try_into()?),
297 ExecutionResult(result) => Self::ExecutionResult(
298 result
299 .map(|(num, old_error)| (num, old_error.try_into()))
300 .map(|(num, result)| result.map(|inner| (num, inner)))
301 .transpose()?,
302 ),
303 Version(version) => Self::Version(version),
304 PalletsInfo(pallet_info) => {
305 let inner = pallet_info
306 .into_iter()
307 .map(TryInto::try_into)
308 .collect::<result::Result<Vec<_>, _>>()?;
309 Self::PalletsInfo(
310 BoundedVec::<PalletInfo, MaxPalletsInfo>::try_from(inner).map_err(|_| ())?,
311 )
312 },
313 DispatchResult(maybe_error) => Self::DispatchResult(maybe_error),
314 })
315 }
316}
317
318#[derive(Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
320pub struct QueryResponseInfo {
321 pub destination: Location,
323 #[codec(compact)]
325 pub query_id: QueryId,
326 pub max_weight: Weight,
328}
329
330impl TryFrom<OldQueryResponseInfo> for QueryResponseInfo {
331 type Error = ();
332
333 fn try_from(old: OldQueryResponseInfo) -> result::Result<Self, Self::Error> {
334 Ok(Self {
335 destination: old.destination.try_into()?,
336 query_id: old.query_id,
337 max_weight: old.max_weight,
338 })
339 }
340}
341
342#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
344pub struct XcmContext {
345 pub origin: Option<Location>,
347 pub message_id: XcmHash,
350 pub topic: Option<[u8; 32]>,
352}
353
354impl XcmContext {
355 pub fn with_message_id(message_id: XcmHash) -> XcmContext {
358 XcmContext { origin: None, message_id, topic: None }
359 }
360
361 pub fn topic_or_message_id(&self) -> XcmHash {
363 if let Some(id) = self.topic {
364 id.into()
365 } else {
366 self.message_id
367 }
368 }
369}
370
371#[derive(
380 Encode,
381 Decode,
382 DecodeWithMemTracking,
383 TypeInfo,
384 xcm_procedural::XcmWeightInfoTrait,
385 xcm_procedural::Builder,
386)]
387#[derive_where(Clone, Eq, PartialEq, Debug)]
388#[codec(encode_bound())]
389#[codec(decode_bound())]
390#[codec(decode_with_mem_tracking_bound())]
391#[scale_info(bounds(), skip_type_params(Call))]
392pub enum Instruction<Call> {
393 #[builder(loads_holding)]
402 WithdrawAsset(Assets),
403
404 #[builder(loads_holding)]
416 ReserveAssetDeposited(Assets),
417
418 #[builder(loads_holding)]
430 ReceiveTeleportedAsset(Assets),
431
432 QueryResponse {
450 #[codec(compact)]
451 query_id: QueryId,
452 response: Response,
453 max_weight: Weight,
454 querier: Option<Location>,
455 },
456
457 TransferAsset { assets: Assets, beneficiary: Location },
469
470 TransferReserveAsset { assets: Assets, dest: Location, xcm: Xcm<()> },
489
490 Transact {
508 origin_kind: OriginKind,
509 fallback_max_weight: Option<Weight>,
510 call: DoubleEncoded<Call>,
511 },
512
513 HrmpNewChannelOpenRequest {
525 #[codec(compact)]
526 sender: u32,
527 #[codec(compact)]
528 max_message_size: u32,
529 #[codec(compact)]
530 max_capacity: u32,
531 },
532
533 HrmpChannelAccepted {
543 #[codec(compact)]
546 recipient: u32,
547 },
548
549 HrmpChannelClosing {
560 #[codec(compact)]
561 initiator: u32,
562 #[codec(compact)]
563 sender: u32,
564 #[codec(compact)]
565 recipient: u32,
566 },
567
568 ClearOrigin,
580
581 DescendOrigin(InteriorLocation),
587
588 ReportError(QueryResponseInfo),
598
599 DepositAsset { assets: AssetFilter, beneficiary: Location },
609
610 DepositReserveAsset { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
627
628 ExchangeAsset { give: AssetFilter, want: Assets, maximal: bool },
644
645 InitiateReserveWithdraw { assets: AssetFilter, reserve: Location, xcm: Xcm<()> },
660
661 InitiateTeleport { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
676
677 ReportHolding { response_info: QueryResponseInfo, assets: AssetFilter },
690
691 #[builder(pays_fees)]
703 BuyExecution { fees: Asset, weight_limit: WeightLimit },
704
705 RefundSurplus,
711
712 SetErrorHandler(Xcm<Call>),
727
728 SetAppendix(Xcm<Call>),
743
744 ClearError,
750
751 #[builder(loads_holding)]
762 ClaimAsset { assets: Assets, ticket: Location },
763
764 Trap(#[codec(compact)] u64),
771
772 SubscribeVersion {
785 #[codec(compact)]
786 query_id: QueryId,
787 max_response_weight: Weight,
788 },
789
790 UnsubscribeVersion,
796
797 BurnAsset(Assets),
807
808 ExpectAsset(Assets),
815
816 ExpectOrigin(Option<Location>),
823
824 ExpectError(Option<(u32, Error)>),
831
832 ExpectTransactStatus(MaybeErrorCode),
841
842 QueryPallet { module_name: Vec<u8>, response_info: QueryResponseInfo },
857
858 ExpectPallet {
877 #[codec(compact)]
878 index: u32,
879 name: Vec<u8>,
880 module_name: Vec<u8>,
881 #[codec(compact)]
882 crate_major: u32,
883 #[codec(compact)]
884 min_crate_minor: u32,
885 },
886
887 ReportTransactStatus(QueryResponseInfo),
899
900 ClearTransactStatus,
908
909 UniversalOrigin(Junction),
923
924 ExportMessage { network: NetworkId, destination: InteriorLocation, xcm: Xcm<()> },
944
945 LockAsset { asset: Asset, unlocker: Location },
960
961 UnlockAsset { asset: Asset, target: Location },
973
974 NoteUnlockable { asset: Asset, owner: Location },
988
989 RequestUnlock { asset: Asset, locker: Location },
1002
1003 SetFeesMode { jit_withdraw: bool },
1012
1013 SetTopic([u8; 32]),
1025
1026 ClearTopic,
1032
1033 AliasOrigin(Location),
1039
1040 UnpaidExecution { weight_limit: WeightLimit, check_origin: Option<Location> },
1051
1052 #[builder(pays_fees)]
1058 PayFees { asset: Asset },
1059
1060 InitiateTransfer {
1107 destination: Location,
1108 remote_fees: Option<AssetTransferFilter>,
1109 preserve_origin: bool,
1110 assets: BoundedVec<AssetTransferFilter, MaxAssetTransferFilters>,
1111 remote_xcm: Xcm<()>,
1112 },
1113
1114 ExecuteWithOrigin { descendant_origin: Option<InteriorLocation>, xcm: Xcm<Call> },
1132
1133 SetHints { hints: BoundedVec<Hint, HintNumVariants> },
1142}
1143
1144#[derive(
1145 Encode,
1146 Decode,
1147 DecodeWithMemTracking,
1148 TypeInfo,
1149 Debug,
1150 PartialEq,
1151 Eq,
1152 Clone,
1153 xcm_procedural::NumVariants,
1154)]
1155pub enum Hint {
1156 AssetClaimer { location: Location },
1161}
1162
1163impl<Call> Xcm<Call> {
1164 pub fn into<C>(self) -> Xcm<C> {
1165 Xcm::from(self)
1166 }
1167 pub fn from<C>(xcm: Xcm<C>) -> Self {
1168 Self(xcm.0.into_iter().map(Instruction::<Call>::from).collect())
1169 }
1170}
1171
1172impl<Call> Instruction<Call> {
1173 pub fn into<C>(self) -> Instruction<C> {
1174 Instruction::from(self)
1175 }
1176 pub fn from<C>(xcm: Instruction<C>) -> Self {
1177 use Instruction::*;
1178 match xcm {
1179 WithdrawAsset(assets) => WithdrawAsset(assets),
1180 ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets),
1181 ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets),
1182 QueryResponse { query_id, response, max_weight, querier } =>
1183 QueryResponse { query_id, response, max_weight, querier },
1184 TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
1185 TransferReserveAsset { assets, dest, xcm } =>
1186 TransferReserveAsset { assets, dest, xcm },
1187 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1188 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
1189 HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
1190 HrmpChannelClosing { initiator, sender, recipient } =>
1191 HrmpChannelClosing { initiator, sender, recipient },
1192 Transact { origin_kind, call, fallback_max_weight } =>
1193 Transact { origin_kind, call: call.into(), fallback_max_weight },
1194 ReportError(response_info) => ReportError(response_info),
1195 DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary },
1196 DepositReserveAsset { assets, dest, xcm } => DepositReserveAsset { assets, dest, xcm },
1197 ExchangeAsset { give, want, maximal } => ExchangeAsset { give, want, maximal },
1198 InitiateReserveWithdraw { assets, reserve, xcm } =>
1199 InitiateReserveWithdraw { assets, reserve, xcm },
1200 InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm },
1201 ReportHolding { response_info, assets } => ReportHolding { response_info, assets },
1202 BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit },
1203 ClearOrigin => ClearOrigin,
1204 DescendOrigin(who) => DescendOrigin(who),
1205 RefundSurplus => RefundSurplus,
1206 SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
1207 SetAppendix(xcm) => SetAppendix(xcm.into()),
1208 ClearError => ClearError,
1209 SetHints { hints } => SetHints { hints },
1210 ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
1211 Trap(code) => Trap(code),
1212 SubscribeVersion { query_id, max_response_weight } =>
1213 SubscribeVersion { query_id, max_response_weight },
1214 UnsubscribeVersion => UnsubscribeVersion,
1215 BurnAsset(assets) => BurnAsset(assets),
1216 ExpectAsset(assets) => ExpectAsset(assets),
1217 ExpectOrigin(origin) => ExpectOrigin(origin),
1218 ExpectError(error) => ExpectError(error),
1219 ExpectTransactStatus(transact_status) => ExpectTransactStatus(transact_status),
1220 QueryPallet { module_name, response_info } =>
1221 QueryPallet { module_name, response_info },
1222 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1223 ExpectPallet { index, name, module_name, crate_major, min_crate_minor },
1224 ReportTransactStatus(response_info) => ReportTransactStatus(response_info),
1225 ClearTransactStatus => ClearTransactStatus,
1226 UniversalOrigin(j) => UniversalOrigin(j),
1227 ExportMessage { network, destination, xcm } =>
1228 ExportMessage { network, destination, xcm },
1229 LockAsset { asset, unlocker } => LockAsset { asset, unlocker },
1230 UnlockAsset { asset, target } => UnlockAsset { asset, target },
1231 NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner },
1232 RequestUnlock { asset, locker } => RequestUnlock { asset, locker },
1233 SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw },
1234 SetTopic(topic) => SetTopic(topic),
1235 ClearTopic => ClearTopic,
1236 AliasOrigin(location) => AliasOrigin(location),
1237 UnpaidExecution { weight_limit, check_origin } =>
1238 UnpaidExecution { weight_limit, check_origin },
1239 PayFees { asset } => PayFees { asset },
1240 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } =>
1241 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm },
1242 ExecuteWithOrigin { descendant_origin, xcm } =>
1243 ExecuteWithOrigin { descendant_origin, xcm: xcm.into() },
1244 }
1245 }
1246}
1247
1248impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
1250 fn weight(&self) -> Weight {
1251 use Instruction::*;
1252 match self {
1253 WithdrawAsset(assets) => W::withdraw_asset(assets),
1254 ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
1255 ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
1256 QueryResponse { query_id, response, max_weight, querier } =>
1257 W::query_response(query_id, response, max_weight, querier),
1258 TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
1259 TransferReserveAsset { assets, dest, xcm } =>
1260 W::transfer_reserve_asset(&assets, dest, xcm),
1261 Transact { origin_kind, fallback_max_weight, call } =>
1262 W::transact(origin_kind, fallback_max_weight, call),
1263 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1264 W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity),
1265 HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
1266 HrmpChannelClosing { initiator, sender, recipient } =>
1267 W::hrmp_channel_closing(initiator, sender, recipient),
1268 ClearOrigin => W::clear_origin(),
1269 DescendOrigin(who) => W::descend_origin(who),
1270 ReportError(response_info) => W::report_error(&response_info),
1271 DepositAsset { assets, beneficiary } => W::deposit_asset(assets, beneficiary),
1272 DepositReserveAsset { assets, dest, xcm } =>
1273 W::deposit_reserve_asset(assets, dest, xcm),
1274 ExchangeAsset { give, want, maximal } => W::exchange_asset(give, want, maximal),
1275 InitiateReserveWithdraw { assets, reserve, xcm } =>
1276 W::initiate_reserve_withdraw(assets, reserve, xcm),
1277 InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
1278 ReportHolding { response_info, assets } => W::report_holding(&response_info, &assets),
1279 BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
1280 RefundSurplus => W::refund_surplus(),
1281 SetErrorHandler(xcm) => W::set_error_handler(xcm),
1282 SetAppendix(xcm) => W::set_appendix(xcm),
1283 ClearError => W::clear_error(),
1284 SetHints { hints } => W::set_hints(hints),
1285 ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
1286 Trap(code) => W::trap(code),
1287 SubscribeVersion { query_id, max_response_weight } =>
1288 W::subscribe_version(query_id, max_response_weight),
1289 UnsubscribeVersion => W::unsubscribe_version(),
1290 BurnAsset(assets) => W::burn_asset(assets),
1291 ExpectAsset(assets) => W::expect_asset(assets),
1292 ExpectOrigin(origin) => W::expect_origin(origin),
1293 ExpectError(error) => W::expect_error(error),
1294 ExpectTransactStatus(transact_status) => W::expect_transact_status(transact_status),
1295 QueryPallet { module_name, response_info } =>
1296 W::query_pallet(module_name, response_info),
1297 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1298 W::expect_pallet(index, name, module_name, crate_major, min_crate_minor),
1299 ReportTransactStatus(response_info) => W::report_transact_status(response_info),
1300 ClearTransactStatus => W::clear_transact_status(),
1301 UniversalOrigin(j) => W::universal_origin(j),
1302 ExportMessage { network, destination, xcm } =>
1303 W::export_message(network, destination, xcm),
1304 LockAsset { asset, unlocker } => W::lock_asset(asset, unlocker),
1305 UnlockAsset { asset, target } => W::unlock_asset(asset, target),
1306 NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner),
1307 RequestUnlock { asset, locker } => W::request_unlock(asset, locker),
1308 SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw),
1309 SetTopic(topic) => W::set_topic(topic),
1310 ClearTopic => W::clear_topic(),
1311 AliasOrigin(location) => W::alias_origin(location),
1312 UnpaidExecution { weight_limit, check_origin } =>
1313 W::unpaid_execution(weight_limit, check_origin),
1314 PayFees { asset } => W::pay_fees(asset),
1315 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } =>
1316 W::initiate_transfer(destination, remote_fees, preserve_origin, assets, remote_xcm),
1317 ExecuteWithOrigin { descendant_origin, xcm } =>
1318 W::execute_with_origin(descendant_origin, xcm),
1319 }
1320 }
1321}
1322
1323pub mod opaque {
1324 pub type Xcm = super::Xcm<()>;
1327
1328 pub type Instruction = super::Instruction<()>;
1331}
1332
1333impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
1335 type Error = ();
1336 fn try_from(old_xcm: OldXcm<Call>) -> result::Result<Self, Self::Error> {
1337 Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
1338 }
1339}
1340
1341impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
1343 type Error = ();
1344 fn try_from(old_instruction: OldInstruction<Call>) -> result::Result<Self, Self::Error> {
1345 use OldInstruction::*;
1346 Ok(match old_instruction {
1347 WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
1348 ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
1349 ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
1350 QueryResponse { query_id, response, max_weight, querier: Some(querier) } =>
1351 Self::QueryResponse {
1352 query_id,
1353 querier: querier.try_into()?,
1354 response: response.try_into()?,
1355 max_weight,
1356 },
1357 QueryResponse { query_id, response, max_weight, querier: None } =>
1358 Self::QueryResponse {
1359 query_id,
1360 querier: None,
1361 response: response.try_into()?,
1362 max_weight,
1363 },
1364 TransferAsset { assets, beneficiary } => Self::TransferAsset {
1365 assets: assets.try_into()?,
1366 beneficiary: beneficiary.try_into()?,
1367 },
1368 TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
1369 assets: assets.try_into()?,
1370 dest: dest.try_into()?,
1371 xcm: xcm.try_into()?,
1372 },
1373 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1374 Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
1375 HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
1376 HrmpChannelClosing { initiator, sender, recipient } =>
1377 Self::HrmpChannelClosing { initiator, sender, recipient },
1378 Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
1379 origin_kind,
1380 call: call.into(),
1381 fallback_max_weight: Some(require_weight_at_most),
1382 },
1383 ReportError(response_info) => Self::ReportError(QueryResponseInfo {
1384 query_id: response_info.query_id,
1385 destination: response_info.destination.try_into().map_err(|_| ())?,
1386 max_weight: response_info.max_weight,
1387 }),
1388 DepositAsset { assets, beneficiary } => {
1389 let beneficiary = beneficiary.try_into()?;
1390 let assets = assets.try_into()?;
1391 Self::DepositAsset { assets, beneficiary }
1392 },
1393 DepositReserveAsset { assets, dest, xcm } => {
1394 let dest = dest.try_into()?;
1395 let xcm = xcm.try_into()?;
1396 let assets = assets.try_into()?;
1397 Self::DepositReserveAsset { assets, dest, xcm }
1398 },
1399 ExchangeAsset { give, want, maximal } => {
1400 let give = give.try_into()?;
1401 let want = want.try_into()?;
1402 Self::ExchangeAsset { give, want, maximal }
1403 },
1404 InitiateReserveWithdraw { assets, reserve, xcm } => {
1405 let assets = assets.try_into()?;
1406 let reserve = reserve.try_into()?;
1407 let xcm = xcm.try_into()?;
1408 Self::InitiateReserveWithdraw { assets, reserve, xcm }
1409 },
1410 InitiateTeleport { assets, dest, xcm } => {
1411 let assets = assets.try_into()?;
1412 let dest = dest.try_into()?;
1413 let xcm = xcm.try_into()?;
1414 Self::InitiateTeleport { assets, dest, xcm }
1415 },
1416 ReportHolding { response_info, assets } => {
1417 let response_info = QueryResponseInfo {
1418 destination: response_info.destination.try_into().map_err(|_| ())?,
1419 query_id: response_info.query_id,
1420 max_weight: response_info.max_weight,
1421 };
1422 Self::ReportHolding { response_info, assets: assets.try_into()? }
1423 },
1424 BuyExecution { fees, weight_limit } => {
1425 let fees = fees.try_into()?;
1426 let weight_limit = weight_limit.into();
1427 Self::BuyExecution { fees, weight_limit }
1428 },
1429 ClearOrigin => Self::ClearOrigin,
1430 DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
1431 RefundSurplus => Self::RefundSurplus,
1432 SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
1433 SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
1434 ClearError => Self::ClearError,
1435 ClaimAsset { assets, ticket } => {
1436 let assets = assets.try_into()?;
1437 let ticket = ticket.try_into()?;
1438 Self::ClaimAsset { assets, ticket }
1439 },
1440 Trap(code) => Self::Trap(code),
1441 SubscribeVersion { query_id, max_response_weight } =>
1442 Self::SubscribeVersion { query_id, max_response_weight },
1443 UnsubscribeVersion => Self::UnsubscribeVersion,
1444 BurnAsset(assets) => Self::BurnAsset(assets.try_into()?),
1445 ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?),
1446 ExpectOrigin(maybe_location) => Self::ExpectOrigin(
1447 maybe_location.map(|location| location.try_into()).transpose().map_err(|_| ())?,
1448 ),
1449 ExpectError(maybe_error) => Self::ExpectError(
1450 maybe_error
1451 .map(|(num, old_error)| (num, old_error.try_into()))
1452 .map(|(num, result)| result.map(|inner| (num, inner)))
1453 .transpose()
1454 .map_err(|_| ())?,
1455 ),
1456 ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code),
1457 QueryPallet { module_name, response_info } => Self::QueryPallet {
1458 module_name,
1459 response_info: response_info.try_into().map_err(|_| ())?,
1460 },
1461 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1462 Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor },
1463 ReportTransactStatus(response_info) =>
1464 Self::ReportTransactStatus(response_info.try_into().map_err(|_| ())?),
1465 ClearTransactStatus => Self::ClearTransactStatus,
1466 UniversalOrigin(junction) =>
1467 Self::UniversalOrigin(junction.try_into().map_err(|_| ())?),
1468 ExportMessage { network, destination, xcm } => Self::ExportMessage {
1469 network: network.into(),
1470 destination: destination.try_into().map_err(|_| ())?,
1471 xcm: xcm.try_into().map_err(|_| ())?,
1472 },
1473 LockAsset { asset, unlocker } => Self::LockAsset {
1474 asset: asset.try_into().map_err(|_| ())?,
1475 unlocker: unlocker.try_into().map_err(|_| ())?,
1476 },
1477 UnlockAsset { asset, target } => Self::UnlockAsset {
1478 asset: asset.try_into().map_err(|_| ())?,
1479 target: target.try_into().map_err(|_| ())?,
1480 },
1481 NoteUnlockable { asset, owner } => Self::NoteUnlockable {
1482 asset: asset.try_into().map_err(|_| ())?,
1483 owner: owner.try_into().map_err(|_| ())?,
1484 },
1485 RequestUnlock { asset, locker } => Self::RequestUnlock {
1486 asset: asset.try_into().map_err(|_| ())?,
1487 locker: locker.try_into().map_err(|_| ())?,
1488 },
1489 SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw },
1490 SetTopic(topic) => Self::SetTopic(topic),
1491 ClearTopic => Self::ClearTopic,
1492 AliasOrigin(location) => Self::AliasOrigin(location.try_into().map_err(|_| ())?),
1493 UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution {
1494 weight_limit,
1495 check_origin: check_origin
1496 .map(|location| location.try_into())
1497 .transpose()
1498 .map_err(|_| ())?,
1499 },
1500 })
1501 }
1502}
1503
1504#[cfg(test)]
1505mod tests {
1506 use super::{prelude::*, *};
1507 use crate::{
1508 v4::{
1509 AssetFilter as OldAssetFilter, Junctions::Here as OldHere, WildAsset as OldWildAsset,
1510 },
1511 MAX_INSTRUCTIONS_TO_DECODE,
1512 };
1513
1514 #[test]
1515 fn basic_roundtrip_works() {
1516 let xcm = Xcm::<()>(vec![TransferAsset {
1517 assets: (Here, 1u128).into(),
1518 beneficiary: Here.into(),
1519 }]);
1520 let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset {
1521 assets: (OldHere, 1u128).into(),
1522 beneficiary: OldHere.into(),
1523 }]);
1524 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1525 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1526 assert_eq!(new_xcm, xcm);
1527 }
1528
1529 #[test]
1530 fn teleport_roundtrip_works() {
1531 let xcm = Xcm::<()>(vec![
1532 ReceiveTeleportedAsset((Here, 1u128).into()),
1533 ClearOrigin,
1534 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1535 ]);
1536 let old_xcm: OldXcm<()> = OldXcm::<()>(vec![
1537 OldInstruction::ReceiveTeleportedAsset((OldHere, 1u128).into()),
1538 OldInstruction::ClearOrigin,
1539 OldInstruction::DepositAsset {
1540 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1541 beneficiary: OldHere.into(),
1542 },
1543 ]);
1544 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1545 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1546 assert_eq!(new_xcm, xcm);
1547 }
1548
1549 #[test]
1550 fn reserve_deposit_roundtrip_works() {
1551 let xcm = Xcm::<()>(vec![
1552 ReserveAssetDeposited((Here, 1u128).into()),
1553 ClearOrigin,
1554 BuyExecution {
1555 fees: (Here, 1u128).into(),
1556 weight_limit: Some(Weight::from_parts(1, 1)).into(),
1557 },
1558 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1559 ]);
1560 let old_xcm = OldXcm::<()>(vec![
1561 OldInstruction::ReserveAssetDeposited((OldHere, 1u128).into()),
1562 OldInstruction::ClearOrigin,
1563 OldInstruction::BuyExecution {
1564 fees: (OldHere, 1u128).into(),
1565 weight_limit: WeightLimit::Limited(Weight::from_parts(1, 1)),
1566 },
1567 OldInstruction::DepositAsset {
1568 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1569 beneficiary: OldHere.into(),
1570 },
1571 ]);
1572 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1573 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1574 assert_eq!(new_xcm, xcm);
1575 }
1576
1577 #[test]
1578 fn deposit_asset_roundtrip_works() {
1579 let xcm = Xcm::<()>(vec![
1580 WithdrawAsset((Here, 1u128).into()),
1581 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1582 ]);
1583 let old_xcm = OldXcm::<()>(vec![
1584 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1585 OldInstruction::DepositAsset {
1586 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1587 beneficiary: OldHere.into(),
1588 },
1589 ]);
1590 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1591 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1592 assert_eq!(new_xcm, xcm);
1593 }
1594
1595 #[test]
1596 fn deposit_reserve_asset_roundtrip_works() {
1597 let xcm = Xcm::<()>(vec![
1598 WithdrawAsset((Here, 1u128).into()),
1599 DepositReserveAsset {
1600 assets: Wild(AllCounted(1)),
1601 dest: Here.into(),
1602 xcm: Xcm::<()>(vec![]),
1603 },
1604 ]);
1605 let old_xcm = OldXcm::<()>(vec![
1606 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1607 OldInstruction::DepositReserveAsset {
1608 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1609 dest: OldHere.into(),
1610 xcm: OldXcm::<()>(vec![]),
1611 },
1612 ]);
1613 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1614 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1615 assert_eq!(new_xcm, xcm);
1616 }
1617
1618 #[test]
1619 fn transact_roundtrip_works() {
1620 let xcm = Xcm::<()>(vec![
1622 WithdrawAsset((Here, 1u128).into()),
1623 Transact {
1624 origin_kind: OriginKind::SovereignAccount,
1625 call: vec![200, 200, 200].into(),
1626 fallback_max_weight: Some(Weight::from_parts(1_000_000, 1_024)),
1627 },
1628 ]);
1629 let old_xcm = OldXcm::<()>(vec![
1630 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1631 OldInstruction::Transact {
1632 origin_kind: OriginKind::SovereignAccount,
1633 call: vec![200, 200, 200].into(),
1634 require_weight_at_most: Weight::from_parts(1_000_000, 1_024),
1635 },
1636 ]);
1637 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1638 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1639 assert_eq!(new_xcm, xcm);
1640
1641 let xcm_without_fallback = Xcm::<()>(vec![
1643 WithdrawAsset((Here, 1u128).into()),
1644 Transact {
1645 origin_kind: OriginKind::SovereignAccount,
1646 call: vec![200, 200, 200].into(),
1647 fallback_max_weight: None,
1648 },
1649 ]);
1650 let old_xcm = OldXcm::<()>(vec![
1651 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1652 OldInstruction::Transact {
1653 origin_kind: OriginKind::SovereignAccount,
1654 call: vec![200, 200, 200].into(),
1655 require_weight_at_most: Weight::MAX,
1656 },
1657 ]);
1658 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm_without_fallback.clone()).unwrap());
1659 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1660 let xcm_with_max_weight_fallback = Xcm::<()>(vec![
1661 WithdrawAsset((Here, 1u128).into()),
1662 Transact {
1663 origin_kind: OriginKind::SovereignAccount,
1664 call: vec![200, 200, 200].into(),
1665 fallback_max_weight: Some(Weight::MAX),
1666 },
1667 ]);
1668 assert_eq!(new_xcm, xcm_with_max_weight_fallback);
1669 }
1670
1671 #[test]
1672 fn decoding_respects_limit() {
1673 let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1674 let encoded = max_xcm.encode();
1675 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1676
1677 let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1678 let encoded = big_xcm.encode();
1679 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1680
1681 let nested_xcm = Xcm::<()>(vec![
1682 DepositReserveAsset {
1683 assets: All.into(),
1684 dest: Here.into(),
1685 xcm: max_xcm,
1686 };
1687 (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1688 ]);
1689 let encoded = nested_xcm.encode();
1690 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1691
1692 let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1693 let encoded = even_more_nested_xcm.encode();
1694 assert_eq!(encoded.len(), 342530);
1695 assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1697 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1698 }
1699}