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 crate::mock::*;
18
use crate::*;
19
use cumulus_primitives_core::relay_chain::HrmpChannelId;
20
use frame_support::weights::Weight;
21
use frame_support::{assert_noop, assert_ok};
22
use sp_runtime::traits::Convert;
23
use sp_runtime::DispatchError;
24
use sp_std::boxed::Box;
25
use xcm::latest::prelude::*;
26
use xcm_primitives::{UtilityAvailableCalls, UtilityEncodeCall, XcmFeeTrader};
27

            
28
const TEST_RELATIVE_PRICE: u128 = 1_000_000_000_000_000_000u128; // 1e18
29

            
30
#[test]
31
1
fn test_register_address() {
32
1
	ExtBuilder::default()
33
1
		.with_balances(vec![])
34
1
		.build()
35
1
		.execute_with(|| {
36
			// Only root can do this, as specified in runtime
37
1
			assert_noop!(
38
1
				XcmTransactor::register(RuntimeOrigin::signed(1u64), 1u64, 1),
39
1
				DispatchError::BadOrigin
40
			);
41

            
42
			// Root can register
43
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
44

            
45
1
			assert_eq!(XcmTransactor::index_to_account(&1).unwrap(), 1u64);
46

            
47
1
			let expected = vec![crate::Event::RegisteredDerivative {
48
1
				account_id: 1u64,
49
1
				index: 1,
50
1
			}];
51
1
			assert_eq!(events(), expected);
52
1
		})
53
1
}
54

            
55
#[test]
56
1
fn test_transact_through_derivative_errors() {
57
1
	ExtBuilder::default()
58
1
		.with_balances(vec![])
59
1
		.build()
60
1
		.execute_with(|| {
61
			// Non-claimed index so cannot transfer
62
1
			assert_noop!(
63
1
				XcmTransactor::transact_through_derivative(
64
1
					RuntimeOrigin::signed(1u64),
65
1
					Transactors::Relay,
66
					1,
67
1
					CurrencyPayment {
68
1
						currency: Currency::AsMultiLocation(Box::new(
69
1
							xcm::VersionedLocation::from(Location::parent())
70
1
						)),
71
1
						fee_amount: None
72
1
					},
73
1
					vec![0u8],
74
1
					TransactWeights {
75
1
						transact_required_weight_at_most: 100u64.into(),
76
1
						overall_weight: None
77
1
					},
78
					false
79
				),
80
1
				Error::<Test>::UnclaimedIndex
81
			);
82

            
83
			// Root can register
84
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
85

            
86
			// TransactInfo not yet set
87
1
			assert_noop!(
88
1
				XcmTransactor::transact_through_derivative(
89
1
					RuntimeOrigin::signed(1u64),
90
1
					Transactors::Relay,
91
					1,
92
1
					CurrencyPayment {
93
1
						currency: Currency::AsMultiLocation(Box::new(
94
1
							xcm::VersionedLocation::from(Location::parent())
95
1
						)),
96
1
						fee_amount: None
97
1
					},
98
1
					vec![0u8],
99
1
					TransactWeights {
100
1
						transact_required_weight_at_most: 100u64.into(),
101
1
						overall_weight: None
102
1
					},
103
					false
104
				),
105
1
				Error::<Test>::TransactorInfoNotSet
106
			);
107

            
108
			// Root can set transact info
109
1
			assert_ok!(XcmTransactor::set_transact_info(
110
1
				RuntimeOrigin::root(),
111
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
112
1
				0.into(),
113
1
				10000.into(),
114
1
				None
115
			));
116

            
117
			// TransactInfo present, but FeePerSecond not set
118
1
			assert_noop!(
119
1
				XcmTransactor::transact_through_derivative(
120
1
					RuntimeOrigin::signed(1u64),
121
1
					Transactors::Relay,
122
					1,
123
1
					CurrencyPayment {
124
1
						currency: Currency::AsMultiLocation(Box::new(
125
1
							xcm::VersionedLocation::from(Location::parent())
126
1
						)),
127
1
						fee_amount: None
128
1
					},
129
1
					vec![0u8],
130
1
					TransactWeights {
131
1
						transact_required_weight_at_most: 100u64.into(),
132
1
						overall_weight: None
133
1
					},
134
					false
135
				),
136
1
				DispatchError::Other("Asset relative price not set")
137
			);
138

            
139
			// Set relative price using FeeTrader
140
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
141
1
				Location::new(1, [Junction::Parachain(1000)]),
142
				TEST_RELATIVE_PRICE
143
			));
144

            
145
			// TransactInfo present, but the asset is not a reserve of dest
146
1
			assert_noop!(
147
1
				XcmTransactor::transact_through_derivative(
148
1
					RuntimeOrigin::signed(1u64),
149
1
					Transactors::Relay,
150
					1,
151
1
					CurrencyPayment {
152
1
						currency: Currency::AsMultiLocation(Box::new(
153
1
							xcm::VersionedLocation::from(Location::new(
154
1
								1,
155
1
								[Junction::Parachain(1000)]
156
1
							))
157
1
						)),
158
1
						fee_amount: None
159
1
					},
160
1
					vec![0u8],
161
1
					TransactWeights {
162
1
						transact_required_weight_at_most: 100u64.into(),
163
1
						overall_weight: None
164
1
					},
165
					false
166
				),
167
1
				Error::<Test>::AssetIsNotReserveInDestination
168
			);
169

            
170
			// Set relative price using FeeTrader
171
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
172
1
				Location::parent(),
173
				TEST_RELATIVE_PRICE
174
			));
175

            
176
			// Cannot exceed the max weight
