1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
use frame_support::{
18
	assert_ok,
19
	traits::{ConstU32, PalletInfo},
20
	weights::constants::WEIGHT_REF_TIME_PER_SECOND,
21
	BoundedVec,
22
};
23
use pallet_xcm_transactor::{Currency, CurrencyPayment, TransactWeights};
24
use sp_std::boxed::Box;
25
use xcm::latest::prelude::{
26
	AccountKey20, Junctions, Limited, Location, PalletInstance, Parachain, Reanchorable,
27
};
28
use xcm_executor::traits::ConvertLocation;
29

            
30
use crate::xcm_mock::*;
31
use crate::xcm_testing::helpers::*;
32
use parity_scale_codec::Encode;
33
use xcm_simulator::TestExt;
34

            
35
#[test]
36
1
fn transact_through_signed_multilocation() {
37
1
	MockNet::reset();
38
1
	let mut ancestry = Location::parent();
39
1

            
40
1
	ParaA::execute_with(|| {
41
1
		// Root can set transact info
42
1
		assert_ok!(XcmTransactor::set_transact_info(
43
1
			parachain::RuntimeOrigin::root(),
44
1
			Box::new(xcm::VersionedLocation::from(Location::parent())),
45
1
			// Relay charges 1000 for every instruction, and we have 3, so 3000
46
1
			3000.into(),
47
1
			20000000000.into(),
48
1
			// 4 instructions in transact through signed
49
1
			Some(4000.into())
50
1
		));
51
		// Root can set transact info
52
1
		assert_ok!(XcmTransactor::set_fee_per_second(
53
1
			parachain::RuntimeOrigin::root(),
54
1
			Box::new(xcm::VersionedLocation::from(Location::parent())),
55
1
			WEIGHT_REF_TIME_PER_SECOND as u128,
56
1
		));
57
1
		ancestry = parachain::UniversalLocation::get().into();
58
1
	});
59
1

            
60
1
	// Let's construct the Junction that we will append with DescendOrigin
61
1
	let signed_origin: Junctions = [AccountKey20 {
62
1
		network: None,
63
1
		key: PARAALICE,
64
1
	}]
65
1
	.into();
66
1

            
67
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
68
1
	descend_origin_multilocation
69
1
		.append_with(signed_origin)
70
1
		.unwrap();
71
1

            
72
1
	// To convert it to what the relay will see instead of us
73
1
	descend_origin_multilocation
74
1
		.reanchor(&Location::parent(), &ancestry.interior)
75
1
		.unwrap();
76
1

            
77
1
	let derived = xcm_builder::Account32Hash::<
78
1
		relay_chain::KusamaNetwork,
79
1
		relay_chain::AccountId,
80
1
	>::convert_location(&descend_origin_multilocation)
81
1
	.unwrap();
82
1

            
83
1
	Relay::execute_with(|| {
84
1
		// free execution, full amount received
85
1
		assert_ok!(RelayBalances::transfer_allow_death(
86
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
87
1
			derived.clone(),
88
1
			4000004100u128,
89
1
		));
90
		// derived account has all funds
91
1
		assert!(RelayBalances::free_balance(&derived) == 4000004100);
92
		// sovereign account has 0 funds
93
1
		assert!(RelayBalances::free_balance(&para_a_account()) == 0);
94
1
	});
95
1

            
96
1
	// Encode the call. Balances transact to para_a_account
97
1
	// First index
98
1
	let encoded = encode_relay_balance_transfer_call(para_a_account(), 100u128);
99
1

            
100
1
	ParaA::execute_with(|| {
101
1
		assert_ok!(XcmTransactor::transact_through_signed(
102
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
103
1
			Box::new(xcm::VersionedLocation::from(Location::parent())),
104
1
			CurrencyPayment {
105
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
106
1
					Location::parent()
107
1
				))),
108
1
				fee_amount: None
109
1
			},
110
1
			encoded,
111
1
			// 4000000000 for transfer + 4000 for XCM
112
1
			TransactWeights {
113
1
				transact_required_weight_at_most: 4000000000.into(),
114
1
				overall_weight: None
115
1
			},
116
1
			false
117
1
		));
118
1
	});
119
1

            
120
1
	Relay::execute_with(|| {
121
1
		assert!(RelayBalances::free_balance(&para_a_account()) == 100);
122

            
123
1
		assert!(RelayBalances::free_balance(&derived) == 0);
124
1
	});
