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
	events, AssetAccount, ExtBuilder, PCall, Precompiles, PrecompilesValue, Runtime,
19
	SelfReserveAccount,
20
};
21
use crate::{Currency, EvmAsset, MAX_ASSETS};
22
use pallet_xcm::Event as PolkadotXcmEvent;
23
use precompile_utils::{prelude::*, testing::*};
24
use sp_core::U256;
25
use sp_weights::Weight;
26
use xcm::latest::{Junction, Location};
27

            
28
19
fn precompiles() -> Precompiles<Runtime> {
29
19
	PrecompilesValue::get()
30
19
}
31

            
32
#[test]
33
1
fn test_selector_enum() {
34
1
	assert!(PCall::transfer_selectors().contains(&0xb9f813ff));
35
1
	assert!(PCall::transfer_multiasset_selectors().contains(&0xb4f76f96));
36
1
	assert!(PCall::transfer_multi_currencies_selectors().contains(&0xab946323));
37
1
	assert!(PCall::transfer_with_fee_selectors().contains(&0x3e506ef0));
38
1
	assert!(PCall::transfer_multiasset_with_fee_selectors().contains(&0x150c016a));
39
1
}
40

            
41
#[test]
42
1
fn modifiers() {
43
1
	ExtBuilder::default().build().execute_with(|| {
44
1
		let mut tester = PrecompilesModifierTester::new(precompiles(), Alice, Precompile1);
45
1

            
46
1
		tester.test_default_modifier(PCall::transfer_selectors());
47
1
		tester.test_default_modifier(PCall::transfer_multiasset_selectors());
48
1
		tester.test_default_modifier(PCall::transfer_multi_currencies_selectors());
49
1
		tester.test_default_modifier(PCall::transfer_with_fee_selectors());
50
1
		tester.test_default_modifier(PCall::transfer_multiasset_with_fee_selectors());
51
1
	});
52
1
}
53

            
54
#[test]
55
1
fn selector_less_than_four_bytes() {
56
1
	ExtBuilder::default().build().execute_with(|| {
57
1
		precompiles()
58
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8])
59
1
			.execute_reverts(|output| output == b"Tried to read selector out of bounds");
60
1
	});
61
1
}
62

            
63
#[test]
64
1
fn no_selector_exists_but_length_is_right() {
65
1
	ExtBuilder::default().build().execute_with(|| {
66
1
		precompiles()
67
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2u8, 3u8, 4u8])
68
1
			.execute_reverts(|output| output == b"Unknown selector");
69
1
	});
70
1
}
71

            
72
#[test]
73
1
fn transfer_self_reserve_works() {
74
1
	ExtBuilder::default()
75
1
		.with_balances(vec![(Alice.into(), 1000)])
76
1
		.build()
77
1
		.execute_with(|| {
78
1
			let destination = Location::new(
79
1
				1,
80
1
				[Junction::AccountId32 {
81
1
					network: None,
82
1
					id: [1u8; 32],
83
1
				}],
84
1
			);
85
1

            
86
1
			precompiles()
87
1
				.prepare_test(
88
1
					Alice,
89
1
					Precompile1,
90
1
					PCall::transfer {
91
1
						currency_address: Address(SelfReserveAccount.into()),
92
1
						amount: 500.into(),
93
1
						destination: destination.clone().into(),
94
1
						weight: 4_000_000,
95
1
					},
96
1
				)
97
1
				.expect_cost(100000000)
98
1
				.expect_no_logs()
99
1
				.execute_returns(());
100
1

            
101
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
102
1
				outcome: xcm::latest::Outcome::Complete {
103
1
					used: Weight::from_parts(3000, 3000),
104
1
				},
105
1
			}
106
1
			.into();
107
1

            
108
1
			// Assert that the events vector contains the one expected
109
1
			assert!(events().contains(&expected));
110
1
		});