177
1
			assert_noop!(
178
1
				XcmTransactor::transact_through_derivative(
179
1
					RuntimeOrigin::signed(1u64),
180
1
					Transactors::Relay,
181
					1,
182
1
					CurrencyPayment {
183
1
						currency: Currency::AsMultiLocation(Box::new(
184
1
							xcm::VersionedLocation::from(Location::parent())
185
1
						)),
186
1
						fee_amount: None
187
1
					},
188
1
					vec![0u8],
189
1
					TransactWeights {
190
1
						transact_required_weight_at_most: 10001u64.into(),
191
1
						overall_weight: None
192
1
					},
193
					false
194
				),
195
1
				Error::<Test>::MaxWeightTransactReached
196
			);
197
1
		})
198
1
}
199

            
200
#[test]
201
1
fn test_transact_through_signed_errors() {
202
1
	ExtBuilder::default()
203
1
		.with_balances(vec![])
204
1
		.build()
205
1
		.execute_with(|| {
206
			// TransactInfo not yet set
207
1
			assert_noop!(
208
1
				XcmTransactor::transact_through_signed(
209
1
					RuntimeOrigin::signed(1u64),
210
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
211
1
					CurrencyPayment {
212
1
						currency: Currency::AsMultiLocation(Box::new(
213
1
							xcm::VersionedLocation::from(Location::parent())
214
1
						)),
215
1
						fee_amount: None
216
1
					},
217
1
					vec![0u8],
218
1
					TransactWeights {
219
1
						transact_required_weight_at_most: 100u64.into(),
220
1
						overall_weight: None
221
1
					},
222
					false
223
				),
224
1
				Error::<Test>::TransactorInfoNotSet
225
			);
226

            
227
			// Root can set transact info without extra_signed being None
228
1
			assert_ok!(XcmTransactor::set_transact_info(
229
1
				RuntimeOrigin::root(),
230
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
231
1
				0.into(),
232
1
				10000.into(),
233
1
				None
234
			));
235

            
236
			// TransactInfo present, but FeePerSecond not set
237
1
			assert_noop!(
238
1
				XcmTransactor::transact_through_signed(
239
1
					RuntimeOrigin::signed(1u64),
240
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
241
1
					CurrencyPayment {
242
1
						currency: Currency::AsMultiLocation(Box::new(
243
1
							xcm::VersionedLocation::from(Location::parent())
244
1
						)),
245
1
						fee_amount: None
246
1
					},
247
1
					vec![0u8],
248
1
					TransactWeights {
249
1
						transact_required_weight_at_most: 100u64.into(),
250
1
						overall_weight: None
251
1
					},
252
					false
253
				),
254
1
				Error::<Test>::SignedTransactNotAllowedForDestination
255
			);
256

            
257
			// Root can set transact info, with extra signed
258
1
			assert_ok!(XcmTransactor::set_transact_info(
259
1
				RuntimeOrigin::root(),
260
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
261
1
				0.into(),
262
1
				15000.into(),
263
1
				Some(12000.into())
264
			));
265

            
266
			// TransactInfo present, but FeePerSecond not set
267
1
			assert_noop!(
268
1
				XcmTransactor::transact_through_signed(
269
1
					RuntimeOrigin::signed(1u64),
270
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
271
1
					CurrencyPayment {
272
1
						currency: Currency::AsMultiLocation(Box::new(
273
1
							xcm::VersionedLocation::from(Location::parent())
274
1
						)),
275
1
						fee_amount: None
276
1
					},
277
1
					vec![0u8],
278
1
					TransactWeights {
279
1
						transact_required_weight_at_most: 100u64.into(),
280
1
						overall_weight: None
281
1
					},
282
					false
283
				),
284
1
				DispatchError::Other("Asset relative price not set")
285
			);
286

            
287
			// Set relative price using FeeTrader
288
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
289
1
				Location::new(1, [Junction::Parachain(1000)]),
290
				TEST_RELATIVE_PRICE
291
			));
292

            
293
			// TransactInfo present, but the asset is not a reserve of dest
294
1
			assert_noop!(
295
1
				XcmTransactor::transact_through_signed(
296
1
					RuntimeOrigin::signed(1u64),
297
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
298
1
					CurrencyPayment {
299
1
						currency: Currency::AsMultiLocation(Box::new(
300
1
							xcm::VersionedLocation::from(Location::new(
301
1
								1,
302
1
								[Junction::Parachain(1000)]
303
1
							))
304
1
						)),
305
1
						fee_amount: None
306
1
					},
307
1
					vec![0u8],
308
1
					TransactWeights {
309
1
						transact_required_weight_at_most: 100u64.into(),
310
1
						overall_weight: None
311
1
					},
312
					false
313
				),
314
1
				Error::<Test>::AssetIsNotReserveInDestination
315
			);
316
1
		})
317
1
}
318

            
319
#[test]
320
1
fn test_transact_through_derivative_multilocation_success() {
321
1
	ExtBuilder::default()
322
1
		.with_balances(vec![])
323
1
		.build()
324
1
		.execute_with(|| {
325
			// Root can register
326
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
327

            
328
			// Root can set transact info
329
1
			assert_ok!(XcmTransactor::set_transact_info(
330
1
				RuntimeOrigin::root(),
331
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
332
1
				0.into(),
333
1
				10000.into(),
334
1
				None
335
			));
336

            
337
			// Set fee per second using FeeTrader
338
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
339
1
				Location::parent(),
340
				1
341
			));
342

            
343
			// fee as destination are the same, this time it should work
344
1
			assert_ok!(XcmTransactor::transact_through_derivative(
345
1
				RuntimeOrigin::signed(1u64),
346
1
				Transactors::Relay,
347
				1,
348
1
				CurrencyPayment {
349
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
350
1
						Location::parent()
351
1
					))),
352
1
					fee_amount: None
353
1
				},