125
1
}
126

            
127
#[test]
128
1
fn transact_through_signed_multilocation_custom_fee_and_weight() {
129
1
	MockNet::reset();
130
1
	let mut ancestry = Location::parent();
131
1

            
132
1
	ParaA::execute_with(|| {
133
1
		ancestry = parachain::UniversalLocation::get().into();
134
1
	});
135
1

            
136
1
	// Let's construct the Junction that we will append with DescendOrigin
137
1
	let signed_origin: Junctions = [AccountKey20 {
138
1
		network: None,
139
1
		key: PARAALICE,
140
1
	}]
141
1
	.into();
142
1

            
143
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
144
1
	descend_origin_multilocation
145
1
		.append_with(signed_origin)
146
1
		.unwrap();
147
1

            
148
1
	// To convert it to what the relay will see instead of us
149
1
	descend_origin_multilocation
150
1
		.reanchor(&Location::parent(), &ancestry.interior)
151
1
		.unwrap();
152
1

            
153
1
	let derived = xcm_builder::Account32Hash::<
154
1
		relay_chain::KusamaNetwork,
155
1
		relay_chain::AccountId,
156
1
	>::convert_location(&descend_origin_multilocation)
157
1
	.unwrap();
158
1

            
159
1
	Relay::execute_with(|| {
160
1
		// free execution, full amount received
161
1
		assert_ok!(RelayBalances::transfer_allow_death(
162
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
163
1
			derived.clone(),
164
1
			4000004100u128,
165
1
		));
166
		// derived account has all funds
167
1
		assert!(RelayBalances::free_balance(&derived) == 4000004100);
168
		// sovereign account has 0 funds
169
1
		assert!(RelayBalances::free_balance(&para_a_account()) == 0);
170
1
	});
171
1

            
172
1
	// Encode the call. Balances transact to para_a_account
173
1
	// First index
174
1
	let encoded = encode_relay_balance_transfer_call(para_a_account(), 100u128);
175
1

            
176
1
	let total_weight = 4000004000u64;
177
1
	ParaA::execute_with(|| {
178
1
		assert_ok!(XcmTransactor::transact_through_signed(
179
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
180
1
			Box::new(xcm::VersionedLocation::from(Location::parent())),
181
1
			CurrencyPayment {
182
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
183
1
					Location::parent()
184
1
				))),
185
1
				fee_amount: Some(total_weight as u128)
186
1
			},
187
1
			encoded,
188
1
			// 4000000000 for transfer + 4000 for XCM
189
1
			TransactWeights {
190
1
				transact_required_weight_at_most: 4000000000.into(),
191
1
				overall_weight: Some(Limited(total_weight.into()))
192
1
			},
193
1
			false
194
1
		));
195
1
	});
196
1

            
197
1
	Relay::execute_with(|| {
198
1
		assert!(RelayBalances::free_balance(&para_a_account()) == 100);
199

            
200
1
		assert!(RelayBalances::free_balance(&derived) == 0);
201
1
	});
202
1
}
203

            
204
#[test]
205
1
fn transact_through_signed_multilocation_custom_fee_and_weight_refund() {
206
1
	MockNet::reset();
207
1
	let mut ancestry = Location::parent();
208
1

            
209
1
	ParaA::execute_with(|| {
210
1
		ancestry = parachain::UniversalLocation::get().into();
211
1
	});
212
1

            
213
1
	// Let's construct the Junction that we will append with DescendOrigin
214
1
	let signed_origin: Junctions = [AccountKey20 {
215
1
		network: None,
216
1
		key: PARAALICE,
217
1
	}]
218
1
	.into();
219
1

            
220
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
221
1
	descend_origin_multilocation
222
1
		.append_with(signed_origin)
223
1
		.unwrap();
224
1

            
225
1
	// To convert it to what the relay will see instead of us
226
1
	descend_origin_multilocation
227
1
		.reanchor(&Location::parent(), &ancestry.interior)
228
1
		.unwrap();
229
1

            
230
1
	let derived = xcm_builder::Account32Hash::<
231
1
		relay_chain::KusamaNetwork,
232
1
		relay_chain::AccountId,
233
1
	>::convert_location(&descend_origin_multilocation)
234
1
	.unwrap();
235
1

            
236
1
	Relay::execute_with(|| {
237
1
		// free execution, full amount received
238
1
		assert_ok!(RelayBalances::transfer_allow_death(
239
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
240
1
			derived.clone(),
241
1
			4000009100u128,
242
1
		));
243
		// derived account has all funds
244
1
		assert!(RelayBalances::free_balance(&derived) == 4000009100);
245
		// sovereign account has 0 funds
246
1
		assert!(RelayBalances::free_balance(&para_a_account()) == 0);
247
1
	});
248
1

            
249
1
	// Encode the call. Balances transact to para_a_account
250
1
	// First index
251
1
	let encoded = encode_relay_balance_transfer_call(para_a_account(), 100u128);
252
1

            
253
1
	let total_weight = 4000009000u64;
254
1
	ParaA::execute_with(|| {
255
1
		assert_ok!(XcmTransactor::transact_through_signed(
256
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
257
1
			Box::new(xcm::VersionedLocation::from(Location::parent())),
258
1
			CurrencyPayment {
259
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
260
1
					Location::parent()
261
1
				))),
262
1
				fee_amount: Some(total_weight as u128)
263
1
			},
264
1
			encoded,
265
1
			// 4000000000 for transfer + 9000 for XCM
266
1
			TransactWeights {
267
1
				transact_required_weight_at_most: 4000000000.into(),
268
1
				overall_weight: Some(Limited(total_weight.into()))
269
1
			},
270
1
			true
271
1
		));