111
1
}
112

            
113
#[test]
114
1
fn transfer_to_reserve_works() {
115
1
	ExtBuilder::default()
116
1
		.with_balances(vec![(Alice.into(), 1000)])
117
1
		.build()
118
1
		.execute_with(|| {
119
1
			let destination = Location::new(
120
1
				1,
121
1
				[Junction::AccountId32 {
122
1
					network: None,
123
1
					id: [1u8; 32],
124
1
				}],
125
1
			);
126
1
			// We are transferring asset 0, which we have instructed to be the relay asset
127
1
			precompiles()
128
1
				.prepare_test(
129
1
					Alice,
130
1
					Precompile1,
131
1
					PCall::transfer {
132
1
						currency_address: Address(AssetAccount(0u128).into()),
133
1
						amount: 500.into(),
134
1
						destination: destination.clone().into(),
135
1
						weight: 4_000_000,
136
1
					},
137
1
				)
138
1
				.expect_cost(100000000)
139
1
				.expect_no_logs()
140
1
				.execute_returns(());
141
1

            
142
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
143
1
				outcome: xcm::latest::Outcome::Complete {
144
1
					used: Weight::from_parts(2000, 2000),
145
1
				},
146
1
			}
147
1
			.into();
148
1
			// Assert that the events vector contains the one expected
149
1
			assert!(events().contains(&expected));
150
1
		});
151
1
}
152

            
153
#[test]
154
1
fn transfer_to_reserve_with_unlimited_weight_works() {
155
1
	ExtBuilder::default()
156
1
		.with_balances(vec![(Alice.into(), 1000)])
157
1
		.build()
158
1
		.execute_with(|| {
159
1
			let destination = Location::new(
160
1
				1,
161
1
				[Junction::AccountId32 {
162
1
					network: None,
163
1
					id: [1u8; 32],
164
1
				}],
165
1
			);
166
1
			// We are transferring asset 0, which we have instructed to be the relay asset
167
1
			precompiles()
168
1
				.prepare_test(
169
1
					Alice,
170
1
					Precompile1,
171
1
					PCall::transfer {
172
1
						currency_address: Address(AssetAccount(0u128).into()),
173
1
						amount: 500.into(),
174
1
						destination: destination.clone().into(),
175
1
						weight: u64::MAX,
176
1
					},
177
1
				)
178
1
				.expect_cost(100000000)
179
1
				.expect_no_logs()
180
1
				.execute_returns(());
181
1

            
182
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
183
1
				outcome: xcm::latest::Outcome::Complete {
184
1
					used: Weight::from_parts(2000, 2000),
185
1
				},
186
1
			}
187
1
			.into();
188
1

            
189
1
			// Assert that the events vector contains the one expected
190
1
			assert!(events().contains(&expected));
191
1
		});
192
1
}
193

            
194
#[test]
195
1
fn transfer_to_reserve_with_fee_works() {
196
1
	ExtBuilder::default()
197
1
		.with_balances(vec![(Alice.into(), 1000)])
198
1
		.build()
199
1
		.execute_with(|| {
200
1
			let destination = Location::new(
201
1
				1,
202
1
				[Junction::AccountId32 {
203
1
					network: None,
204
1
					id: [1u8; 32],
205
1
				}],
206
1
			);
207
1
			// We are transferring asset 0, which we have instructed to be the relay asset
208
1
			// Fees are not trully charged, so no worries
209
1
			precompiles()
210
1
				.prepare_test(
211
1
					Alice,
212
1
					Precompile1,
213
1
					PCall::transfer_with_fee {
214
1
						currency_address: Address(AssetAccount(0u128).into()),
215
1
						amount: 500.into(),
216
1
						_fee: 50.into(),
217
1
						destination: destination.clone().into(),
218
1
						weight: 4_000_000,
219
1
					},
220
1
				)
221
1
				.expect_cost(100000000)
222
1
				.expect_no_logs()
223
1
				.execute_returns(());
224
1

            
225
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
226
1
				outcome: xcm::latest::Outcome::Complete {
227
1
					used: Weight::from_parts(2000, 2000),
228
1
				},
229
1
			}
230
1
			.into();
231
1

            
232
1
			// Assert that the events vector contains the one expected
233
1
			assert!(events().contains(&expected));
234
1
		});
