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, Outcome, PreparedMessage, Reanchorable, Result,
52 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 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
362#[derive(
371 Encode,
372 Decode,
373 DecodeWithMemTracking,
374 TypeInfo,
375 xcm_procedural::XcmWeightInfoTrait,
376 xcm_procedural::Builder,
377)]
378#[derive_where(Clone, Eq, PartialEq, Debug)]
379#[codec(encode_bound())]
380#[codec(decode_bound())]
381#[codec(decode_with_mem_tracking_bound())]
382#[scale_info(bounds(), skip_type_params(Call))]
383pub enum Instruction<Call> {
384 #[builder(loads_holding)]
393 WithdrawAsset(Assets),
394
395 #[builder(loads_holding)]
407 ReserveAssetDeposited(Assets),
408
409 #[builder(loads_holding)]
421 ReceiveTeleportedAsset(Assets),
422
423 QueryResponse {
441 #[codec(compact)]
442 query_id: QueryId,
443 response: Response,
444 max_weight: Weight,
445 querier: Option<Location>,
446 },
447
448 TransferAsset { assets: Assets, beneficiary: Location },
460
461 TransferReserveAsset { assets: Assets, dest: Location, xcm: Xcm<()> },
480
481 Transact {
499 origin_kind: OriginKind,
500 fallback_max_weight: Option<Weight>,
501 call: DoubleEncoded<Call>,
502 },
503
504 HrmpNewChannelOpenRequest {
516 #[codec(compact)]
517 sender: u32,
518 #[codec(compact)]
519 max_message_size: u32,
520 #[codec(compact)]
521 max_capacity: u32,
522 },
523
524 HrmpChannelAccepted {
534 #[codec(compact)]
537 recipient: u32,
538 },
539
540 HrmpChannelClosing {
551 #[codec(compact)]
552 initiator: u32,
553 #[codec(compact)]
554 sender: u32,
555 #[codec(compact)]
556 recipient: u32,
557 },
558
559 ClearOrigin,
571
572 DescendOrigin(InteriorLocation),
578
579 ReportError(QueryResponseInfo),
589
590 DepositAsset { assets: AssetFilter, beneficiary: Location },
600
601 DepositReserveAsset { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
618
619 ExchangeAsset { give: AssetFilter, want: Assets, maximal: bool },
635
636 InitiateReserveWithdraw { assets: AssetFilter, reserve: Location, xcm: Xcm<()> },
651
652 InitiateTeleport { assets: AssetFilter, dest: Location, xcm: Xcm<()> },
667
668 ReportHolding { response_info: QueryResponseInfo, assets: AssetFilter },
681
682 #[builder(pays_fees)]
694 BuyExecution { fees: Asset, weight_limit: WeightLimit },
695
696 RefundSurplus,
702
703 SetErrorHandler(Xcm<Call>),
718
719 SetAppendix(Xcm<Call>),
734
735 ClearError,
741
742 #[builder(loads_holding)]
753 ClaimAsset { assets: Assets, ticket: Location },
754
755 Trap(#[codec(compact)] u64),
762
763 SubscribeVersion {
776 #[codec(compact)]
777 query_id: QueryId,
778 max_response_weight: Weight,
779 },
780
781 UnsubscribeVersion,
787
788 BurnAsset(Assets),
798
799 ExpectAsset(Assets),
806
807 ExpectOrigin(Option<Location>),
814
815 ExpectError(Option<(u32, Error)>),
822
823 ExpectTransactStatus(MaybeErrorCode),
832
833 QueryPallet { module_name: Vec<u8>, response_info: QueryResponseInfo },
848
849 ExpectPallet {
868 #[codec(compact)]
869 index: u32,
870 name: Vec<u8>,
871 module_name: Vec<u8>,
872 #[codec(compact)]
873 crate_major: u32,
874 #[codec(compact)]
875 min_crate_minor: u32,
876 },
877
878 ReportTransactStatus(QueryResponseInfo),
890
891 ClearTransactStatus,
899
900 UniversalOrigin(Junction),
914
915 ExportMessage { network: NetworkId, destination: InteriorLocation, xcm: Xcm<()> },
935
936 LockAsset { asset: Asset, unlocker: Location },
951
952 UnlockAsset { asset: Asset, target: Location },
964
965 NoteUnlockable { asset: Asset, owner: Location },
979
980 RequestUnlock { asset: Asset, locker: Location },
993
994 SetFeesMode { jit_withdraw: bool },
1003
1004 SetTopic([u8; 32]),
1016
1017 ClearTopic,
1023
1024 AliasOrigin(Location),
1030
1031 UnpaidExecution { weight_limit: WeightLimit, check_origin: Option<Location> },
1042
1043 #[builder(pays_fees)]
1049 PayFees { asset: Asset },
1050
1051 InitiateTransfer {
1098 destination: Location,
1099 remote_fees: Option<AssetTransferFilter>,
1100 preserve_origin: bool,
1101 assets: BoundedVec<AssetTransferFilter, MaxAssetTransferFilters>,
1102 remote_xcm: Xcm<()>,
1103 },
1104
1105 ExecuteWithOrigin { descendant_origin: Option<InteriorLocation>, xcm: Xcm<Call> },
1123
1124 SetHints { hints: BoundedVec<Hint, HintNumVariants> },
1133}
1134
1135#[derive(
1136 Encode,
1137 Decode,
1138 DecodeWithMemTracking,
1139 TypeInfo,
1140 Debug,
1141 PartialEq,
1142 Eq,
1143 Clone,
1144 xcm_procedural::NumVariants,
1145)]
1146pub enum Hint {
1147 AssetClaimer { location: Location },
1152}
1153
1154impl<Call> Xcm<Call> {
1155 pub fn into<C>(self) -> Xcm<C> {
1156 Xcm::from(self)
1157 }
1158 pub fn from<C>(xcm: Xcm<C>) -> Self {
1159 Self(xcm.0.into_iter().map(Instruction::<Call>::from).collect())
1160 }
1161}
1162
1163impl<Call> Instruction<Call> {
1164 pub fn into<C>(self) -> Instruction<C> {
1165 Instruction::from(self)
1166 }
1167 pub fn from<C>(xcm: Instruction<C>) -> Self {
1168 use Instruction::*;
1169 match xcm {
1170 WithdrawAsset(assets) => WithdrawAsset(assets),
1171 ReserveAssetDeposited(assets) => ReserveAssetDeposited(assets),
1172 ReceiveTeleportedAsset(assets) => ReceiveTeleportedAsset(assets),
1173 QueryResponse { query_id, response, max_weight, querier } =>
1174 QueryResponse { query_id, response, max_weight, querier },
1175 TransferAsset { assets, beneficiary } => TransferAsset { assets, beneficiary },
1176 TransferReserveAsset { assets, dest, xcm } =>
1177 TransferReserveAsset { assets, dest, xcm },
1178 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1179 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
1180 HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient },
1181 HrmpChannelClosing { initiator, sender, recipient } =>
1182 HrmpChannelClosing { initiator, sender, recipient },
1183 Transact { origin_kind, call, fallback_max_weight } =>
1184 Transact { origin_kind, call: call.into(), fallback_max_weight },
1185 ReportError(response_info) => ReportError(response_info),
1186 DepositAsset { assets, beneficiary } => DepositAsset { assets, beneficiary },
1187 DepositReserveAsset { assets, dest, xcm } => DepositReserveAsset { assets, dest, xcm },
1188 ExchangeAsset { give, want, maximal } => ExchangeAsset { give, want, maximal },
1189 InitiateReserveWithdraw { assets, reserve, xcm } =>
1190 InitiateReserveWithdraw { assets, reserve, xcm },
1191 InitiateTeleport { assets, dest, xcm } => InitiateTeleport { assets, dest, xcm },
1192 ReportHolding { response_info, assets } => ReportHolding { response_info, assets },
1193 BuyExecution { fees, weight_limit } => BuyExecution { fees, weight_limit },
1194 ClearOrigin => ClearOrigin,
1195 DescendOrigin(who) => DescendOrigin(who),
1196 RefundSurplus => RefundSurplus,
1197 SetErrorHandler(xcm) => SetErrorHandler(xcm.into()),
1198 SetAppendix(xcm) => SetAppendix(xcm.into()),
1199 ClearError => ClearError,
1200 SetHints { hints } => SetHints { hints },
1201 ClaimAsset { assets, ticket } => ClaimAsset { assets, ticket },
1202 Trap(code) => Trap(code),
1203 SubscribeVersion { query_id, max_response_weight } =>
1204 SubscribeVersion { query_id, max_response_weight },
1205 UnsubscribeVersion => UnsubscribeVersion,
1206 BurnAsset(assets) => BurnAsset(assets),
1207 ExpectAsset(assets) => ExpectAsset(assets),
1208 ExpectOrigin(origin) => ExpectOrigin(origin),
1209 ExpectError(error) => ExpectError(error),
1210 ExpectTransactStatus(transact_status) => ExpectTransactStatus(transact_status),
1211 QueryPallet { module_name, response_info } =>
1212 QueryPallet { module_name, response_info },
1213 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1214 ExpectPallet { index, name, module_name, crate_major, min_crate_minor },
1215 ReportTransactStatus(response_info) => ReportTransactStatus(response_info),
1216 ClearTransactStatus => ClearTransactStatus,
1217 UniversalOrigin(j) => UniversalOrigin(j),
1218 ExportMessage { network, destination, xcm } =>
1219 ExportMessage { network, destination, xcm },
1220 LockAsset { asset, unlocker } => LockAsset { asset, unlocker },
1221 UnlockAsset { asset, target } => UnlockAsset { asset, target },
1222 NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner },
1223 RequestUnlock { asset, locker } => RequestUnlock { asset, locker },
1224 SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw },
1225 SetTopic(topic) => SetTopic(topic),
1226 ClearTopic => ClearTopic,
1227 AliasOrigin(location) => AliasOrigin(location),
1228 UnpaidExecution { weight_limit, check_origin } =>
1229 UnpaidExecution { weight_limit, check_origin },
1230 PayFees { asset } => PayFees { asset },
1231 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } =>
1232 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm },
1233 ExecuteWithOrigin { descendant_origin, xcm } =>
1234 ExecuteWithOrigin { descendant_origin, xcm: xcm.into() },
1235 }
1236 }
1237}
1238
1239impl<Call, W: XcmWeightInfo<Call>> GetWeight<W> for Instruction<Call> {
1241 fn weight(&self) -> Weight {
1242 use Instruction::*;
1243 match self {
1244 WithdrawAsset(assets) => W::withdraw_asset(assets),
1245 ReserveAssetDeposited(assets) => W::reserve_asset_deposited(assets),
1246 ReceiveTeleportedAsset(assets) => W::receive_teleported_asset(assets),
1247 QueryResponse { query_id, response, max_weight, querier } =>
1248 W::query_response(query_id, response, max_weight, querier),
1249 TransferAsset { assets, beneficiary } => W::transfer_asset(assets, beneficiary),
1250 TransferReserveAsset { assets, dest, xcm } =>
1251 W::transfer_reserve_asset(&assets, dest, xcm),
1252 Transact { origin_kind, fallback_max_weight, call } =>
1253 W::transact(origin_kind, fallback_max_weight, call),
1254 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1255 W::hrmp_new_channel_open_request(sender, max_message_size, max_capacity),
1256 HrmpChannelAccepted { recipient } => W::hrmp_channel_accepted(recipient),
1257 HrmpChannelClosing { initiator, sender, recipient } =>
1258 W::hrmp_channel_closing(initiator, sender, recipient),
1259 ClearOrigin => W::clear_origin(),
1260 DescendOrigin(who) => W::descend_origin(who),
1261 ReportError(response_info) => W::report_error(&response_info),
1262 DepositAsset { assets, beneficiary } => W::deposit_asset(assets, beneficiary),
1263 DepositReserveAsset { assets, dest, xcm } =>
1264 W::deposit_reserve_asset(assets, dest, xcm),
1265 ExchangeAsset { give, want, maximal } => W::exchange_asset(give, want, maximal),
1266 InitiateReserveWithdraw { assets, reserve, xcm } =>
1267 W::initiate_reserve_withdraw(assets, reserve, xcm),
1268 InitiateTeleport { assets, dest, xcm } => W::initiate_teleport(assets, dest, xcm),
1269 ReportHolding { response_info, assets } => W::report_holding(&response_info, &assets),
1270 BuyExecution { fees, weight_limit } => W::buy_execution(fees, weight_limit),
1271 RefundSurplus => W::refund_surplus(),
1272 SetErrorHandler(xcm) => W::set_error_handler(xcm),
1273 SetAppendix(xcm) => W::set_appendix(xcm),
1274 ClearError => W::clear_error(),
1275 SetHints { hints } => W::set_hints(hints),
1276 ClaimAsset { assets, ticket } => W::claim_asset(assets, ticket),
1277 Trap(code) => W::trap(code),
1278 SubscribeVersion { query_id, max_response_weight } =>
1279 W::subscribe_version(query_id, max_response_weight),
1280 UnsubscribeVersion => W::unsubscribe_version(),
1281 BurnAsset(assets) => W::burn_asset(assets),
1282 ExpectAsset(assets) => W::expect_asset(assets),
1283 ExpectOrigin(origin) => W::expect_origin(origin),
1284 ExpectError(error) => W::expect_error(error),
1285 ExpectTransactStatus(transact_status) => W::expect_transact_status(transact_status),
1286 QueryPallet { module_name, response_info } =>
1287 W::query_pallet(module_name, response_info),
1288 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1289 W::expect_pallet(index, name, module_name, crate_major, min_crate_minor),
1290 ReportTransactStatus(response_info) => W::report_transact_status(response_info),
1291 ClearTransactStatus => W::clear_transact_status(),
1292 UniversalOrigin(j) => W::universal_origin(j),
1293 ExportMessage { network, destination, xcm } =>
1294 W::export_message(network, destination, xcm),
1295 LockAsset { asset, unlocker } => W::lock_asset(asset, unlocker),
1296 UnlockAsset { asset, target } => W::unlock_asset(asset, target),
1297 NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner),
1298 RequestUnlock { asset, locker } => W::request_unlock(asset, locker),
1299 SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw),
1300 SetTopic(topic) => W::set_topic(topic),
1301 ClearTopic => W::clear_topic(),
1302 AliasOrigin(location) => W::alias_origin(location),
1303 UnpaidExecution { weight_limit, check_origin } =>
1304 W::unpaid_execution(weight_limit, check_origin),
1305 PayFees { asset } => W::pay_fees(asset),
1306 InitiateTransfer { destination, remote_fees, preserve_origin, assets, remote_xcm } =>
1307 W::initiate_transfer(destination, remote_fees, preserve_origin, assets, remote_xcm),
1308 ExecuteWithOrigin { descendant_origin, xcm } =>
1309 W::execute_with_origin(descendant_origin, xcm),
1310 }
1311 }
1312}
1313
1314pub mod opaque {
1315 pub type Xcm = super::Xcm<()>;
1318
1319 pub type Instruction = super::Instruction<()>;
1322}
1323
1324impl<Call> TryFrom<OldXcm<Call>> for Xcm<Call> {
1326 type Error = ();
1327 fn try_from(old_xcm: OldXcm<Call>) -> result::Result<Self, Self::Error> {
1328 Ok(Xcm(old_xcm.0.into_iter().map(TryInto::try_into).collect::<result::Result<_, _>>()?))
1329 }
1330}
1331
1332impl<Call> TryFrom<OldInstruction<Call>> for Instruction<Call> {
1334 type Error = ();
1335 fn try_from(old_instruction: OldInstruction<Call>) -> result::Result<Self, Self::Error> {
1336 use OldInstruction::*;
1337 Ok(match old_instruction {
1338 WithdrawAsset(assets) => Self::WithdrawAsset(assets.try_into()?),
1339 ReserveAssetDeposited(assets) => Self::ReserveAssetDeposited(assets.try_into()?),
1340 ReceiveTeleportedAsset(assets) => Self::ReceiveTeleportedAsset(assets.try_into()?),
1341 QueryResponse { query_id, response, max_weight, querier: Some(querier) } =>
1342 Self::QueryResponse {
1343 query_id,
1344 querier: querier.try_into()?,
1345 response: response.try_into()?,
1346 max_weight,
1347 },
1348 QueryResponse { query_id, response, max_weight, querier: None } =>
1349 Self::QueryResponse {
1350 query_id,
1351 querier: None,
1352 response: response.try_into()?,
1353 max_weight,
1354 },
1355 TransferAsset { assets, beneficiary } => Self::TransferAsset {
1356 assets: assets.try_into()?,
1357 beneficiary: beneficiary.try_into()?,
1358 },
1359 TransferReserveAsset { assets, dest, xcm } => Self::TransferReserveAsset {
1360 assets: assets.try_into()?,
1361 dest: dest.try_into()?,
1362 xcm: xcm.try_into()?,
1363 },
1364 HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } =>
1365 Self::HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity },
1366 HrmpChannelAccepted { recipient } => Self::HrmpChannelAccepted { recipient },
1367 HrmpChannelClosing { initiator, sender, recipient } =>
1368 Self::HrmpChannelClosing { initiator, sender, recipient },
1369 Transact { origin_kind, require_weight_at_most, call } => Self::Transact {
1370 origin_kind,
1371 call: call.into(),
1372 fallback_max_weight: Some(require_weight_at_most),
1373 },
1374 ReportError(response_info) => Self::ReportError(QueryResponseInfo {
1375 query_id: response_info.query_id,
1376 destination: response_info.destination.try_into().map_err(|_| ())?,
1377 max_weight: response_info.max_weight,
1378 }),
1379 DepositAsset { assets, beneficiary } => {
1380 let beneficiary = beneficiary.try_into()?;
1381 let assets = assets.try_into()?;
1382 Self::DepositAsset { assets, beneficiary }
1383 },
1384 DepositReserveAsset { assets, dest, xcm } => {
1385 let dest = dest.try_into()?;
1386 let xcm = xcm.try_into()?;
1387 let assets = assets.try_into()?;
1388 Self::DepositReserveAsset { assets, dest, xcm }
1389 },
1390 ExchangeAsset { give, want, maximal } => {
1391 let give = give.try_into()?;
1392 let want = want.try_into()?;
1393 Self::ExchangeAsset { give, want, maximal }
1394 },
1395 InitiateReserveWithdraw { assets, reserve, xcm } => {
1396 let assets = assets.try_into()?;
1397 let reserve = reserve.try_into()?;
1398 let xcm = xcm.try_into()?;
1399 Self::InitiateReserveWithdraw { assets, reserve, xcm }
1400 },
1401 InitiateTeleport { assets, dest, xcm } => {
1402 let assets = assets.try_into()?;
1403 let dest = dest.try_into()?;
1404 let xcm = xcm.try_into()?;
1405 Self::InitiateTeleport { assets, dest, xcm }
1406 },
1407 ReportHolding { response_info, assets } => {
1408 let response_info = QueryResponseInfo {
1409 destination: response_info.destination.try_into().map_err(|_| ())?,
1410 query_id: response_info.query_id,
1411 max_weight: response_info.max_weight,
1412 };
1413 Self::ReportHolding { response_info, assets: assets.try_into()? }
1414 },
1415 BuyExecution { fees, weight_limit } => {
1416 let fees = fees.try_into()?;
1417 let weight_limit = weight_limit.into();
1418 Self::BuyExecution { fees, weight_limit }
1419 },
1420 ClearOrigin => Self::ClearOrigin,
1421 DescendOrigin(who) => Self::DescendOrigin(who.try_into()?),
1422 RefundSurplus => Self::RefundSurplus,
1423 SetErrorHandler(xcm) => Self::SetErrorHandler(xcm.try_into()?),
1424 SetAppendix(xcm) => Self::SetAppendix(xcm.try_into()?),
1425 ClearError => Self::ClearError,
1426 ClaimAsset { assets, ticket } => {
1427 let assets = assets.try_into()?;
1428 let ticket = ticket.try_into()?;
1429 Self::ClaimAsset { assets, ticket }
1430 },
1431 Trap(code) => Self::Trap(code),
1432 SubscribeVersion { query_id, max_response_weight } =>
1433 Self::SubscribeVersion { query_id, max_response_weight },
1434 UnsubscribeVersion => Self::UnsubscribeVersion,
1435 BurnAsset(assets) => Self::BurnAsset(assets.try_into()?),
1436 ExpectAsset(assets) => Self::ExpectAsset(assets.try_into()?),
1437 ExpectOrigin(maybe_location) => Self::ExpectOrigin(
1438 maybe_location.map(|location| location.try_into()).transpose().map_err(|_| ())?,
1439 ),
1440 ExpectError(maybe_error) => Self::ExpectError(
1441 maybe_error
1442 .map(|(num, old_error)| (num, old_error.try_into()))
1443 .map(|(num, result)| result.map(|inner| (num, inner)))
1444 .transpose()
1445 .map_err(|_| ())?,
1446 ),
1447 ExpectTransactStatus(maybe_error_code) => Self::ExpectTransactStatus(maybe_error_code),
1448 QueryPallet { module_name, response_info } => Self::QueryPallet {
1449 module_name,
1450 response_info: response_info.try_into().map_err(|_| ())?,
1451 },
1452 ExpectPallet { index, name, module_name, crate_major, min_crate_minor } =>
1453 Self::ExpectPallet { index, name, module_name, crate_major, min_crate_minor },
1454 ReportTransactStatus(response_info) =>
1455 Self::ReportTransactStatus(response_info.try_into().map_err(|_| ())?),
1456 ClearTransactStatus => Self::ClearTransactStatus,
1457 UniversalOrigin(junction) =>
1458 Self::UniversalOrigin(junction.try_into().map_err(|_| ())?),
1459 ExportMessage { network, destination, xcm } => Self::ExportMessage {
1460 network: network.into(),
1461 destination: destination.try_into().map_err(|_| ())?,
1462 xcm: xcm.try_into().map_err(|_| ())?,
1463 },
1464 LockAsset { asset, unlocker } => Self::LockAsset {
1465 asset: asset.try_into().map_err(|_| ())?,
1466 unlocker: unlocker.try_into().map_err(|_| ())?,
1467 },
1468 UnlockAsset { asset, target } => Self::UnlockAsset {
1469 asset: asset.try_into().map_err(|_| ())?,
1470 target: target.try_into().map_err(|_| ())?,
1471 },
1472 NoteUnlockable { asset, owner } => Self::NoteUnlockable {
1473 asset: asset.try_into().map_err(|_| ())?,
1474 owner: owner.try_into().map_err(|_| ())?,
1475 },
1476 RequestUnlock { asset, locker } => Self::RequestUnlock {
1477 asset: asset.try_into().map_err(|_| ())?,
1478 locker: locker.try_into().map_err(|_| ())?,
1479 },
1480 SetFeesMode { jit_withdraw } => Self::SetFeesMode { jit_withdraw },
1481 SetTopic(topic) => Self::SetTopic(topic),
1482 ClearTopic => Self::ClearTopic,
1483 AliasOrigin(location) => Self::AliasOrigin(location.try_into().map_err(|_| ())?),
1484 UnpaidExecution { weight_limit, check_origin } => Self::UnpaidExecution {
1485 weight_limit,
1486 check_origin: check_origin
1487 .map(|location| location.try_into())
1488 .transpose()
1489 .map_err(|_| ())?,
1490 },
1491 })
1492 }
1493}
1494
1495#[cfg(test)]
1496mod tests {
1497 use super::{prelude::*, *};
1498 use crate::{
1499 v4::{
1500 AssetFilter as OldAssetFilter, Junctions::Here as OldHere, WildAsset as OldWildAsset,
1501 },
1502 MAX_INSTRUCTIONS_TO_DECODE,
1503 };
1504
1505 #[test]
1506 fn basic_roundtrip_works() {
1507 let xcm = Xcm::<()>(vec![TransferAsset {
1508 assets: (Here, 1u128).into(),
1509 beneficiary: Here.into(),
1510 }]);
1511 let old_xcm = OldXcm::<()>(vec![OldInstruction::TransferAsset {
1512 assets: (OldHere, 1u128).into(),
1513 beneficiary: OldHere.into(),
1514 }]);
1515 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1516 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1517 assert_eq!(new_xcm, xcm);
1518 }
1519
1520 #[test]
1521 fn teleport_roundtrip_works() {
1522 let xcm = Xcm::<()>(vec![
1523 ReceiveTeleportedAsset((Here, 1u128).into()),
1524 ClearOrigin,
1525 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1526 ]);
1527 let old_xcm: OldXcm<()> = OldXcm::<()>(vec![
1528 OldInstruction::ReceiveTeleportedAsset((OldHere, 1u128).into()),
1529 OldInstruction::ClearOrigin,
1530 OldInstruction::DepositAsset {
1531 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1532 beneficiary: OldHere.into(),
1533 },
1534 ]);
1535 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1536 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1537 assert_eq!(new_xcm, xcm);
1538 }
1539
1540 #[test]
1541 fn reserve_deposit_roundtrip_works() {
1542 let xcm = Xcm::<()>(vec![
1543 ReserveAssetDeposited((Here, 1u128).into()),
1544 ClearOrigin,
1545 BuyExecution {
1546 fees: (Here, 1u128).into(),
1547 weight_limit: Some(Weight::from_parts(1, 1)).into(),
1548 },
1549 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1550 ]);
1551 let old_xcm = OldXcm::<()>(vec![
1552 OldInstruction::ReserveAssetDeposited((OldHere, 1u128).into()),
1553 OldInstruction::ClearOrigin,
1554 OldInstruction::BuyExecution {
1555 fees: (OldHere, 1u128).into(),
1556 weight_limit: WeightLimit::Limited(Weight::from_parts(1, 1)),
1557 },
1558 OldInstruction::DepositAsset {
1559 assets: crate::v4::AssetFilter::Wild(crate::v4::WildAsset::AllCounted(1)),
1560 beneficiary: OldHere.into(),
1561 },
1562 ]);
1563 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1564 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1565 assert_eq!(new_xcm, xcm);
1566 }
1567
1568 #[test]
1569 fn deposit_asset_roundtrip_works() {
1570 let xcm = Xcm::<()>(vec![
1571 WithdrawAsset((Here, 1u128).into()),
1572 DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Here.into() },
1573 ]);
1574 let old_xcm = OldXcm::<()>(vec![
1575 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1576 OldInstruction::DepositAsset {
1577 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1578 beneficiary: OldHere.into(),
1579 },
1580 ]);
1581 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1582 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1583 assert_eq!(new_xcm, xcm);
1584 }
1585
1586 #[test]
1587 fn deposit_reserve_asset_roundtrip_works() {
1588 let xcm = Xcm::<()>(vec![
1589 WithdrawAsset((Here, 1u128).into()),
1590 DepositReserveAsset {
1591 assets: Wild(AllCounted(1)),
1592 dest: Here.into(),
1593 xcm: Xcm::<()>(vec![]),
1594 },
1595 ]);
1596 let old_xcm = OldXcm::<()>(vec![
1597 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1598 OldInstruction::DepositReserveAsset {
1599 assets: OldAssetFilter::Wild(OldWildAsset::AllCounted(1)),
1600 dest: OldHere.into(),
1601 xcm: OldXcm::<()>(vec![]),
1602 },
1603 ]);
1604 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm.clone()).unwrap());
1605 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1606 assert_eq!(new_xcm, xcm);
1607 }
1608
1609 #[test]
1610 fn transact_roundtrip_works() {
1611 let xcm = Xcm::<()>(vec![
1613 WithdrawAsset((Here, 1u128).into()),
1614 Transact {
1615 origin_kind: OriginKind::SovereignAccount,
1616 call: vec![200, 200, 200].into(),
1617 fallback_max_weight: Some(Weight::from_parts(1_000_000, 1_024)),
1618 },
1619 ]);
1620 let old_xcm = OldXcm::<()>(vec![
1621 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1622 OldInstruction::Transact {
1623 origin_kind: OriginKind::SovereignAccount,
1624 call: vec![200, 200, 200].into(),
1625 require_weight_at_most: Weight::from_parts(1_000_000, 1_024),
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 let xcm_without_fallback = Xcm::<()>(vec![
1634 WithdrawAsset((Here, 1u128).into()),
1635 Transact {
1636 origin_kind: OriginKind::SovereignAccount,
1637 call: vec![200, 200, 200].into(),
1638 fallback_max_weight: None,
1639 },
1640 ]);
1641 let old_xcm = OldXcm::<()>(vec![
1642 OldInstruction::WithdrawAsset((OldHere, 1u128).into()),
1643 OldInstruction::Transact {
1644 origin_kind: OriginKind::SovereignAccount,
1645 call: vec![200, 200, 200].into(),
1646 require_weight_at_most: Weight::MAX,
1647 },
1648 ]);
1649 assert_eq!(old_xcm, OldXcm::<()>::try_from(xcm_without_fallback.clone()).unwrap());
1650 let new_xcm: Xcm<()> = old_xcm.try_into().unwrap();
1651 let xcm_with_max_weight_fallback = Xcm::<()>(vec![
1652 WithdrawAsset((Here, 1u128).into()),
1653 Transact {
1654 origin_kind: OriginKind::SovereignAccount,
1655 call: vec![200, 200, 200].into(),
1656 fallback_max_weight: Some(Weight::MAX),
1657 },
1658 ]);
1659 assert_eq!(new_xcm, xcm_with_max_weight_fallback);
1660 }
1661
1662 #[test]
1663 fn decoding_respects_limit() {
1664 let max_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize]);
1665 let encoded = max_xcm.encode();
1666 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
1667
1668 let big_xcm = Xcm::<()>(vec![ClearOrigin; MAX_INSTRUCTIONS_TO_DECODE as usize + 1]);
1669 let encoded = big_xcm.encode();
1670 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1671
1672 let nested_xcm = Xcm::<()>(vec![
1673 DepositReserveAsset {
1674 assets: All.into(),
1675 dest: Here.into(),
1676 xcm: max_xcm,
1677 };
1678 (MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
1679 ]);
1680 let encoded = nested_xcm.encode();
1681 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1682
1683 let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
1684 let encoded = even_more_nested_xcm.encode();
1685 assert_eq!(encoded.len(), 342530);
1686 assert_eq!(MAX_INSTRUCTIONS_TO_DECODE, 100, "precondition");
1688 assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
1689 }
1690}