272
1
	});
273
1

            
274
1
	Relay::execute_with(|| {
275
1
		// 100 transferred
276
1
		assert_eq!(RelayBalances::free_balance(&para_a_account()), 100);
277

            
278
		// 4000009000 refunded
279
1
		assert_eq!(RelayBalances::free_balance(&derived), 4000009000);
280
1
	});
281
1
}
282

            
283
#[test]
284
1
fn transact_through_signed_multilocation_para_to_para() {
285
1
	MockNet::reset();
286
1
	let mut ancestry = Location::parent();
287
1

            
288
1
	let para_b_location = Location::new(1, [Parachain(2)]);
289
1

            
290
1
	let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]);
291
1

            
292
1
	ParaA::execute_with(|| {
293
1
		// Root can set transact info
294
1
		assert_ok!(XcmTransactor::set_transact_info(
295
1
			parachain::RuntimeOrigin::root(),
296
1
			// ParaB
297
1
			Box::new(xcm::VersionedLocation::from(para_b_location.clone())),
298
1
			// Para charges 1000 for every instruction, and we have 3, so 3
299
1
			3.into(),
300
1
			20000000000.into(),
301
1
			// 4 instructions in transact through signed
302
1
			Some(4.into())
303
1
		));
304
		// Root can set transact info
305
1
		assert_ok!(XcmTransactor::set_fee_per_second(
306
1
			parachain::RuntimeOrigin::root(),
307
1
			Box::new(xcm::VersionedLocation::from(para_b_balances.clone())),
308
1
			parachain::ParaTokensPerSecond::get(),
309
1
		));
310
1
		ancestry = parachain::UniversalLocation::get().into();
311
1
	});
312
1

            
313
1
	// Let's construct the Junction that we will append with DescendOrigin
314
1
	let signed_origin: Junctions = [AccountKey20 {
315
1
		network: None,
316
1
		key: PARAALICE,
317
1
	}]
318
1
	.into();
319
1

            
320
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
321
1
	descend_origin_multilocation
322
1
		.append_with(signed_origin)
323
1
		.unwrap();
324
1

            
325
1
	// To convert it to what the paraB will see instead of us
326
1
	descend_origin_multilocation
327
1
		.reanchor(&para_b_location, &ancestry.interior)
328
1
		.unwrap();
329
1

            
330
1
	let derived = xcm_builder::HashedDescription::<
331
1
		parachain::AccountId,
332
1
		xcm_builder::DescribeFamily<xcm_builder::DescribeAllTerminal>,
333
1
	>::convert_location(&descend_origin_multilocation)
334
1
	.unwrap();
335
1

            
336
1
	ParaB::execute_with(|| {
337
1
		// free execution, full amount received
338
1
		assert_ok!(ParaBalances::transfer_allow_death(
339
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
340
1
			derived.clone(),
341
1
			4000000104u128,
342
1
		));
343
		// derived account has all funds
344
1
		assert!(ParaBalances::free_balance(&derived) == 4000000104);
345
		// sovereign account has 0 funds
346
1
		assert!(ParaBalances::free_balance(&para_a_account_20()) == 0);
347
1
	});
348
1

            
349
1
	// Encode the call. Balances transact to para_a_account