354
1
				vec![1u8],
355
1
				TransactWeights {
356
1
					transact_required_weight_at_most: 100u64.into(),
357
1
					overall_weight: None
358
1
				},
359
				false
360
			));
361
1
			let expected = vec![
362
1
				crate::Event::RegisteredDerivative {
363
1
					account_id: 1u64,
364
1
					index: 1,
365
1
				},
366
1
				crate::Event::TransactInfoChanged {
367
1
					location: Location::parent(),
368
1
					remote_info: RemoteTransactInfoWithMaxWeight {
369
1
						transact_extra_weight: 0.into(),
370
1
						max_weight: 10000.into(),
371
1
						transact_extra_weight_signed: None,
372
1
					},
373
1
				},
374
				// DestFeePerSecondChanged event removed - fee configuration now handled by pallet-xcm-weight-trader
375
1
				crate::Event::TransactedDerivative {
376
1
					account_id: 1u64,
377
1
					dest: Location::parent(),
378
1
					call: <XcmTransactor as UtilityEncodeCall>::encode_call(
379
1
						Transactors::Relay,
380
1
						UtilityAvailableCalls::AsDerivative(1, vec![1u8]),
381
1
					),
382
1
					index: 1,
383
1
				},
384
			];
385
1
			assert_eq!(events(), expected);
386
1
		})
387
1
}
388

            
389
#[test]
390
1
fn test_transact_through_derivative_success() {
391
1
	ExtBuilder::default()
392
1
		.with_balances(vec![])
393
1
		.build()
394
1
		.execute_with(|| {
395
			// Root can register
396
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
397

            
398
			// Root can set transact info
399
1
			assert_ok!(XcmTransactor::set_transact_info(
400
1
				RuntimeOrigin::root(),
401
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
402
1
				0.into(),
403
1
				10000.into(),
404
1
				None
405
			));
406

            
407
			// Set fee per second using FeeTrader
408
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
409
1
				Location::parent(),
410
				1
411
			));
412

            
413
			// fee as destination are the same, this time it should work
414
1
			assert_ok!(XcmTransactor::transact_through_derivative(
415
1
				RuntimeOrigin::signed(1u64),
416
1
				Transactors::Relay,
417
				1,
418
1
				CurrencyPayment {
419
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
420
1
					fee_amount: None
421
1
				},
422
1
				vec![1u8],
423
1
				TransactWeights {
424
1
					transact_required_weight_at_most: 100u64.into(),
425
1
					overall_weight: None
426
1
				},
427
				false
428
			));
429
1
			let expected = vec![
430
1
				crate::Event::RegisteredDerivative {
431
1
					account_id: 1u64,
432
1
					index: 1,
433
1
				},
434
1
				crate::Event::TransactInfoChanged {
435
1
					location: Location::parent(),
436
1
					remote_info: RemoteTransactInfoWithMaxWeight {
437
1
						transact_extra_weight: 0.into(),
438
1
						max_weight: 10000.into(),
439
1
						transact_extra_weight_signed: None,
440
1
					},
441
1
				},
442
				// DestFeePerSecondChanged event removed - fee configuration now handled by pallet-xcm-weight-trader
443
1
				crate::Event::TransactedDerivative {
444
1
					account_id: 1u64,
445
1
					dest: Location::parent(),
446
1
					call: <XcmTransactor as UtilityEncodeCall>::encode_call(
447
1
						Transactors::Relay,
448
1
						UtilityAvailableCalls::AsDerivative(1, vec![1u8]),
449
1
					),
450
1
					index: 1,
451
1
				},
452
			];
453
1
			assert_eq!(events(), expected);
454
1
			let sent_messages = mock::sent_xcm();
455
1
			let (_, sent_message) = sent_messages.first().unwrap();
456

            
457
			// Check message doesn't contain the appendix
458
1
			assert!(!sent_message.0.contains(&SetAppendix(Xcm(vec![
459
1
				RefundSurplus,
460
1
				DepositAsset {
461
1
					assets: Wild(AllCounted(1u32)),
462
1
					beneficiary: Location {
463
1
						parents: 0,
464
1
						interior: [Junction::Parachain(100)].into()
465
1
					}
466
1
				}
467
1
			]))));
468
1
		})
469
1
}
470

            
471
#[test]
472
1
fn test_root_can_transact_through_sovereign() {
473
1
	ExtBuilder::default()
474
1
		.with_balances(vec![])
475
1
		.build()
476
1
		.execute_with(|| {
477
			// Only root can do this
478
1
			assert_noop!(
479
1
				XcmTransactor::transact_through_sovereign(
480
1
					RuntimeOrigin::signed(1),
481
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
482
1
					Some(1u64),
483
1
					CurrencyPayment {
484
1
						currency: Currency::AsMultiLocation(Box::new(
485
1
							xcm::VersionedLocation::from(Location::parent())
486
1
						)),
487
1
						fee_amount: None
488
1
					},
489
1
					vec![1u8],
490
1
					OriginKind::SovereignAccount,
491
1
					TransactWeights {
492
1
						transact_required_weight_at_most: 100u64.into(),
493
1
						overall_weight: None
494
1
					},
495
					false
496
				),
497
1
				DispatchError::BadOrigin
498
			);
499

            
500
			// Root can set transact info
501
1
			assert_ok!(XcmTransactor::set_transact_info(
502
1
				RuntimeOrigin::root(),
503
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
504
1
				0.into(),
505
1
				10000.into(),
506
1
				None
507
			));
508

            
509
			// Set fee per second using FeeTrader
510
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
511
1
				Location::parent(),
512
				1
513
			));
514

            
515
			// fee as destination are the same, this time it should work
516
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
517
1
				RuntimeOrigin::root(),
518
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
519
1
				Some(1u64),
520
1
				CurrencyPayment {
521
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
522
1
						Location::parent()
523
1
					))),
