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 },
1185 TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
1186 TransferReserveAsset { assets, dest, xcm } => {
1187 TransferReserveAsset { assets, dest, xcm }
1188 },
1189 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => {
1190 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }
1191 },
1192 HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
1193 HrmpChannelClosing { initiator, sender, recipient } => {
1194 HrmpChannelClosing { initiator, sender, recipient }
1195 },
1196 Transact { origin_kind, call, fallback_max_weight } => {
1197 Transact { origin_kind, call: call.into(), fallback_max_weight }
1198 },
1199 ReportError(response_info) => ReportError(response_info),
1200 DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary },
1201 DepositReserveAsset { assets, dest, xcm } => DepositReserveAsset { assets, dest, xcm },
1202 ExchangeAsset { give, want, maximal } => ExchangeAsset { give, want, maximal },
1203 InitiateReserveWithdraw { assets, reserve, xcm } => {
1204 InitiateReserveWithdraw { assets, reserve, xcm }
1205 },
1206 InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm },
1207 ReportHolding { response_info, assets } => ReportHolding { response_info, assets },
1208 BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit },
1209 ClearOrigin => ClearOrigin,
1210 DescendOrigin(who) => DescendOrigin(who),
1211 RefundSurplus => RefundSurplus,
1212 SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
1213 SetAppendix(xcm) => SetAppendix(xcm.into()),
1214 ClearError => ClearError,
1215 SetHints { hints } => SetHints { hints },
1216 ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
1217 Trap(code) => Trap(code),
1218 SubscribeVersion { query_id, max_response_weight } => {
1219 SubscribeVersion { query_id, max_response_weight }
1220 },
1221 UnsubscribeVersion => UnsubscribeVersion,
1222 BurnAsset(assets) => BurnAsset(assets),
1223 ExpectAsset(assets) => ExpectAsset(assets),
1224 ExpectOrigin(origin) => ExpectOrigin(origin),
1225 ExpectError(error) => ExpectError(error),
1226 ExpectTransactStatus(transact_status) => ExpectTransactStatus(transact_status),
1227 QueryPallet { module_name, response_info } => {
1228 QueryPallet { module_name, response_info }
1229 },
1230 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1231 ExpectPallet { index, name, module_name, crate_major, min_crate_minor }
1232 },
1233 ReportTransactStatus(response_info) => ReportTransactStatus(response_info),
1234 ClearTransactStatus => ClearTransactStatus,
1235 UniversalOrigin(j) => UniversalOrigin(j),
1236 ExportMessage { network, destination, xcm } => {
1237 ExportMessage { network, destination, xcm }
1238 },
1239 LockAsset { asset, unlocker } => LockAsset { asset, unlocker },
1240 UnlockAsset { asset, target } => UnlockAsset { asset, target },
1241 NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner },
1242 RequestUnlock { asset, locker } => RequestUnlock { asset, locker },
1243 SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw },
1244 SetTopic(topic) => SetTopic(topic),
1245 ClearTopic => ClearTopic,
1246 AliasOrigin(location) => AliasOrigin(location),
1247 UnpaidExecution { weight_limit, check_origin } => {
1248 UnpaidExecution { weight_limit, check_origin }
1249 },
1250 PayFees { asset } => PayFees { asset },
1251 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => {
1252 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm }
1253 },
1254 ExecuteWithOrigin { descendant_origin, xcm } => {
1255 ExecuteWithOrigin { descendant_origin, xcm: xcm.into() }
1256 },
1257 }
1258 }
1259}
1260
1261impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
1263 fn weight(&self) -> Weight {
1264 use Instruction::*;
1265 match self {
1266 WithdrawAsset(assets) => W::withdraw_asset(assets),
1267 ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
1268 ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
1269 QueryResponse { query_id, response, max_weight, querier } => {
1270 W::query_response(query_id, response, max_weight, querier)
1271 },
1272 TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
1273 TransferReserveAsset { assets, dest, xcm } => {
1274 W::transfer_reserve_asset(&assets, dest, xcm)
1275 },
1276 Transact { origin_kind, fallback_max_weight, call } => {
1277 W::transact(origin_kind, fallback_max_weight, call)
1278 },
1279 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => {
1280 W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity)
1281 },
1282 HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
1283 HrmpChannelClosing { initiator, sender, recipient } => {
1284 W::hrmp_channel_closing(initiator, sender, recipient)
1285 },
1286 ClearOrigin => W::clear_origin(),
1287 DescendOrigin(who) => W::descend_origin(who),
1288 ReportError(response_info) => W::report_error(&response_info),
1289 DepositAsset { assets, beneficiary } => W::deposit_asset(assets, beneficiary),
1290 DepositReserveAsset { assets, dest, xcm } => {
1291 W::deposit_reserve_asset(assets, dest, xcm)
1292 },
1293 ExchangeAsset { give, want, maximal } => W::exchange_asset(give, want, maximal),
1294 InitiateReserveWithdraw { assets, reserve, xcm } => {
1295 W::initiate_reserve_withdraw(assets, reserve, xcm)
1296 },
1297 InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
1298 ReportHolding { response_info, assets } => W::report_holding(&response_info, &assets),
1299 BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
1300 RefundSurplus => W::refund_surplus(),
1301 SetErrorHandler(xcm) => W::set_error_handler(xcm),
1302 SetAppendix(xcm) => W::set_appendix(xcm),
1303 ClearError => W::clear_error(),
1304 SetHints { hints } => W::set_hints(hints),
1305 ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
1306 Trap(code) => W::trap(code),
1307 SubscribeVersion { query_id, max_response_weight } => {
1308 W::subscribe_version(query_id, max_response_weight)
1309 },
1310 UnsubscribeVersion => W::unsubscribe_version(),
1311 BurnAsset(assets) => W::burn_asset(assets),
1312 ExpectAsset(assets) => W::expect_asset(assets),
1313 ExpectOrigin(origin) => W::expect_origin(origin),
1314 ExpectError(error) => W::expect_error(error),
1315 ExpectTransactStatus(transact_status) => W::expect_transact_status(transact_status),
1316 QueryPallet { module_name, response_info } => {
1317 W::query_pallet(module_name, response_info)
1318 },
1319 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1320 W::expect_pallet(index, name, module_name, crate_major, min_crate_minor)
1321 },
1322 ReportTransactStatus(response_info) => W::report_transact_status(response_info),
1323 ClearTransactStatus => W::clear_transact_status(),
1324 UniversalOrigin(j) => W::universal_origin(j),
1325 ExportMessage { network, destination, xcm } => {
1326 W::export_message(network, destination, xcm)
1327 },
1328 LockAsset { asset, unlocker } => W::lock_asset(asset, unlocker),
1329 UnlockAsset { asset, target } => W::unlock_asset(asset, target),
1330 NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner),
1331 RequestUnlock { asset, locker } => W::request_unlock(asset, locker),
1332 SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw),
1333 SetTopic(topic) => W::set_topic(topic),
1334 ClearTopic => W::clear_topic(),
1335 AliasOrigin(location) => W::alias_origin(location),
1336 UnpaidExecution { weight_limit, check_origin } => {
1337 W::unpaid_execution(weight_limit, check_origin)
1338 },
1339 PayFees { asset } => W::pay_fees(asset),
1340 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => {
1341 W::initiate_transfer(destination, remote_fees, preserve_origin, assets, remote_xcm)
1342 },
1343 ExecuteWithOrigin { descendant_origin, xcm } => {
1344 W::execute_with_origin(descendant_origin, xcm)
1345 },
1346 }
1347 }
1348}
1349
1350pub mod opaque {
1351 pub type Xcm = super::Xcm<()>;
1354
1355 pub type Instruction = super::Instruction<()>;
1358}
1359
1360impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
1362 type Error = ();
1363 fn try_from(old_xcm: OldXcm<Call>) -> result::Result<Self, Self::Error> {
1364 Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
1365 }
1366}
1367
1368impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
1370 type Error = ();
1371 fn try_from(old_instruction: OldInstruction<Call>) -> result::Result<Self, Self::Error> {
1372 use OldInstruction::*;
1373 Ok(match old_instruction {
1374 WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
1375 ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
1376 ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
1377 QueryResponse { query_id, response, max_weight, querier: Some(querier) } => {
1378 Self::QueryResponse {
1379 query_id,
1380 querier: querier.try_into()?,
1381 response: response.try_into()?,
1382 max_weight,
1383 }
1384 },
1385 QueryResponse { query_id, response, max_weight, querier: None } => {
1386 Self::QueryResponse {
1387 query_id,
1388 querier: None,
1389 response: response.try_into()?,
1390 max_weight,
1391 }
1392 },
1393 TransferAsset { assets, beneficiary } => Self::TransferAsset {
1394 assets: assets.try_into()?,
1395 beneficiary: beneficiary.try_into()?,
1396 },
1397 TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
1398 assets: assets.try_into()?,
1399 dest: dest.try_into()?,
1400 xcm: xcm.try_into()?,
1401 },
1402 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => {
1403 Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }
1404 },
1405 HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
1406 HrmpChannelClosing { initiator, sender, recipient } => {
1407 Self::HrmpChannelClosing { initiator, sender, recipient }
1408 },
1409 Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
1410 origin_kind,
1411 call: call.into(),
1412 fallback_max_weight: Some(require_weight_at_most),
1413 },
1414 ReportError(response_info) => Self::ReportError(QueryResponseInfo {
1415 query_id: response_info.query_id,
1416 destination: response_info.destination.try_into().map_err(|_| ())?,
1417 max_weight: response_info.max_weight,
1418 }),
1419 DepositAsset { assets, beneficiary } => {
1420 let beneficiary = beneficiary.try_into()?;
1421 let assets = assets.try_into()?;
1422 Self::DepositAsset { assets, beneficiary }
1423 },
1424 DepositReserveAsset { assets, dest, xcm } => {
1425 let dest = dest.try_into()?;
1426 let xcm = xcm.try_into()?;
1427 let assets = assets.try_into()?;
1428 Self::DepositReserveAsset { assets, dest, xcm }
1429 },
1430 ExchangeAsset { give, want, maximal } => {
1431 let give = give.try_into()?;
1432 let want = want.try_into()?;
1433 Self::ExchangeAsset { give, want, maximal }
1434 },
1435 InitiateReserveWithdraw { assets, reserve, xcm } => {
1436 let assets = assets.try_into()?;
1437 let reserve = reserve.try_into()?;
1438 let xcm = xcm.try_into()?;
1439 Self::InitiateReserveWithdraw { assets, reserve, xcm }
1440 },
1441 InitiateTeleport { assets, dest, xcm } => {
1442 let assets = assets.try_into()?;
1443 let dest = dest.try_into()?;
1444 let xcm = xcm.try_into()?;
1445 Self::InitiateTeleport { assets, dest, xcm }
1446 },
1447 ReportHolding { response_info, assets } => {
1448 let response_info = QueryResponseInfo {
1449 destination: response_info.destination.try_into().map_err(|_| ())?,
1450 query_id: response_info.query_id,
1451 max_weight: response_info.max_weight,
1452 };
1453 Self::ReportHolding { response_info, assets: assets.try_into()? }
1454 },
1455 BuyExecution { fees, weight_limit } => {
1456 let fees = fees.try_into()?;
1457 let weight_limit = weight_limit.into();
1458 Self::BuyExecution { fees, weight_limit }
1459 },
1460 ClearOrigin => Self::ClearOrigin,
1461 DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
1462 RefundSurplus => Self::RefundSurplus,
1463 SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
1464 SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
1465 ClearError => Self::ClearError,
1466 ClaimAsset { assets, ticket } => {
1467 let assets = assets.try_into()?;
1468 let ticket = ticket.try_into()?;
1469 Self::ClaimAsset { assets, ticket }
1470 },
1471 Trap(code) => Self::Trap(code),
1472 SubscribeVersion { query_id, max_response_weight } => {
1473 Self::SubscribeVersion { query_id, max_response_weight }
1474 },
1475 UnsubscribeVersion => Self::UnsubscribeVersion,
1476 BurnAsset(assets) => Self::BurnAsset(assets.try_into()?),
1477 ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?),
1478 ExpectOrigin(maybe_location) => Self::ExpectOrigin(
1479 maybe_location.map(|location| location.try_into()).transpose().map_err(|_| ())?,
1480 ),
1481 ExpectError(maybe_error) => Self::ExpectError(
1482 maybe_error
1483 .map(|(num, old_error)| (num, old_error.try_into()))
1484 .map(|(num, result)| result.map(|inner| (num, inner)))
1485 .transpose()
1486 .map_err(|_| ())?,
1487 ),
1488 ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code),
1489 QueryPallet { module_name, response_info } => Self::QueryPallet {
1490 module_name,
1491 response_info: response_info.try_into().map_err(|_| ())?,
1492 },
1493 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1494 Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor }
1495 },
1496 ReportTransactStatus(response_info) => {
1497 Self::ReportTransactStatus(response_info.try_into().map_err(|_| ())?)
1498 },
1499 ClearTransactStatus => Self::ClearTransactStatus,
1500 UniversalOrigin(junction) => {
1501 Self::UniversalOrigin(junction.try_into().map_err(|_| ())?)
1502 },
1503 ExportMessage { network, destination, xcm } => Self::ExportMessage {
1504 network: network.into(),
1505 destination: destination.try_into().map_err(|_| ())?,
1506 xcm: xcm.try_into().map_err(|_| ())?,
1507 },
1508 LockAsset { asset, unlocker } => Self::LockAsset {
1509 asset: asset.try_into().map_err(|_| ())?,
1510 unlocker: unlocker.try_into().map_err(|_| ())?,
1511 },
1512 UnlockAsset { asset, target } => Self::UnlockAsset {
1513 asset: asset.try_into().map_err(|_| ())?,
1514 target: target.try_into().map_err(|_| ())?,
1515 },
1516 NoteUnlockable { asset, owner } => Self::NoteUnlockable {
1517 asset: asset.try_into().map_err(|_| ())?,
1518 owner: owner.try_into().map_err(|_| ())?,
1519 },
1520 RequestUnlock { asset, locker } => Self::RequestUnlock {
1521 asset: asset.try_into().map_err(|_| ())?,
1522 locker: locker.try_into().map_err(|_| ())?,
1523 },
1524 SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw },
1525 SetTopic(topic) => Self::SetTopic(topic),
1526 ClearTopic => Self::ClearTopic,
1527 AliasOrigin(location) => Self::AliasOrigin(location.try_into().map_err(|_| ())?),
1528 UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution {
1529 weight_limit,
1530 check_origin: check_origin
1531 .map(|location| location.try_into())
1532 .transpose()
1533 .map_err(|_| ())?,
1534 },
1535 })
1536 }
1537}
1538
1539#[cfg(test)]
1540mod tests {
1541 use super::{prelude::*, *};
1542 use crate::{
1543 v4::{
1544 AssetFilter as OldAssetFilter, Junctions::Here as OldHere, WildAsset as OldWildAsset,
1545 },
1546 MAX_INSTRUCTIONS_TO_DECODE,
1547 };
1548
1549 #[test]
1550 fn basic_roundtrip_works() {
1551 let xcm = Xcm::<()>(vec![TransferAsset {
1552 assets: (Here, 1u128).into(),
1553 beneficiary: Here.into(),
1554 }]);
1555 let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset {
1556 assets: (OldHere, 1u128).into(),
1557 beneficiary: OldHere.into(),
1558 }]);
1559 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1560 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1561 assert_eq!(new_xcm, xcm);
1562 }
1563
1564 #[test]
1565 fn teleport_roundtrip_works() {
1566 let xcm = Xcm::<()>(vec![
1567 ReceiveTeleportedAsset((Here, 1u128).into()),
1568 ClearOrigin,
1569 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1570 ]);
1571 let old_xcm: OldXcm<()> = OldXcm::<()>(vec![
1572 OldInstruction::ReceiveTeleportedAsset((OldHere, 1u128).into()),
1573 OldInstruction::ClearOrigin,
1574 OldInstruction::DepositAsset {
1575 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1576 beneficiary: OldHere.into(),
1577 },
1578 ]);
1579 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1580 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1581 assert_eq!(new_xcm, xcm);
1582 }
1583
1584 #[test]
1585 fn reserve_deposit_roundtrip_works() {
1586 let xcm = Xcm::<()>(vec![
1587 ReserveAssetDeposited((Here, 1u128).into()),
1588 ClearOrigin,
1589 BuyExecution {
1590 fees: (Here, 1u128).into(),
1591 weight_limit: Some(Weight::from_parts(1, 1)).into(),
1592 },
1593 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1594 ]);
1595 let old_xcm = OldXcm::<()>(vec![
1596 OldInstruction::ReserveAssetDeposited((OldHere, 1u128).into()),
1597 OldInstruction::ClearOrigin,
1598 OldInstruction::BuyExecution {
1599 fees: (OldHere, 1u128).into(),
1600 weight_limit: WeightLimit::Limited(Weight::from_parts(1, 1)),
1601 },
1602 OldInstruction::DepositAsset {
1603 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1604 beneficiary: OldHere.into(),
1605 },
1606 ]);
1607 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1608 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1609 assert_eq!(new_xcm, xcm);
1610 }
1611
1612 #[test]
1613 fn deposit_asset_roundtrip_works() {
1614 let xcm = Xcm::<()>(vec![
1615 WithdrawAsset((Here, 1u128).into()),
1616 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1617 ]);
1618 let old_xcm = OldXcm::<()>(vec![
1619 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1620 OldInstruction::DepositAsset {
1621 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1622 beneficiary: OldHere.into(),
1623 },
1624 ]);
1625 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1626 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1627 assert_eq!(new_xcm, xcm);
1628 }
1629
1630 #[test]
1631 fn deposit_reserve_asset_roundtrip_works() {
1632 let xcm = Xcm::<()>(vec![
1633 WithdrawAsset((Here, 1u128).into()),
1634 DepositReserveAsset {
1635 assets: Wild(AllCounted(1)),
1636 dest: Here.into(),
1637 xcm: Xcm::<()>(vec![]),
1638 },
1639 ]);
1640 let old_xcm = OldXcm::<()>(vec![
1641 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1642 OldInstruction::DepositReserveAsset {
1643 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1644 dest: OldHere.into(),
1645 xcm: OldXcm::<()>(vec![]),
1646 },
1647 ]);
1648 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1649 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1650 assert_eq!(new_xcm, xcm);
1651 }
1652
1653 #[test]
1654 fn transact_roundtrip_works() {
1655 let xcm = Xcm::<()>(vec![
1657 WithdrawAsset((Here, 1u128).into()),
1658 Transact {
1659 origin_kind: OriginKind::SovereignAccount,
1660 call: vec![200, 200, 200].into(),
1661 fallback_max_weight: Some(Weight::from_parts(1_000_000, 1_024)),
1662 },
1663 ]);
1664 let old_xcm = OldXcm::<()>(vec![
1665 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1666 OldInstruction::Transact {
1667 origin_kind: OriginKind::SovereignAccount,
1668 call: vec![200, 200, 200].into(),
1669 require_weight_at_most: Weight::from_parts(1_000_000, 1_024),
1670 },
1671 ]);
1672 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1673 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1674 assert_eq!(new_xcm, xcm);
1675
1676 let xcm_without_fallback = Xcm::<()>(vec![
1678 WithdrawAsset((Here, 1u128).into()),
1679 Transact {
1680 origin_kind: OriginKind::SovereignAccount,
1681 call: vec![200, 200, 200].into(),
1682 fallback_max_weight: None,
1683 },
1684 ]);
1685 let old_xcm = OldXcm::<()>(vec![
1686 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1687 OldInstruction::Transact {
1688 origin_kind: OriginKind::SovereignAccount,
1689 call: vec![200, 200, 200].into(),
1690 require_weight_at_most: Weight::MAX,
1691 },
1692 ]);
1693 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm_without_fallback.clone()).unwrap());
1694 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1695 let xcm_with_max_weight_fallback = Xcm::<()>(vec![
1696 WithdrawAsset((Here, 1u128).into()),
1697 Transact {
1698 origin_kind: OriginKind::SovereignAccount,
1699 call: vec![200, 200, 200].into(),
1700 fallback_max_weight: Some(Weight::MAX),
1701 },
1702 ]);
1703 assert_eq!(new_xcm, xcm_with_max_weight_fallback);
1704 }
1705
1706 #[test]
1707 fn decoding_respects_limit() {
1708 let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1709 let encoded = max_xcm.encode();
1710 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1711
1712 let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1713 let encoded = big_xcm.encode();
1714 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1715
1716 let nested_xcm = Xcm::<()>(vec![
1717 DepositReserveAsset {
1718 assets: All.into(),
1719 dest: Here.into(),
1720 xcm: max_xcm,
1721 };
1722 (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1723 ]);
1724 let encoded = nested_xcm.encode();
1725 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1726
1727 let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1728 let encoded = even_more_nested_xcm.encode();
1729 assert_eq!(encoded.len(), 342530);
1730 assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1732 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1733 }
1734}