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

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

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

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

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

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

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

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

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

            
136
			// Set fee per second
137
1
			assert_ok!(XcmTransactor::set_fee_per_second(
138
1
				RuntimeOrigin::root(),
139
1
				Box::new(xcm::VersionedLocation::from(Location::new(
140
1
					1,
141
1
					[Junction::Parachain(1000)]
142
1
				))),
143
1
				1
144
1
			));
145

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

            
171
			// Set fee per second
172
1
			assert_ok!(XcmTransactor::set_fee_per_second(
173
1
				RuntimeOrigin::root(),
174
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
175
1
				1
176
1
			));
177

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

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

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

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

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

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

            
289
			// Set fee per second
290
1
			assert_ok!(XcmTransactor::set_fee_per_second(
291
1
				RuntimeOrigin::root(),
292
1
				Box::new(xcm::VersionedLocation::from(Location::new(
293
1
					1,
294
1
					[Junction::Parachain(1000)]
295
1
				))),
296
1
				1
297
1
			));
298

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

            
325
#[test]
326
1
fn test_transact_through_derivative_multilocation_success() {
327
1
	ExtBuilder::default()
328
1
		.with_balances(vec![])
329
1
		.build()
330
1
		.execute_with(|| {
331
1
			// Root can register
332
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
333

            
334
			// Root can set transact info
335
1
			assert_ok!(XcmTransactor::set_transact_info(
336
1
				RuntimeOrigin::root(),
337
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
338
1
				0.into(),
339
1
				10000.into(),
340
1
				None
341
1
			));
342

            
343
			// Set fee per second
344
1
			assert_ok!(XcmTransactor::set_fee_per_second(
345
1
				RuntimeOrigin::root(),
346
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
347
1
				1
348
1
			));
349

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

            
397
#[test]
398
1
fn test_transact_through_derivative_success() {
399
1
	ExtBuilder::default()
400
1
		.with_balances(vec![])
401
1
		.build()
402
1
		.execute_with(|| {
403
1
			// Root can register
404
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
405

            
406
			// Root can set transact info
407
1
			assert_ok!(XcmTransactor::set_transact_info(
408
1
				RuntimeOrigin::root(),
409
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
410
1
				0.into(),
411
1
				10000.into(),
412
1
				None
413
1
			));
414

            
415
			// Set fee per second
416
1
			assert_ok!(XcmTransactor::set_fee_per_second(
417
1
				RuntimeOrigin::root(),
418
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
419
1
				1
420
1
			));
421

            
422
			// fee as destination are the same, this time it should work
423
1
			assert_ok!(XcmTransactor::transact_through_derivative(
424
1
				RuntimeOrigin::signed(1u64),
425
1
				Transactors::Relay,
426
1
				1,
427
1
				CurrencyPayment {
428
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
429
1
					fee_amount: None
430
1
				},
431
1
				vec![1u8],
432
1
				TransactWeights {
433
1
					transact_required_weight_at_most: 100u64.into(),
434
1
					overall_weight: None
435
1
				},
436
1
				false
437
1
			));
438
1
			let expected = vec![
439
1
				crate::Event::RegisteredDerivative {
440
1
					account_id: 1u64,
441
1
					index: 1,
442
1
				},
443
1
				crate::Event::TransactInfoChanged {
444
1
					location: Location::parent(),
445
1
					remote_info: RemoteTransactInfoWithMaxWeight {
446
1
						transact_extra_weight: 0.into(),
447
1
						max_weight: 10000.into(),
448
1
						transact_extra_weight_signed: None,
449
1
					},
450
1
				},
451
1
				crate::Event::DestFeePerSecondChanged {
452
1
					location: Location::parent(),
453
1
					fee_per_second: 1,
454
1
				},
455
1
				crate::Event::TransactedDerivative {
456
1
					account_id: 1u64,
457
1
					dest: Location::parent(),
458
1
					call: Transactors::Relay
459
1
						.encode_call(UtilityAvailableCalls::AsDerivative(1, vec![1u8])),
460
1
					index: 1,
461
1
				},
462
1
			];
463
1
			assert_eq!(events(), expected);
464
1
			let sent_messages = mock::sent_xcm();
465
1
			let (_, sent_message) = sent_messages.first().unwrap();
466
1

            
467
1
			// Check message doesn't contain the appendix
468
1
			assert!(!sent_message.0.contains(&SetAppendix(Xcm(vec![
469
1
				RefundSurplus,
470
1
				DepositAsset {
471
1
					assets: Wild(AllCounted(1u32)),
472
1
					beneficiary: Location {
473
1
						parents: 0,
474
1
						interior: [Junction::Parachain(100)].into()
475
1
					}
476
1
				}
477
1
			]))));
478
1
		})
479
1
}
480

            
481
#[test]
482
1
fn test_root_can_transact_through_sovereign() {
483
1
	ExtBuilder::default()
484
1
		.with_balances(vec![])
485
1
		.build()
486
1
		.execute_with(|| {
487
1
			// Only root can do this
488
1
			assert_noop!(
489
1
				XcmTransactor::transact_through_sovereign(
490
1
					RuntimeOrigin::signed(1),
491
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
492
1
					Some(1u64),
493
1
					CurrencyPayment {
494
1
						currency: Currency::AsMultiLocation(Box::new(
495
1
							xcm::VersionedLocation::from(Location::parent())
496
1
						)),
497
1
						fee_amount: None
498
1
					},
499
1
					vec![1u8],
500
1
					OriginKind::SovereignAccount,
501
1
					TransactWeights {
502
1
						transact_required_weight_at_most: 100u64.into(),
503
1
						overall_weight: None
504
1
					},
505
1
					false
506
1
				),
507
1
				DispatchError::BadOrigin
508
1
			);
509

            
510
			// Root can set transact info
511
1
			assert_ok!(XcmTransactor::set_transact_info(
512
1
				RuntimeOrigin::root(),
513
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
514
1
				0.into(),
515
1
				10000.into(),
516
1
				None
517
1
			));
518

            
519
			// Set fee per second
520
1
			assert_ok!(XcmTransactor::set_fee_per_second(
521
1
				RuntimeOrigin::root(),
522
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
523
1
				1
524
1
			));
525

            
526
			// fee as destination are the same, this time it should work
527
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
528
1
				RuntimeOrigin::root(),
529
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
530
1
				Some(1u64),
531
1
				CurrencyPayment {
532
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
533
1
						Location::parent()
534
1
					))),
535
1
					fee_amount: None
536
1
				},
537
1
				vec![1u8],
538
1
				OriginKind::SovereignAccount,
539
1
				TransactWeights {
540
1
					transact_required_weight_at_most: 100u64.into(),
541
1
					overall_weight: None
542
1
				},
543
1
				false
544
1
			));
545

            
546
1
			let expected = vec![
547
1
				crate::Event::TransactInfoChanged {
548
1
					location: Location::parent(),
549
1
					remote_info: RemoteTransactInfoWithMaxWeight {
550
1
						transact_extra_weight: 0.into(),
551
1
						max_weight: 10000.into(),
552
1
						transact_extra_weight_signed: None,
553
1
					},
554
1
				},
555
1
				crate::Event::DestFeePerSecondChanged {
556
1
					location: Location::parent(),
557
1
					fee_per_second: 1,
558
1
				},
559
1
				crate::Event::TransactedSovereign {
560
1
					fee_payer: Some(1u64),
561
1
					dest: Location::parent(),
562
1
					call: vec![1u8],
563
1
				},
564
1
			];
565
1
			assert_eq!(events(), expected);
566
1
		})
