1use crate::{Config, VersionedLocation, VersionedXcm, Weight, WeightInfo};
17use alloc::vec::Vec;
18use codec::{DecodeAll, DecodeLimit};
19use core::{fmt, marker::PhantomData, num::NonZero};
20use pallet_revive::{
21 precompiles::{
22 alloy::{self, sol_types::SolValue},
23 AddressMatcher, Error, Ext, Precompile,
24 },
25 DispatchInfo, Origin,
26};
27use tracing::error;
28use xcm::{v5, IdentifyVersion, MAX_XCM_DECODE_DEPTH};
29use xcm_executor::traits::WeightBounds;
30
31alloy::sol!("src/precompiles/IXcm.sol");
32use IXcm::IXcmCalls;
33
34const LOG_TARGET: &str = "xcm::precompiles";
35
36fn revert(error: &impl fmt::Debug, message: &str) -> Error {
37 error!(target: LOG_TARGET, ?error, "{}", message);
38 Error::Revert(message.into())
39}
40
41fn ensure_xcm_version<V: IdentifyVersion>(input: &V) -> Result<(), Error> {
43 let version = input.identify_version();
44 if version < v5::VERSION {
45 return Err(Error::Revert("Only XCM version 5 and onwards are supported.".into()));
46 }
47 Ok(())
48}
49
50pub struct XcmPrecompile<T>(PhantomData<T>);
51
52impl<Runtime> Precompile for XcmPrecompile<Runtime>
53where
54 Runtime: crate::Config + pallet_revive::Config,
55{
56 type T = Runtime;
57 const MATCHER: AddressMatcher = AddressMatcher::Fixed(NonZero::new(10).unwrap());
58 const HAS_CONTRACT_INFO: bool = false;
59 type Interface = IXcm::IXcmCalls;
60
61 fn call(
62 _address: &[u8; 20],
63 input: &Self::Interface,
64 env: &mut impl Ext<T = Self::T>,
65 ) -> Result<Vec<u8>, Error> {
66 let origin = env.caller();
67 let frame_origin = match origin {
68 Origin::Root => frame_system::RawOrigin::Root.into(),
69 Origin::Signed(account_id) =>
70 frame_system::RawOrigin::Signed(account_id.clone()).into(),
71 };
72
73 match input {
74 IXcmCalls::send(IXcm::sendCall { destination, message }) => {
75 let _ = env.charge(<Runtime as Config>::WeightInfo::send())?;
76
77 let final_destination = VersionedLocation::decode_all(&mut &destination[..])
78 .map_err(|error| {
79 revert(&error, "XCM send failed: Invalid destination format")
80 })?;
81
82 ensure_xcm_version(&final_destination)?;
83
84 let final_message = VersionedXcm::<()>::decode_all_with_depth_limit(
85 MAX_XCM_DECODE_DEPTH,
86 &mut &message[..],
87 )
88 .map_err(|error| revert(&error, "XCM send failed: Invalid message format"))?;
89
90 ensure_xcm_version(&final_message)?;
91
92 crate::Pallet::<Runtime>::send(
93 frame_origin,
94 final_destination.into(),
95 final_message.into(),
96 )
97 .map(|_| Vec::new())
98 .map_err(|error| {
99 revert(
100 &error,
101 "XCM send failed: destination or message format may be incompatible",
102 )
103 })
104 },
105 IXcmCalls::execute(IXcm::executeCall { message, weight }) => {
106 let max_weight = Weight::from_parts(weight.refTime, weight.proofSize);
107 let weight_to_charge =
108 max_weight.saturating_add(<Runtime as Config>::WeightInfo::execute());
109 let charged_amount = env.charge(weight_to_charge)?;
110
111 let final_message = VersionedXcm::decode_all_with_depth_limit(
112 MAX_XCM_DECODE_DEPTH,
113 &mut &message[..],
114 )
115 .map_err(|error| revert(&error, "XCM execute failed: Invalid message format"))?;
116
117 ensure_xcm_version(&final_message)?;
118
119 let result = crate::Pallet::<Runtime>::execute(
120 frame_origin,
121 final_message.into(),
122 max_weight,
123 );
124
125 let pre = DispatchInfo {
126 call_weight: weight_to_charge,
127 extension_weight: Weight::zero(),
128 ..Default::default()
129 };
130
131 let actual_weight = frame_support::dispatch::extract_actual_weight(&result, &pre);
133 env.adjust_gas(charged_amount, actual_weight);
134
135 result.map(|_| Vec::new()).map_err(|error| {
136 revert(
137 &error,
138 "XCM execute failed: message may be invalid or execution constraints not satisfied"
139 )
140 })
141 },
142 IXcmCalls::weighMessage(IXcm::weighMessageCall { message }) => {
143 let _ = env.charge(<Runtime as Config>::WeightInfo::weigh_message())?;
144
145 let converted_message = VersionedXcm::decode_all_with_depth_limit(
146 MAX_XCM_DECODE_DEPTH,
147 &mut &message[..],
148 )
149 .map_err(|error| revert(&error, "XCM weightMessage: Invalid message format"))?;
150
151 ensure_xcm_version(&converted_message)?;
152
153 let mut final_message = converted_message.try_into().map_err(|error| {
154 revert(&error, "XCM weightMessage: Conversion to Xcm failed")
155 })?;
156
157 let weight = <<Runtime>::Weigher>::weight(&mut final_message, Weight::MAX)
158 .map_err(|error| {
159 revert(&error, "XCM weightMessage: Failed to calculate weight")
160 })?;
161
162 let final_weight =
163 IXcm::Weight { proofSize: weight.proof_size(), refTime: weight.ref_time() };
164
165 Ok(final_weight.abi_encode())
166 },
167 }
168 }
169}
170
171#[cfg(test)]
172mod test {
173 use crate::{
174 mock::*,
175 precompiles::IXcm::{self, weighMessageCall},
176 VersionedLocation, VersionedXcm,
177 };
178 use frame_support::traits::Currency;
179 use pallet_revive::{
180 precompiles::{
181 alloy::{
182 hex,
183 sol_types::{SolInterface, SolValue},
184 },
185 H160,
186 },
187 DepositLimit, U256,
188 };
189 use polkadot_parachain_primitives::primitives::Id as ParaId;
190 use sp_runtime::traits::AccountIdConversion;
191 use xcm::{prelude::*, v3, v4};
192
193 const BOB: AccountId = AccountId::new([1u8; 32]);
194 const CHARLIE: AccountId = AccountId::new([2u8; 32]);
195 const SEND_AMOUNT: u128 = 10;
196 const CUSTOM_INITIAL_BALANCE: u128 = 100_000_000_000u128;
197
198 #[test]
199 fn test_xcm_send_precompile_works() {
200 use codec::Encode;
201
202 let balances = vec![
203 (ALICE, CUSTOM_INITIAL_BALANCE),
204 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
205 ];
206 new_test_ext_with_balances(balances).execute_with(|| {
207 let xcm_precompile_addr = H160::from(
208 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
209 );
210
211 let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
212 let message = Xcm(vec![
213 ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
214 ClearOrigin,
215 buy_execution((Parent, SEND_AMOUNT)),
216 DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
217 ]);
218
219 let versioned_dest: VersionedLocation = RelayLocation::get().into();
220 let versioned_message: VersionedXcm<()> = VersionedXcm::from(message.clone());
221
222 let xcm_send_params = IXcm::sendCall {
223 destination: versioned_dest.encode().into(),
224 message: versioned_message.encode().into(),
225 };
226 let call = IXcm::IXcmCalls::send(xcm_send_params);
227 let encoded_call = call.abi_encode();
228
229 let result = pallet_revive::Pallet::<Test>::bare_call(
230 RuntimeOrigin::signed(ALICE),
231 xcm_precompile_addr,
232 U256::zero(),
233 Weight::MAX,
234 DepositLimit::UnsafeOnlyForDryRun,
235 encoded_call,
236 );
237 assert!(result.result.is_ok());
238 let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
239 .into_iter()
240 .chain(message.0.clone().into_iter())
241 .collect());
242 assert_eq!(sent_xcm(), vec![(Here.into(), sent_message)]);
243 });
244 }
245
246 #[test]
247 fn test_xcm_send_precompile_to_parachain() {
248 use codec::Encode;
249
250 let balances = vec![
251 (ALICE, CUSTOM_INITIAL_BALANCE),
252 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
253 ];
254 new_test_ext_with_balances(balances).execute_with(|| {
255 let xcm_precompile_addr = H160::from(
256 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
257 );
258
259 let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
260 let message = Xcm(vec![
261 ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
262 ClearOrigin,
263 buy_execution((Parent, SEND_AMOUNT)),
264 DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
265 ]);
266
267 let destination: VersionedLocation = Parachain(OTHER_PARA_ID).into();
268 let versioned_message: VersionedXcm<()> = VersionedXcm::from(message.clone());
269
270 let xcm_send_params = IXcm::sendCall {
271 destination: destination.encode().into(),
272 message: versioned_message.encode().into(),
273 };
274 let call = IXcm::IXcmCalls::send(xcm_send_params);
275 let encoded_call = call.abi_encode();
276
277 let result = pallet_revive::Pallet::<Test>::bare_call(
278 RuntimeOrigin::signed(ALICE),
279 xcm_precompile_addr,
280 U256::zero(),
281 Weight::MAX,
282 DepositLimit::UnsafeOnlyForDryRun,
283 encoded_call,
284 );
285
286 assert!(result.result.is_ok());
287 let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
288 .into_iter()
289 .chain(message.0.clone().into_iter())
290 .collect());
291 assert_eq!(sent_xcm(), vec![(Parachain(OTHER_PARA_ID).into(), sent_message)]);
292 });
293 }
294
295 #[test]
296 fn test_xcm_send_precompile_fails() {
297 use codec::Encode;
298
299 let balances = vec![
300 (ALICE, CUSTOM_INITIAL_BALANCE),
301 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
302 ];
303 new_test_ext_with_balances(balances).execute_with(|| {
304 let xcm_precompile_addr = H160::from(
305 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
306 );
307
308 let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
309 let message = Xcm(vec![
310 ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
311 buy_execution((Parent, SEND_AMOUNT)),
312 DepositAsset { assets: AllCounted(1).into(), beneficiary: sender },
313 ]);
314
315 let destination: VersionedLocation = VersionedLocation::from(Location::ancestor(8));
316 let versioned_message: VersionedXcm<()> = VersionedXcm::from(message.clone());
317
318 let xcm_send_params = IXcm::sendCall {
319 destination: destination.encode().into(),
320 message: versioned_message.encode().into(),
321 };
322 let call = IXcm::IXcmCalls::send(xcm_send_params);
323 let encoded_call = call.abi_encode();
324
325 let result = pallet_revive::Pallet::<Test>::bare_call(
326 RuntimeOrigin::signed(ALICE),
327 xcm_precompile_addr,
328 U256::zero(),
329 Weight::MAX,
330 DepositLimit::UnsafeOnlyForDryRun,
331 encoded_call,
332 );
333 let return_value = match result.result {
334 Ok(value) => value,
335 Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
336 };
337 assert!(return_value.did_revert());
338 });
339 }
340
341 #[test]
342 fn send_fails_on_old_location_version() {
343 use codec::Encode;
344
345 let balances = vec![
346 (ALICE, CUSTOM_INITIAL_BALANCE),
347 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
348 ];
349 new_test_ext_with_balances(balances).execute_with(|| {
350 let xcm_precompile_addr = H160::from(
351 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
352 );
353
354 let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
355 let message = Xcm(vec![
356 ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
357 ClearOrigin,
358 buy_execution((Parent, SEND_AMOUNT)),
359 DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
360 ]);
361
362 let destination: VersionedLocation =
364 VersionedLocation::V4(v4::Junction::Parachain(OTHER_PARA_ID).into());
365 let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::from(message.clone());
366
367 let xcm_send_params = IXcm::sendCall {
368 destination: destination.encode().into(),
369 message: versioned_message.encode().into(),
370 };
371 let call = IXcm::IXcmCalls::send(xcm_send_params);
372 let encoded_call = call.abi_encode();
373
374 let result = pallet_revive::Pallet::<Test>::bare_call(
375 RuntimeOrigin::signed(ALICE),
376 xcm_precompile_addr,
377 U256::zero(),
378 Weight::MAX,
379 DepositLimit::UnsafeOnlyForDryRun,
380 encoded_call,
381 );
382 let return_value = match result.result {
383 Ok(value) => value,
384 Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
385 };
386 assert!(return_value.did_revert());
387
388 let destination: VersionedLocation =
390 VersionedLocation::V3(v3::Junction::Parachain(OTHER_PARA_ID).into());
391 let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::from(message);
392
393 let xcm_send_params = IXcm::sendCall {
394 destination: destination.encode().into(),
395 message: versioned_message.encode().into(),
396 };
397 let call = IXcm::IXcmCalls::send(xcm_send_params);
398 let encoded_call = call.abi_encode();
399
400 let result = pallet_revive::Pallet::<Test>::bare_call(
401 RuntimeOrigin::signed(ALICE),
402 xcm_precompile_addr,
403 U256::zero(),
404 Weight::MAX,
405 DepositLimit::UnsafeOnlyForDryRun,
406 encoded_call,
407 );
408 let return_value = match result.result {
409 Ok(value) => value,
410 Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
411 };
412 assert!(return_value.did_revert());
413 });
414 }
415
416 #[test]
417 fn send_fails_on_old_xcm_version() {
418 use codec::Encode;
419
420 let balances = vec![
421 (ALICE, CUSTOM_INITIAL_BALANCE),
422 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
423 ];
424 new_test_ext_with_balances(balances).execute_with(|| {
425 let xcm_precompile_addr = H160::from(
426 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
427 );
428
429 let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
430 let message = Xcm(vec![
431 ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
432 ClearOrigin,
433 buy_execution((Parent, SEND_AMOUNT)),
434 DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
435 ]);
436 let v4_message: v4::Xcm<RuntimeCall> = message.try_into().unwrap();
438
439 let destination: VersionedLocation = Parachain(OTHER_PARA_ID).into();
440 let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::V4(v4_message.clone());
441
442 let xcm_send_params = IXcm::sendCall {
443 destination: destination.encode().into(),
444 message: versioned_message.encode().into(),
445 };
446 let call = IXcm::IXcmCalls::send(xcm_send_params);
447 let encoded_call = call.abi_encode();
448
449 let result = pallet_revive::Pallet::<Test>::bare_call(
450 RuntimeOrigin::signed(ALICE),
451 xcm_precompile_addr,
452 U256::zero(),
453 Weight::MAX,
454 DepositLimit::UnsafeOnlyForDryRun,
455 encoded_call,
456 );
457 let return_value = match result.result {
458 Ok(value) => value,
459 Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
460 };
461 assert!(return_value.did_revert());
462
463 let v3_message: v3::Xcm<RuntimeCall> = v4_message.try_into().unwrap();
465
466 let destination: VersionedLocation = Parachain(OTHER_PARA_ID).into();
467 let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::V3(v3_message);
468
469 let xcm_send_params = IXcm::sendCall {
470 destination: destination.encode().into(),
471 message: versioned_message.encode().into(),
472 };
473 let call = IXcm::IXcmCalls::send(xcm_send_params);
474 let encoded_call = call.abi_encode();
475
476 let result = pallet_revive::Pallet::<Test>::bare_call(
477 RuntimeOrigin::signed(ALICE),
478 xcm_precompile_addr,
479 U256::zero(),
480 Weight::MAX,
481 DepositLimit::UnsafeOnlyForDryRun,
482 encoded_call,
483 );
484 let return_value = match result.result {
485 Ok(value) => value,
486 Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
487 };
488 assert!(return_value.did_revert());
489 });
490 }
491
492 #[test]
493 fn test_xcm_execute_precompile_works() {
494 use codec::Encode;
495
496 let balances = vec![
497 (ALICE, CUSTOM_INITIAL_BALANCE),
498 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
499 ];
500 new_test_ext_with_balances(balances).execute_with(|| {
501 let xcm_precompile_addr = H160::from(
502 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
503 );
504
505 let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
506 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
507
508 let message: VersionedXcm<RuntimeCall> = VersionedXcm::from(Xcm(vec![
509 WithdrawAsset((Here, SEND_AMOUNT).into()),
510 buy_execution((Here, SEND_AMOUNT)),
511 DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
512 ]));
513
514 let weight_params = weighMessageCall { message: message.encode().into() };
515 let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
516 let encoded_weight_call = weight_call.abi_encode();
517
518 let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
519 RuntimeOrigin::signed(ALICE),
520 xcm_precompile_addr,
521 U256::zero(),
522 Weight::MAX,
523 DepositLimit::UnsafeOnlyForDryRun,
524 encoded_weight_call,
525 );
526
527 let weight_result = match xcm_weight_results.result {
528 Ok(value) => value,
529 Err(err) =>
530 panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
531 };
532
533 let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
534 .expect("XcmExecutePrecompile Failed to decode weight");
535
536 let xcm_execute_params = IXcm::executeCall { message: message.encode().into(), weight };
537 let call = IXcm::IXcmCalls::execute(xcm_execute_params);
538 let encoded_call = call.abi_encode();
539
540 let result = pallet_revive::Pallet::<Test>::bare_call(
541 RuntimeOrigin::signed(ALICE),
542 xcm_precompile_addr,
543 U256::zero(),
544 Weight::MAX,
545 DepositLimit::UnsafeOnlyForDryRun,
546 encoded_call,
547 );
548
549 assert!(result.result.is_ok());
550 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE - SEND_AMOUNT);
551 assert_eq!(Balances::total_balance(&BOB), SEND_AMOUNT);
552 });
553 }
554
555 #[test]
556 fn test_xcm_execute_precompile_different_beneficiary() {
557 use codec::Encode;
558
559 let balances = vec![(ALICE, CUSTOM_INITIAL_BALANCE), (CHARLIE, CUSTOM_INITIAL_BALANCE)];
560 new_test_ext_with_balances(balances).execute_with(|| {
561 let xcm_precompile_addr = H160::from(
562 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
563 );
564
565 let dest: Location = Junction::AccountId32 { network: None, id: CHARLIE.into() }.into();
566 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
567
568 let message: VersionedXcm<RuntimeCall> = VersionedXcm::from(Xcm(vec![
569 WithdrawAsset((Here, SEND_AMOUNT).into()),
570 buy_execution((Here, SEND_AMOUNT)),
571 DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
572 ]));
573
574 let weight_params = weighMessageCall { message: message.encode().into() };
575 let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
576 let encoded_weight_call = weight_call.abi_encode();
577
578 let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
579 RuntimeOrigin::signed(ALICE),
580 xcm_precompile_addr,
581 U256::zero(),
582 Weight::MAX,
583 DepositLimit::UnsafeOnlyForDryRun,
584 encoded_weight_call,
585 );
586
587 let weight_result = match xcm_weight_results.result {
588 Ok(value) => value,
589 Err(err) =>
590 panic!("XcmExecutePrecompile Failed to decode weight with error: {err:?}"),
591 };
592
593 let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
594 .expect("XcmExecutePrecompile Failed to decode weight");
595
596 let xcm_execute_params = IXcm::executeCall { message: message.encode().into(), weight };
597 let call = IXcm::IXcmCalls::execute(xcm_execute_params);
598 let encoded_call = call.abi_encode();
599
600 let result = pallet_revive::Pallet::<Test>::bare_call(
601 RuntimeOrigin::signed(ALICE),
602 xcm_precompile_addr,
603 U256::zero(),
604 Weight::MAX,
605 DepositLimit::UnsafeOnlyForDryRun,
606 encoded_call,
607 );
608
609 let return_value = match result.result {
610 Ok(value) => value,
611 Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
612 };
613
614 assert!(!return_value.did_revert());
615 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE - SEND_AMOUNT);
616 assert_eq!(Balances::total_balance(&CHARLIE), CUSTOM_INITIAL_BALANCE + SEND_AMOUNT);
617 });
618 }
619
620 #[test]
621 fn test_xcm_execute_precompile_fails() {
622 use codec::Encode;
623
624 let balances = vec![(ALICE, CUSTOM_INITIAL_BALANCE), (BOB, CUSTOM_INITIAL_BALANCE)];
625 new_test_ext_with_balances(balances).execute_with(|| {
626 let xcm_precompile_addr = H160::from(
627 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
628 );
629
630 let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
631 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
632 let amount_to_send = CUSTOM_INITIAL_BALANCE - ExistentialDeposit::get();
633 let assets: Assets = (Here, amount_to_send).into();
634
635 let message: VersionedXcm<RuntimeCall> = VersionedXcm::from(Xcm(vec![
636 WithdrawAsset(assets.clone()),
637 buy_execution(assets.inner()[0].clone()),
638 DepositAsset { assets: assets.clone().into(), beneficiary: dest },
639 WithdrawAsset(assets),
640 ]));
641
642 let weight_params = weighMessageCall { message: message.encode().into() };
643 let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
644 let encoded_weight_call = weight_call.abi_encode();
645
646 let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
647 RuntimeOrigin::signed(ALICE),
648 xcm_precompile_addr,
649 U256::zero(),
650 Weight::MAX,
651 DepositLimit::UnsafeOnlyForDryRun,
652 encoded_weight_call,
653 );
654
655 let weight_result = match xcm_weight_results.result {
656 Ok(value) => value,
657 Err(err) =>
658 panic!("XcmExecutePrecompile Failed to decode weight with error: {err:?}"),
659 };
660
661 let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
662 .expect("XcmExecutePrecompile Failed to decode weight");
663
664 let xcm_execute_params = IXcm::executeCall { message: message.encode().into(), weight };
665 let call = IXcm::IXcmCalls::execute(xcm_execute_params);
666 let encoded_call = call.abi_encode();
667
668 let result = pallet_revive::Pallet::<Test>::bare_call(
669 RuntimeOrigin::signed(ALICE),
670 xcm_precompile_addr,
671 U256::zero(),
672 Weight::MAX,
673 DepositLimit::UnsafeOnlyForDryRun,
674 encoded_call,
675 );
676 let return_value = match result.result {
677 Ok(value) => value,
678 Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
679 };
680 assert!(return_value.did_revert());
681 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
682 assert_eq!(Balances::total_balance(&BOB), CUSTOM_INITIAL_BALANCE);
683 });
684 }
685
686 #[test]
687 fn execute_fails_on_old_version() {
688 use codec::Encode;
689
690 let balances = vec![
691 (ALICE, CUSTOM_INITIAL_BALANCE),
692 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
693 ];
694 new_test_ext_with_balances(balances).execute_with(|| {
695 let xcm_precompile_addr = H160::from(
696 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
697 );
698
699 let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
700 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
701
702 let message = Xcm(vec![
703 WithdrawAsset((Here, SEND_AMOUNT).into()),
704 buy_execution((Here, SEND_AMOUNT)),
705 DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
706 ]);
707 let versioned_message = VersionedXcm::from(message.clone());
708
709 let weight_params = weighMessageCall { message: versioned_message.encode().into() };
710 let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
711 let encoded_weight_call = weight_call.abi_encode();
712
713 let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
714 RuntimeOrigin::signed(ALICE),
715 xcm_precompile_addr,
716 U256::zero(),
717 Weight::MAX,
718 DepositLimit::UnsafeOnlyForDryRun,
719 encoded_weight_call,
720 );
721
722 let weight_result = match xcm_weight_results.result {
723 Ok(value) => value,
724 Err(err) =>
725 panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
726 };
727
728 let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
729 .expect("XcmExecutePrecompile Failed to decode weight");
730
731 let v4_message: v4::Xcm<RuntimeCall> = message.clone().try_into().unwrap();
733 let versioned_message = VersionedXcm::V4(v4_message.clone());
734
735 let xcm_execute_params = IXcm::executeCall {
736 message: versioned_message.encode().into(),
737 weight: weight.clone(),
738 };
739 let call = IXcm::IXcmCalls::execute(xcm_execute_params);
740 let encoded_call = call.abi_encode();
741
742 let result = pallet_revive::Pallet::<Test>::bare_call(
743 RuntimeOrigin::signed(ALICE),
744 xcm_precompile_addr,
745 U256::zero(),
746 Weight::MAX,
747 DepositLimit::UnsafeOnlyForDryRun,
748 encoded_call,
749 );
750
751 let return_value = match result.result {
752 Ok(value) => value,
753 Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
754 };
755 assert!(return_value.did_revert());
756 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
757 assert_eq!(Balances::total_balance(&BOB), 0);
758
759 let v3_message: v3::Xcm<RuntimeCall> = v4_message.try_into().unwrap();
761 let versioned_message = VersionedXcm::V3(v3_message);
762
763 let xcm_execute_params =
764 IXcm::executeCall { message: versioned_message.encode().into(), weight };
765 let call = IXcm::IXcmCalls::execute(xcm_execute_params);
766 let encoded_call = call.abi_encode();
767
768 let result = pallet_revive::Pallet::<Test>::bare_call(
769 RuntimeOrigin::signed(ALICE),
770 xcm_precompile_addr,
771 U256::zero(),
772 Weight::MAX,
773 DepositLimit::UnsafeOnlyForDryRun,
774 encoded_call,
775 );
776
777 let return_value = match result.result {
778 Ok(value) => value,
779 Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
780 };
781 assert!(return_value.did_revert());
782 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
783 assert_eq!(Balances::total_balance(&BOB), 0);
784 });
785 }
786
787 #[test]
788 fn weight_fails_on_old_version() {
789 use codec::Encode;
790
791 let balances = vec![
792 (ALICE, CUSTOM_INITIAL_BALANCE),
793 (ParaId::from(OTHER_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
794 ];
795 new_test_ext_with_balances(balances).execute_with(|| {
796 let xcm_precompile_addr = H160::from(
797 hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
798 );
799
800 let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
801 assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
802
803 let message: Xcm<RuntimeCall> = Xcm(vec![
804 WithdrawAsset((Here, SEND_AMOUNT).into()),
805 buy_execution((Here, SEND_AMOUNT)),
806 DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
807 ]);
808 let v4_message: v4::Xcm<RuntimeCall> = message.try_into().unwrap();
810 let versioned_message = VersionedXcm::V4(v4_message.clone());
811
812 let weight_params = weighMessageCall { message: versioned_message.encode().into() };
813 let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
814 let encoded_weight_call = weight_call.abi_encode();
815
816 let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
817 RuntimeOrigin::signed(ALICE),
818 xcm_precompile_addr,
819 U256::zero(),
820 Weight::MAX,
821 DepositLimit::UnsafeOnlyForDryRun,
822 encoded_weight_call,
823 );
824
825 let result = match xcm_weight_results.result {
826 Ok(value) => value,
827 Err(err) =>
828 panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
829 };
830 assert!(result.did_revert());
831
832 let v3_message: v3::Xcm<RuntimeCall> = v4_message.try_into().unwrap();
834 let versioned_message = VersionedXcm::V3(v3_message);
835
836 let weight_params = weighMessageCall { message: versioned_message.encode().into() };
837 let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
838 let encoded_weight_call = weight_call.abi_encode();
839
840 let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
841 RuntimeOrigin::signed(ALICE),
842 xcm_precompile_addr,
843 U256::zero(),
844 Weight::MAX,
845 DepositLimit::UnsafeOnlyForDryRun,
846 encoded_weight_call,
847 );
848
849 let result = match xcm_weight_results.result {
850 Ok(value) => value,
851 Err(err) =>
852 panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
853 };
854 assert!(result.did_revert());
855 });
856 }
857}