524
1
					fee_amount: None
525
1
				},
526
1
				vec![1u8],
527
1
				OriginKind::SovereignAccount,
528
1
				TransactWeights {
529
1
					transact_required_weight_at_most: 100u64.into(),
530
1
					overall_weight: None
531
1
				},
532
				false
533
			));
534

            
535
1
			let expected = vec![
536
1
				crate::Event::TransactInfoChanged {
537
1
					location: Location::parent(),
538
1
					remote_info: RemoteTransactInfoWithMaxWeight {
539
1
						transact_extra_weight: 0.into(),
540
1
						max_weight: 10000.into(),
541
1
						transact_extra_weight_signed: None,
542
1
					},
543
1
				},
544
				// DestFeePerSecondChanged event removed - fee configuration now handled by pallet-xcm-weight-trader
545
1
				crate::Event::TransactedSovereign {
546
1
					fee_payer: Some(1u64),
547
1
					dest: Location::parent(),
548
1
					call: vec![1u8],
549
1
				},
550
			];
551
1
			assert_eq!(events(), expected);
552
1
		})
553
1
}
554

            
555
// Removed: test_fee_calculation_works
556
// The calculate_fee_per_second function was removed. Fee calculation is now handled by pallet-xcm-weight-trader.
557

            
558
// Removed: test_fee_calculation_works_kusama_0_9_20_case
559
// The calculate_fee_per_second function was removed. Fee calculation is now handled by pallet-xcm-weight-trader.
560

            
561
#[test]
562
1
fn de_registering_works() {
563
1
	ExtBuilder::default()
564
1
		.with_balances(vec![])
565
1
		.build()
566
1
		.execute_with(|| {
567
			// Root can register
568
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
569

            
570
1
			assert_eq!(XcmTransactor::index_to_account(&1).unwrap(), 1u64);
571

            
572
1
			assert_ok!(XcmTransactor::deregister(RuntimeOrigin::root(), 1));
573

            
574
1
			assert!(XcmTransactor::index_to_account(&1).is_none());
575

            
576
1
			let expected = vec![
577
1
				crate::Event::RegisteredDerivative {
578
1
					account_id: 1u64,
579
1
					index: 1,
580
1
				},
581
1
				crate::Event::DeRegisteredDerivative { index: 1 },
582
			];
583
1
			assert_eq!(events(), expected);
584
1
		})
585
1
}
586

            
587
#[test]
588
1
fn removing_transact_info_works() {
589
1
	ExtBuilder::default()
590
1
		.with_balances(vec![])
591
1
		.build()
592
1
		.execute_with(|| {
593
			// Root can set transact info
594
1
			assert_ok!(XcmTransactor::set_transact_info(
595
1
				RuntimeOrigin::root(),
596
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
597
1
				0.into(),
598
1
				10000.into(),
599
1
				None
600
			));
601

            
602
			// Root can remove transact info
603
1
			assert_ok!(XcmTransactor::remove_transact_info(
604
1
				RuntimeOrigin::root(),
605
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
606
			));
607

            
608
1
			assert!(XcmTransactor::transact_info(Location::parent()).is_none());
609

            
610
1
			let expected = vec![
611
1
				crate::Event::TransactInfoChanged {
612
1
					location: Location::parent(),
613
1
					remote_info: RemoteTransactInfoWithMaxWeight {
614
1
						transact_extra_weight: 0.into(),
615
1
						max_weight: 10000.into(),
616
1
						transact_extra_weight_signed: None,
617
1
					},
618
1
				},
619
1
				crate::Event::TransactInfoRemoved {
620
1
					location: Location::parent(),
621
1
				},
622
			];
623
1
			assert_eq!(events(), expected);
624
1
		})
625
1
}
626

            
627
#[test]
628
1
fn test_transact_through_signed_fails_if_transact_info_not_set_at_all() {
629
1
	ExtBuilder::default()
630
1
		.with_balances(vec![])
631
1
		.build()
632
1
		.execute_with(|| {
633
			// fee as destination are the same, this time it should work
634
1
			assert_noop!(
635
1
				XcmTransactor::transact_through_signed(
636
1
					RuntimeOrigin::signed(1u64),
637
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
638
1
					CurrencyPayment {
639
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
640
1
						fee_amount: None
641
1
					},
642
1
					vec![1u8],
643
1
					TransactWeights {
644
1
						transact_required_weight_at_most: 100u64.into(),
645
1
						overall_weight: None
646
1
					},
647
					false
648
				),
649
1
				Error::<Test>::TransactorInfoNotSet
650
			);
651
1
		})
652
1
}
653

            
654
#[test]
655
1
fn test_transact_through_signed_fails_if_weight_is_not_set() {
656
1
	ExtBuilder::default()
657
1
		.with_balances(vec![])
658
1
		.build()
659
1
		.execute_with(|| {
660
			// Root can set transact info
661
1
			assert_ok!(XcmTransactor::set_transact_info(
662
1
				RuntimeOrigin::root(),
663
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
664
1
				0.into(),
665
1
				10000.into(),
666
1
				None
667
			));
668

            
669
			// weight value not set for signed transact, fails
670
1
			assert_noop!(
671
1
				XcmTransactor::transact_through_signed(
672
1
					RuntimeOrigin::signed(1u64),
673
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
674
1
					CurrencyPayment {
675
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
676
1
						fee_amount: None
677
1
					},
678
1
					vec![1u8],
679
1
					TransactWeights {
680
1
						transact_required_weight_at_most: 100u64.into(),
681
1
						overall_weight: None
682
1
					},
683
					false
684
				),
685
1
				Error::<Test>::SignedTransactNotAllowedForDestination
686
			);
687
1
		})
688
1
}
689

            
690
#[test]
691
1
fn test_transact_through_signed_fails_if_weight_overflows() {
692
1
	ExtBuilder::default()
693
1
		.with_balances(vec![])
694
1
		.build()
695
1
		.execute_with(|| {
696
			// Root can set transact info
697
1
			assert_ok!(XcmTransactor::set_transact_info(
698
1
				RuntimeOrigin::root(),
699
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
700
1
				0.into(),
701
1
				10000.into(),
702
1
				Some(Weight::MAX)
703
			));
704

            
705
			// weight should overflow
706
1
			assert_noop!(
707
1
				XcmTransactor::transact_through_signed(
708
1
					RuntimeOrigin::signed(1u64),
709
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
710
1
					CurrencyPayment {
711
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
712
1
						fee_amount: None
713
1
					},
714
1
					vec![1u8],
715
1
					TransactWeights {
716
1
						transact_required_weight_at_most: 10064u64.into(),
717
1
						overall_weight: None
718
1
					},
719
					false
720
				),
721
1
				Error::<Test>::WeightOverflow
722
			);
723
1
		})
724
1
}
725

            
726
#[test]
727
1
fn test_transact_through_signed_fails_if_weight_is_bigger_than_max_weight() {
728
1
	ExtBuilder::default()
729
1
		.with_balances(vec![])
730
1
		.build()
731
1
		.execute_with(|| {
732
			// Root can set transact info
733
1
			assert_ok!(XcmTransactor::set_transact_info(
734
1
				RuntimeOrigin::root(),
735
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
736
1
				0.into(),
737
1
				10000.into(),
738
1
				Some(1.into())
739
			));
740

            
741
			// 10000 + 1 > 10000 (max weight permitted by dest chain)
742
1
			assert_noop!(
743
1
				XcmTransactor::transact_through_signed(
744
1
					RuntimeOrigin::signed(1u64),
745
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
746
1
					CurrencyPayment {
747
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
748
1
						fee_amount: None
749
1
					},
750
1
					vec![1u8],
751
1
					TransactWeights {
752
1
						transact_required_weight_at_most: 100000u64.into(),
753
1
						overall_weight: None
754
1
					},
755
					false
756
				),
757
1
				Error::<Test>::MaxWeightTransactReached
758
			);
759
1
		})
760
1
}
761

            
762
#[test]
763
1
fn test_transact_through_signed_fails_if_fee_per_second_not_set() {
764
1
	ExtBuilder::default()
765
1
		.with_balances(vec![])
766
1
		.build()
767
1
		.execute_with(|| {
768
			// Root can set transact info
769
1
			assert_ok!(XcmTransactor::set_transact_info(
770
1
				RuntimeOrigin::root(),
771
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
772
1
				0.into(),
773
1
				10000.into(),
774
1
				Some(1.into())
775
			));
776

            
777
			// fee per second not set, fails
778
1
			assert_noop!(
779
1
				XcmTransactor::transact_through_signed(
780
1
					RuntimeOrigin::signed(1u64),
781
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
782
1
					CurrencyPayment {
783
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
784
1
						fee_amount: None
785
1
					},
786
1
					vec![1u8],
787
1
					TransactWeights {
788
1
						transact_required_weight_at_most: 100u64.into(),
789
1
						overall_weight: None
790
1
					},
791
					false
792
				),
793
1
				DispatchError::Other("Asset relative price not set")
794
			);
795
1
		})
796
1
}
797

            
798
#[test]
799
1
fn test_transact_through_signed_works() {
800
1
	ExtBuilder::default()
801
1
		.with_balances(vec![])
802
1
		.build()
803
1
		.execute_with(|| {
804
			// Root can set transact info
805
1
			assert_ok!(XcmTransactor::set_transact_info(
806
1
				RuntimeOrigin::root(),
807
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
808
1
				0.into(),
809
1
				10000.into(),
810
1
				Some(1.into())
811
			));
812

            
813
			// Set relative price using FeeTrader
814
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
815
1
				Location::parent(),
816
				TEST_RELATIVE_PRICE
817
			));
818

            
819
			// transact info and fee per second set
820
			// this time it should work
821
1
			assert_ok!(XcmTransactor::transact_through_signed(
822
1
				RuntimeOrigin::signed(1u64),
823
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
824
1
				CurrencyPayment {
825
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
826
1
					fee_amount: None
827
1
				},
828
1
				vec![1u8],
829
1
				TransactWeights {
830
1
					transact_required_weight_at_most: 100u64.into(),
831
1
					overall_weight: None
832
1
				},
833
				false
834
			));
835

            
836
1
			let expected = vec![
837
1
				crate::Event::TransactInfoChanged {
838
1
					location: Location::parent(),
839
1
					remote_info: RemoteTransactInfoWithMaxWeight {
840
1
						transact_extra_weight: 0.into(),
841
1
						max_weight: 10000.into(),
842
1
						transact_extra_weight_signed: Some(1.into()),
843
1
					},
844
1
				},
845
				// DestFeePerSecondChanged event removed - fee configuration now handled by pallet-xcm-weight-trader
846
1
				crate::Event::TransactedSigned {
847
1
					fee_payer: 1u64,
848
1
					dest: Location::parent(),
849
1
					call: vec![1u8],
850
1
				},
851
			];
852
1
			assert_eq!(events(), expected);
853
1
		})