567
1
}
568

            
569
#[test]
570
1
fn test_fee_calculation_works() {
571
1
	ExtBuilder::default()
572
1
		.with_balances(vec![])
573
1
		.build()
574
1
		.execute_with(|| {
575
1
			assert_eq!(
576
1
				XcmTransactor::calculate_fee_per_second(
577
1
					1000000000.into(),
578
1
					8 * WEIGHT_REF_TIME_PER_SECOND as u128
579
1
				),
580
1
				8000000000
581
1
			);
582
1
		})
583
1
}
584

            
585
// Kusama case
586
#[test]
587
1
fn test_fee_calculation_works_kusama_0_9_20_case() {
588
1
	ExtBuilder::default()
589
1
		.with_balances(vec![])
590
1
		.build()
591
1
		.execute_with(|| {
592
1
			// 38620923000 * 319324000/1e12 = 12332587.6161
593
1
			// integer arithmetic would round this to 12332587
594
1
			// we test here that it rounds up to 12332588 instead
595
1
			assert_eq!(
596
1
				XcmTransactor::calculate_fee_per_second(319324000.into(), 38620923000),
597
1
				12332588
598
1
			);
599
1
		})
600
1
}
601

            
602
#[test]
603
1
fn de_registering_works() {
604
1
	ExtBuilder::default()
605
1
		.with_balances(vec![])
606
1
		.build()
607
1
		.execute_with(|| {
608
1
			// Root can register
609
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
610

            
611
1
			assert_eq!(XcmTransactor::index_to_account(&1).unwrap(), 1u64);
612

            
613
1
			assert_ok!(XcmTransactor::deregister(RuntimeOrigin::root(), 1));
614

            
615
1
			assert!(XcmTransactor::index_to_account(&1).is_none());
616

            
617
1
			let expected = vec![
618
1
				crate::Event::RegisteredDerivative {
619
1
					account_id: 1u64,
620
1
					index: 1,
621
1
				},
622
1
				crate::Event::DeRegisteredDerivative { index: 1 },
623
1
			];
624
1
			assert_eq!(events(), expected);
625
1
		})
626
1
}
627

            
628
#[test]
629
1
fn removing_transact_info_works() {
630
1
	ExtBuilder::default()
631
1
		.with_balances(vec![])
632
1
		.build()
633
1
		.execute_with(|| {
634
1
			// Root can set transact info
635
1
			assert_ok!(XcmTransactor::set_transact_info(
636
1
				RuntimeOrigin::root(),
637
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
638
1
				0.into(),
639
1
				10000.into(),
640
1
				None
641
1
			));
642

            
643
			// Root can remove transact info
644
1
			assert_ok!(XcmTransactor::remove_transact_info(
645
1
				RuntimeOrigin::root(),
646
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
647
1
			));
648

            
649
1
			assert!(XcmTransactor::transact_info(Location::parent()).is_none());
650

            
651
1
			let expected = vec![
652
1
				crate::Event::TransactInfoChanged {
653
1
					location: Location::parent(),
654
1
					remote_info: RemoteTransactInfoWithMaxWeight {
655
1
						transact_extra_weight: 0.into(),
656
1
						max_weight: 10000.into(),
657
1
						transact_extra_weight_signed: None,
658
1
					},
659
1
				},
660
1
				crate::Event::TransactInfoRemoved {
661
1
					location: Location::parent(),
662
1
				},
663
1
			];
664
1
			assert_eq!(events(), expected);
665
1
		})
666
1
}
667

            
668
#[test]
669
1
fn test_transact_through_signed_fails_if_transact_info_not_set_at_all() {
670
1
	ExtBuilder::default()
671
1
		.with_balances(vec![])
672
1
		.build()
673
1
		.execute_with(|| {
674
1
			// fee as destination are the same, this time it should work
675
1
			assert_noop!(
676
1
				XcmTransactor::transact_through_signed(
677
1
					RuntimeOrigin::signed(1u64),
678
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
679
1
					CurrencyPayment {
680
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
681
1
						fee_amount: None
682
1
					},
683
1
					vec![1u8],
684
1
					TransactWeights {
685
1
						transact_required_weight_at_most: 100u64.into(),
686
1
						overall_weight: None
687
1
					},
688
1
					false
689
1
				),
690
1
				Error::<Test>::TransactorInfoNotSet
691
1
			);
692
1
		})
693
1
}
694

            
695
#[test]
696
1
fn test_transact_through_signed_fails_if_weight_is_not_set() {
697
1
	ExtBuilder::default()
698
1
		.with_balances(vec![])
699
1
		.build()
700
1
		.execute_with(|| {
701
1
			// Root can set transact info
702
1
			assert_ok!(XcmTransactor::set_transact_info(
703
1
				RuntimeOrigin::root(),
704
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
705
1
				0.into(),
706
1
				10000.into(),
707
1
				None
708
1
			));
709

            
710
			// weight value not set for signed transact, fails
711
1
			assert_noop!(
712
1
				XcmTransactor::transact_through_signed(
713
1
					RuntimeOrigin::signed(1u64),
714
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
715
1
					CurrencyPayment {
716
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
717
1
						fee_amount: None
718
1
					},
719
1
					vec![1u8],
720
1
					TransactWeights {
721
1
						transact_required_weight_at_most: 100u64.into(),
722
1
						overall_weight: None
723
1
					},
724
1
					false
725
1
				),
726
1
				Error::<Test>::SignedTransactNotAllowedForDestination
727
1
			);
728
1
		})
729
1
}
730

            
731
#[test]
732
1
fn test_transact_through_signed_fails_if_weight_overflows() {
733
1
	ExtBuilder::default()
734
1
		.with_balances(vec![])
735
1
		.build()
736
1
		.execute_with(|| {
737
1
			// Root can set transact info
738
1
			assert_ok!(XcmTransactor::set_transact_info(
739
1
				RuntimeOrigin::root(),
740
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
741
1
				0.into(),
742
1
				10000.into(),
743
1
				Some(Weight::MAX)
744
1
			));
745

            
746
			// weight should overflow
747
1
			assert_noop!(
748
1
				XcmTransactor::transact_through_signed(
749
1
					RuntimeOrigin::signed(1u64),
750
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
751
1
					CurrencyPayment {
752
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
753
1
						fee_amount: None
754
1
					},
755
1
					vec![1u8],
756
1
					TransactWeights {
757
1
						transact_required_weight_at_most: 10064u64.into(),
758
1
						overall_weight: None
759
1
					},
760
1
					false
761
1
				),
762
1
				Error::<Test>::WeightOverflow
763
1
			);
764
1
		})
765
1
}
766

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

            
782
			// 10000 + 1 > 10000 (max weight permitted by dest chain)