350
1
	// First index
351
1
	let mut encoded: Vec<u8> = Vec::new();
352
1
	let index =
353
1
		<parachain::Runtime as frame_system::Config>::PalletInfo::index::<parachain::Balances>()
354
1
			.unwrap() as u8;
355
1

            
356
1
	encoded.push(index);
357
1

            
358
1
	// Then call bytes
359
1
	let mut call_bytes = pallet_balances::Call::<parachain::Runtime>::transfer_allow_death {
360
1
		// 100 to sovereign
361
1
		dest: para_a_account_20(),
362
1
		value: 100u32.into(),
363
1
	}
364
1
	.encode();
365
1
	encoded.append(&mut call_bytes);
366
1

            
367
1
	ParaA::execute_with(|| {
368
1
		assert_ok!(XcmTransactor::transact_through_signed(
369
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
370
1
			Box::new(xcm::VersionedLocation::from(para_b_location)),
371
1
			CurrencyPayment {
372
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
373
1
					para_b_balances
374
1
				))),
375
1
				fee_amount: None
376
1
			},
377
1
			encoded,
378
1
			// 4000000000 for transfer + 4000 for XCM
379
1
			// 1-1 to fee
380
1
			TransactWeights {
381
1
				transact_required_weight_at_most: 4000000000.into(),
382
1
				overall_weight: None
383
1
			},
384
1
			false
385
1
		));
386
1
	});
387
1

            
388
1
	ParaB::execute_with(|| {
389
1
		assert!(ParaBalances::free_balance(&derived) == 0);
390

            
391
1
		assert!(ParaBalances::free_balance(&para_a_account_20()) == 100);
392
1
	});
393
1
}
394

            
395
#[test]
396
1
fn transact_through_signed_multilocation_para_to_para_refund() {
397
1
	MockNet::reset();
398
1
	let mut ancestry = Location::parent();
399
1

            
400
1
	let para_b_location = Location::new(1, [Parachain(2)]);
401
1

            
402
1
	let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]);
403
1

            
404
1
	ParaA::execute_with(|| {
405
1
		assert_ok!(XcmTransactor::set_fee_per_second(
406
1
			parachain::RuntimeOrigin::root(),
407
1
			Box::new(xcm::VersionedLocation::from(para_b_balances.clone())),
408
1
			parachain::ParaTokensPerSecond::get(),
409
1
		));
410
1
		ancestry = parachain::UniversalLocation::get().into();
411
1
	});
412
1

            
413
1
	// Let's construct the Junction that we will append with DescendOrigin
414
1
	let signed_origin: Junctions = [AccountKey20 {
415
1
		network: None,
416
1
		key: PARAALICE,
417
1
	}]
418
1
	.into();
419
1

            
420
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
421
1
	descend_origin_multilocation
422
1
		.append_with(signed_origin)
423
1
		.unwrap();
424
1

            
425
1
	// To convert it to what the paraB will see instead of us
426
1
	descend_origin_multilocation
427
1
		.reanchor(&para_b_location, &ancestry.interior)
428
1
		.unwrap();
429
1

            
430
1
	let derived = xcm_builder::HashedDescription::<
431
1
		parachain::AccountId,
432
1
		xcm_builder::DescribeFamily<xcm_builder::DescribeAllTerminal>,
433
1
	>::convert_location(&descend_origin_multilocation)
434
1
	.unwrap();
435
1

            
436
1
	ParaB::execute_with(|| {
437
1
		// free execution, full amount received
438
1
		assert_ok!(ParaBalances::transfer_allow_death(
439
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
440
1
			derived.clone(),
441
1
			4000009100u128,
442
1
		));
443
		// derived account has all funds
444
1
		assert!(ParaBalances::free_balance(&derived) == 4000009100);
445
		// sovereign account has 0 funds
446
1
		assert!(ParaBalances::free_balance(&para_a_account_20()) == 0);
447
1
	});
448
1

            
449
1
	// Encode the call. Balances transact to para_a_account
450
1
	// First index
451
1
	let mut encoded: Vec<u8> = Vec::new();
452
1
	let index =
453
1
		<parachain::Runtime as frame_system::Config>::PalletInfo::index::<parachain::Balances>()
454
1
			.unwrap() as u8;
455
1

            
456
1
	encoded.push(index);
457
1

            
458
1
	// Then call bytes
