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
use frame_support::{assert_ok, traits::PalletInfoAccess, weights::Weight};
18
use moonbase_runtime::xcm_config::AssetType;
19

            
20
use crate::{
21
	xcm_mock::{parachain::PolkadotXcm, *},
22
	xcm_testing::{add_supported_asset, currency_to_asset, helpers::*},
23
};
24
use sp_std::boxed::Box;
25
use xcm::VersionedLocation;
26
use xcm::{
27
	latest::prelude::{
28
		AccountId32, Asset, AssetId, Assets as XcmAssets, Fungibility, GeneralIndex, Junction,
29
		Location, PalletInstance, Parachain, WeightLimit,
30
	},
31
	VersionedAssets,
32
};
33
use xcm_executor::traits::ConvertLocation;
34
use xcm_primitives::split_location_into_chain_part_and_beneficiary;
35
use xcm_simulator::TestExt;
36

            
37
#[test]
38
1
fn test_statemint_like() {
39
1
	let setup = setup_statemint_test_environment();
40
1
	let (_, source_location, source_id) = create_statemint_asset_location(0);
41
1

            
42
1
	// Register asset on ParaA
43
1
	register_statemint_asset_on_para(source_location, b"StatemintToken", b"StatemintToken");
44
1

            
45
1
	// Setup asset on Statemint and transfer to ParaA
46
1
	setup_statemint_asset(0, 300000000000000, setup.sov_account.clone());
47
1

            
48
1
	Statemint::execute_with(|| {
49
1
		// Send asset to ParaA
50
1
		assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets(
51
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
52
1
			Box::new(setup.dest_para.clone().into()),
53
1
			Box::new(VersionedLocation::from(setup.parachain_beneficiary.clone()).into()),
54
1
			Box::new(
55
1
				(
56
1
					[
57
1
						xcm::latest::prelude::PalletInstance(
58
1
							<StatemintAssets as PalletInfoAccess>::index() as u8
59
1
						),
60
1
						xcm::latest::prelude::GeneralIndex(0),
61
1
					],
62
1
					123
63
1
				)
64
1
					.into()
65
1
			),
66
1
			0,
67
1
			WeightLimit::Unlimited
68
1
		));
69
1
	});
70
1

            
71
1
	assert_asset_balance(&PARAALICE, source_id, 123);