783
1
			assert_noop!(
784
1
				XcmTransactor::transact_through_signed(
785
1
					RuntimeOrigin::signed(1u64),
786
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
787
1
					CurrencyPayment {
788
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
789
1
						fee_amount: None
790
1
					},
791
1
					vec![1u8],
792
1
					TransactWeights {
793
1
						transact_required_weight_at_most: 100000u64.into(),
794
1
						overall_weight: None
795
1
					},
796
1
					false
797
1
				),
798
1
				Error::<Test>::MaxWeightTransactReached
799
1
			);
800
1
		})
801
1
}
802

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

            
818
			// fee per second not set, fails
819
1
			assert_noop!(
820
1
				XcmTransactor::transact_through_signed(
821
1
					RuntimeOrigin::signed(1u64),
822
1
					Box::new(xcm::VersionedLocation::from(Location::parent())),
823
1
					CurrencyPayment {
824
1
						currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
825
1
						fee_amount: None
826
1
					},
827
1
					vec![1u8],
828
1
					TransactWeights {
829
1
						transact_required_weight_at_most: 100u64.into(),
830
1
						overall_weight: None
831
1
					},
832
1
					false
833
1
				),
834
1
				Error::<Test>::FeePerSecondNotSet
835
1
			);
836
1
		})