459
1
	let mut call_bytes = pallet_balances::Call::<parachain::Runtime>::transfer_allow_death {
460
1
		// 100 to sovereign
461
1
		dest: para_a_account_20(),
462
1
		value: 100u32.into(),
463
1
	}
464
1
	.encode();
465
1
	encoded.append(&mut call_bytes);
466
1

            
467
1
	let overall_weight = 4000009000u64;
468
1
	ParaA::execute_with(|| {
469
1
		assert_ok!(XcmTransactor::transact_through_signed(
470
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
471
1
			Box::new(xcm::VersionedLocation::from(para_b_location)),
472
1
			CurrencyPayment {
473
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
474
1
					para_b_balances
475
1
				))),
476
1
				fee_amount: Some(overall_weight as u128)
477
1
			},
478
1
			encoded,
479
1
			// 4000000000 for transfer + 9000 for XCM
480
1
			TransactWeights {
481
1
				transact_required_weight_at_most: 4000000000.into(),
482
1
				overall_weight: Some(Limited(overall_weight.into()))
483
1
			},
484
1
			true
485
1
		));
486
1
	});
487
1

            
488
1
	ParaB::execute_with(|| {
489
1
		// Check the derived account was refunded
490
1
		assert_eq!(ParaBalances::free_balance(&derived), 3823903993);
491

            
492
		// Check the transfer was executed
493
1
		assert_eq!(ParaBalances::free_balance(&para_a_account_20()), 100);
494
1
	});
495
1
}
496

            
497
#[test]
498
1
fn transact_through_signed_multilocation_para_to_para_ethereum() {
499
1
	MockNet::reset();
500
1
	let mut ancestry = Location::parent();
501
1

            
502
1
	let para_b_location = Location::new(1, [Parachain(2)]);
503
1

            
504
1
	let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]);
505
1

            
506
1
	ParaA::execute_with(|| {
507
1
		// Root can set transact info
508
1
		assert_ok!(XcmTransactor::set_transact_info(
509
1
			parachain::RuntimeOrigin::root(),
510
1
			// ParaB
511
1
			Box::new(xcm::VersionedLocation::from(para_b_location.clone())),
512
1
			// Para charges 1000 for every instruction, and we have 3, so 3
513
1
			3.into(),
514
1
			20000000000.into(),
515
1
			// 4 instructions in transact through signed
516
1
			Some(4.into())
517
1
		));
518
		// Root can set transact info
519
1
		assert_ok!(XcmTransactor::set_fee_per_second(
520
1
			parachain::RuntimeOrigin::root(),
521
1
			Box::new(xcm::VersionedLocation::from(para_b_balances.clone())),
522
1
			parachain::ParaTokensPerSecond::get(),
523
1
		));
524
1
		ancestry = parachain::UniversalLocation::get().into();
525
1
	});
526
1

            
527
1
	// Let's construct the Junction that we will append with DescendOrigin
528
1
	let signed_origin: Junctions = [AccountKey20 {
529
1
		network: None,
530
1
		key: PARAALICE,
531
1
	}]
532
1
	.into();
533
1

            
534
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
535
1
	descend_origin_multilocation
536
1
		.append_with(signed_origin)
537
1
		.unwrap();
538
1

            
539
1
	// To convert it to what the paraB will see instead of us
540
1
	descend_origin_multilocation
541
1
		.reanchor(&para_b_location, &ancestry.interior)
542
1
		.unwrap();
543
1

            
544
1
	let derived = xcm_builder::HashedDescription::<
545
1
		parachain::AccountId,
546
1
		xcm_builder::DescribeFamily<xcm_builder::DescribeAllTerminal>,
547
1
	>::convert_location(&descend_origin_multilocation)
548
1
	.unwrap();
549
1

            
550
1
	let mut parachain_b_alice_balances_before = 0;
551
1
	ParaB::execute_with(|| {
552
1
		assert_ok!(ParaBalances::transfer_allow_death(
553
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
554
1
			derived.clone(),
555
1
			4000000104u128,
556
1
		));
557
		// derived account has all funds
558
1
		assert!(ParaBalances::free_balance(&derived) == 4000000104);
559
		// sovereign account has 0 funds
560
1
		assert!(ParaBalances::free_balance(&para_a_account_20()) == 0);
561

            
562
1
		parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into())
563
1
	});
564
1

            
565
1
	// Encode the call. Balances transact to para_a_account
566
1
	// First index
567
1
	let mut encoded: Vec<u8> = Vec::new();
568
1
	let index =