72
1
}
73

            
74
#[test]
75
1
fn send_statemint_asset_from_para_a_to_statemint_with_relay_fee() {
76
1
	MockNet::reset();
77
1

            
78
1
	// Setup relay asset using helper
79
1
	let (_relay_location, source_relay_id) = setup_relay_asset_for_statemint();
80
1

            
81
1
	// Statemint asset
82
1
	let statemint_asset = Location::new(
83
1
		1,
84
1
		[
85
1
			Parachain(1000u32),
86
1
			PalletInstance(5u8),
87
1
			GeneralIndex(10u128),
88
1
		],
89
1
	);
90
1
	let statemint_location_asset: AssetType = statemint_asset
91
1
		.clone()
92
1
		.try_into()
93
1
		.expect("Location convertion to AssetType should succeed");
94
1
	let source_statemint_asset_id: parachain::AssetId = statemint_location_asset.clone().into();
95
1

            
96
1
	let asset_metadata_statemint_asset = parachain::AssetMetadata {
97
1
		name: b"USDC".to_vec(),
98
1
		symbol: b"USDC".to_vec(),
99
1
		decimals: 12,
100
1
	};
101
1

            
102
1
	let dest_para = parachain_location(1);
103
1

            
104
1
	let sov = xcm_builder::SiblingParachainConvertsVia::<
105
1
		polkadot_parachain::primitives::Sibling,
106
1
		statemint_like::AccountId,
107
1
	>::convert_location(&dest_para)
108
1
	.unwrap();
109
1

            
110
1
	ParaA::execute_with(|| {
111
1
		assert_ok!(AssetManager::register_foreign_asset(
112
1
			parachain::RuntimeOrigin::root(),
113
1
			statemint_location_asset.clone(),
114
1
			asset_metadata_statemint_asset,
115
1
			1u128,
116
1
			true
117
1
		));
118
1
		assert_ok!(add_supported_asset(statemint_location_asset, 0));
119
1
	});
120
1

            
121
1
	let parachain_beneficiary_from_relay = account_key20_location(PARAALICE);
122
1

            
123
1
	// Send relay chain asset to Alice in Parachain A
124
1
	Relay::execute_with(|| {
125
1
		assert_ok!(RelayChainPalletXcm::limited_reserve_transfer_assets(
126
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
127
1
			Box::new(Parachain(1).into()),
128
1
			Box::new(
129
1
				VersionedLocation::from(parachain_beneficiary_from_relay)
130
1
					.clone()
131
1
					.into()
132
1
			),
133
1
			Box::new(([], 200).into()),
134
1
			0,
135
1
			WeightLimit::Unlimited
136
1
		));
137
1
	});
138
1

            
139
1
	Statemint::execute_with(|| {
140
1
		// Set new prefix
141
1
		statemint_like::PrefixChanger::set_prefix(
142
1
			PalletInstance(<StatemintAssets as PalletInfoAccess>::index() as u8).into(),
143
1
		);
144
1

            
145
1
		assert_ok!(StatemintAssets::create(
146
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
147
1
			10,
148
1
			RELAYALICE,
149
1
			1
150
1
		));
151

            
152
1
		assert_ok!(StatemintAssets::mint(
153
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
154
1
			10,
155
1
			RELAYALICE,
156
1
			300000000000000
157
1
		));
158

            
159
		// Send some native statemint tokens to sovereign for fees.
160
		// We can't pay fees with USDC as the asset is minted as non-sufficient.
161
1
		assert_ok!(StatemintBalances::transfer_allow_death(
162
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
163
1
			sov,
164
1
			100000000000000
165
1
		));
166

            
167
		// Send statemint USDC asset to Alice in Parachain A
168
1
		let parachain_beneficiary_from_statemint = account_key20_location(PARAALICE);
169
1

            
170
1
		// Send with new prefix
171
1
		assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets(
172
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
173
1
			Box::new(parachain_location(1).into()),
174
1
			Box::new(
175
1
				VersionedLocation::from(parachain_beneficiary_from_statemint)
176
1
					.clone()
177
1
					.into()
178
1
			),
179
1
			Box::new(
180
1
				(
181
1
					[
182
1
						xcm::latest::prelude::PalletInstance(
183
1
							<StatemintAssets as PalletInfoAccess>::index() as u8
184
1
						),
185
1
						GeneralIndex(10),
186
1
					],
187
1
					125
188
1
				)
189
1
					.into()
190
1
			),
191
1
			0,
192
1
			WeightLimit::Unlimited
193
1
		));
194
1
	});
195
1

            
196
1
	let statemint_beneficiary = Location {
197
1
		parents: 1,
198
1
		interior: [
199
1
			Parachain(1000),
200
1
			AccountId32 {
201
1
				network: None,
202
1
				id: RELAYBOB.into(),
203
1
			},
204
1
		]
205
1
		.into(),
206
1
	};
207
1

            
208
1
	ParaA::execute_with(|| {
209
1
		// Alice has received 125 USDC
210
1
		assert_eq!(
211
1
			Assets::balance(source_statemint_asset_id, &PARAALICE.into()),
212
1
			125
213
1
		);
214

            
215
		// Alice has received 200 Relay assets
216
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
217
1
	});
218
1

            
219
1
	Statemint::execute_with(|| {
220
1
		// Check that BOB's balance is empty before the transfer
221
1
		assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![]);
222
1
	});
223
1

            
224
1
	let (chain_part, beneficiary) =
225
1
		split_location_into_chain_part_and_beneficiary(statemint_beneficiary).unwrap();
226
1

            
227
1
	// Transfer USDC from Parachain A to Statemint using Relay asset as fee