235
1
}
236

            
237
#[test]
238
1
fn transfer_non_reserve_to_non_reserve_works() {
239
1
	ExtBuilder::default()
240
1
		.with_balances(vec![(Alice.into(), 1000)])
241
1
		.build()
242
1
		.execute_with(|| {
243
1
			let destination = Location::new(
244
1
				1,
245
1
				[Junction::AccountId32 {
246
1
					network: None,
247
1
					id: [1u8; 32],
248
1
				}],
249
1
			);
250
1

            
251
1
			// We are transferring asset 1, which corresponds to another parachain Id asset
252
1
			precompiles()
253
1
				.prepare_test(
254
1
					Alice,
255
1
					Precompile1,
256
1
					PCall::transfer {
257
1
						currency_address: Address(AssetAccount(1u128).into()),
258
1
						amount: 500.into(),
259
1
						destination: destination.clone().into(),
260
1
						weight: 4_000_000,
261
1
					},
262
1
				)
263
1
				.expect_cost(100000000)
264
1
				.expect_no_logs()
265
1
				.execute_returns(());
266
1

            
267
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
268
1
				outcome: xcm::latest::Outcome::Complete {
269
1
					used: Weight::from_parts(3000, 3000),
270
1
				},
271
1
			}
272
1
			.into();
273
1
			// Assert that the events vector contains the one expected
274
1
			assert!(events().contains(&expected));
275
1
		});
276
1
}
277

            
278
#[test]
279
1
fn transfer_non_reserve_to_non_reserve_with_fee_works() {
280
1
	ExtBuilder::default()
281
1
		.with_balances(vec![(Alice.into(), 1000)])
282
1
		.build()
283
1
		.execute_with(|| {
284
1
			let destination = Location::new(
285
1
				1,
286
1
				[Junction::AccountId32 {
287
1
					network: None,
288
1
					id: [1u8; 32],
289
1
				}],
290
1
			);
291
1

            
292
1
			// We are transferring asset 1, which corresponds to another parachain Id asset
293
1
			precompiles()
294
1
				.prepare_test(
295
1
					Alice,
296
1
					Precompile1,
297
1
					PCall::transfer_with_fee {
298
1
						currency_address: Address(AssetAccount(1u128).into()),
299
1
						amount: 500.into(),
300
1
						_fee: 50.into(),
301
1
						destination: destination.clone().into(),
302
1
						weight: 4_000_000,
303
1
					},
304
1
				)
305
1
				.expect_cost(100000000)
306
1
				.expect_no_logs()
307
1
				.execute_returns(());
308
1

            
309
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
310
1
				outcome: xcm::latest::Outcome::Complete {
311
1
					used: Weight::from_parts(3000, 3000),
312
1
				},
313
1
			}
314
1
			.into();
315
1
			// Assert that the events vector contains the one expected
316
1
			assert!(events().contains(&expected));
317
1
		});
318
1
}
319

            
320
#[test]
321
1
fn transfer_multi_asset_to_reserve_works() {
322
1
	ExtBuilder::default()
323
1
		.with_balances(vec![(Alice.into(), 1000)])
324
1
		.build()
325
1
		.execute_with(|| {
326
1
			let destination = Location::new(
327
1
				1,
328
1
				[Junction::AccountId32 {
329
1
					network: None,
330
1
					id: [1u8; 32],
331
1
				}],
332
1
			);
333
1

            
334
1
			let asset = Location::parent();
335
1

            
336
1
			precompiles()
337
1
				.prepare_test(
338
1
					Alice,
339
1
					Precompile1,
340
1
					PCall::transfer_multiasset {
341
1
						asset: asset.clone().into(),
342
1
						amount: 500.into(),
343
1
						destination: destination.clone().into(),
344
1
						weight: 4_000_000,
345
1
					},
346
1
				)
347
1
				.expect_cost(100000000)
348
1
				.expect_no_logs()
349
1
				.execute_returns(());
350
1

            
351
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
352
1
				outcome: xcm::latest::Outcome::Complete {
353
1
					used: Weight::from_parts(2000, 2000),
354
1
				},
355
1
			}
356
1
			.into();
357
1

            
358
1
			// Assert that the events vector contains the one expected
359
1
			assert!(events().contains(&expected));
360
1
		});
