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: <XcmTransactor as UtilityEncodeCall>::encode_call(
389
1
						Transactors::Relay,
390
1
						UtilityAvailableCalls::AsDerivative(1, vec![1u8]),
391
1
					),
392
1
					index: 1,
393
1
				},
394
1
			];
395
1
			assert_eq!(events(), expected);
396
1
		})
397
1
}
398

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

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

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

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

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

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

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

            
523
			// Set fee per second
524
1
			assert_ok!(XcmTransactor::set_fee_per_second(
525
1
				RuntimeOrigin::root(),
526
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
527
1
				1
528
1
			));
529

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

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

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

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

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

            
615
1
			assert_eq!(XcmTransactor::index_to_account(&1).unwrap(), 1u64);
616

            
617
1
			assert_ok!(XcmTransactor::deregister(RuntimeOrigin::root(), 1));
618

            
619
1
			assert!(XcmTransactor::index_to_account(&1).is_none());
620

            
621
1
			let expected = vec![
622
1
				crate::Event::RegisteredDerivative {
623
1
					account_id: 1u64,
624
1
					index: 1,
625
1
				},
626
1
				crate::Event::DeRegisteredDerivative { index: 1 },
627
1
			];
628
1
			assert_eq!(events(), expected);
629
1
		})
630
1
}
631

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

            
647
			// Root can remove transact info
648
1
			assert_ok!(XcmTransactor::remove_transact_info(
649
1
				RuntimeOrigin::root(),
650
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
651
1
			));
652

            
653
1
			assert!(XcmTransactor::transact_info(Location::parent()).is_none());
654

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

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

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

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

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

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

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

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

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

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

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

            
858
			// Set fee per second
859
1
			assert_ok!(XcmTransactor::set_fee_per_second(
860
1
				RuntimeOrigin::root(),
861
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
862
1
				1
863
1
			));
864

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

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

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

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

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

            
976
#[test]
977
1
fn test_send_through_sovereign_with_custom_weight_and_fee() {
978
1
	ExtBuilder::default()
979
1
		.with_balances(vec![])
980
1
		.build()
981
1
		.execute_with(|| {
982
1
			// Root can register
983
1
			assert_ok!(XcmTransactor::register(RuntimeOrigin::root(), 1u64, 1));
984

            
985
			// We are gonna use a total weight of 10_100, a tx weight of 100,
986
			// and a total fee of 100
987
1
			let total_weight: Weight = 10_100u64.into();
988
1
			let tx_weight: Weight = 100_u64.into();
989
1
			let total_fee = 100u128;
990
1

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

            
995
1
			// fee as destination are the same, this time it should work
996
1
			assert_ok!(XcmTransactor::transact_through_sovereign(
997
1
				RuntimeOrigin::root(),
998
1
				Box::new(xcm::VersionedLocation::from(Location::parent())),
999
1
				Some(1u64),
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: 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: <XcmTransactor as UtilityEncodeCall>::encode_call(
1
						Transactors::Relay,
1
						UtilityAvailableCalls::AsDerivative(1, vec![1u8]),
1
					),
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
}