moonbeam_runtime_common/tests/
xcm.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright 2025 Moonbeam foundation
// This file is part of Moonbeam.

// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.

//! # Common XCM tests
//!
//! A collection of XCM tests common to all runtimes

#[macro_export]
macro_rules! generate_common_xcm_tests {
	($runtime: ident) => {
		#[cfg(test)]
		pub mod common_xcm_tests {
			use crate::common::{ExtBuilder, ALICE};
			use cumulus_primitives_core::ExecuteXcm;
			use frame_support::assert_ok;
			use frame_support::traits::fungible::Inspect;
			use frame_support::traits::EnsureOrigin;
			use moonbeam_core_primitives::{AccountId, Balance};
			use parity_scale_codec::Encode;
			use sp_weights::Weight;
			use xcm::{
				latest::{prelude::AccountKey20, Assets as XcmAssets, Xcm},
				VersionedAssets, VersionedLocation, VersionedXcm,
			};
			use $runtime::{
				xcm_config::SelfReserve, Balances, PolkadotXcm, Runtime, RuntimeEvent,
				RuntimeOrigin, System,
			};

			pub(crate) fn last_events(n: usize) -> Vec<RuntimeEvent> {
				System::events()
					.into_iter()
					.map(|e| e.event)
					.rev()
					.take(n)
					.rev()
					.collect()
			}

			#[test]
			fn claim_assets_works() {
				const INITIAL_BALANCE: Balance = 10_000_000_000_000_000_000;
				const SEND_AMOUNT: Balance = 1_000_000_000_000_000_000;

				let alice = AccountId::from(ALICE);
				let balances = vec![(alice, INITIAL_BALANCE)];

				ExtBuilder::default()
					.with_balances(balances)
					.build()
					.execute_with(|| {
						let assets = XcmAssets::from((SelfReserve::get(), SEND_AMOUNT));
						// First trap some assets.
						let trapping_program =
							Xcm::builder_unsafe().withdraw_asset(assets.clone()).build();
						// Even though assets are trapped, the extrinsic returns success.
						let origin_location =
							<Runtime as pallet_xcm::Config>::ExecuteXcmOrigin::ensure_origin(
								RuntimeOrigin::signed(alice),
							)
							.expect("qed");
						let message = Box::new(VersionedXcm::V5(trapping_program));
						let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
						let message = (*message).try_into().expect("qed");
						let _ = <Runtime as pallet_xcm::Config>::XcmExecutor::prepare_and_execute(
							origin_location,
							message,
							&mut hash,
							Weight::MAX,
							Weight::MAX,
						);
						assert_eq!(
							Balances::total_balance(&alice),
							INITIAL_BALANCE - SEND_AMOUNT
						);

						// Assets were indeed trapped.
						assert!(last_events(2).iter().any(|evt| matches!(
							evt,
							RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { .. })
						)));

						// Now claim them with the extrinsic.
						assert_ok!(PolkadotXcm::claim_assets(
							RuntimeOrigin::signed(alice),
							Box::new(VersionedAssets::V5(assets)),
							Box::new(VersionedLocation::V5(
								AccountKey20 {
									network: None,
									key: alice.clone().into()
								}
								.into()
							)),
						));
						// Confirm that trapped assets were claimed back
						assert_eq!(Balances::total_balance(&alice), INITIAL_BALANCE);
					});
			}
		}
	};
}