228
1
	ParaA::execute_with(|| {
229
1
		let asset = currency_to_asset(
230
1
			parachain::CurrencyId::ForeignAsset(source_statemint_asset_id),
231
1
			100,
232
1
		);
233
1
		let asset_fee =
234
1
			currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100);
235
1
		assert_ok!(PolkadotXcm::transfer_assets(
236
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
237
1
			Box::new(VersionedLocation::from(chain_part)),
238
1
			Box::new(VersionedLocation::from(beneficiary)),
239
1
			Box::new(VersionedAssets::from(vec![asset_fee, asset])),
240
1
			0,
241
1
			WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64))
242
1
		));
243
1
	});
244
1

            
245
1
	ParaA::execute_with(|| {
246
1
		// Alice has 100 USDC less
247
1
		assert_eq!(
248
1
			Assets::balance(source_statemint_asset_id, &PARAALICE.into()),
249
1
			25
250
1
		);
251

            
252
		// Alice has 100 relay asset less
253
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100);
254
1
	});
255
1

            
256
1
	Statemint::execute_with(|| {
257
1
		// Check that BOB received 100 USDC on statemint
258
1
		assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]);
259
1
	});
260
1
}
261

            
262
#[test]
263
1
fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer() {
264
1
	let setup = setup_statemint_test_environment();
265
1
	let (_relay_location, source_relay_id) = setup_relay_asset_for_statemint();
266
1

            
267
1
	ParaA::execute_with(|| {
268
1
		XcmWeightTrader::set_asset_price(Location::parent(), 0u128);
269
1
	});
270
1

            
271
1
	// Execute relay->statemint->para transfer sequence
272
1
	execute_relay_to_statemint_transfer(setup.statemint_beneficiary.clone(), 200);
273
1

            
274
1
	Statemint::execute_with(|| {
275
1
		assert_eq!(
276
1
			StatemintBalances::free_balance(RELAYALICE),
277
1
			INITIAL_BALANCE + 200
278
1
		);
279
1
		assert_ok!(StatemintBalances::transfer_allow_death(
280
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
281
1
			setup.sov_account.clone(),
282
1
			110000000000000
283
1
		));
284
1
	});
285
1

            
286
1
	execute_statemint_to_para_dot_transfer(&setup, RELAYALICE, 200);
287
1
	assert_asset_balance(&PARAALICE, source_relay_id, 200);
288
1

            
289
1
	// ParaA to Statemint transfer to RELAYBOB
290
1
	let dest = Location::new(
291
1
		1,
292
1
		[
293
1
			Parachain(1000),
294
1
			AccountId32 {
295
1
				network: None,
296
1
				id: RELAYBOB.into(),
297
1
			},
298
1
		],
299
1
	);
300
1
	let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap();
301
1

            
302
1
	ParaA::execute_with(|| {
303
1
		let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100);
304
1
		assert_ok!(PolkadotXcm::transfer_assets(
305
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
306
1
			Box::new(VersionedLocation::from(chain_part)),
307
1
			Box::new(VersionedLocation::from(beneficiary)),
308
1
			Box::new(VersionedAssets::from(vec![asset])),
309
1
			0,
310
1
			medium_transfer_weight()
311
1
		));
312
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100);
313
1
	});
314
1

            
315
1
	Statemint::execute_with(|| {
316
1
		assert_eq!(
317
1
			StatemintBalances::free_balance(RELAYBOB),
318
1
			INITIAL_BALANCE + 100
319
1
		);
320
1
	});
321
1

            
322
1
	// Send back tokens from AH to ParaA from Bob's account
323
1
	execute_statemint_to_para_transfer_with_balance_check(RELAYBOB.into(), PARAALICE, 100);
324
1

            
325
1
	ParaA::execute_with(|| {
326
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
327
1
	});
