1
// Copyright 2025 Moonbeam foundation
2
// This file is part of Moonbeam.
3

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

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

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

            
17
//! # Common XCM tests
18
//!
19
//! A collection of XCM tests common to all runtimes
20

            
21
#[macro_export]
22
macro_rules! generate_common_xcm_tests {
23
	($runtime: ident) => {
24
		#[cfg(test)]
25
		pub mod common_xcm_tests {
26
			use crate::common::{ExtBuilder, ALICE};
27
			use cumulus_primitives_core::ExecuteXcm;
28
			use frame_support::assert_ok;
29
			use frame_support::traits::fungible::Inspect;
30
			use frame_support::traits::EnsureOrigin;
31
			use moonbeam_core_primitives::{AccountId, Balance};
32
			use parity_scale_codec::Encode;
33
			use sp_weights::Weight;
34
			use xcm::{
35
				latest::{prelude::AccountKey20, Assets as XcmAssets, Xcm},
36
				VersionedAssets, VersionedLocation, VersionedXcm,
37
			};
38
			use $runtime::{
39
				xcm_config::SelfReserve, Balances, PolkadotXcm, Runtime, RuntimeEvent,
40
				RuntimeOrigin, System,
41
			};
42

            
43
3
			pub(crate) fn last_events(n: usize) -> Vec<RuntimeEvent> {
44
3
				System::events()
45
3
					.into_iter()
46
6
					.map(|e| e.event)
47
3
					.rev()
48
3
					.take(n)
49
3
					.rev()
50
3
					.collect()
51
3
			}
52

            
53
			#[test]
54
3
			fn claim_assets_works() {
55
				const INITIAL_BALANCE: Balance = 10_000_000_000_000_000_000;
56
				const SEND_AMOUNT: Balance = 1_000_000_000_000_000_000;
57

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

            
61
3
				ExtBuilder::default()
62
3
					.with_balances(balances)
63
3
					.build()
64
3
					.execute_with(|| {
65
3
						let assets = XcmAssets::from((SelfReserve::get(), SEND_AMOUNT));
66
3
						// First trap some assets.
67
3
						let trapping_program =
68
3
							Xcm::builder_unsafe().withdraw_asset(assets.clone()).build();
69
3
						// Even though assets are trapped, the extrinsic returns success.
70
3
						let origin_location =
71
3
							<Runtime as pallet_xcm::Config>::ExecuteXcmOrigin::ensure_origin(
72
3
								RuntimeOrigin::signed(alice),
73
3
							)
74
3
							.expect("qed");
75
3
						let message = Box::new(VersionedXcm::V5(trapping_program));
76
3
						let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
77
3
						let message = (*message).try_into().expect("qed");
78
3
						let _ = <Runtime as pallet_xcm::Config>::XcmExecutor::prepare_and_execute(
79
3
							origin_location,
80
3
							message,
81
3
							&mut hash,
82
3
							Weight::MAX,
83
3
							Weight::MAX,
84
3
						);
85
3
						assert_eq!(
86
3
							Balances::total_balance(&alice),
87
3
							INITIAL_BALANCE - SEND_AMOUNT
88
3
						);
89

            
90
						// Assets were indeed trapped.
91
3
						assert!(last_events(2).iter().any(|evt| matches!(
92
3
							evt,
93
							RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { .. })
94
3
						)));
95

            
96
						// Now claim them with the extrinsic.
97
3
						assert_ok!(PolkadotXcm::claim_assets(
98
3
							RuntimeOrigin::signed(alice),
99
3
							Box::new(VersionedAssets::V5(assets)),
100
3
							Box::new(VersionedLocation::V5(
101
3
								AccountKey20 {
102
3
									network: None,
103
3
									key: alice.clone().into()
104
3
								}
105
3
								.into()
106
3
							)),
107
3
						));
108
						// Confirm that trapped assets were claimed back
109
3
						assert_eq!(Balances::total_balance(&alice), INITIAL_BALANCE);
110
3
					});
111
3
			}
112
		}
113
	};
114
}