1
// Copyright 2019-2025 PureStake Inc.
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
//! Statemint-specific helpers for XCM tests
18

            
19
use crate::xcm_mock::{
20
	parachain, statemint_like, AssetManager, ParaA, Statemint, StatemintAssets, StatemintBalances,
21
	StatemintChainPalletXcm, XcmWeightTrader, RELAYALICE,
22
};
23
use crate::xcm_testing::add_supported_asset;
24
use frame_support::{assert_ok, traits::PalletInfoAccess};
25
use sp_runtime::AccountId32;
26
use sp_std::boxed::Box;
27
use xcm::latest::prelude::{GeneralIndex, Location, PalletInstance, Parachain, WeightLimit};
28
use xcm_executor::traits::ConvertLocation;
29
use xcm_simulator::TestExt;
30

            
31
use super::assets::setup_relay_asset_for_statemint;
32
use super::core::{account_key20_location, parachain_location};
33
use super::transfers::execute_relay_to_statemint_transfer;
34

            
35
// Statemint test setup struct and functions
36

            
37
#[derive(Clone)]
38
pub struct StatemintTestSetup {
39
	pub dest_para: Location,
40
	pub sov_account: crate::xcm_mock::statemint_like::AccountId,
41
	pub parachain_beneficiary: Location,
42
	pub statemint_beneficiary: Location,
43
}
44

            
45
5
pub fn setup_statemint_test_environment() -> StatemintTestSetup {
46
5
	crate::xcm_mock::MockNet::reset();
47
5

            
48
5
	let dest_para = parachain_location(1);
49
5
	let sov = xcm_builder::SiblingParachainConvertsVia::<
50
5
		polkadot_parachain::primitives::Sibling,
51
5
		crate::xcm_mock::statemint_like::AccountId,
52
5
	>::convert_location(&dest_para)
53
5
	.unwrap();
54
5

            
55
5
	let parachain_beneficiary = account_key20_location(crate::xcm_mock::PARAALICE);
56
5
	let statemint_beneficiary: Location = xcm::latest::prelude::Junction::AccountId32 {
57
5
		network: None,
58
5
		id: crate::xcm_mock::RELAYALICE.into(),
59
5
	}
60
5
	.into();
61
5

            
62
5
	StatemintTestSetup {
63
5
		dest_para,
64
5
		sov_account: sov,
65
5
		parachain_beneficiary,
66
5
		statemint_beneficiary,
67
5
	}
68
5
}
69

            
70
// Statemint asset creation and management helpers
71

            
72
3
pub fn setup_statemint_asset(
73
3
	asset_id: u128,
74
3
	mint_amount: u128,
75
3
	sov_account: crate::xcm_mock::statemint_like::AccountId,
76
3
) {
77
3
	Statemint::execute_with(|| {
78
3
		// Set prefix
79
3
		statemint_like::PrefixChanger::set_prefix(
80
3
			PalletInstance(<StatemintAssets as PalletInfoAccess>::index() as u8).into(),
81
3
		);
82
3

            
83
3
		// Create and mint asset
84
3
		assert_ok!(StatemintAssets::create(
85
3
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
86
3
			asset_id,
87
3
			RELAYALICE,
88
3
			1
89
3
		));
90

            
91
3
		assert_ok!(StatemintAssets::mint(
92
3
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
93
3
			asset_id,
94
3
			RELAYALICE,
95
3
			mint_amount
96
3
		));
97

            
98
		// Transfer native tokens for fees
99
3
		assert_ok!(StatemintBalances::transfer_allow_death(
100
3
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
101
3
			sov_account,
102
3
			100000000000000
103
3
		));
104
3
	});
105
3
}
106

            
107
3
pub fn create_statemint_asset_location(
108
3
	asset_index: u128,
109
3
) -> (
110
3
	Location,
111
3
	crate::xcm_mock::parachain::AssetType,
112
3
	crate::xcm_mock::parachain::AssetId,
113
3
) {
114
3
	let location = Location::new(
115
3
		1,
116
3
		[
117
3
			Parachain(1000u32),
118
3
			PalletInstance(5u8),
119
3
			GeneralIndex(asset_index),
120
3
		],
121
3
	);
122
3

            
123
3
	let asset_type: crate::xcm_mock::parachain::AssetType = location
124
3
		.clone()
125
3
		.try_into()
126
3
		.expect("Location conversion to AssetType should succeed");
127
3
	let asset_id: crate::xcm_mock::parachain::AssetId = asset_type.clone().into();
128
3

            
129
3
	(location, asset_type, asset_id)
130
3
}
131

            
132
3
pub fn register_statemint_asset_on_para(
133
3
	asset_location: crate::xcm_mock::parachain::AssetType,
134
3
	name: &[u8],
135
3
	symbol: &[u8],
136
3
) {
137
3
	let asset_metadata = parachain::AssetMetadata {
138
3
		name: name.to_vec(),
139
3
		symbol: symbol.to_vec(),
140
3
		decimals: 12,
141
3
	};
142
3

            
143
3
	ParaA::execute_with(|| {
144
3
		assert_ok!(AssetManager::register_foreign_asset(
145
3
			parachain::RuntimeOrigin::root(),
146
3
			asset_location.clone(),
147
3
			asset_metadata,
148
3
			1u128,
149
3
			true
150
3
		));
151
3
		assert_ok!(add_supported_asset(asset_location, 0));
152
3
	});
153
3
}
154

            
155
// Statemint-specific transfer helpers
156

            
157
4
pub fn execute_statemint_to_para_dot_transfer(
158
4
	setup: &StatemintTestSetup,
159
4
	from_account: crate::xcm_mock::statemint_like::AccountId,
160
4
	amount: u128,
161
4
) {
162
4
	Statemint::execute_with(|| {
163
4
		assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets(
164
4
			statemint_like::RuntimeOrigin::signed(from_account),
165
4
			Box::new(setup.dest_para.clone().into()),
166
4
			Box::new(xcm::VersionedLocation::from(setup.parachain_beneficiary.clone()).into()),
167
4
			Box::new((Location::parent(), amount).into()),
168
4
			0,
169
4
			WeightLimit::Unlimited
170
4
		));
171
4
	});