328
1
}
329

            
330
#[test]
331
1
fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_with_fee() {
332
1
	let setup = setup_statemint_test_environment();
333
1
	let (_relay_location, source_relay_id) = setup_relay_asset_for_statemint();
334
1

            
335
1
	ParaA::execute_with(|| {
336
1
		XcmWeightTrader::set_asset_price(Location::parent(), 0u128);
337
1
	});
338
1

            
339
1
	// Execute relay->statemint->para transfer sequence
340
1
	execute_relay_to_statemint_transfer(setup.statemint_beneficiary.clone(), 200);
341
1

            
342
1
	Statemint::execute_with(|| {
343
1
		assert_eq!(
344
1
			StatemintBalances::free_balance(RELAYALICE),
345
1
			INITIAL_BALANCE + 200
346
1
		);
347
1
		assert_ok!(StatemintBalances::transfer_allow_death(
348
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
349
1
			setup.sov_account.clone(),
350
1
			110000000000000
351
1
		));
352
1
	});
353
1

            
354
1
	execute_statemint_to_para_dot_transfer(&setup, RELAYALICE, 200);
355
1
	assert_asset_balance(&PARAALICE, source_relay_id, 200);
356
1

            
357
1
	// ParaA to Statemint transfer with fee
358
1
	let dest = Location::new(
359
1
		1,
360
1
		[
361
1
			Parachain(1000),
362
1
			AccountId32 {
363
1
				network: None,
364
1
				id: RELAYBOB.into(),
365
1
			},
366
1
		],
367
1
	);
368
1
	let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap();
369
1

            
370
1
	ParaA::execute_with(|| {
371
1
		let asset = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100);
372
1
		let asset_fee = currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 10);
373
1
		assert_ok!(PolkadotXcm::transfer_assets(
374
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
375
1
			Box::new(VersionedLocation::from(chain_part)),
376
1
			Box::new(VersionedLocation::from(beneficiary)),
377
1
			Box::new(VersionedAssets::from(vec![asset_fee, asset])),
378
1
			0,
379
1
			medium_transfer_weight()
380
1
		));
381
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 90);
382
1
	});
383
1

            
384
1
	Statemint::execute_with(|| {
385
1
		assert_eq!(
386
1
			StatemintBalances::free_balance(RELAYBOB),
387
1
			INITIAL_BALANCE + 110
388
1
		);
389
1
	});
390
1

            
391
1
	execute_statemint_to_para_transfer_with_balance_check(RELAYBOB.into(), PARAALICE, 100);
392
1

            
393
1
	ParaA::execute_with(|| {
394
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 190);
395
1
	});