837
1
}
838

            
839
#[test]
840
1
fn test_transact_through_signed_works() {
841
1
	ExtBuilder::default()
842
1
		.with_balances(vec![])
843
1
		.build()
844
1
		.execute_with(|| {
845
1
			// Root can set transact info
846
1
			assert_ok!(XcmTransactor::set_transact_info(
847
1
				RuntimeOrigin::root(),
848
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
849
1
				0.into(),
850
1
				10000.into(),
851
1
				Some(1.into())
852
1
			));
853

            
854
			// Set fee per second
855
1
			assert_ok!(XcmTransactor::set_fee_per_second(
856
1
				RuntimeOrigin::root(),
857
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
858
1
				1
859
1
			));
860

            
861
			// transact info and fee per second set
862
			// this time it should work
863
1
			assert_ok!(XcmTransactor::transact_through_signed(
864
1
				RuntimeOrigin::signed(1u64),
865
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
866
1
				CurrencyPayment {
867
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
868
1
					fee_amount: None
869
1
				},
870
1
				vec![1u8],
871
1
				TransactWeights {
872
1
					transact_required_weight_at_most: 100u64.into(),
873
1
					overall_weight: None
874
1
				},
875
1
				false
876
1
			));
877

            
878
1
			let expected = vec![
879
1
				crate::Event::TransactInfoChanged {
880
1
					location: Location::parent(),
881
1
					remote_info: RemoteTransactInfoWithMaxWeight {
882
1
						transact_extra_weight: 0.into(),
883
1
						max_weight: 10000.into(),
884
1
						transact_extra_weight_signed: Some(1.into()),
885
1
					},
886
1
				},
887
1
				crate::Event::DestFeePerSecondChanged {
888
1
					location: Location::parent(),
889
1
					fee_per_second: 1,
890
1
				},
891
1
				crate::Event::TransactedSigned {
892
1
					fee_payer: 1u64,
893
1
					dest: Location::parent(),
894
1
					call: vec![1u8],
895
1
				},
896
1
			];
897
1
			assert_eq!(events(), expected);
898
1
		})
899
1
}
900

            
901
#[test]
902
1
fn test_send_through_derivative_with_custom_weight_and_fee() {
903
1
	ExtBuilder::default()
904
1
		.with_balances(vec![])
905
1
		.build()
906
1
		.execute_with(|| {
907
1
			// Root can register
908
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
909

            
910
			// We are gonna use a total weight of 10_100, a tx weight of 100,
911
			// and a total fee of 100
912
1
			let total_weight: Weight = 10_100u64.into();
913
1
			let tx_weight: Weight = 100_u64.into();
914
1
			let total_fee = 100u128;
915
1

            
916
1
			// By specifying total fee and total weight, we ensure
917
1
			// that even if the transact_info is not populated,
918
1
			// the message is forged with our parameters
919
1
			assert_ok!(XcmTransactor::transact_through_derivative(
920
1
				RuntimeOrigin::signed(1u64),
921
1
				Transactors::Relay,
922
1
				1,
923
1
				CurrencyPayment {
924
1
					currency: Currency::AsCurrencyId(CurrencyId::OtherReserve(0)),
925
1
					fee_amount: Some(total_fee)
926
1
				},
927
1
				vec![1u8],
928
1
				TransactWeights {
929
1
					transact_required_weight_at_most: tx_weight,
930
1
					overall_weight: Some(Limited(total_weight))
931
1
				},
932
1
				false
933
1
			));
934
1
			let expected = vec![
935
1
				crate::Event::RegisteredDerivative {
936
1
					account_id: 1u64,
937
1
					index: 1,
938
1
				},
939
1
				crate::Event::TransactedDerivative {
940
1
					account_id: 1u64,
941
1
					dest: Location::parent(),
942
1
					call: Transactors::Relay
943
1
						.encode_call(UtilityAvailableCalls::AsDerivative(1, vec![1u8])),
944
1
					index: 1,
945
1
				},
946
1
			];
947
1
			assert_eq!(events(), expected);
948
1
			let sent_messages = mock::sent_xcm();
949
1
			let (_, sent_message) = sent_messages.first().unwrap();
950
1
			// Lets make sure the message is as expected
951
1
			assert!(sent_message
952
1
				.0
953
1
				.contains(&WithdrawAsset((Location::here(), total_fee).into())));
954
1
			assert!(sent_message.0.contains(&BuyExecution {
955
1
				fees: (Location::here(), total_fee).into(),
956
1
				weight_limit: Limited(total_weight),
957
1
			}));
958
1
			assert!(sent_message.0.contains(&Transact {
959
1
				origin_kind: OriginKind::SovereignAccount,
960
1
				fallback_max_weight: Some(tx_weight),
961
1
				call: Transactors::Relay
962
1
					.encode_call(UtilityAvailableCalls::AsDerivative(1, vec![1u8]))
963
1
					.into(),
964
1
			}));
965
1
		})
966
1
}
967

            
968
#[test]
969
1
fn test_send_through_sovereign_with_custom_weight_and_fee() {
970
1
	ExtBuilder::default()
971
1
		.with_balances(vec![])
972
1
		.build()
973
1
		.execute_with(|| {
974
1
			// Root can register
975
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
976

            
977
			// We are gonna use a total weight of 10_100, a tx weight of 100,
978
			// and a total fee of 100
979
1
			let total_weight: Weight = 10_100u64.into();
980
1
			let tx_weight: Weight = 100_u64.into();
981
1
			let total_fee = 100u128;
982
1

            
983
1
			// By specifying total fee and total weight, we ensure
984
1
			// that even if the transact_info is not populated,
985
1
			// the message is forged with our parameters
986
1

            
987
1
			// fee as destination are the same, this time it should work
988
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
989
1
				RuntimeOrigin::root(),
990
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
991
1
				Some(1u64),
992
1
				CurrencyPayment {
993
1
					currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
994
1
						Location::parent()
995
1
					))),
996
1
					fee_amount: Some(total_fee)
997
1
				},
