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_with_mem_tracking_bound(Call: Decode))]
66#[scale_info(bounds(), skip_type_params(Call))]
67pub struct Xcm<Call>(pub Vec<Instruction<Call>>);
68
69impl<Call> Decode for Xcm<Call>
70where
71 Call: Decode,
72{
73 fn decode<I: CodecInput>(input: &mut I) -> core::result::Result<Self, CodecError> {
74 Ok(Xcm(decode_xcm_instructions(input)?))
75 }
76}
77
78impl<Call> Xcm<Call> {
79 pub fn new() -> Self {
81 Self(vec![])
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.0.is_empty()
87 }
88
89 pub fn len(&self) -> usize {
91 self.0.len()
92 }
93
94 pub fn inner(&self) -> &[Instruction<Call>] {
96 &self.0
97 }
98
99 pub fn inner_mut(&mut self) -> &mut Vec<Instruction<Call>> {
101 &mut self.0
102 }
103
104 pub fn into_inner(self) -> Vec<Instruction<Call>> {
106 self.0
107 }
108
109 pub fn iter(&self) -> impl Iterator<Item = &Instruction<Call>> {
111 self.0.iter()
112 }
113
114 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Instruction<Call>> {
116 self.0.iter_mut()
117 }
118
119 pub fn into_iter(self) -> impl Iterator<Item = Instruction<Call>> {
121 self.0.into_iter()
122 }
123
124 pub fn or_else(self, f: impl FnOnce() -> Self) -> Self {
127 if self.0.is_empty() {
128 f()
129 } else {
130 self
131 }
132 }
133
134 pub fn first(&self) -> Option<&Instruction<Call>> {
136 self.0.first()
137 }
138
139 pub fn last(&self) -> Option<&Instruction<Call>> {
141 self.0.last()
142 }
143
144 pub fn only(&self) -> Option<&Instruction<Call>> {
146 if self.0.len() == 1 {
147 self.0.first()
148 } else {
149 None
150 }
151 }
152
153 pub fn into_only(mut self) -> core::result::Result<Instruction<Call>, Self> {
156 if self.0.len() == 1 {
157 self.0.pop().ok_or(self)
158 } else {
159 Err(self)
160 }
161 }
162}
163
164impl<Call> From<Vec<Instruction<Call>>> for Xcm<Call> {
165 fn from(c: Vec<Instruction<Call>>) -> Self {
166 Self(c)
167 }
168}
169
170impl<Call> From<Xcm<Call>> for Vec<Instruction<Call>> {
171 fn from(c: Xcm<Call>) -> Self {
172 c.0
173 }
174}
175
176pub mod prelude {
178 mod contents {
179 pub use super::super::{
180 send_xcm, validate_send, Ancestor, AncestorThen, Asset,
181 AssetFilter::{self, *},
182 AssetId,
183 AssetInstance::{self, *},
184 Assets, BodyId, BodyPart, Error as XcmError, ExecuteXcm,
185 Fungibility::{self, *},
186 Hint::{self, *},
187 HintNumVariants,
188 Instruction::*,
189 InstructionError, InstructionIndex, InteriorLocation,
190 Junction::{self, *},
191 Junctions::{self, Here},
192 Location, MaxAssetTransferFilters, MaybeErrorCode,
193 NetworkId::{self, *},
194 OriginKind, Outcome, PalletInfo, Parent, ParentThen, PreparedMessage, QueryId,
195 QueryResponseInfo, Reanchorable, Response, Result as XcmResult, SendError, SendResult,
196 SendXcm, Weight,
197 WeightLimit::{self, *},
198 WildAsset::{self, *},
199 WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible},
200 XcmContext, XcmHash, XcmWeightInfo, VERSION as XCM_VERSION,
201 };
202 }
203 pub use super::{Instruction, Xcm};
204 pub use contents::*;
205 pub mod opaque {
206 pub use super::{
207 super::opaque::{Instruction, Xcm},
208 contents::*,
209 };
210 }
211}
212
213parameter_types! {
214 pub MaxPalletNameLen: u32 = 48;
215 pub MaxPalletsInfo: u32 = 64;
216 pub MaxAssetTransferFilters: u32 = 6;
217}
218
219#[derive(
220 Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen,
221)]
222pub struct PalletInfo {
223 #[codec(compact)]
224 pub index: u32,
225 pub name: BoundedVec<u8, MaxPalletNameLen>,
226 pub module_name: BoundedVec<u8, MaxPalletNameLen>,
227 #[codec(compact)]
228 pub major: u32,
229 #[codec(compact)]
230 pub minor: u32,
231 #[codec(compact)]
232 pub patch: u32,
233}
234
235impl TryInto<OldPalletInfo> for PalletInfo {
236 type Error = ();
237
238 fn try_into(self) -> result::Result<OldPalletInfo, Self::Error> {
239 OldPalletInfo::new(
240 self.index,
241 self.name.into_inner(),
242 self.module_name.into_inner(),
243 self.major,
244 self.minor,
245 self.patch,
246 )
247 .map_err(|_| ())
248 }
249}
250
251impl PalletInfo {
252 pub fn new(
253 index: u32,
254 name: Vec<u8>,
255 module_name: Vec<u8>,
256 major: u32,
257 minor: u32,
258 patch: u32,
259 ) -> result::Result<Self, Error> {
260 let name = BoundedVec::try_from(name).map_err(|_| Error::Overflow)?;
261 let module_name = BoundedVec::try_from(module_name).map_err(|_| Error::Overflow)?;
262
263 Ok(Self { index, name, module_name, major, minor, patch })
264 }
265}
266
267#[derive(
269 Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo, MaxEncodedLen,
270)]
271pub enum Response {
272 Null,
274 Assets(Assets),
276 ExecutionResult(Option<(u32, Error)>),
278 Version(super::Version),
280 PalletsInfo(BoundedVec<PalletInfo, MaxPalletsInfo>),
282 DispatchResult(MaybeErrorCode),
284}
285
286impl Default for Response {
287 fn default() -> Self {
288 Self::Null
289 }
290}
291
292impl TryFrom<OldResponse> for Response {
293 type Error = ();
294
295 fn try_from(old: OldResponse) -> result::Result<Self, Self::Error> {
296 use OldResponse::*;
297 Ok(match old {
298 Null => Self::Null,
299 Assets(assets) => Self::Assets(assets.try_into()?),
300 ExecutionResult(result) => Self::ExecutionResult(
301 result
302 .map(|(num, old_error)| (num, old_error.try_into()))
303 .map(|(num, result)| result.map(|inner| (num, inner)))
304 .transpose()?,
305 ),
306 Version(version) => Self::Version(version),
307 PalletsInfo(pallet_info) => {
308 let inner = pallet_info
309 .into_iter()
310 .map(TryInto::try_into)
311 .collect::<result::Result<Vec<_>, _>>()?;
312 Self::PalletsInfo(
313 BoundedVec::<PalletInfo, MaxPalletsInfo>::try_from(inner).map_err(|_| ())?,
314 )
315 },
316 DispatchResult(maybe_error) => Self::DispatchResult(maybe_error),
317 })
318 }
319}
320
321#[derive(Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
323pub struct QueryResponseInfo {
324 pub destination: Location,
326 #[codec(compact)]
328 pub query_id: QueryId,
329 pub max_weight: Weight,
331}
332
333impl TryFrom<OldQueryResponseInfo> for QueryResponseInfo {
334 type Error = ();
335
336 fn try_from(old: OldQueryResponseInfo) -> result::Result<Self, Self::Error> {
337 Ok(Self {
338 destination: old.destination.try_into()?,
339 query_id: old.query_id,
340 max_weight: old.max_weight,
341 })
342 }
343}
344
345#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)]
347pub struct XcmContext {
348 pub origin: Option<Location>,
350 pub message_id: XcmHash,
353 pub topic: Option<[u8; 32]>,
355}
356
357impl XcmContext {
358 pub fn with_message_id(message_id: XcmHash) -> XcmContext {
361 XcmContext { origin: None, message_id, topic: None }
362 }
363
364 pub fn topic_or_message_id(&self) -> XcmHash {
366 if let Some(id) = self.topic {
367 id.into()
368 } else {
369 self.message_id
370 }
371 }
372}
373
374#[derive(
383 Encode,
384 Decode,
385 DecodeWithMemTracking,
386 TypeInfo,
387 xcm_procedural::XcmWeightInfoTrait,
388 xcm_procedural::Builder,
389)]
390#[derive_where(Clone, Eq, PartialEq, Debug)]
391#[codec(encode_bound())]
392#[codec(decode_bound(Call: Decode))]
393#[codec(decode_with_mem_tracking_bound(Call: Decode))]
394#[scale_info(bounds(), skip_type_params(Call))]
395pub enum Instruction<Call> {
396 #[builder(loads_holding)]
405 WithdrawAsset(Assets),
406
407 #[builder(loads_holding)]
419 ReserveAssetDeposited(Assets),
420
421 #[builder(loads_holding)]
433 ReceiveTeleportedAsset(Assets),
434
435 QueryResponse {
453 #[codec(compact)]
454 query_id: QueryId,
455 response: Response,
456 max_weight: Weight,
457 querier: Option<Location>,
458 },
459
460 TransferAsset { assets: Assets, beneficiary: Location },
472
473 TransferReserveAsset { assets: Assets, dest: Location, xcm: Xcm<()> },
492
493 Transact {
511 origin_kind: OriginKind,
512 fallback_max_weight: Option<Weight>,
513 call: DoubleEncoded<Call>,
514 },
515
516 HrmpNewChannelOpenRequest {
528 #[codec(compact)]
529 sender: u32,
530 #[codec(compact)]
531 max_message_size: u32,
532 #[codec(compact)]
533 max_capacity: u32,
534 },
535
536 HrmpChannelAccepted {
546 #[codec(compact)]
549 recipient: u32,
550 },
551
552 HrmpChannelClosing {
563 #[codec(compact)]
564 initiator: u32,
565 #[codec(compact)]
566 sender: u32,
567 #[codec(compact)]
568 recipient: u32,
569 },
570
571 ClearOrigin,
583
584 DescendOrigin(InteriorLocation),
590
591 ReportError(QueryResponseInfo),
601
602 DepositAsset { assets: AssetFilter, beneficiary: Location },
612
613 DepositReserveAsset { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
630
631 ExchangeAsset { give: AssetFilter, want: Assets, maximal: bool },
647
648 InitiateReserveWithdraw { assets: AssetFilter, reserve: Location, xcm: Xcm<()> },
663
664 InitiateTeleport { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
679
680 ReportHolding { response_info: QueryResponseInfo, assets: AssetFilter },
693
694 #[builder(pays_fees)]
706 BuyExecution { fees: Asset, weight_limit: WeightLimit },
707
708 RefundSurplus,
714
715 SetErrorHandler(Xcm<Call>),
730
731 SetAppendix(Xcm<Call>),
746
747 ClearError,
753
754 #[builder(loads_holding)]
765 ClaimAsset { assets: Assets, ticket: Location },
766
767 Trap(#[codec(compact)] u64),
774
775 SubscribeVersion {
788 #[codec(compact)]
789 query_id: QueryId,
790 max_response_weight: Weight,
791 },
792
793 UnsubscribeVersion,
799
800 BurnAsset(Assets),
810
811 ExpectAsset(Assets),
818
819 ExpectOrigin(Option<Location>),
826
827 ExpectError(Option<(u32, Error)>),
834
835 ExpectTransactStatus(MaybeErrorCode),
844
845 QueryPallet { module_name: Vec<u8>, response_info: QueryResponseInfo },
860
861 ExpectPallet {
880 #[codec(compact)]
881 index: u32,
882 name: Vec<u8>,
883 module_name: Vec<u8>,
884 #[codec(compact)]
885 crate_major: u32,
886 #[codec(compact)]
887 min_crate_minor: u32,
888 },
889
890 ReportTransactStatus(QueryResponseInfo),
902
903 ClearTransactStatus,
911
912 UniversalOrigin(Junction),
926
927 ExportMessage { network: NetworkId, destination: InteriorLocation, xcm: Xcm<()> },
947
948 LockAsset { asset: Asset, unlocker: Location },
963
964 UnlockAsset { asset: Asset, target: Location },
976
977 NoteUnlockable { asset: Asset, owner: Location },
991
992 RequestUnlock { asset: Asset, locker: Location },
1005
1006 SetFeesMode { jit_withdraw: bool },
1015
1016 SetTopic([u8; 32]),
1028
1029 ClearTopic,
1035
1036 AliasOrigin(Location),
1042
1043 UnpaidExecution { weight_limit: WeightLimit, check_origin: Option<Location> },
1054
1055 #[builder(pays_fees)]
1061 PayFees { asset: Asset },
1062
1063 InitiateTransfer {
1110 destination: Location,
1111 remote_fees: Option<AssetTransferFilter>,
1112 preserve_origin: bool,
1113 assets: BoundedVec<AssetTransferFilter, MaxAssetTransferFilters>,
1114 remote_xcm: Xcm<()>,
1115 },
1116
1117 ExecuteWithOrigin { descendant_origin: Option<InteriorLocation>, xcm: Xcm<Call> },
1135
1136 SetHints { hints: BoundedVec<Hint, HintNumVariants> },
1145}
1146
1147#[derive(
1148 Encode,
1149 Decode,
1150 DecodeWithMemTracking,
1151 TypeInfo,
1152 Debug,
1153 PartialEq,
1154 Eq,
1155 Clone,
1156 xcm_procedural::NumVariants,
1157)]
1158pub enum Hint {
1159 AssetClaimer { location: Location },
1164}
1165
1166impl<Call> Xcm<Call> {
1167 pub fn into<C>(self) -> Xcm<C> {
1168 Xcm::from(self)
1169 }
1170 pub fn from<C>(xcm: Xcm<C>) -> Self {
1171 Self(xcm.0.into_iter().map(Instruction::<Call>::from).collect())
1172 }
1173}
1174
1175impl<Call> Instruction<Call> {
1176 pub fn into<C>(self) -> Instruction<C> {
1177 Instruction::from(self)
1178 }
1179 pub fn from<C>(xcm: Instruction<C>) -> Self {
1180 use Instruction::*;
1181 match xcm {
1182 WithdrawAsset(assets) => WithdrawAsset(assets),
1183 ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets),
1184 ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets),
1185 QueryResponse { query_id, response, max_weight, querier } => {
1186 QueryResponse { query_id, response, max_weight, querier }
1187 },
1188 TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
1189 TransferReserveAsset { assets, dest, xcm } => {
1190 TransferReserveAsset { assets, dest, xcm }
1191 },
1192 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => {
1193 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }
1194 },
1195 HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
1196 HrmpChannelClosing { initiator, sender, recipient } => {
1197 HrmpChannelClosing { initiator, sender, recipient }
1198 },
1199 Transact { origin_kind, call, fallback_max_weight } => {
1200 Transact { origin_kind, call: call.transmute_encoded(), fallback_max_weight }
1201 },
1202 ReportError(response_info) => ReportError(response_info),
1203 DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary },
1204 DepositReserveAsset { assets, dest, xcm } => DepositReserveAsset { assets, dest, xcm },
1205 ExchangeAsset { give, want, maximal } => ExchangeAsset { give, want, maximal },
1206 InitiateReserveWithdraw { assets, reserve, xcm } => {
1207 InitiateReserveWithdraw { assets, reserve, xcm }
1208 },
1209 InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm },
1210 ReportHolding { response_info, assets } => ReportHolding { response_info, assets },
1211 BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit },
1212 ClearOrigin => ClearOrigin,
1213 DescendOrigin(who) => DescendOrigin(who),
1214 RefundSurplus => RefundSurplus,
1215 SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
1216 SetAppendix(xcm) => SetAppendix(xcm.into()),
1217 ClearError => ClearError,
1218 SetHints { hints } => SetHints { hints },
1219 ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
1220 Trap(code) => Trap(code),
1221 SubscribeVersion { query_id, max_response_weight } => {
1222 SubscribeVersion { query_id, max_response_weight }
1223 },
1224 UnsubscribeVersion => UnsubscribeVersion,
1225 BurnAsset(assets) => BurnAsset(assets),
1226 ExpectAsset(assets) => ExpectAsset(assets),
1227 ExpectOrigin(origin) => ExpectOrigin(origin),
1228 ExpectError(error) => ExpectError(error),
1229 ExpectTransactStatus(transact_status) => ExpectTransactStatus(transact_status),
1230 QueryPallet { module_name, response_info } => {
1231 QueryPallet { module_name, response_info }
1232 },
1233 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1234 ExpectPallet { index, name, module_name, crate_major, min_crate_minor }
1235 },
1236 ReportTransactStatus(response_info) => ReportTransactStatus(response_info),
1237 ClearTransactStatus => ClearTransactStatus,
1238 UniversalOrigin(j) => UniversalOrigin(j),
1239 ExportMessage { network, destination, xcm } => {
1240 ExportMessage { network, destination, xcm }
1241 },
1242 LockAsset { asset, unlocker } => LockAsset { asset, unlocker },
1243 UnlockAsset { asset, target } => UnlockAsset { asset, target },
1244 NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner },
1245 RequestUnlock { asset, locker } => RequestUnlock { asset, locker },
1246 SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw },
1247 SetTopic(topic) => SetTopic(topic),
1248 ClearTopic => ClearTopic,
1249 AliasOrigin(location) => AliasOrigin(location),
1250 UnpaidExecution { weight_limit, check_origin } => {
1251 UnpaidExecution { weight_limit, check_origin }
1252 },
1253 PayFees { asset } => PayFees { asset },
1254 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => {
1255 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm }
1256 },
1257 ExecuteWithOrigin { descendant_origin, xcm } => {
1258 ExecuteWithOrigin { descendant_origin, xcm: xcm.into() }
1259 },
1260 }
1261 }
1262}
1263
1264impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
1266 fn weight(&self) -> Weight {
1267 use Instruction::*;
1268 match self {
1269 WithdrawAsset(assets) => W::withdraw_asset(assets),
1270 ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
1271 ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
1272 QueryResponse { query_id, response, max_weight, querier } => {
1273 W::query_response(query_id, response, max_weight, querier)
1274 },
1275 TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
1276 TransferReserveAsset { assets, dest, xcm } => {
1277 W::transfer_reserve_asset(&assets, dest, xcm)
1278 },
1279 Transact { origin_kind, fallback_max_weight, call } => {
1280 W::transact(origin_kind, fallback_max_weight, call)
1281 },
1282 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => {
1283 W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity)
1284 },
1285 HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
1286 HrmpChannelClosing { initiator, sender, recipient } => {
1287 W::hrmp_channel_closing(initiator, sender, recipient)
1288 },
1289 ClearOrigin => W::clear_origin(),
1290 DescendOrigin(who) => W::descend_origin(who),
1291 ReportError(response_info) => W::report_error(&response_info),
1292 DepositAsset { assets, beneficiary } => W::deposit_asset(assets, beneficiary),
1293 DepositReserveAsset { assets, dest, xcm } => {
1294 W::deposit_reserve_asset(assets, dest, xcm)
1295 },
1296 ExchangeAsset { give, want, maximal } => W::exchange_asset(give, want, maximal),
1297 InitiateReserveWithdraw { assets, reserve, xcm } => {
1298 W::initiate_reserve_withdraw(assets, reserve, xcm)
1299 },
1300 InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
1301 ReportHolding { response_info, assets } => W::report_holding(&response_info, &assets),
1302 BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
1303 RefundSurplus => W::refund_surplus(),
1304 SetErrorHandler(xcm) => W::set_error_handler(xcm),
1305 SetAppendix(xcm) => W::set_appendix(xcm),
1306 ClearError => W::clear_error(),
1307 SetHints { hints } => W::set_hints(hints),
1308 ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
1309 Trap(code) => W::trap(code),
1310 SubscribeVersion { query_id, max_response_weight } => {
1311 W::subscribe_version(query_id, max_response_weight)
1312 },
1313 UnsubscribeVersion => W::unsubscribe_version(),
1314 BurnAsset(assets) => W::burn_asset(assets),
1315 ExpectAsset(assets) => W::expect_asset(assets),
1316 ExpectOrigin(origin) => W::expect_origin(origin),
1317 ExpectError(error) => W::expect_error(error),
1318 ExpectTransactStatus(transact_status) => W::expect_transact_status(transact_status),
1319 QueryPallet { module_name, response_info } => {
1320 W::query_pallet(module_name, response_info)
1321 },
1322 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1323 W::expect_pallet(index, name, module_name, crate_major, min_crate_minor)
1324 },
1325 ReportTransactStatus(response_info) => W::report_transact_status(response_info),
1326 ClearTransactStatus => W::clear_transact_status(),
1327 UniversalOrigin(j) => W::universal_origin(j),
1328 ExportMessage { network, destination, xcm } => {
1329 W::export_message(network, destination, xcm)
1330 },
1331 LockAsset { asset, unlocker } => W::lock_asset(asset, unlocker),
1332 UnlockAsset { asset, target } => W::unlock_asset(asset, target),
1333 NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner),
1334 RequestUnlock { asset, locker } => W::request_unlock(asset, locker),
1335 SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw),
1336 SetTopic(topic) => W::set_topic(topic),
1337 ClearTopic => W::clear_topic(),
1338 AliasOrigin(location) => W::alias_origin(location),
1339 UnpaidExecution { weight_limit, check_origin } => {
1340 W::unpaid_execution(weight_limit, check_origin)
1341 },
1342 PayFees { asset } => W::pay_fees(asset),
1343 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } => {
1344 W::initiate_transfer(destination, remote_fees, preserve_origin, assets, remote_xcm)
1345 },
1346 ExecuteWithOrigin { descendant_origin, xcm } => {
1347 W::execute_with_origin(descendant_origin, xcm)
1348 },
1349 }
1350 }
1351}
1352
1353pub mod opaque {
1354 pub type Xcm = super::Xcm<()>;
1357
1358 pub type Instruction = super::Instruction<()>;
1361}
1362
1363impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
1365 type Error = ();
1366 fn try_from(old_xcm: OldXcm<Call>) -> result::Result<Self, Self::Error> {
1367 Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
1368 }
1369}
1370
1371impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
1373 type Error = ();
1374 fn try_from(old_instruction: OldInstruction<Call>) -> result::Result<Self, Self::Error> {
1375 use OldInstruction::*;
1376 Ok(match old_instruction {
1377 WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
1378 ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
1379 ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
1380 QueryResponse { query_id, response, max_weight, querier: Some(querier) } => {
1381 Self::QueryResponse {
1382 query_id,
1383 querier: querier.try_into()?,
1384 response: response.try_into()?,
1385 max_weight,
1386 }
1387 },
1388 QueryResponse { query_id, response, max_weight, querier: None } => {
1389 Self::QueryResponse {
1390 query_id,
1391 querier: None,
1392 response: response.try_into()?,
1393 max_weight,
1394 }
1395 },
1396 TransferAsset { assets, beneficiary } => Self::TransferAsset {
1397 assets: assets.try_into()?,
1398 beneficiary: beneficiary.try_into()?,
1399 },
1400 TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
1401 assets: assets.try_into()?,
1402 dest: dest.try_into()?,
1403 xcm: xcm.try_into()?,
1404 },
1405 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => {
1406 Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }
1407 },
1408 HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
1409 HrmpChannelClosing { initiator, sender, recipient } => {
1410 Self::HrmpChannelClosing { initiator, sender, recipient }
1411 },
1412 Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
1413 origin_kind,
1414 call: call.into(),
1415 fallback_max_weight: Some(require_weight_at_most),
1416 },
1417 ReportError(response_info) => Self::ReportError(QueryResponseInfo {
1418 query_id: response_info.query_id,
1419 destination: response_info.destination.try_into().map_err(|_| ())?,
1420 max_weight: response_info.max_weight,
1421 }),
1422 DepositAsset { assets, beneficiary } => {
1423 let beneficiary = beneficiary.try_into()?;
1424 let assets = assets.try_into()?;
1425 Self::DepositAsset { assets, beneficiary }
1426 },
1427 DepositReserveAsset { assets, dest, xcm } => {
1428 let dest = dest.try_into()?;
1429 let xcm = xcm.try_into()?;
1430 let assets = assets.try_into()?;
1431 Self::DepositReserveAsset { assets, dest, xcm }
1432 },
1433 ExchangeAsset { give, want, maximal } => {
1434 let give = give.try_into()?;
1435 let want = want.try_into()?;
1436 Self::ExchangeAsset { give, want, maximal }
1437 },
1438 InitiateReserveWithdraw { assets, reserve, xcm } => {
1439 let assets = assets.try_into()?;
1440 let reserve = reserve.try_into()?;
1441 let xcm = xcm.try_into()?;
1442 Self::InitiateReserveWithdraw { assets, reserve, xcm }
1443 },
1444 InitiateTeleport { assets, dest, xcm } => {
1445 let assets = assets.try_into()?;
1446 let dest = dest.try_into()?;
1447 let xcm = xcm.try_into()?;
1448 Self::InitiateTeleport { assets, dest, xcm }
1449 },
1450 ReportHolding { response_info, assets } => {
1451 let response_info = QueryResponseInfo {
1452 destination: response_info.destination.try_into().map_err(|_| ())?,
1453 query_id: response_info.query_id,
1454 max_weight: response_info.max_weight,
1455 };
1456 Self::ReportHolding { response_info, assets: assets.try_into()? }
1457 },
1458 BuyExecution { fees, weight_limit } => {
1459 let fees = fees.try_into()?;
1460 let weight_limit = weight_limit.into();
1461 Self::BuyExecution { fees, weight_limit }
1462 },
1463 ClearOrigin => Self::ClearOrigin,
1464 DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
1465 RefundSurplus => Self::RefundSurplus,
1466 SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
1467 SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
1468 ClearError => Self::ClearError,
1469 ClaimAsset { assets, ticket } => {
1470 let assets = assets.try_into()?;
1471 let ticket = ticket.try_into()?;
1472 Self::ClaimAsset { assets, ticket }
1473 },
1474 Trap(code) => Self::Trap(code),
1475 SubscribeVersion { query_id, max_response_weight } => {
1476 Self::SubscribeVersion { query_id, max_response_weight }
1477 },
1478 UnsubscribeVersion => Self::UnsubscribeVersion,
1479 BurnAsset(assets) => Self::BurnAsset(assets.try_into()?),
1480 ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?),
1481 ExpectOrigin(maybe_location) => Self::ExpectOrigin(
1482 maybe_location.map(|location| location.try_into()).transpose().map_err(|_| ())?,
1483 ),
1484 ExpectError(maybe_error) => Self::ExpectError(
1485 maybe_error
1486 .map(|(num, old_error)| (num, old_error.try_into()))
1487 .map(|(num, result)| result.map(|inner| (num, inner)))
1488 .transpose()
1489 .map_err(|_| ())?,
1490 ),
1491 ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code),
1492 QueryPallet { module_name, response_info } => Self::QueryPallet {
1493 module_name,
1494 response_info: response_info.try_into().map_err(|_| ())?,
1495 },
1496 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } => {
1497 Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor }
1498 },
1499 ReportTransactStatus(response_info) => {
1500 Self::ReportTransactStatus(response_info.try_into().map_err(|_| ())?)
1501 },
1502 ClearTransactStatus => Self::ClearTransactStatus,
1503 UniversalOrigin(junction) => {
1504 Self::UniversalOrigin(junction.try_into().map_err(|_| ())?)
1505 },
1506 ExportMessage { network, destination, xcm } => Self::ExportMessage {
1507 network: network.into(),
1508 destination: destination.try_into().map_err(|_| ())?,
1509 xcm: xcm.try_into().map_err(|_| ())?,
1510 },
1511 LockAsset { asset, unlocker } => Self::LockAsset {
1512 asset: asset.try_into().map_err(|_| ())?,
1513 unlocker: unlocker.try_into().map_err(|_| ())?,
1514 },
1515 UnlockAsset { asset, target } => Self::UnlockAsset {
1516 asset: asset.try_into().map_err(|_| ())?,
1517 target: target.try_into().map_err(|_| ())?,
1518 },
1519 NoteUnlockable { asset, owner } => Self::NoteUnlockable {
1520 asset: asset.try_into().map_err(|_| ())?,
1521 owner: owner.try_into().map_err(|_| ())?,
1522 },
1523 RequestUnlock { asset, locker } => Self::RequestUnlock {
1524 asset: asset.try_into().map_err(|_| ())?,
1525 locker: locker.try_into().map_err(|_| ())?,
1526 },
1527 SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw },
1528 SetTopic(topic) => Self::SetTopic(topic),
1529 ClearTopic => Self::ClearTopic,
1530 AliasOrigin(location) => Self::AliasOrigin(location.try_into().map_err(|_| ())?),
1531 UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution {
1532 weight_limit,
1533 check_origin: check_origin
1534 .map(|location| location.try_into())
1535 .transpose()
1536 .map_err(|_| ())?,
1537 },
1538 })
1539 }
1540}
1541
1542#[cfg(test)]
1543mod tests {
1544 use super::{prelude::*, *};
1545 use crate::{
1546 v4::{
1547 AssetFilter as OldAssetFilter, Junctions::Here as OldHere, WildAsset as OldWildAsset,
1548 },
1549 MAX_INSTRUCTIONS_TO_DECODE,
1550 };
1551
1552 #[test]
1553 fn basic_roundtrip_works() {
1554 let xcm = Xcm::<()>(vec![TransferAsset {
1555 assets: (Here, 1u128).into(),
1556 beneficiary: Here.into(),
1557 }]);
1558 let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset {
1559 assets: (OldHere, 1u128).into(),
1560 beneficiary: OldHere.into(),
1561 }]);
1562 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1563 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1564 assert_eq!(new_xcm, xcm);
1565 }
1566
1567 #[test]
1568 fn teleport_roundtrip_works() {
1569 let xcm = Xcm::<()>(vec![
1570 ReceiveTeleportedAsset((Here, 1u128).into()),
1571 ClearOrigin,
1572 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1573 ]);
1574 let old_xcm: OldXcm<()> = OldXcm::<()>(vec![
1575 OldInstruction::ReceiveTeleportedAsset((OldHere, 1u128).into()),
1576 OldInstruction::ClearOrigin,
1577 OldInstruction::DepositAsset {
1578 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1579 beneficiary: OldHere.into(),
1580 },
1581 ]);
1582 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1583 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1584 assert_eq!(new_xcm, xcm);
1585 }
1586
1587 #[test]
1588 fn reserve_deposit_roundtrip_works() {
1589 let xcm = Xcm::<()>(vec![
1590 ReserveAssetDeposited((Here, 1u128).into()),
1591 ClearOrigin,
1592 BuyExecution {
1593 fees: (Here, 1u128).into(),
1594 weight_limit: Some(Weight::from_parts(1, 1)).into(),
1595 },
1596 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1597 ]);
1598 let old_xcm = OldXcm::<()>(vec![
1599 OldInstruction::ReserveAssetDeposited((OldHere, 1u128).into()),
1600 OldInstruction::ClearOrigin,
1601 OldInstruction::BuyExecution {
1602 fees: (OldHere, 1u128).into(),
1603 weight_limit: WeightLimit::Limited(Weight::from_parts(1, 1)),
1604 },
1605 OldInstruction::DepositAsset {
1606 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1607 beneficiary: OldHere.into(),
1608 },
1609 ]);
1610 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1611 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1612 assert_eq!(new_xcm, xcm);
1613 }
1614
1615 #[test]
1616 fn deposit_asset_roundtrip_works() {
1617 let xcm = Xcm::<()>(vec![
1618 WithdrawAsset((Here, 1u128).into()),
1619 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1620 ]);
1621 let old_xcm = OldXcm::<()>(vec![
1622 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1623 OldInstruction::DepositAsset {
1624 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1625 beneficiary: OldHere.into(),
1626 },
1627 ]);
1628 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1629 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1630 assert_eq!(new_xcm, xcm);
1631 }
1632
1633 #[test]
1634 fn deposit_reserve_asset_roundtrip_works() {
1635 let xcm = Xcm::<()>(vec![
1636 WithdrawAsset((Here, 1u128).into()),
1637 DepositReserveAsset {
1638 assets: Wild(AllCounted(1)),
1639 dest: Here.into(),
1640 xcm: Xcm::<()>(vec![]),
1641 },
1642 ]);
1643 let old_xcm = OldXcm::<()>(vec![
1644 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1645 OldInstruction::DepositReserveAsset {
1646 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1647 dest: OldHere.into(),
1648 xcm: OldXcm::<()>(vec![]),
1649 },
1650 ]);
1651 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1652 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1653 assert_eq!(new_xcm, xcm);
1654 }
1655
1656 #[test]
1657 fn transact_roundtrip_works() {
1658 let xcm = Xcm::<()>(vec![
1660 WithdrawAsset((Here, 1u128).into()),
1661 Transact {
1662 origin_kind: OriginKind::SovereignAccount,
1663 call: vec![200, 200, 200].into(),
1664 fallback_max_weight: Some(Weight::from_parts(1_000_000, 1_024)),
1665 },
1666 ]);
1667 let old_xcm = OldXcm::<()>(vec![
1668 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1669 OldInstruction::Transact {
1670 origin_kind: OriginKind::SovereignAccount,
1671 call: vec![200, 200, 200].into(),
1672 require_weight_at_most: Weight::from_parts(1_000_000, 1_024),
1673 },
1674 ]);
1675 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1676 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1677 assert_eq!(new_xcm, xcm);
1678
1679 let xcm_without_fallback = Xcm::<()>(vec![
1681 WithdrawAsset((Here, 1u128).into()),
1682 Transact {
1683 origin_kind: OriginKind::SovereignAccount,
1684 call: vec![200, 200, 200].into(),
1685 fallback_max_weight: None,
1686 },
1687 ]);
1688 let old_xcm = OldXcm::<()>(vec![
1689 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1690 OldInstruction::Transact {
1691 origin_kind: OriginKind::SovereignAccount,
1692 call: vec![200, 200, 200].into(),
1693 require_weight_at_most: Weight::MAX,
1694 },
1695 ]);
1696 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm_without_fallback.clone()).unwrap());
1697 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1698 let xcm_with_max_weight_fallback = Xcm::<()>(vec![
1699 WithdrawAsset((Here, 1u128).into()),
1700 Transact {
1701 origin_kind: OriginKind::SovereignAccount,
1702 call: vec![200, 200, 200].into(),
1703 fallback_max_weight: Some(Weight::MAX),
1704 },
1705 ]);
1706 assert_eq!(new_xcm, xcm_with_max_weight_fallback);
1707 }
1708
1709 #[test]
1710 fn decoding_respects_limit() {
1711 let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1712 let encoded = max_xcm.encode();
1713 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1714
1715 let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1716 let encoded = big_xcm.encode();
1717 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1718
1719 let nested_xcm = Xcm::<()>(vec![
1720 DepositReserveAsset {
1721 assets: All.into(),
1722 dest: Here.into(),
1723 xcm: max_xcm,
1724 };
1725 (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1726 ]);
1727 let encoded = nested_xcm.encode();
1728 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1729
1730 let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1731 let encoded = even_more_nested_xcm.encode();
1732 assert_eq!(encoded.len(), 342530);
1733 assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1735 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1736 }
1737}