854
1
}
855

            
856
#[test]
857
1
fn test_send_through_derivative_with_custom_weight_and_fee() {
858
1
	ExtBuilder::default()
859
1
		.with_balances(vec![])
860
1
		.build()
861
1
		.execute_with(|| {
862
			// Root can register
863
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
864

            
865
			// We are gonna use a total weight of 10_100, a tx weight of 100,
866
			// and a total fee of 100
867
1
			let total_weight: Weight = 10_100u64.into();
868
1
			let tx_weight: Weight = 100_u64.into();
869
1
			let total_fee = 100u128;
870

            
871
			// By specifying total fee and total weight, we ensure
872
			// that even if the transact_info is not populated,
873
			// the message is forged with our parameters
874
1
			assert_ok!(XcmTransactor::transact_through_derivative(
875
1
				RuntimeOrigin::signed(1u64),
876
1
				Transactors::Relay,
877
				1,
878
1
				CurrencyPayment {
879
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
880
1
					fee_amount: Some(total_fee)
881
1
				},
882
1
				vec![1u8],
883
1
				TransactWeights {
884
1
					transact_required_weight_at_most: tx_weight,
885
1
					overall_weight: Some(Limited(total_weight))
886
1
				},
887
				false
888
			));
889
1
			let expected = vec![
890
1
				crate::Event::RegisteredDerivative {
891
1
					account_id: 1u64,
892
1
					index: 1,
893
1
				},
894
1
				crate::Event::TransactedDerivative {
895
1
					account_id: 1u64,
896
1
					dest: Location::parent(),
897
1
					call: <XcmTransactor as UtilityEncodeCall>::encode_call(
898
1
						Transactors::Relay,
899
1
						UtilityAvailableCalls::AsDerivative(1, vec![1u8]),
900
1
					),
901
1
					index: 1,
902
1
				},
903
			];
904
1
			assert_eq!(events(), expected);
905
1
			let sent_messages = mock::sent_xcm();
906
1
			let (_, sent_message) = sent_messages.first().unwrap();
907
			// Lets make sure the message is as expected
908
1
			assert!(sent_message
909
1
				.0
910
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
911
1
			assert!(sent_message.0.contains(&BuyExecution {
912
1
				fees: (Location::here(), total_fee).into(),
913
1
				weight_limit: Limited(total_weight),
914
1
			}));
915
1
			assert!(sent_message.0.contains(&Transact {
916
1
				origin_kind: OriginKind::SovereignAccount,
917
1
				fallback_max_weight: Some(tx_weight),
918
1
				call: <XcmTransactor as UtilityEncodeCall>::encode_call(
919
1
					Transactors::Relay,
920
1
					UtilityAvailableCalls::AsDerivative(1, vec![1u8])
921
1
				)
922
1
				.into(),
923
1
			}));
924
1
		})
925
1
}
926

            
927
#[test]
928
1
fn test_send_through_sovereign_with_custom_weight_and_fee() {
929
1
	ExtBuilder::default()
930
1
		.with_balances(vec![])
931
1
		.build()
932
1
		.execute_with(|| {
933
			// Root can register
934
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
935

            
936
			// We are gonna use a total weight of 10_100, a tx weight of 100,
937
			// and a total fee of 100
938
1
			let total_weight: Weight = 10_100u64.into();
939
1
			let tx_weight: Weight = 100_u64.into();
940
1
			let total_fee = 100u128;
941

            
942
			// By specifying total fee and total weight, we ensure
943
			// that even if the transact_info is not populated,
944
			// the message is forged with our parameters
945

            
946
			// fee as destination are the same, this time it should work
947
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
948
1
				RuntimeOrigin::root(),
949
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
950
1
				Some(1u64),
951
1
				CurrencyPayment {
952
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
953
1
						Location::parent()
954
1
					))),
955
1
					fee_amount: Some(total_fee)
956
1
				},
957
1
				vec![1u8],
958
1
				OriginKind::SovereignAccount,
959
1
				TransactWeights {
960
1
					transact_required_weight_at_most: tx_weight,
961
1
					overall_weight: Some(Limited(total_weight))
962
1
				},
963
				false
964
			));