998
1
				vec![1u8],
999
1
				OriginKind::SovereignAccount,
1
				TransactWeights {
1
					transact_required_weight_at_most: tx_weight,
1
					overall_weight: Some(Limited(total_weight))
1
				},
1
				false
1
			));
1
			let expected = vec![
1
				crate::Event::RegisteredDerivative {
1
					account_id: 1u64,
1
					index: 1,
1
				},
1
				crate::Event::TransactedSovereign {
1
					fee_payer: Some(1u64),
1
					dest: Location::parent(),
1
					call: vec![1u8],
1
				},
1
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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_transact_through_sovereign_with_fee_payer_none() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
1
			// 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

            
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				// We don't specify any fee_payer, instead we pay fees with the
1
				// 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
				},
1
				false
1
			));
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
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// Lets make sure the message is as expected even if we haven't indicated a
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

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

            
1
			// 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
				},
1
				false
1
			));
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();
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
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
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
1
			// Change xcm version
1
			CustomVersionWrapper::set_version(3);
1

            
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
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
1
			// Change xcm version
1
			CustomVersionWrapper::set_version(5);
1

            
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
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
1
			// Change xcm version
1
			CustomVersionWrapper::set_version(6);
1

            
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
				),
1
				Error::<Test>::ErrorValidating