569
1
		<parachain::Runtime as frame_system::Config>::PalletInfo::index::<parachain::EthereumXcm>()
570
1
			.unwrap() as u8;
571
1

            
572
1
	encoded.push(index);
573

            
574
	use sp_core::U256;
575
	// Let's do a EVM transfer
576
1
	let eth_tx =
577
1
		xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 {
578
1
			gas_limit: U256::from(21000),
579
1
			fee_payment: xcm_primitives::EthereumXcmFee::Auto,
580
1
			action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()),
581
1
			value: U256::from(100),
582
1
			input: BoundedVec::<
583
1
				u8,
584
1
				ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }>
585
1
			>::try_from(vec![]).unwrap(),
586
1
			access_list: None,
587
1
		});
588
1

            
589
1
	// Then call bytes
590
1
	let mut call_bytes = pallet_ethereum_xcm::Call::<parachain::Runtime>::transact {
591
1
		xcm_transaction: eth_tx,
592
1
	}
593
1
	.encode();
594
1
	encoded.append(&mut call_bytes);
595
1

            
596
1
	ParaA::execute_with(|| {
597
1
		assert_ok!(XcmTransactor::transact_through_signed(
598
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
599
1
			Box::new(xcm::VersionedLocation::from(para_b_location)),
600
1
			CurrencyPayment {
601
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
602
1
					para_b_balances
603
1
				))),
604
1
				fee_amount: None
605
1
			},
606
1
			encoded,
607
1
			// 4000000000 for transfer + 4000 for XCM
608
1
			// 1-1 to fee
609
1
			TransactWeights {
610
1
				transact_required_weight_at_most: 4000000000.into(),
611
1
				overall_weight: None
612
1
			},
613
1
			false
614
1
		));
615
1
	});
616
1

            
617
1
	ParaB::execute_with(|| {
618
1
		// Make sure the EVM transfer went through
619
1
		assert!(
620
1
			ParaBalances::free_balance(&PARAALICE.into())
621
1
				== parachain_b_alice_balances_before + 100
622
1
		);
623
1
	});
624
1
}
625

            
626
#[test]
627
1
fn transact_through_signed_multilocation_para_to_para_ethereum_no_proxy_fails() {
628
1
	MockNet::reset();
629
1
	let mut ancestry = Location::parent();
630
1

            
631
1
	let para_b_location = Location::new(1, [Parachain(2)]);
632
1

            
633
1
	let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]);
634
1

            
635
1
	ParaA::execute_with(|| {
636
1
		// Root can set transact info
637
1
		assert_ok!(XcmTransactor::set_transact_info(
638
1
			parachain::RuntimeOrigin::root(),
639
1
			// ParaB
640
1
			Box::new(xcm::VersionedLocation::from(para_b_location.clone())),
641
1
			// Para charges 1000 for every instruction, and we have 3, so 3
642
1
			3.into(),
643
1
			20000000000.into(),
644
1
			// 4 instructions in transact through signed
645
1
			Some(4.into())
646
1
		));
647
		// Root can set transact info
648
1
		assert_ok!(XcmTransactor::set_fee_per_second(
649
1
			parachain::RuntimeOrigin::root(),
650
1
			Box::new(xcm::VersionedLocation::from(para_b_balances.clone())),
651
1
			parachain::ParaTokensPerSecond::get(),
652
1
		));
653
1
		ancestry = parachain::UniversalLocation::get().into();
654
1
	});
655
1

            
656
1
	// Let's construct the Junction that we will append with DescendOrigin
657
1
	let signed_origin: Junctions = [AccountKey20 {
658
1
		network: None,
659
1
		key: PARAALICE,
660
1
	}]
661
1
	.into();
662
1

            
663
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
664
1
	descend_origin_multilocation
665
1
		.append_with(signed_origin)
666
1
		.unwrap();
667
1

            
668
1
	// To convert it to what the paraB will see instead of us
669
1
	descend_origin_multilocation
670
1
		.reanchor(&para_b_location, &ancestry.interior)
671
1
		.unwrap();
672
1

            
673
1
	let derived = xcm_builder::HashedDescription::<
674
1
		parachain::AccountId,
675
1
		xcm_builder::DescribeFamily<xcm_builder::DescribeAllTerminal>,
676
1
	>::convert_location(&descend_origin_multilocation)
677
1
	.unwrap();
678
1

            
679
1
	let mut parachain_b_alice_balances_before = 0;