396
1
}
397

            
398
#[test]
399
1
fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiasset() {
400
1
	MockNet::reset();
401
1

            
402
1
	// Setup relay asset using helper
403
1
	let (_relay_location, source_relay_id) = setup_relay_asset_for_statemint();
404
1

            
405
1
	let dest_para = parachain_location(1);
406
1

            
407
1
	let sov = xcm_builder::SiblingParachainConvertsVia::<
408
1
		polkadot_parachain::primitives::Sibling,
409
1
		statemint_like::AccountId,
410
1
	>::convert_location(&dest_para)
411
1
	.unwrap();
412
1

            
413
1
	ParaA::execute_with(|| {
414
1
		XcmWeightTrader::set_asset_price(Location::parent(), 0u128);
415
1
	});
416
1

            
417
1
	let parachain_beneficiary_absolute = account_key20_location(PARAALICE);
418
1

            
419
1
	let statemint_beneficiary_absolute: Location = Junction::AccountId32 {
420
1
		network: None,
421
1
		id: RELAYALICE.into(),
422
1
	}
423
1
	.into();
424
1

            
425
1
	// First we send relay chain asset to Alice in AssetHub (via teleport)
426
1
	Relay::execute_with(|| {
427
1
		assert_ok!(RelayChainPalletXcm::limited_teleport_assets(
428
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
429
1
			Box::new(Parachain(1000).into()),
430
1
			Box::new(
431
1
				VersionedLocation::from(statemint_beneficiary_absolute)
432
1
					.clone()
433
1
					.into()
434
1
			),
435
1
			Box::new(([], 200).into()),
436
1
			0,
437
1
			WeightLimit::Unlimited
438
1
		));
439
1
	});
440
1

            
441
1
	// Send DOTs from AssetHub to ParaA (Moonbeam)
442
1
	Statemint::execute_with(|| {
443
1
		// Check Alice received 200 tokens on AssetHub
444
1
		assert_eq!(
445
1
			StatemintBalances::free_balance(RELAYALICE),
446
1
			INITIAL_BALANCE + 200
447
1
		);
448

            
449
1
		assert_ok!(StatemintBalances::transfer_allow_death(
450
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
451
1
			sov,
452
1
			110000000000000
453
1
		));
454

            
455
		// Now send those tokens to ParaA
456
1
		assert_ok!(StatemintChainPalletXcm::limited_reserve_transfer_assets(
457
1
			statemint_like::RuntimeOrigin::signed(RELAYALICE),
458
1
			Box::new(parachain_location(1).into()),
459
1
			Box::new(
460
1
				VersionedLocation::from(parachain_beneficiary_absolute.clone())
461
1
					.clone()
462
1
					.into()
463
1
			),
464
1
			Box::new((Location::parent(), 200).into()),
465
1
			0,
466
1
			WeightLimit::Unlimited
467
1
		));
468
1
	});
469
1

            
470
1
	// Alice should have received the DOTs
471
1
	assert_asset_balance(&PARAALICE, source_relay_id, 200);
472
1

            
473
1
	let dest = Location::new(
474
1
		1,
475
1
		[
476
1
			Parachain(1000),
477
1
			AccountId32 {
478
1
				network: None,
479
1
				id: RELAYBOB.into(),
480
1
			},
481
1
		],
482
1
	);
483
1

            
484
1
	let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap();
485
1
	let asset = Asset {
486
1
		id: AssetId(Location::parent()),
487
1
		fun: Fungibility::Fungible(100),
488
1
	};
489
1
	// Finally we test that we are able to send back the DOTs to AssetHub from the ParaA
490
1
	ParaA::execute_with(|| {
491
1
		assert_ok!(PolkadotXcm::transfer_assets(
492
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
493
1
			Box::new(VersionedLocation::from(chain_part)),
494
1
			Box::new(VersionedLocation::from(beneficiary)),
495
1
			Box::new(VersionedAssets::from(asset)),
496
1
			0,
497
1
			medium_transfer_weight()
498
1
		));
499

            
500
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100);
501
1
	});
502
1

            
503
1
	Statemint::execute_with(|| {
504
1
		// Check that Bob received the tokens back in AssetHub
505
1
		assert_eq!(
506
1
			StatemintBalances::free_balance(RELAYBOB),
507
1
			INITIAL_BALANCE + 100
508
1
		);
509
1
	});
510
1

            
511
1
	// Send back tokens from AH to ParaA from Bob's account
512
1
	execute_statemint_to_para_transfer_with_balance_check(RELAYBOB.into(), PARAALICE, 100);
513
1

            
514
1
	ParaA::execute_with(|| {
515
1
		// Alice should have received 100 DOTs
516
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
517
1
	});
518
1
}
519

            
520
#[test]
521
1
fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multicurrencies() {
522
1
	let (_setup, source_relay_id, source_statemint_asset_id) = setup_multi_asset_statemint_test(10);
523
1

            
524
1
	// Verify initial balances after setup
525
1
	ParaA::execute_with(|| {
526
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
527
1
		assert_eq!(
528
1
			Assets::balance(source_statemint_asset_id, &PARAALICE.into()),
529
1
			125
530
1
		);
531
1
	});
532
1

            
533
1
	// ParaA to Statemint multi-asset transfer
534
1
	let dest = Location::new(
535
1
		1,
536
1
		[
537
1
			Parachain(1000),
538
1
			AccountId32 {
539
1
				network: None,
540
1
				id: RELAYBOB.into(),
541
1
			},
542
1
		],
543
1
	);
544
1
	let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap();
545
1

            
546
1
	ParaA::execute_with(|| {
547
1
		let asset = currency_to_asset(
548
1
			parachain::CurrencyId::ForeignAsset(source_statemint_asset_id),
549
1
			100,
550
1
		);
551
1
		let asset_fee =
552
1
			currency_to_asset(parachain::CurrencyId::ForeignAsset(source_relay_id), 100);
553
1
		assert_ok!(PolkadotXcm::transfer_assets(
554
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
555
1
			Box::new(VersionedLocation::from(chain_part)),
556
1
			Box::new(VersionedLocation::from(beneficiary)),
557
1
			Box::new(VersionedAssets::from(vec![asset_fee, asset])),
558
1
			0,
559
1
			WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64))
560
1
		));
561
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100);
562
1
	});