1
			);
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

            
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
				),
1
				Error::<Test>::TooMuchFeeUsed
1
			);
1
		})
1
}
#[test]
1
fn test_hrmp_manipulator_accept() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
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
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
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
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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(|| {
1
			// We are gonna use a total weight of 10_100, a tx weight of 100,
1
			// 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

            
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
			));
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1
			// 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(|| {
1
			// 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
1
			));
			// Set fee per second
1
			assert_ok!(XcmTransactor::set_fee_per_second(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				1
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,
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
				},
1
				true
1
			));
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
				},
1
				crate::Event::DestFeePerSecondChanged {
1
					location: Location::parent(),
1
					fee_per_second: 1,
1
				},
1
				crate::Event::TransactedDerivative {
1
					account_id: 1u64,
1
					dest: Location::parent(),
1
					call: Transactors::Relay
1
						.encode_call(UtilityAvailableCalls::AsDerivative(1, vec![1u8])),
1
					index: 1,
1
				},
1
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1

            
1
			// 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(|| {
1
			// 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
1
			));
			// Set fee per second
1
			assert_ok!(XcmTransactor::set_fee_per_second(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				1
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,
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
					},
1
					true
1
				),
1
				Error::<Test>::RefundNotSupportedWithTransactInfo
1
			);
1
		})
1
}
#[test]
1
fn test_transact_through_signed_with_refund_works() {
1
	ExtBuilder::default()
1
		.with_balances(vec![])
1
		.build()
1
		.execute_with(|| {
1
			// Set fee per second
1
			assert_ok!(XcmTransactor::set_fee_per_second(
1
				RuntimeOrigin::root(),
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
1
				1
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
				},
1
				true
1
			));
1
			let expected = vec![
1
				crate::Event::DestFeePerSecondChanged {
1
					location: Location::parent(),
1
					fee_per_second: 1,
1
				},
1
				crate::Event::TransactedSigned {
1
					fee_payer: 1u64,
1
					dest: Location::parent(),
1
					call: vec![1u8],
1
				},
1
			];
1
			assert_eq!(events(), expected);
1
			let sent_messages = mock::sent_xcm();
1
			let (_, sent_message) = sent_messages.first().unwrap();
1

            
1
			// 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
}