965

            
966
1
			let expected = vec![
967
1
				crate::Event::RegisteredDerivative {
968
1
					account_id: 1u64,
969
1
					index: 1,
970
1
				},
971
1
				crate::Event::TransactedSovereign {
972
1
					fee_payer: Some(1u64),
973
1
					dest: Location::parent(),
974
1
					call: vec![1u8],
975
1
				},
976
			];
977
1
			assert_eq!(events(), expected);
978
1
			let sent_messages = mock::sent_xcm();
979
1
			let (_, sent_message) = sent_messages.first().unwrap();
980
			// Lets make sure the message is as expected
981
1
			assert!(sent_message
982
1
				.0
983
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
984
1
			assert!(sent_message.0.contains(&BuyExecution {
985
1
				fees: (Location::here(), total_fee).into(),
986
1
				weight_limit: Limited(total_weight),
987
1
			}));
988
1
			assert!(sent_message.0.contains(&Transact {
989
1
				origin_kind: OriginKind::SovereignAccount,
990
1
				fallback_max_weight: Some(tx_weight),
991
1
				call: vec![1u8].into(),
992
1
			}));
993
1
		})
994
1
}
995

            
996
#[test]
997
1
fn test_transact_through_sovereign_with_fee_payer_none() {
998
1
	ExtBuilder::default()
999
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// Root can register
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
				// We don't specify any fee_payer, instead we pay fees with the
				// sovereign account funds directly on the destination.
1
				None,
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				vec![1u8],
1
				OriginKind::SovereignAccount,
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				},
				false
			));