680
1
	ParaB::execute_with(|| {
681
1
		assert_ok!(ParaBalances::transfer_allow_death(
682
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
683
1
			derived.clone(),
684
1
			4000000104u128,
685
1
		));
686
		// derived account has all funds
687
1
		assert!(ParaBalances::free_balance(&derived) == 4000000104);
688
		// sovereign account has 0 funds
689
1
		assert!(ParaBalances::free_balance(&para_a_account_20()) == 0);
690

            
691
1
		parachain_b_alice_balances_before = ParaBalances::free_balance(&PARAALICE.into())
692
1
	});
693
1

            
694
1
	// Encode the call. Balances transact to para_a_account
695
1
	// First index
696
1
	let mut encoded: Vec<u8> = Vec::new();
697
1
	let index =
698
1
		<parachain::Runtime as frame_system::Config>::PalletInfo::index::<parachain::EthereumXcm>()
699
1
			.unwrap() as u8;
700
1

            
701
1
	encoded.push(index);
702

            
703
	use sp_core::U256;
704
	// Let's do a EVM transfer
705
1
	let eth_tx =
706
1
		xcm_primitives::EthereumXcmTransaction::V1(xcm_primitives::EthereumXcmTransactionV1 {
707
1
			gas_limit: U256::from(21000),
708
1
			fee_payment: xcm_primitives::EthereumXcmFee::Auto,
709
1
			action: pallet_ethereum::TransactionAction::Call(PARAALICE.into()),
710
1
			value: U256::from(100),
711
1
			input: BoundedVec::<
712
1
				u8,
713
1
				ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }>
714
1
			>::try_from(vec![]).unwrap(),
715
1
			access_list: None,
716
1
		});
717
1

            
718
1
	// Then call bytes
719
1
	let mut call_bytes = pallet_ethereum_xcm::Call::<parachain::Runtime>::transact_through_proxy {
720
1
		transact_as: PARAALICE.into(),
721
1
		xcm_transaction: eth_tx,
722
1
	}
723
1
	.encode();
724
1
	encoded.append(&mut call_bytes);
725
1

            
726
1
	ParaA::execute_with(|| {
727
1
		assert_ok!(XcmTransactor::transact_through_signed(
728
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
729
1
			Box::new(xcm::VersionedLocation::from(para_b_location)),
730
1
			CurrencyPayment {
731
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
732
1
					para_b_balances
733
1
				))),
734
1
				fee_amount: None
735
1
			},
736
1
			encoded,
737
1
			TransactWeights {
738
1
				transact_required_weight_at_most: 4000000000.into(),
739
1
				overall_weight: None
740
1
			},
741
1
			false
742
1
		));
743
1
	});
744
1

            
745
1
	ParaB::execute_with(|| {
746
1
		// Make sure the EVM transfer wasn't executed
747
1
		assert!(ParaBalances::free_balance(&PARAALICE.into()) == parachain_b_alice_balances_before);
748
1
	});