563
1

            
564
1
	// Verify transfers to Bob
565
1
	Statemint::execute_with(|| {
566
1
		assert_eq!(
567
1
			StatemintBalances::free_balance(RELAYBOB),
568
1
			INITIAL_BALANCE + 100
569
1
		);
570
1
		assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]);
571
1
	});
572
1

            
573
1
	// Return transfer from Statemint to ParaA
574
1
	execute_statemint_to_para_transfer_with_balance_check(RELAYBOB.into(), PARAALICE, 100);
575
1

            
576
1
	ParaA::execute_with(|| {
577
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
578
1
	});
579
1
}
580

            
581
#[test]
582
1
fn send_dot_from_moonbeam_to_statemint_via_xtokens_transfer_multiassets() {
583
1
	let (_setup, source_relay_id, source_statemint_asset_id) = setup_multi_asset_statemint_test(10);
584
1

            
585
1
	// Verify initial balances
586
1
	ParaA::execute_with(|| {
587
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
588
1
		assert_eq!(
589
1
			Assets::balance(source_statemint_asset_id, &PARAALICE.into()),
590
1
			125
591
1
		);
592
1
	});
593
1

            
594
1
	// Multi-asset transfer using Asset structs
595
1
	let dest = Location::new(
596
1
		1,
597
1
		[
598
1
			Parachain(1000),
599
1
			AccountId32 {
600
1
				network: None,
601
1
				id: RELAYBOB.into(),
602
1
			},
603
1
		],
604
1
	);
605
1
	let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap();
606
1

            
607
1
	let statemint_asset = Location::new(
608
1
		1,
609
1
		[
610
1
			Parachain(1000u32),
611
1
			PalletInstance(5u8),
612
1
			GeneralIndex(10u128),
613
1
		],
614
1
	);
615
1
	let statemint_asset_to_send = Asset {
616
1
		id: AssetId(statemint_asset),
617
1
		fun: Fungibility::Fungible(100),
618
1
	};
619
1
	let relay_asset_to_send = Asset {
620
1
		id: AssetId(Location::parent()),
621
1
		fun: Fungibility::Fungible(100),
622
1
	};
623
1
	let assets_to_send: XcmAssets =
624
1
		XcmAssets::from(vec![statemint_asset_to_send, relay_asset_to_send.clone()]);
625
1

            
626
1
	// Verify asset ordering for fees
627
1
	assert_eq!(assets_to_send.get(0).unwrap(), &relay_asset_to_send);
628

            
629
1
	ParaA::execute_with(|| {
630
1
		assert_ok!(PolkadotXcm::transfer_assets(
631
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
632
1
			Box::new(VersionedLocation::from(chain_part)),
633
1
			Box::new(VersionedLocation::from(beneficiary)),
634
1
			Box::new(VersionedAssets::from(assets_to_send)),
635
1
			0,
636
1
			WeightLimit::Limited(Weight::from_parts(80_000_000u64, 100_000u64))
637
1
		));
638
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 100);
639
1
	});
640
1

            
641
1
	// Verify Bob received both assets
642
1
	Statemint::execute_with(|| {
643
1
		assert_eq!(
644
1
			StatemintBalances::free_balance(RELAYBOB),
645
1
			INITIAL_BALANCE + 100
646
1
		);
647
1
		assert_eq!(StatemintAssets::account_balances(RELAYBOB), vec![(10, 100)]);
648
1
	});
649
1

            
650
1
	// Return transfer
651
1
	execute_statemint_to_para_transfer_with_balance_check(RELAYBOB.into(), PARAALICE, 100);
652
1

            
653
1
	ParaA::execute_with(|| {
654
1
		assert_eq!(Assets::balance(source_relay_id, &PARAALICE.into()), 200);
655
1
	});
656
1
}