1
			let expected = vec![
1
				crate::Event::RegisteredDerivative {
1
					account_id: 1u64,
1
					index: 1,
1
				},
1
				crate::Event::TransactedSovereign {
1
					fee_payer: None,
1
					dest: Location::parent(),
1
					call: vec![1u8],
1
				},
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected even if we haven't indicated a
			// fee_payer.
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::SovereignAccount,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![1u8].into(),
1
			}));
1
		})
1
}
#[test]
1
fn test_send_through_signed_with_custom_weight_and_fee() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
			// By specifying total fee and total weight, we ensure
			// that even if the transact_info is not populated,
			// the message is forged with our parameters
			// fee as destination are the same, this time it should work
1
			assert_ok!(XcmTransactor::transact_through_signed(
1
				RuntimeOrigin::signed(1u64),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				vec![1u8],
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				},
				false
			));
1
			let expected = vec![crate::Event::TransactedSigned {
1
				fee_payer: 1u64,
1
				dest: Location::parent(),
1
				call: vec![1u8],
1
			}];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::SovereignAccount,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![1u8].into(),
1
			}));
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_init() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
1
			assert_ok!(XcmTransactor::hrmp_manage(
1
				RuntimeOrigin::root(),
1
				HrmpOperation::InitOpen(HrmpInitParams {
1
					para_id: 1u32.into(),
1
					proposed_max_capacity: 1,
1
					proposed_max_message_size: 1
1
				}),
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				}
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::Native,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0].into(),
1
			}));
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_init_v3_convert_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
			// Change xcm version
1
			CustomVersionWrapper::set_version(3);
1
			assert_ok!(XcmTransactor::hrmp_manage(
1
				RuntimeOrigin::root(),
1
				HrmpOperation::InitOpen(HrmpInitParams {
1
					para_id: 1u32.into(),
1
					proposed_max_capacity: 1,
1
					proposed_max_message_size: 1
1
				}),
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				}
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::Native,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0].into(),
1
			}));
			// Check message contains the new appendix
1
			assert!(sent_message.0.contains(&SetAppendix(Xcm(vec![
1
				RefundSurplus,
1
				DepositAsset {
1
					assets: Wild(AllCounted(1)),
1
					beneficiary: Location {
1
						parents: 0,
1
						interior: [Junction::Parachain(100)].into()
1
					}
1
				}
1
			]))));
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_init_v5_convert_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
			// Change xcm version
1
			CustomVersionWrapper::set_version(5);
1
			assert_ok!(XcmTransactor::hrmp_manage(
1
				RuntimeOrigin::root(),
1
				HrmpOperation::InitOpen(HrmpInitParams {
1
					para_id: 1u32.into(),
1
					proposed_max_capacity: 1,
1
					proposed_max_message_size: 1
1
				}),
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				}
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::Native,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0].into(),
1
			}));
			// Check message contains the new appendix
1
			assert!(sent_message.0.contains(&SetAppendix(Xcm(vec![
1
				RefundSurplus,
1
				DepositAsset {
1
					assets: Wild(AllCounted(1)),
1
					beneficiary: Location {
1
						parents: 0,
1
						interior: [Junction::Parachain(100)].into()
1
					}
1
				}
1
			]))));
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_init_v6_convert_fails() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
			// Change xcm version
1
			CustomVersionWrapper::set_version(6);
1
			assert_noop!(
1
				XcmTransactor::hrmp_manage(
1
					RuntimeOrigin::root(),
1
					HrmpOperation::InitOpen(HrmpInitParams {
1
						para_id: 1u32.into(),
1
						proposed_max_capacity: 1,
1
						proposed_max_message_size: 1
1
					}),
1
					CurrencyPayment {
1
						currency: Currency::AsMultiLocation(Box::new(
1
							xcm::VersionedLocation::from(Location::parent())
1
						)),
1
						fee_amount: Some(total_fee)
1
					},
1
					TransactWeights {
1
						transact_required_weight_at_most: tx_weight,
1
						overall_weight: Some(Limited(total_weight))
1
					}
				),
1
				Error::<Test>::ErrorValidating
			);
1
		})
1
}
#[test]
1
fn test_hrmp_max_fee_errors() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 10_000_000_000_000u128;
1
			assert_noop!(
1
				XcmTransactor::hrmp_manage(
1
					RuntimeOrigin::root(),
1
					HrmpOperation::InitOpen(HrmpInitParams {
1
						para_id: 1u32.into(),
1
						proposed_max_capacity: 1,
1
						proposed_max_message_size: 1
1
					}),
1
					CurrencyPayment {
1
						currency: Currency::AsMultiLocation(Box::new(
1
							xcm::VersionedLocation::from(Location::parent())
1
						)),
1
						fee_amount: Some(total_fee)
1
					},
1
					TransactWeights {
1
						transact_required_weight_at_most: tx_weight,
1
						overall_weight: Some(Limited(total_weight))
1
					}
				),
1
				Error::<Test>::TooMuchFeeUsed
			);
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_accept() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
1
			assert_ok!(XcmTransactor::hrmp_manage(
1
				RuntimeOrigin::root(),
1
				HrmpOperation::Accept {
1
					para_id: 1u32.into()
1
				},
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				}
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::Native,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![0, 0, 1, 0, 0, 0].into(),
1
			}));
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_cancel() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
1
			let channel_id = HrmpChannelId {
1
				sender: 1u32.into(),
1
				recipient: 1u32.into(),
1
			};
1
			let open_requests: u32 = 1;
1
			assert_ok!(XcmTransactor::hrmp_manage(
1
				RuntimeOrigin::root(),
1
				HrmpOperation::Cancel {
1
					channel_id,
1
					open_requests
1
				},
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				}
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::Native,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0].into(),
1
			}));
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_close() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// We are gonna use a total weight of 10_100, a tx weight of 100,
			// and a total fee of 100