361
1
}
362

            
363
#[test]
364
1
fn transfer_multi_asset_self_reserve_works() {
365
1
	ExtBuilder::default()
366
1
		.with_balances(vec![(Alice.into(), 1000)])
367
1
		.build()
368
1
		.execute_with(|| {
369
1
			let destination = Location::new(
370
1
				1,
371
1
				[Junction::AccountId32 {
372
1
					network: None,
373
1
					id: [1u8; 32],
374
1
				}],
375
1
			);
376
1

            
377
1
			let self_reserve = crate::mock::SelfReserve::get();
378
1

            
379
1
			precompiles()
380
1
				.prepare_test(
381
1
					Alice,
382
1
					Precompile1,
383
1
					PCall::transfer_multiasset {
384
1
						asset: self_reserve.clone().into(),
385
1
						amount: 500.into(),
386
1
						destination: destination.clone().into(),
387
1
						weight: 4_000_000,
388
1
					},
389
1
				)
390
1
				.expect_cost(100000000)
391
1
				.expect_no_logs()
392
1
				.execute_returns(());
393
1

            
394
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
395
1
				outcome: xcm::latest::Outcome::Complete {
396
1
					used: Weight::from_parts(3000, 3000),
397
1
				},
398
1
			}
399
1
			.into();
400
1
			// Assert that the events vector contains the one expected
401
1
			assert!(events().contains(&expected));
402
1
		});
403
1
}
404

            
405
#[test]
406
1
fn transfer_multi_asset_self_reserve_with_fee_works() {
407
1
	ExtBuilder::default()
408
1
		.with_balances(vec![(Alice.into(), 1000)])
409
1
		.build()
410
1
		.execute_with(|| {
411
1
			let destination = Location::new(
412
1
				1,
413
1
				[Junction::AccountId32 {
414
1
					network: None,
415
1
					id: [1u8; 32],
416
1
				}],
417
1
			);
418
1

            
419
1
			let self_reserve = crate::mock::SelfReserve::get();
420
1

            
421
1
			precompiles()
422
1
				.prepare_test(
423
1
					Alice,
424
1
					Precompile1,
425
1
					PCall::transfer_multiasset_with_fee {
426
1
						asset: self_reserve.clone().into(),
427
1
						amount: 500.into(),
428
1
						_fee: 50.into(),
429
1
						destination: destination.clone().into(),
430
1
						weight: 4_000_000,
431
1
					},
432
1
				)
433
1
				.expect_cost(100000000)
434
1
				.expect_no_logs()
435
1
				.execute_returns(());
436
1

            
437
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
438
1
				outcome: xcm::latest::Outcome::Complete {
439
1
					used: Weight::from_parts(3000, 3000),
440
1
				},
441
1
			}
442
1
			.into();
443
1
			// Assert that the events vector contains the one expected
444
1
			assert!(events().contains(&expected));
445
1
		});