749
1
}
750

            
751
#[test]
752
1
fn transact_through_signed_multilocation_para_to_para_ethereum_proxy_succeeds() {
753
1
	MockNet::reset();
754
1
	let mut ancestry = Location::parent();
755
1

            
756
1
	let para_b_location = Location::new(1, [Parachain(2)]);
757
1

            
758
1
	let para_b_balances = Location::new(1, [Parachain(2), PalletInstance(1u8)]);
759
1

            
760
1
	ParaA::execute_with(|| {
761
1
		// Root can set transact info
762
1
		assert_ok!(XcmTransactor::set_transact_info(
763
1
			parachain::RuntimeOrigin::root(),
764
1
			// ParaB
765
1
			Box::new(xcm::VersionedLocation::from(para_b_location.clone())),
766
1
			// Para charges 1000 for every instruction, and we have 3, so 3
767
1
			3.into(),
768
1
			20000000000.into(),
769
1
			// 4 instructions in transact through signed
770
1
			Some(4.into())
771
1
		));
772
		// Root can set transact info
773
1
		assert_ok!(XcmTransactor::set_fee_per_second(
774
1
			parachain::RuntimeOrigin::root(),
775
1
			Box::new(xcm::VersionedLocation::from(para_b_balances.clone())),
776
1
			parachain::ParaTokensPerSecond::get(),
777
1
		));
778
1
		ancestry = parachain::UniversalLocation::get().into();
779
1
	});
780
1

            
781
1
	// Let's construct the Junction that we will append with DescendOrigin
782
1
	let signed_origin: Junctions = [AccountKey20 {
783
1
		network: None,
784
1
		key: PARAALICE,
785
1
	}]
786
1
	.into();
787
1

            
788
1
	let mut descend_origin_multilocation = parachain::SelfLocation::get();
789
1
	descend_origin_multilocation
790
1
		.append_with(signed_origin)
791
1
		.unwrap();
792
1

            
793
1
	// To convert it to what the paraB will see instead of us
794
1
	descend_origin_multilocation
795
1
		.reanchor(&para_b_location, &ancestry.interior)
796
1
		.unwrap();
797
1

            
798
1
	let derived = xcm_builder::HashedDescription::<
799
1
		parachain::AccountId,
800
1
		xcm_builder::DescribeFamily<xcm_builder::DescribeAllTerminal>,
801
1
	>::convert_location(&descend_origin_multilocation)
802
1
	.unwrap();
803
1

            
804
1
	let transfer_recipient = evm_account();
805
1
	let mut transfer_recipient_balance_before = 0;
806
1
	ParaB::execute_with(|| {
807
1
		assert_ok!(ParaBalances::transfer_allow_death(
808
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
809
1
			derived.clone(),
810
1
			4000000104u128,
811
1
		));
812
		// derived account has all funds
813
1
		assert!(ParaBalances::free_balance(&derived) == 4000000104);
814
		// sovereign account has 0 funds
815
1
		assert!(ParaBalances::free_balance(&para_a_account_20()) == 0);
816

            
817
1
		transfer_recipient_balance_before = ParaBalances::free_balance(&transfer_recipient.into());
818
1

            
819
1
		// Add proxy ALICE  -> derived
820
1
		let _ = parachain::Proxy::add_proxy_delegate(
821
1
			&PARAALICE.into(),
822
1
			derived,
823
1
			parachain::ProxyType::Any,
824
1
			0,
825
1
		);
826
1
	});
827
1

            
828
1
	// Encode the call. Balances transact to para_a_account
829
1
	// First index
830
1
	let mut encoded: Vec<u8> = Vec::new();
831
1
	let index =
832
1
		<parachain::Runtime as frame_system::Config>::PalletInfo::index::<parachain::EthereumXcm>()
833
1
			.unwrap() as u8;
834
1

            
835
1
	encoded.push(index);
836

            
837
	use sp_core::U256;
838
	// Let's do a EVM transfer
839
1
	let eth_tx =
840
1
		xcm_primitives::EthereumXcmTransaction::V2(xcm_primitives::EthereumXcmTransactionV2 {
841
1
			gas_limit: U256::from(21000),
842
1
			action: pallet_ethereum::TransactionAction::Call(transfer_recipient.into()),
843
1
			value: U256::from(100),
844
1
			input: BoundedVec::<
845
1
				u8,
846
1
				ConstU32<{ xcm_primitives::MAX_ETHEREUM_XCM_INPUT_SIZE }>
847
1
			>::try_from(vec![]).unwrap(),
848
1
			access_list: None,
849
1
		});
850
1

            
851
1
	// Then call bytes
852
1
	let mut call_bytes = pallet_ethereum_xcm::Call::<parachain::Runtime>::transact_through_proxy {
853
1
		transact_as: PARAALICE.into(),
854
1
		xcm_transaction: eth_tx,
855
1
	}
856
1
	.encode();
857
1
	encoded.append(&mut call_bytes);
858
1

            
859
1
	ParaA::execute_with(|| {
860
1
		assert_ok!(XcmTransactor::transact_through_signed(
861
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
862
1
			Box::new(xcm::VersionedLocation::from(para_b_location)),
863
1
			CurrencyPayment {
864
1
				currency: Currency::AsMultiLocation(Box::new(xcm::VersionedLocation::from(
865
1
					para_b_balances
866
1
				))),
867
1
				fee_amount: None
868
1
			},
869
1
			encoded,
870
1
			TransactWeights {
871
1
				transact_required_weight_at_most: 4000000000.into(),
872
1
				overall_weight: None
873
1
			},
874
1
			false
875
1
		));
876
1
	});
877
1

            
878
1
	ParaB::execute_with(|| {
879
1
		// Make sure the EVM transfer was executed
880
1
		assert!(
881
1
			ParaBalances::free_balance(&transfer_recipient.into())
882
1
				== transfer_recipient_balance_before + 100
883
1
		);
884
1
	});
885
1
}