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

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

            
43
1
			assert_eq!(XcmTransactor::index_to_account(&1).unwrap(), 1u64);
44

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

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

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

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

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

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

            
137
			// Set fee per second using FeeTrader
138
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
139
1
				Location::new(1, [Junction::Parachain(1000)]),
140
				1
141
			));
142

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

            
168
			// Set fee per second using FeeTrader
169
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
170
1
				Location::parent(),
171
				1
172
			));
173

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

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

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

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

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

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

            
285
			// Set fee per second using FeeTrader
286
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
287
1
				Location::new(1, [Junction::Parachain(1000)]),
288
				1
289
			));
290

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
568
1
			assert_eq!(XcmTransactor::index_to_account(&1).unwrap(), 1u64);
569

            
570
1
			assert_ok!(XcmTransactor::deregister(RuntimeOrigin::root(), 1));
571

            
572
1
			assert!(XcmTransactor::index_to_account(&1).is_none());
573

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

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

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

            
606
1
			assert!(XcmTransactor::transact_info(Location::parent()).is_none());
607

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

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

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

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

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

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

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

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

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

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

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

            
811
			// Set fee per second using FeeTrader
812
1
			assert_ok!(<Test as Config>::FeeTrader::set_asset_price(
813
1
				Location::parent(),
814
				1
815
			));
816

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

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

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

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

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

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

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

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

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

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

            
994
#[test]
995
1
fn test_transact_through_sovereign_with_fee_payer_none() {
996
1
	ExtBuilder::default()
997
1
		.with_balances(vec![])
998
1
		.build()
999
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
}