446
1
}
447

            
448
#[test]
449
1
fn transfer_multi_asset_non_reserve_to_non_reserve() {
450
1
	ExtBuilder::default()
451
1
		.with_balances(vec![(Alice.into(), 1000)])
452
1
		.build()
453
1
		.execute_with(|| {
454
1
			let destination = Location::new(
455
1
				1,
456
1
				[Junction::AccountId32 {
457
1
					network: None,
458
1
					id: [1u8; 32],
459
1
				}],
460
1
			);
461
1

            
462
1
			let asset_location =
463
1
				Location::new(1, [Junction::Parachain(2), Junction::GeneralIndex(5u128)]);
464
1

            
465
1
			precompiles()
466
1
				.prepare_test(
467
1
					Alice,
468
1
					Precompile1,
469
1
					PCall::transfer_multiasset {
470
1
						asset: asset_location.clone().into(),
471
1
						amount: 500.into(),
472
1
						destination: destination.clone().into(),
473
1
						weight: 4_000_000,
474
1
					},
475
1
				)
476
1
				.expect_cost(100000000)
477
1
				.expect_no_logs()
478
1
				.execute_returns(());
479
1

            
480
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
481
1
				outcome: xcm::latest::Outcome::Complete {
482
1
					used: Weight::from_parts(3000, 3000),
483
1
				},
484
1
			}
485
1
			.into();
486
1
			// Assert that the events vector contains the one expected
487
1
			assert!(events().contains(&expected));
488
1
		});
489
1
}
490

            
491
#[test]
492
1
fn transfer_multi_asset_non_reserve_to_non_reserve_with_fee() {
493
1
	ExtBuilder::default()
494
1
		.with_balances(vec![(Alice.into(), 1000)])
495
1
		.build()
496
1
		.execute_with(|| {
497
1
			let destination = Location::new(
498
1
				1,
499
1
				[Junction::AccountId32 {
500
1
					network: None,
501
1
					id: [1u8; 32],
502
1
				}],
503
1
			);
504
1

            
505
1
			let asset_location =
506
1
				Location::new(1, [Junction::Parachain(2), Junction::GeneralIndex(5u128)]);
507
1

            
508
1
			precompiles()
509
1
				.prepare_test(
510
1
					Alice,
511
1
					Precompile1,
512
1
					PCall::transfer_multiasset_with_fee {
513
1
						asset: asset_location.clone().into(),
514
1
						amount: 500.into(),
515
1
						_fee: 50.into(),
516
1
						destination: destination.clone().into(),
517
1
						weight: 4_000_000,
518
1
					},
519
1
				)
520
1
				.expect_cost(100000000)
521
1
				.expect_no_logs()
522
1
				.execute_returns(());
523
1

            
524
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
525
1
				outcome: xcm::latest::Outcome::Complete {
526
1
					used: Weight::from_parts(3000, 3000),
527
1
				},
528
1
			}
529
1
			.into();
530
1
			// Assert that the events vector contains the one expected
531
1
			assert!(events().contains(&expected));
532
1
		});
533
1
}
534

            
535
#[test]
536
1
fn transfer_multi_currencies() {
537
1
	ExtBuilder::default()
538
1
		.with_balances(vec![(Alice.into(), 1000)])
539
1
		.build()
540
1
		.execute_with(|| {
541
1
			let destination = Location::new(
542
1
				1,
543
1
				[Junction::AccountId32 {
544
1
					network: None,
545
1
					id: [1u8; 32],
546
1
				}],
547
1
			);
548
1
			let currencies: Vec<Currency> = vec![
549
1
				(Address(AssetAccount(1u128).into()), U256::from(500)).into(),
550
1
				(Address(AssetAccount(2u128).into()), U256::from(500)).into(),
551
1
			];
552
1

            
553
1
			// We are transferring 2 assets
554
1
			precompiles()
555
1
				.prepare_test(
556
1
					Alice,
557
1
					Precompile1,
558
1
					PCall::transfer_multi_currencies {
559
1
						currencies: currencies.into(),
560
1
						fee_item: 0,
561
1
						destination: destination.clone().into(),
562
1
						weight: 4_000_000,
563
1
					},
564
1
				)
565
1
				.expect_cost(100000000)
566
1
				.expect_no_logs()
567
1
				.execute_returns(());
568
1

            
569
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
570
1
				outcome: xcm::latest::Outcome::Complete {
571
1
					used: Weight::from_parts(3000, 3000),
572
1
				},
573
1
			}
574
1
			.into();
575
1
			// Assert that the events vector contains the one expected
576
1
			assert!(events().contains(&expected));
577
1
		});