172
4
}
173

            
174
5
pub fn execute_statemint_to_para_transfer_with_balance_check(
175
5
	from_account: [u8; 32],
176
5
	to_account: [u8; 20],
177
5
	amount: u128,
178
5
) {
179
5
	Statemint::execute_with(|| {
180
5
		let account_id = AccountId32::from(from_account);
181
5
		let previous_balance = StatemintBalances::free_balance(&account_id);
182
5

            
183
5
		assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets(
184
5
			statemint_like::RuntimeOrigin::signed(account_id.clone()),
185
5
			Box::new(parachain_location(1).into()),
186
5
			Box::new(
187
5
				xcm::VersionedLocation::from(account_key20_location(to_account))
188
5
					.clone()
189
5
					.into()
190
5
			),
191
5
			Box::new((Location::parent(), amount).into()),
192
5
			0,
193
5
			WeightLimit::Unlimited
194
5
		));
195

            
196
		// Assert that the amount was deducted from sender's account
197
5
		assert_eq!(
198
5
			StatemintBalances::free_balance(&account_id),
199
5
			previous_balance - amount
200
5
		);
201
5
	});
202
5
}
203

            
204
// Complete multi-asset test setup (USDC + DOT scenario)
205
2
pub fn setup_multi_asset_statemint_test(
206
2
	asset_id: u128,
207
2
) -> (
208
2
	StatemintTestSetup,
209
2
	crate::xcm_mock::parachain::AssetId,
210
2
	crate::xcm_mock::parachain::AssetId,
211
2
) {
212
2
	let setup = setup_statemint_test_environment();
213
2
	let (_relay_location, source_relay_id) = setup_relay_asset_for_statemint();
214
2

            
215
2
	// Setup USDC asset
216
2
	let (_, usdc_location, usdc_asset_id) = create_statemint_asset_location(asset_id);
217
2
	register_statemint_asset_on_para(usdc_location.clone(), b"USDC", b"USDC");
218
2

            
219
2
	// Setup statemint asset and native token transfers
220
2
	setup_statemint_asset(asset_id, 300000000000000, setup.sov_account.clone());
221
2

            
222
2
	// Setup ParaA weight trader
223
2
	ParaA::execute_with(|| {
224
2
		XcmWeightTrader::set_asset_price(Location::parent(), 0u128);
225
2
		let statemint_asset = Location::new(
226
2
			1,
227
2
			[
228
2
				Parachain(1000u32),
229
2
				PalletInstance(5u8),
230
2
				GeneralIndex(asset_id),
231
2
			],
232
2
		);
233
2
		XcmWeightTrader::set_asset_price(statemint_asset, 0u128);
234
2
	});
235
2

            
236
2
	// Execute relay->statemint transfer
237
2
	execute_relay_to_statemint_transfer(setup.statemint_beneficiary.clone(), 200);
238
2

            
239
2
	// Initial setup transfers
240
2
	Statemint::execute_with(|| {
241
2
		// Note: Balance may be less than INITIAL_BALANCE + 200 due to teleport fees
242
2
		assert_ok!(StatemintBalances::transfer_allow_death(
243
2
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
244
2
			setup.sov_account.clone(),
245
2
			110000000000000
246
2
		));
247
2
	});
248
2

            
249
2
	// Transfer DOTs to ParaA
250
2
	execute_statemint_to_para_dot_transfer(&setup, RELAYALICE, 200);
251
2

            
252
2
	// Transfer USDC to ParaA
253
2
	Statemint::execute_with(|| {
254
2
		assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets(
255
2
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
256
2
			Box::new(setup.dest_para.clone().into()),
257
2
			Box::new(xcm::VersionedLocation::from(setup.parachain_beneficiary.clone()).into()),
258
2
			Box::new(
259
2
				(
260
2
					[
261
2
						PalletInstance(<StatemintAssets as PalletInfoAccess>::index() as u8),
262
2
						GeneralIndex(asset_id),
263
2
					],
264
2
					125
265
2
				)
266
2
					.into()
267
2
			),
268
2
			0,
269
2
			WeightLimit::Unlimited
270
2
		));
271
2
	});
272
2

            
273
2
	(setup, source_relay_id, usdc_asset_id)
274
2
}