1
			let total_weight: Weight = 10_100u64.into();
1
			let tx_weight: Weight = 100_u64.into();
1
			let total_fee = 100u128;
1
			assert_ok!(XcmTransactor::hrmp_manage(
1
				RuntimeOrigin::root(),
1
				HrmpOperation::Close(HrmpChannelId {
1
					sender: 1u32.into(),
1
					recipient: 1u32.into()
1
				}),
1
				CurrencyPayment {
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
1
						Location::parent()
1
					))),
1
					fee_amount: Some(total_fee)
1
				},
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				}
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Lets make sure the message is as expected
1
			assert!(sent_message
1
				.0
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
1
			assert!(sent_message.0.contains(&BuyExecution {
1
				fees: (Location::here(), total_fee).into(),
1
				weight_limit: Limited(total_weight),
1
			}));
1
			assert!(sent_message.0.contains(&Transact {
1
				origin_kind: OriginKind::Native,
1
				fallback_max_weight: Some(tx_weight),
1
				call: vec![0, 0, 1, 0, 0, 0, 1, 0, 0, 0].into(),
1
			}));
1
		})
1
}
#[test]
1
fn test_transact_through_derivative_with_refund_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// Root can register
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
			// Root can set transact info
1
			assert_ok!(XcmTransactor::set_transact_info(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				0.into(),
1
				10000.into(),
1
				None
			));
			// Set fee per second using FeeTrader
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
1
				Location::parent(),
				1
			));
			// fee as destination are the same, this time it should work
1
			assert_ok!(XcmTransactor::transact_through_derivative(
1
				RuntimeOrigin::signed(1u64),
1
				Transactors::Relay,
				1,
1
				CurrencyPayment {
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
1
					fee_amount: None
1
				},
1
				vec![1u8],
1
				TransactWeights {
1
					transact_required_weight_at_most: 100u64.into(),
1
					overall_weight: Some(Limited(1000.into()))
1
				},
				true
			));
1
			let expected = vec![
1
				crate::Event::RegisteredDerivative {
1
					account_id: 1u64,
1
					index: 1,
1
				},
1
				crate::Event::TransactInfoChanged {
1
					location: Location::parent(),
1
					remote_info: RemoteTransactInfoWithMaxWeight {
1
						transact_extra_weight: 0.into(),
1
						max_weight: 10000.into(),
1
						transact_extra_weight_signed: None,
1
					},
1
				},
				// DestFeePerSecondChanged event removed - fee configuration now handled by pallet-xcm-weight-trader
1
				crate::Event::TransactedDerivative {
1
					account_id: 1u64,
1
					dest: Location::parent(),
1
					call: <XcmTransactor as UtilityEncodeCall>::encode_call(
1
						Transactors::Relay,
1
						UtilityAvailableCalls::AsDerivative(1, vec![1u8]),
1
					),
1
					index: 1,
1
				},
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Check message contains the new appendix
1
			assert!(sent_message.0.contains(&SetAppendix(Xcm(vec![
1
				RefundSurplus,
1
				DepositAsset {
1
					assets: Wild(AllCounted(1u32)),
1
					beneficiary: Location {
1
						parents: 0,
1
						interior: [Junction::Parachain(100)].into()
1
					}
1
				}
1
			]))));
1
		})
1
}
#[test]
1
fn test_transact_through_derivative_with_refund_fails_overall_weight_not_set() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// Root can register
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
			// Root can set transact info
1
			assert_ok!(XcmTransactor::set_transact_info(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				0.into(),
1
				10000.into(),
1
				None
			));
			// Set fee per second using FeeTrader
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
1
				Location::parent(),
				1
			));
			// fee as destination are the same, this time it should work
1
			assert_noop!(
1
				XcmTransactor::transact_through_derivative(
1
					RuntimeOrigin::signed(1u64),
1
					Transactors::Relay,
					1,
1
					CurrencyPayment {
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
1
						fee_amount: None
1
					},
1
					vec![1u8],
1
					TransactWeights {
1
						transact_required_weight_at_most: 100u64.into(),
1
						overall_weight: None
1
					},
					true
				),
1
				Error::<Test>::RefundNotSupportedWithTransactInfo
			);
1
		})
1
}
#[test]
1
fn test_transact_through_signed_with_refund_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
			// Set fee per second using FeeTrader
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
1
				Location::parent(),
				1
			));
			// Overall weight to use
1
			let total_weight: Weight = 10_100u64.into();
1
			assert_ok!(XcmTransactor::transact_through_signed(
1
				RuntimeOrigin::signed(1u64),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				CurrencyPayment {
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
1
					fee_amount: None
1
				},
1
				vec![1u8],
1
				TransactWeights {
1
					transact_required_weight_at_most: 100u64.into(),
1
					overall_weight: Some(Limited(total_weight))
1
				},
				true
			));
1
			let expected = vec![
				// DestFeePerSecondChanged event removed - fee configuration now handled by pallet-xcm-weight-trader
1
				crate::Event::TransactedSigned {
1
					fee_payer: 1u64,
1
					dest: Location::parent(),
1
					call: vec![1u8],
1
				},
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
			// Check message contains the new appendix
1
			assert!(sent_message.0.contains(&SetAppendix(Xcm(vec![
1
				RefundSurplus,
1
				DepositAsset {
1
					assets: Wild(AllCounted(1u32)),
1
					beneficiary: Location {
1
						parents: 0,
1
						interior: [
1
							Junction::Parachain(100),
1
							AccountIdToLocation::convert(1)
1
								.interior
1
								.take_first()
1
								.unwrap()
1
						]
1
						.into()
1
					}
1
				}
1
			]))));
1
		})
1
}