578
1
}
579

            
580
#[test]
581
1
fn transfer_multi_assets() {
582
1
	ExtBuilder::default()
583
1
		.with_balances(vec![(Alice.into(), 1000)])
584
1
		.build()
585
1
		.execute_with(|| {
586
1
			let destination = Location::new(
587
1
				1,
588
1
				[
589
1
					Junction::Parachain(2),
590
1
					Junction::AccountId32 {
591
1
						network: None,
592
1
						id: [1u8; 32],
593
1
					},
594
1
				],
595
1
			);
596
1

            
597
1
			let asset_1_location =
598
1
				Location::new(1, [Junction::Parachain(2), Junction::GeneralIndex(0u128)]);
599
1
			let asset_2_location =
600
1
				Location::new(1, [Junction::Parachain(2), Junction::GeneralIndex(1u128)]);
601
1

            
602
1
			let assets: Vec<EvmAsset> = vec![
603
1
				(asset_1_location.clone(), U256::from(500)).into(),
604
1
				(asset_2_location.clone(), U256::from(500)).into(),
605
1
			];
606
1

            
607
1
			// We are transferring 2 assets
608
1
			precompiles()
609
1
				.prepare_test(
610
1
					Alice,
611
1
					Precompile1,
612
1
					PCall::transfer_multi_assets {
613
1
						assets: assets.into(),
614
1
						fee_item: 0,
615
1
						destination: destination.clone().into(),
616
1
						weight: 4_000_000,
617
1
					},
618
1
				)
619
1
				.expect_cost(100000000)
620
1
				.expect_no_logs()
621
1
				.execute_returns(());
622
1

            
623
1
			let expected: crate::mock::RuntimeEvent = PolkadotXcmEvent::Attempted {
624
1
				outcome: xcm::latest::Outcome::Complete {
625
1
					used: Weight::from_parts(2000, 2000),
626
1
				},
627
1
			}
628
1
			.into();
629
1
			println!("Events are {:?}", events());
630
1
			println!("Expected is {:?}", expected);
631
1
			// Assert that the events vector contains the one expected
632
1
			assert!(events().contains(&expected));
633
1
		});
634
1
}
635

            
636
#[test]
637
1
fn transfer_multi_currencies_cannot_insert_more_than_max() {
638
1
	ExtBuilder::default()
639
1
		.with_balances(vec![(Alice.into(), 1000)])
640
1
		.build()
641
1
		.execute_with(|| {
642
1
			let destination = Location::new(
643
1
				1,
644
1
				[Junction::AccountId32 {
645
1
					network: None,
646
1
					id: [1u8; 32],
647
1
				}],
648
1
			);
649
1
			let mut currencies: Vec<Currency> = Vec::new();
650
21
			for i in 0..MAX_ASSETS + 1 {
651
21
				currencies.push((Address(AssetAccount(i as u128).into()), U256::from(500)).into());
652
21
			}
653

            
654
			// We are transferring 3 assets, when max is 2
655
1
			precompiles()
656
1
				.prepare_test(
657
1
					Alice,
658
1
					Precompile1,
659
1
					PCall::transfer_multi_currencies {
660
1
						currencies: currencies.into(),
661
1
						fee_item: 0,
662
1
						destination: destination.clone().into(),
663
1
						weight: 4_000_000,
664
1
					},
665
1
				)
666
1
				.execute_reverts(|output| output == b"currencies: Value is too large for length");
667
1
		});
668
1
}
669

            
670
#[test]
671
1
fn transfer_multi_assets_cannot_insert_more_than_max() {
672
1
	ExtBuilder::default()
673
1
		.with_balances(vec![(Alice.into(), 1000)])
674
1
		.build()
675
1
		.execute_with(|| {
676
1
			let destination = Location::new(
677
1
				1,
678
1
				[
679
1
					Junction::Parachain(2),
680
1
					Junction::AccountId32 {
681
1
						network: None,
682
1
						id: [1u8; 32],
683
1
					},
684
1
				],
685
1
			);
686
1

            
687
1
			let mut assets: Vec<EvmAsset> = Vec::new();
688
21
			for i in 0..MAX_ASSETS + 1 {
689
21
				let asset_location = Location::new(
690
21
					1,
691
21
					[Junction::Parachain(2), Junction::GeneralIndex(i as u128)],
692
21
				);
693
21
				assets.push((asset_location, U256::from(500)).into());
694
21
			}
695

            
696
			// We are transferring 3 assets, when max is 2
697
1
			precompiles()
698
1
				.prepare_test(
699
1
					Alice,
700
1
					Precompile1,
701
1
					PCall::transfer_multi_assets {
702
1
						assets: assets.into(),
703
1
						fee_item: 0,
704
1
						destination: destination.clone().into(),
705
1
						weight: 4_000_000,
706
1
					},
707
1
				)
708
1
				.execute_reverts(|output| output == b"assets: Value is too large for length");
709
1
		});
710
1
}
711

            
712
#[test]
713
1
fn transfer_multi_assets_is_not_sorted_error() {
714
1
	ExtBuilder::default()
715
1
		.with_balances(vec![(Alice.into(), 1000)])
716
1
		.build()
717
1
		.execute_with(|| {
718
1
			let destination = Location::new(
719
1
				1,
720
1
				[
721
1
					Junction::Parachain(2),
722
1
					Junction::AccountId32 {
723
1
						network: None,
724
1
						id: [1u8; 32],
725
1
					},
726
1
				],
727
1
			);
728
1

            
729
1
			// Disordered vec creation
730
1
			let asset_1_location =
731
1
				Location::new(1, [Junction::Parachain(2), Junction::GeneralIndex(1u128)]);
732
1
			let asset_2_location =
733
1
				Location::new(1, [Junction::Parachain(2), Junction::GeneralIndex(0u128)]);
734
1

            
735
1
			let assets: Vec<EvmAsset> = vec![
736
1
				(asset_1_location.clone(), U256::from(500)).into(),
737
1
				(asset_2_location.clone(), U256::from(500)).into(),
738
1
			];
739
1

            
740
1
			// We are transferring 3 assets, when max is 2
741
1
			precompiles()
742
1
				.prepare_test(
743
1
					Alice,
744
1
					Precompile1,
745
1
					PCall::transfer_multi_assets {
746
1
						assets: assets.into(),
747
1
						fee_item: 0,
748
1
						destination: destination.clone().into(),
749
1
						weight: 4_000_000,
750
1
					},
751
1
				)
752
1
				.execute_reverts(|output| {
753
1
					output == b"assets: Provided assets either not sorted nor deduplicated"
754
1
				});
755
1
		});
756
1
}
757

            
758
#[test]
759
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
760
1
	check_precompile_implements_solidity_interfaces(&["Xtokens.sol"], PCall::supports_selector)
761
1
}
762

            
763
#[test]
764
1
fn test_deprecated_solidity_selectors_are_supported() {
765
5
	for deprecated_function in [
766
		"transfer_with_fee(address,uint256,uint256,(uint8,bytes[]),uint64)",
767
1
		"transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),uint64)",
768
1
		"transfer_multiasset_with_fee((uint8,bytes[]),uint256,uint256,(uint8,bytes[]),uint64)",
769
1
		"transfer_multi_currencies((address,uint256)[],uint32,(uint8,bytes[]),uint64)",
770
1
		"transfer_multi_assets(((uint8,bytes[]),uint256)[],uint32,(uint8,bytes[]),uint64)",
771
	] {
772
5
		let selector = compute_selector(deprecated_function);
773
5
		if !PCall::supports_selector(selector) {
774
			panic!(
775
				"failed decoding selector 0x{:x} => '{}' as Action",
776
				selector, deprecated_function,
777
			)
778
5
		}
779
	}
780
1
}